roojs-ui.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isGecko = !isSafari && ua.indexOf("gecko") > -1,
61         isBorderBox = isIE && !isStrict,
62         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
63         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
64         isLinux = (ua.indexOf("linux") != -1),
65         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
66         isIOS = /iphone|ipad/.test(ua),
67         isTouch =  (function() {
68             try {  
69                 document.createEvent("TouchEvent");  
70                 return true;  
71             } catch (e) {  
72                 return false;  
73             } 
74             
75         })();
76     // remove css image flicker
77         if(isIE && !isIE7){
78         try{
79             document.execCommand("BackgroundImageCache", false, true);
80         }catch(e){}
81     }
82     
83     Roo.apply(Roo, {
84         /**
85          * True if the browser is in strict mode
86          * @type Boolean
87          */
88         isStrict : isStrict,
89         /**
90          * True if the page is running over SSL
91          * @type Boolean
92          */
93         isSecure : isSecure,
94         /**
95          * True when the document is fully initialized and ready for action
96          * @type Boolean
97          */
98         isReady : false,
99         /**
100          * Turn on debugging output (currently only the factory uses this)
101          * @type Boolean
102          */
103         
104         debug: false,
105
106         /**
107          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
108          * @type Boolean
109          */
110         enableGarbageCollector : true,
111
112         /**
113          * True to automatically purge event listeners after uncaching an element (defaults to false).
114          * Note: this only happens if enableGarbageCollector is true.
115          * @type Boolean
116          */
117         enableListenerCollection:false,
118
119         /**
120          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
121          * the IE insecure content warning (defaults to javascript:false).
122          * @type String
123          */
124         SSL_SECURE_URL : "javascript:false",
125
126         /**
127          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
128          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
129          * @type String
130          */
131         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
132
133         emptyFn : function(){},
134         
135         /**
136          * Copies all the properties of config to obj if they don't already exist.
137          * @param {Object} obj The receiver of the properties
138          * @param {Object} config The source of the properties
139          * @return {Object} returns obj
140          */
141         applyIf : function(o, c){
142             if(o && c){
143                 for(var p in c){
144                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
145                 }
146             }
147             return o;
148         },
149
150         /**
151          * Applies event listeners to elements by selectors when the document is ready.
152          * The event name is specified with an @ suffix.
153 <pre><code>
154 Roo.addBehaviors({
155    // add a listener for click on all anchors in element with id foo
156    '#foo a@click' : function(e, t){
157        // do something
158    },
159
160    // add the same listener to multiple selectors (separated by comma BEFORE the @)
161    '#foo a, #bar span.some-class@mouseover' : function(){
162        // do something
163    }
164 });
165 </code></pre>
166          * @param {Object} obj The list of behaviors to apply
167          */
168         addBehaviors : function(o){
169             if(!Roo.isReady){
170                 Roo.onReady(function(){
171                     Roo.addBehaviors(o);
172                 });
173                 return;
174             }
175             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
176             for(var b in o){
177                 var parts = b.split('@');
178                 if(parts[1]){ // for Object prototype breakers
179                     var s = parts[0];
180                     if(!cache[s]){
181                         cache[s] = Roo.select(s);
182                     }
183                     cache[s].on(parts[1], o[b]);
184                 }
185             }
186             cache = null;
187         },
188
189         /**
190          * Generates unique ids. If the element already has an id, it is unchanged
191          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
192          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
193          * @return {String} The generated Id.
194          */
195         id : function(el, prefix){
196             prefix = prefix || "roo-gen";
197             el = Roo.getDom(el);
198             var id = prefix + (++idSeed);
199             return el ? (el.id ? el.id : (el.id = id)) : id;
200         },
201          
202        
203         /**
204          * Extends one class with another class and optionally overrides members with the passed literal. This class
205          * also adds the function "override()" to the class that can be used to override
206          * members on an instance.
207          * @param {Object} subclass The class inheriting the functionality
208          * @param {Object} superclass The class being extended
209          * @param {Object} overrides (optional) A literal with members
210          * @method extend
211          */
212         extend : function(){
213             // inline overrides
214             var io = function(o){
215                 for(var m in o){
216                     this[m] = o[m];
217                 }
218             };
219             return function(sb, sp, overrides){
220                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
221                     overrides = sp;
222                     sp = sb;
223                     sb = function(){sp.apply(this, arguments);};
224                 }
225                 var F = function(){}, sbp, spp = sp.prototype;
226                 F.prototype = spp;
227                 sbp = sb.prototype = new F();
228                 sbp.constructor=sb;
229                 sb.superclass=spp;
230                 
231                 if(spp.constructor == Object.prototype.constructor){
232                     spp.constructor=sp;
233                    
234                 }
235                 
236                 sb.override = function(o){
237                     Roo.override(sb, o);
238                 };
239                 sbp.override = io;
240                 Roo.override(sb, overrides);
241                 return sb;
242             };
243         }(),
244
245         /**
246          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
247          * Usage:<pre><code>
248 Roo.override(MyClass, {
249     newMethod1: function(){
250         // etc.
251     },
252     newMethod2: function(foo){
253         // etc.
254     }
255 });
256  </code></pre>
257          * @param {Object} origclass The class to override
258          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
259          * containing one or more methods.
260          * @method override
261          */
262         override : function(origclass, overrides){
263             if(overrides){
264                 var p = origclass.prototype;
265                 for(var method in overrides){
266                     p[method] = overrides[method];
267                 }
268             }
269         },
270         /**
271          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
272          * <pre><code>
273 Roo.namespace('Company', 'Company.data');
274 Company.Widget = function() { ... }
275 Company.data.CustomStore = function(config) { ... }
276 </code></pre>
277          * @param {String} namespace1
278          * @param {String} namespace2
279          * @param {String} etc
280          * @method namespace
281          */
282         namespace : function(){
283             var a=arguments, o=null, i, j, d, rt;
284             for (i=0; i<a.length; ++i) {
285                 d=a[i].split(".");
286                 rt = d[0];
287                 /** eval:var:o */
288                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
289                 for (j=1; j<d.length; ++j) {
290                     o[d[j]]=o[d[j]] || {};
291                     o=o[d[j]];
292                 }
293             }
294         },
295         /**
296          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
297          * <pre><code>
298 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
299 Roo.factory(conf, Roo.data);
300 </code></pre>
301          * @param {String} classname
302          * @param {String} namespace (optional)
303          * @method factory
304          */
305          
306         factory : function(c, ns)
307         {
308             // no xtype, no ns or c.xns - or forced off by c.xns
309             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
310                 return c;
311             }
312             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
313             if (c.constructor == ns[c.xtype]) {// already created...
314                 return c;
315             }
316             if (ns[c.xtype]) {
317                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
318                 var ret = new ns[c.xtype](c);
319                 ret.xns = false;
320                 return ret;
321             }
322             c.xns = false; // prevent recursion..
323             return c;
324         },
325          /**
326          * Logs to console if it can.
327          *
328          * @param {String|Object} string
329          * @method log
330          */
331         log : function(s)
332         {
333             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
334                 return; // alerT?
335             }
336             console.log(s);
337             
338         },
339         /**
340          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
341          * @param {Object} o
342          * @return {String}
343          */
344         urlEncode : function(o){
345             if(!o){
346                 return "";
347             }
348             var buf = [];
349             for(var key in o){
350                 var ov = o[key], k = Roo.encodeURIComponent(key);
351                 var type = typeof ov;
352                 if(type == 'undefined'){
353                     buf.push(k, "=&");
354                 }else if(type != "function" && type != "object"){
355                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
356                 }else if(ov instanceof Array){
357                     if (ov.length) {
358                             for(var i = 0, len = ov.length; i < len; i++) {
359                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
360                             }
361                         } else {
362                             buf.push(k, "=&");
363                         }
364                 }
365             }
366             buf.pop();
367             return buf.join("");
368         },
369          /**
370          * Safe version of encodeURIComponent
371          * @param {String} data 
372          * @return {String} 
373          */
374         
375         encodeURIComponent : function (data)
376         {
377             try {
378                 return encodeURIComponent(data);
379             } catch(e) {} // should be an uri encode error.
380             
381             if (data == '' || data == null){
382                return '';
383             }
384             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
385             function nibble_to_hex(nibble){
386                 var chars = '0123456789ABCDEF';
387                 return chars.charAt(nibble);
388             }
389             data = data.toString();
390             var buffer = '';
391             for(var i=0; i<data.length; i++){
392                 var c = data.charCodeAt(i);
393                 var bs = new Array();
394                 if (c > 0x10000){
395                         // 4 bytes
396                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
397                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
398                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
399                     bs[3] = 0x80 | (c & 0x3F);
400                 }else if (c > 0x800){
401                          // 3 bytes
402                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
403                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
404                     bs[2] = 0x80 | (c & 0x3F);
405                 }else if (c > 0x80){
406                        // 2 bytes
407                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
408                     bs[1] = 0x80 | (c & 0x3F);
409                 }else{
410                         // 1 byte
411                     bs[0] = c;
412                 }
413                 for(var j=0; j<bs.length; j++){
414                     var b = bs[j];
415                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
416                             + nibble_to_hex(b &0x0F);
417                     buffer += '%'+hex;
418                }
419             }
420             return buffer;    
421              
422         },
423
424         /**
425          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
426          * @param {String} string
427          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
428          * @return {Object} A literal with members
429          */
430         urlDecode : function(string, overwrite){
431             if(!string || !string.length){
432                 return {};
433             }
434             var obj = {};
435             var pairs = string.split('&');
436             var pair, name, value;
437             for(var i = 0, len = pairs.length; i < len; i++){
438                 pair = pairs[i].split('=');
439                 name = decodeURIComponent(pair[0]);
440                 value = decodeURIComponent(pair[1]);
441                 if(overwrite !== true){
442                     if(typeof obj[name] == "undefined"){
443                         obj[name] = value;
444                     }else if(typeof obj[name] == "string"){
445                         obj[name] = [obj[name]];
446                         obj[name].push(value);
447                     }else{
448                         obj[name].push(value);
449                     }
450                 }else{
451                     obj[name] = value;
452                 }
453             }
454             return obj;
455         },
456
457         /**
458          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
459          * passed array is not really an array, your function is called once with it.
460          * The supplied function is called with (Object item, Number index, Array allItems).
461          * @param {Array/NodeList/Mixed} array
462          * @param {Function} fn
463          * @param {Object} scope
464          */
465         each : function(array, fn, scope){
466             if(typeof array.length == "undefined" || typeof array == "string"){
467                 array = [array];
468             }
469             for(var i = 0, len = array.length; i < len; i++){
470                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
471             }
472         },
473
474         // deprecated
475         combine : function(){
476             var as = arguments, l = as.length, r = [];
477             for(var i = 0; i < l; i++){
478                 var a = as[i];
479                 if(a instanceof Array){
480                     r = r.concat(a);
481                 }else if(a.length !== undefined && !a.substr){
482                     r = r.concat(Array.prototype.slice.call(a, 0));
483                 }else{
484                     r.push(a);
485                 }
486             }
487             return r;
488         },
489
490         /**
491          * Escapes the passed string for use in a regular expression
492          * @param {String} str
493          * @return {String}
494          */
495         escapeRe : function(s) {
496             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
497         },
498
499         // internal
500         callback : function(cb, scope, args, delay){
501             if(typeof cb == "function"){
502                 if(delay){
503                     cb.defer(delay, scope, args || []);
504                 }else{
505                     cb.apply(scope, args || []);
506                 }
507             }
508         },
509
510         /**
511          * Return the dom node for the passed string (id), dom node, or Roo.Element
512          * @param {String/HTMLElement/Roo.Element} el
513          * @return HTMLElement
514          */
515         getDom : function(el){
516             if(!el){
517                 return null;
518             }
519             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
520         },
521
522         /**
523         * Shorthand for {@link Roo.ComponentMgr#get}
524         * @param {String} id
525         * @return Roo.Component
526         */
527         getCmp : function(id){
528             return Roo.ComponentMgr.get(id);
529         },
530          
531         num : function(v, defaultValue){
532             if(typeof v != 'number'){
533                 return defaultValue;
534             }
535             return v;
536         },
537
538         destroy : function(){
539             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
540                 var as = a[i];
541                 if(as){
542                     if(as.dom){
543                         as.removeAllListeners();
544                         as.remove();
545                         continue;
546                     }
547                     if(typeof as.purgeListeners == 'function'){
548                         as.purgeListeners();
549                     }
550                     if(typeof as.destroy == 'function'){
551                         as.destroy();
552                     }
553                 }
554             }
555         },
556
557         // inpired by a similar function in mootools library
558         /**
559          * Returns the type of object that is passed in. If the object passed in is null or undefined it
560          * return false otherwise it returns one of the following values:<ul>
561          * <li><b>string</b>: If the object passed is a string</li>
562          * <li><b>number</b>: If the object passed is a number</li>
563          * <li><b>boolean</b>: If the object passed is a boolean value</li>
564          * <li><b>function</b>: If the object passed is a function reference</li>
565          * <li><b>object</b>: If the object passed is an object</li>
566          * <li><b>array</b>: If the object passed is an array</li>
567          * <li><b>regexp</b>: If the object passed is a regular expression</li>
568          * <li><b>element</b>: If the object passed is a DOM Element</li>
569          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
570          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
571          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
572          * @param {Mixed} object
573          * @return {String}
574          */
575         type : function(o){
576             if(o === undefined || o === null){
577                 return false;
578             }
579             if(o.htmlElement){
580                 return 'element';
581             }
582             var t = typeof o;
583             if(t == 'object' && o.nodeName) {
584                 switch(o.nodeType) {
585                     case 1: return 'element';
586                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
587                 }
588             }
589             if(t == 'object' || t == 'function') {
590                 switch(o.constructor) {
591                     case Array: return 'array';
592                     case RegExp: return 'regexp';
593                 }
594                 if(typeof o.length == 'number' && typeof o.item == 'function') {
595                     return 'nodelist';
596                 }
597             }
598             return t;
599         },
600
601         /**
602          * Returns true if the passed value is null, undefined or an empty string (optional).
603          * @param {Mixed} value The value to test
604          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
605          * @return {Boolean}
606          */
607         isEmpty : function(v, allowBlank){
608             return v === null || v === undefined || (!allowBlank ? v === '' : false);
609         },
610         
611         /** @type Boolean */
612         isOpera : isOpera,
613         /** @type Boolean */
614         isSafari : isSafari,
615         /** @type Boolean */
616         isFirefox : isFirefox,
617         /** @type Boolean */
618         isIE : isIE,
619         /** @type Boolean */
620         isIE7 : isIE7,
621         /** @type Boolean */
622         isIE11 : isIE11,
623         /** @type Boolean */
624         isGecko : isGecko,
625         /** @type Boolean */
626         isBorderBox : isBorderBox,
627         /** @type Boolean */
628         isWindows : isWindows,
629         /** @type Boolean */
630         isLinux : isLinux,
631         /** @type Boolean */
632         isMac : isMac,
633         /** @type Boolean */
634         isIOS : isIOS,
635         /** @type Boolean */
636         isTouch : isTouch,
637
638         /**
639          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
640          * you may want to set this to true.
641          * @type Boolean
642          */
643         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
644         
645         
646                 
647         /**
648          * Selects a single element as a Roo Element
649          * This is about as close as you can get to jQuery's $('do crazy stuff')
650          * @param {String} selector The selector/xpath query
651          * @param {Node} root (optional) The start of the query (defaults to document).
652          * @return {Roo.Element}
653          */
654         selectNode : function(selector, root) 
655         {
656             var node = Roo.DomQuery.selectNode(selector,root);
657             return node ? Roo.get(node) : new Roo.Element(false);
658         }
659         
660     });
661
662
663 })();
664
665 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
666                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
667                 "Roo.app", "Roo.ux",
668                 "Roo.bootstrap",
669                 "Roo.bootstrap.dash");
670 /*
671  * Based on:
672  * Ext JS Library 1.1.1
673  * Copyright(c) 2006-2007, Ext JS, LLC.
674  *
675  * Originally Released Under LGPL - original licence link has changed is not relivant.
676  *
677  * Fork - LGPL
678  * <script type="text/javascript">
679  */
680
681 (function() {    
682     // wrappedn so fnCleanup is not in global scope...
683     if(Roo.isIE) {
684         function fnCleanUp() {
685             var p = Function.prototype;
686             delete p.createSequence;
687             delete p.defer;
688             delete p.createDelegate;
689             delete p.createCallback;
690             delete p.createInterceptor;
691
692             window.detachEvent("onunload", fnCleanUp);
693         }
694         window.attachEvent("onunload", fnCleanUp);
695     }
696 })();
697
698
699 /**
700  * @class Function
701  * These functions are available on every Function object (any JavaScript function).
702  */
703 Roo.apply(Function.prototype, {
704      /**
705      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
706      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
707      * Will create a function that is bound to those 2 args.
708      * @return {Function} The new function
709     */
710     createCallback : function(/*args...*/){
711         // make args available, in function below
712         var args = arguments;
713         var method = this;
714         return function() {
715             return method.apply(window, args);
716         };
717     },
718
719     /**
720      * Creates a delegate (callback) that sets the scope to obj.
721      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
722      * Will create a function that is automatically scoped to this.
723      * @param {Object} obj (optional) The object for which the scope is set
724      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
725      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
726      *                                             if a number the args are inserted at the specified position
727      * @return {Function} The new function
728      */
729     createDelegate : function(obj, args, appendArgs){
730         var method = this;
731         return function() {
732             var callArgs = args || arguments;
733             if(appendArgs === true){
734                 callArgs = Array.prototype.slice.call(arguments, 0);
735                 callArgs = callArgs.concat(args);
736             }else if(typeof appendArgs == "number"){
737                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
738                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
739                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
740             }
741             return method.apply(obj || window, callArgs);
742         };
743     },
744
745     /**
746      * Calls this function after the number of millseconds specified.
747      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
748      * @param {Object} obj (optional) The object for which the scope is set
749      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
750      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
751      *                                             if a number the args are inserted at the specified position
752      * @return {Number} The timeout id that can be used with clearTimeout
753      */
754     defer : function(millis, obj, args, appendArgs){
755         var fn = this.createDelegate(obj, args, appendArgs);
756         if(millis){
757             return setTimeout(fn, millis);
758         }
759         fn();
760         return 0;
761     },
762     /**
763      * Create a combined function call sequence of the original function + the passed function.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function
766      * @param {Function} fcn The function to sequence
767      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
768      * @return {Function} The new function
769      */
770     createSequence : function(fcn, scope){
771         if(typeof fcn != "function"){
772             return this;
773         }
774         var method = this;
775         return function() {
776             var retval = method.apply(this || window, arguments);
777             fcn.apply(scope || this || window, arguments);
778             return retval;
779         };
780     },
781
782     /**
783      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
784      * The resulting function returns the results of the original function.
785      * The passed fcn is called with the parameters of the original function.
786      * @addon
787      * @param {Function} fcn The function to call before the original
788      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
789      * @return {Function} The new function
790      */
791     createInterceptor : function(fcn, scope){
792         if(typeof fcn != "function"){
793             return this;
794         }
795         var method = this;
796         return function() {
797             fcn.target = this;
798             fcn.method = method;
799             if(fcn.apply(scope || this || window, arguments) === false){
800                 return;
801             }
802             return method.apply(this || window, arguments);
803         };
804     }
805 });
806 /*
807  * Based on:
808  * Ext JS Library 1.1.1
809  * Copyright(c) 2006-2007, Ext JS, LLC.
810  *
811  * Originally Released Under LGPL - original licence link has changed is not relivant.
812  *
813  * Fork - LGPL
814  * <script type="text/javascript">
815  */
816
817 Roo.applyIf(String, {
818     
819     /** @scope String */
820     
821     /**
822      * Escapes the passed string for ' and \
823      * @param {String} string The string to escape
824      * @return {String} The escaped string
825      * @static
826      */
827     escape : function(string) {
828         return string.replace(/('|\\)/g, "\\$1");
829     },
830
831     /**
832      * Pads the left side of a string with a specified character.  This is especially useful
833      * for normalizing number and date strings.  Example usage:
834      * <pre><code>
835 var s = String.leftPad('123', 5, '0');
836 // s now contains the string: '00123'
837 </code></pre>
838      * @param {String} string The original string
839      * @param {Number} size The total length of the output string
840      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
841      * @return {String} The padded string
842      * @static
843      */
844     leftPad : function (val, size, ch) {
845         var result = new String(val);
846         if(ch === null || ch === undefined || ch === '') {
847             ch = " ";
848         }
849         while (result.length < size) {
850             result = ch + result;
851         }
852         return result;
853     },
854
855     /**
856      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
857      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
858      * <pre><code>
859 var cls = 'my-class', text = 'Some text';
860 var s = String.format('<div class="{0}">{1}</div>', cls, text);
861 // s now contains the string: '<div class="my-class">Some text</div>'
862 </code></pre>
863      * @param {String} string The tokenized string to be formatted
864      * @param {String} value1 The value to replace token {0}
865      * @param {String} value2 Etc...
866      * @return {String} The formatted string
867      * @static
868      */
869     format : function(format){
870         var args = Array.prototype.slice.call(arguments, 1);
871         return format.replace(/\{(\d+)\}/g, function(m, i){
872             return Roo.util.Format.htmlEncode(args[i]);
873         });
874     }
875 });
876
877 /**
878  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
879  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
880  * they are already different, the first value passed in is returned.  Note that this method returns the new value
881  * but does not change the current string.
882  * <pre><code>
883 // alternate sort directions
884 sort = sort.toggle('ASC', 'DESC');
885
886 // instead of conditional logic:
887 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
888 </code></pre>
889  * @param {String} value The value to compare to the current string
890  * @param {String} other The new value to use if the string already equals the first value passed in
891  * @return {String} The new value
892  */
893  
894 String.prototype.toggle = function(value, other){
895     return this == value ? other : value;
896 };/*
897  * Based on:
898  * Ext JS Library 1.1.1
899  * Copyright(c) 2006-2007, Ext JS, LLC.
900  *
901  * Originally Released Under LGPL - original licence link has changed is not relivant.
902  *
903  * Fork - LGPL
904  * <script type="text/javascript">
905  */
906
907  /**
908  * @class Number
909  */
910 Roo.applyIf(Number.prototype, {
911     /**
912      * Checks whether or not the current number is within a desired range.  If the number is already within the
913      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
914      * exceeded.  Note that this method returns the constrained value but does not change the current number.
915      * @param {Number} min The minimum number in the range
916      * @param {Number} max The maximum number in the range
917      * @return {Number} The constrained value if outside the range, otherwise the current value
918      */
919     constrain : function(min, max){
920         return Math.min(Math.max(this, min), max);
921     }
922 });/*
923  * Based on:
924  * Ext JS Library 1.1.1
925  * Copyright(c) 2006-2007, Ext JS, LLC.
926  *
927  * Originally Released Under LGPL - original licence link has changed is not relivant.
928  *
929  * Fork - LGPL
930  * <script type="text/javascript">
931  */
932  /**
933  * @class Array
934  */
935 Roo.applyIf(Array.prototype, {
936     /**
937      * 
938      * Checks whether or not the specified object exists in the array.
939      * @param {Object} o The object to check for
940      * @return {Number} The index of o in the array (or -1 if it is not found)
941      */
942     indexOf : function(o){
943        for (var i = 0, len = this.length; i < len; i++){
944               if(this[i] == o) { return i; }
945        }
946            return -1;
947     },
948
949     /**
950      * Removes the specified object from the array.  If the object is not found nothing happens.
951      * @param {Object} o The object to remove
952      */
953     remove : function(o){
954        var index = this.indexOf(o);
955        if(index != -1){
956            this.splice(index, 1);
957        }
958     },
959     /**
960      * Map (JS 1.6 compatibility)
961      * @param {Function} function  to call
962      */
963     map : function(fun )
964     {
965         var len = this.length >>> 0;
966         if (typeof fun != "function") {
967             throw new TypeError();
968         }
969         var res = new Array(len);
970         var thisp = arguments[1];
971         for (var i = 0; i < len; i++)
972         {
973             if (i in this) {
974                 res[i] = fun.call(thisp, this[i], i, this);
975             }
976         }
977
978         return res;
979     }
980     
981 });
982
983
984  
985 /*
986  * Based on:
987  * Ext JS Library 1.1.1
988  * Copyright(c) 2006-2007, Ext JS, LLC.
989  *
990  * Originally Released Under LGPL - original licence link has changed is not relivant.
991  *
992  * Fork - LGPL
993  * <script type="text/javascript">
994  */
995
996 /**
997  * @class Date
998  *
999  * The date parsing and format syntax is a subset of
1000  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1001  * supported will provide results equivalent to their PHP versions.
1002  *
1003  * Following is the list of all currently supported formats:
1004  *<pre>
1005 Sample date:
1006 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1007
1008 Format  Output      Description
1009 ------  ----------  --------------------------------------------------------------
1010   d      10         Day of the month, 2 digits with leading zeros
1011   D      Wed        A textual representation of a day, three letters
1012   j      10         Day of the month without leading zeros
1013   l      Wednesday  A full textual representation of the day of the week
1014   S      th         English ordinal day of month suffix, 2 chars (use with j)
1015   w      3          Numeric representation of the day of the week
1016   z      9          The julian date, or day of the year (0-365)
1017   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1018   F      January    A full textual representation of the month
1019   m      01         Numeric representation of a month, with leading zeros
1020   M      Jan        Month name abbreviation, three letters
1021   n      1          Numeric representation of a month, without leading zeros
1022   t      31         Number of days in the given month
1023   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1024   Y      2007       A full numeric representation of a year, 4 digits
1025   y      07         A two digit representation of a year
1026   a      pm         Lowercase Ante meridiem and Post meridiem
1027   A      PM         Uppercase Ante meridiem and Post meridiem
1028   g      3          12-hour format of an hour without leading zeros
1029   G      15         24-hour format of an hour without leading zeros
1030   h      03         12-hour format of an hour with leading zeros
1031   H      15         24-hour format of an hour with leading zeros
1032   i      05         Minutes with leading zeros
1033   s      01         Seconds, with leading zeros
1034   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1035   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1036   T      CST        Timezone setting of the machine running the code
1037   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1038 </pre>
1039  *
1040  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1041  * <pre><code>
1042 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1043 document.write(dt.format('Y-m-d'));                         //2007-01-10
1044 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1045 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1046  </code></pre>
1047  *
1048  * Here are some standard date/time patterns that you might find helpful.  They
1049  * are not part of the source of Date.js, but to use them you can simply copy this
1050  * block of code into any script that is included after Date.js and they will also become
1051  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1052  * <pre><code>
1053 Date.patterns = {
1054     ISO8601Long:"Y-m-d H:i:s",
1055     ISO8601Short:"Y-m-d",
1056     ShortDate: "n/j/Y",
1057     LongDate: "l, F d, Y",
1058     FullDateTime: "l, F d, Y g:i:s A",
1059     MonthDay: "F d",
1060     ShortTime: "g:i A",
1061     LongTime: "g:i:s A",
1062     SortableDateTime: "Y-m-d\\TH:i:s",
1063     UniversalSortableDateTime: "Y-m-d H:i:sO",
1064     YearMonth: "F, Y"
1065 };
1066 </code></pre>
1067  *
1068  * Example usage:
1069  * <pre><code>
1070 var dt = new Date();
1071 document.write(dt.format(Date.patterns.ShortDate));
1072  </code></pre>
1073  */
1074
1075 /*
1076  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1077  * They generate precompiled functions from date formats instead of parsing and
1078  * processing the pattern every time you format a date.  These functions are available
1079  * on every Date object (any javascript function).
1080  *
1081  * The original article and download are here:
1082  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1083  *
1084  */
1085  
1086  
1087  // was in core
1088 /**
1089  Returns the number of milliseconds between this date and date
1090  @param {Date} date (optional) Defaults to now
1091  @return {Number} The diff in milliseconds
1092  @member Date getElapsed
1093  */
1094 Date.prototype.getElapsed = function(date) {
1095         return Math.abs((date || new Date()).getTime()-this.getTime());
1096 };
1097 // was in date file..
1098
1099
1100 // private
1101 Date.parseFunctions = {count:0};
1102 // private
1103 Date.parseRegexes = [];
1104 // private
1105 Date.formatFunctions = {count:0};
1106
1107 // private
1108 Date.prototype.dateFormat = function(format) {
1109     if (Date.formatFunctions[format] == null) {
1110         Date.createNewFormat(format);
1111     }
1112     var func = Date.formatFunctions[format];
1113     return this[func]();
1114 };
1115
1116
1117 /**
1118  * Formats a date given the supplied format string
1119  * @param {String} format The format string
1120  * @return {String} The formatted date
1121  * @method
1122  */
1123 Date.prototype.format = Date.prototype.dateFormat;
1124
1125 // private
1126 Date.createNewFormat = function(format) {
1127     var funcName = "format" + Date.formatFunctions.count++;
1128     Date.formatFunctions[format] = funcName;
1129     var code = "Date.prototype." + funcName + " = function(){return ";
1130     var special = false;
1131     var ch = '';
1132     for (var i = 0; i < format.length; ++i) {
1133         ch = format.charAt(i);
1134         if (!special && ch == "\\") {
1135             special = true;
1136         }
1137         else if (special) {
1138             special = false;
1139             code += "'" + String.escape(ch) + "' + ";
1140         }
1141         else {
1142             code += Date.getFormatCode(ch);
1143         }
1144     }
1145     /** eval:var:zzzzzzzzzzzzz */
1146     eval(code.substring(0, code.length - 3) + ";}");
1147 };
1148
1149 // private
1150 Date.getFormatCode = function(character) {
1151     switch (character) {
1152     case "d":
1153         return "String.leftPad(this.getDate(), 2, '0') + ";
1154     case "D":
1155         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1156     case "j":
1157         return "this.getDate() + ";
1158     case "l":
1159         return "Date.dayNames[this.getDay()] + ";
1160     case "S":
1161         return "this.getSuffix() + ";
1162     case "w":
1163         return "this.getDay() + ";
1164     case "z":
1165         return "this.getDayOfYear() + ";
1166     case "W":
1167         return "this.getWeekOfYear() + ";
1168     case "F":
1169         return "Date.monthNames[this.getMonth()] + ";
1170     case "m":
1171         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1172     case "M":
1173         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1174     case "n":
1175         return "(this.getMonth() + 1) + ";
1176     case "t":
1177         return "this.getDaysInMonth() + ";
1178     case "L":
1179         return "(this.isLeapYear() ? 1 : 0) + ";
1180     case "Y":
1181         return "this.getFullYear() + ";
1182     case "y":
1183         return "('' + this.getFullYear()).substring(2, 4) + ";
1184     case "a":
1185         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1186     case "A":
1187         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1188     case "g":
1189         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1190     case "G":
1191         return "this.getHours() + ";
1192     case "h":
1193         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1194     case "H":
1195         return "String.leftPad(this.getHours(), 2, '0') + ";
1196     case "i":
1197         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1198     case "s":
1199         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1200     case "O":
1201         return "this.getGMTOffset() + ";
1202     case "P":
1203         return "this.getGMTColonOffset() + ";
1204     case "T":
1205         return "this.getTimezone() + ";
1206     case "Z":
1207         return "(this.getTimezoneOffset() * -60) + ";
1208     default:
1209         return "'" + String.escape(character) + "' + ";
1210     }
1211 };
1212
1213 /**
1214  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1215  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1216  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1217  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1218  * string or the parse operation will fail.
1219  * Example Usage:
1220 <pre><code>
1221 //dt = Fri May 25 2007 (current date)
1222 var dt = new Date();
1223
1224 //dt = Thu May 25 2006 (today's month/day in 2006)
1225 dt = Date.parseDate("2006", "Y");
1226
1227 //dt = Sun Jan 15 2006 (all date parts specified)
1228 dt = Date.parseDate("2006-1-15", "Y-m-d");
1229
1230 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1231 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1232 </code></pre>
1233  * @param {String} input The unparsed date as a string
1234  * @param {String} format The format the date is in
1235  * @return {Date} The parsed date
1236  * @static
1237  */
1238 Date.parseDate = function(input, format) {
1239     if (Date.parseFunctions[format] == null) {
1240         Date.createParser(format);
1241     }
1242     var func = Date.parseFunctions[format];
1243     return Date[func](input);
1244 };
1245 /**
1246  * @private
1247  */
1248
1249 Date.createParser = function(format) {
1250     var funcName = "parse" + Date.parseFunctions.count++;
1251     var regexNum = Date.parseRegexes.length;
1252     var currentGroup = 1;
1253     Date.parseFunctions[format] = funcName;
1254
1255     var code = "Date." + funcName + " = function(input){\n"
1256         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1257         + "var d = new Date();\n"
1258         + "y = d.getFullYear();\n"
1259         + "m = d.getMonth();\n"
1260         + "d = d.getDate();\n"
1261         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1262         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1263         + "if (results && results.length > 0) {";
1264     var regex = "";
1265
1266     var special = false;
1267     var ch = '';
1268     for (var i = 0; i < format.length; ++i) {
1269         ch = format.charAt(i);
1270         if (!special && ch == "\\") {
1271             special = true;
1272         }
1273         else if (special) {
1274             special = false;
1275             regex += String.escape(ch);
1276         }
1277         else {
1278             var obj = Date.formatCodeToRegex(ch, currentGroup);
1279             currentGroup += obj.g;
1280             regex += obj.s;
1281             if (obj.g && obj.c) {
1282                 code += obj.c;
1283             }
1284         }
1285     }
1286
1287     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1288         + "{v = new Date(y, m, d, h, i, s);}\n"
1289         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1290         + "{v = new Date(y, m, d, h, i);}\n"
1291         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1292         + "{v = new Date(y, m, d, h);}\n"
1293         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1294         + "{v = new Date(y, m, d);}\n"
1295         + "else if (y >= 0 && m >= 0)\n"
1296         + "{v = new Date(y, m);}\n"
1297         + "else if (y >= 0)\n"
1298         + "{v = new Date(y);}\n"
1299         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1300         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1301         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1302         + ";}";
1303
1304     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1305     /** eval:var:zzzzzzzzzzzzz */
1306     eval(code);
1307 };
1308
1309 // private
1310 Date.formatCodeToRegex = function(character, currentGroup) {
1311     switch (character) {
1312     case "D":
1313         return {g:0,
1314         c:null,
1315         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1316     case "j":
1317         return {g:1,
1318             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1319             s:"(\\d{1,2})"}; // day of month without leading zeroes
1320     case "d":
1321         return {g:1,
1322             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1323             s:"(\\d{2})"}; // day of month with leading zeroes
1324     case "l":
1325         return {g:0,
1326             c:null,
1327             s:"(?:" + Date.dayNames.join("|") + ")"};
1328     case "S":
1329         return {g:0,
1330             c:null,
1331             s:"(?:st|nd|rd|th)"};
1332     case "w":
1333         return {g:0,
1334             c:null,
1335             s:"\\d"};
1336     case "z":
1337         return {g:0,
1338             c:null,
1339             s:"(?:\\d{1,3})"};
1340     case "W":
1341         return {g:0,
1342             c:null,
1343             s:"(?:\\d{2})"};
1344     case "F":
1345         return {g:1,
1346             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1347             s:"(" + Date.monthNames.join("|") + ")"};
1348     case "M":
1349         return {g:1,
1350             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1351             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1352     case "n":
1353         return {g:1,
1354             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1355             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1356     case "m":
1357         return {g:1,
1358             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1359             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1360     case "t":
1361         return {g:0,
1362             c:null,
1363             s:"\\d{1,2}"};
1364     case "L":
1365         return {g:0,
1366             c:null,
1367             s:"(?:1|0)"};
1368     case "Y":
1369         return {g:1,
1370             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{4})"};
1372     case "y":
1373         return {g:1,
1374             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1375                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1376             s:"(\\d{1,2})"};
1377     case "a":
1378         return {g:1,
1379             c:"if (results[" + currentGroup + "] == 'am') {\n"
1380                 + "if (h == 12) { h = 0; }\n"
1381                 + "} else { if (h < 12) { h += 12; }}",
1382             s:"(am|pm)"};
1383     case "A":
1384         return {g:1,
1385             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1386                 + "if (h == 12) { h = 0; }\n"
1387                 + "} else { if (h < 12) { h += 12; }}",
1388             s:"(AM|PM)"};
1389     case "g":
1390     case "G":
1391         return {g:1,
1392             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1393             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1394     case "h":
1395     case "H":
1396         return {g:1,
1397             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1398             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1399     case "i":
1400         return {g:1,
1401             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1402             s:"(\\d{2})"};
1403     case "s":
1404         return {g:1,
1405             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1406             s:"(\\d{2})"};
1407     case "O":
1408         return {g:1,
1409             c:[
1410                 "o = results[", currentGroup, "];\n",
1411                 "var sn = o.substring(0,1);\n", // get + / - sign
1412                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1413                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1414                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1415                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1416             ].join(""),
1417             s:"([+\-]\\d{2,4})"};
1418     
1419     
1420     case "P":
1421         return {g:1,
1422                 c:[
1423                    "o = results[", currentGroup, "];\n",
1424                    "var sn = o.substring(0,1);\n",
1425                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1426                    "var mn = o.substring(4,6) % 60;\n",
1427                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1428                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1429             ].join(""),
1430             s:"([+\-]\\d{4})"};
1431     case "T":
1432         return {g:0,
1433             c:null,
1434             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1435     case "Z":
1436         return {g:1,
1437             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1438                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1439             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1440     default:
1441         return {g:0,
1442             c:null,
1443             s:String.escape(character)};
1444     }
1445 };
1446
1447 /**
1448  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1449  * @return {String} The abbreviated timezone name (e.g. 'CST')
1450  */
1451 Date.prototype.getTimezone = function() {
1452     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1453 };
1454
1455 /**
1456  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1457  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1458  */
1459 Date.prototype.getGMTOffset = function() {
1460     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1461         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1462         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1463 };
1464
1465 /**
1466  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1467  * @return {String} 2-characters representing hours and 2-characters representing minutes
1468  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1469  */
1470 Date.prototype.getGMTColonOffset = function() {
1471         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1472                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1473                 + ":"
1474                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1475 }
1476
1477 /**
1478  * Get the numeric day number of the year, adjusted for leap year.
1479  * @return {Number} 0 through 364 (365 in leap years)
1480  */
1481 Date.prototype.getDayOfYear = function() {
1482     var num = 0;
1483     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1484     for (var i = 0; i < this.getMonth(); ++i) {
1485         num += Date.daysInMonth[i];
1486     }
1487     return num + this.getDate() - 1;
1488 };
1489
1490 /**
1491  * Get the string representation of the numeric week number of the year
1492  * (equivalent to the format specifier 'W').
1493  * @return {String} '00' through '52'
1494  */
1495 Date.prototype.getWeekOfYear = function() {
1496     // Skip to Thursday of this week
1497     var now = this.getDayOfYear() + (4 - this.getDay());
1498     // Find the first Thursday of the year
1499     var jan1 = new Date(this.getFullYear(), 0, 1);
1500     var then = (7 - jan1.getDay() + 4);
1501     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1502 };
1503
1504 /**
1505  * Whether or not the current date is in a leap year.
1506  * @return {Boolean} True if the current date is in a leap year, else false
1507  */
1508 Date.prototype.isLeapYear = function() {
1509     var year = this.getFullYear();
1510     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1511 };
1512
1513 /**
1514  * Get the first day of the current month, adjusted for leap year.  The returned value
1515  * is the numeric day index within the week (0-6) which can be used in conjunction with
1516  * the {@link #monthNames} array to retrieve the textual day name.
1517  * Example:
1518  *<pre><code>
1519 var dt = new Date('1/10/2007');
1520 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1521 </code></pre>
1522  * @return {Number} The day number (0-6)
1523  */
1524 Date.prototype.getFirstDayOfMonth = function() {
1525     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1526     return (day < 0) ? (day + 7) : day;
1527 };
1528
1529 /**
1530  * Get the last day of the current month, adjusted for leap year.  The returned value
1531  * is the numeric day index within the week (0-6) which can be used in conjunction with
1532  * the {@link #monthNames} array to retrieve the textual day name.
1533  * Example:
1534  *<pre><code>
1535 var dt = new Date('1/10/2007');
1536 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1537 </code></pre>
1538  * @return {Number} The day number (0-6)
1539  */
1540 Date.prototype.getLastDayOfMonth = function() {
1541     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1542     return (day < 0) ? (day + 7) : day;
1543 };
1544
1545
1546 /**
1547  * Get the first date of this date's month
1548  * @return {Date}
1549  */
1550 Date.prototype.getFirstDateOfMonth = function() {
1551     return new Date(this.getFullYear(), this.getMonth(), 1);
1552 };
1553
1554 /**
1555  * Get the last date of this date's month
1556  * @return {Date}
1557  */
1558 Date.prototype.getLastDateOfMonth = function() {
1559     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1560 };
1561 /**
1562  * Get the number of days in the current month, adjusted for leap year.
1563  * @return {Number} The number of days in the month
1564  */
1565 Date.prototype.getDaysInMonth = function() {
1566     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1567     return Date.daysInMonth[this.getMonth()];
1568 };
1569
1570 /**
1571  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1572  * @return {String} 'st, 'nd', 'rd' or 'th'
1573  */
1574 Date.prototype.getSuffix = function() {
1575     switch (this.getDate()) {
1576         case 1:
1577         case 21:
1578         case 31:
1579             return "st";
1580         case 2:
1581         case 22:
1582             return "nd";
1583         case 3:
1584         case 23:
1585             return "rd";
1586         default:
1587             return "th";
1588     }
1589 };
1590
1591 // private
1592 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1593
1594 /**
1595  * An array of textual month names.
1596  * Override these values for international dates, for example...
1597  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1598  * @type Array
1599  * @static
1600  */
1601 Date.monthNames =
1602    ["January",
1603     "February",
1604     "March",
1605     "April",
1606     "May",
1607     "June",
1608     "July",
1609     "August",
1610     "September",
1611     "October",
1612     "November",
1613     "December"];
1614
1615 /**
1616  * An array of textual day names.
1617  * Override these values for international dates, for example...
1618  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1619  * @type Array
1620  * @static
1621  */
1622 Date.dayNames =
1623    ["Sunday",
1624     "Monday",
1625     "Tuesday",
1626     "Wednesday",
1627     "Thursday",
1628     "Friday",
1629     "Saturday"];
1630
1631 // private
1632 Date.y2kYear = 50;
1633 // private
1634 Date.monthNumbers = {
1635     Jan:0,
1636     Feb:1,
1637     Mar:2,
1638     Apr:3,
1639     May:4,
1640     Jun:5,
1641     Jul:6,
1642     Aug:7,
1643     Sep:8,
1644     Oct:9,
1645     Nov:10,
1646     Dec:11};
1647
1648 /**
1649  * Creates and returns a new Date instance with the exact same date value as the called instance.
1650  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1651  * variable will also be changed.  When the intention is to create a new variable that will not
1652  * modify the original instance, you should create a clone.
1653  *
1654  * Example of correctly cloning a date:
1655  * <pre><code>
1656 //wrong way:
1657 var orig = new Date('10/1/2006');
1658 var copy = orig;
1659 copy.setDate(5);
1660 document.write(orig);  //returns 'Thu Oct 05 2006'!
1661
1662 //correct way:
1663 var orig = new Date('10/1/2006');
1664 var copy = orig.clone();
1665 copy.setDate(5);
1666 document.write(orig);  //returns 'Thu Oct 01 2006'
1667 </code></pre>
1668  * @return {Date} The new Date instance
1669  */
1670 Date.prototype.clone = function() {
1671         return new Date(this.getTime());
1672 };
1673
1674 /**
1675  * Clears any time information from this date
1676  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1677  @return {Date} this or the clone
1678  */
1679 Date.prototype.clearTime = function(clone){
1680     if(clone){
1681         return this.clone().clearTime();
1682     }
1683     this.setHours(0);
1684     this.setMinutes(0);
1685     this.setSeconds(0);
1686     this.setMilliseconds(0);
1687     return this;
1688 };
1689
1690 // private
1691 // safari setMonth is broken
1692 if(Roo.isSafari){
1693     Date.brokenSetMonth = Date.prototype.setMonth;
1694         Date.prototype.setMonth = function(num){
1695                 if(num <= -1){
1696                         var n = Math.ceil(-num);
1697                         var back_year = Math.ceil(n/12);
1698                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1699                         this.setFullYear(this.getFullYear() - back_year);
1700                         return Date.brokenSetMonth.call(this, month);
1701                 } else {
1702                         return Date.brokenSetMonth.apply(this, arguments);
1703                 }
1704         };
1705 }
1706
1707 /** Date interval constant 
1708 * @static 
1709 * @type String */
1710 Date.MILLI = "ms";
1711 /** Date interval constant 
1712 * @static 
1713 * @type String */
1714 Date.SECOND = "s";
1715 /** Date interval constant 
1716 * @static 
1717 * @type String */
1718 Date.MINUTE = "mi";
1719 /** Date interval constant 
1720 * @static 
1721 * @type String */
1722 Date.HOUR = "h";
1723 /** Date interval constant 
1724 * @static 
1725 * @type String */
1726 Date.DAY = "d";
1727 /** Date interval constant 
1728 * @static 
1729 * @type String */
1730 Date.MONTH = "mo";
1731 /** Date interval constant 
1732 * @static 
1733 * @type String */
1734 Date.YEAR = "y";
1735
1736 /**
1737  * Provides a convenient method of performing basic date arithmetic.  This method
1738  * does not modify the Date instance being called - it creates and returns
1739  * a new Date instance containing the resulting date value.
1740  *
1741  * Examples:
1742  * <pre><code>
1743 //Basic usage:
1744 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1745 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1746
1747 //Negative values will subtract correctly:
1748 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1749 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1750
1751 //You can even chain several calls together in one line!
1752 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1753 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1754  </code></pre>
1755  *
1756  * @param {String} interval   A valid date interval enum value
1757  * @param {Number} value      The amount to add to the current date
1758  * @return {Date} The new Date instance
1759  */
1760 Date.prototype.add = function(interval, value){
1761   var d = this.clone();
1762   if (!interval || value === 0) { return d; }
1763   switch(interval.toLowerCase()){
1764     case Date.MILLI:
1765       d.setMilliseconds(this.getMilliseconds() + value);
1766       break;
1767     case Date.SECOND:
1768       d.setSeconds(this.getSeconds() + value);
1769       break;
1770     case Date.MINUTE:
1771       d.setMinutes(this.getMinutes() + value);
1772       break;
1773     case Date.HOUR:
1774       d.setHours(this.getHours() + value);
1775       break;
1776     case Date.DAY:
1777       d.setDate(this.getDate() + value);
1778       break;
1779     case Date.MONTH:
1780       var day = this.getDate();
1781       if(day > 28){
1782           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1783       }
1784       d.setDate(day);
1785       d.setMonth(this.getMonth() + value);
1786       break;
1787     case Date.YEAR:
1788       d.setFullYear(this.getFullYear() + value);
1789       break;
1790   }
1791   return d;
1792 };
1793 /*
1794  * Based on:
1795  * Ext JS Library 1.1.1
1796  * Copyright(c) 2006-2007, Ext JS, LLC.
1797  *
1798  * Originally Released Under LGPL - original licence link has changed is not relivant.
1799  *
1800  * Fork - LGPL
1801  * <script type="text/javascript">
1802  */
1803
1804 /**
1805  * @class Roo.lib.Dom
1806  * @static
1807  * 
1808  * Dom utils (from YIU afaik)
1809  * 
1810  **/
1811 Roo.lib.Dom = {
1812     /**
1813      * Get the view width
1814      * @param {Boolean} full True will get the full document, otherwise it's the view width
1815      * @return {Number} The width
1816      */
1817      
1818     getViewWidth : function(full) {
1819         return full ? this.getDocumentWidth() : this.getViewportWidth();
1820     },
1821     /**
1822      * Get the view height
1823      * @param {Boolean} full True will get the full document, otherwise it's the view height
1824      * @return {Number} The height
1825      */
1826     getViewHeight : function(full) {
1827         return full ? this.getDocumentHeight() : this.getViewportHeight();
1828     },
1829
1830     getDocumentHeight: function() {
1831         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1832         return Math.max(scrollHeight, this.getViewportHeight());
1833     },
1834
1835     getDocumentWidth: function() {
1836         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1837         return Math.max(scrollWidth, this.getViewportWidth());
1838     },
1839
1840     getViewportHeight: function() {
1841         var height = self.innerHeight;
1842         var mode = document.compatMode;
1843
1844         if ((mode || Roo.isIE) && !Roo.isOpera) {
1845             height = (mode == "CSS1Compat") ?
1846                      document.documentElement.clientHeight :
1847                      document.body.clientHeight;
1848         }
1849
1850         return height;
1851     },
1852
1853     getViewportWidth: function() {
1854         var width = self.innerWidth;
1855         var mode = document.compatMode;
1856
1857         if (mode || Roo.isIE) {
1858             width = (mode == "CSS1Compat") ?
1859                     document.documentElement.clientWidth :
1860                     document.body.clientWidth;
1861         }
1862         return width;
1863     },
1864
1865     isAncestor : function(p, c) {
1866         p = Roo.getDom(p);
1867         c = Roo.getDom(c);
1868         if (!p || !c) {
1869             return false;
1870         }
1871
1872         if (p.contains && !Roo.isSafari) {
1873             return p.contains(c);
1874         } else if (p.compareDocumentPosition) {
1875             return !!(p.compareDocumentPosition(c) & 16);
1876         } else {
1877             var parent = c.parentNode;
1878             while (parent) {
1879                 if (parent == p) {
1880                     return true;
1881                 }
1882                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1883                     return false;
1884                 }
1885                 parent = parent.parentNode;
1886             }
1887             return false;
1888         }
1889     },
1890
1891     getRegion : function(el) {
1892         return Roo.lib.Region.getRegion(el);
1893     },
1894
1895     getY : function(el) {
1896         return this.getXY(el)[1];
1897     },
1898
1899     getX : function(el) {
1900         return this.getXY(el)[0];
1901     },
1902
1903     getXY : function(el) {
1904         var p, pe, b, scroll, bd = document.body;
1905         el = Roo.getDom(el);
1906         var fly = Roo.lib.AnimBase.fly;
1907         if (el.getBoundingClientRect) {
1908             b = el.getBoundingClientRect();
1909             scroll = fly(document).getScroll();
1910             return [b.left + scroll.left, b.top + scroll.top];
1911         }
1912         var x = 0, y = 0;
1913
1914         p = el;
1915
1916         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1917
1918         while (p) {
1919
1920             x += p.offsetLeft;
1921             y += p.offsetTop;
1922
1923             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1924                 hasAbsolute = true;
1925             }
1926
1927             if (Roo.isGecko) {
1928                 pe = fly(p);
1929
1930                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1931                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1932
1933
1934                 x += bl;
1935                 y += bt;
1936
1937
1938                 if (p != el && pe.getStyle('overflow') != 'visible') {
1939                     x += bl;
1940                     y += bt;
1941                 }
1942             }
1943             p = p.offsetParent;
1944         }
1945
1946         if (Roo.isSafari && hasAbsolute) {
1947             x -= bd.offsetLeft;
1948             y -= bd.offsetTop;
1949         }
1950
1951         if (Roo.isGecko && !hasAbsolute) {
1952             var dbd = fly(bd);
1953             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1954             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1955         }
1956
1957         p = el.parentNode;
1958         while (p && p != bd) {
1959             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1960                 x -= p.scrollLeft;
1961                 y -= p.scrollTop;
1962             }
1963             p = p.parentNode;
1964         }
1965         return [x, y];
1966     },
1967  
1968   
1969
1970
1971     setXY : function(el, xy) {
1972         el = Roo.fly(el, '_setXY');
1973         el.position();
1974         var pts = el.translatePoints(xy);
1975         if (xy[0] !== false) {
1976             el.dom.style.left = pts.left + "px";
1977         }
1978         if (xy[1] !== false) {
1979             el.dom.style.top = pts.top + "px";
1980         }
1981     },
1982
1983     setX : function(el, x) {
1984         this.setXY(el, [x, false]);
1985     },
1986
1987     setY : function(el, y) {
1988         this.setXY(el, [false, y]);
1989     }
1990 };
1991 /*
1992  * Portions of this file are based on pieces of Yahoo User Interface Library
1993  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1994  * YUI licensed under the BSD License:
1995  * http://developer.yahoo.net/yui/license.txt
1996  * <script type="text/javascript">
1997  *
1998  */
1999
2000 Roo.lib.Event = function() {
2001     var loadComplete = false;
2002     var listeners = [];
2003     var unloadListeners = [];
2004     var retryCount = 0;
2005     var onAvailStack = [];
2006     var counter = 0;
2007     var lastError = null;
2008
2009     return {
2010         POLL_RETRYS: 200,
2011         POLL_INTERVAL: 20,
2012         EL: 0,
2013         TYPE: 1,
2014         FN: 2,
2015         WFN: 3,
2016         OBJ: 3,
2017         ADJ_SCOPE: 4,
2018         _interval: null,
2019
2020         startInterval: function() {
2021             if (!this._interval) {
2022                 var self = this;
2023                 var callback = function() {
2024                     self._tryPreloadAttach();
2025                 };
2026                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2027
2028             }
2029         },
2030
2031         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2032             onAvailStack.push({ id:         p_id,
2033                 fn:         p_fn,
2034                 obj:        p_obj,
2035                 override:   p_override,
2036                 checkReady: false    });
2037
2038             retryCount = this.POLL_RETRYS;
2039             this.startInterval();
2040         },
2041
2042
2043         addListener: function(el, eventName, fn) {
2044             el = Roo.getDom(el);
2045             if (!el || !fn) {
2046                 return false;
2047             }
2048
2049             if ("unload" == eventName) {
2050                 unloadListeners[unloadListeners.length] =
2051                 [el, eventName, fn];
2052                 return true;
2053             }
2054
2055             var wrappedFn = function(e) {
2056                 return fn(Roo.lib.Event.getEvent(e));
2057             };
2058
2059             var li = [el, eventName, fn, wrappedFn];
2060
2061             var index = listeners.length;
2062             listeners[index] = li;
2063
2064             this.doAdd(el, eventName, wrappedFn, false);
2065             return true;
2066
2067         },
2068
2069
2070         removeListener: function(el, eventName, fn) {
2071             var i, len;
2072
2073             el = Roo.getDom(el);
2074
2075             if(!fn) {
2076                 return this.purgeElement(el, false, eventName);
2077             }
2078
2079
2080             if ("unload" == eventName) {
2081
2082                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2083                     var li = unloadListeners[i];
2084                     if (li &&
2085                         li[0] == el &&
2086                         li[1] == eventName &&
2087                         li[2] == fn) {
2088                         unloadListeners.splice(i, 1);
2089                         return true;
2090                     }
2091                 }
2092
2093                 return false;
2094             }
2095
2096             var cacheItem = null;
2097
2098
2099             var index = arguments[3];
2100
2101             if ("undefined" == typeof index) {
2102                 index = this._getCacheIndex(el, eventName, fn);
2103             }
2104
2105             if (index >= 0) {
2106                 cacheItem = listeners[index];
2107             }
2108
2109             if (!el || !cacheItem) {
2110                 return false;
2111             }
2112
2113             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2114
2115             delete listeners[index][this.WFN];
2116             delete listeners[index][this.FN];
2117             listeners.splice(index, 1);
2118
2119             return true;
2120
2121         },
2122
2123
2124         getTarget: function(ev, resolveTextNode) {
2125             ev = ev.browserEvent || ev;
2126             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2127             var t = ev.target || ev.srcElement;
2128             return this.resolveTextNode(t);
2129         },
2130
2131
2132         resolveTextNode: function(node) {
2133             if (Roo.isSafari && node && 3 == node.nodeType) {
2134                 return node.parentNode;
2135             } else {
2136                 return node;
2137             }
2138         },
2139
2140
2141         getPageX: function(ev) {
2142             ev = ev.browserEvent || ev;
2143             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2144             var x = ev.pageX;
2145             if (!x && 0 !== x) {
2146                 x = ev.clientX || 0;
2147
2148                 if (Roo.isIE) {
2149                     x += this.getScroll()[1];
2150                 }
2151             }
2152
2153             return x;
2154         },
2155
2156
2157         getPageY: function(ev) {
2158             ev = ev.browserEvent || ev;
2159             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2160             var y = ev.pageY;
2161             if (!y && 0 !== y) {
2162                 y = ev.clientY || 0;
2163
2164                 if (Roo.isIE) {
2165                     y += this.getScroll()[0];
2166                 }
2167             }
2168
2169
2170             return y;
2171         },
2172
2173
2174         getXY: function(ev) {
2175             ev = ev.browserEvent || ev;
2176             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2177             return [this.getPageX(ev), this.getPageY(ev)];
2178         },
2179
2180
2181         getRelatedTarget: function(ev) {
2182             ev = ev.browserEvent || ev;
2183             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2184             var t = ev.relatedTarget;
2185             if (!t) {
2186                 if (ev.type == "mouseout") {
2187                     t = ev.toElement;
2188                 } else if (ev.type == "mouseover") {
2189                     t = ev.fromElement;
2190                 }
2191             }
2192
2193             return this.resolveTextNode(t);
2194         },
2195
2196
2197         getTime: function(ev) {
2198             ev = ev.browserEvent || ev;
2199             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2200             if (!ev.time) {
2201                 var t = new Date().getTime();
2202                 try {
2203                     ev.time = t;
2204                 } catch(ex) {
2205                     this.lastError = ex;
2206                     return t;
2207                 }
2208             }
2209
2210             return ev.time;
2211         },
2212
2213
2214         stopEvent: function(ev) {
2215             this.stopPropagation(ev);
2216             this.preventDefault(ev);
2217         },
2218
2219
2220         stopPropagation: function(ev) {
2221             ev = ev.browserEvent || ev;
2222             if (ev.stopPropagation) {
2223                 ev.stopPropagation();
2224             } else {
2225                 ev.cancelBubble = true;
2226             }
2227         },
2228
2229
2230         preventDefault: function(ev) {
2231             ev = ev.browserEvent || ev;
2232             if(ev.preventDefault) {
2233                 ev.preventDefault();
2234             } else {
2235                 ev.returnValue = false;
2236             }
2237         },
2238
2239
2240         getEvent: function(e) {
2241             var ev = e || window.event;
2242             if (!ev) {
2243                 var c = this.getEvent.caller;
2244                 while (c) {
2245                     ev = c.arguments[0];
2246                     if (ev && Event == ev.constructor) {
2247                         break;
2248                     }
2249                     c = c.caller;
2250                 }
2251             }
2252             return ev;
2253         },
2254
2255
2256         getCharCode: function(ev) {
2257             ev = ev.browserEvent || ev;
2258             return ev.charCode || ev.keyCode || 0;
2259         },
2260
2261
2262         _getCacheIndex: function(el, eventName, fn) {
2263             for (var i = 0,len = listeners.length; i < len; ++i) {
2264                 var li = listeners[i];
2265                 if (li &&
2266                     li[this.FN] == fn &&
2267                     li[this.EL] == el &&
2268                     li[this.TYPE] == eventName) {
2269                     return i;
2270                 }
2271             }
2272
2273             return -1;
2274         },
2275
2276
2277         elCache: {},
2278
2279
2280         getEl: function(id) {
2281             return document.getElementById(id);
2282         },
2283
2284
2285         clearCache: function() {
2286         },
2287
2288
2289         _load: function(e) {
2290             loadComplete = true;
2291             var EU = Roo.lib.Event;
2292
2293
2294             if (Roo.isIE) {
2295                 EU.doRemove(window, "load", EU._load);
2296             }
2297         },
2298
2299
2300         _tryPreloadAttach: function() {
2301
2302             if (this.locked) {
2303                 return false;
2304             }
2305
2306             this.locked = true;
2307
2308
2309             var tryAgain = !loadComplete;
2310             if (!tryAgain) {
2311                 tryAgain = (retryCount > 0);
2312             }
2313
2314
2315             var notAvail = [];
2316             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2317                 var item = onAvailStack[i];
2318                 if (item) {
2319                     var el = this.getEl(item.id);
2320
2321                     if (el) {
2322                         if (!item.checkReady ||
2323                             loadComplete ||
2324                             el.nextSibling ||
2325                             (document && document.body)) {
2326
2327                             var scope = el;
2328                             if (item.override) {
2329                                 if (item.override === true) {
2330                                     scope = item.obj;
2331                                 } else {
2332                                     scope = item.override;
2333                                 }
2334                             }
2335                             item.fn.call(scope, item.obj);
2336                             onAvailStack[i] = null;
2337                         }
2338                     } else {
2339                         notAvail.push(item);
2340                     }
2341                 }
2342             }
2343
2344             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2345
2346             if (tryAgain) {
2347
2348                 this.startInterval();
2349             } else {
2350                 clearInterval(this._interval);
2351                 this._interval = null;
2352             }
2353
2354             this.locked = false;
2355
2356             return true;
2357
2358         },
2359
2360
2361         purgeElement: function(el, recurse, eventName) {
2362             var elListeners = this.getListeners(el, eventName);
2363             if (elListeners) {
2364                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2365                     var l = elListeners[i];
2366                     this.removeListener(el, l.type, l.fn);
2367                 }
2368             }
2369
2370             if (recurse && el && el.childNodes) {
2371                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2372                     this.purgeElement(el.childNodes[i], recurse, eventName);
2373                 }
2374             }
2375         },
2376
2377
2378         getListeners: function(el, eventName) {
2379             var results = [], searchLists;
2380             if (!eventName) {
2381                 searchLists = [listeners, unloadListeners];
2382             } else if (eventName == "unload") {
2383                 searchLists = [unloadListeners];
2384             } else {
2385                 searchLists = [listeners];
2386             }
2387
2388             for (var j = 0; j < searchLists.length; ++j) {
2389                 var searchList = searchLists[j];
2390                 if (searchList && searchList.length > 0) {
2391                     for (var i = 0,len = searchList.length; i < len; ++i) {
2392                         var l = searchList[i];
2393                         if (l && l[this.EL] === el &&
2394                             (!eventName || eventName === l[this.TYPE])) {
2395                             results.push({
2396                                 type:   l[this.TYPE],
2397                                 fn:     l[this.FN],
2398                                 obj:    l[this.OBJ],
2399                                 adjust: l[this.ADJ_SCOPE],
2400                                 index:  i
2401                             });
2402                         }
2403                     }
2404                 }
2405             }
2406
2407             return (results.length) ? results : null;
2408         },
2409
2410
2411         _unload: function(e) {
2412
2413             var EU = Roo.lib.Event, i, j, l, len, index;
2414
2415             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2416                 l = unloadListeners[i];
2417                 if (l) {
2418                     var scope = window;
2419                     if (l[EU.ADJ_SCOPE]) {
2420                         if (l[EU.ADJ_SCOPE] === true) {
2421                             scope = l[EU.OBJ];
2422                         } else {
2423                             scope = l[EU.ADJ_SCOPE];
2424                         }
2425                     }
2426                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2427                     unloadListeners[i] = null;
2428                     l = null;
2429                     scope = null;
2430                 }
2431             }
2432
2433             unloadListeners = null;
2434
2435             if (listeners && listeners.length > 0) {
2436                 j = listeners.length;
2437                 while (j) {
2438                     index = j - 1;
2439                     l = listeners[index];
2440                     if (l) {
2441                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2442                                 l[EU.FN], index);
2443                     }
2444                     j = j - 1;
2445                 }
2446                 l = null;
2447
2448                 EU.clearCache();
2449             }
2450
2451             EU.doRemove(window, "unload", EU._unload);
2452
2453         },
2454
2455
2456         getScroll: function() {
2457             var dd = document.documentElement, db = document.body;
2458             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2459                 return [dd.scrollTop, dd.scrollLeft];
2460             } else if (db) {
2461                 return [db.scrollTop, db.scrollLeft];
2462             } else {
2463                 return [0, 0];
2464             }
2465         },
2466
2467
2468         doAdd: function () {
2469             if (window.addEventListener) {
2470                 return function(el, eventName, fn, capture) {
2471                     el.addEventListener(eventName, fn, (capture));
2472                 };
2473             } else if (window.attachEvent) {
2474                 return function(el, eventName, fn, capture) {
2475                     el.attachEvent("on" + eventName, fn);
2476                 };
2477             } else {
2478                 return function() {
2479                 };
2480             }
2481         }(),
2482
2483
2484         doRemove: function() {
2485             if (window.removeEventListener) {
2486                 return function (el, eventName, fn, capture) {
2487                     el.removeEventListener(eventName, fn, (capture));
2488                 };
2489             } else if (window.detachEvent) {
2490                 return function (el, eventName, fn) {
2491                     el.detachEvent("on" + eventName, fn);
2492                 };
2493             } else {
2494                 return function() {
2495                 };
2496             }
2497         }()
2498     };
2499     
2500 }();
2501 (function() {     
2502    
2503     var E = Roo.lib.Event;
2504     E.on = E.addListener;
2505     E.un = E.removeListener;
2506
2507     if (document && document.body) {
2508         E._load();
2509     } else {
2510         E.doAdd(window, "load", E._load);
2511     }
2512     E.doAdd(window, "unload", E._unload);
2513     E._tryPreloadAttach();
2514 })();
2515
2516 /*
2517  * Portions of this file are based on pieces of Yahoo User Interface Library
2518  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2519  * YUI licensed under the BSD License:
2520  * http://developer.yahoo.net/yui/license.txt
2521  * <script type="text/javascript">
2522  *
2523  */
2524
2525 (function() {
2526     /**
2527      * @class Roo.lib.Ajax
2528      *
2529      */
2530     Roo.lib.Ajax = {
2531         /**
2532          * @static 
2533          */
2534         request : function(method, uri, cb, data, options) {
2535             if(options){
2536                 var hs = options.headers;
2537                 if(hs){
2538                     for(var h in hs){
2539                         if(hs.hasOwnProperty(h)){
2540                             this.initHeader(h, hs[h], false);
2541                         }
2542                     }
2543                 }
2544                 if(options.xmlData){
2545                     this.initHeader('Content-Type', 'text/xml', false);
2546                     method = 'POST';
2547                     data = options.xmlData;
2548                 }
2549             }
2550
2551             return this.asyncRequest(method, uri, cb, data);
2552         },
2553
2554         serializeForm : function(form) {
2555             if(typeof form == 'string') {
2556                 form = (document.getElementById(form) || document.forms[form]);
2557             }
2558
2559             var el, name, val, disabled, data = '', hasSubmit = false;
2560             for (var i = 0; i < form.elements.length; i++) {
2561                 el = form.elements[i];
2562                 disabled = form.elements[i].disabled;
2563                 name = form.elements[i].name;
2564                 val = form.elements[i].value;
2565
2566                 if (!disabled && name){
2567                     switch (el.type)
2568                             {
2569                         case 'select-one':
2570                         case 'select-multiple':
2571                             for (var j = 0; j < el.options.length; j++) {
2572                                 if (el.options[j].selected) {
2573                                     if (Roo.isIE) {
2574                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2575                                     }
2576                                     else {
2577                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2578                                     }
2579                                 }
2580                             }
2581                             break;
2582                         case 'radio':
2583                         case 'checkbox':
2584                             if (el.checked) {
2585                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2586                             }
2587                             break;
2588                         case 'file':
2589
2590                         case undefined:
2591
2592                         case 'reset':
2593
2594                         case 'button':
2595
2596                             break;
2597                         case 'submit':
2598                             if(hasSubmit == false) {
2599                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2600                                 hasSubmit = true;
2601                             }
2602                             break;
2603                         default:
2604                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2605                             break;
2606                     }
2607                 }
2608             }
2609             data = data.substr(0, data.length - 1);
2610             return data;
2611         },
2612
2613         headers:{},
2614
2615         hasHeaders:false,
2616
2617         useDefaultHeader:true,
2618
2619         defaultPostHeader:'application/x-www-form-urlencoded',
2620
2621         useDefaultXhrHeader:true,
2622
2623         defaultXhrHeader:'XMLHttpRequest',
2624
2625         hasDefaultHeaders:true,
2626
2627         defaultHeaders:{},
2628
2629         poll:{},
2630
2631         timeout:{},
2632
2633         pollInterval:50,
2634
2635         transactionId:0,
2636
2637         setProgId:function(id)
2638         {
2639             this.activeX.unshift(id);
2640         },
2641
2642         setDefaultPostHeader:function(b)
2643         {
2644             this.useDefaultHeader = b;
2645         },
2646
2647         setDefaultXhrHeader:function(b)
2648         {
2649             this.useDefaultXhrHeader = b;
2650         },
2651
2652         setPollingInterval:function(i)
2653         {
2654             if (typeof i == 'number' && isFinite(i)) {
2655                 this.pollInterval = i;
2656             }
2657         },
2658
2659         createXhrObject:function(transactionId)
2660         {
2661             var obj,http;
2662             try
2663             {
2664
2665                 http = new XMLHttpRequest();
2666
2667                 obj = { conn:http, tId:transactionId };
2668             }
2669             catch(e)
2670             {
2671                 for (var i = 0; i < this.activeX.length; ++i) {
2672                     try
2673                     {
2674
2675                         http = new ActiveXObject(this.activeX[i]);
2676
2677                         obj = { conn:http, tId:transactionId };
2678                         break;
2679                     }
2680                     catch(e) {
2681                     }
2682                 }
2683             }
2684             finally
2685             {
2686                 return obj;
2687             }
2688         },
2689
2690         getConnectionObject:function()
2691         {
2692             var o;
2693             var tId = this.transactionId;
2694
2695             try
2696             {
2697                 o = this.createXhrObject(tId);
2698                 if (o) {
2699                     this.transactionId++;
2700                 }
2701             }
2702             catch(e) {
2703             }
2704             finally
2705             {
2706                 return o;
2707             }
2708         },
2709
2710         asyncRequest:function(method, uri, callback, postData)
2711         {
2712             var o = this.getConnectionObject();
2713
2714             if (!o) {
2715                 return null;
2716             }
2717             else {
2718                 o.conn.open(method, uri, true);
2719
2720                 if (this.useDefaultXhrHeader) {
2721                     if (!this.defaultHeaders['X-Requested-With']) {
2722                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2723                     }
2724                 }
2725
2726                 if(postData && this.useDefaultHeader){
2727                     this.initHeader('Content-Type', this.defaultPostHeader);
2728                 }
2729
2730                  if (this.hasDefaultHeaders || this.hasHeaders) {
2731                     this.setHeader(o);
2732                 }
2733
2734                 this.handleReadyState(o, callback);
2735                 o.conn.send(postData || null);
2736
2737                 return o;
2738             }
2739         },
2740
2741         handleReadyState:function(o, callback)
2742         {
2743             var oConn = this;
2744
2745             if (callback && callback.timeout) {
2746                 
2747                 this.timeout[o.tId] = window.setTimeout(function() {
2748                     oConn.abort(o, callback, true);
2749                 }, callback.timeout);
2750             }
2751
2752             this.poll[o.tId] = window.setInterval(
2753                     function() {
2754                         if (o.conn && o.conn.readyState == 4) {
2755                             window.clearInterval(oConn.poll[o.tId]);
2756                             delete oConn.poll[o.tId];
2757
2758                             if(callback && callback.timeout) {
2759                                 window.clearTimeout(oConn.timeout[o.tId]);
2760                                 delete oConn.timeout[o.tId];
2761                             }
2762
2763                             oConn.handleTransactionResponse(o, callback);
2764                         }
2765                     }
2766                     , this.pollInterval);
2767         },
2768
2769         handleTransactionResponse:function(o, callback, isAbort)
2770         {
2771
2772             if (!callback) {
2773                 this.releaseObject(o);
2774                 return;
2775             }
2776
2777             var httpStatus, responseObject;
2778
2779             try
2780             {
2781                 if (o.conn.status !== undefined && o.conn.status != 0) {
2782                     httpStatus = o.conn.status;
2783                 }
2784                 else {
2785                     httpStatus = 13030;
2786                 }
2787             }
2788             catch(e) {
2789
2790
2791                 httpStatus = 13030;
2792             }
2793
2794             if (httpStatus >= 200 && httpStatus < 300) {
2795                 responseObject = this.createResponseObject(o, callback.argument);
2796                 if (callback.success) {
2797                     if (!callback.scope) {
2798                         callback.success(responseObject);
2799                     }
2800                     else {
2801
2802
2803                         callback.success.apply(callback.scope, [responseObject]);
2804                     }
2805                 }
2806             }
2807             else {
2808                 switch (httpStatus) {
2809
2810                     case 12002:
2811                     case 12029:
2812                     case 12030:
2813                     case 12031:
2814                     case 12152:
2815                     case 13030:
2816                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2817                         if (callback.failure) {
2818                             if (!callback.scope) {
2819                                 callback.failure(responseObject);
2820                             }
2821                             else {
2822                                 callback.failure.apply(callback.scope, [responseObject]);
2823                             }
2824                         }
2825                         break;
2826                     default:
2827                         responseObject = this.createResponseObject(o, callback.argument);
2828                         if (callback.failure) {
2829                             if (!callback.scope) {
2830                                 callback.failure(responseObject);
2831                             }
2832                             else {
2833                                 callback.failure.apply(callback.scope, [responseObject]);
2834                             }
2835                         }
2836                 }
2837             }
2838
2839             this.releaseObject(o);
2840             responseObject = null;
2841         },
2842
2843         createResponseObject:function(o, callbackArg)
2844         {
2845             var obj = {};
2846             var headerObj = {};
2847
2848             try
2849             {
2850                 var headerStr = o.conn.getAllResponseHeaders();
2851                 var header = headerStr.split('\n');
2852                 for (var i = 0; i < header.length; i++) {
2853                     var delimitPos = header[i].indexOf(':');
2854                     if (delimitPos != -1) {
2855                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2856                     }
2857                 }
2858             }
2859             catch(e) {
2860             }
2861
2862             obj.tId = o.tId;
2863             obj.status = o.conn.status;
2864             obj.statusText = o.conn.statusText;
2865             obj.getResponseHeader = headerObj;
2866             obj.getAllResponseHeaders = headerStr;
2867             obj.responseText = o.conn.responseText;
2868             obj.responseXML = o.conn.responseXML;
2869
2870             if (typeof callbackArg !== undefined) {
2871                 obj.argument = callbackArg;
2872             }
2873
2874             return obj;
2875         },
2876
2877         createExceptionObject:function(tId, callbackArg, isAbort)
2878         {
2879             var COMM_CODE = 0;
2880             var COMM_ERROR = 'communication failure';
2881             var ABORT_CODE = -1;
2882             var ABORT_ERROR = 'transaction aborted';
2883
2884             var obj = {};
2885
2886             obj.tId = tId;
2887             if (isAbort) {
2888                 obj.status = ABORT_CODE;
2889                 obj.statusText = ABORT_ERROR;
2890             }
2891             else {
2892                 obj.status = COMM_CODE;
2893                 obj.statusText = COMM_ERROR;
2894             }
2895
2896             if (callbackArg) {
2897                 obj.argument = callbackArg;
2898             }
2899
2900             return obj;
2901         },
2902
2903         initHeader:function(label, value, isDefault)
2904         {
2905             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2906
2907             if (headerObj[label] === undefined) {
2908                 headerObj[label] = value;
2909             }
2910             else {
2911
2912
2913                 headerObj[label] = value + "," + headerObj[label];
2914             }
2915
2916             if (isDefault) {
2917                 this.hasDefaultHeaders = true;
2918             }
2919             else {
2920                 this.hasHeaders = true;
2921             }
2922         },
2923
2924
2925         setHeader:function(o)
2926         {
2927             if (this.hasDefaultHeaders) {
2928                 for (var prop in this.defaultHeaders) {
2929                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2930                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2931                     }
2932                 }
2933             }
2934
2935             if (this.hasHeaders) {
2936                 for (var prop in this.headers) {
2937                     if (this.headers.hasOwnProperty(prop)) {
2938                         o.conn.setRequestHeader(prop, this.headers[prop]);
2939                     }
2940                 }
2941                 this.headers = {};
2942                 this.hasHeaders = false;
2943             }
2944         },
2945
2946         resetDefaultHeaders:function() {
2947             delete this.defaultHeaders;
2948             this.defaultHeaders = {};
2949             this.hasDefaultHeaders = false;
2950         },
2951
2952         abort:function(o, callback, isTimeout)
2953         {
2954             if(this.isCallInProgress(o)) {
2955                 o.conn.abort();
2956                 window.clearInterval(this.poll[o.tId]);
2957                 delete this.poll[o.tId];
2958                 if (isTimeout) {
2959                     delete this.timeout[o.tId];
2960                 }
2961
2962                 this.handleTransactionResponse(o, callback, true);
2963
2964                 return true;
2965             }
2966             else {
2967                 return false;
2968             }
2969         },
2970
2971
2972         isCallInProgress:function(o)
2973         {
2974             if (o && o.conn) {
2975                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2976             }
2977             else {
2978
2979                 return false;
2980             }
2981         },
2982
2983
2984         releaseObject:function(o)
2985         {
2986
2987             o.conn = null;
2988
2989             o = null;
2990         },
2991
2992         activeX:[
2993         'MSXML2.XMLHTTP.3.0',
2994         'MSXML2.XMLHTTP',
2995         'Microsoft.XMLHTTP'
2996         ]
2997
2998
2999     };
3000 })();/*
3001  * Portions of this file are based on pieces of Yahoo User Interface Library
3002  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3003  * YUI licensed under the BSD License:
3004  * http://developer.yahoo.net/yui/license.txt
3005  * <script type="text/javascript">
3006  *
3007  */
3008
3009 Roo.lib.Region = function(t, r, b, l) {
3010     this.top = t;
3011     this[1] = t;
3012     this.right = r;
3013     this.bottom = b;
3014     this.left = l;
3015     this[0] = l;
3016 };
3017
3018
3019 Roo.lib.Region.prototype = {
3020     contains : function(region) {
3021         return ( region.left >= this.left &&
3022                  region.right <= this.right &&
3023                  region.top >= this.top &&
3024                  region.bottom <= this.bottom    );
3025
3026     },
3027
3028     getArea : function() {
3029         return ( (this.bottom - this.top) * (this.right - this.left) );
3030     },
3031
3032     intersect : function(region) {
3033         var t = Math.max(this.top, region.top);
3034         var r = Math.min(this.right, region.right);
3035         var b = Math.min(this.bottom, region.bottom);
3036         var l = Math.max(this.left, region.left);
3037
3038         if (b >= t && r >= l) {
3039             return new Roo.lib.Region(t, r, b, l);
3040         } else {
3041             return null;
3042         }
3043     },
3044     union : function(region) {
3045         var t = Math.min(this.top, region.top);
3046         var r = Math.max(this.right, region.right);
3047         var b = Math.max(this.bottom, region.bottom);
3048         var l = Math.min(this.left, region.left);
3049
3050         return new Roo.lib.Region(t, r, b, l);
3051     },
3052
3053     adjust : function(t, l, b, r) {
3054         this.top += t;
3055         this.left += l;
3056         this.right += r;
3057         this.bottom += b;
3058         return this;
3059     }
3060 };
3061
3062 Roo.lib.Region.getRegion = function(el) {
3063     var p = Roo.lib.Dom.getXY(el);
3064
3065     var t = p[1];
3066     var r = p[0] + el.offsetWidth;
3067     var b = p[1] + el.offsetHeight;
3068     var l = p[0];
3069
3070     return new Roo.lib.Region(t, r, b, l);
3071 };
3072 /*
3073  * Portions of this file are based on pieces of Yahoo User Interface Library
3074  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3075  * YUI licensed under the BSD License:
3076  * http://developer.yahoo.net/yui/license.txt
3077  * <script type="text/javascript">
3078  *
3079  */
3080 //@@dep Roo.lib.Region
3081
3082
3083 Roo.lib.Point = function(x, y) {
3084     if (x instanceof Array) {
3085         y = x[1];
3086         x = x[0];
3087     }
3088     this.x = this.right = this.left = this[0] = x;
3089     this.y = this.top = this.bottom = this[1] = y;
3090 };
3091
3092 Roo.lib.Point.prototype = new Roo.lib.Region();
3093 /*
3094  * Portions of this file are based on pieces of Yahoo User Interface Library
3095  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3096  * YUI licensed under the BSD License:
3097  * http://developer.yahoo.net/yui/license.txt
3098  * <script type="text/javascript">
3099  *
3100  */
3101  
3102 (function() {   
3103
3104     Roo.lib.Anim = {
3105         scroll : function(el, args, duration, easing, cb, scope) {
3106             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3107         },
3108
3109         motion : function(el, args, duration, easing, cb, scope) {
3110             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3111         },
3112
3113         color : function(el, args, duration, easing, cb, scope) {
3114             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3115         },
3116
3117         run : function(el, args, duration, easing, cb, scope, type) {
3118             type = type || Roo.lib.AnimBase;
3119             if (typeof easing == "string") {
3120                 easing = Roo.lib.Easing[easing];
3121             }
3122             var anim = new type(el, args, duration, easing);
3123             anim.animateX(function() {
3124                 Roo.callback(cb, scope);
3125             });
3126             return anim;
3127         }
3128     };
3129 })();/*
3130  * Portions of this file are based on pieces of Yahoo User Interface Library
3131  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3132  * YUI licensed under the BSD License:
3133  * http://developer.yahoo.net/yui/license.txt
3134  * <script type="text/javascript">
3135  *
3136  */
3137
3138 (function() {    
3139     var libFlyweight;
3140     
3141     function fly(el) {
3142         if (!libFlyweight) {
3143             libFlyweight = new Roo.Element.Flyweight();
3144         }
3145         libFlyweight.dom = el;
3146         return libFlyweight;
3147     }
3148
3149     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3150     
3151    
3152     
3153     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3154         if (el) {
3155             this.init(el, attributes, duration, method);
3156         }
3157     };
3158
3159     Roo.lib.AnimBase.fly = fly;
3160     
3161     
3162     
3163     Roo.lib.AnimBase.prototype = {
3164
3165         toString: function() {
3166             var el = this.getEl();
3167             var id = el.id || el.tagName;
3168             return ("Anim " + id);
3169         },
3170
3171         patterns: {
3172             noNegatives:        /width|height|opacity|padding/i,
3173             offsetAttribute:  /^((width|height)|(top|left))$/,
3174             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3175             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3176         },
3177
3178
3179         doMethod: function(attr, start, end) {
3180             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3181         },
3182
3183
3184         setAttribute: function(attr, val, unit) {
3185             if (this.patterns.noNegatives.test(attr)) {
3186                 val = (val > 0) ? val : 0;
3187             }
3188
3189             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3190         },
3191
3192
3193         getAttribute: function(attr) {
3194             var el = this.getEl();
3195             var val = fly(el).getStyle(attr);
3196
3197             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3198                 return parseFloat(val);
3199             }
3200
3201             var a = this.patterns.offsetAttribute.exec(attr) || [];
3202             var pos = !!( a[3] );
3203             var box = !!( a[2] );
3204
3205
3206             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3207                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3208             } else {
3209                 val = 0;
3210             }
3211
3212             return val;
3213         },
3214
3215
3216         getDefaultUnit: function(attr) {
3217             if (this.patterns.defaultUnit.test(attr)) {
3218                 return 'px';
3219             }
3220
3221             return '';
3222         },
3223
3224         animateX : function(callback, scope) {
3225             var f = function() {
3226                 this.onComplete.removeListener(f);
3227                 if (typeof callback == "function") {
3228                     callback.call(scope || this, this);
3229                 }
3230             };
3231             this.onComplete.addListener(f, this);
3232             this.animate();
3233         },
3234
3235
3236         setRuntimeAttribute: function(attr) {
3237             var start;
3238             var end;
3239             var attributes = this.attributes;
3240
3241             this.runtimeAttributes[attr] = {};
3242
3243             var isset = function(prop) {
3244                 return (typeof prop !== 'undefined');
3245             };
3246
3247             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3248                 return false;
3249             }
3250
3251             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3252
3253
3254             if (isset(attributes[attr]['to'])) {
3255                 end = attributes[attr]['to'];
3256             } else if (isset(attributes[attr]['by'])) {
3257                 if (start.constructor == Array) {
3258                     end = [];
3259                     for (var i = 0, len = start.length; i < len; ++i) {
3260                         end[i] = start[i] + attributes[attr]['by'][i];
3261                     }
3262                 } else {
3263                     end = start + attributes[attr]['by'];
3264                 }
3265             }
3266
3267             this.runtimeAttributes[attr].start = start;
3268             this.runtimeAttributes[attr].end = end;
3269
3270
3271             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3272         },
3273
3274
3275         init: function(el, attributes, duration, method) {
3276
3277             var isAnimated = false;
3278
3279
3280             var startTime = null;
3281
3282
3283             var actualFrames = 0;
3284
3285
3286             el = Roo.getDom(el);
3287
3288
3289             this.attributes = attributes || {};
3290
3291
3292             this.duration = duration || 1;
3293
3294
3295             this.method = method || Roo.lib.Easing.easeNone;
3296
3297
3298             this.useSeconds = true;
3299
3300
3301             this.currentFrame = 0;
3302
3303
3304             this.totalFrames = Roo.lib.AnimMgr.fps;
3305
3306
3307             this.getEl = function() {
3308                 return el;
3309             };
3310
3311
3312             this.isAnimated = function() {
3313                 return isAnimated;
3314             };
3315
3316
3317             this.getStartTime = function() {
3318                 return startTime;
3319             };
3320
3321             this.runtimeAttributes = {};
3322
3323
3324             this.animate = function() {
3325                 if (this.isAnimated()) {
3326                     return false;
3327                 }
3328
3329                 this.currentFrame = 0;
3330
3331                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3332
3333                 Roo.lib.AnimMgr.registerElement(this);
3334             };
3335
3336
3337             this.stop = function(finish) {
3338                 if (finish) {
3339                     this.currentFrame = this.totalFrames;
3340                     this._onTween.fire();
3341                 }
3342                 Roo.lib.AnimMgr.stop(this);
3343             };
3344
3345             var onStart = function() {
3346                 this.onStart.fire();
3347
3348                 this.runtimeAttributes = {};
3349                 for (var attr in this.attributes) {
3350                     this.setRuntimeAttribute(attr);
3351                 }
3352
3353                 isAnimated = true;
3354                 actualFrames = 0;
3355                 startTime = new Date();
3356             };
3357
3358
3359             var onTween = function() {
3360                 var data = {
3361                     duration: new Date() - this.getStartTime(),
3362                     currentFrame: this.currentFrame
3363                 };
3364
3365                 data.toString = function() {
3366                     return (
3367                             'duration: ' + data.duration +
3368                             ', currentFrame: ' + data.currentFrame
3369                             );
3370                 };
3371
3372                 this.onTween.fire(data);
3373
3374                 var runtimeAttributes = this.runtimeAttributes;
3375
3376                 for (var attr in runtimeAttributes) {
3377                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3378                 }
3379
3380                 actualFrames += 1;
3381             };
3382
3383             var onComplete = function() {
3384                 var actual_duration = (new Date() - startTime) / 1000 ;
3385
3386                 var data = {
3387                     duration: actual_duration,
3388                     frames: actualFrames,
3389                     fps: actualFrames / actual_duration
3390                 };
3391
3392                 data.toString = function() {
3393                     return (
3394                             'duration: ' + data.duration +
3395                             ', frames: ' + data.frames +
3396                             ', fps: ' + data.fps
3397                             );
3398                 };
3399
3400                 isAnimated = false;
3401                 actualFrames = 0;
3402                 this.onComplete.fire(data);
3403             };
3404
3405
3406             this._onStart = new Roo.util.Event(this);
3407             this.onStart = new Roo.util.Event(this);
3408             this.onTween = new Roo.util.Event(this);
3409             this._onTween = new Roo.util.Event(this);
3410             this.onComplete = new Roo.util.Event(this);
3411             this._onComplete = new Roo.util.Event(this);
3412             this._onStart.addListener(onStart);
3413             this._onTween.addListener(onTween);
3414             this._onComplete.addListener(onComplete);
3415         }
3416     };
3417 })();
3418 /*
3419  * Portions of this file are based on pieces of Yahoo User Interface Library
3420  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3421  * YUI licensed under the BSD License:
3422  * http://developer.yahoo.net/yui/license.txt
3423  * <script type="text/javascript">
3424  *
3425  */
3426
3427 Roo.lib.AnimMgr = new function() {
3428
3429     var thread = null;
3430
3431
3432     var queue = [];
3433
3434
3435     var tweenCount = 0;
3436
3437
3438     this.fps = 1000;
3439
3440
3441     this.delay = 1;
3442
3443
3444     this.registerElement = function(tween) {
3445         queue[queue.length] = tween;
3446         tweenCount += 1;
3447         tween._onStart.fire();
3448         this.start();
3449     };
3450
3451
3452     this.unRegister = function(tween, index) {
3453         tween._onComplete.fire();
3454         index = index || getIndex(tween);
3455         if (index != -1) {
3456             queue.splice(index, 1);
3457         }
3458
3459         tweenCount -= 1;
3460         if (tweenCount <= 0) {
3461             this.stop();
3462         }
3463     };
3464
3465
3466     this.start = function() {
3467         if (thread === null) {
3468             thread = setInterval(this.run, this.delay);
3469         }
3470     };
3471
3472
3473     this.stop = function(tween) {
3474         if (!tween) {
3475             clearInterval(thread);
3476
3477             for (var i = 0, len = queue.length; i < len; ++i) {
3478                 if (queue[0].isAnimated()) {
3479                     this.unRegister(queue[0], 0);
3480                 }
3481             }
3482
3483             queue = [];
3484             thread = null;
3485             tweenCount = 0;
3486         }
3487         else {
3488             this.unRegister(tween);
3489         }
3490     };
3491
3492
3493     this.run = function() {
3494         for (var i = 0, len = queue.length; i < len; ++i) {
3495             var tween = queue[i];
3496             if (!tween || !tween.isAnimated()) {
3497                 continue;
3498             }
3499
3500             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3501             {
3502                 tween.currentFrame += 1;
3503
3504                 if (tween.useSeconds) {
3505                     correctFrame(tween);
3506                 }
3507                 tween._onTween.fire();
3508             }
3509             else {
3510                 Roo.lib.AnimMgr.stop(tween, i);
3511             }
3512         }
3513     };
3514
3515     var getIndex = function(anim) {
3516         for (var i = 0, len = queue.length; i < len; ++i) {
3517             if (queue[i] == anim) {
3518                 return i;
3519             }
3520         }
3521         return -1;
3522     };
3523
3524
3525     var correctFrame = function(tween) {
3526         var frames = tween.totalFrames;
3527         var frame = tween.currentFrame;
3528         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3529         var elapsed = (new Date() - tween.getStartTime());
3530         var tweak = 0;
3531
3532         if (elapsed < tween.duration * 1000) {
3533             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3534         } else {
3535             tweak = frames - (frame + 1);
3536         }
3537         if (tweak > 0 && isFinite(tweak)) {
3538             if (tween.currentFrame + tweak >= frames) {
3539                 tweak = frames - (frame + 1);
3540             }
3541
3542             tween.currentFrame += tweak;
3543         }
3544     };
3545 };
3546
3547     /*
3548  * Portions of this file are based on pieces of Yahoo User Interface Library
3549  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3550  * YUI licensed under the BSD License:
3551  * http://developer.yahoo.net/yui/license.txt
3552  * <script type="text/javascript">
3553  *
3554  */
3555 Roo.lib.Bezier = new function() {
3556
3557         this.getPosition = function(points, t) {
3558             var n = points.length;
3559             var tmp = [];
3560
3561             for (var i = 0; i < n; ++i) {
3562                 tmp[i] = [points[i][0], points[i][1]];
3563             }
3564
3565             for (var j = 1; j < n; ++j) {
3566                 for (i = 0; i < n - j; ++i) {
3567                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3568                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3569                 }
3570             }
3571
3572             return [ tmp[0][0], tmp[0][1] ];
3573
3574         };
3575     };/*
3576  * Portions of this file are based on pieces of Yahoo User Interface Library
3577  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3578  * YUI licensed under the BSD License:
3579  * http://developer.yahoo.net/yui/license.txt
3580  * <script type="text/javascript">
3581  *
3582  */
3583 (function() {
3584
3585     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3586         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3587     };
3588
3589     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3590
3591     var fly = Roo.lib.AnimBase.fly;
3592     var Y = Roo.lib;
3593     var superclass = Y.ColorAnim.superclass;
3594     var proto = Y.ColorAnim.prototype;
3595
3596     proto.toString = function() {
3597         var el = this.getEl();
3598         var id = el.id || el.tagName;
3599         return ("ColorAnim " + id);
3600     };
3601
3602     proto.patterns.color = /color$/i;
3603     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3604     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3605     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3606     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3607
3608
3609     proto.parseColor = function(s) {
3610         if (s.length == 3) {
3611             return s;
3612         }
3613
3614         var c = this.patterns.hex.exec(s);
3615         if (c && c.length == 4) {
3616             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3617         }
3618
3619         c = this.patterns.rgb.exec(s);
3620         if (c && c.length == 4) {
3621             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3622         }
3623
3624         c = this.patterns.hex3.exec(s);
3625         if (c && c.length == 4) {
3626             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3627         }
3628
3629         return null;
3630     };
3631     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3632     proto.getAttribute = function(attr) {
3633         var el = this.getEl();
3634         if (this.patterns.color.test(attr)) {
3635             var val = fly(el).getStyle(attr);
3636
3637             if (this.patterns.transparent.test(val)) {
3638                 var parent = el.parentNode;
3639                 val = fly(parent).getStyle(attr);
3640
3641                 while (parent && this.patterns.transparent.test(val)) {
3642                     parent = parent.parentNode;
3643                     val = fly(parent).getStyle(attr);
3644                     if (parent.tagName.toUpperCase() == 'HTML') {
3645                         val = '#fff';
3646                     }
3647                 }
3648             }
3649         } else {
3650             val = superclass.getAttribute.call(this, attr);
3651         }
3652
3653         return val;
3654     };
3655     proto.getAttribute = function(attr) {
3656         var el = this.getEl();
3657         if (this.patterns.color.test(attr)) {
3658             var val = fly(el).getStyle(attr);
3659
3660             if (this.patterns.transparent.test(val)) {
3661                 var parent = el.parentNode;
3662                 val = fly(parent).getStyle(attr);
3663
3664                 while (parent && this.patterns.transparent.test(val)) {
3665                     parent = parent.parentNode;
3666                     val = fly(parent).getStyle(attr);
3667                     if (parent.tagName.toUpperCase() == 'HTML') {
3668                         val = '#fff';
3669                     }
3670                 }
3671             }
3672         } else {
3673             val = superclass.getAttribute.call(this, attr);
3674         }
3675
3676         return val;
3677     };
3678
3679     proto.doMethod = function(attr, start, end) {
3680         var val;
3681
3682         if (this.patterns.color.test(attr)) {
3683             val = [];
3684             for (var i = 0, len = start.length; i < len; ++i) {
3685                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3686             }
3687
3688             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3689         }
3690         else {
3691             val = superclass.doMethod.call(this, attr, start, end);
3692         }
3693
3694         return val;
3695     };
3696
3697     proto.setRuntimeAttribute = function(attr) {
3698         superclass.setRuntimeAttribute.call(this, attr);
3699
3700         if (this.patterns.color.test(attr)) {
3701             var attributes = this.attributes;
3702             var start = this.parseColor(this.runtimeAttributes[attr].start);
3703             var end = this.parseColor(this.runtimeAttributes[attr].end);
3704
3705             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3706                 end = this.parseColor(attributes[attr].by);
3707
3708                 for (var i = 0, len = start.length; i < len; ++i) {
3709                     end[i] = start[i] + end[i];
3710                 }
3711             }
3712
3713             this.runtimeAttributes[attr].start = start;
3714             this.runtimeAttributes[attr].end = end;
3715         }
3716     };
3717 })();
3718
3719 /*
3720  * Portions of this file are based on pieces of Yahoo User Interface Library
3721  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3722  * YUI licensed under the BSD License:
3723  * http://developer.yahoo.net/yui/license.txt
3724  * <script type="text/javascript">
3725  *
3726  */
3727 Roo.lib.Easing = {
3728
3729
3730     easeNone: function (t, b, c, d) {
3731         return c * t / d + b;
3732     },
3733
3734
3735     easeIn: function (t, b, c, d) {
3736         return c * (t /= d) * t + b;
3737     },
3738
3739
3740     easeOut: function (t, b, c, d) {
3741         return -c * (t /= d) * (t - 2) + b;
3742     },
3743
3744
3745     easeBoth: function (t, b, c, d) {
3746         if ((t /= d / 2) < 1) {
3747             return c / 2 * t * t + b;
3748         }
3749
3750         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3751     },
3752
3753
3754     easeInStrong: function (t, b, c, d) {
3755         return c * (t /= d) * t * t * t + b;
3756     },
3757
3758
3759     easeOutStrong: function (t, b, c, d) {
3760         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3761     },
3762
3763
3764     easeBothStrong: function (t, b, c, d) {
3765         if ((t /= d / 2) < 1) {
3766             return c / 2 * t * t * t * t + b;
3767         }
3768
3769         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3770     },
3771
3772
3773
3774     elasticIn: function (t, b, c, d, a, p) {
3775         if (t == 0) {
3776             return b;
3777         }
3778         if ((t /= d) == 1) {
3779             return b + c;
3780         }
3781         if (!p) {
3782             p = d * .3;
3783         }
3784
3785         if (!a || a < Math.abs(c)) {
3786             a = c;
3787             var s = p / 4;
3788         }
3789         else {
3790             var s = p / (2 * Math.PI) * Math.asin(c / a);
3791         }
3792
3793         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3794     },
3795
3796
3797     elasticOut: function (t, b, c, d, a, p) {
3798         if (t == 0) {
3799             return b;
3800         }
3801         if ((t /= d) == 1) {
3802             return b + c;
3803         }
3804         if (!p) {
3805             p = d * .3;
3806         }
3807
3808         if (!a || a < Math.abs(c)) {
3809             a = c;
3810             var s = p / 4;
3811         }
3812         else {
3813             var s = p / (2 * Math.PI) * Math.asin(c / a);
3814         }
3815
3816         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3817     },
3818
3819
3820     elasticBoth: function (t, b, c, d, a, p) {
3821         if (t == 0) {
3822             return b;
3823         }
3824
3825         if ((t /= d / 2) == 2) {
3826             return b + c;
3827         }
3828
3829         if (!p) {
3830             p = d * (.3 * 1.5);
3831         }
3832
3833         if (!a || a < Math.abs(c)) {
3834             a = c;
3835             var s = p / 4;
3836         }
3837         else {
3838             var s = p / (2 * Math.PI) * Math.asin(c / a);
3839         }
3840
3841         if (t < 1) {
3842             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3843                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3844         }
3845         return a * Math.pow(2, -10 * (t -= 1)) *
3846                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3847     },
3848
3849
3850
3851     backIn: function (t, b, c, d, s) {
3852         if (typeof s == 'undefined') {
3853             s = 1.70158;
3854         }
3855         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3856     },
3857
3858
3859     backOut: function (t, b, c, d, s) {
3860         if (typeof s == 'undefined') {
3861             s = 1.70158;
3862         }
3863         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3864     },
3865
3866
3867     backBoth: function (t, b, c, d, s) {
3868         if (typeof s == 'undefined') {
3869             s = 1.70158;
3870         }
3871
3872         if ((t /= d / 2 ) < 1) {
3873             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3874         }
3875         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3876     },
3877
3878
3879     bounceIn: function (t, b, c, d) {
3880         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3881     },
3882
3883
3884     bounceOut: function (t, b, c, d) {
3885         if ((t /= d) < (1 / 2.75)) {
3886             return c * (7.5625 * t * t) + b;
3887         } else if (t < (2 / 2.75)) {
3888             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3889         } else if (t < (2.5 / 2.75)) {
3890             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3891         }
3892         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3893     },
3894
3895
3896     bounceBoth: function (t, b, c, d) {
3897         if (t < d / 2) {
3898             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3899         }
3900         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3901     }
3902 };/*
3903  * Portions of this file are based on pieces of Yahoo User Interface Library
3904  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3905  * YUI licensed under the BSD License:
3906  * http://developer.yahoo.net/yui/license.txt
3907  * <script type="text/javascript">
3908  *
3909  */
3910     (function() {
3911         Roo.lib.Motion = function(el, attributes, duration, method) {
3912             if (el) {
3913                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3914             }
3915         };
3916
3917         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3918
3919
3920         var Y = Roo.lib;
3921         var superclass = Y.Motion.superclass;
3922         var proto = Y.Motion.prototype;
3923
3924         proto.toString = function() {
3925             var el = this.getEl();
3926             var id = el.id || el.tagName;
3927             return ("Motion " + id);
3928         };
3929
3930         proto.patterns.points = /^points$/i;
3931
3932         proto.setAttribute = function(attr, val, unit) {
3933             if (this.patterns.points.test(attr)) {
3934                 unit = unit || 'px';
3935                 superclass.setAttribute.call(this, 'left', val[0], unit);
3936                 superclass.setAttribute.call(this, 'top', val[1], unit);
3937             } else {
3938                 superclass.setAttribute.call(this, attr, val, unit);
3939             }
3940         };
3941
3942         proto.getAttribute = function(attr) {
3943             if (this.patterns.points.test(attr)) {
3944                 var val = [
3945                         superclass.getAttribute.call(this, 'left'),
3946                         superclass.getAttribute.call(this, 'top')
3947                         ];
3948             } else {
3949                 val = superclass.getAttribute.call(this, attr);
3950             }
3951
3952             return val;
3953         };
3954
3955         proto.doMethod = function(attr, start, end) {
3956             var val = null;
3957
3958             if (this.patterns.points.test(attr)) {
3959                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3960                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3961             } else {
3962                 val = superclass.doMethod.call(this, attr, start, end);
3963             }
3964             return val;
3965         };
3966
3967         proto.setRuntimeAttribute = function(attr) {
3968             if (this.patterns.points.test(attr)) {
3969                 var el = this.getEl();
3970                 var attributes = this.attributes;
3971                 var start;
3972                 var control = attributes['points']['control'] || [];
3973                 var end;
3974                 var i, len;
3975
3976                 if (control.length > 0 && !(control[0] instanceof Array)) {
3977                     control = [control];
3978                 } else {
3979                     var tmp = [];
3980                     for (i = 0,len = control.length; i < len; ++i) {
3981                         tmp[i] = control[i];
3982                     }
3983                     control = tmp;
3984                 }
3985
3986                 Roo.fly(el).position();
3987
3988                 if (isset(attributes['points']['from'])) {
3989                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3990                 }
3991                 else {
3992                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3993                 }
3994
3995                 start = this.getAttribute('points');
3996
3997
3998                 if (isset(attributes['points']['to'])) {
3999                     end = translateValues.call(this, attributes['points']['to'], start);
4000
4001                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4002                     for (i = 0,len = control.length; i < len; ++i) {
4003                         control[i] = translateValues.call(this, control[i], start);
4004                     }
4005
4006
4007                 } else if (isset(attributes['points']['by'])) {
4008                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4009
4010                     for (i = 0,len = control.length; i < len; ++i) {
4011                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4012                     }
4013                 }
4014
4015                 this.runtimeAttributes[attr] = [start];
4016
4017                 if (control.length > 0) {
4018                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4019                 }
4020
4021                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4022             }
4023             else {
4024                 superclass.setRuntimeAttribute.call(this, attr);
4025             }
4026         };
4027
4028         var translateValues = function(val, start) {
4029             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4030             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4031
4032             return val;
4033         };
4034
4035         var isset = function(prop) {
4036             return (typeof prop !== 'undefined');
4037         };
4038     })();
4039 /*
4040  * Portions of this file are based on pieces of Yahoo User Interface Library
4041  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4042  * YUI licensed under the BSD License:
4043  * http://developer.yahoo.net/yui/license.txt
4044  * <script type="text/javascript">
4045  *
4046  */
4047     (function() {
4048         Roo.lib.Scroll = function(el, attributes, duration, method) {
4049             if (el) {
4050                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4051             }
4052         };
4053
4054         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4055
4056
4057         var Y = Roo.lib;
4058         var superclass = Y.Scroll.superclass;
4059         var proto = Y.Scroll.prototype;
4060
4061         proto.toString = function() {
4062             var el = this.getEl();
4063             var id = el.id || el.tagName;
4064             return ("Scroll " + id);
4065         };
4066
4067         proto.doMethod = function(attr, start, end) {
4068             var val = null;
4069
4070             if (attr == 'scroll') {
4071                 val = [
4072                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4073                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4074                         ];
4075
4076             } else {
4077                 val = superclass.doMethod.call(this, attr, start, end);
4078             }
4079             return val;
4080         };
4081
4082         proto.getAttribute = function(attr) {
4083             var val = null;
4084             var el = this.getEl();
4085
4086             if (attr == 'scroll') {
4087                 val = [ el.scrollLeft, el.scrollTop ];
4088             } else {
4089                 val = superclass.getAttribute.call(this, attr);
4090             }
4091
4092             return val;
4093         };
4094
4095         proto.setAttribute = function(attr, val, unit) {
4096             var el = this.getEl();
4097
4098             if (attr == 'scroll') {
4099                 el.scrollLeft = val[0];
4100                 el.scrollTop = val[1];
4101             } else {
4102                 superclass.setAttribute.call(this, attr, val, unit);
4103             }
4104         };
4105     })();
4106 /*
4107  * Based on:
4108  * Ext JS Library 1.1.1
4109  * Copyright(c) 2006-2007, Ext JS, LLC.
4110  *
4111  * Originally Released Under LGPL - original licence link has changed is not relivant.
4112  *
4113  * Fork - LGPL
4114  * <script type="text/javascript">
4115  */
4116
4117
4118 // nasty IE9 hack - what a pile of crap that is..
4119
4120  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4121     Range.prototype.createContextualFragment = function (html) {
4122         var doc = window.document;
4123         var container = doc.createElement("div");
4124         container.innerHTML = html;
4125         var frag = doc.createDocumentFragment(), n;
4126         while ((n = container.firstChild)) {
4127             frag.appendChild(n);
4128         }
4129         return frag;
4130     };
4131 }
4132
4133 /**
4134  * @class Roo.DomHelper
4135  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4136  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4137  * @singleton
4138  */
4139 Roo.DomHelper = function(){
4140     var tempTableEl = null;
4141     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4142     var tableRe = /^table|tbody|tr|td$/i;
4143     var xmlns = {};
4144     // build as innerHTML where available
4145     /** @ignore */
4146     var createHtml = function(o){
4147         if(typeof o == 'string'){
4148             return o;
4149         }
4150         var b = "";
4151         if(!o.tag){
4152             o.tag = "div";
4153         }
4154         b += "<" + o.tag;
4155         for(var attr in o){
4156             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4157             if(attr == "style"){
4158                 var s = o["style"];
4159                 if(typeof s == "function"){
4160                     s = s.call();
4161                 }
4162                 if(typeof s == "string"){
4163                     b += ' style="' + s + '"';
4164                 }else if(typeof s == "object"){
4165                     b += ' style="';
4166                     for(var key in s){
4167                         if(typeof s[key] != "function"){
4168                             b += key + ":" + s[key] + ";";
4169                         }
4170                     }
4171                     b += '"';
4172                 }
4173             }else{
4174                 if(attr == "cls"){
4175                     b += ' class="' + o["cls"] + '"';
4176                 }else if(attr == "htmlFor"){
4177                     b += ' for="' + o["htmlFor"] + '"';
4178                 }else{
4179                     b += " " + attr + '="' + o[attr] + '"';
4180                 }
4181             }
4182         }
4183         if(emptyTags.test(o.tag)){
4184             b += "/>";
4185         }else{
4186             b += ">";
4187             var cn = o.children || o.cn;
4188             if(cn){
4189                 //http://bugs.kde.org/show_bug.cgi?id=71506
4190                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4191                     for(var i = 0, len = cn.length; i < len; i++) {
4192                         b += createHtml(cn[i], b);
4193                     }
4194                 }else{
4195                     b += createHtml(cn, b);
4196                 }
4197             }
4198             if(o.html){
4199                 b += o.html;
4200             }
4201             b += "</" + o.tag + ">";
4202         }
4203         return b;
4204     };
4205
4206     // build as dom
4207     /** @ignore */
4208     var createDom = function(o, parentNode){
4209          
4210         // defininition craeted..
4211         var ns = false;
4212         if (o.ns && o.ns != 'html') {
4213                
4214             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4215                 xmlns[o.ns] = o.xmlns;
4216                 ns = o.xmlns;
4217             }
4218             if (typeof(xmlns[o.ns]) == 'undefined') {
4219                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4220             }
4221             ns = xmlns[o.ns];
4222         }
4223         
4224         
4225         if (typeof(o) == 'string') {
4226             return parentNode.appendChild(document.createTextNode(o));
4227         }
4228         o.tag = o.tag || div;
4229         if (o.ns && Roo.isIE) {
4230             ns = false;
4231             o.tag = o.ns + ':' + o.tag;
4232             
4233         }
4234         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4235         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4236         for(var attr in o){
4237             
4238             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4239                     attr == "style" || typeof o[attr] == "function") { continue; }
4240                     
4241             if(attr=="cls" && Roo.isIE){
4242                 el.className = o["cls"];
4243             }else{
4244                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4245                 else { 
4246                     el[attr] = o[attr];
4247                 }
4248             }
4249         }
4250         Roo.DomHelper.applyStyles(el, o.style);
4251         var cn = o.children || o.cn;
4252         if(cn){
4253             //http://bugs.kde.org/show_bug.cgi?id=71506
4254              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4255                 for(var i = 0, len = cn.length; i < len; i++) {
4256                     createDom(cn[i], el);
4257                 }
4258             }else{
4259                 createDom(cn, el);
4260             }
4261         }
4262         if(o.html){
4263             el.innerHTML = o.html;
4264         }
4265         if(parentNode){
4266            parentNode.appendChild(el);
4267         }
4268         return el;
4269     };
4270
4271     var ieTable = function(depth, s, h, e){
4272         tempTableEl.innerHTML = [s, h, e].join('');
4273         var i = -1, el = tempTableEl;
4274         while(++i < depth){
4275             el = el.firstChild;
4276         }
4277         return el;
4278     };
4279
4280     // kill repeat to save bytes
4281     var ts = '<table>',
4282         te = '</table>',
4283         tbs = ts+'<tbody>',
4284         tbe = '</tbody>'+te,
4285         trs = tbs + '<tr>',
4286         tre = '</tr>'+tbe;
4287
4288     /**
4289      * @ignore
4290      * Nasty code for IE's broken table implementation
4291      */
4292     var insertIntoTable = function(tag, where, el, html){
4293         if(!tempTableEl){
4294             tempTableEl = document.createElement('div');
4295         }
4296         var node;
4297         var before = null;
4298         if(tag == 'td'){
4299             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4300                 return;
4301             }
4302             if(where == 'beforebegin'){
4303                 before = el;
4304                 el = el.parentNode;
4305             } else{
4306                 before = el.nextSibling;
4307                 el = el.parentNode;
4308             }
4309             node = ieTable(4, trs, html, tre);
4310         }
4311         else if(tag == 'tr'){
4312             if(where == 'beforebegin'){
4313                 before = el;
4314                 el = el.parentNode;
4315                 node = ieTable(3, tbs, html, tbe);
4316             } else if(where == 'afterend'){
4317                 before = el.nextSibling;
4318                 el = el.parentNode;
4319                 node = ieTable(3, tbs, html, tbe);
4320             } else{ // INTO a TR
4321                 if(where == 'afterbegin'){
4322                     before = el.firstChild;
4323                 }
4324                 node = ieTable(4, trs, html, tre);
4325             }
4326         } else if(tag == 'tbody'){
4327             if(where == 'beforebegin'){
4328                 before = el;
4329                 el = el.parentNode;
4330                 node = ieTable(2, ts, html, te);
4331             } else if(where == 'afterend'){
4332                 before = el.nextSibling;
4333                 el = el.parentNode;
4334                 node = ieTable(2, ts, html, te);
4335             } else{
4336                 if(where == 'afterbegin'){
4337                     before = el.firstChild;
4338                 }
4339                 node = ieTable(3, tbs, html, tbe);
4340             }
4341         } else{ // TABLE
4342             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4343                 return;
4344             }
4345             if(where == 'afterbegin'){
4346                 before = el.firstChild;
4347             }
4348             node = ieTable(2, ts, html, te);
4349         }
4350         el.insertBefore(node, before);
4351         return node;
4352     };
4353
4354     return {
4355     /** True to force the use of DOM instead of html fragments @type Boolean */
4356     useDom : false,
4357
4358     /**
4359      * Returns the markup for the passed Element(s) config
4360      * @param {Object} o The Dom object spec (and children)
4361      * @return {String}
4362      */
4363     markup : function(o){
4364         return createHtml(o);
4365     },
4366
4367     /**
4368      * Applies a style specification to an element
4369      * @param {String/HTMLElement} el The element to apply styles to
4370      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4371      * a function which returns such a specification.
4372      */
4373     applyStyles : function(el, styles){
4374         if(styles){
4375            el = Roo.fly(el);
4376            if(typeof styles == "string"){
4377                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4378                var matches;
4379                while ((matches = re.exec(styles)) != null){
4380                    el.setStyle(matches[1], matches[2]);
4381                }
4382            }else if (typeof styles == "object"){
4383                for (var style in styles){
4384                   el.setStyle(style, styles[style]);
4385                }
4386            }else if (typeof styles == "function"){
4387                 Roo.DomHelper.applyStyles(el, styles.call());
4388            }
4389         }
4390     },
4391
4392     /**
4393      * Inserts an HTML fragment into the Dom
4394      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4395      * @param {HTMLElement} el The context element
4396      * @param {String} html The HTML fragmenet
4397      * @return {HTMLElement} The new node
4398      */
4399     insertHtml : function(where, el, html){
4400         where = where.toLowerCase();
4401         if(el.insertAdjacentHTML){
4402             if(tableRe.test(el.tagName)){
4403                 var rs;
4404                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4405                     return rs;
4406                 }
4407             }
4408             switch(where){
4409                 case "beforebegin":
4410                     el.insertAdjacentHTML('BeforeBegin', html);
4411                     return el.previousSibling;
4412                 case "afterbegin":
4413                     el.insertAdjacentHTML('AfterBegin', html);
4414                     return el.firstChild;
4415                 case "beforeend":
4416                     el.insertAdjacentHTML('BeforeEnd', html);
4417                     return el.lastChild;
4418                 case "afterend":
4419                     el.insertAdjacentHTML('AfterEnd', html);
4420                     return el.nextSibling;
4421             }
4422             throw 'Illegal insertion point -> "' + where + '"';
4423         }
4424         var range = el.ownerDocument.createRange();
4425         var frag;
4426         switch(where){
4427              case "beforebegin":
4428                 range.setStartBefore(el);
4429                 frag = range.createContextualFragment(html);
4430                 el.parentNode.insertBefore(frag, el);
4431                 return el.previousSibling;
4432              case "afterbegin":
4433                 if(el.firstChild){
4434                     range.setStartBefore(el.firstChild);
4435                     frag = range.createContextualFragment(html);
4436                     el.insertBefore(frag, el.firstChild);
4437                     return el.firstChild;
4438                 }else{
4439                     el.innerHTML = html;
4440                     return el.firstChild;
4441                 }
4442             case "beforeend":
4443                 if(el.lastChild){
4444                     range.setStartAfter(el.lastChild);
4445                     frag = range.createContextualFragment(html);
4446                     el.appendChild(frag);
4447                     return el.lastChild;
4448                 }else{
4449                     el.innerHTML = html;
4450                     return el.lastChild;
4451                 }
4452             case "afterend":
4453                 range.setStartAfter(el);
4454                 frag = range.createContextualFragment(html);
4455                 el.parentNode.insertBefore(frag, el.nextSibling);
4456                 return el.nextSibling;
4457             }
4458             throw 'Illegal insertion point -> "' + where + '"';
4459     },
4460
4461     /**
4462      * Creates new Dom element(s) and inserts them before el
4463      * @param {String/HTMLElement/Element} el The context element
4464      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4465      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4466      * @return {HTMLElement/Roo.Element} The new node
4467      */
4468     insertBefore : function(el, o, returnElement){
4469         return this.doInsert(el, o, returnElement, "beforeBegin");
4470     },
4471
4472     /**
4473      * Creates new Dom element(s) and inserts them after el
4474      * @param {String/HTMLElement/Element} el The context element
4475      * @param {Object} o The Dom object spec (and children)
4476      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4477      * @return {HTMLElement/Roo.Element} The new node
4478      */
4479     insertAfter : function(el, o, returnElement){
4480         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4481     },
4482
4483     /**
4484      * Creates new Dom element(s) and inserts them as the first child of el
4485      * @param {String/HTMLElement/Element} el The context element
4486      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4487      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4488      * @return {HTMLElement/Roo.Element} The new node
4489      */
4490     insertFirst : function(el, o, returnElement){
4491         return this.doInsert(el, o, returnElement, "afterBegin");
4492     },
4493
4494     // private
4495     doInsert : function(el, o, returnElement, pos, sibling){
4496         el = Roo.getDom(el);
4497         var newNode;
4498         if(this.useDom || o.ns){
4499             newNode = createDom(o, null);
4500             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4501         }else{
4502             var html = createHtml(o);
4503             newNode = this.insertHtml(pos, el, html);
4504         }
4505         return returnElement ? Roo.get(newNode, true) : newNode;
4506     },
4507
4508     /**
4509      * Creates new Dom element(s) and appends them to el
4510      * @param {String/HTMLElement/Element} el The context element
4511      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4512      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4513      * @return {HTMLElement/Roo.Element} The new node
4514      */
4515     append : function(el, o, returnElement){
4516         el = Roo.getDom(el);
4517         var newNode;
4518         if(this.useDom || o.ns){
4519             newNode = createDom(o, null);
4520             el.appendChild(newNode);
4521         }else{
4522             var html = createHtml(o);
4523             newNode = this.insertHtml("beforeEnd", el, html);
4524         }
4525         return returnElement ? Roo.get(newNode, true) : newNode;
4526     },
4527
4528     /**
4529      * Creates new Dom element(s) and overwrites the contents of el with them
4530      * @param {String/HTMLElement/Element} el The context element
4531      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4532      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4533      * @return {HTMLElement/Roo.Element} The new node
4534      */
4535     overwrite : function(el, o, returnElement){
4536         el = Roo.getDom(el);
4537         if (o.ns) {
4538           
4539             while (el.childNodes.length) {
4540                 el.removeChild(el.firstChild);
4541             }
4542             createDom(o, el);
4543         } else {
4544             el.innerHTML = createHtml(o);   
4545         }
4546         
4547         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4548     },
4549
4550     /**
4551      * Creates a new Roo.DomHelper.Template from the Dom object spec
4552      * @param {Object} o The Dom object spec (and children)
4553      * @return {Roo.DomHelper.Template} The new template
4554      */
4555     createTemplate : function(o){
4556         var html = createHtml(o);
4557         return new Roo.Template(html);
4558     }
4559     };
4560 }();
4561 /*
4562  * Based on:
4563  * Ext JS Library 1.1.1
4564  * Copyright(c) 2006-2007, Ext JS, LLC.
4565  *
4566  * Originally Released Under LGPL - original licence link has changed is not relivant.
4567  *
4568  * Fork - LGPL
4569  * <script type="text/javascript">
4570  */
4571  
4572 /**
4573 * @class Roo.Template
4574 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4575 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4576 * Usage:
4577 <pre><code>
4578 var t = new Roo.Template({
4579     html :  '&lt;div name="{id}"&gt;' + 
4580         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4581         '&lt;/div&gt;',
4582     myformat: function (value, allValues) {
4583         return 'XX' + value;
4584     }
4585 });
4586 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4587 </code></pre>
4588 * For more information see this blog post with examples:
4589 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4590      - Create Elements using DOM, HTML fragments and Templates</a>. 
4591 * @constructor
4592 * @param {Object} cfg - Configuration object.
4593 */
4594 Roo.Template = function(cfg){
4595     // BC!
4596     if(cfg instanceof Array){
4597         cfg = cfg.join("");
4598     }else if(arguments.length > 1){
4599         cfg = Array.prototype.join.call(arguments, "");
4600     }
4601     
4602     
4603     if (typeof(cfg) == 'object') {
4604         Roo.apply(this,cfg)
4605     } else {
4606         // bc
4607         this.html = cfg;
4608     }
4609     if (this.url) {
4610         this.load();
4611     }
4612     
4613 };
4614 Roo.Template.prototype = {
4615     
4616     /**
4617      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4618      *                    it should be fixed so that template is observable...
4619      */
4620     url : false,
4621     /**
4622      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4623      */
4624     html : '',
4625     /**
4626      * Returns an HTML fragment of this template with the specified values applied.
4627      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4628      * @return {String} The HTML fragment
4629      */
4630     applyTemplate : function(values){
4631         try {
4632            
4633             if(this.compiled){
4634                 return this.compiled(values);
4635             }
4636             var useF = this.disableFormats !== true;
4637             var fm = Roo.util.Format, tpl = this;
4638             var fn = function(m, name, format, args){
4639                 if(format && useF){
4640                     if(format.substr(0, 5) == "this."){
4641                         return tpl.call(format.substr(5), values[name], values);
4642                     }else{
4643                         if(args){
4644                             // quoted values are required for strings in compiled templates, 
4645                             // but for non compiled we need to strip them
4646                             // quoted reversed for jsmin
4647                             var re = /^\s*['"](.*)["']\s*$/;
4648                             args = args.split(',');
4649                             for(var i = 0, len = args.length; i < len; i++){
4650                                 args[i] = args[i].replace(re, "$1");
4651                             }
4652                             args = [values[name]].concat(args);
4653                         }else{
4654                             args = [values[name]];
4655                         }
4656                         return fm[format].apply(fm, args);
4657                     }
4658                 }else{
4659                     return values[name] !== undefined ? values[name] : "";
4660                 }
4661             };
4662             return this.html.replace(this.re, fn);
4663         } catch (e) {
4664             Roo.log(e);
4665             throw e;
4666         }
4667          
4668     },
4669     
4670     loading : false,
4671       
4672     load : function ()
4673     {
4674          
4675         if (this.loading) {
4676             return;
4677         }
4678         var _t = this;
4679         
4680         this.loading = true;
4681         this.compiled = false;
4682         
4683         var cx = new Roo.data.Connection();
4684         cx.request({
4685             url : this.url,
4686             method : 'GET',
4687             success : function (response) {
4688                 _t.loading = false;
4689                 _t.html = response.responseText;
4690                 _t.url = false;
4691                 _t.compile();
4692              },
4693             failure : function(response) {
4694                 Roo.log("Template failed to load from " + _t.url);
4695                 _t.loading = false;
4696             }
4697         });
4698     },
4699
4700     /**
4701      * Sets the HTML used as the template and optionally compiles it.
4702      * @param {String} html
4703      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4704      * @return {Roo.Template} this
4705      */
4706     set : function(html, compile){
4707         this.html = html;
4708         this.compiled = null;
4709         if(compile){
4710             this.compile();
4711         }
4712         return this;
4713     },
4714     
4715     /**
4716      * True to disable format functions (defaults to false)
4717      * @type Boolean
4718      */
4719     disableFormats : false,
4720     
4721     /**
4722     * The regular expression used to match template variables 
4723     * @type RegExp
4724     * @property 
4725     */
4726     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4727     
4728     /**
4729      * Compiles the template into an internal function, eliminating the RegEx overhead.
4730      * @return {Roo.Template} this
4731      */
4732     compile : function(){
4733         var fm = Roo.util.Format;
4734         var useF = this.disableFormats !== true;
4735         var sep = Roo.isGecko ? "+" : ",";
4736         var fn = function(m, name, format, args){
4737             if(format && useF){
4738                 args = args ? ',' + args : "";
4739                 if(format.substr(0, 5) != "this."){
4740                     format = "fm." + format + '(';
4741                 }else{
4742                     format = 'this.call("'+ format.substr(5) + '", ';
4743                     args = ", values";
4744                 }
4745             }else{
4746                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4747             }
4748             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4749         };
4750         var body;
4751         // branched to use + in gecko and [].join() in others
4752         if(Roo.isGecko){
4753             body = "this.compiled = function(values){ return '" +
4754                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4755                     "';};";
4756         }else{
4757             body = ["this.compiled = function(values){ return ['"];
4758             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4759             body.push("'].join('');};");
4760             body = body.join('');
4761         }
4762         /**
4763          * eval:var:values
4764          * eval:var:fm
4765          */
4766         eval(body);
4767         return this;
4768     },
4769     
4770     // private function used to call members
4771     call : function(fnName, value, allValues){
4772         return this[fnName](value, allValues);
4773     },
4774     
4775     /**
4776      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4777      * @param {String/HTMLElement/Roo.Element} el The context element
4778      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4779      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4780      * @return {HTMLElement/Roo.Element} The new node or Element
4781      */
4782     insertFirst: function(el, values, returnElement){
4783         return this.doInsert('afterBegin', el, values, returnElement);
4784     },
4785
4786     /**
4787      * Applies the supplied values to the template and inserts the new node(s) before el.
4788      * @param {String/HTMLElement/Roo.Element} el The context element
4789      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4790      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4791      * @return {HTMLElement/Roo.Element} The new node or Element
4792      */
4793     insertBefore: function(el, values, returnElement){
4794         return this.doInsert('beforeBegin', el, values, returnElement);
4795     },
4796
4797     /**
4798      * Applies the supplied values to the template and inserts the new node(s) after el.
4799      * @param {String/HTMLElement/Roo.Element} el The context element
4800      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4801      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4802      * @return {HTMLElement/Roo.Element} The new node or Element
4803      */
4804     insertAfter : function(el, values, returnElement){
4805         return this.doInsert('afterEnd', el, values, returnElement);
4806     },
4807     
4808     /**
4809      * Applies the supplied values to the template and appends the new node(s) to el.
4810      * @param {String/HTMLElement/Roo.Element} el The context element
4811      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4812      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4813      * @return {HTMLElement/Roo.Element} The new node or Element
4814      */
4815     append : function(el, values, returnElement){
4816         return this.doInsert('beforeEnd', el, values, returnElement);
4817     },
4818
4819     doInsert : function(where, el, values, returnEl){
4820         el = Roo.getDom(el);
4821         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4822         return returnEl ? Roo.get(newNode, true) : newNode;
4823     },
4824
4825     /**
4826      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4827      * @param {String/HTMLElement/Roo.Element} el The context element
4828      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4829      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4830      * @return {HTMLElement/Roo.Element} The new node or Element
4831      */
4832     overwrite : function(el, values, returnElement){
4833         el = Roo.getDom(el);
4834         el.innerHTML = this.applyTemplate(values);
4835         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4836     }
4837 };
4838 /**
4839  * Alias for {@link #applyTemplate}
4840  * @method
4841  */
4842 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4843
4844 // backwards compat
4845 Roo.DomHelper.Template = Roo.Template;
4846
4847 /**
4848  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4849  * @param {String/HTMLElement} el A DOM element or its id
4850  * @returns {Roo.Template} The created template
4851  * @static
4852  */
4853 Roo.Template.from = function(el){
4854     el = Roo.getDom(el);
4855     return new Roo.Template(el.value || el.innerHTML);
4856 };/*
4857  * Based on:
4858  * Ext JS Library 1.1.1
4859  * Copyright(c) 2006-2007, Ext JS, LLC.
4860  *
4861  * Originally Released Under LGPL - original licence link has changed is not relivant.
4862  *
4863  * Fork - LGPL
4864  * <script type="text/javascript">
4865  */
4866  
4867
4868 /*
4869  * This is code is also distributed under MIT license for use
4870  * with jQuery and prototype JavaScript libraries.
4871  */
4872 /**
4873  * @class Roo.DomQuery
4874 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4875 <p>
4876 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4877
4878 <p>
4879 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4880 </p>
4881 <h4>Element Selectors:</h4>
4882 <ul class="list">
4883     <li> <b>*</b> any element</li>
4884     <li> <b>E</b> an element with the tag E</li>
4885     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4886     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4887     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4888     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4889 </ul>
4890 <h4>Attribute Selectors:</h4>
4891 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4892 <ul class="list">
4893     <li> <b>E[foo]</b> has an attribute "foo"</li>
4894     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4895     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4896     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4897     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4898     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4899     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4900 </ul>
4901 <h4>Pseudo Classes:</h4>
4902 <ul class="list">
4903     <li> <b>E:first-child</b> E is the first child of its parent</li>
4904     <li> <b>E:last-child</b> E is the last child of its parent</li>
4905     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4906     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4907     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4908     <li> <b>E:only-child</b> E is the only child of its parent</li>
4909     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4910     <li> <b>E:first</b> the first E in the resultset</li>
4911     <li> <b>E:last</b> the last E in the resultset</li>
4912     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4913     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4914     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4915     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4916     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4917     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4918     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4919     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4920     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4921 </ul>
4922 <h4>CSS Value Selectors:</h4>
4923 <ul class="list">
4924     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4925     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4926     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4927     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4928     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4929     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4930 </ul>
4931  * @singleton
4932  */
4933 Roo.DomQuery = function(){
4934     var cache = {}, simpleCache = {}, valueCache = {};
4935     var nonSpace = /\S/;
4936     var trimRe = /^\s+|\s+$/g;
4937     var tplRe = /\{(\d+)\}/g;
4938     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4939     var tagTokenRe = /^(#)?([\w-\*]+)/;
4940     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4941
4942     function child(p, index){
4943         var i = 0;
4944         var n = p.firstChild;
4945         while(n){
4946             if(n.nodeType == 1){
4947                if(++i == index){
4948                    return n;
4949                }
4950             }
4951             n = n.nextSibling;
4952         }
4953         return null;
4954     };
4955
4956     function next(n){
4957         while((n = n.nextSibling) && n.nodeType != 1);
4958         return n;
4959     };
4960
4961     function prev(n){
4962         while((n = n.previousSibling) && n.nodeType != 1);
4963         return n;
4964     };
4965
4966     function children(d){
4967         var n = d.firstChild, ni = -1;
4968             while(n){
4969                 var nx = n.nextSibling;
4970                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4971                     d.removeChild(n);
4972                 }else{
4973                     n.nodeIndex = ++ni;
4974                 }
4975                 n = nx;
4976             }
4977             return this;
4978         };
4979
4980     function byClassName(c, a, v){
4981         if(!v){
4982             return c;
4983         }
4984         var r = [], ri = -1, cn;
4985         for(var i = 0, ci; ci = c[i]; i++){
4986             if((' '+ci.className+' ').indexOf(v) != -1){
4987                 r[++ri] = ci;
4988             }
4989         }
4990         return r;
4991     };
4992
4993     function attrValue(n, attr){
4994         if(!n.tagName && typeof n.length != "undefined"){
4995             n = n[0];
4996         }
4997         if(!n){
4998             return null;
4999         }
5000         if(attr == "for"){
5001             return n.htmlFor;
5002         }
5003         if(attr == "class" || attr == "className"){
5004             return n.className;
5005         }
5006         return n.getAttribute(attr) || n[attr];
5007
5008     };
5009
5010     function getNodes(ns, mode, tagName){
5011         var result = [], ri = -1, cs;
5012         if(!ns){
5013             return result;
5014         }
5015         tagName = tagName || "*";
5016         if(typeof ns.getElementsByTagName != "undefined"){
5017             ns = [ns];
5018         }
5019         if(!mode){
5020             for(var i = 0, ni; ni = ns[i]; i++){
5021                 cs = ni.getElementsByTagName(tagName);
5022                 for(var j = 0, ci; ci = cs[j]; j++){
5023                     result[++ri] = ci;
5024                 }
5025             }
5026         }else if(mode == "/" || mode == ">"){
5027             var utag = tagName.toUpperCase();
5028             for(var i = 0, ni, cn; ni = ns[i]; i++){
5029                 cn = ni.children || ni.childNodes;
5030                 for(var j = 0, cj; cj = cn[j]; j++){
5031                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5032                         result[++ri] = cj;
5033                     }
5034                 }
5035             }
5036         }else if(mode == "+"){
5037             var utag = tagName.toUpperCase();
5038             for(var i = 0, n; n = ns[i]; i++){
5039                 while((n = n.nextSibling) && n.nodeType != 1);
5040                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5041                     result[++ri] = n;
5042                 }
5043             }
5044         }else if(mode == "~"){
5045             for(var i = 0, n; n = ns[i]; i++){
5046                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5047                 if(n){
5048                     result[++ri] = n;
5049                 }
5050             }
5051         }
5052         return result;
5053     };
5054
5055     function concat(a, b){
5056         if(b.slice){
5057             return a.concat(b);
5058         }
5059         for(var i = 0, l = b.length; i < l; i++){
5060             a[a.length] = b[i];
5061         }
5062         return a;
5063     }
5064
5065     function byTag(cs, tagName){
5066         if(cs.tagName || cs == document){
5067             cs = [cs];
5068         }
5069         if(!tagName){
5070             return cs;
5071         }
5072         var r = [], ri = -1;
5073         tagName = tagName.toLowerCase();
5074         for(var i = 0, ci; ci = cs[i]; i++){
5075             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5076                 r[++ri] = ci;
5077             }
5078         }
5079         return r;
5080     };
5081
5082     function byId(cs, attr, id){
5083         if(cs.tagName || cs == document){
5084             cs = [cs];
5085         }
5086         if(!id){
5087             return cs;
5088         }
5089         var r = [], ri = -1;
5090         for(var i = 0,ci; ci = cs[i]; i++){
5091             if(ci && ci.id == id){
5092                 r[++ri] = ci;
5093                 return r;
5094             }
5095         }
5096         return r;
5097     };
5098
5099     function byAttribute(cs, attr, value, op, custom){
5100         var r = [], ri = -1, st = custom=="{";
5101         var f = Roo.DomQuery.operators[op];
5102         for(var i = 0, ci; ci = cs[i]; i++){
5103             var a;
5104             if(st){
5105                 a = Roo.DomQuery.getStyle(ci, attr);
5106             }
5107             else if(attr == "class" || attr == "className"){
5108                 a = ci.className;
5109             }else if(attr == "for"){
5110                 a = ci.htmlFor;
5111             }else if(attr == "href"){
5112                 a = ci.getAttribute("href", 2);
5113             }else{
5114                 a = ci.getAttribute(attr);
5115             }
5116             if((f && f(a, value)) || (!f && a)){
5117                 r[++ri] = ci;
5118             }
5119         }
5120         return r;
5121     };
5122
5123     function byPseudo(cs, name, value){
5124         return Roo.DomQuery.pseudos[name](cs, value);
5125     };
5126
5127     // This is for IE MSXML which does not support expandos.
5128     // IE runs the same speed using setAttribute, however FF slows way down
5129     // and Safari completely fails so they need to continue to use expandos.
5130     var isIE = window.ActiveXObject ? true : false;
5131
5132     // this eval is stop the compressor from
5133     // renaming the variable to something shorter
5134     
5135     /** eval:var:batch */
5136     var batch = 30803; 
5137
5138     var key = 30803;
5139
5140     function nodupIEXml(cs){
5141         var d = ++key;
5142         cs[0].setAttribute("_nodup", d);
5143         var r = [cs[0]];
5144         for(var i = 1, len = cs.length; i < len; i++){
5145             var c = cs[i];
5146             if(!c.getAttribute("_nodup") != d){
5147                 c.setAttribute("_nodup", d);
5148                 r[r.length] = c;
5149             }
5150         }
5151         for(var i = 0, len = cs.length; i < len; i++){
5152             cs[i].removeAttribute("_nodup");
5153         }
5154         return r;
5155     }
5156
5157     function nodup(cs){
5158         if(!cs){
5159             return [];
5160         }
5161         var len = cs.length, c, i, r = cs, cj, ri = -1;
5162         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5163             return cs;
5164         }
5165         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5166             return nodupIEXml(cs);
5167         }
5168         var d = ++key;
5169         cs[0]._nodup = d;
5170         for(i = 1; c = cs[i]; i++){
5171             if(c._nodup != d){
5172                 c._nodup = d;
5173             }else{
5174                 r = [];
5175                 for(var j = 0; j < i; j++){
5176                     r[++ri] = cs[j];
5177                 }
5178                 for(j = i+1; cj = cs[j]; j++){
5179                     if(cj._nodup != d){
5180                         cj._nodup = d;
5181                         r[++ri] = cj;
5182                     }
5183                 }
5184                 return r;
5185             }
5186         }
5187         return r;
5188     }
5189
5190     function quickDiffIEXml(c1, c2){
5191         var d = ++key;
5192         for(var i = 0, len = c1.length; i < len; i++){
5193             c1[i].setAttribute("_qdiff", d);
5194         }
5195         var r = [];
5196         for(var i = 0, len = c2.length; i < len; i++){
5197             if(c2[i].getAttribute("_qdiff") != d){
5198                 r[r.length] = c2[i];
5199             }
5200         }
5201         for(var i = 0, len = c1.length; i < len; i++){
5202            c1[i].removeAttribute("_qdiff");
5203         }
5204         return r;
5205     }
5206
5207     function quickDiff(c1, c2){
5208         var len1 = c1.length;
5209         if(!len1){
5210             return c2;
5211         }
5212         if(isIE && c1[0].selectSingleNode){
5213             return quickDiffIEXml(c1, c2);
5214         }
5215         var d = ++key;
5216         for(var i = 0; i < len1; i++){
5217             c1[i]._qdiff = d;
5218         }
5219         var r = [];
5220         for(var i = 0, len = c2.length; i < len; i++){
5221             if(c2[i]._qdiff != d){
5222                 r[r.length] = c2[i];
5223             }
5224         }
5225         return r;
5226     }
5227
5228     function quickId(ns, mode, root, id){
5229         if(ns == root){
5230            var d = root.ownerDocument || root;
5231            return d.getElementById(id);
5232         }
5233         ns = getNodes(ns, mode, "*");
5234         return byId(ns, null, id);
5235     }
5236
5237     return {
5238         getStyle : function(el, name){
5239             return Roo.fly(el).getStyle(name);
5240         },
5241         /**
5242          * Compiles a selector/xpath query into a reusable function. The returned function
5243          * takes one parameter "root" (optional), which is the context node from where the query should start.
5244          * @param {String} selector The selector/xpath query
5245          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5246          * @return {Function}
5247          */
5248         compile : function(path, type){
5249             type = type || "select";
5250             
5251             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5252             var q = path, mode, lq;
5253             var tk = Roo.DomQuery.matchers;
5254             var tklen = tk.length;
5255             var mm;
5256
5257             // accept leading mode switch
5258             var lmode = q.match(modeRe);
5259             if(lmode && lmode[1]){
5260                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5261                 q = q.replace(lmode[1], "");
5262             }
5263             // strip leading slashes
5264             while(path.substr(0, 1)=="/"){
5265                 path = path.substr(1);
5266             }
5267
5268             while(q && lq != q){
5269                 lq = q;
5270                 var tm = q.match(tagTokenRe);
5271                 if(type == "select"){
5272                     if(tm){
5273                         if(tm[1] == "#"){
5274                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5275                         }else{
5276                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5277                         }
5278                         q = q.replace(tm[0], "");
5279                     }else if(q.substr(0, 1) != '@'){
5280                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5281                     }
5282                 }else{
5283                     if(tm){
5284                         if(tm[1] == "#"){
5285                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5286                         }else{
5287                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5288                         }
5289                         q = q.replace(tm[0], "");
5290                     }
5291                 }
5292                 while(!(mm = q.match(modeRe))){
5293                     var matched = false;
5294                     for(var j = 0; j < tklen; j++){
5295                         var t = tk[j];
5296                         var m = q.match(t.re);
5297                         if(m){
5298                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5299                                                     return m[i];
5300                                                 });
5301                             q = q.replace(m[0], "");
5302                             matched = true;
5303                             break;
5304                         }
5305                     }
5306                     // prevent infinite loop on bad selector
5307                     if(!matched){
5308                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5309                     }
5310                 }
5311                 if(mm[1]){
5312                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5313                     q = q.replace(mm[1], "");
5314                 }
5315             }
5316             fn[fn.length] = "return nodup(n);\n}";
5317             
5318              /** 
5319               * list of variables that need from compression as they are used by eval.
5320              *  eval:var:batch 
5321              *  eval:var:nodup
5322              *  eval:var:byTag
5323              *  eval:var:ById
5324              *  eval:var:getNodes
5325              *  eval:var:quickId
5326              *  eval:var:mode
5327              *  eval:var:root
5328              *  eval:var:n
5329              *  eval:var:byClassName
5330              *  eval:var:byPseudo
5331              *  eval:var:byAttribute
5332              *  eval:var:attrValue
5333              * 
5334              **/ 
5335             eval(fn.join(""));
5336             return f;
5337         },
5338
5339         /**
5340          * Selects a group of elements.
5341          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5342          * @param {Node} root (optional) The start of the query (defaults to document).
5343          * @return {Array}
5344          */
5345         select : function(path, root, type){
5346             if(!root || root == document){
5347                 root = document;
5348             }
5349             if(typeof root == "string"){
5350                 root = document.getElementById(root);
5351             }
5352             var paths = path.split(",");
5353             var results = [];
5354             for(var i = 0, len = paths.length; i < len; i++){
5355                 var p = paths[i].replace(trimRe, "");
5356                 if(!cache[p]){
5357                     cache[p] = Roo.DomQuery.compile(p);
5358                     if(!cache[p]){
5359                         throw p + " is not a valid selector";
5360                     }
5361                 }
5362                 var result = cache[p](root);
5363                 if(result && result != document){
5364                     results = results.concat(result);
5365                 }
5366             }
5367             if(paths.length > 1){
5368                 return nodup(results);
5369             }
5370             return results;
5371         },
5372
5373         /**
5374          * Selects a single element.
5375          * @param {String} selector The selector/xpath query
5376          * @param {Node} root (optional) The start of the query (defaults to document).
5377          * @return {Element}
5378          */
5379         selectNode : function(path, root){
5380             return Roo.DomQuery.select(path, root)[0];
5381         },
5382
5383         /**
5384          * Selects the value of a node, optionally replacing null with the defaultValue.
5385          * @param {String} selector The selector/xpath query
5386          * @param {Node} root (optional) The start of the query (defaults to document).
5387          * @param {String} defaultValue
5388          */
5389         selectValue : function(path, root, defaultValue){
5390             path = path.replace(trimRe, "");
5391             if(!valueCache[path]){
5392                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5393             }
5394             var n = valueCache[path](root);
5395             n = n[0] ? n[0] : n;
5396             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5397             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5398         },
5399
5400         /**
5401          * Selects the value of a node, parsing integers and floats.
5402          * @param {String} selector The selector/xpath query
5403          * @param {Node} root (optional) The start of the query (defaults to document).
5404          * @param {Number} defaultValue
5405          * @return {Number}
5406          */
5407         selectNumber : function(path, root, defaultValue){
5408             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5409             return parseFloat(v);
5410         },
5411
5412         /**
5413          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5414          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5415          * @param {String} selector The simple selector to test
5416          * @return {Boolean}
5417          */
5418         is : function(el, ss){
5419             if(typeof el == "string"){
5420                 el = document.getElementById(el);
5421             }
5422             var isArray = (el instanceof Array);
5423             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5424             return isArray ? (result.length == el.length) : (result.length > 0);
5425         },
5426
5427         /**
5428          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5429          * @param {Array} el An array of elements to filter
5430          * @param {String} selector The simple selector to test
5431          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5432          * the selector instead of the ones that match
5433          * @return {Array}
5434          */
5435         filter : function(els, ss, nonMatches){
5436             ss = ss.replace(trimRe, "");
5437             if(!simpleCache[ss]){
5438                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5439             }
5440             var result = simpleCache[ss](els);
5441             return nonMatches ? quickDiff(result, els) : result;
5442         },
5443
5444         /**
5445          * Collection of matching regular expressions and code snippets.
5446          */
5447         matchers : [{
5448                 re: /^\.([\w-]+)/,
5449                 select: 'n = byClassName(n, null, " {1} ");'
5450             }, {
5451                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5452                 select: 'n = byPseudo(n, "{1}", "{2}");'
5453             },{
5454                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5455                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5456             }, {
5457                 re: /^#([\w-]+)/,
5458                 select: 'n = byId(n, null, "{1}");'
5459             },{
5460                 re: /^@([\w-]+)/,
5461                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5462             }
5463         ],
5464
5465         /**
5466          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5467          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5468          */
5469         operators : {
5470             "=" : function(a, v){
5471                 return a == v;
5472             },
5473             "!=" : function(a, v){
5474                 return a != v;
5475             },
5476             "^=" : function(a, v){
5477                 return a && a.substr(0, v.length) == v;
5478             },
5479             "$=" : function(a, v){
5480                 return a && a.substr(a.length-v.length) == v;
5481             },
5482             "*=" : function(a, v){
5483                 return a && a.indexOf(v) !== -1;
5484             },
5485             "%=" : function(a, v){
5486                 return (a % v) == 0;
5487             },
5488             "|=" : function(a, v){
5489                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5490             },
5491             "~=" : function(a, v){
5492                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5493             }
5494         },
5495
5496         /**
5497          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5498          * and the argument (if any) supplied in the selector.
5499          */
5500         pseudos : {
5501             "first-child" : function(c){
5502                 var r = [], ri = -1, n;
5503                 for(var i = 0, ci; ci = n = c[i]; i++){
5504                     while((n = n.previousSibling) && n.nodeType != 1);
5505                     if(!n){
5506                         r[++ri] = ci;
5507                     }
5508                 }
5509                 return r;
5510             },
5511
5512             "last-child" : function(c){
5513                 var r = [], ri = -1, n;
5514                 for(var i = 0, ci; ci = n = c[i]; i++){
5515                     while((n = n.nextSibling) && n.nodeType != 1);
5516                     if(!n){
5517                         r[++ri] = ci;
5518                     }
5519                 }
5520                 return r;
5521             },
5522
5523             "nth-child" : function(c, a) {
5524                 var r = [], ri = -1;
5525                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5526                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5527                 for(var i = 0, n; n = c[i]; i++){
5528                     var pn = n.parentNode;
5529                     if (batch != pn._batch) {
5530                         var j = 0;
5531                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5532                             if(cn.nodeType == 1){
5533                                cn.nodeIndex = ++j;
5534                             }
5535                         }
5536                         pn._batch = batch;
5537                     }
5538                     if (f == 1) {
5539                         if (l == 0 || n.nodeIndex == l){
5540                             r[++ri] = n;
5541                         }
5542                     } else if ((n.nodeIndex + l) % f == 0){
5543                         r[++ri] = n;
5544                     }
5545                 }
5546
5547                 return r;
5548             },
5549
5550             "only-child" : function(c){
5551                 var r = [], ri = -1;;
5552                 for(var i = 0, ci; ci = c[i]; i++){
5553                     if(!prev(ci) && !next(ci)){
5554                         r[++ri] = ci;
5555                     }
5556                 }
5557                 return r;
5558             },
5559
5560             "empty" : function(c){
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     var cns = ci.childNodes, j = 0, cn, empty = true;
5564                     while(cn = cns[j]){
5565                         ++j;
5566                         if(cn.nodeType == 1 || cn.nodeType == 3){
5567                             empty = false;
5568                             break;
5569                         }
5570                     }
5571                     if(empty){
5572                         r[++ri] = ci;
5573                     }
5574                 }
5575                 return r;
5576             },
5577
5578             "contains" : function(c, v){
5579                 var r = [], ri = -1;
5580                 for(var i = 0, ci; ci = c[i]; i++){
5581                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5582                         r[++ri] = ci;
5583                     }
5584                 }
5585                 return r;
5586             },
5587
5588             "nodeValue" : function(c, v){
5589                 var r = [], ri = -1;
5590                 for(var i = 0, ci; ci = c[i]; i++){
5591                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5592                         r[++ri] = ci;
5593                     }
5594                 }
5595                 return r;
5596             },
5597
5598             "checked" : function(c){
5599                 var r = [], ri = -1;
5600                 for(var i = 0, ci; ci = c[i]; i++){
5601                     if(ci.checked == true){
5602                         r[++ri] = ci;
5603                     }
5604                 }
5605                 return r;
5606             },
5607
5608             "not" : function(c, ss){
5609                 return Roo.DomQuery.filter(c, ss, true);
5610             },
5611
5612             "odd" : function(c){
5613                 return this["nth-child"](c, "odd");
5614             },
5615
5616             "even" : function(c){
5617                 return this["nth-child"](c, "even");
5618             },
5619
5620             "nth" : function(c, a){
5621                 return c[a-1] || [];
5622             },
5623
5624             "first" : function(c){
5625                 return c[0] || [];
5626             },
5627
5628             "last" : function(c){
5629                 return c[c.length-1] || [];
5630             },
5631
5632             "has" : function(c, ss){
5633                 var s = Roo.DomQuery.select;
5634                 var r = [], ri = -1;
5635                 for(var i = 0, ci; ci = c[i]; i++){
5636                     if(s(ss, ci).length > 0){
5637                         r[++ri] = ci;
5638                     }
5639                 }
5640                 return r;
5641             },
5642
5643             "next" : function(c, ss){
5644                 var is = Roo.DomQuery.is;
5645                 var r = [], ri = -1;
5646                 for(var i = 0, ci; ci = c[i]; i++){
5647                     var n = next(ci);
5648                     if(n && is(n, ss)){
5649                         r[++ri] = ci;
5650                     }
5651                 }
5652                 return r;
5653             },
5654
5655             "prev" : function(c, ss){
5656                 var is = Roo.DomQuery.is;
5657                 var r = [], ri = -1;
5658                 for(var i = 0, ci; ci = c[i]; i++){
5659                     var n = prev(ci);
5660                     if(n && is(n, ss)){
5661                         r[++ri] = ci;
5662                     }
5663                 }
5664                 return r;
5665             }
5666         }
5667     };
5668 }();
5669
5670 /**
5671  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5672  * @param {String} path The selector/xpath query
5673  * @param {Node} root (optional) The start of the query (defaults to document).
5674  * @return {Array}
5675  * @member Roo
5676  * @method query
5677  */
5678 Roo.query = Roo.DomQuery.select;
5679 /*
5680  * Based on:
5681  * Ext JS Library 1.1.1
5682  * Copyright(c) 2006-2007, Ext JS, LLC.
5683  *
5684  * Originally Released Under LGPL - original licence link has changed is not relivant.
5685  *
5686  * Fork - LGPL
5687  * <script type="text/javascript">
5688  */
5689
5690 /**
5691  * @class Roo.util.Observable
5692  * Base class that provides a common interface for publishing events. Subclasses are expected to
5693  * to have a property "events" with all the events defined.<br>
5694  * For example:
5695  * <pre><code>
5696  Employee = function(name){
5697     this.name = name;
5698     this.addEvents({
5699         "fired" : true,
5700         "quit" : true
5701     });
5702  }
5703  Roo.extend(Employee, Roo.util.Observable);
5704 </code></pre>
5705  * @param {Object} config properties to use (incuding events / listeners)
5706  */
5707
5708 Roo.util.Observable = function(cfg){
5709     
5710     cfg = cfg|| {};
5711     this.addEvents(cfg.events || {});
5712     if (cfg.events) {
5713         delete cfg.events; // make sure
5714     }
5715      
5716     Roo.apply(this, cfg);
5717     
5718     if(this.listeners){
5719         this.on(this.listeners);
5720         delete this.listeners;
5721     }
5722 };
5723 Roo.util.Observable.prototype = {
5724     /** 
5725  * @cfg {Object} listeners  list of events and functions to call for this object, 
5726  * For example :
5727  * <pre><code>
5728     listeners :  { 
5729        'click' : function(e) {
5730            ..... 
5731         } ,
5732         .... 
5733     } 
5734   </code></pre>
5735  */
5736     
5737     
5738     /**
5739      * Fires the specified event with the passed parameters (minus the event name).
5740      * @param {String} eventName
5741      * @param {Object...} args Variable number of parameters are passed to handlers
5742      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5743      */
5744     fireEvent : function(){
5745         var ce = this.events[arguments[0].toLowerCase()];
5746         if(typeof ce == "object"){
5747             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5748         }else{
5749             return true;
5750         }
5751     },
5752
5753     // private
5754     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5755
5756     /**
5757      * Appends an event handler to this component
5758      * @param {String}   eventName The type of event to listen for
5759      * @param {Function} handler The method the event invokes
5760      * @param {Object}   scope (optional) The scope in which to execute the handler
5761      * function. The handler function's "this" context.
5762      * @param {Object}   options (optional) An object containing handler configuration
5763      * properties. This may contain any of the following properties:<ul>
5764      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5765      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5766      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5767      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5768      * by the specified number of milliseconds. If the event fires again within that time, the original
5769      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5770      * </ul><br>
5771      * <p>
5772      * <b>Combining Options</b><br>
5773      * Using the options argument, it is possible to combine different types of listeners:<br>
5774      * <br>
5775      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5776                 <pre><code>
5777                 el.on('click', this.onClick, this, {
5778                         single: true,
5779                 delay: 100,
5780                 forumId: 4
5781                 });
5782                 </code></pre>
5783      * <p>
5784      * <b>Attaching multiple handlers in 1 call</b><br>
5785      * The method also allows for a single argument to be passed which is a config object containing properties
5786      * which specify multiple handlers.
5787      * <pre><code>
5788                 el.on({
5789                         'click': {
5790                         fn: this.onClick,
5791                         scope: this,
5792                         delay: 100
5793                 }, 
5794                 'mouseover': {
5795                         fn: this.onMouseOver,
5796                         scope: this
5797                 },
5798                 'mouseout': {
5799                         fn: this.onMouseOut,
5800                         scope: this
5801                 }
5802                 });
5803                 </code></pre>
5804      * <p>
5805      * Or a shorthand syntax which passes the same scope object to all handlers:
5806         <pre><code>
5807                 el.on({
5808                         'click': this.onClick,
5809                 'mouseover': this.onMouseOver,
5810                 'mouseout': this.onMouseOut,
5811                 scope: this
5812                 });
5813                 </code></pre>
5814      */
5815     addListener : function(eventName, fn, scope, o){
5816         if(typeof eventName == "object"){
5817             o = eventName;
5818             for(var e in o){
5819                 if(this.filterOptRe.test(e)){
5820                     continue;
5821                 }
5822                 if(typeof o[e] == "function"){
5823                     // shared options
5824                     this.addListener(e, o[e], o.scope,  o);
5825                 }else{
5826                     // individual options
5827                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5828                 }
5829             }
5830             return;
5831         }
5832         o = (!o || typeof o == "boolean") ? {} : o;
5833         eventName = eventName.toLowerCase();
5834         var ce = this.events[eventName] || true;
5835         if(typeof ce == "boolean"){
5836             ce = new Roo.util.Event(this, eventName);
5837             this.events[eventName] = ce;
5838         }
5839         ce.addListener(fn, scope, o);
5840     },
5841
5842     /**
5843      * Removes a listener
5844      * @param {String}   eventName     The type of event to listen for
5845      * @param {Function} handler        The handler to remove
5846      * @param {Object}   scope  (optional) The scope (this object) for the handler
5847      */
5848     removeListener : function(eventName, fn, scope){
5849         var ce = this.events[eventName.toLowerCase()];
5850         if(typeof ce == "object"){
5851             ce.removeListener(fn, scope);
5852         }
5853     },
5854
5855     /**
5856      * Removes all listeners for this object
5857      */
5858     purgeListeners : function(){
5859         for(var evt in this.events){
5860             if(typeof this.events[evt] == "object"){
5861                  this.events[evt].clearListeners();
5862             }
5863         }
5864     },
5865
5866     relayEvents : function(o, events){
5867         var createHandler = function(ename){
5868             return function(){
5869                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5870             };
5871         };
5872         for(var i = 0, len = events.length; i < len; i++){
5873             var ename = events[i];
5874             if(!this.events[ename]){ this.events[ename] = true; };
5875             o.on(ename, createHandler(ename), this);
5876         }
5877     },
5878
5879     /**
5880      * Used to define events on this Observable
5881      * @param {Object} object The object with the events defined
5882      */
5883     addEvents : function(o){
5884         if(!this.events){
5885             this.events = {};
5886         }
5887         Roo.applyIf(this.events, o);
5888     },
5889
5890     /**
5891      * Checks to see if this object has any listeners for a specified event
5892      * @param {String} eventName The name of the event to check for
5893      * @return {Boolean} True if the event is being listened for, else false
5894      */
5895     hasListener : function(eventName){
5896         var e = this.events[eventName];
5897         return typeof e == "object" && e.listeners.length > 0;
5898     }
5899 };
5900 /**
5901  * Appends an event handler to this element (shorthand for addListener)
5902  * @param {String}   eventName     The type of event to listen for
5903  * @param {Function} handler        The method the event invokes
5904  * @param {Object}   scope (optional) The scope in which to execute the handler
5905  * function. The handler function's "this" context.
5906  * @param {Object}   options  (optional)
5907  * @method
5908  */
5909 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5910 /**
5911  * Removes a listener (shorthand for removeListener)
5912  * @param {String}   eventName     The type of event to listen for
5913  * @param {Function} handler        The handler to remove
5914  * @param {Object}   scope  (optional) The scope (this object) for the handler
5915  * @method
5916  */
5917 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5918
5919 /**
5920  * Starts capture on the specified Observable. All events will be passed
5921  * to the supplied function with the event name + standard signature of the event
5922  * <b>before</b> the event is fired. If the supplied function returns false,
5923  * the event will not fire.
5924  * @param {Observable} o The Observable to capture
5925  * @param {Function} fn The function to call
5926  * @param {Object} scope (optional) The scope (this object) for the fn
5927  * @static
5928  */
5929 Roo.util.Observable.capture = function(o, fn, scope){
5930     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5931 };
5932
5933 /**
5934  * Removes <b>all</b> added captures from the Observable.
5935  * @param {Observable} o The Observable to release
5936  * @static
5937  */
5938 Roo.util.Observable.releaseCapture = function(o){
5939     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5940 };
5941
5942 (function(){
5943
5944     var createBuffered = function(h, o, scope){
5945         var task = new Roo.util.DelayedTask();
5946         return function(){
5947             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5948         };
5949     };
5950
5951     var createSingle = function(h, e, fn, scope){
5952         return function(){
5953             e.removeListener(fn, scope);
5954             return h.apply(scope, arguments);
5955         };
5956     };
5957
5958     var createDelayed = function(h, o, scope){
5959         return function(){
5960             var args = Array.prototype.slice.call(arguments, 0);
5961             setTimeout(function(){
5962                 h.apply(scope, args);
5963             }, o.delay || 10);
5964         };
5965     };
5966
5967     Roo.util.Event = function(obj, name){
5968         this.name = name;
5969         this.obj = obj;
5970         this.listeners = [];
5971     };
5972
5973     Roo.util.Event.prototype = {
5974         addListener : function(fn, scope, options){
5975             var o = options || {};
5976             scope = scope || this.obj;
5977             if(!this.isListening(fn, scope)){
5978                 var l = {fn: fn, scope: scope, options: o};
5979                 var h = fn;
5980                 if(o.delay){
5981                     h = createDelayed(h, o, scope);
5982                 }
5983                 if(o.single){
5984                     h = createSingle(h, this, fn, scope);
5985                 }
5986                 if(o.buffer){
5987                     h = createBuffered(h, o, scope);
5988                 }
5989                 l.fireFn = h;
5990                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5991                     this.listeners.push(l);
5992                 }else{
5993                     this.listeners = this.listeners.slice(0);
5994                     this.listeners.push(l);
5995                 }
5996             }
5997         },
5998
5999         findListener : function(fn, scope){
6000             scope = scope || this.obj;
6001             var ls = this.listeners;
6002             for(var i = 0, len = ls.length; i < len; i++){
6003                 var l = ls[i];
6004                 if(l.fn == fn && l.scope == scope){
6005                     return i;
6006                 }
6007             }
6008             return -1;
6009         },
6010
6011         isListening : function(fn, scope){
6012             return this.findListener(fn, scope) != -1;
6013         },
6014
6015         removeListener : function(fn, scope){
6016             var index;
6017             if((index = this.findListener(fn, scope)) != -1){
6018                 if(!this.firing){
6019                     this.listeners.splice(index, 1);
6020                 }else{
6021                     this.listeners = this.listeners.slice(0);
6022                     this.listeners.splice(index, 1);
6023                 }
6024                 return true;
6025             }
6026             return false;
6027         },
6028
6029         clearListeners : function(){
6030             this.listeners = [];
6031         },
6032
6033         fire : function(){
6034             var ls = this.listeners, scope, len = ls.length;
6035             if(len > 0){
6036                 this.firing = true;
6037                 var args = Array.prototype.slice.call(arguments, 0);
6038                 for(var i = 0; i < len; i++){
6039                     var l = ls[i];
6040                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6041                         this.firing = false;
6042                         return false;
6043                     }
6044                 }
6045                 this.firing = false;
6046             }
6047             return true;
6048         }
6049     };
6050 })();/*
6051  * Based on:
6052  * Ext JS Library 1.1.1
6053  * Copyright(c) 2006-2007, Ext JS, LLC.
6054  *
6055  * Originally Released Under LGPL - original licence link has changed is not relivant.
6056  *
6057  * Fork - LGPL
6058  * <script type="text/javascript">
6059  */
6060
6061 /**
6062  * @class Roo.EventManager
6063  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6064  * several useful events directly.
6065  * See {@link Roo.EventObject} for more details on normalized event objects.
6066  * @singleton
6067  */
6068 Roo.EventManager = function(){
6069     var docReadyEvent, docReadyProcId, docReadyState = false;
6070     var resizeEvent, resizeTask, textEvent, textSize;
6071     var E = Roo.lib.Event;
6072     var D = Roo.lib.Dom;
6073
6074     
6075     
6076
6077     var fireDocReady = function(){
6078         if(!docReadyState){
6079             docReadyState = true;
6080             Roo.isReady = true;
6081             if(docReadyProcId){
6082                 clearInterval(docReadyProcId);
6083             }
6084             if(Roo.isGecko || Roo.isOpera) {
6085                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6086             }
6087             if(Roo.isIE){
6088                 var defer = document.getElementById("ie-deferred-loader");
6089                 if(defer){
6090                     defer.onreadystatechange = null;
6091                     defer.parentNode.removeChild(defer);
6092                 }
6093             }
6094             if(docReadyEvent){
6095                 docReadyEvent.fire();
6096                 docReadyEvent.clearListeners();
6097             }
6098         }
6099     };
6100     
6101     var initDocReady = function(){
6102         docReadyEvent = new Roo.util.Event();
6103         if(Roo.isGecko || Roo.isOpera) {
6104             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6105         }else if(Roo.isIE){
6106             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6107             var defer = document.getElementById("ie-deferred-loader");
6108             defer.onreadystatechange = function(){
6109                 if(this.readyState == "complete"){
6110                     fireDocReady();
6111                 }
6112             };
6113         }else if(Roo.isSafari){ 
6114             docReadyProcId = setInterval(function(){
6115                 var rs = document.readyState;
6116                 if(rs == "complete") {
6117                     fireDocReady();     
6118                  }
6119             }, 10);
6120         }
6121         // no matter what, make sure it fires on load
6122         E.on(window, "load", fireDocReady);
6123     };
6124
6125     var createBuffered = function(h, o){
6126         var task = new Roo.util.DelayedTask(h);
6127         return function(e){
6128             // create new event object impl so new events don't wipe out properties
6129             e = new Roo.EventObjectImpl(e);
6130             task.delay(o.buffer, h, null, [e]);
6131         };
6132     };
6133
6134     var createSingle = function(h, el, ename, fn){
6135         return function(e){
6136             Roo.EventManager.removeListener(el, ename, fn);
6137             h(e);
6138         };
6139     };
6140
6141     var createDelayed = function(h, o){
6142         return function(e){
6143             // create new event object impl so new events don't wipe out properties
6144             e = new Roo.EventObjectImpl(e);
6145             setTimeout(function(){
6146                 h(e);
6147             }, o.delay || 10);
6148         };
6149     };
6150     var transitionEndVal = false;
6151     
6152     var transitionEnd = function()
6153     {
6154         if (transitionEndVal) {
6155             return transitionEndVal;
6156         }
6157         var el = document.createElement('div');
6158
6159         var transEndEventNames = {
6160             WebkitTransition : 'webkitTransitionEnd',
6161             MozTransition    : 'transitionend',
6162             OTransition      : 'oTransitionEnd otransitionend',
6163             transition       : 'transitionend'
6164         };
6165     
6166         for (var name in transEndEventNames) {
6167             if (el.style[name] !== undefined) {
6168                 transitionEndVal = transEndEventNames[name];
6169                 return  transitionEndVal ;
6170             }
6171         }
6172     }
6173     
6174
6175     var listen = function(element, ename, opt, fn, scope){
6176         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6177         fn = fn || o.fn; scope = scope || o.scope;
6178         var el = Roo.getDom(element);
6179         
6180         
6181         if(!el){
6182             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6183         }
6184         
6185         if (ename == 'transitionend') {
6186             ename = transitionEnd();
6187         }
6188         var h = function(e){
6189             e = Roo.EventObject.setEvent(e);
6190             var t;
6191             if(o.delegate){
6192                 t = e.getTarget(o.delegate, el);
6193                 if(!t){
6194                     return;
6195                 }
6196             }else{
6197                 t = e.target;
6198             }
6199             if(o.stopEvent === true){
6200                 e.stopEvent();
6201             }
6202             if(o.preventDefault === true){
6203                e.preventDefault();
6204             }
6205             if(o.stopPropagation === true){
6206                 e.stopPropagation();
6207             }
6208
6209             if(o.normalized === false){
6210                 e = e.browserEvent;
6211             }
6212
6213             fn.call(scope || el, e, t, o);
6214         };
6215         if(o.delay){
6216             h = createDelayed(h, o);
6217         }
6218         if(o.single){
6219             h = createSingle(h, el, ename, fn);
6220         }
6221         if(o.buffer){
6222             h = createBuffered(h, o);
6223         }
6224         fn._handlers = fn._handlers || [];
6225         
6226         
6227         fn._handlers.push([Roo.id(el), ename, h]);
6228         
6229         
6230          
6231         E.on(el, ename, h);
6232         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6233             el.addEventListener("DOMMouseScroll", h, false);
6234             E.on(window, 'unload', function(){
6235                 el.removeEventListener("DOMMouseScroll", h, false);
6236             });
6237         }
6238         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6239             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6240         }
6241         return h;
6242     };
6243
6244     var stopListening = function(el, ename, fn){
6245         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6246         if(hds){
6247             for(var i = 0, len = hds.length; i < len; i++){
6248                 var h = hds[i];
6249                 if(h[0] == id && h[1] == ename){
6250                     hd = h[2];
6251                     hds.splice(i, 1);
6252                     break;
6253                 }
6254             }
6255         }
6256         E.un(el, ename, hd);
6257         el = Roo.getDom(el);
6258         if(ename == "mousewheel" && el.addEventListener){
6259             el.removeEventListener("DOMMouseScroll", hd, false);
6260         }
6261         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6262             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6263         }
6264     };
6265
6266     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6267     
6268     var pub = {
6269         
6270         
6271         /** 
6272          * Fix for doc tools
6273          * @scope Roo.EventManager
6274          */
6275         
6276         
6277         /** 
6278          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6279          * object with a Roo.EventObject
6280          * @param {Function} fn        The method the event invokes
6281          * @param {Object}   scope    An object that becomes the scope of the handler
6282          * @param {boolean}  override If true, the obj passed in becomes
6283          *                             the execution scope of the listener
6284          * @return {Function} The wrapped function
6285          * @deprecated
6286          */
6287         wrap : function(fn, scope, override){
6288             return function(e){
6289                 Roo.EventObject.setEvent(e);
6290                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6291             };
6292         },
6293         
6294         /**
6295      * Appends an event handler to an element (shorthand for addListener)
6296      * @param {String/HTMLElement}   element        The html element or id to assign the
6297      * @param {String}   eventName The type of event to listen for
6298      * @param {Function} handler The method the event invokes
6299      * @param {Object}   scope (optional) The scope in which to execute the handler
6300      * function. The handler function's "this" context.
6301      * @param {Object}   options (optional) An object containing handler configuration
6302      * properties. This may contain any of the following properties:<ul>
6303      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6304      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6305      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6306      * <li>preventDefault {Boolean} True to prevent the default action</li>
6307      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6308      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6309      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6310      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6311      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6312      * by the specified number of milliseconds. If the event fires again within that time, the original
6313      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6314      * </ul><br>
6315      * <p>
6316      * <b>Combining Options</b><br>
6317      * Using the options argument, it is possible to combine different types of listeners:<br>
6318      * <br>
6319      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6320      * Code:<pre><code>
6321 el.on('click', this.onClick, this, {
6322     single: true,
6323     delay: 100,
6324     stopEvent : true,
6325     forumId: 4
6326 });</code></pre>
6327      * <p>
6328      * <b>Attaching multiple handlers in 1 call</b><br>
6329       * The method also allows for a single argument to be passed which is a config object containing properties
6330      * which specify multiple handlers.
6331      * <p>
6332      * Code:<pre><code>
6333 el.on({
6334     'click' : {
6335         fn: this.onClick
6336         scope: this,
6337         delay: 100
6338     },
6339     'mouseover' : {
6340         fn: this.onMouseOver
6341         scope: this
6342     },
6343     'mouseout' : {
6344         fn: this.onMouseOut
6345         scope: this
6346     }
6347 });</code></pre>
6348      * <p>
6349      * Or a shorthand syntax:<br>
6350      * Code:<pre><code>
6351 el.on({
6352     'click' : this.onClick,
6353     'mouseover' : this.onMouseOver,
6354     'mouseout' : this.onMouseOut
6355     scope: this
6356 });</code></pre>
6357      */
6358         addListener : function(element, eventName, fn, scope, options){
6359             if(typeof eventName == "object"){
6360                 var o = eventName;
6361                 for(var e in o){
6362                     if(propRe.test(e)){
6363                         continue;
6364                     }
6365                     if(typeof o[e] == "function"){
6366                         // shared options
6367                         listen(element, e, o, o[e], o.scope);
6368                     }else{
6369                         // individual options
6370                         listen(element, e, o[e]);
6371                     }
6372                 }
6373                 return;
6374             }
6375             return listen(element, eventName, options, fn, scope);
6376         },
6377         
6378         /**
6379          * Removes an event handler
6380          *
6381          * @param {String/HTMLElement}   element        The id or html element to remove the 
6382          *                             event from
6383          * @param {String}   eventName     The type of event
6384          * @param {Function} fn
6385          * @return {Boolean} True if a listener was actually removed
6386          */
6387         removeListener : function(element, eventName, fn){
6388             return stopListening(element, eventName, fn);
6389         },
6390         
6391         /**
6392          * Fires when the document is ready (before onload and before images are loaded). Can be 
6393          * accessed shorthanded Roo.onReady().
6394          * @param {Function} fn        The method the event invokes
6395          * @param {Object}   scope    An  object that becomes the scope of the handler
6396          * @param {boolean}  options
6397          */
6398         onDocumentReady : function(fn, scope, options){
6399             if(docReadyState){ // if it already fired
6400                 docReadyEvent.addListener(fn, scope, options);
6401                 docReadyEvent.fire();
6402                 docReadyEvent.clearListeners();
6403                 return;
6404             }
6405             if(!docReadyEvent){
6406                 initDocReady();
6407             }
6408             docReadyEvent.addListener(fn, scope, options);
6409         },
6410         
6411         /**
6412          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6413          * @param {Function} fn        The method the event invokes
6414          * @param {Object}   scope    An object that becomes the scope of the handler
6415          * @param {boolean}  options
6416          */
6417         onWindowResize : function(fn, scope, options){
6418             if(!resizeEvent){
6419                 resizeEvent = new Roo.util.Event();
6420                 resizeTask = new Roo.util.DelayedTask(function(){
6421                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6422                 });
6423                 E.on(window, "resize", function(){
6424                     if(Roo.isIE){
6425                         resizeTask.delay(50);
6426                     }else{
6427                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6428                     }
6429                 });
6430             }
6431             resizeEvent.addListener(fn, scope, options);
6432         },
6433
6434         /**
6435          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6436          * @param {Function} fn        The method the event invokes
6437          * @param {Object}   scope    An object that becomes the scope of the handler
6438          * @param {boolean}  options
6439          */
6440         onTextResize : function(fn, scope, options){
6441             if(!textEvent){
6442                 textEvent = new Roo.util.Event();
6443                 var textEl = new Roo.Element(document.createElement('div'));
6444                 textEl.dom.className = 'x-text-resize';
6445                 textEl.dom.innerHTML = 'X';
6446                 textEl.appendTo(document.body);
6447                 textSize = textEl.dom.offsetHeight;
6448                 setInterval(function(){
6449                     if(textEl.dom.offsetHeight != textSize){
6450                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6451                     }
6452                 }, this.textResizeInterval);
6453             }
6454             textEvent.addListener(fn, scope, options);
6455         },
6456
6457         /**
6458          * Removes the passed window resize listener.
6459          * @param {Function} fn        The method the event invokes
6460          * @param {Object}   scope    The scope of handler
6461          */
6462         removeResizeListener : function(fn, scope){
6463             if(resizeEvent){
6464                 resizeEvent.removeListener(fn, scope);
6465             }
6466         },
6467
6468         // private
6469         fireResize : function(){
6470             if(resizeEvent){
6471                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6472             }   
6473         },
6474         /**
6475          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6476          */
6477         ieDeferSrc : false,
6478         /**
6479          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6480          */
6481         textResizeInterval : 50
6482     };
6483     
6484     /**
6485      * Fix for doc tools
6486      * @scopeAlias pub=Roo.EventManager
6487      */
6488     
6489      /**
6490      * Appends an event handler to an element (shorthand for addListener)
6491      * @param {String/HTMLElement}   element        The html element or id to assign the
6492      * @param {String}   eventName The type of event to listen for
6493      * @param {Function} handler The method the event invokes
6494      * @param {Object}   scope (optional) The scope in which to execute the handler
6495      * function. The handler function's "this" context.
6496      * @param {Object}   options (optional) An object containing handler configuration
6497      * properties. This may contain any of the following properties:<ul>
6498      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6499      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6500      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6501      * <li>preventDefault {Boolean} True to prevent the default action</li>
6502      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6503      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6504      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6505      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6506      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6507      * by the specified number of milliseconds. If the event fires again within that time, the original
6508      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6509      * </ul><br>
6510      * <p>
6511      * <b>Combining Options</b><br>
6512      * Using the options argument, it is possible to combine different types of listeners:<br>
6513      * <br>
6514      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6515      * Code:<pre><code>
6516 el.on('click', this.onClick, this, {
6517     single: true,
6518     delay: 100,
6519     stopEvent : true,
6520     forumId: 4
6521 });</code></pre>
6522      * <p>
6523      * <b>Attaching multiple handlers in 1 call</b><br>
6524       * The method also allows for a single argument to be passed which is a config object containing properties
6525      * which specify multiple handlers.
6526      * <p>
6527      * Code:<pre><code>
6528 el.on({
6529     'click' : {
6530         fn: this.onClick
6531         scope: this,
6532         delay: 100
6533     },
6534     'mouseover' : {
6535         fn: this.onMouseOver
6536         scope: this
6537     },
6538     'mouseout' : {
6539         fn: this.onMouseOut
6540         scope: this
6541     }
6542 });</code></pre>
6543      * <p>
6544      * Or a shorthand syntax:<br>
6545      * Code:<pre><code>
6546 el.on({
6547     'click' : this.onClick,
6548     'mouseover' : this.onMouseOver,
6549     'mouseout' : this.onMouseOut
6550     scope: this
6551 });</code></pre>
6552      */
6553     pub.on = pub.addListener;
6554     pub.un = pub.removeListener;
6555
6556     pub.stoppedMouseDownEvent = new Roo.util.Event();
6557     return pub;
6558 }();
6559 /**
6560   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6561   * @param {Function} fn        The method the event invokes
6562   * @param {Object}   scope    An  object that becomes the scope of the handler
6563   * @param {boolean}  override If true, the obj passed in becomes
6564   *                             the execution scope of the listener
6565   * @member Roo
6566   * @method onReady
6567  */
6568 Roo.onReady = Roo.EventManager.onDocumentReady;
6569
6570 Roo.onReady(function(){
6571     var bd = Roo.get(document.body);
6572     if(!bd){ return; }
6573
6574     var cls = [
6575             Roo.isIE ? "roo-ie"
6576             : Roo.isGecko ? "roo-gecko"
6577             : Roo.isOpera ? "roo-opera"
6578             : Roo.isSafari ? "roo-safari" : ""];
6579
6580     if(Roo.isMac){
6581         cls.push("roo-mac");
6582     }
6583     if(Roo.isLinux){
6584         cls.push("roo-linux");
6585     }
6586     if(Roo.isIOS){
6587         cls.push("roo-ios");
6588     }
6589     if(Roo.isTouch){
6590         cls.push("roo-touch");
6591     }
6592     if(Roo.isBorderBox){
6593         cls.push('roo-border-box');
6594     }
6595     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6596         var p = bd.dom.parentNode;
6597         if(p){
6598             p.className += ' roo-strict';
6599         }
6600     }
6601     bd.addClass(cls.join(' '));
6602 });
6603
6604 /**
6605  * @class Roo.EventObject
6606  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6607  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6608  * Example:
6609  * <pre><code>
6610  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6611     e.preventDefault();
6612     var target = e.getTarget();
6613     ...
6614  }
6615  var myDiv = Roo.get("myDiv");
6616  myDiv.on("click", handleClick);
6617  //or
6618  Roo.EventManager.on("myDiv", 'click', handleClick);
6619  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6620  </code></pre>
6621  * @singleton
6622  */
6623 Roo.EventObject = function(){
6624     
6625     var E = Roo.lib.Event;
6626     
6627     // safari keypress events for special keys return bad keycodes
6628     var safariKeys = {
6629         63234 : 37, // left
6630         63235 : 39, // right
6631         63232 : 38, // up
6632         63233 : 40, // down
6633         63276 : 33, // page up
6634         63277 : 34, // page down
6635         63272 : 46, // delete
6636         63273 : 36, // home
6637         63275 : 35  // end
6638     };
6639
6640     // normalize button clicks
6641     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6642                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6643
6644     Roo.EventObjectImpl = function(e){
6645         if(e){
6646             this.setEvent(e.browserEvent || e);
6647         }
6648     };
6649     Roo.EventObjectImpl.prototype = {
6650         /**
6651          * Used to fix doc tools.
6652          * @scope Roo.EventObject.prototype
6653          */
6654             
6655
6656         
6657         
6658         /** The normal browser event */
6659         browserEvent : null,
6660         /** The button pressed in a mouse event */
6661         button : -1,
6662         /** True if the shift key was down during the event */
6663         shiftKey : false,
6664         /** True if the control key was down during the event */
6665         ctrlKey : false,
6666         /** True if the alt key was down during the event */
6667         altKey : false,
6668
6669         /** Key constant 
6670         * @type Number */
6671         BACKSPACE : 8,
6672         /** Key constant 
6673         * @type Number */
6674         TAB : 9,
6675         /** Key constant 
6676         * @type Number */
6677         RETURN : 13,
6678         /** Key constant 
6679         * @type Number */
6680         ENTER : 13,
6681         /** Key constant 
6682         * @type Number */
6683         SHIFT : 16,
6684         /** Key constant 
6685         * @type Number */
6686         CONTROL : 17,
6687         /** Key constant 
6688         * @type Number */
6689         ESC : 27,
6690         /** Key constant 
6691         * @type Number */
6692         SPACE : 32,
6693         /** Key constant 
6694         * @type Number */
6695         PAGEUP : 33,
6696         /** Key constant 
6697         * @type Number */
6698         PAGEDOWN : 34,
6699         /** Key constant 
6700         * @type Number */
6701         END : 35,
6702         /** Key constant 
6703         * @type Number */
6704         HOME : 36,
6705         /** Key constant 
6706         * @type Number */
6707         LEFT : 37,
6708         /** Key constant 
6709         * @type Number */
6710         UP : 38,
6711         /** Key constant 
6712         * @type Number */
6713         RIGHT : 39,
6714         /** Key constant 
6715         * @type Number */
6716         DOWN : 40,
6717         /** Key constant 
6718         * @type Number */
6719         DELETE : 46,
6720         /** Key constant 
6721         * @type Number */
6722         F5 : 116,
6723
6724            /** @private */
6725         setEvent : function(e){
6726             if(e == this || (e && e.browserEvent)){ // already wrapped
6727                 return e;
6728             }
6729             this.browserEvent = e;
6730             if(e){
6731                 // normalize buttons
6732                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6733                 if(e.type == 'click' && this.button == -1){
6734                     this.button = 0;
6735                 }
6736                 this.type = e.type;
6737                 this.shiftKey = e.shiftKey;
6738                 // mac metaKey behaves like ctrlKey
6739                 this.ctrlKey = e.ctrlKey || e.metaKey;
6740                 this.altKey = e.altKey;
6741                 // in getKey these will be normalized for the mac
6742                 this.keyCode = e.keyCode;
6743                 // keyup warnings on firefox.
6744                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6745                 // cache the target for the delayed and or buffered events
6746                 this.target = E.getTarget(e);
6747                 // same for XY
6748                 this.xy = E.getXY(e);
6749             }else{
6750                 this.button = -1;
6751                 this.shiftKey = false;
6752                 this.ctrlKey = false;
6753                 this.altKey = false;
6754                 this.keyCode = 0;
6755                 this.charCode =0;
6756                 this.target = null;
6757                 this.xy = [0, 0];
6758             }
6759             return this;
6760         },
6761
6762         /**
6763          * Stop the event (preventDefault and stopPropagation)
6764          */
6765         stopEvent : function(){
6766             if(this.browserEvent){
6767                 if(this.browserEvent.type == 'mousedown'){
6768                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6769                 }
6770                 E.stopEvent(this.browserEvent);
6771             }
6772         },
6773
6774         /**
6775          * Prevents the browsers default handling of the event.
6776          */
6777         preventDefault : function(){
6778             if(this.browserEvent){
6779                 E.preventDefault(this.browserEvent);
6780             }
6781         },
6782
6783         /** @private */
6784         isNavKeyPress : function(){
6785             var k = this.keyCode;
6786             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6787             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6788         },
6789
6790         isSpecialKey : function(){
6791             var k = this.keyCode;
6792             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6793             (k == 16) || (k == 17) ||
6794             (k >= 18 && k <= 20) ||
6795             (k >= 33 && k <= 35) ||
6796             (k >= 36 && k <= 39) ||
6797             (k >= 44 && k <= 45);
6798         },
6799         /**
6800          * Cancels bubbling of the event.
6801          */
6802         stopPropagation : function(){
6803             if(this.browserEvent){
6804                 if(this.type == 'mousedown'){
6805                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6806                 }
6807                 E.stopPropagation(this.browserEvent);
6808             }
6809         },
6810
6811         /**
6812          * Gets the key code for the event.
6813          * @return {Number}
6814          */
6815         getCharCode : function(){
6816             return this.charCode || this.keyCode;
6817         },
6818
6819         /**
6820          * Returns a normalized keyCode for the event.
6821          * @return {Number} The key code
6822          */
6823         getKey : function(){
6824             var k = this.keyCode || this.charCode;
6825             return Roo.isSafari ? (safariKeys[k] || k) : k;
6826         },
6827
6828         /**
6829          * Gets the x coordinate of the event.
6830          * @return {Number}
6831          */
6832         getPageX : function(){
6833             return this.xy[0];
6834         },
6835
6836         /**
6837          * Gets the y coordinate of the event.
6838          * @return {Number}
6839          */
6840         getPageY : function(){
6841             return this.xy[1];
6842         },
6843
6844         /**
6845          * Gets the time of the event.
6846          * @return {Number}
6847          */
6848         getTime : function(){
6849             if(this.browserEvent){
6850                 return E.getTime(this.browserEvent);
6851             }
6852             return null;
6853         },
6854
6855         /**
6856          * Gets the page coordinates of the event.
6857          * @return {Array} The xy values like [x, y]
6858          */
6859         getXY : function(){
6860             return this.xy;
6861         },
6862
6863         /**
6864          * Gets the target for the event.
6865          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6866          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6867                 search as a number or element (defaults to 10 || document.body)
6868          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6869          * @return {HTMLelement}
6870          */
6871         getTarget : function(selector, maxDepth, returnEl){
6872             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6873         },
6874         /**
6875          * Gets the related target.
6876          * @return {HTMLElement}
6877          */
6878         getRelatedTarget : function(){
6879             if(this.browserEvent){
6880                 return E.getRelatedTarget(this.browserEvent);
6881             }
6882             return null;
6883         },
6884
6885         /**
6886          * Normalizes mouse wheel delta across browsers
6887          * @return {Number} The delta
6888          */
6889         getWheelDelta : function(){
6890             var e = this.browserEvent;
6891             var delta = 0;
6892             if(e.wheelDelta){ /* IE/Opera. */
6893                 delta = e.wheelDelta/120;
6894             }else if(e.detail){ /* Mozilla case. */
6895                 delta = -e.detail/3;
6896             }
6897             return delta;
6898         },
6899
6900         /**
6901          * Returns true if the control, meta, shift or alt key was pressed during this event.
6902          * @return {Boolean}
6903          */
6904         hasModifier : function(){
6905             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6906         },
6907
6908         /**
6909          * Returns true if the target of this event equals el or is a child of el
6910          * @param {String/HTMLElement/Element} el
6911          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6912          * @return {Boolean}
6913          */
6914         within : function(el, related){
6915             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6916             return t && Roo.fly(el).contains(t);
6917         },
6918
6919         getPoint : function(){
6920             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6921         }
6922     };
6923
6924     return new Roo.EventObjectImpl();
6925 }();
6926             
6927     /*
6928  * Based on:
6929  * Ext JS Library 1.1.1
6930  * Copyright(c) 2006-2007, Ext JS, LLC.
6931  *
6932  * Originally Released Under LGPL - original licence link has changed is not relivant.
6933  *
6934  * Fork - LGPL
6935  * <script type="text/javascript">
6936  */
6937
6938  
6939 // was in Composite Element!??!?!
6940  
6941 (function(){
6942     var D = Roo.lib.Dom;
6943     var E = Roo.lib.Event;
6944     var A = Roo.lib.Anim;
6945
6946     // local style camelizing for speed
6947     var propCache = {};
6948     var camelRe = /(-[a-z])/gi;
6949     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6950     var view = document.defaultView;
6951
6952 /**
6953  * @class Roo.Element
6954  * Represents an Element in the DOM.<br><br>
6955  * Usage:<br>
6956 <pre><code>
6957 var el = Roo.get("my-div");
6958
6959 // or with getEl
6960 var el = getEl("my-div");
6961
6962 // or with a DOM element
6963 var el = Roo.get(myDivElement);
6964 </code></pre>
6965  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6966  * each call instead of constructing a new one.<br><br>
6967  * <b>Animations</b><br />
6968  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6969  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6970 <pre>
6971 Option    Default   Description
6972 --------- --------  ---------------------------------------------
6973 duration  .35       The duration of the animation in seconds
6974 easing    easeOut   The YUI easing method
6975 callback  none      A function to execute when the anim completes
6976 scope     this      The scope (this) of the callback function
6977 </pre>
6978 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6979 * manipulate the animation. Here's an example:
6980 <pre><code>
6981 var el = Roo.get("my-div");
6982
6983 // no animation
6984 el.setWidth(100);
6985
6986 // default animation
6987 el.setWidth(100, true);
6988
6989 // animation with some options set
6990 el.setWidth(100, {
6991     duration: 1,
6992     callback: this.foo,
6993     scope: this
6994 });
6995
6996 // using the "anim" property to get the Anim object
6997 var opt = {
6998     duration: 1,
6999     callback: this.foo,
7000     scope: this
7001 };
7002 el.setWidth(100, opt);
7003 ...
7004 if(opt.anim.isAnimated()){
7005     opt.anim.stop();
7006 }
7007 </code></pre>
7008 * <b> Composite (Collections of) Elements</b><br />
7009  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7010  * @constructor Create a new Element directly.
7011  * @param {String/HTMLElement} element
7012  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
7013  */
7014     Roo.Element = function(element, forceNew){
7015         var dom = typeof element == "string" ?
7016                 document.getElementById(element) : element;
7017         if(!dom){ // invalid id/element
7018             return null;
7019         }
7020         var id = dom.id;
7021         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7022             return Roo.Element.cache[id];
7023         }
7024
7025         /**
7026          * The DOM element
7027          * @type HTMLElement
7028          */
7029         this.dom = dom;
7030
7031         /**
7032          * The DOM element ID
7033          * @type String
7034          */
7035         this.id = id || Roo.id(dom);
7036     };
7037
7038     var El = Roo.Element;
7039
7040     El.prototype = {
7041         /**
7042          * The element's default display mode  (defaults to "")
7043          * @type String
7044          */
7045         originalDisplay : "",
7046
7047         visibilityMode : 1,
7048         /**
7049          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7050          * @type String
7051          */
7052         defaultUnit : "px",
7053         
7054         /**
7055          * Sets the element's visibility mode. When setVisible() is called it
7056          * will use this to determine whether to set the visibility or the display property.
7057          * @param visMode Element.VISIBILITY or Element.DISPLAY
7058          * @return {Roo.Element} this
7059          */
7060         setVisibilityMode : function(visMode){
7061             this.visibilityMode = visMode;
7062             return this;
7063         },
7064         /**
7065          * Convenience method for setVisibilityMode(Element.DISPLAY)
7066          * @param {String} display (optional) What to set display to when visible
7067          * @return {Roo.Element} this
7068          */
7069         enableDisplayMode : function(display){
7070             this.setVisibilityMode(El.DISPLAY);
7071             if(typeof display != "undefined") { this.originalDisplay = display; }
7072             return this;
7073         },
7074
7075         /**
7076          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7077          * @param {String} selector The simple selector to test
7078          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7079                 search as a number or element (defaults to 10 || document.body)
7080          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7081          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7082          */
7083         findParent : function(simpleSelector, maxDepth, returnEl){
7084             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7085             maxDepth = maxDepth || 50;
7086             if(typeof maxDepth != "number"){
7087                 stopEl = Roo.getDom(maxDepth);
7088                 maxDepth = 10;
7089             }
7090             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7091                 if(dq.is(p, simpleSelector)){
7092                     return returnEl ? Roo.get(p) : p;
7093                 }
7094                 depth++;
7095                 p = p.parentNode;
7096             }
7097             return null;
7098         },
7099
7100
7101         /**
7102          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7103          * @param {String} selector The simple selector to test
7104          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7105                 search as a number or element (defaults to 10 || document.body)
7106          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7107          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7108          */
7109         findParentNode : function(simpleSelector, maxDepth, returnEl){
7110             var p = Roo.fly(this.dom.parentNode, '_internal');
7111             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7112         },
7113
7114         /**
7115          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7116          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7117          * @param {String} selector The simple selector to test
7118          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7119                 search as a number or element (defaults to 10 || document.body)
7120          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7121          */
7122         up : function(simpleSelector, maxDepth){
7123             return this.findParentNode(simpleSelector, maxDepth, true);
7124         },
7125
7126
7127
7128         /**
7129          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7130          * @param {String} selector The simple selector to test
7131          * @return {Boolean} True if this element matches the selector, else false
7132          */
7133         is : function(simpleSelector){
7134             return Roo.DomQuery.is(this.dom, simpleSelector);
7135         },
7136
7137         /**
7138          * Perform animation on this element.
7139          * @param {Object} args The YUI animation control args
7140          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7141          * @param {Function} onComplete (optional) Function to call when animation completes
7142          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7143          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7144          * @return {Roo.Element} this
7145          */
7146         animate : function(args, duration, onComplete, easing, animType){
7147             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7148             return this;
7149         },
7150
7151         /*
7152          * @private Internal animation call
7153          */
7154         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7155             animType = animType || 'run';
7156             opt = opt || {};
7157             var anim = Roo.lib.Anim[animType](
7158                 this.dom, args,
7159                 (opt.duration || defaultDur) || .35,
7160                 (opt.easing || defaultEase) || 'easeOut',
7161                 function(){
7162                     Roo.callback(cb, this);
7163                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7164                 },
7165                 this
7166             );
7167             opt.anim = anim;
7168             return anim;
7169         },
7170
7171         // private legacy anim prep
7172         preanim : function(a, i){
7173             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7174         },
7175
7176         /**
7177          * Removes worthless text nodes
7178          * @param {Boolean} forceReclean (optional) By default the element
7179          * keeps track if it has been cleaned already so
7180          * you can call this over and over. However, if you update the element and
7181          * need to force a reclean, you can pass true.
7182          */
7183         clean : function(forceReclean){
7184             if(this.isCleaned && forceReclean !== true){
7185                 return this;
7186             }
7187             var ns = /\S/;
7188             var d = this.dom, n = d.firstChild, ni = -1;
7189             while(n){
7190                 var nx = n.nextSibling;
7191                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7192                     d.removeChild(n);
7193                 }else{
7194                     n.nodeIndex = ++ni;
7195                 }
7196                 n = nx;
7197             }
7198             this.isCleaned = true;
7199             return this;
7200         },
7201
7202         // private
7203         calcOffsetsTo : function(el){
7204             el = Roo.get(el);
7205             var d = el.dom;
7206             var restorePos = false;
7207             if(el.getStyle('position') == 'static'){
7208                 el.position('relative');
7209                 restorePos = true;
7210             }
7211             var x = 0, y =0;
7212             var op = this.dom;
7213             while(op && op != d && op.tagName != 'HTML'){
7214                 x+= op.offsetLeft;
7215                 y+= op.offsetTop;
7216                 op = op.offsetParent;
7217             }
7218             if(restorePos){
7219                 el.position('static');
7220             }
7221             return [x, y];
7222         },
7223
7224         /**
7225          * Scrolls this element into view within the passed container.
7226          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7227          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7228          * @return {Roo.Element} this
7229          */
7230         scrollIntoView : function(container, hscroll){
7231             var c = Roo.getDom(container) || document.body;
7232             var el = this.dom;
7233
7234             var o = this.calcOffsetsTo(c),
7235                 l = o[0],
7236                 t = o[1],
7237                 b = t+el.offsetHeight,
7238                 r = l+el.offsetWidth;
7239
7240             var ch = c.clientHeight;
7241             var ct = parseInt(c.scrollTop, 10);
7242             var cl = parseInt(c.scrollLeft, 10);
7243             var cb = ct + ch;
7244             var cr = cl + c.clientWidth;
7245
7246             if(t < ct){
7247                 c.scrollTop = t;
7248             }else if(b > cb){
7249                 c.scrollTop = b-ch;
7250             }
7251
7252             if(hscroll !== false){
7253                 if(l < cl){
7254                     c.scrollLeft = l;
7255                 }else if(r > cr){
7256                     c.scrollLeft = r-c.clientWidth;
7257                 }
7258             }
7259             return this;
7260         },
7261
7262         // private
7263         scrollChildIntoView : function(child, hscroll){
7264             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7265         },
7266
7267         /**
7268          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7269          * the new height may not be available immediately.
7270          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7271          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7272          * @param {Function} onComplete (optional) Function to call when animation completes
7273          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7274          * @return {Roo.Element} this
7275          */
7276         autoHeight : function(animate, duration, onComplete, easing){
7277             var oldHeight = this.getHeight();
7278             this.clip();
7279             this.setHeight(1); // force clipping
7280             setTimeout(function(){
7281                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7282                 if(!animate){
7283                     this.setHeight(height);
7284                     this.unclip();
7285                     if(typeof onComplete == "function"){
7286                         onComplete();
7287                     }
7288                 }else{
7289                     this.setHeight(oldHeight); // restore original height
7290                     this.setHeight(height, animate, duration, function(){
7291                         this.unclip();
7292                         if(typeof onComplete == "function") { onComplete(); }
7293                     }.createDelegate(this), easing);
7294                 }
7295             }.createDelegate(this), 0);
7296             return this;
7297         },
7298
7299         /**
7300          * Returns true if this element is an ancestor of the passed element
7301          * @param {HTMLElement/String} el The element to check
7302          * @return {Boolean} True if this element is an ancestor of el, else false
7303          */
7304         contains : function(el){
7305             if(!el){return false;}
7306             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7307         },
7308
7309         /**
7310          * Checks whether the element is currently visible using both visibility and display properties.
7311          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7312          * @return {Boolean} True if the element is currently visible, else false
7313          */
7314         isVisible : function(deep) {
7315             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7316             if(deep !== true || !vis){
7317                 return vis;
7318             }
7319             var p = this.dom.parentNode;
7320             while(p && p.tagName.toLowerCase() != "body"){
7321                 if(!Roo.fly(p, '_isVisible').isVisible()){
7322                     return false;
7323                 }
7324                 p = p.parentNode;
7325             }
7326             return true;
7327         },
7328
7329         /**
7330          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7331          * @param {String} selector The CSS selector
7332          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7333          * @return {CompositeElement/CompositeElementLite} The composite element
7334          */
7335         select : function(selector, unique){
7336             return El.select(selector, unique, this.dom);
7337         },
7338
7339         /**
7340          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7341          * @param {String} selector The CSS selector
7342          * @return {Array} An array of the matched nodes
7343          */
7344         query : function(selector, unique){
7345             return Roo.DomQuery.select(selector, this.dom);
7346         },
7347
7348         /**
7349          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7350          * @param {String} selector The CSS selector
7351          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7352          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7353          */
7354         child : function(selector, returnDom){
7355             var n = Roo.DomQuery.selectNode(selector, this.dom);
7356             return returnDom ? n : Roo.get(n);
7357         },
7358
7359         /**
7360          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7361          * @param {String} selector The CSS selector
7362          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7363          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7364          */
7365         down : function(selector, returnDom){
7366             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7367             return returnDom ? n : Roo.get(n);
7368         },
7369
7370         /**
7371          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7372          * @param {String} group The group the DD object is member of
7373          * @param {Object} config The DD config object
7374          * @param {Object} overrides An object containing methods to override/implement on the DD object
7375          * @return {Roo.dd.DD} The DD object
7376          */
7377         initDD : function(group, config, overrides){
7378             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7379             return Roo.apply(dd, overrides);
7380         },
7381
7382         /**
7383          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7384          * @param {String} group The group the DDProxy object is member of
7385          * @param {Object} config The DDProxy config object
7386          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7387          * @return {Roo.dd.DDProxy} The DDProxy object
7388          */
7389         initDDProxy : function(group, config, overrides){
7390             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7391             return Roo.apply(dd, overrides);
7392         },
7393
7394         /**
7395          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7396          * @param {String} group The group the DDTarget object is member of
7397          * @param {Object} config The DDTarget config object
7398          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7399          * @return {Roo.dd.DDTarget} The DDTarget object
7400          */
7401         initDDTarget : function(group, config, overrides){
7402             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7403             return Roo.apply(dd, overrides);
7404         },
7405
7406         /**
7407          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7408          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7409          * @param {Boolean} visible Whether the element is visible
7410          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7411          * @return {Roo.Element} this
7412          */
7413          setVisible : function(visible, animate){
7414             if(!animate || !A){
7415                 if(this.visibilityMode == El.DISPLAY){
7416                     this.setDisplayed(visible);
7417                 }else{
7418                     this.fixDisplay();
7419                     this.dom.style.visibility = visible ? "visible" : "hidden";
7420                 }
7421             }else{
7422                 // closure for composites
7423                 var dom = this.dom;
7424                 var visMode = this.visibilityMode;
7425                 if(visible){
7426                     this.setOpacity(.01);
7427                     this.setVisible(true);
7428                 }
7429                 this.anim({opacity: { to: (visible?1:0) }},
7430                       this.preanim(arguments, 1),
7431                       null, .35, 'easeIn', function(){
7432                          if(!visible){
7433                              if(visMode == El.DISPLAY){
7434                                  dom.style.display = "none";
7435                              }else{
7436                                  dom.style.visibility = "hidden";
7437                              }
7438                              Roo.get(dom).setOpacity(1);
7439                          }
7440                      });
7441             }
7442             return this;
7443         },
7444
7445         /**
7446          * Returns true if display is not "none"
7447          * @return {Boolean}
7448          */
7449         isDisplayed : function() {
7450             return this.getStyle("display") != "none";
7451         },
7452
7453         /**
7454          * Toggles the element's visibility or display, depending on visibility mode.
7455          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7456          * @return {Roo.Element} this
7457          */
7458         toggle : function(animate){
7459             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7460             return this;
7461         },
7462
7463         /**
7464          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7465          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7466          * @return {Roo.Element} this
7467          */
7468         setDisplayed : function(value) {
7469             if(typeof value == "boolean"){
7470                value = value ? this.originalDisplay : "none";
7471             }
7472             this.setStyle("display", value);
7473             return this;
7474         },
7475
7476         /**
7477          * Tries to focus the element. Any exceptions are caught and ignored.
7478          * @return {Roo.Element} this
7479          */
7480         focus : function() {
7481             try{
7482                 this.dom.focus();
7483             }catch(e){}
7484             return this;
7485         },
7486
7487         /**
7488          * Tries to blur the element. Any exceptions are caught and ignored.
7489          * @return {Roo.Element} this
7490          */
7491         blur : function() {
7492             try{
7493                 this.dom.blur();
7494             }catch(e){}
7495             return this;
7496         },
7497
7498         /**
7499          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7500          * @param {String/Array} className The CSS class to add, or an array of classes
7501          * @return {Roo.Element} this
7502          */
7503         addClass : function(className){
7504             if(className instanceof Array){
7505                 for(var i = 0, len = className.length; i < len; i++) {
7506                     this.addClass(className[i]);
7507                 }
7508             }else{
7509                 if(className && !this.hasClass(className)){
7510                     this.dom.className = this.dom.className + " " + className;
7511                 }
7512             }
7513             return this;
7514         },
7515
7516         /**
7517          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7518          * @param {String/Array} className The CSS class to add, or an array of classes
7519          * @return {Roo.Element} this
7520          */
7521         radioClass : function(className){
7522             var siblings = this.dom.parentNode.childNodes;
7523             for(var i = 0; i < siblings.length; i++) {
7524                 var s = siblings[i];
7525                 if(s.nodeType == 1){
7526                     Roo.get(s).removeClass(className);
7527                 }
7528             }
7529             this.addClass(className);
7530             return this;
7531         },
7532
7533         /**
7534          * Removes one or more CSS classes from the element.
7535          * @param {String/Array} className The CSS class to remove, or an array of classes
7536          * @return {Roo.Element} this
7537          */
7538         removeClass : function(className){
7539             if(!className || !this.dom.className){
7540                 return this;
7541             }
7542             if(className instanceof Array){
7543                 for(var i = 0, len = className.length; i < len; i++) {
7544                     this.removeClass(className[i]);
7545                 }
7546             }else{
7547                 if(this.hasClass(className)){
7548                     var re = this.classReCache[className];
7549                     if (!re) {
7550                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7551                        this.classReCache[className] = re;
7552                     }
7553                     this.dom.className =
7554                         this.dom.className.replace(re, " ");
7555                 }
7556             }
7557             return this;
7558         },
7559
7560         // private
7561         classReCache: {},
7562
7563         /**
7564          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7565          * @param {String} className The CSS class to toggle
7566          * @return {Roo.Element} this
7567          */
7568         toggleClass : function(className){
7569             if(this.hasClass(className)){
7570                 this.removeClass(className);
7571             }else{
7572                 this.addClass(className);
7573             }
7574             return this;
7575         },
7576
7577         /**
7578          * Checks if the specified CSS class exists on this element's DOM node.
7579          * @param {String} className The CSS class to check for
7580          * @return {Boolean} True if the class exists, else false
7581          */
7582         hasClass : function(className){
7583             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7584         },
7585
7586         /**
7587          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7588          * @param {String} oldClassName The CSS class to replace
7589          * @param {String} newClassName The replacement CSS class
7590          * @return {Roo.Element} this
7591          */
7592         replaceClass : function(oldClassName, newClassName){
7593             this.removeClass(oldClassName);
7594             this.addClass(newClassName);
7595             return this;
7596         },
7597
7598         /**
7599          * Returns an object with properties matching the styles requested.
7600          * For example, el.getStyles('color', 'font-size', 'width') might return
7601          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7602          * @param {String} style1 A style name
7603          * @param {String} style2 A style name
7604          * @param {String} etc.
7605          * @return {Object} The style object
7606          */
7607         getStyles : function(){
7608             var a = arguments, len = a.length, r = {};
7609             for(var i = 0; i < len; i++){
7610                 r[a[i]] = this.getStyle(a[i]);
7611             }
7612             return r;
7613         },
7614
7615         /**
7616          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7617          * @param {String} property The style property whose value is returned.
7618          * @return {String} The current value of the style property for this element.
7619          */
7620         getStyle : function(){
7621             return view && view.getComputedStyle ?
7622                 function(prop){
7623                     var el = this.dom, v, cs, camel;
7624                     if(prop == 'float'){
7625                         prop = "cssFloat";
7626                     }
7627                     if(el.style && (v = el.style[prop])){
7628                         return v;
7629                     }
7630                     if(cs = view.getComputedStyle(el, "")){
7631                         if(!(camel = propCache[prop])){
7632                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7633                         }
7634                         return cs[camel];
7635                     }
7636                     return null;
7637                 } :
7638                 function(prop){
7639                     var el = this.dom, v, cs, camel;
7640                     if(prop == 'opacity'){
7641                         if(typeof el.style.filter == 'string'){
7642                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7643                             if(m){
7644                                 var fv = parseFloat(m[1]);
7645                                 if(!isNaN(fv)){
7646                                     return fv ? fv / 100 : 0;
7647                                 }
7648                             }
7649                         }
7650                         return 1;
7651                     }else if(prop == 'float'){
7652                         prop = "styleFloat";
7653                     }
7654                     if(!(camel = propCache[prop])){
7655                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7656                     }
7657                     if(v = el.style[camel]){
7658                         return v;
7659                     }
7660                     if(cs = el.currentStyle){
7661                         return cs[camel];
7662                     }
7663                     return null;
7664                 };
7665         }(),
7666
7667         /**
7668          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7669          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7670          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7671          * @return {Roo.Element} this
7672          */
7673         setStyle : function(prop, value){
7674             if(typeof prop == "string"){
7675                 
7676                 if (prop == 'float') {
7677                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7678                     return this;
7679                 }
7680                 
7681                 var camel;
7682                 if(!(camel = propCache[prop])){
7683                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7684                 }
7685                 
7686                 if(camel == 'opacity') {
7687                     this.setOpacity(value);
7688                 }else{
7689                     this.dom.style[camel] = value;
7690                 }
7691             }else{
7692                 for(var style in prop){
7693                     if(typeof prop[style] != "function"){
7694                        this.setStyle(style, prop[style]);
7695                     }
7696                 }
7697             }
7698             return this;
7699         },
7700
7701         /**
7702          * More flexible version of {@link #setStyle} for setting style properties.
7703          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7704          * a function which returns such a specification.
7705          * @return {Roo.Element} this
7706          */
7707         applyStyles : function(style){
7708             Roo.DomHelper.applyStyles(this.dom, style);
7709             return this;
7710         },
7711
7712         /**
7713           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7714           * @return {Number} The X position of the element
7715           */
7716         getX : function(){
7717             return D.getX(this.dom);
7718         },
7719
7720         /**
7721           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7722           * @return {Number} The Y position of the element
7723           */
7724         getY : function(){
7725             return D.getY(this.dom);
7726         },
7727
7728         /**
7729           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7730           * @return {Array} The XY position of the element
7731           */
7732         getXY : function(){
7733             return D.getXY(this.dom);
7734         },
7735
7736         /**
7737          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7738          * @param {Number} The X position of the element
7739          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7740          * @return {Roo.Element} this
7741          */
7742         setX : function(x, animate){
7743             if(!animate || !A){
7744                 D.setX(this.dom, x);
7745             }else{
7746                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7747             }
7748             return this;
7749         },
7750
7751         /**
7752          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7753          * @param {Number} The Y position of the element
7754          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7755          * @return {Roo.Element} this
7756          */
7757         setY : function(y, animate){
7758             if(!animate || !A){
7759                 D.setY(this.dom, y);
7760             }else{
7761                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7762             }
7763             return this;
7764         },
7765
7766         /**
7767          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7768          * @param {String} left The left CSS property value
7769          * @return {Roo.Element} this
7770          */
7771         setLeft : function(left){
7772             this.setStyle("left", this.addUnits(left));
7773             return this;
7774         },
7775
7776         /**
7777          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7778          * @param {String} top The top CSS property value
7779          * @return {Roo.Element} this
7780          */
7781         setTop : function(top){
7782             this.setStyle("top", this.addUnits(top));
7783             return this;
7784         },
7785
7786         /**
7787          * Sets the element's CSS right style.
7788          * @param {String} right The right CSS property value
7789          * @return {Roo.Element} this
7790          */
7791         setRight : function(right){
7792             this.setStyle("right", this.addUnits(right));
7793             return this;
7794         },
7795
7796         /**
7797          * Sets the element's CSS bottom style.
7798          * @param {String} bottom The bottom CSS property value
7799          * @return {Roo.Element} this
7800          */
7801         setBottom : function(bottom){
7802             this.setStyle("bottom", this.addUnits(bottom));
7803             return this;
7804         },
7805
7806         /**
7807          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7808          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7809          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7810          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7811          * @return {Roo.Element} this
7812          */
7813         setXY : function(pos, animate){
7814             if(!animate || !A){
7815                 D.setXY(this.dom, pos);
7816             }else{
7817                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7818             }
7819             return this;
7820         },
7821
7822         /**
7823          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7824          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7825          * @param {Number} x X value for new position (coordinates are page-based)
7826          * @param {Number} y Y value for new position (coordinates are page-based)
7827          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7828          * @return {Roo.Element} this
7829          */
7830         setLocation : function(x, y, animate){
7831             this.setXY([x, y], this.preanim(arguments, 2));
7832             return this;
7833         },
7834
7835         /**
7836          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7837          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7838          * @param {Number} x X value for new position (coordinates are page-based)
7839          * @param {Number} y Y value for new position (coordinates are page-based)
7840          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7841          * @return {Roo.Element} this
7842          */
7843         moveTo : function(x, y, animate){
7844             this.setXY([x, y], this.preanim(arguments, 2));
7845             return this;
7846         },
7847
7848         /**
7849          * Returns the region of the given element.
7850          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7851          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7852          */
7853         getRegion : function(){
7854             return D.getRegion(this.dom);
7855         },
7856
7857         /**
7858          * Returns the offset height of the element
7859          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7860          * @return {Number} The element's height
7861          */
7862         getHeight : function(contentHeight){
7863             var h = this.dom.offsetHeight || 0;
7864             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7865         },
7866
7867         /**
7868          * Returns the offset width of the element
7869          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7870          * @return {Number} The element's width
7871          */
7872         getWidth : function(contentWidth){
7873             var w = this.dom.offsetWidth || 0;
7874             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7875         },
7876
7877         /**
7878          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7879          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7880          * if a height has not been set using CSS.
7881          * @return {Number}
7882          */
7883         getComputedHeight : function(){
7884             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7885             if(!h){
7886                 h = parseInt(this.getStyle('height'), 10) || 0;
7887                 if(!this.isBorderBox()){
7888                     h += this.getFrameWidth('tb');
7889                 }
7890             }
7891             return h;
7892         },
7893
7894         /**
7895          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7896          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7897          * if a width has not been set using CSS.
7898          * @return {Number}
7899          */
7900         getComputedWidth : function(){
7901             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7902             if(!w){
7903                 w = parseInt(this.getStyle('width'), 10) || 0;
7904                 if(!this.isBorderBox()){
7905                     w += this.getFrameWidth('lr');
7906                 }
7907             }
7908             return w;
7909         },
7910
7911         /**
7912          * Returns the size of the element.
7913          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7914          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7915          */
7916         getSize : function(contentSize){
7917             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7918         },
7919
7920         /**
7921          * Returns the width and height of the viewport.
7922          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7923          */
7924         getViewSize : function(){
7925             var d = this.dom, doc = document, aw = 0, ah = 0;
7926             if(d == doc || d == doc.body){
7927                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7928             }else{
7929                 return {
7930                     width : d.clientWidth,
7931                     height: d.clientHeight
7932                 };
7933             }
7934         },
7935
7936         /**
7937          * Returns the value of the "value" attribute
7938          * @param {Boolean} asNumber true to parse the value as a number
7939          * @return {String/Number}
7940          */
7941         getValue : function(asNumber){
7942             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7943         },
7944
7945         // private
7946         adjustWidth : function(width){
7947             if(typeof width == "number"){
7948                 if(this.autoBoxAdjust && !this.isBorderBox()){
7949                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7950                 }
7951                 if(width < 0){
7952                     width = 0;
7953                 }
7954             }
7955             return width;
7956         },
7957
7958         // private
7959         adjustHeight : function(height){
7960             if(typeof height == "number"){
7961                if(this.autoBoxAdjust && !this.isBorderBox()){
7962                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7963                }
7964                if(height < 0){
7965                    height = 0;
7966                }
7967             }
7968             return height;
7969         },
7970
7971         /**
7972          * Set the width of the element
7973          * @param {Number} width The new width
7974          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7975          * @return {Roo.Element} this
7976          */
7977         setWidth : function(width, animate){
7978             width = this.adjustWidth(width);
7979             if(!animate || !A){
7980                 this.dom.style.width = this.addUnits(width);
7981             }else{
7982                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7983             }
7984             return this;
7985         },
7986
7987         /**
7988          * Set the height of the element
7989          * @param {Number} height The new height
7990          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7991          * @return {Roo.Element} this
7992          */
7993          setHeight : function(height, animate){
7994             height = this.adjustHeight(height);
7995             if(!animate || !A){
7996                 this.dom.style.height = this.addUnits(height);
7997             }else{
7998                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7999             }
8000             return this;
8001         },
8002
8003         /**
8004          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8005          * @param {Number} width The new width
8006          * @param {Number} height The new height
8007          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8008          * @return {Roo.Element} this
8009          */
8010          setSize : function(width, height, animate){
8011             if(typeof width == "object"){ // in case of object from getSize()
8012                 height = width.height; width = width.width;
8013             }
8014             width = this.adjustWidth(width); height = this.adjustHeight(height);
8015             if(!animate || !A){
8016                 this.dom.style.width = this.addUnits(width);
8017                 this.dom.style.height = this.addUnits(height);
8018             }else{
8019                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8020             }
8021             return this;
8022         },
8023
8024         /**
8025          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8026          * @param {Number} x X value for new position (coordinates are page-based)
8027          * @param {Number} y Y value for new position (coordinates are page-based)
8028          * @param {Number} width The new width
8029          * @param {Number} height The new height
8030          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8031          * @return {Roo.Element} this
8032          */
8033         setBounds : function(x, y, width, height, animate){
8034             if(!animate || !A){
8035                 this.setSize(width, height);
8036                 this.setLocation(x, y);
8037             }else{
8038                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8039                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8040                               this.preanim(arguments, 4), 'motion');
8041             }
8042             return this;
8043         },
8044
8045         /**
8046          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
8047          * @param {Roo.lib.Region} region The region to fill
8048          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8049          * @return {Roo.Element} this
8050          */
8051         setRegion : function(region, animate){
8052             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8053             return this;
8054         },
8055
8056         /**
8057          * Appends an event handler
8058          *
8059          * @param {String}   eventName     The type of event to append
8060          * @param {Function} fn        The method the event invokes
8061          * @param {Object} scope       (optional) The scope (this object) of the fn
8062          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8063          */
8064         addListener : function(eventName, fn, scope, options){
8065             if (this.dom) {
8066                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8067             }
8068         },
8069
8070         /**
8071          * Removes an event handler from this element
8072          * @param {String} eventName the type of event to remove
8073          * @param {Function} fn the method the event invokes
8074          * @return {Roo.Element} this
8075          */
8076         removeListener : function(eventName, fn){
8077             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8078             return this;
8079         },
8080
8081         /**
8082          * Removes all previous added listeners from this element
8083          * @return {Roo.Element} this
8084          */
8085         removeAllListeners : function(){
8086             E.purgeElement(this.dom);
8087             return this;
8088         },
8089
8090         relayEvent : function(eventName, observable){
8091             this.on(eventName, function(e){
8092                 observable.fireEvent(eventName, e);
8093             });
8094         },
8095
8096         /**
8097          * Set the opacity of the element
8098          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8099          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8100          * @return {Roo.Element} this
8101          */
8102          setOpacity : function(opacity, animate){
8103             if(!animate || !A){
8104                 var s = this.dom.style;
8105                 if(Roo.isIE){
8106                     s.zoom = 1;
8107                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8108                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8109                 }else{
8110                     s.opacity = opacity;
8111                 }
8112             }else{
8113                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8114             }
8115             return this;
8116         },
8117
8118         /**
8119          * Gets the left X coordinate
8120          * @param {Boolean} local True to get the local css position instead of page coordinate
8121          * @return {Number}
8122          */
8123         getLeft : function(local){
8124             if(!local){
8125                 return this.getX();
8126             }else{
8127                 return parseInt(this.getStyle("left"), 10) || 0;
8128             }
8129         },
8130
8131         /**
8132          * Gets the right X coordinate of the element (element X position + element width)
8133          * @param {Boolean} local True to get the local css position instead of page coordinate
8134          * @return {Number}
8135          */
8136         getRight : function(local){
8137             if(!local){
8138                 return this.getX() + this.getWidth();
8139             }else{
8140                 return (this.getLeft(true) + this.getWidth()) || 0;
8141             }
8142         },
8143
8144         /**
8145          * Gets the top Y coordinate
8146          * @param {Boolean} local True to get the local css position instead of page coordinate
8147          * @return {Number}
8148          */
8149         getTop : function(local) {
8150             if(!local){
8151                 return this.getY();
8152             }else{
8153                 return parseInt(this.getStyle("top"), 10) || 0;
8154             }
8155         },
8156
8157         /**
8158          * Gets the bottom Y coordinate of the element (element Y position + element height)
8159          * @param {Boolean} local True to get the local css position instead of page coordinate
8160          * @return {Number}
8161          */
8162         getBottom : function(local){
8163             if(!local){
8164                 return this.getY() + this.getHeight();
8165             }else{
8166                 return (this.getTop(true) + this.getHeight()) || 0;
8167             }
8168         },
8169
8170         /**
8171         * Initializes positioning on this element. If a desired position is not passed, it will make the
8172         * the element positioned relative IF it is not already positioned.
8173         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8174         * @param {Number} zIndex (optional) The zIndex to apply
8175         * @param {Number} x (optional) Set the page X position
8176         * @param {Number} y (optional) Set the page Y position
8177         */
8178         position : function(pos, zIndex, x, y){
8179             if(!pos){
8180                if(this.getStyle('position') == 'static'){
8181                    this.setStyle('position', 'relative');
8182                }
8183             }else{
8184                 this.setStyle("position", pos);
8185             }
8186             if(zIndex){
8187                 this.setStyle("z-index", zIndex);
8188             }
8189             if(x !== undefined && y !== undefined){
8190                 this.setXY([x, y]);
8191             }else if(x !== undefined){
8192                 this.setX(x);
8193             }else if(y !== undefined){
8194                 this.setY(y);
8195             }
8196         },
8197
8198         /**
8199         * Clear positioning back to the default when the document was loaded
8200         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8201         * @return {Roo.Element} this
8202          */
8203         clearPositioning : function(value){
8204             value = value ||'';
8205             this.setStyle({
8206                 "left": value,
8207                 "right": value,
8208                 "top": value,
8209                 "bottom": value,
8210                 "z-index": "",
8211                 "position" : "static"
8212             });
8213             return this;
8214         },
8215
8216         /**
8217         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8218         * snapshot before performing an update and then restoring the element.
8219         * @return {Object}
8220         */
8221         getPositioning : function(){
8222             var l = this.getStyle("left");
8223             var t = this.getStyle("top");
8224             return {
8225                 "position" : this.getStyle("position"),
8226                 "left" : l,
8227                 "right" : l ? "" : this.getStyle("right"),
8228                 "top" : t,
8229                 "bottom" : t ? "" : this.getStyle("bottom"),
8230                 "z-index" : this.getStyle("z-index")
8231             };
8232         },
8233
8234         /**
8235          * Gets the width of the border(s) for the specified side(s)
8236          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8237          * passing lr would get the border (l)eft width + the border (r)ight width.
8238          * @return {Number} The width of the sides passed added together
8239          */
8240         getBorderWidth : function(side){
8241             return this.addStyles(side, El.borders);
8242         },
8243
8244         /**
8245          * Gets the width of the padding(s) for the specified side(s)
8246          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8247          * passing lr would get the padding (l)eft + the padding (r)ight.
8248          * @return {Number} The padding of the sides passed added together
8249          */
8250         getPadding : function(side){
8251             return this.addStyles(side, El.paddings);
8252         },
8253
8254         /**
8255         * Set positioning with an object returned by getPositioning().
8256         * @param {Object} posCfg
8257         * @return {Roo.Element} this
8258          */
8259         setPositioning : function(pc){
8260             this.applyStyles(pc);
8261             if(pc.right == "auto"){
8262                 this.dom.style.right = "";
8263             }
8264             if(pc.bottom == "auto"){
8265                 this.dom.style.bottom = "";
8266             }
8267             return this;
8268         },
8269
8270         // private
8271         fixDisplay : function(){
8272             if(this.getStyle("display") == "none"){
8273                 this.setStyle("visibility", "hidden");
8274                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8275                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8276                     this.setStyle("display", "block");
8277                 }
8278             }
8279         },
8280
8281         /**
8282          * Quick set left and top adding default units
8283          * @param {String} left The left CSS property value
8284          * @param {String} top The top CSS property value
8285          * @return {Roo.Element} this
8286          */
8287          setLeftTop : function(left, top){
8288             this.dom.style.left = this.addUnits(left);
8289             this.dom.style.top = this.addUnits(top);
8290             return this;
8291         },
8292
8293         /**
8294          * Move this element relative to its current position.
8295          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8296          * @param {Number} distance How far to move the element in pixels
8297          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8298          * @return {Roo.Element} this
8299          */
8300          move : function(direction, distance, animate){
8301             var xy = this.getXY();
8302             direction = direction.toLowerCase();
8303             switch(direction){
8304                 case "l":
8305                 case "left":
8306                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8307                     break;
8308                case "r":
8309                case "right":
8310                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8311                     break;
8312                case "t":
8313                case "top":
8314                case "up":
8315                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8316                     break;
8317                case "b":
8318                case "bottom":
8319                case "down":
8320                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8321                     break;
8322             }
8323             return this;
8324         },
8325
8326         /**
8327          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8328          * @return {Roo.Element} this
8329          */
8330         clip : function(){
8331             if(!this.isClipped){
8332                this.isClipped = true;
8333                this.originalClip = {
8334                    "o": this.getStyle("overflow"),
8335                    "x": this.getStyle("overflow-x"),
8336                    "y": this.getStyle("overflow-y")
8337                };
8338                this.setStyle("overflow", "hidden");
8339                this.setStyle("overflow-x", "hidden");
8340                this.setStyle("overflow-y", "hidden");
8341             }
8342             return this;
8343         },
8344
8345         /**
8346          *  Return clipping (overflow) to original clipping before clip() was called
8347          * @return {Roo.Element} this
8348          */
8349         unclip : function(){
8350             if(this.isClipped){
8351                 this.isClipped = false;
8352                 var o = this.originalClip;
8353                 if(o.o){this.setStyle("overflow", o.o);}
8354                 if(o.x){this.setStyle("overflow-x", o.x);}
8355                 if(o.y){this.setStyle("overflow-y", o.y);}
8356             }
8357             return this;
8358         },
8359
8360
8361         /**
8362          * Gets the x,y coordinates specified by the anchor position on the element.
8363          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8364          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8365          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8366          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8367          * @return {Array} [x, y] An array containing the element's x and y coordinates
8368          */
8369         getAnchorXY : function(anchor, local, s){
8370             //Passing a different size is useful for pre-calculating anchors,
8371             //especially for anchored animations that change the el size.
8372
8373             var w, h, vp = false;
8374             if(!s){
8375                 var d = this.dom;
8376                 if(d == document.body || d == document){
8377                     vp = true;
8378                     w = D.getViewWidth(); h = D.getViewHeight();
8379                 }else{
8380                     w = this.getWidth(); h = this.getHeight();
8381                 }
8382             }else{
8383                 w = s.width;  h = s.height;
8384             }
8385             var x = 0, y = 0, r = Math.round;
8386             switch((anchor || "tl").toLowerCase()){
8387                 case "c":
8388                     x = r(w*.5);
8389                     y = r(h*.5);
8390                 break;
8391                 case "t":
8392                     x = r(w*.5);
8393                     y = 0;
8394                 break;
8395                 case "l":
8396                     x = 0;
8397                     y = r(h*.5);
8398                 break;
8399                 case "r":
8400                     x = w;
8401                     y = r(h*.5);
8402                 break;
8403                 case "b":
8404                     x = r(w*.5);
8405                     y = h;
8406                 break;
8407                 case "tl":
8408                     x = 0;
8409                     y = 0;
8410                 break;
8411                 case "bl":
8412                     x = 0;
8413                     y = h;
8414                 break;
8415                 case "br":
8416                     x = w;
8417                     y = h;
8418                 break;
8419                 case "tr":
8420                     x = w;
8421                     y = 0;
8422                 break;
8423             }
8424             if(local === true){
8425                 return [x, y];
8426             }
8427             if(vp){
8428                 var sc = this.getScroll();
8429                 return [x + sc.left, y + sc.top];
8430             }
8431             //Add the element's offset xy
8432             var o = this.getXY();
8433             return [x+o[0], y+o[1]];
8434         },
8435
8436         /**
8437          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8438          * supported position values.
8439          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8440          * @param {String} position The position to align to.
8441          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8442          * @return {Array} [x, y]
8443          */
8444         getAlignToXY : function(el, p, o){
8445             el = Roo.get(el);
8446             var d = this.dom;
8447             if(!el.dom){
8448                 throw "Element.alignTo with an element that doesn't exist";
8449             }
8450             var c = false; //constrain to viewport
8451             var p1 = "", p2 = "";
8452             o = o || [0,0];
8453
8454             if(!p){
8455                 p = "tl-bl";
8456             }else if(p == "?"){
8457                 p = "tl-bl?";
8458             }else if(p.indexOf("-") == -1){
8459                 p = "tl-" + p;
8460             }
8461             p = p.toLowerCase();
8462             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8463             if(!m){
8464                throw "Element.alignTo with an invalid alignment " + p;
8465             }
8466             p1 = m[1]; p2 = m[2]; c = !!m[3];
8467
8468             //Subtract the aligned el's internal xy from the target's offset xy
8469             //plus custom offset to get the aligned el's new offset xy
8470             var a1 = this.getAnchorXY(p1, true);
8471             var a2 = el.getAnchorXY(p2, false);
8472             var x = a2[0] - a1[0] + o[0];
8473             var y = a2[1] - a1[1] + o[1];
8474             if(c){
8475                 //constrain the aligned el to viewport if necessary
8476                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8477                 // 5px of margin for ie
8478                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8479
8480                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8481                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8482                 //otherwise swap the aligned el to the opposite border of the target.
8483                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8484                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8485                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8486                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8487
8488                var doc = document;
8489                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8490                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8491
8492                if((x+w) > dw + scrollX){
8493                     x = swapX ? r.left-w : dw+scrollX-w;
8494                 }
8495                if(x < scrollX){
8496                    x = swapX ? r.right : scrollX;
8497                }
8498                if((y+h) > dh + scrollY){
8499                     y = swapY ? r.top-h : dh+scrollY-h;
8500                 }
8501                if (y < scrollY){
8502                    y = swapY ? r.bottom : scrollY;
8503                }
8504             }
8505             return [x,y];
8506         },
8507
8508         // private
8509         getConstrainToXY : function(){
8510             var os = {top:0, left:0, bottom:0, right: 0};
8511
8512             return function(el, local, offsets, proposedXY){
8513                 el = Roo.get(el);
8514                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8515
8516                 var vw, vh, vx = 0, vy = 0;
8517                 if(el.dom == document.body || el.dom == document){
8518                     vw = Roo.lib.Dom.getViewWidth();
8519                     vh = Roo.lib.Dom.getViewHeight();
8520                 }else{
8521                     vw = el.dom.clientWidth;
8522                     vh = el.dom.clientHeight;
8523                     if(!local){
8524                         var vxy = el.getXY();
8525                         vx = vxy[0];
8526                         vy = vxy[1];
8527                     }
8528                 }
8529
8530                 var s = el.getScroll();
8531
8532                 vx += offsets.left + s.left;
8533                 vy += offsets.top + s.top;
8534
8535                 vw -= offsets.right;
8536                 vh -= offsets.bottom;
8537
8538                 var vr = vx+vw;
8539                 var vb = vy+vh;
8540
8541                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8542                 var x = xy[0], y = xy[1];
8543                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8544
8545                 // only move it if it needs it
8546                 var moved = false;
8547
8548                 // first validate right/bottom
8549                 if((x + w) > vr){
8550                     x = vr - w;
8551                     moved = true;
8552                 }
8553                 if((y + h) > vb){
8554                     y = vb - h;
8555                     moved = true;
8556                 }
8557                 // then make sure top/left isn't negative
8558                 if(x < vx){
8559                     x = vx;
8560                     moved = true;
8561                 }
8562                 if(y < vy){
8563                     y = vy;
8564                     moved = true;
8565                 }
8566                 return moved ? [x, y] : false;
8567             };
8568         }(),
8569
8570         // private
8571         adjustForConstraints : function(xy, parent, offsets){
8572             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8573         },
8574
8575         /**
8576          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8577          * document it aligns it to the viewport.
8578          * The position parameter is optional, and can be specified in any one of the following formats:
8579          * <ul>
8580          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8581          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8582          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8583          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8584          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8585          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8586          * </ul>
8587          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8588          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8589          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8590          * that specified in order to enforce the viewport constraints.
8591          * Following are all of the supported anchor positions:
8592     <pre>
8593     Value  Description
8594     -----  -----------------------------
8595     tl     The top left corner (default)
8596     t      The center of the top edge
8597     tr     The top right corner
8598     l      The center of the left edge
8599     c      In the center of the element
8600     r      The center of the right edge
8601     bl     The bottom left corner
8602     b      The center of the bottom edge
8603     br     The bottom right corner
8604     </pre>
8605     Example Usage:
8606     <pre><code>
8607     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8608     el.alignTo("other-el");
8609
8610     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8611     el.alignTo("other-el", "tr?");
8612
8613     // align the bottom right corner of el with the center left edge of other-el
8614     el.alignTo("other-el", "br-l?");
8615
8616     // align the center of el with the bottom left corner of other-el and
8617     // adjust the x position by -6 pixels (and the y position by 0)
8618     el.alignTo("other-el", "c-bl", [-6, 0]);
8619     </code></pre>
8620          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8621          * @param {String} position The position to align to.
8622          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8623          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8624          * @return {Roo.Element} this
8625          */
8626         alignTo : function(element, position, offsets, animate){
8627             var xy = this.getAlignToXY(element, position, offsets);
8628             this.setXY(xy, this.preanim(arguments, 3));
8629             return this;
8630         },
8631
8632         /**
8633          * Anchors an element to another element and realigns it when the window is resized.
8634          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8635          * @param {String} position The position to align to.
8636          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8637          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8638          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8639          * is a number, it is used as the buffer delay (defaults to 50ms).
8640          * @param {Function} callback The function to call after the animation finishes
8641          * @return {Roo.Element} this
8642          */
8643         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8644             var action = function(){
8645                 this.alignTo(el, alignment, offsets, animate);
8646                 Roo.callback(callback, this);
8647             };
8648             Roo.EventManager.onWindowResize(action, this);
8649             var tm = typeof monitorScroll;
8650             if(tm != 'undefined'){
8651                 Roo.EventManager.on(window, 'scroll', action, this,
8652                     {buffer: tm == 'number' ? monitorScroll : 50});
8653             }
8654             action.call(this); // align immediately
8655             return this;
8656         },
8657         /**
8658          * Clears any opacity settings from this element. Required in some cases for IE.
8659          * @return {Roo.Element} this
8660          */
8661         clearOpacity : function(){
8662             if (window.ActiveXObject) {
8663                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8664                     this.dom.style.filter = "";
8665                 }
8666             } else {
8667                 this.dom.style.opacity = "";
8668                 this.dom.style["-moz-opacity"] = "";
8669                 this.dom.style["-khtml-opacity"] = "";
8670             }
8671             return this;
8672         },
8673
8674         /**
8675          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8676          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8677          * @return {Roo.Element} this
8678          */
8679         hide : function(animate){
8680             this.setVisible(false, this.preanim(arguments, 0));
8681             return this;
8682         },
8683
8684         /**
8685         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8686         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8687          * @return {Roo.Element} this
8688          */
8689         show : function(animate){
8690             this.setVisible(true, this.preanim(arguments, 0));
8691             return this;
8692         },
8693
8694         /**
8695          * @private Test if size has a unit, otherwise appends the default
8696          */
8697         addUnits : function(size){
8698             return Roo.Element.addUnits(size, this.defaultUnit);
8699         },
8700
8701         /**
8702          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8703          * @return {Roo.Element} this
8704          */
8705         beginMeasure : function(){
8706             var el = this.dom;
8707             if(el.offsetWidth || el.offsetHeight){
8708                 return this; // offsets work already
8709             }
8710             var changed = [];
8711             var p = this.dom, b = document.body; // start with this element
8712             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8713                 var pe = Roo.get(p);
8714                 if(pe.getStyle('display') == 'none'){
8715                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8716                     p.style.visibility = "hidden";
8717                     p.style.display = "block";
8718                 }
8719                 p = p.parentNode;
8720             }
8721             this._measureChanged = changed;
8722             return this;
8723
8724         },
8725
8726         /**
8727          * Restores displays to before beginMeasure was called
8728          * @return {Roo.Element} this
8729          */
8730         endMeasure : function(){
8731             var changed = this._measureChanged;
8732             if(changed){
8733                 for(var i = 0, len = changed.length; i < len; i++) {
8734                     var r = changed[i];
8735                     r.el.style.visibility = r.visibility;
8736                     r.el.style.display = "none";
8737                 }
8738                 this._measureChanged = null;
8739             }
8740             return this;
8741         },
8742
8743         /**
8744         * Update the innerHTML of this element, optionally searching for and processing scripts
8745         * @param {String} html The new HTML
8746         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8747         * @param {Function} callback For async script loading you can be noticed when the update completes
8748         * @return {Roo.Element} this
8749          */
8750         update : function(html, loadScripts, callback){
8751             if(typeof html == "undefined"){
8752                 html = "";
8753             }
8754             if(loadScripts !== true){
8755                 this.dom.innerHTML = html;
8756                 if(typeof callback == "function"){
8757                     callback();
8758                 }
8759                 return this;
8760             }
8761             var id = Roo.id();
8762             var dom = this.dom;
8763
8764             html += '<span id="' + id + '"></span>';
8765
8766             E.onAvailable(id, function(){
8767                 var hd = document.getElementsByTagName("head")[0];
8768                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8769                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8770                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8771
8772                 var match;
8773                 while(match = re.exec(html)){
8774                     var attrs = match[1];
8775                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8776                     if(srcMatch && srcMatch[2]){
8777                        var s = document.createElement("script");
8778                        s.src = srcMatch[2];
8779                        var typeMatch = attrs.match(typeRe);
8780                        if(typeMatch && typeMatch[2]){
8781                            s.type = typeMatch[2];
8782                        }
8783                        hd.appendChild(s);
8784                     }else if(match[2] && match[2].length > 0){
8785                         if(window.execScript) {
8786                            window.execScript(match[2]);
8787                         } else {
8788                             /**
8789                              * eval:var:id
8790                              * eval:var:dom
8791                              * eval:var:html
8792                              * 
8793                              */
8794                            window.eval(match[2]);
8795                         }
8796                     }
8797                 }
8798                 var el = document.getElementById(id);
8799                 if(el){el.parentNode.removeChild(el);}
8800                 if(typeof callback == "function"){
8801                     callback();
8802                 }
8803             });
8804             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8805             return this;
8806         },
8807
8808         /**
8809          * Direct access to the UpdateManager update() method (takes the same parameters).
8810          * @param {String/Function} url The url for this request or a function to call to get the url
8811          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8812          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8813          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8814          * @return {Roo.Element} this
8815          */
8816         load : function(){
8817             var um = this.getUpdateManager();
8818             um.update.apply(um, arguments);
8819             return this;
8820         },
8821
8822         /**
8823         * Gets this element's UpdateManager
8824         * @return {Roo.UpdateManager} The UpdateManager
8825         */
8826         getUpdateManager : function(){
8827             if(!this.updateManager){
8828                 this.updateManager = new Roo.UpdateManager(this);
8829             }
8830             return this.updateManager;
8831         },
8832
8833         /**
8834          * Disables text selection for this element (normalized across browsers)
8835          * @return {Roo.Element} this
8836          */
8837         unselectable : function(){
8838             this.dom.unselectable = "on";
8839             this.swallowEvent("selectstart", true);
8840             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8841             this.addClass("x-unselectable");
8842             return this;
8843         },
8844
8845         /**
8846         * Calculates the x, y to center this element on the screen
8847         * @return {Array} The x, y values [x, y]
8848         */
8849         getCenterXY : function(){
8850             return this.getAlignToXY(document, 'c-c');
8851         },
8852
8853         /**
8854         * Centers the Element in either the viewport, or another Element.
8855         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8856         */
8857         center : function(centerIn){
8858             this.alignTo(centerIn || document, 'c-c');
8859             return this;
8860         },
8861
8862         /**
8863          * Tests various css rules/browsers to determine if this element uses a border box
8864          * @return {Boolean}
8865          */
8866         isBorderBox : function(){
8867             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8868         },
8869
8870         /**
8871          * Return a box {x, y, width, height} that can be used to set another elements
8872          * size/location to match this element.
8873          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8874          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8875          * @return {Object} box An object in the format {x, y, width, height}
8876          */
8877         getBox : function(contentBox, local){
8878             var xy;
8879             if(!local){
8880                 xy = this.getXY();
8881             }else{
8882                 var left = parseInt(this.getStyle("left"), 10) || 0;
8883                 var top = parseInt(this.getStyle("top"), 10) || 0;
8884                 xy = [left, top];
8885             }
8886             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8887             if(!contentBox){
8888                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8889             }else{
8890                 var l = this.getBorderWidth("l")+this.getPadding("l");
8891                 var r = this.getBorderWidth("r")+this.getPadding("r");
8892                 var t = this.getBorderWidth("t")+this.getPadding("t");
8893                 var b = this.getBorderWidth("b")+this.getPadding("b");
8894                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8895             }
8896             bx.right = bx.x + bx.width;
8897             bx.bottom = bx.y + bx.height;
8898             return bx;
8899         },
8900
8901         /**
8902          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8903          for more information about the sides.
8904          * @param {String} sides
8905          * @return {Number}
8906          */
8907         getFrameWidth : function(sides, onlyContentBox){
8908             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8909         },
8910
8911         /**
8912          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8913          * @param {Object} box The box to fill {x, y, width, height}
8914          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8915          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8916          * @return {Roo.Element} this
8917          */
8918         setBox : function(box, adjust, animate){
8919             var w = box.width, h = box.height;
8920             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8921                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8922                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8923             }
8924             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8925             return this;
8926         },
8927
8928         /**
8929          * Forces the browser to repaint this element
8930          * @return {Roo.Element} this
8931          */
8932          repaint : function(){
8933             var dom = this.dom;
8934             this.addClass("x-repaint");
8935             setTimeout(function(){
8936                 Roo.get(dom).removeClass("x-repaint");
8937             }, 1);
8938             return this;
8939         },
8940
8941         /**
8942          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8943          * then it returns the calculated width of the sides (see getPadding)
8944          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8945          * @return {Object/Number}
8946          */
8947         getMargins : function(side){
8948             if(!side){
8949                 return {
8950                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8951                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8952                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8953                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8954                 };
8955             }else{
8956                 return this.addStyles(side, El.margins);
8957              }
8958         },
8959
8960         // private
8961         addStyles : function(sides, styles){
8962             var val = 0, v, w;
8963             for(var i = 0, len = sides.length; i < len; i++){
8964                 v = this.getStyle(styles[sides.charAt(i)]);
8965                 if(v){
8966                      w = parseInt(v, 10);
8967                      if(w){ val += w; }
8968                 }
8969             }
8970             return val;
8971         },
8972
8973         /**
8974          * Creates a proxy element of this element
8975          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8976          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8977          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8978          * @return {Roo.Element} The new proxy element
8979          */
8980         createProxy : function(config, renderTo, matchBox){
8981             if(renderTo){
8982                 renderTo = Roo.getDom(renderTo);
8983             }else{
8984                 renderTo = document.body;
8985             }
8986             config = typeof config == "object" ?
8987                 config : {tag : "div", cls: config};
8988             var proxy = Roo.DomHelper.append(renderTo, config, true);
8989             if(matchBox){
8990                proxy.setBox(this.getBox());
8991             }
8992             return proxy;
8993         },
8994
8995         /**
8996          * Puts a mask over this element to disable user interaction. Requires core.css.
8997          * This method can only be applied to elements which accept child nodes.
8998          * @param {String} msg (optional) A message to display in the mask
8999          * @param {String} msgCls (optional) A css class to apply to the msg element
9000          * @return {Element} The mask  element
9001          */
9002         mask : function(msg, msgCls)
9003         {
9004             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9005                 this.setStyle("position", "relative");
9006             }
9007             if(!this._mask){
9008                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9009             }
9010             this.addClass("x-masked");
9011             this._mask.setDisplayed(true);
9012             
9013             // we wander
9014             var z = 0;
9015             var dom = this.dom;
9016             while (dom && dom.style) {
9017                 if (!isNaN(parseInt(dom.style.zIndex))) {
9018                     z = Math.max(z, parseInt(dom.style.zIndex));
9019                 }
9020                 dom = dom.parentNode;
9021             }
9022             // if we are masking the body - then it hides everything..
9023             if (this.dom == document.body) {
9024                 z = 1000000;
9025                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9026                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9027             }
9028            
9029             if(typeof msg == 'string'){
9030                 if(!this._maskMsg){
9031                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9032                 }
9033                 var mm = this._maskMsg;
9034                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9035                 if (mm.dom.firstChild) { // weird IE issue?
9036                     mm.dom.firstChild.innerHTML = msg;
9037                 }
9038                 mm.setDisplayed(true);
9039                 mm.center(this);
9040                 mm.setStyle('z-index', z + 102);
9041             }
9042             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9043                 this._mask.setHeight(this.getHeight());
9044             }
9045             this._mask.setStyle('z-index', z + 100);
9046             
9047             return this._mask;
9048         },
9049
9050         /**
9051          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9052          * it is cached for reuse.
9053          */
9054         unmask : function(removeEl){
9055             if(this._mask){
9056                 if(removeEl === true){
9057                     this._mask.remove();
9058                     delete this._mask;
9059                     if(this._maskMsg){
9060                         this._maskMsg.remove();
9061                         delete this._maskMsg;
9062                     }
9063                 }else{
9064                     this._mask.setDisplayed(false);
9065                     if(this._maskMsg){
9066                         this._maskMsg.setDisplayed(false);
9067                     }
9068                 }
9069             }
9070             this.removeClass("x-masked");
9071         },
9072
9073         /**
9074          * Returns true if this element is masked
9075          * @return {Boolean}
9076          */
9077         isMasked : function(){
9078             return this._mask && this._mask.isVisible();
9079         },
9080
9081         /**
9082          * Creates an iframe shim for this element to keep selects and other windowed objects from
9083          * showing through.
9084          * @return {Roo.Element} The new shim element
9085          */
9086         createShim : function(){
9087             var el = document.createElement('iframe');
9088             el.frameBorder = 'no';
9089             el.className = 'roo-shim';
9090             if(Roo.isIE && Roo.isSecure){
9091                 el.src = Roo.SSL_SECURE_URL;
9092             }
9093             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9094             shim.autoBoxAdjust = false;
9095             return shim;
9096         },
9097
9098         /**
9099          * Removes this element from the DOM and deletes it from the cache
9100          */
9101         remove : function(){
9102             if(this.dom.parentNode){
9103                 this.dom.parentNode.removeChild(this.dom);
9104             }
9105             delete El.cache[this.dom.id];
9106         },
9107
9108         /**
9109          * Sets up event handlers to add and remove a css class when the mouse is over this element
9110          * @param {String} className
9111          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9112          * mouseout events for children elements
9113          * @return {Roo.Element} this
9114          */
9115         addClassOnOver : function(className, preventFlicker){
9116             this.on("mouseover", function(){
9117                 Roo.fly(this, '_internal').addClass(className);
9118             }, this.dom);
9119             var removeFn = function(e){
9120                 if(preventFlicker !== true || !e.within(this, true)){
9121                     Roo.fly(this, '_internal').removeClass(className);
9122                 }
9123             };
9124             this.on("mouseout", removeFn, this.dom);
9125             return this;
9126         },
9127
9128         /**
9129          * Sets up event handlers to add and remove a css class when this element has the focus
9130          * @param {String} className
9131          * @return {Roo.Element} this
9132          */
9133         addClassOnFocus : function(className){
9134             this.on("focus", function(){
9135                 Roo.fly(this, '_internal').addClass(className);
9136             }, this.dom);
9137             this.on("blur", function(){
9138                 Roo.fly(this, '_internal').removeClass(className);
9139             }, this.dom);
9140             return this;
9141         },
9142         /**
9143          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9144          * @param {String} className
9145          * @return {Roo.Element} this
9146          */
9147         addClassOnClick : function(className){
9148             var dom = this.dom;
9149             this.on("mousedown", function(){
9150                 Roo.fly(dom, '_internal').addClass(className);
9151                 var d = Roo.get(document);
9152                 var fn = function(){
9153                     Roo.fly(dom, '_internal').removeClass(className);
9154                     d.removeListener("mouseup", fn);
9155                 };
9156                 d.on("mouseup", fn);
9157             });
9158             return this;
9159         },
9160
9161         /**
9162          * Stops the specified event from bubbling and optionally prevents the default action
9163          * @param {String} eventName
9164          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9165          * @return {Roo.Element} this
9166          */
9167         swallowEvent : function(eventName, preventDefault){
9168             var fn = function(e){
9169                 e.stopPropagation();
9170                 if(preventDefault){
9171                     e.preventDefault();
9172                 }
9173             };
9174             if(eventName instanceof Array){
9175                 for(var i = 0, len = eventName.length; i < len; i++){
9176                      this.on(eventName[i], fn);
9177                 }
9178                 return this;
9179             }
9180             this.on(eventName, fn);
9181             return this;
9182         },
9183
9184         /**
9185          * @private
9186          */
9187       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9188
9189         /**
9190          * Sizes this element to its parent element's dimensions performing
9191          * neccessary box adjustments.
9192          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9193          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9194          * @return {Roo.Element} this
9195          */
9196         fitToParent : function(monitorResize, targetParent) {
9197           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9198           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9199           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9200             return;
9201           }
9202           var p = Roo.get(targetParent || this.dom.parentNode);
9203           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9204           if (monitorResize === true) {
9205             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9206             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9207           }
9208           return this;
9209         },
9210
9211         /**
9212          * Gets the next sibling, skipping text nodes
9213          * @return {HTMLElement} The next sibling or null
9214          */
9215         getNextSibling : function(){
9216             var n = this.dom.nextSibling;
9217             while(n && n.nodeType != 1){
9218                 n = n.nextSibling;
9219             }
9220             return n;
9221         },
9222
9223         /**
9224          * Gets the previous sibling, skipping text nodes
9225          * @return {HTMLElement} The previous sibling or null
9226          */
9227         getPrevSibling : function(){
9228             var n = this.dom.previousSibling;
9229             while(n && n.nodeType != 1){
9230                 n = n.previousSibling;
9231             }
9232             return n;
9233         },
9234
9235
9236         /**
9237          * Appends the passed element(s) to this element
9238          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9239          * @return {Roo.Element} this
9240          */
9241         appendChild: function(el){
9242             el = Roo.get(el);
9243             el.appendTo(this);
9244             return this;
9245         },
9246
9247         /**
9248          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9249          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9250          * automatically generated with the specified attributes.
9251          * @param {HTMLElement} insertBefore (optional) a child element of this element
9252          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9253          * @return {Roo.Element} The new child element
9254          */
9255         createChild: function(config, insertBefore, returnDom){
9256             config = config || {tag:'div'};
9257             if(insertBefore){
9258                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9259             }
9260             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9261         },
9262
9263         /**
9264          * Appends this element to the passed element
9265          * @param {String/HTMLElement/Element} el The new parent element
9266          * @return {Roo.Element} this
9267          */
9268         appendTo: function(el){
9269             el = Roo.getDom(el);
9270             el.appendChild(this.dom);
9271             return this;
9272         },
9273
9274         /**
9275          * Inserts this element before the passed element in the DOM
9276          * @param {String/HTMLElement/Element} el The element to insert before
9277          * @return {Roo.Element} this
9278          */
9279         insertBefore: function(el){
9280             el = Roo.getDom(el);
9281             el.parentNode.insertBefore(this.dom, el);
9282             return this;
9283         },
9284
9285         /**
9286          * Inserts this element after the passed element in the DOM
9287          * @param {String/HTMLElement/Element} el The element to insert after
9288          * @return {Roo.Element} this
9289          */
9290         insertAfter: function(el){
9291             el = Roo.getDom(el);
9292             el.parentNode.insertBefore(this.dom, el.nextSibling);
9293             return this;
9294         },
9295
9296         /**
9297          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9298          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9299          * @return {Roo.Element} The new child
9300          */
9301         insertFirst: function(el, returnDom){
9302             el = el || {};
9303             if(typeof el == 'object' && !el.nodeType){ // dh config
9304                 return this.createChild(el, this.dom.firstChild, returnDom);
9305             }else{
9306                 el = Roo.getDom(el);
9307                 this.dom.insertBefore(el, this.dom.firstChild);
9308                 return !returnDom ? Roo.get(el) : el;
9309             }
9310         },
9311
9312         /**
9313          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9314          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9315          * @param {String} where (optional) 'before' or 'after' defaults to before
9316          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9317          * @return {Roo.Element} the inserted Element
9318          */
9319         insertSibling: function(el, where, returnDom){
9320             where = where ? where.toLowerCase() : 'before';
9321             el = el || {};
9322             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9323
9324             if(typeof el == 'object' && !el.nodeType){ // dh config
9325                 if(where == 'after' && !this.dom.nextSibling){
9326                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9327                 }else{
9328                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9329                 }
9330
9331             }else{
9332                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9333                             where == 'before' ? this.dom : this.dom.nextSibling);
9334                 if(!returnDom){
9335                     rt = Roo.get(rt);
9336                 }
9337             }
9338             return rt;
9339         },
9340
9341         /**
9342          * Creates and wraps this element with another element
9343          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9344          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9345          * @return {HTMLElement/Element} The newly created wrapper element
9346          */
9347         wrap: function(config, returnDom){
9348             if(!config){
9349                 config = {tag: "div"};
9350             }
9351             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9352             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9353             return newEl;
9354         },
9355
9356         /**
9357          * Replaces the passed element with this element
9358          * @param {String/HTMLElement/Element} el The element to replace
9359          * @return {Roo.Element} this
9360          */
9361         replace: function(el){
9362             el = Roo.get(el);
9363             this.insertBefore(el);
9364             el.remove();
9365             return this;
9366         },
9367
9368         /**
9369          * Inserts an html fragment into this element
9370          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9371          * @param {String} html The HTML fragment
9372          * @param {Boolean} returnEl True to return an Roo.Element
9373          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9374          */
9375         insertHtml : function(where, html, returnEl){
9376             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9377             return returnEl ? Roo.get(el) : el;
9378         },
9379
9380         /**
9381          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9382          * @param {Object} o The object with the attributes
9383          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9384          * @return {Roo.Element} this
9385          */
9386         set : function(o, useSet){
9387             var el = this.dom;
9388             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9389             for(var attr in o){
9390                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9391                 if(attr=="cls"){
9392                     el.className = o["cls"];
9393                 }else{
9394                     if(useSet) {
9395                         el.setAttribute(attr, o[attr]);
9396                     } else {
9397                         el[attr] = o[attr];
9398                     }
9399                 }
9400             }
9401             if(o.style){
9402                 Roo.DomHelper.applyStyles(el, o.style);
9403             }
9404             return this;
9405         },
9406
9407         /**
9408          * Convenience method for constructing a KeyMap
9409          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9410          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9411          * @param {Function} fn The function to call
9412          * @param {Object} scope (optional) The scope of the function
9413          * @return {Roo.KeyMap} The KeyMap created
9414          */
9415         addKeyListener : function(key, fn, scope){
9416             var config;
9417             if(typeof key != "object" || key instanceof Array){
9418                 config = {
9419                     key: key,
9420                     fn: fn,
9421                     scope: scope
9422                 };
9423             }else{
9424                 config = {
9425                     key : key.key,
9426                     shift : key.shift,
9427                     ctrl : key.ctrl,
9428                     alt : key.alt,
9429                     fn: fn,
9430                     scope: scope
9431                 };
9432             }
9433             return new Roo.KeyMap(this, config);
9434         },
9435
9436         /**
9437          * Creates a KeyMap for this element
9438          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9439          * @return {Roo.KeyMap} The KeyMap created
9440          */
9441         addKeyMap : function(config){
9442             return new Roo.KeyMap(this, config);
9443         },
9444
9445         /**
9446          * Returns true if this element is scrollable.
9447          * @return {Boolean}
9448          */
9449          isScrollable : function(){
9450             var dom = this.dom;
9451             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9452         },
9453
9454         /**
9455          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9456          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9457          * @param {Number} value The new scroll value
9458          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9459          * @return {Element} this
9460          */
9461
9462         scrollTo : function(side, value, animate){
9463             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9464             if(!animate || !A){
9465                 this.dom[prop] = value;
9466             }else{
9467                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9468                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9469             }
9470             return this;
9471         },
9472
9473         /**
9474          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9475          * within this element's scrollable range.
9476          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9477          * @param {Number} distance How far to scroll the element in pixels
9478          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9479          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9480          * was scrolled as far as it could go.
9481          */
9482          scroll : function(direction, distance, animate){
9483              if(!this.isScrollable()){
9484                  return;
9485              }
9486              var el = this.dom;
9487              var l = el.scrollLeft, t = el.scrollTop;
9488              var w = el.scrollWidth, h = el.scrollHeight;
9489              var cw = el.clientWidth, ch = el.clientHeight;
9490              direction = direction.toLowerCase();
9491              var scrolled = false;
9492              var a = this.preanim(arguments, 2);
9493              switch(direction){
9494                  case "l":
9495                  case "left":
9496                      if(w - l > cw){
9497                          var v = Math.min(l + distance, w-cw);
9498                          this.scrollTo("left", v, a);
9499                          scrolled = true;
9500                      }
9501                      break;
9502                 case "r":
9503                 case "right":
9504                      if(l > 0){
9505                          var v = Math.max(l - distance, 0);
9506                          this.scrollTo("left", v, a);
9507                          scrolled = true;
9508                      }
9509                      break;
9510                 case "t":
9511                 case "top":
9512                 case "up":
9513                      if(t > 0){
9514                          var v = Math.max(t - distance, 0);
9515                          this.scrollTo("top", v, a);
9516                          scrolled = true;
9517                      }
9518                      break;
9519                 case "b":
9520                 case "bottom":
9521                 case "down":
9522                      if(h - t > ch){
9523                          var v = Math.min(t + distance, h-ch);
9524                          this.scrollTo("top", v, a);
9525                          scrolled = true;
9526                      }
9527                      break;
9528              }
9529              return scrolled;
9530         },
9531
9532         /**
9533          * Translates the passed page coordinates into left/top css values for this element
9534          * @param {Number/Array} x The page x or an array containing [x, y]
9535          * @param {Number} y The page y
9536          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9537          */
9538         translatePoints : function(x, y){
9539             if(typeof x == 'object' || x instanceof Array){
9540                 y = x[1]; x = x[0];
9541             }
9542             var p = this.getStyle('position');
9543             var o = this.getXY();
9544
9545             var l = parseInt(this.getStyle('left'), 10);
9546             var t = parseInt(this.getStyle('top'), 10);
9547
9548             if(isNaN(l)){
9549                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9550             }
9551             if(isNaN(t)){
9552                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9553             }
9554
9555             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9556         },
9557
9558         /**
9559          * Returns the current scroll position of the element.
9560          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9561          */
9562         getScroll : function(){
9563             var d = this.dom, doc = document;
9564             if(d == doc || d == doc.body){
9565                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9566                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9567                 return {left: l, top: t};
9568             }else{
9569                 return {left: d.scrollLeft, top: d.scrollTop};
9570             }
9571         },
9572
9573         /**
9574          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9575          * are convert to standard 6 digit hex color.
9576          * @param {String} attr The css attribute
9577          * @param {String} defaultValue The default value to use when a valid color isn't found
9578          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9579          * YUI color anims.
9580          */
9581         getColor : function(attr, defaultValue, prefix){
9582             var v = this.getStyle(attr);
9583             if(!v || v == "transparent" || v == "inherit") {
9584                 return defaultValue;
9585             }
9586             var color = typeof prefix == "undefined" ? "#" : prefix;
9587             if(v.substr(0, 4) == "rgb("){
9588                 var rvs = v.slice(4, v.length -1).split(",");
9589                 for(var i = 0; i < 3; i++){
9590                     var h = parseInt(rvs[i]).toString(16);
9591                     if(h < 16){
9592                         h = "0" + h;
9593                     }
9594                     color += h;
9595                 }
9596             } else {
9597                 if(v.substr(0, 1) == "#"){
9598                     if(v.length == 4) {
9599                         for(var i = 1; i < 4; i++){
9600                             var c = v.charAt(i);
9601                             color +=  c + c;
9602                         }
9603                     }else if(v.length == 7){
9604                         color += v.substr(1);
9605                     }
9606                 }
9607             }
9608             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9609         },
9610
9611         /**
9612          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9613          * gradient background, rounded corners and a 4-way shadow.
9614          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9615          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9616          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9617          * @return {Roo.Element} this
9618          */
9619         boxWrap : function(cls){
9620             cls = cls || 'x-box';
9621             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9622             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9623             return el;
9624         },
9625
9626         /**
9627          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9628          * @param {String} namespace The namespace in which to look for the attribute
9629          * @param {String} name The attribute name
9630          * @return {String} The attribute value
9631          */
9632         getAttributeNS : Roo.isIE ? function(ns, name){
9633             var d = this.dom;
9634             var type = typeof d[ns+":"+name];
9635             if(type != 'undefined' && type != 'unknown'){
9636                 return d[ns+":"+name];
9637             }
9638             return d[name];
9639         } : function(ns, name){
9640             var d = this.dom;
9641             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9642         },
9643         
9644         
9645         /**
9646          * Sets or Returns the value the dom attribute value
9647          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9648          * @param {String} value (optional) The value to set the attribute to
9649          * @return {String} The attribute value
9650          */
9651         attr : function(name){
9652             if (arguments.length > 1) {
9653                 this.dom.setAttribute(name, arguments[1]);
9654                 return arguments[1];
9655             }
9656             if (typeof(name) == 'object') {
9657                 for(var i in name) {
9658                     this.attr(i, name[i]);
9659                 }
9660                 return name;
9661             }
9662             
9663             
9664             if (!this.dom.hasAttribute(name)) {
9665                 return undefined;
9666             }
9667             return this.dom.getAttribute(name);
9668         }
9669         
9670         
9671         
9672     };
9673
9674     var ep = El.prototype;
9675
9676     /**
9677      * Appends an event handler (Shorthand for addListener)
9678      * @param {String}   eventName     The type of event to append
9679      * @param {Function} fn        The method the event invokes
9680      * @param {Object} scope       (optional) The scope (this object) of the fn
9681      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9682      * @method
9683      */
9684     ep.on = ep.addListener;
9685         // backwards compat
9686     ep.mon = ep.addListener;
9687
9688     /**
9689      * Removes an event handler from this element (shorthand for removeListener)
9690      * @param {String} eventName the type of event to remove
9691      * @param {Function} fn the method the event invokes
9692      * @return {Roo.Element} this
9693      * @method
9694      */
9695     ep.un = ep.removeListener;
9696
9697     /**
9698      * true to automatically adjust width and height settings for box-model issues (default to true)
9699      */
9700     ep.autoBoxAdjust = true;
9701
9702     // private
9703     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9704
9705     // private
9706     El.addUnits = function(v, defaultUnit){
9707         if(v === "" || v == "auto"){
9708             return v;
9709         }
9710         if(v === undefined){
9711             return '';
9712         }
9713         if(typeof v == "number" || !El.unitPattern.test(v)){
9714             return v + (defaultUnit || 'px');
9715         }
9716         return v;
9717     };
9718
9719     // special markup used throughout Roo when box wrapping elements
9720     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9721     /**
9722      * Visibility mode constant - Use visibility to hide element
9723      * @static
9724      * @type Number
9725      */
9726     El.VISIBILITY = 1;
9727     /**
9728      * Visibility mode constant - Use display to hide element
9729      * @static
9730      * @type Number
9731      */
9732     El.DISPLAY = 2;
9733
9734     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9735     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9736     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9737
9738
9739
9740     /**
9741      * @private
9742      */
9743     El.cache = {};
9744
9745     var docEl;
9746
9747     /**
9748      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9749      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9750      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9751      * @return {Element} The Element object
9752      * @static
9753      */
9754     El.get = function(el){
9755         var ex, elm, id;
9756         if(!el){ return null; }
9757         if(typeof el == "string"){ // element id
9758             if(!(elm = document.getElementById(el))){
9759                 return null;
9760             }
9761             if(ex = El.cache[el]){
9762                 ex.dom = elm;
9763             }else{
9764                 ex = El.cache[el] = new El(elm);
9765             }
9766             return ex;
9767         }else if(el.tagName){ // dom element
9768             if(!(id = el.id)){
9769                 id = Roo.id(el);
9770             }
9771             if(ex = El.cache[id]){
9772                 ex.dom = el;
9773             }else{
9774                 ex = El.cache[id] = new El(el);
9775             }
9776             return ex;
9777         }else if(el instanceof El){
9778             if(el != docEl){
9779                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9780                                                               // catch case where it hasn't been appended
9781                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9782             }
9783             return el;
9784         }else if(el.isComposite){
9785             return el;
9786         }else if(el instanceof Array){
9787             return El.select(el);
9788         }else if(el == document){
9789             // create a bogus element object representing the document object
9790             if(!docEl){
9791                 var f = function(){};
9792                 f.prototype = El.prototype;
9793                 docEl = new f();
9794                 docEl.dom = document;
9795             }
9796             return docEl;
9797         }
9798         return null;
9799     };
9800
9801     // private
9802     El.uncache = function(el){
9803         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9804             if(a[i]){
9805                 delete El.cache[a[i].id || a[i]];
9806             }
9807         }
9808     };
9809
9810     // private
9811     // Garbage collection - uncache elements/purge listeners on orphaned elements
9812     // so we don't hold a reference and cause the browser to retain them
9813     El.garbageCollect = function(){
9814         if(!Roo.enableGarbageCollector){
9815             clearInterval(El.collectorThread);
9816             return;
9817         }
9818         for(var eid in El.cache){
9819             var el = El.cache[eid], d = el.dom;
9820             // -------------------------------------------------------
9821             // Determining what is garbage:
9822             // -------------------------------------------------------
9823             // !d
9824             // dom node is null, definitely garbage
9825             // -------------------------------------------------------
9826             // !d.parentNode
9827             // no parentNode == direct orphan, definitely garbage
9828             // -------------------------------------------------------
9829             // !d.offsetParent && !document.getElementById(eid)
9830             // display none elements have no offsetParent so we will
9831             // also try to look it up by it's id. However, check
9832             // offsetParent first so we don't do unneeded lookups.
9833             // This enables collection of elements that are not orphans
9834             // directly, but somewhere up the line they have an orphan
9835             // parent.
9836             // -------------------------------------------------------
9837             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9838                 delete El.cache[eid];
9839                 if(d && Roo.enableListenerCollection){
9840                     E.purgeElement(d);
9841                 }
9842             }
9843         }
9844     }
9845     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9846
9847
9848     // dom is optional
9849     El.Flyweight = function(dom){
9850         this.dom = dom;
9851     };
9852     El.Flyweight.prototype = El.prototype;
9853
9854     El._flyweights = {};
9855     /**
9856      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9857      * the dom node can be overwritten by other code.
9858      * @param {String/HTMLElement} el The dom node or id
9859      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9860      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9861      * @static
9862      * @return {Element} The shared Element object
9863      */
9864     El.fly = function(el, named){
9865         named = named || '_global';
9866         el = Roo.getDom(el);
9867         if(!el){
9868             return null;
9869         }
9870         if(!El._flyweights[named]){
9871             El._flyweights[named] = new El.Flyweight();
9872         }
9873         El._flyweights[named].dom = el;
9874         return El._flyweights[named];
9875     };
9876
9877     /**
9878      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9879      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9880      * Shorthand of {@link Roo.Element#get}
9881      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9882      * @return {Element} The Element object
9883      * @member Roo
9884      * @method get
9885      */
9886     Roo.get = El.get;
9887     /**
9888      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9889      * the dom node can be overwritten by other code.
9890      * Shorthand of {@link Roo.Element#fly}
9891      * @param {String/HTMLElement} el The dom node or id
9892      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9893      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9894      * @static
9895      * @return {Element} The shared Element object
9896      * @member Roo
9897      * @method fly
9898      */
9899     Roo.fly = El.fly;
9900
9901     // speedy lookup for elements never to box adjust
9902     var noBoxAdjust = Roo.isStrict ? {
9903         select:1
9904     } : {
9905         input:1, select:1, textarea:1
9906     };
9907     if(Roo.isIE || Roo.isGecko){
9908         noBoxAdjust['button'] = 1;
9909     }
9910
9911
9912     Roo.EventManager.on(window, 'unload', function(){
9913         delete El.cache;
9914         delete El._flyweights;
9915     });
9916 })();
9917
9918
9919
9920
9921 if(Roo.DomQuery){
9922     Roo.Element.selectorFunction = Roo.DomQuery.select;
9923 }
9924
9925 Roo.Element.select = function(selector, unique, root){
9926     var els;
9927     if(typeof selector == "string"){
9928         els = Roo.Element.selectorFunction(selector, root);
9929     }else if(selector.length !== undefined){
9930         els = selector;
9931     }else{
9932         throw "Invalid selector";
9933     }
9934     if(unique === true){
9935         return new Roo.CompositeElement(els);
9936     }else{
9937         return new Roo.CompositeElementLite(els);
9938     }
9939 };
9940 /**
9941  * Selects elements based on the passed CSS selector to enable working on them as 1.
9942  * @param {String/Array} selector The CSS selector or an array of elements
9943  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9944  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9945  * @return {CompositeElementLite/CompositeElement}
9946  * @member Roo
9947  * @method select
9948  */
9949 Roo.select = Roo.Element.select;
9950
9951
9952
9953
9954
9955
9956
9957
9958
9959
9960
9961
9962
9963
9964 /*
9965  * Based on:
9966  * Ext JS Library 1.1.1
9967  * Copyright(c) 2006-2007, Ext JS, LLC.
9968  *
9969  * Originally Released Under LGPL - original licence link has changed is not relivant.
9970  *
9971  * Fork - LGPL
9972  * <script type="text/javascript">
9973  */
9974
9975
9976
9977 //Notifies Element that fx methods are available
9978 Roo.enableFx = true;
9979
9980 /**
9981  * @class Roo.Fx
9982  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9983  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9984  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9985  * Element effects to work.</p><br/>
9986  *
9987  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9988  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9989  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9990  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9991  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9992  * expected results and should be done with care.</p><br/>
9993  *
9994  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9995  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9996 <pre>
9997 Value  Description
9998 -----  -----------------------------
9999 tl     The top left corner
10000 t      The center of the top edge
10001 tr     The top right corner
10002 l      The center of the left edge
10003 r      The center of the right edge
10004 bl     The bottom left corner
10005 b      The center of the bottom edge
10006 br     The bottom right corner
10007 </pre>
10008  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10009  * below are common options that can be passed to any Fx method.</b>
10010  * @cfg {Function} callback A function called when the effect is finished
10011  * @cfg {Object} scope The scope of the effect function
10012  * @cfg {String} easing A valid Easing value for the effect
10013  * @cfg {String} afterCls A css class to apply after the effect
10014  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10015  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10016  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10017  * effects that end with the element being visually hidden, ignored otherwise)
10018  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10019  * a function which returns such a specification that will be applied to the Element after the effect finishes
10020  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10021  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
10022  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10023  */
10024 Roo.Fx = {
10025         /**
10026          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10027          * origin for the slide effect.  This function automatically handles wrapping the element with
10028          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10029          * Usage:
10030          *<pre><code>
10031 // default: slide the element in from the top
10032 el.slideIn();
10033
10034 // custom: slide the element in from the right with a 2-second duration
10035 el.slideIn('r', { duration: 2 });
10036
10037 // common config options shown with default values
10038 el.slideIn('t', {
10039     easing: 'easeOut',
10040     duration: .5
10041 });
10042 </code></pre>
10043          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10044          * @param {Object} options (optional) Object literal with any of the Fx config options
10045          * @return {Roo.Element} The Element
10046          */
10047     slideIn : function(anchor, o){
10048         var el = this.getFxEl();
10049         o = o || {};
10050
10051         el.queueFx(o, function(){
10052
10053             anchor = anchor || "t";
10054
10055             // fix display to visibility
10056             this.fixDisplay();
10057
10058             // restore values after effect
10059             var r = this.getFxRestore();
10060             var b = this.getBox();
10061             // fixed size for slide
10062             this.setSize(b);
10063
10064             // wrap if needed
10065             var wrap = this.fxWrap(r.pos, o, "hidden");
10066
10067             var st = this.dom.style;
10068             st.visibility = "visible";
10069             st.position = "absolute";
10070
10071             // clear out temp styles after slide and unwrap
10072             var after = function(){
10073                 el.fxUnwrap(wrap, r.pos, o);
10074                 st.width = r.width;
10075                 st.height = r.height;
10076                 el.afterFx(o);
10077             };
10078             // time to calc the positions
10079             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10080
10081             switch(anchor.toLowerCase()){
10082                 case "t":
10083                     wrap.setSize(b.width, 0);
10084                     st.left = st.bottom = "0";
10085                     a = {height: bh};
10086                 break;
10087                 case "l":
10088                     wrap.setSize(0, b.height);
10089                     st.right = st.top = "0";
10090                     a = {width: bw};
10091                 break;
10092                 case "r":
10093                     wrap.setSize(0, b.height);
10094                     wrap.setX(b.right);
10095                     st.left = st.top = "0";
10096                     a = {width: bw, points: pt};
10097                 break;
10098                 case "b":
10099                     wrap.setSize(b.width, 0);
10100                     wrap.setY(b.bottom);
10101                     st.left = st.top = "0";
10102                     a = {height: bh, points: pt};
10103                 break;
10104                 case "tl":
10105                     wrap.setSize(0, 0);
10106                     st.right = st.bottom = "0";
10107                     a = {width: bw, height: bh};
10108                 break;
10109                 case "bl":
10110                     wrap.setSize(0, 0);
10111                     wrap.setY(b.y+b.height);
10112                     st.right = st.top = "0";
10113                     a = {width: bw, height: bh, points: pt};
10114                 break;
10115                 case "br":
10116                     wrap.setSize(0, 0);
10117                     wrap.setXY([b.right, b.bottom]);
10118                     st.left = st.top = "0";
10119                     a = {width: bw, height: bh, points: pt};
10120                 break;
10121                 case "tr":
10122                     wrap.setSize(0, 0);
10123                     wrap.setX(b.x+b.width);
10124                     st.left = st.bottom = "0";
10125                     a = {width: bw, height: bh, points: pt};
10126                 break;
10127             }
10128             this.dom.style.visibility = "visible";
10129             wrap.show();
10130
10131             arguments.callee.anim = wrap.fxanim(a,
10132                 o,
10133                 'motion',
10134                 .5,
10135                 'easeOut', after);
10136         });
10137         return this;
10138     },
10139     
10140         /**
10141          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10142          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10143          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10144          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10145          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10146          * Usage:
10147          *<pre><code>
10148 // default: slide the element out to the top
10149 el.slideOut();
10150
10151 // custom: slide the element out to the right with a 2-second duration
10152 el.slideOut('r', { duration: 2 });
10153
10154 // common config options shown with default values
10155 el.slideOut('t', {
10156     easing: 'easeOut',
10157     duration: .5,
10158     remove: false,
10159     useDisplay: false
10160 });
10161 </code></pre>
10162          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10163          * @param {Object} options (optional) Object literal with any of the Fx config options
10164          * @return {Roo.Element} The Element
10165          */
10166     slideOut : function(anchor, o){
10167         var el = this.getFxEl();
10168         o = o || {};
10169
10170         el.queueFx(o, function(){
10171
10172             anchor = anchor || "t";
10173
10174             // restore values after effect
10175             var r = this.getFxRestore();
10176             
10177             var b = this.getBox();
10178             // fixed size for slide
10179             this.setSize(b);
10180
10181             // wrap if needed
10182             var wrap = this.fxWrap(r.pos, o, "visible");
10183
10184             var st = this.dom.style;
10185             st.visibility = "visible";
10186             st.position = "absolute";
10187
10188             wrap.setSize(b);
10189
10190             var after = function(){
10191                 if(o.useDisplay){
10192                     el.setDisplayed(false);
10193                 }else{
10194                     el.hide();
10195                 }
10196
10197                 el.fxUnwrap(wrap, r.pos, o);
10198
10199                 st.width = r.width;
10200                 st.height = r.height;
10201
10202                 el.afterFx(o);
10203             };
10204
10205             var a, zero = {to: 0};
10206             switch(anchor.toLowerCase()){
10207                 case "t":
10208                     st.left = st.bottom = "0";
10209                     a = {height: zero};
10210                 break;
10211                 case "l":
10212                     st.right = st.top = "0";
10213                     a = {width: zero};
10214                 break;
10215                 case "r":
10216                     st.left = st.top = "0";
10217                     a = {width: zero, points: {to:[b.right, b.y]}};
10218                 break;
10219                 case "b":
10220                     st.left = st.top = "0";
10221                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10222                 break;
10223                 case "tl":
10224                     st.right = st.bottom = "0";
10225                     a = {width: zero, height: zero};
10226                 break;
10227                 case "bl":
10228                     st.right = st.top = "0";
10229                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10230                 break;
10231                 case "br":
10232                     st.left = st.top = "0";
10233                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10234                 break;
10235                 case "tr":
10236                     st.left = st.bottom = "0";
10237                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10238                 break;
10239             }
10240
10241             arguments.callee.anim = wrap.fxanim(a,
10242                 o,
10243                 'motion',
10244                 .5,
10245                 "easeOut", after);
10246         });
10247         return this;
10248     },
10249
10250         /**
10251          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10252          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10253          * The element must be removed from the DOM using the 'remove' config option if desired.
10254          * Usage:
10255          *<pre><code>
10256 // default
10257 el.puff();
10258
10259 // common config options shown with default values
10260 el.puff({
10261     easing: 'easeOut',
10262     duration: .5,
10263     remove: false,
10264     useDisplay: false
10265 });
10266 </code></pre>
10267          * @param {Object} options (optional) Object literal with any of the Fx config options
10268          * @return {Roo.Element} The Element
10269          */
10270     puff : function(o){
10271         var el = this.getFxEl();
10272         o = o || {};
10273
10274         el.queueFx(o, function(){
10275             this.clearOpacity();
10276             this.show();
10277
10278             // restore values after effect
10279             var r = this.getFxRestore();
10280             var st = this.dom.style;
10281
10282             var after = function(){
10283                 if(o.useDisplay){
10284                     el.setDisplayed(false);
10285                 }else{
10286                     el.hide();
10287                 }
10288
10289                 el.clearOpacity();
10290
10291                 el.setPositioning(r.pos);
10292                 st.width = r.width;
10293                 st.height = r.height;
10294                 st.fontSize = '';
10295                 el.afterFx(o);
10296             };
10297
10298             var width = this.getWidth();
10299             var height = this.getHeight();
10300
10301             arguments.callee.anim = this.fxanim({
10302                     width : {to: this.adjustWidth(width * 2)},
10303                     height : {to: this.adjustHeight(height * 2)},
10304                     points : {by: [-(width * .5), -(height * .5)]},
10305                     opacity : {to: 0},
10306                     fontSize: {to:200, unit: "%"}
10307                 },
10308                 o,
10309                 'motion',
10310                 .5,
10311                 "easeOut", after);
10312         });
10313         return this;
10314     },
10315
10316         /**
10317          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10318          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10319          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10320          * Usage:
10321          *<pre><code>
10322 // default
10323 el.switchOff();
10324
10325 // all config options shown with default values
10326 el.switchOff({
10327     easing: 'easeIn',
10328     duration: .3,
10329     remove: false,
10330     useDisplay: false
10331 });
10332 </code></pre>
10333          * @param {Object} options (optional) Object literal with any of the Fx config options
10334          * @return {Roo.Element} The Element
10335          */
10336     switchOff : function(o){
10337         var el = this.getFxEl();
10338         o = o || {};
10339
10340         el.queueFx(o, function(){
10341             this.clearOpacity();
10342             this.clip();
10343
10344             // restore values after effect
10345             var r = this.getFxRestore();
10346             var st = this.dom.style;
10347
10348             var after = function(){
10349                 if(o.useDisplay){
10350                     el.setDisplayed(false);
10351                 }else{
10352                     el.hide();
10353                 }
10354
10355                 el.clearOpacity();
10356                 el.setPositioning(r.pos);
10357                 st.width = r.width;
10358                 st.height = r.height;
10359
10360                 el.afterFx(o);
10361             };
10362
10363             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10364                 this.clearOpacity();
10365                 (function(){
10366                     this.fxanim({
10367                         height:{to:1},
10368                         points:{by:[0, this.getHeight() * .5]}
10369                     }, o, 'motion', 0.3, 'easeIn', after);
10370                 }).defer(100, this);
10371             });
10372         });
10373         return this;
10374     },
10375
10376     /**
10377      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10378      * changed using the "attr" config option) and then fading back to the original color. If no original
10379      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10380      * Usage:
10381 <pre><code>
10382 // default: highlight background to yellow
10383 el.highlight();
10384
10385 // custom: highlight foreground text to blue for 2 seconds
10386 el.highlight("0000ff", { attr: 'color', duration: 2 });
10387
10388 // common config options shown with default values
10389 el.highlight("ffff9c", {
10390     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10391     endColor: (current color) or "ffffff",
10392     easing: 'easeIn',
10393     duration: 1
10394 });
10395 </code></pre>
10396      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10397      * @param {Object} options (optional) Object literal with any of the Fx config options
10398      * @return {Roo.Element} The Element
10399      */ 
10400     highlight : function(color, o){
10401         var el = this.getFxEl();
10402         o = o || {};
10403
10404         el.queueFx(o, function(){
10405             color = color || "ffff9c";
10406             attr = o.attr || "backgroundColor";
10407
10408             this.clearOpacity();
10409             this.show();
10410
10411             var origColor = this.getColor(attr);
10412             var restoreColor = this.dom.style[attr];
10413             endColor = (o.endColor || origColor) || "ffffff";
10414
10415             var after = function(){
10416                 el.dom.style[attr] = restoreColor;
10417                 el.afterFx(o);
10418             };
10419
10420             var a = {};
10421             a[attr] = {from: color, to: endColor};
10422             arguments.callee.anim = this.fxanim(a,
10423                 o,
10424                 'color',
10425                 1,
10426                 'easeIn', after);
10427         });
10428         return this;
10429     },
10430
10431    /**
10432     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10433     * Usage:
10434 <pre><code>
10435 // default: a single light blue ripple
10436 el.frame();
10437
10438 // custom: 3 red ripples lasting 3 seconds total
10439 el.frame("ff0000", 3, { duration: 3 });
10440
10441 // common config options shown with default values
10442 el.frame("C3DAF9", 1, {
10443     duration: 1 //duration of entire animation (not each individual ripple)
10444     // Note: Easing is not configurable and will be ignored if included
10445 });
10446 </code></pre>
10447     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10448     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10449     * @param {Object} options (optional) Object literal with any of the Fx config options
10450     * @return {Roo.Element} The Element
10451     */
10452     frame : function(color, count, o){
10453         var el = this.getFxEl();
10454         o = o || {};
10455
10456         el.queueFx(o, function(){
10457             color = color || "#C3DAF9";
10458             if(color.length == 6){
10459                 color = "#" + color;
10460             }
10461             count = count || 1;
10462             duration = o.duration || 1;
10463             this.show();
10464
10465             var b = this.getBox();
10466             var animFn = function(){
10467                 var proxy = this.createProxy({
10468
10469                      style:{
10470                         visbility:"hidden",
10471                         position:"absolute",
10472                         "z-index":"35000", // yee haw
10473                         border:"0px solid " + color
10474                      }
10475                   });
10476                 var scale = Roo.isBorderBox ? 2 : 1;
10477                 proxy.animate({
10478                     top:{from:b.y, to:b.y - 20},
10479                     left:{from:b.x, to:b.x - 20},
10480                     borderWidth:{from:0, to:10},
10481                     opacity:{from:1, to:0},
10482                     height:{from:b.height, to:(b.height + (20*scale))},
10483                     width:{from:b.width, to:(b.width + (20*scale))}
10484                 }, duration, function(){
10485                     proxy.remove();
10486                 });
10487                 if(--count > 0){
10488                      animFn.defer((duration/2)*1000, this);
10489                 }else{
10490                     el.afterFx(o);
10491                 }
10492             };
10493             animFn.call(this);
10494         });
10495         return this;
10496     },
10497
10498    /**
10499     * Creates a pause before any subsequent queued effects begin.  If there are
10500     * no effects queued after the pause it will have no effect.
10501     * Usage:
10502 <pre><code>
10503 el.pause(1);
10504 </code></pre>
10505     * @param {Number} seconds The length of time to pause (in seconds)
10506     * @return {Roo.Element} The Element
10507     */
10508     pause : function(seconds){
10509         var el = this.getFxEl();
10510         var o = {};
10511
10512         el.queueFx(o, function(){
10513             setTimeout(function(){
10514                 el.afterFx(o);
10515             }, seconds * 1000);
10516         });
10517         return this;
10518     },
10519
10520    /**
10521     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10522     * using the "endOpacity" config option.
10523     * Usage:
10524 <pre><code>
10525 // default: fade in from opacity 0 to 100%
10526 el.fadeIn();
10527
10528 // custom: fade in from opacity 0 to 75% over 2 seconds
10529 el.fadeIn({ endOpacity: .75, duration: 2});
10530
10531 // common config options shown with default values
10532 el.fadeIn({
10533     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10534     easing: 'easeOut',
10535     duration: .5
10536 });
10537 </code></pre>
10538     * @param {Object} options (optional) Object literal with any of the Fx config options
10539     * @return {Roo.Element} The Element
10540     */
10541     fadeIn : function(o){
10542         var el = this.getFxEl();
10543         o = o || {};
10544         el.queueFx(o, function(){
10545             this.setOpacity(0);
10546             this.fixDisplay();
10547             this.dom.style.visibility = 'visible';
10548             var to = o.endOpacity || 1;
10549             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10550                 o, null, .5, "easeOut", function(){
10551                 if(to == 1){
10552                     this.clearOpacity();
10553                 }
10554                 el.afterFx(o);
10555             });
10556         });
10557         return this;
10558     },
10559
10560    /**
10561     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10562     * using the "endOpacity" config option.
10563     * Usage:
10564 <pre><code>
10565 // default: fade out from the element's current opacity to 0
10566 el.fadeOut();
10567
10568 // custom: fade out from the element's current opacity to 25% over 2 seconds
10569 el.fadeOut({ endOpacity: .25, duration: 2});
10570
10571 // common config options shown with default values
10572 el.fadeOut({
10573     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10574     easing: 'easeOut',
10575     duration: .5
10576     remove: false,
10577     useDisplay: false
10578 });
10579 </code></pre>
10580     * @param {Object} options (optional) Object literal with any of the Fx config options
10581     * @return {Roo.Element} The Element
10582     */
10583     fadeOut : function(o){
10584         var el = this.getFxEl();
10585         o = o || {};
10586         el.queueFx(o, function(){
10587             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10588                 o, null, .5, "easeOut", function(){
10589                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10590                      this.dom.style.display = "none";
10591                 }else{
10592                      this.dom.style.visibility = "hidden";
10593                 }
10594                 this.clearOpacity();
10595                 el.afterFx(o);
10596             });
10597         });
10598         return this;
10599     },
10600
10601    /**
10602     * Animates the transition of an element's dimensions from a starting height/width
10603     * to an ending height/width.
10604     * Usage:
10605 <pre><code>
10606 // change height and width to 100x100 pixels
10607 el.scale(100, 100);
10608
10609 // common config options shown with default values.  The height and width will default to
10610 // the element's existing values if passed as null.
10611 el.scale(
10612     [element's width],
10613     [element's height], {
10614     easing: 'easeOut',
10615     duration: .35
10616 });
10617 </code></pre>
10618     * @param {Number} width  The new width (pass undefined to keep the original width)
10619     * @param {Number} height  The new height (pass undefined to keep the original height)
10620     * @param {Object} options (optional) Object literal with any of the Fx config options
10621     * @return {Roo.Element} The Element
10622     */
10623     scale : function(w, h, o){
10624         this.shift(Roo.apply({}, o, {
10625             width: w,
10626             height: h
10627         }));
10628         return this;
10629     },
10630
10631    /**
10632     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10633     * Any of these properties not specified in the config object will not be changed.  This effect 
10634     * requires that at least one new dimension, position or opacity setting must be passed in on
10635     * the config object in order for the function to have any effect.
10636     * Usage:
10637 <pre><code>
10638 // slide the element horizontally to x position 200 while changing the height and opacity
10639 el.shift({ x: 200, height: 50, opacity: .8 });
10640
10641 // common config options shown with default values.
10642 el.shift({
10643     width: [element's width],
10644     height: [element's height],
10645     x: [element's x position],
10646     y: [element's y position],
10647     opacity: [element's opacity],
10648     easing: 'easeOut',
10649     duration: .35
10650 });
10651 </code></pre>
10652     * @param {Object} options  Object literal with any of the Fx config options
10653     * @return {Roo.Element} The Element
10654     */
10655     shift : function(o){
10656         var el = this.getFxEl();
10657         o = o || {};
10658         el.queueFx(o, function(){
10659             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10660             if(w !== undefined){
10661                 a.width = {to: this.adjustWidth(w)};
10662             }
10663             if(h !== undefined){
10664                 a.height = {to: this.adjustHeight(h)};
10665             }
10666             if(x !== undefined || y !== undefined){
10667                 a.points = {to: [
10668                     x !== undefined ? x : this.getX(),
10669                     y !== undefined ? y : this.getY()
10670                 ]};
10671             }
10672             if(op !== undefined){
10673                 a.opacity = {to: op};
10674             }
10675             if(o.xy !== undefined){
10676                 a.points = {to: o.xy};
10677             }
10678             arguments.callee.anim = this.fxanim(a,
10679                 o, 'motion', .35, "easeOut", function(){
10680                 el.afterFx(o);
10681             });
10682         });
10683         return this;
10684     },
10685
10686         /**
10687          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10688          * ending point of the effect.
10689          * Usage:
10690          *<pre><code>
10691 // default: slide the element downward while fading out
10692 el.ghost();
10693
10694 // custom: slide the element out to the right with a 2-second duration
10695 el.ghost('r', { duration: 2 });
10696
10697 // common config options shown with default values
10698 el.ghost('b', {
10699     easing: 'easeOut',
10700     duration: .5
10701     remove: false,
10702     useDisplay: false
10703 });
10704 </code></pre>
10705          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10706          * @param {Object} options (optional) Object literal with any of the Fx config options
10707          * @return {Roo.Element} The Element
10708          */
10709     ghost : function(anchor, o){
10710         var el = this.getFxEl();
10711         o = o || {};
10712
10713         el.queueFx(o, function(){
10714             anchor = anchor || "b";
10715
10716             // restore values after effect
10717             var r = this.getFxRestore();
10718             var w = this.getWidth(),
10719                 h = this.getHeight();
10720
10721             var st = this.dom.style;
10722
10723             var after = function(){
10724                 if(o.useDisplay){
10725                     el.setDisplayed(false);
10726                 }else{
10727                     el.hide();
10728                 }
10729
10730                 el.clearOpacity();
10731                 el.setPositioning(r.pos);
10732                 st.width = r.width;
10733                 st.height = r.height;
10734
10735                 el.afterFx(o);
10736             };
10737
10738             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10739             switch(anchor.toLowerCase()){
10740                 case "t":
10741                     pt.by = [0, -h];
10742                 break;
10743                 case "l":
10744                     pt.by = [-w, 0];
10745                 break;
10746                 case "r":
10747                     pt.by = [w, 0];
10748                 break;
10749                 case "b":
10750                     pt.by = [0, h];
10751                 break;
10752                 case "tl":
10753                     pt.by = [-w, -h];
10754                 break;
10755                 case "bl":
10756                     pt.by = [-w, h];
10757                 break;
10758                 case "br":
10759                     pt.by = [w, h];
10760                 break;
10761                 case "tr":
10762                     pt.by = [w, -h];
10763                 break;
10764             }
10765
10766             arguments.callee.anim = this.fxanim(a,
10767                 o,
10768                 'motion',
10769                 .5,
10770                 "easeOut", after);
10771         });
10772         return this;
10773     },
10774
10775         /**
10776          * Ensures that all effects queued after syncFx is called on the element are
10777          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10778          * @return {Roo.Element} The Element
10779          */
10780     syncFx : function(){
10781         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10782             block : false,
10783             concurrent : true,
10784             stopFx : false
10785         });
10786         return this;
10787     },
10788
10789         /**
10790          * Ensures that all effects queued after sequenceFx is called on the element are
10791          * run in sequence.  This is the opposite of {@link #syncFx}.
10792          * @return {Roo.Element} The Element
10793          */
10794     sequenceFx : function(){
10795         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10796             block : false,
10797             concurrent : false,
10798             stopFx : false
10799         });
10800         return this;
10801     },
10802
10803         /* @private */
10804     nextFx : function(){
10805         var ef = this.fxQueue[0];
10806         if(ef){
10807             ef.call(this);
10808         }
10809     },
10810
10811         /**
10812          * Returns true if the element has any effects actively running or queued, else returns false.
10813          * @return {Boolean} True if element has active effects, else false
10814          */
10815     hasActiveFx : function(){
10816         return this.fxQueue && this.fxQueue[0];
10817     },
10818
10819         /**
10820          * Stops any running effects and clears the element's internal effects queue if it contains
10821          * any additional effects that haven't started yet.
10822          * @return {Roo.Element} The Element
10823          */
10824     stopFx : function(){
10825         if(this.hasActiveFx()){
10826             var cur = this.fxQueue[0];
10827             if(cur && cur.anim && cur.anim.isAnimated()){
10828                 this.fxQueue = [cur]; // clear out others
10829                 cur.anim.stop(true);
10830             }
10831         }
10832         return this;
10833     },
10834
10835         /* @private */
10836     beforeFx : function(o){
10837         if(this.hasActiveFx() && !o.concurrent){
10838            if(o.stopFx){
10839                this.stopFx();
10840                return true;
10841            }
10842            return false;
10843         }
10844         return true;
10845     },
10846
10847         /**
10848          * Returns true if the element is currently blocking so that no other effect can be queued
10849          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10850          * used to ensure that an effect initiated by a user action runs to completion prior to the
10851          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10852          * @return {Boolean} True if blocking, else false
10853          */
10854     hasFxBlock : function(){
10855         var q = this.fxQueue;
10856         return q && q[0] && q[0].block;
10857     },
10858
10859         /* @private */
10860     queueFx : function(o, fn){
10861         if(!this.fxQueue){
10862             this.fxQueue = [];
10863         }
10864         if(!this.hasFxBlock()){
10865             Roo.applyIf(o, this.fxDefaults);
10866             if(!o.concurrent){
10867                 var run = this.beforeFx(o);
10868                 fn.block = o.block;
10869                 this.fxQueue.push(fn);
10870                 if(run){
10871                     this.nextFx();
10872                 }
10873             }else{
10874                 fn.call(this);
10875             }
10876         }
10877         return this;
10878     },
10879
10880         /* @private */
10881     fxWrap : function(pos, o, vis){
10882         var wrap;
10883         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10884             var wrapXY;
10885             if(o.fixPosition){
10886                 wrapXY = this.getXY();
10887             }
10888             var div = document.createElement("div");
10889             div.style.visibility = vis;
10890             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10891             wrap.setPositioning(pos);
10892             if(wrap.getStyle("position") == "static"){
10893                 wrap.position("relative");
10894             }
10895             this.clearPositioning('auto');
10896             wrap.clip();
10897             wrap.dom.appendChild(this.dom);
10898             if(wrapXY){
10899                 wrap.setXY(wrapXY);
10900             }
10901         }
10902         return wrap;
10903     },
10904
10905         /* @private */
10906     fxUnwrap : function(wrap, pos, o){
10907         this.clearPositioning();
10908         this.setPositioning(pos);
10909         if(!o.wrap){
10910             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10911             wrap.remove();
10912         }
10913     },
10914
10915         /* @private */
10916     getFxRestore : function(){
10917         var st = this.dom.style;
10918         return {pos: this.getPositioning(), width: st.width, height : st.height};
10919     },
10920
10921         /* @private */
10922     afterFx : function(o){
10923         if(o.afterStyle){
10924             this.applyStyles(o.afterStyle);
10925         }
10926         if(o.afterCls){
10927             this.addClass(o.afterCls);
10928         }
10929         if(o.remove === true){
10930             this.remove();
10931         }
10932         Roo.callback(o.callback, o.scope, [this]);
10933         if(!o.concurrent){
10934             this.fxQueue.shift();
10935             this.nextFx();
10936         }
10937     },
10938
10939         /* @private */
10940     getFxEl : function(){ // support for composite element fx
10941         return Roo.get(this.dom);
10942     },
10943
10944         /* @private */
10945     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10946         animType = animType || 'run';
10947         opt = opt || {};
10948         var anim = Roo.lib.Anim[animType](
10949             this.dom, args,
10950             (opt.duration || defaultDur) || .35,
10951             (opt.easing || defaultEase) || 'easeOut',
10952             function(){
10953                 Roo.callback(cb, this);
10954             },
10955             this
10956         );
10957         opt.anim = anim;
10958         return anim;
10959     }
10960 };
10961
10962 // backwords compat
10963 Roo.Fx.resize = Roo.Fx.scale;
10964
10965 //When included, Roo.Fx is automatically applied to Element so that all basic
10966 //effects are available directly via the Element API
10967 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10968  * Based on:
10969  * Ext JS Library 1.1.1
10970  * Copyright(c) 2006-2007, Ext JS, LLC.
10971  *
10972  * Originally Released Under LGPL - original licence link has changed is not relivant.
10973  *
10974  * Fork - LGPL
10975  * <script type="text/javascript">
10976  */
10977
10978
10979 /**
10980  * @class Roo.CompositeElement
10981  * Standard composite class. Creates a Roo.Element for every element in the collection.
10982  * <br><br>
10983  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10984  * actions will be performed on all the elements in this collection.</b>
10985  * <br><br>
10986  * All methods return <i>this</i> and can be chained.
10987  <pre><code>
10988  var els = Roo.select("#some-el div.some-class", true);
10989  // or select directly from an existing element
10990  var el = Roo.get('some-el');
10991  el.select('div.some-class', true);
10992
10993  els.setWidth(100); // all elements become 100 width
10994  els.hide(true); // all elements fade out and hide
10995  // or
10996  els.setWidth(100).hide(true);
10997  </code></pre>
10998  */
10999 Roo.CompositeElement = function(els){
11000     this.elements = [];
11001     this.addElements(els);
11002 };
11003 Roo.CompositeElement.prototype = {
11004     isComposite: true,
11005     addElements : function(els){
11006         if(!els) {
11007             return this;
11008         }
11009         if(typeof els == "string"){
11010             els = Roo.Element.selectorFunction(els);
11011         }
11012         var yels = this.elements;
11013         var index = yels.length-1;
11014         for(var i = 0, len = els.length; i < len; i++) {
11015                 yels[++index] = Roo.get(els[i]);
11016         }
11017         return this;
11018     },
11019
11020     /**
11021     * Clears this composite and adds the elements returned by the passed selector.
11022     * @param {String/Array} els A string CSS selector, an array of elements or an element
11023     * @return {CompositeElement} this
11024     */
11025     fill : function(els){
11026         this.elements = [];
11027         this.add(els);
11028         return this;
11029     },
11030
11031     /**
11032     * Filters this composite to only elements that match the passed selector.
11033     * @param {String} selector A string CSS selector
11034     * @param {Boolean} inverse return inverse filter (not matches)
11035     * @return {CompositeElement} this
11036     */
11037     filter : function(selector, inverse){
11038         var els = [];
11039         inverse = inverse || false;
11040         this.each(function(el){
11041             var match = inverse ? !el.is(selector) : el.is(selector);
11042             if(match){
11043                 els[els.length] = el.dom;
11044             }
11045         });
11046         this.fill(els);
11047         return this;
11048     },
11049
11050     invoke : function(fn, args){
11051         var els = this.elements;
11052         for(var i = 0, len = els.length; i < len; i++) {
11053                 Roo.Element.prototype[fn].apply(els[i], args);
11054         }
11055         return this;
11056     },
11057     /**
11058     * Adds elements to this composite.
11059     * @param {String/Array} els A string CSS selector, an array of elements or an element
11060     * @return {CompositeElement} this
11061     */
11062     add : function(els){
11063         if(typeof els == "string"){
11064             this.addElements(Roo.Element.selectorFunction(els));
11065         }else if(els.length !== undefined){
11066             this.addElements(els);
11067         }else{
11068             this.addElements([els]);
11069         }
11070         return this;
11071     },
11072     /**
11073     * Calls the passed function passing (el, this, index) for each element in this composite.
11074     * @param {Function} fn The function to call
11075     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11076     * @return {CompositeElement} this
11077     */
11078     each : function(fn, scope){
11079         var els = this.elements;
11080         for(var i = 0, len = els.length; i < len; i++){
11081             if(fn.call(scope || els[i], els[i], this, i) === false) {
11082                 break;
11083             }
11084         }
11085         return this;
11086     },
11087
11088     /**
11089      * Returns the Element object at the specified index
11090      * @param {Number} index
11091      * @return {Roo.Element}
11092      */
11093     item : function(index){
11094         return this.elements[index] || null;
11095     },
11096
11097     /**
11098      * Returns the first Element
11099      * @return {Roo.Element}
11100      */
11101     first : function(){
11102         return this.item(0);
11103     },
11104
11105     /**
11106      * Returns the last Element
11107      * @return {Roo.Element}
11108      */
11109     last : function(){
11110         return this.item(this.elements.length-1);
11111     },
11112
11113     /**
11114      * Returns the number of elements in this composite
11115      * @return Number
11116      */
11117     getCount : function(){
11118         return this.elements.length;
11119     },
11120
11121     /**
11122      * Returns true if this composite contains the passed element
11123      * @return Boolean
11124      */
11125     contains : function(el){
11126         return this.indexOf(el) !== -1;
11127     },
11128
11129     /**
11130      * Returns true if this composite contains the passed element
11131      * @return Boolean
11132      */
11133     indexOf : function(el){
11134         return this.elements.indexOf(Roo.get(el));
11135     },
11136
11137
11138     /**
11139     * Removes the specified element(s).
11140     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11141     * or an array of any of those.
11142     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11143     * @return {CompositeElement} this
11144     */
11145     removeElement : function(el, removeDom){
11146         if(el instanceof Array){
11147             for(var i = 0, len = el.length; i < len; i++){
11148                 this.removeElement(el[i]);
11149             }
11150             return this;
11151         }
11152         var index = typeof el == 'number' ? el : this.indexOf(el);
11153         if(index !== -1){
11154             if(removeDom){
11155                 var d = this.elements[index];
11156                 if(d.dom){
11157                     d.remove();
11158                 }else{
11159                     d.parentNode.removeChild(d);
11160                 }
11161             }
11162             this.elements.splice(index, 1);
11163         }
11164         return this;
11165     },
11166
11167     /**
11168     * Replaces the specified element with the passed element.
11169     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11170     * to replace.
11171     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11172     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11173     * @return {CompositeElement} this
11174     */
11175     replaceElement : function(el, replacement, domReplace){
11176         var index = typeof el == 'number' ? el : this.indexOf(el);
11177         if(index !== -1){
11178             if(domReplace){
11179                 this.elements[index].replaceWith(replacement);
11180             }else{
11181                 this.elements.splice(index, 1, Roo.get(replacement))
11182             }
11183         }
11184         return this;
11185     },
11186
11187     /**
11188      * Removes all elements.
11189      */
11190     clear : function(){
11191         this.elements = [];
11192     }
11193 };
11194 (function(){
11195     Roo.CompositeElement.createCall = function(proto, fnName){
11196         if(!proto[fnName]){
11197             proto[fnName] = function(){
11198                 return this.invoke(fnName, arguments);
11199             };
11200         }
11201     };
11202     for(var fnName in Roo.Element.prototype){
11203         if(typeof Roo.Element.prototype[fnName] == "function"){
11204             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11205         }
11206     };
11207 })();
11208 /*
11209  * Based on:
11210  * Ext JS Library 1.1.1
11211  * Copyright(c) 2006-2007, Ext JS, LLC.
11212  *
11213  * Originally Released Under LGPL - original licence link has changed is not relivant.
11214  *
11215  * Fork - LGPL
11216  * <script type="text/javascript">
11217  */
11218
11219 /**
11220  * @class Roo.CompositeElementLite
11221  * @extends Roo.CompositeElement
11222  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11223  <pre><code>
11224  var els = Roo.select("#some-el div.some-class");
11225  // or select directly from an existing element
11226  var el = Roo.get('some-el');
11227  el.select('div.some-class');
11228
11229  els.setWidth(100); // all elements become 100 width
11230  els.hide(true); // all elements fade out and hide
11231  // or
11232  els.setWidth(100).hide(true);
11233  </code></pre><br><br>
11234  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11235  * actions will be performed on all the elements in this collection.</b>
11236  */
11237 Roo.CompositeElementLite = function(els){
11238     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11239     this.el = new Roo.Element.Flyweight();
11240 };
11241 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11242     addElements : function(els){
11243         if(els){
11244             if(els instanceof Array){
11245                 this.elements = this.elements.concat(els);
11246             }else{
11247                 var yels = this.elements;
11248                 var index = yels.length-1;
11249                 for(var i = 0, len = els.length; i < len; i++) {
11250                     yels[++index] = els[i];
11251                 }
11252             }
11253         }
11254         return this;
11255     },
11256     invoke : function(fn, args){
11257         var els = this.elements;
11258         var el = this.el;
11259         for(var i = 0, len = els.length; i < len; i++) {
11260             el.dom = els[i];
11261                 Roo.Element.prototype[fn].apply(el, args);
11262         }
11263         return this;
11264     },
11265     /**
11266      * Returns a flyweight Element of the dom element object at the specified index
11267      * @param {Number} index
11268      * @return {Roo.Element}
11269      */
11270     item : function(index){
11271         if(!this.elements[index]){
11272             return null;
11273         }
11274         this.el.dom = this.elements[index];
11275         return this.el;
11276     },
11277
11278     // fixes scope with flyweight
11279     addListener : function(eventName, handler, scope, opt){
11280         var els = this.elements;
11281         for(var i = 0, len = els.length; i < len; i++) {
11282             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11283         }
11284         return this;
11285     },
11286
11287     /**
11288     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11289     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11290     * a reference to the dom node, use el.dom.</b>
11291     * @param {Function} fn The function to call
11292     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11293     * @return {CompositeElement} this
11294     */
11295     each : function(fn, scope){
11296         var els = this.elements;
11297         var el = this.el;
11298         for(var i = 0, len = els.length; i < len; i++){
11299             el.dom = els[i];
11300                 if(fn.call(scope || el, el, this, i) === false){
11301                 break;
11302             }
11303         }
11304         return this;
11305     },
11306
11307     indexOf : function(el){
11308         return this.elements.indexOf(Roo.getDom(el));
11309     },
11310
11311     replaceElement : function(el, replacement, domReplace){
11312         var index = typeof el == 'number' ? el : this.indexOf(el);
11313         if(index !== -1){
11314             replacement = Roo.getDom(replacement);
11315             if(domReplace){
11316                 var d = this.elements[index];
11317                 d.parentNode.insertBefore(replacement, d);
11318                 d.parentNode.removeChild(d);
11319             }
11320             this.elements.splice(index, 1, replacement);
11321         }
11322         return this;
11323     }
11324 });
11325 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11326
11327 /*
11328  * Based on:
11329  * Ext JS Library 1.1.1
11330  * Copyright(c) 2006-2007, Ext JS, LLC.
11331  *
11332  * Originally Released Under LGPL - original licence link has changed is not relivant.
11333  *
11334  * Fork - LGPL
11335  * <script type="text/javascript">
11336  */
11337
11338  
11339
11340 /**
11341  * @class Roo.data.Connection
11342  * @extends Roo.util.Observable
11343  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11344  * either to a configured URL, or to a URL specified at request time.<br><br>
11345  * <p>
11346  * Requests made by this class are asynchronous, and will return immediately. No data from
11347  * the server will be available to the statement immediately following the {@link #request} call.
11348  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11349  * <p>
11350  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11351  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11352  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11353  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11354  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11355  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11356  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11357  * standard DOM methods.
11358  * @constructor
11359  * @param {Object} config a configuration object.
11360  */
11361 Roo.data.Connection = function(config){
11362     Roo.apply(this, config);
11363     this.addEvents({
11364         /**
11365          * @event beforerequest
11366          * Fires before a network request is made to retrieve a data object.
11367          * @param {Connection} conn This Connection object.
11368          * @param {Object} options The options config object passed to the {@link #request} method.
11369          */
11370         "beforerequest" : true,
11371         /**
11372          * @event requestcomplete
11373          * Fires if the request was successfully completed.
11374          * @param {Connection} conn This Connection object.
11375          * @param {Object} response The XHR object containing the response data.
11376          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11377          * @param {Object} options The options config object passed to the {@link #request} method.
11378          */
11379         "requestcomplete" : true,
11380         /**
11381          * @event requestexception
11382          * Fires if an error HTTP status was returned from the server.
11383          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11384          * @param {Connection} conn This Connection object.
11385          * @param {Object} response The XHR object containing the response data.
11386          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11387          * @param {Object} options The options config object passed to the {@link #request} method.
11388          */
11389         "requestexception" : true
11390     });
11391     Roo.data.Connection.superclass.constructor.call(this);
11392 };
11393
11394 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11395     /**
11396      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11397      */
11398     /**
11399      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11400      * extra parameters to each request made by this object. (defaults to undefined)
11401      */
11402     /**
11403      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11404      *  to each request made by this object. (defaults to undefined)
11405      */
11406     /**
11407      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11408      */
11409     /**
11410      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11411      */
11412     timeout : 30000,
11413     /**
11414      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11415      * @type Boolean
11416      */
11417     autoAbort:false,
11418
11419     /**
11420      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11421      * @type Boolean
11422      */
11423     disableCaching: true,
11424
11425     /**
11426      * Sends an HTTP request to a remote server.
11427      * @param {Object} options An object which may contain the following properties:<ul>
11428      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11429      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11430      * request, a url encoded string or a function to call to get either.</li>
11431      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11432      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11433      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11434      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11435      * <li>options {Object} The parameter to the request call.</li>
11436      * <li>success {Boolean} True if the request succeeded.</li>
11437      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11438      * </ul></li>
11439      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11440      * The callback is passed the following parameters:<ul>
11441      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11442      * <li>options {Object} The parameter to the request call.</li>
11443      * </ul></li>
11444      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11445      * The callback is passed the following parameters:<ul>
11446      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11447      * <li>options {Object} The parameter to the request call.</li>
11448      * </ul></li>
11449      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11450      * for the callback function. Defaults to the browser window.</li>
11451      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11452      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11453      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11454      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11455      * params for the post data. Any params will be appended to the URL.</li>
11456      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11457      * </ul>
11458      * @return {Number} transactionId
11459      */
11460     request : function(o){
11461         if(this.fireEvent("beforerequest", this, o) !== false){
11462             var p = o.params;
11463
11464             if(typeof p == "function"){
11465                 p = p.call(o.scope||window, o);
11466             }
11467             if(typeof p == "object"){
11468                 p = Roo.urlEncode(o.params);
11469             }
11470             if(this.extraParams){
11471                 var extras = Roo.urlEncode(this.extraParams);
11472                 p = p ? (p + '&' + extras) : extras;
11473             }
11474
11475             var url = o.url || this.url;
11476             if(typeof url == 'function'){
11477                 url = url.call(o.scope||window, o);
11478             }
11479
11480             if(o.form){
11481                 var form = Roo.getDom(o.form);
11482                 url = url || form.action;
11483
11484                 var enctype = form.getAttribute("enctype");
11485                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11486                     return this.doFormUpload(o, p, url);
11487                 }
11488                 var f = Roo.lib.Ajax.serializeForm(form);
11489                 p = p ? (p + '&' + f) : f;
11490             }
11491
11492             var hs = o.headers;
11493             if(this.defaultHeaders){
11494                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11495                 if(!o.headers){
11496                     o.headers = hs;
11497                 }
11498             }
11499
11500             var cb = {
11501                 success: this.handleResponse,
11502                 failure: this.handleFailure,
11503                 scope: this,
11504                 argument: {options: o},
11505                 timeout : o.timeout || this.timeout
11506             };
11507
11508             var method = o.method||this.method||(p ? "POST" : "GET");
11509
11510             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11511                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11512             }
11513
11514             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11515                 if(o.autoAbort){
11516                     this.abort();
11517                 }
11518             }else if(this.autoAbort !== false){
11519                 this.abort();
11520             }
11521
11522             if((method == 'GET' && p) || o.xmlData){
11523                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11524                 p = '';
11525             }
11526             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11527             return this.transId;
11528         }else{
11529             Roo.callback(o.callback, o.scope, [o, null, null]);
11530             return null;
11531         }
11532     },
11533
11534     /**
11535      * Determine whether this object has a request outstanding.
11536      * @param {Number} transactionId (Optional) defaults to the last transaction
11537      * @return {Boolean} True if there is an outstanding request.
11538      */
11539     isLoading : function(transId){
11540         if(transId){
11541             return Roo.lib.Ajax.isCallInProgress(transId);
11542         }else{
11543             return this.transId ? true : false;
11544         }
11545     },
11546
11547     /**
11548      * Aborts any outstanding request.
11549      * @param {Number} transactionId (Optional) defaults to the last transaction
11550      */
11551     abort : function(transId){
11552         if(transId || this.isLoading()){
11553             Roo.lib.Ajax.abort(transId || this.transId);
11554         }
11555     },
11556
11557     // private
11558     handleResponse : function(response){
11559         this.transId = false;
11560         var options = response.argument.options;
11561         response.argument = options ? options.argument : null;
11562         this.fireEvent("requestcomplete", this, response, options);
11563         Roo.callback(options.success, options.scope, [response, options]);
11564         Roo.callback(options.callback, options.scope, [options, true, response]);
11565     },
11566
11567     // private
11568     handleFailure : function(response, e){
11569         this.transId = false;
11570         var options = response.argument.options;
11571         response.argument = options ? options.argument : null;
11572         this.fireEvent("requestexception", this, response, options, e);
11573         Roo.callback(options.failure, options.scope, [response, options]);
11574         Roo.callback(options.callback, options.scope, [options, false, response]);
11575     },
11576
11577     // private
11578     doFormUpload : function(o, ps, url){
11579         var id = Roo.id();
11580         var frame = document.createElement('iframe');
11581         frame.id = id;
11582         frame.name = id;
11583         frame.className = 'x-hidden';
11584         if(Roo.isIE){
11585             frame.src = Roo.SSL_SECURE_URL;
11586         }
11587         document.body.appendChild(frame);
11588
11589         if(Roo.isIE){
11590            document.frames[id].name = id;
11591         }
11592
11593         var form = Roo.getDom(o.form);
11594         form.target = id;
11595         form.method = 'POST';
11596         form.enctype = form.encoding = 'multipart/form-data';
11597         if(url){
11598             form.action = url;
11599         }
11600
11601         var hiddens, hd;
11602         if(ps){ // add dynamic params
11603             hiddens = [];
11604             ps = Roo.urlDecode(ps, false);
11605             for(var k in ps){
11606                 if(ps.hasOwnProperty(k)){
11607                     hd = document.createElement('input');
11608                     hd.type = 'hidden';
11609                     hd.name = k;
11610                     hd.value = ps[k];
11611                     form.appendChild(hd);
11612                     hiddens.push(hd);
11613                 }
11614             }
11615         }
11616
11617         function cb(){
11618             var r = {  // bogus response object
11619                 responseText : '',
11620                 responseXML : null
11621             };
11622
11623             r.argument = o ? o.argument : null;
11624
11625             try { //
11626                 var doc;
11627                 if(Roo.isIE){
11628                     doc = frame.contentWindow.document;
11629                 }else {
11630                     doc = (frame.contentDocument || window.frames[id].document);
11631                 }
11632                 if(doc && doc.body){
11633                     r.responseText = doc.body.innerHTML;
11634                 }
11635                 if(doc && doc.XMLDocument){
11636                     r.responseXML = doc.XMLDocument;
11637                 }else {
11638                     r.responseXML = doc;
11639                 }
11640             }
11641             catch(e) {
11642                 // ignore
11643             }
11644
11645             Roo.EventManager.removeListener(frame, 'load', cb, this);
11646
11647             this.fireEvent("requestcomplete", this, r, o);
11648             Roo.callback(o.success, o.scope, [r, o]);
11649             Roo.callback(o.callback, o.scope, [o, true, r]);
11650
11651             setTimeout(function(){document.body.removeChild(frame);}, 100);
11652         }
11653
11654         Roo.EventManager.on(frame, 'load', cb, this);
11655         form.submit();
11656
11657         if(hiddens){ // remove dynamic params
11658             for(var i = 0, len = hiddens.length; i < len; i++){
11659                 form.removeChild(hiddens[i]);
11660             }
11661         }
11662     }
11663 });
11664 /*
11665  * Based on:
11666  * Ext JS Library 1.1.1
11667  * Copyright(c) 2006-2007, Ext JS, LLC.
11668  *
11669  * Originally Released Under LGPL - original licence link has changed is not relivant.
11670  *
11671  * Fork - LGPL
11672  * <script type="text/javascript">
11673  */
11674  
11675 /**
11676  * Global Ajax request class.
11677  * 
11678  * @class Roo.Ajax
11679  * @extends Roo.data.Connection
11680  * @static
11681  * 
11682  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11683  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11684  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11685  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11686  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11687  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11688  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11689  */
11690 Roo.Ajax = new Roo.data.Connection({
11691     // fix up the docs
11692     /**
11693      * @scope Roo.Ajax
11694      * @type {Boolear} 
11695      */
11696     autoAbort : false,
11697
11698     /**
11699      * Serialize the passed form into a url encoded string
11700      * @scope Roo.Ajax
11701      * @param {String/HTMLElement} form
11702      * @return {String}
11703      */
11704     serializeForm : function(form){
11705         return Roo.lib.Ajax.serializeForm(form);
11706     }
11707 });/*
11708  * Based on:
11709  * Ext JS Library 1.1.1
11710  * Copyright(c) 2006-2007, Ext JS, LLC.
11711  *
11712  * Originally Released Under LGPL - original licence link has changed is not relivant.
11713  *
11714  * Fork - LGPL
11715  * <script type="text/javascript">
11716  */
11717
11718  
11719 /**
11720  * @class Roo.UpdateManager
11721  * @extends Roo.util.Observable
11722  * Provides AJAX-style update for Element object.<br><br>
11723  * Usage:<br>
11724  * <pre><code>
11725  * // Get it from a Roo.Element object
11726  * var el = Roo.get("foo");
11727  * var mgr = el.getUpdateManager();
11728  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11729  * ...
11730  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11731  * <br>
11732  * // or directly (returns the same UpdateManager instance)
11733  * var mgr = new Roo.UpdateManager("myElementId");
11734  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11735  * mgr.on("update", myFcnNeedsToKnow);
11736  * <br>
11737    // short handed call directly from the element object
11738    Roo.get("foo").load({
11739         url: "bar.php",
11740         scripts:true,
11741         params: "for=bar",
11742         text: "Loading Foo..."
11743    });
11744  * </code></pre>
11745  * @constructor
11746  * Create new UpdateManager directly.
11747  * @param {String/HTMLElement/Roo.Element} el The element to update
11748  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11749  */
11750 Roo.UpdateManager = function(el, forceNew){
11751     el = Roo.get(el);
11752     if(!forceNew && el.updateManager){
11753         return el.updateManager;
11754     }
11755     /**
11756      * The Element object
11757      * @type Roo.Element
11758      */
11759     this.el = el;
11760     /**
11761      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11762      * @type String
11763      */
11764     this.defaultUrl = null;
11765
11766     this.addEvents({
11767         /**
11768          * @event beforeupdate
11769          * Fired before an update is made, return false from your handler and the update is cancelled.
11770          * @param {Roo.Element} el
11771          * @param {String/Object/Function} url
11772          * @param {String/Object} params
11773          */
11774         "beforeupdate": true,
11775         /**
11776          * @event update
11777          * Fired after successful update is made.
11778          * @param {Roo.Element} el
11779          * @param {Object} oResponseObject The response Object
11780          */
11781         "update": true,
11782         /**
11783          * @event failure
11784          * Fired on update failure.
11785          * @param {Roo.Element} el
11786          * @param {Object} oResponseObject The response Object
11787          */
11788         "failure": true
11789     });
11790     var d = Roo.UpdateManager.defaults;
11791     /**
11792      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11793      * @type String
11794      */
11795     this.sslBlankUrl = d.sslBlankUrl;
11796     /**
11797      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11798      * @type Boolean
11799      */
11800     this.disableCaching = d.disableCaching;
11801     /**
11802      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11803      * @type String
11804      */
11805     this.indicatorText = d.indicatorText;
11806     /**
11807      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11808      * @type String
11809      */
11810     this.showLoadIndicator = d.showLoadIndicator;
11811     /**
11812      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11813      * @type Number
11814      */
11815     this.timeout = d.timeout;
11816
11817     /**
11818      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11819      * @type Boolean
11820      */
11821     this.loadScripts = d.loadScripts;
11822
11823     /**
11824      * Transaction object of current executing transaction
11825      */
11826     this.transaction = null;
11827
11828     /**
11829      * @private
11830      */
11831     this.autoRefreshProcId = null;
11832     /**
11833      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11834      * @type Function
11835      */
11836     this.refreshDelegate = this.refresh.createDelegate(this);
11837     /**
11838      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11839      * @type Function
11840      */
11841     this.updateDelegate = this.update.createDelegate(this);
11842     /**
11843      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11844      * @type Function
11845      */
11846     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11847     /**
11848      * @private
11849      */
11850     this.successDelegate = this.processSuccess.createDelegate(this);
11851     /**
11852      * @private
11853      */
11854     this.failureDelegate = this.processFailure.createDelegate(this);
11855
11856     if(!this.renderer){
11857      /**
11858       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11859       */
11860     this.renderer = new Roo.UpdateManager.BasicRenderer();
11861     }
11862     
11863     Roo.UpdateManager.superclass.constructor.call(this);
11864 };
11865
11866 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11867     /**
11868      * Get the Element this UpdateManager is bound to
11869      * @return {Roo.Element} The element
11870      */
11871     getEl : function(){
11872         return this.el;
11873     },
11874     /**
11875      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11876      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11877 <pre><code>
11878 um.update({<br/>
11879     url: "your-url.php",<br/>
11880     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11881     callback: yourFunction,<br/>
11882     scope: yourObject, //(optional scope)  <br/>
11883     discardUrl: false, <br/>
11884     nocache: false,<br/>
11885     text: "Loading...",<br/>
11886     timeout: 30,<br/>
11887     scripts: false<br/>
11888 });
11889 </code></pre>
11890      * The only required property is url. The optional properties nocache, text and scripts
11891      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11892      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11893      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11894      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11895      */
11896     update : function(url, params, callback, discardUrl){
11897         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11898             var method = this.method,
11899                 cfg;
11900             if(typeof url == "object"){ // must be config object
11901                 cfg = url;
11902                 url = cfg.url;
11903                 params = params || cfg.params;
11904                 callback = callback || cfg.callback;
11905                 discardUrl = discardUrl || cfg.discardUrl;
11906                 if(callback && cfg.scope){
11907                     callback = callback.createDelegate(cfg.scope);
11908                 }
11909                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11910                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11911                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11912                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11913                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11914             }
11915             this.showLoading();
11916             if(!discardUrl){
11917                 this.defaultUrl = url;
11918             }
11919             if(typeof url == "function"){
11920                 url = url.call(this);
11921             }
11922
11923             method = method || (params ? "POST" : "GET");
11924             if(method == "GET"){
11925                 url = this.prepareUrl(url);
11926             }
11927
11928             var o = Roo.apply(cfg ||{}, {
11929                 url : url,
11930                 params: params,
11931                 success: this.successDelegate,
11932                 failure: this.failureDelegate,
11933                 callback: undefined,
11934                 timeout: (this.timeout*1000),
11935                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11936             });
11937             Roo.log("updated manager called with timeout of " + o.timeout);
11938             this.transaction = Roo.Ajax.request(o);
11939         }
11940     },
11941
11942     /**
11943      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11944      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11945      * @param {String/HTMLElement} form The form Id or form element
11946      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11947      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11948      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11949      */
11950     formUpdate : function(form, url, reset, callback){
11951         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11952             if(typeof url == "function"){
11953                 url = url.call(this);
11954             }
11955             form = Roo.getDom(form);
11956             this.transaction = Roo.Ajax.request({
11957                 form: form,
11958                 url:url,
11959                 success: this.successDelegate,
11960                 failure: this.failureDelegate,
11961                 timeout: (this.timeout*1000),
11962                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11963             });
11964             this.showLoading.defer(1, this);
11965         }
11966     },
11967
11968     /**
11969      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11970      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11971      */
11972     refresh : function(callback){
11973         if(this.defaultUrl == null){
11974             return;
11975         }
11976         this.update(this.defaultUrl, null, callback, true);
11977     },
11978
11979     /**
11980      * Set this element to auto refresh.
11981      * @param {Number} interval How often to update (in seconds).
11982      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11983      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11984      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11985      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11986      */
11987     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11988         if(refreshNow){
11989             this.update(url || this.defaultUrl, params, callback, true);
11990         }
11991         if(this.autoRefreshProcId){
11992             clearInterval(this.autoRefreshProcId);
11993         }
11994         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11995     },
11996
11997     /**
11998      * Stop auto refresh on this element.
11999      */
12000      stopAutoRefresh : function(){
12001         if(this.autoRefreshProcId){
12002             clearInterval(this.autoRefreshProcId);
12003             delete this.autoRefreshProcId;
12004         }
12005     },
12006
12007     isAutoRefreshing : function(){
12008        return this.autoRefreshProcId ? true : false;
12009     },
12010     /**
12011      * Called to update the element to "Loading" state. Override to perform custom action.
12012      */
12013     showLoading : function(){
12014         if(this.showLoadIndicator){
12015             this.el.update(this.indicatorText);
12016         }
12017     },
12018
12019     /**
12020      * Adds unique parameter to query string if disableCaching = true
12021      * @private
12022      */
12023     prepareUrl : function(url){
12024         if(this.disableCaching){
12025             var append = "_dc=" + (new Date().getTime());
12026             if(url.indexOf("?") !== -1){
12027                 url += "&" + append;
12028             }else{
12029                 url += "?" + append;
12030             }
12031         }
12032         return url;
12033     },
12034
12035     /**
12036      * @private
12037      */
12038     processSuccess : function(response){
12039         this.transaction = null;
12040         if(response.argument.form && response.argument.reset){
12041             try{ // put in try/catch since some older FF releases had problems with this
12042                 response.argument.form.reset();
12043             }catch(e){}
12044         }
12045         if(this.loadScripts){
12046             this.renderer.render(this.el, response, this,
12047                 this.updateComplete.createDelegate(this, [response]));
12048         }else{
12049             this.renderer.render(this.el, response, this);
12050             this.updateComplete(response);
12051         }
12052     },
12053
12054     updateComplete : function(response){
12055         this.fireEvent("update", this.el, response);
12056         if(typeof response.argument.callback == "function"){
12057             response.argument.callback(this.el, true, response);
12058         }
12059     },
12060
12061     /**
12062      * @private
12063      */
12064     processFailure : function(response){
12065         this.transaction = null;
12066         this.fireEvent("failure", this.el, response);
12067         if(typeof response.argument.callback == "function"){
12068             response.argument.callback(this.el, false, response);
12069         }
12070     },
12071
12072     /**
12073      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12074      * @param {Object} renderer The object implementing the render() method
12075      */
12076     setRenderer : function(renderer){
12077         this.renderer = renderer;
12078     },
12079
12080     getRenderer : function(){
12081        return this.renderer;
12082     },
12083
12084     /**
12085      * Set the defaultUrl used for updates
12086      * @param {String/Function} defaultUrl The url or a function to call to get the url
12087      */
12088     setDefaultUrl : function(defaultUrl){
12089         this.defaultUrl = defaultUrl;
12090     },
12091
12092     /**
12093      * Aborts the executing transaction
12094      */
12095     abort : function(){
12096         if(this.transaction){
12097             Roo.Ajax.abort(this.transaction);
12098         }
12099     },
12100
12101     /**
12102      * Returns true if an update is in progress
12103      * @return {Boolean}
12104      */
12105     isUpdating : function(){
12106         if(this.transaction){
12107             return Roo.Ajax.isLoading(this.transaction);
12108         }
12109         return false;
12110     }
12111 });
12112
12113 /**
12114  * @class Roo.UpdateManager.defaults
12115  * @static (not really - but it helps the doc tool)
12116  * The defaults collection enables customizing the default properties of UpdateManager
12117  */
12118    Roo.UpdateManager.defaults = {
12119        /**
12120          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12121          * @type Number
12122          */
12123          timeout : 30,
12124
12125          /**
12126          * True to process scripts by default (Defaults to false).
12127          * @type Boolean
12128          */
12129         loadScripts : false,
12130
12131         /**
12132         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12133         * @type String
12134         */
12135         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12136         /**
12137          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12138          * @type Boolean
12139          */
12140         disableCaching : false,
12141         /**
12142          * Whether to show indicatorText when loading (Defaults to true).
12143          * @type Boolean
12144          */
12145         showLoadIndicator : true,
12146         /**
12147          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12148          * @type String
12149          */
12150         indicatorText : '<div class="loading-indicator">Loading...</div>'
12151    };
12152
12153 /**
12154  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12155  *Usage:
12156  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12157  * @param {String/HTMLElement/Roo.Element} el The element to update
12158  * @param {String} url The url
12159  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12160  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12161  * @static
12162  * @deprecated
12163  * @member Roo.UpdateManager
12164  */
12165 Roo.UpdateManager.updateElement = function(el, url, params, options){
12166     var um = Roo.get(el, true).getUpdateManager();
12167     Roo.apply(um, options);
12168     um.update(url, params, options ? options.callback : null);
12169 };
12170 // alias for backwards compat
12171 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12172 /**
12173  * @class Roo.UpdateManager.BasicRenderer
12174  * Default Content renderer. Updates the elements innerHTML with the responseText.
12175  */
12176 Roo.UpdateManager.BasicRenderer = function(){};
12177
12178 Roo.UpdateManager.BasicRenderer.prototype = {
12179     /**
12180      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12181      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12182      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12183      * @param {Roo.Element} el The element being rendered
12184      * @param {Object} response The YUI Connect response object
12185      * @param {UpdateManager} updateManager The calling update manager
12186      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12187      */
12188      render : function(el, response, updateManager, callback){
12189         el.update(response.responseText, updateManager.loadScripts, callback);
12190     }
12191 };
12192 /*
12193  * Based on:
12194  * Roo JS
12195  * (c)) Alan Knowles
12196  * Licence : LGPL
12197  */
12198
12199
12200 /**
12201  * @class Roo.DomTemplate
12202  * @extends Roo.Template
12203  * An effort at a dom based template engine..
12204  *
12205  * Similar to XTemplate, except it uses dom parsing to create the template..
12206  *
12207  * Supported features:
12208  *
12209  *  Tags:
12210
12211 <pre><code>
12212       {a_variable} - output encoded.
12213       {a_variable.format:("Y-m-d")} - call a method on the variable
12214       {a_variable:raw} - unencoded output
12215       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12216       {a_variable:this.method_on_template(...)} - call a method on the template object.
12217  
12218 </code></pre>
12219  *  The tpl tag:
12220 <pre><code>
12221         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12222         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12223         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12224         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12225   
12226 </code></pre>
12227  *      
12228  */
12229 Roo.DomTemplate = function()
12230 {
12231      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12232      if (this.html) {
12233         this.compile();
12234      }
12235 };
12236
12237
12238 Roo.extend(Roo.DomTemplate, Roo.Template, {
12239     /**
12240      * id counter for sub templates.
12241      */
12242     id : 0,
12243     /**
12244      * flag to indicate if dom parser is inside a pre,
12245      * it will strip whitespace if not.
12246      */
12247     inPre : false,
12248     
12249     /**
12250      * The various sub templates
12251      */
12252     tpls : false,
12253     
12254     
12255     
12256     /**
12257      *
12258      * basic tag replacing syntax
12259      * WORD:WORD()
12260      *
12261      * // you can fake an object call by doing this
12262      *  x.t:(test,tesT) 
12263      * 
12264      */
12265     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12266     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12267     
12268     iterChild : function (node, method) {
12269         
12270         var oldPre = this.inPre;
12271         if (node.tagName == 'PRE') {
12272             this.inPre = true;
12273         }
12274         for( var i = 0; i < node.childNodes.length; i++) {
12275             method.call(this, node.childNodes[i]);
12276         }
12277         this.inPre = oldPre;
12278     },
12279     
12280     
12281     
12282     /**
12283      * compile the template
12284      *
12285      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12286      *
12287      */
12288     compile: function()
12289     {
12290         var s = this.html;
12291         
12292         // covert the html into DOM...
12293         var doc = false;
12294         var div =false;
12295         try {
12296             doc = document.implementation.createHTMLDocument("");
12297             doc.documentElement.innerHTML =   this.html  ;
12298             div = doc.documentElement;
12299         } catch (e) {
12300             // old IE... - nasty -- it causes all sorts of issues.. with
12301             // images getting pulled from server..
12302             div = document.createElement('div');
12303             div.innerHTML = this.html;
12304         }
12305         //doc.documentElement.innerHTML = htmlBody
12306          
12307         
12308         
12309         this.tpls = [];
12310         var _t = this;
12311         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12312         
12313         var tpls = this.tpls;
12314         
12315         // create a top level template from the snippet..
12316         
12317         //Roo.log(div.innerHTML);
12318         
12319         var tpl = {
12320             uid : 'master',
12321             id : this.id++,
12322             attr : false,
12323             value : false,
12324             body : div.innerHTML,
12325             
12326             forCall : false,
12327             execCall : false,
12328             dom : div,
12329             isTop : true
12330             
12331         };
12332         tpls.unshift(tpl);
12333         
12334         
12335         // compile them...
12336         this.tpls = [];
12337         Roo.each(tpls, function(tp){
12338             this.compileTpl(tp);
12339             this.tpls[tp.id] = tp;
12340         }, this);
12341         
12342         this.master = tpls[0];
12343         return this;
12344         
12345         
12346     },
12347     
12348     compileNode : function(node, istop) {
12349         // test for
12350         //Roo.log(node);
12351         
12352         
12353         // skip anything not a tag..
12354         if (node.nodeType != 1) {
12355             if (node.nodeType == 3 && !this.inPre) {
12356                 // reduce white space..
12357                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12358                 
12359             }
12360             return;
12361         }
12362         
12363         var tpl = {
12364             uid : false,
12365             id : false,
12366             attr : false,
12367             value : false,
12368             body : '',
12369             
12370             forCall : false,
12371             execCall : false,
12372             dom : false,
12373             isTop : istop
12374             
12375             
12376         };
12377         
12378         
12379         switch(true) {
12380             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12381             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12382             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12383             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12384             // no default..
12385         }
12386         
12387         
12388         if (!tpl.attr) {
12389             // just itterate children..
12390             this.iterChild(node,this.compileNode);
12391             return;
12392         }
12393         tpl.uid = this.id++;
12394         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12395         node.removeAttribute('roo-'+ tpl.attr);
12396         if (tpl.attr != 'name') {
12397             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12398             node.parentNode.replaceChild(placeholder,  node);
12399         } else {
12400             
12401             var placeholder =  document.createElement('span');
12402             placeholder.className = 'roo-tpl-' + tpl.value;
12403             node.parentNode.replaceChild(placeholder,  node);
12404         }
12405         
12406         // parent now sees '{domtplXXXX}
12407         this.iterChild(node,this.compileNode);
12408         
12409         // we should now have node body...
12410         var div = document.createElement('div');
12411         div.appendChild(node);
12412         tpl.dom = node;
12413         // this has the unfortunate side effect of converting tagged attributes
12414         // eg. href="{...}" into %7C...%7D
12415         // this has been fixed by searching for those combo's although it's a bit hacky..
12416         
12417         
12418         tpl.body = div.innerHTML;
12419         
12420         
12421          
12422         tpl.id = tpl.uid;
12423         switch(tpl.attr) {
12424             case 'for' :
12425                 switch (tpl.value) {
12426                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12427                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12428                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12429                 }
12430                 break;
12431             
12432             case 'exec':
12433                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12434                 break;
12435             
12436             case 'if':     
12437                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12438                 break;
12439             
12440             case 'name':
12441                 tpl.id  = tpl.value; // replace non characters???
12442                 break;
12443             
12444         }
12445         
12446         
12447         this.tpls.push(tpl);
12448         
12449         
12450         
12451     },
12452     
12453     
12454     
12455     
12456     /**
12457      * Compile a segment of the template into a 'sub-template'
12458      *
12459      * 
12460      * 
12461      *
12462      */
12463     compileTpl : function(tpl)
12464     {
12465         var fm = Roo.util.Format;
12466         var useF = this.disableFormats !== true;
12467         
12468         var sep = Roo.isGecko ? "+\n" : ",\n";
12469         
12470         var undef = function(str) {
12471             Roo.debug && Roo.log("Property not found :"  + str);
12472             return '';
12473         };
12474           
12475         //Roo.log(tpl.body);
12476         
12477         
12478         
12479         var fn = function(m, lbrace, name, format, args)
12480         {
12481             //Roo.log("ARGS");
12482             //Roo.log(arguments);
12483             args = args ? args.replace(/\\'/g,"'") : args;
12484             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12485             if (typeof(format) == 'undefined') {
12486                 format =  'htmlEncode'; 
12487             }
12488             if (format == 'raw' ) {
12489                 format = false;
12490             }
12491             
12492             if(name.substr(0, 6) == 'domtpl'){
12493                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12494             }
12495             
12496             // build an array of options to determine if value is undefined..
12497             
12498             // basically get 'xxxx.yyyy' then do
12499             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12500             //    (function () { Roo.log("Property not found"); return ''; })() :
12501             //    ......
12502             
12503             var udef_ar = [];
12504             var lookfor = '';
12505             Roo.each(name.split('.'), function(st) {
12506                 lookfor += (lookfor.length ? '.': '') + st;
12507                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12508             });
12509             
12510             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12511             
12512             
12513             if(format && useF){
12514                 
12515                 args = args ? ',' + args : "";
12516                  
12517                 if(format.substr(0, 5) != "this."){
12518                     format = "fm." + format + '(';
12519                 }else{
12520                     format = 'this.call("'+ format.substr(5) + '", ';
12521                     args = ", values";
12522                 }
12523                 
12524                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12525             }
12526              
12527             if (args && args.length) {
12528                 // called with xxyx.yuu:(test,test)
12529                 // change to ()
12530                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12531             }
12532             // raw.. - :raw modifier..
12533             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12534             
12535         };
12536         var body;
12537         // branched to use + in gecko and [].join() in others
12538         if(Roo.isGecko){
12539             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12540                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12541                     "';};};";
12542         }else{
12543             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12544             body.push(tpl.body.replace(/(\r\n|\n)/g,
12545                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12546             body.push("'].join('');};};");
12547             body = body.join('');
12548         }
12549         
12550         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12551        
12552         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12553         eval(body);
12554         
12555         return this;
12556     },
12557      
12558     /**
12559      * same as applyTemplate, except it's done to one of the subTemplates
12560      * when using named templates, you can do:
12561      *
12562      * var str = pl.applySubTemplate('your-name', values);
12563      *
12564      * 
12565      * @param {Number} id of the template
12566      * @param {Object} values to apply to template
12567      * @param {Object} parent (normaly the instance of this object)
12568      */
12569     applySubTemplate : function(id, values, parent)
12570     {
12571         
12572         
12573         var t = this.tpls[id];
12574         
12575         
12576         try { 
12577             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12578                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12579                 return '';
12580             }
12581         } catch(e) {
12582             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12583             Roo.log(values);
12584           
12585             return '';
12586         }
12587         try { 
12588             
12589             if(t.execCall && t.execCall.call(this, values, parent)){
12590                 return '';
12591             }
12592         } catch(e) {
12593             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12594             Roo.log(values);
12595             return '';
12596         }
12597         
12598         try {
12599             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12600             parent = t.target ? values : parent;
12601             if(t.forCall && vs instanceof Array){
12602                 var buf = [];
12603                 for(var i = 0, len = vs.length; i < len; i++){
12604                     try {
12605                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12606                     } catch (e) {
12607                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12608                         Roo.log(e.body);
12609                         //Roo.log(t.compiled);
12610                         Roo.log(vs[i]);
12611                     }   
12612                 }
12613                 return buf.join('');
12614             }
12615         } catch (e) {
12616             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12617             Roo.log(values);
12618             return '';
12619         }
12620         try {
12621             return t.compiled.call(this, vs, parent);
12622         } catch (e) {
12623             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12624             Roo.log(e.body);
12625             //Roo.log(t.compiled);
12626             Roo.log(values);
12627             return '';
12628         }
12629     },
12630
12631    
12632
12633     applyTemplate : function(values){
12634         return this.master.compiled.call(this, values, {});
12635         //var s = this.subs;
12636     },
12637
12638     apply : function(){
12639         return this.applyTemplate.apply(this, arguments);
12640     }
12641
12642  });
12643
12644 Roo.DomTemplate.from = function(el){
12645     el = Roo.getDom(el);
12646     return new Roo.Domtemplate(el.value || el.innerHTML);
12647 };/*
12648  * Based on:
12649  * Ext JS Library 1.1.1
12650  * Copyright(c) 2006-2007, Ext JS, LLC.
12651  *
12652  * Originally Released Under LGPL - original licence link has changed is not relivant.
12653  *
12654  * Fork - LGPL
12655  * <script type="text/javascript">
12656  */
12657
12658 /**
12659  * @class Roo.util.DelayedTask
12660  * Provides a convenient method of performing setTimeout where a new
12661  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12662  * You can use this class to buffer
12663  * the keypress events for a certain number of milliseconds, and perform only if they stop
12664  * for that amount of time.
12665  * @constructor The parameters to this constructor serve as defaults and are not required.
12666  * @param {Function} fn (optional) The default function to timeout
12667  * @param {Object} scope (optional) The default scope of that timeout
12668  * @param {Array} args (optional) The default Array of arguments
12669  */
12670 Roo.util.DelayedTask = function(fn, scope, args){
12671     var id = null, d, t;
12672
12673     var call = function(){
12674         var now = new Date().getTime();
12675         if(now - t >= d){
12676             clearInterval(id);
12677             id = null;
12678             fn.apply(scope, args || []);
12679         }
12680     };
12681     /**
12682      * Cancels any pending timeout and queues a new one
12683      * @param {Number} delay The milliseconds to delay
12684      * @param {Function} newFn (optional) Overrides function passed to constructor
12685      * @param {Object} newScope (optional) Overrides scope passed to constructor
12686      * @param {Array} newArgs (optional) Overrides args passed to constructor
12687      */
12688     this.delay = function(delay, newFn, newScope, newArgs){
12689         if(id && delay != d){
12690             this.cancel();
12691         }
12692         d = delay;
12693         t = new Date().getTime();
12694         fn = newFn || fn;
12695         scope = newScope || scope;
12696         args = newArgs || args;
12697         if(!id){
12698             id = setInterval(call, d);
12699         }
12700     };
12701
12702     /**
12703      * Cancel the last queued timeout
12704      */
12705     this.cancel = function(){
12706         if(id){
12707             clearInterval(id);
12708             id = null;
12709         }
12710     };
12711 };/*
12712  * Based on:
12713  * Ext JS Library 1.1.1
12714  * Copyright(c) 2006-2007, Ext JS, LLC.
12715  *
12716  * Originally Released Under LGPL - original licence link has changed is not relivant.
12717  *
12718  * Fork - LGPL
12719  * <script type="text/javascript">
12720  */
12721  
12722  
12723 Roo.util.TaskRunner = function(interval){
12724     interval = interval || 10;
12725     var tasks = [], removeQueue = [];
12726     var id = 0;
12727     var running = false;
12728
12729     var stopThread = function(){
12730         running = false;
12731         clearInterval(id);
12732         id = 0;
12733     };
12734
12735     var startThread = function(){
12736         if(!running){
12737             running = true;
12738             id = setInterval(runTasks, interval);
12739         }
12740     };
12741
12742     var removeTask = function(task){
12743         removeQueue.push(task);
12744         if(task.onStop){
12745             task.onStop();
12746         }
12747     };
12748
12749     var runTasks = function(){
12750         if(removeQueue.length > 0){
12751             for(var i = 0, len = removeQueue.length; i < len; i++){
12752                 tasks.remove(removeQueue[i]);
12753             }
12754             removeQueue = [];
12755             if(tasks.length < 1){
12756                 stopThread();
12757                 return;
12758             }
12759         }
12760         var now = new Date().getTime();
12761         for(var i = 0, len = tasks.length; i < len; ++i){
12762             var t = tasks[i];
12763             var itime = now - t.taskRunTime;
12764             if(t.interval <= itime){
12765                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12766                 t.taskRunTime = now;
12767                 if(rt === false || t.taskRunCount === t.repeat){
12768                     removeTask(t);
12769                     return;
12770                 }
12771             }
12772             if(t.duration && t.duration <= (now - t.taskStartTime)){
12773                 removeTask(t);
12774             }
12775         }
12776     };
12777
12778     /**
12779      * Queues a new task.
12780      * @param {Object} task
12781      */
12782     this.start = function(task){
12783         tasks.push(task);
12784         task.taskStartTime = new Date().getTime();
12785         task.taskRunTime = 0;
12786         task.taskRunCount = 0;
12787         startThread();
12788         return task;
12789     };
12790
12791     this.stop = function(task){
12792         removeTask(task);
12793         return task;
12794     };
12795
12796     this.stopAll = function(){
12797         stopThread();
12798         for(var i = 0, len = tasks.length; i < len; i++){
12799             if(tasks[i].onStop){
12800                 tasks[i].onStop();
12801             }
12802         }
12803         tasks = [];
12804         removeQueue = [];
12805     };
12806 };
12807
12808 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12809  * Based on:
12810  * Ext JS Library 1.1.1
12811  * Copyright(c) 2006-2007, Ext JS, LLC.
12812  *
12813  * Originally Released Under LGPL - original licence link has changed is not relivant.
12814  *
12815  * Fork - LGPL
12816  * <script type="text/javascript">
12817  */
12818
12819  
12820 /**
12821  * @class Roo.util.MixedCollection
12822  * @extends Roo.util.Observable
12823  * A Collection class that maintains both numeric indexes and keys and exposes events.
12824  * @constructor
12825  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12826  * collection (defaults to false)
12827  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12828  * and return the key value for that item.  This is used when available to look up the key on items that
12829  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12830  * equivalent to providing an implementation for the {@link #getKey} method.
12831  */
12832 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12833     this.items = [];
12834     this.map = {};
12835     this.keys = [];
12836     this.length = 0;
12837     this.addEvents({
12838         /**
12839          * @event clear
12840          * Fires when the collection is cleared.
12841          */
12842         "clear" : true,
12843         /**
12844          * @event add
12845          * Fires when an item is added to the collection.
12846          * @param {Number} index The index at which the item was added.
12847          * @param {Object} o The item added.
12848          * @param {String} key The key associated with the added item.
12849          */
12850         "add" : true,
12851         /**
12852          * @event replace
12853          * Fires when an item is replaced in the collection.
12854          * @param {String} key he key associated with the new added.
12855          * @param {Object} old The item being replaced.
12856          * @param {Object} new The new item.
12857          */
12858         "replace" : true,
12859         /**
12860          * @event remove
12861          * Fires when an item is removed from the collection.
12862          * @param {Object} o The item being removed.
12863          * @param {String} key (optional) The key associated with the removed item.
12864          */
12865         "remove" : true,
12866         "sort" : true
12867     });
12868     this.allowFunctions = allowFunctions === true;
12869     if(keyFn){
12870         this.getKey = keyFn;
12871     }
12872     Roo.util.MixedCollection.superclass.constructor.call(this);
12873 };
12874
12875 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12876     allowFunctions : false,
12877     
12878 /**
12879  * Adds an item to the collection.
12880  * @param {String} key The key to associate with the item
12881  * @param {Object} o The item to add.
12882  * @return {Object} The item added.
12883  */
12884     add : function(key, o){
12885         if(arguments.length == 1){
12886             o = arguments[0];
12887             key = this.getKey(o);
12888         }
12889         if(typeof key == "undefined" || key === null){
12890             this.length++;
12891             this.items.push(o);
12892             this.keys.push(null);
12893         }else{
12894             var old = this.map[key];
12895             if(old){
12896                 return this.replace(key, o);
12897             }
12898             this.length++;
12899             this.items.push(o);
12900             this.map[key] = o;
12901             this.keys.push(key);
12902         }
12903         this.fireEvent("add", this.length-1, o, key);
12904         return o;
12905     },
12906        
12907 /**
12908   * MixedCollection has a generic way to fetch keys if you implement getKey.
12909 <pre><code>
12910 // normal way
12911 var mc = new Roo.util.MixedCollection();
12912 mc.add(someEl.dom.id, someEl);
12913 mc.add(otherEl.dom.id, otherEl);
12914 //and so on
12915
12916 // using getKey
12917 var mc = new Roo.util.MixedCollection();
12918 mc.getKey = function(el){
12919    return el.dom.id;
12920 };
12921 mc.add(someEl);
12922 mc.add(otherEl);
12923
12924 // or via the constructor
12925 var mc = new Roo.util.MixedCollection(false, function(el){
12926    return el.dom.id;
12927 });
12928 mc.add(someEl);
12929 mc.add(otherEl);
12930 </code></pre>
12931  * @param o {Object} The item for which to find the key.
12932  * @return {Object} The key for the passed item.
12933  */
12934     getKey : function(o){
12935          return o.id; 
12936     },
12937    
12938 /**
12939  * Replaces an item in the collection.
12940  * @param {String} key The key associated with the item to replace, or the item to replace.
12941  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12942  * @return {Object}  The new item.
12943  */
12944     replace : function(key, o){
12945         if(arguments.length == 1){
12946             o = arguments[0];
12947             key = this.getKey(o);
12948         }
12949         var old = this.item(key);
12950         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12951              return this.add(key, o);
12952         }
12953         var index = this.indexOfKey(key);
12954         this.items[index] = o;
12955         this.map[key] = o;
12956         this.fireEvent("replace", key, old, o);
12957         return o;
12958     },
12959    
12960 /**
12961  * Adds all elements of an Array or an Object to the collection.
12962  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12963  * an Array of values, each of which are added to the collection.
12964  */
12965     addAll : function(objs){
12966         if(arguments.length > 1 || objs instanceof Array){
12967             var args = arguments.length > 1 ? arguments : objs;
12968             for(var i = 0, len = args.length; i < len; i++){
12969                 this.add(args[i]);
12970             }
12971         }else{
12972             for(var key in objs){
12973                 if(this.allowFunctions || typeof objs[key] != "function"){
12974                     this.add(key, objs[key]);
12975                 }
12976             }
12977         }
12978     },
12979    
12980 /**
12981  * Executes the specified function once for every item in the collection, passing each
12982  * item as the first and only parameter. returning false from the function will stop the iteration.
12983  * @param {Function} fn The function to execute for each item.
12984  * @param {Object} scope (optional) The scope in which to execute the function.
12985  */
12986     each : function(fn, scope){
12987         var items = [].concat(this.items); // each safe for removal
12988         for(var i = 0, len = items.length; i < len; i++){
12989             if(fn.call(scope || items[i], items[i], i, len) === false){
12990                 break;
12991             }
12992         }
12993     },
12994    
12995 /**
12996  * Executes the specified function once for every key in the collection, passing each
12997  * key, and its associated item as the first two parameters.
12998  * @param {Function} fn The function to execute for each item.
12999  * @param {Object} scope (optional) The scope in which to execute the function.
13000  */
13001     eachKey : function(fn, scope){
13002         for(var i = 0, len = this.keys.length; i < len; i++){
13003             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13004         }
13005     },
13006    
13007 /**
13008  * Returns the first item in the collection which elicits a true return value from the
13009  * passed selection function.
13010  * @param {Function} fn The selection function to execute for each item.
13011  * @param {Object} scope (optional) The scope in which to execute the function.
13012  * @return {Object} The first item in the collection which returned true from the selection function.
13013  */
13014     find : function(fn, scope){
13015         for(var i = 0, len = this.items.length; i < len; i++){
13016             if(fn.call(scope || window, this.items[i], this.keys[i])){
13017                 return this.items[i];
13018             }
13019         }
13020         return null;
13021     },
13022    
13023 /**
13024  * Inserts an item at the specified index in the collection.
13025  * @param {Number} index The index to insert the item at.
13026  * @param {String} key The key to associate with the new item, or the item itself.
13027  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13028  * @return {Object} The item inserted.
13029  */
13030     insert : function(index, key, o){
13031         if(arguments.length == 2){
13032             o = arguments[1];
13033             key = this.getKey(o);
13034         }
13035         if(index >= this.length){
13036             return this.add(key, o);
13037         }
13038         this.length++;
13039         this.items.splice(index, 0, o);
13040         if(typeof key != "undefined" && key != null){
13041             this.map[key] = o;
13042         }
13043         this.keys.splice(index, 0, key);
13044         this.fireEvent("add", index, o, key);
13045         return o;
13046     },
13047    
13048 /**
13049  * Removed an item from the collection.
13050  * @param {Object} o The item to remove.
13051  * @return {Object} The item removed.
13052  */
13053     remove : function(o){
13054         return this.removeAt(this.indexOf(o));
13055     },
13056    
13057 /**
13058  * Remove an item from a specified index in the collection.
13059  * @param {Number} index The index within the collection of the item to remove.
13060  */
13061     removeAt : function(index){
13062         if(index < this.length && index >= 0){
13063             this.length--;
13064             var o = this.items[index];
13065             this.items.splice(index, 1);
13066             var key = this.keys[index];
13067             if(typeof key != "undefined"){
13068                 delete this.map[key];
13069             }
13070             this.keys.splice(index, 1);
13071             this.fireEvent("remove", o, key);
13072         }
13073     },
13074    
13075 /**
13076  * Removed an item associated with the passed key fom the collection.
13077  * @param {String} key The key of the item to remove.
13078  */
13079     removeKey : function(key){
13080         return this.removeAt(this.indexOfKey(key));
13081     },
13082    
13083 /**
13084  * Returns the number of items in the collection.
13085  * @return {Number} the number of items in the collection.
13086  */
13087     getCount : function(){
13088         return this.length; 
13089     },
13090    
13091 /**
13092  * Returns index within the collection of the passed Object.
13093  * @param {Object} o The item to find the index of.
13094  * @return {Number} index of the item.
13095  */
13096     indexOf : function(o){
13097         if(!this.items.indexOf){
13098             for(var i = 0, len = this.items.length; i < len; i++){
13099                 if(this.items[i] == o) {
13100                     return i;
13101                 }
13102             }
13103             return -1;
13104         }else{
13105             return this.items.indexOf(o);
13106         }
13107     },
13108    
13109 /**
13110  * Returns index within the collection of the passed key.
13111  * @param {String} key The key to find the index of.
13112  * @return {Number} index of the key.
13113  */
13114     indexOfKey : function(key){
13115         if(!this.keys.indexOf){
13116             for(var i = 0, len = this.keys.length; i < len; i++){
13117                 if(this.keys[i] == key) {
13118                     return i;
13119                 }
13120             }
13121             return -1;
13122         }else{
13123             return this.keys.indexOf(key);
13124         }
13125     },
13126    
13127 /**
13128  * Returns the item associated with the passed key OR index. Key has priority over index.
13129  * @param {String/Number} key The key or index of the item.
13130  * @return {Object} The item associated with the passed key.
13131  */
13132     item : function(key){
13133         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13134         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13135     },
13136     
13137 /**
13138  * Returns the item at the specified index.
13139  * @param {Number} index The index of the item.
13140  * @return {Object}
13141  */
13142     itemAt : function(index){
13143         return this.items[index];
13144     },
13145     
13146 /**
13147  * Returns the item associated with the passed key.
13148  * @param {String/Number} key The key of the item.
13149  * @return {Object} The item associated with the passed key.
13150  */
13151     key : function(key){
13152         return this.map[key];
13153     },
13154    
13155 /**
13156  * Returns true if the collection contains the passed Object as an item.
13157  * @param {Object} o  The Object to look for in the collection.
13158  * @return {Boolean} True if the collection contains the Object as an item.
13159  */
13160     contains : function(o){
13161         return this.indexOf(o) != -1;
13162     },
13163    
13164 /**
13165  * Returns true if the collection contains the passed Object as a key.
13166  * @param {String} key The key to look for in the collection.
13167  * @return {Boolean} True if the collection contains the Object as a key.
13168  */
13169     containsKey : function(key){
13170         return typeof this.map[key] != "undefined";
13171     },
13172    
13173 /**
13174  * Removes all items from the collection.
13175  */
13176     clear : function(){
13177         this.length = 0;
13178         this.items = [];
13179         this.keys = [];
13180         this.map = {};
13181         this.fireEvent("clear");
13182     },
13183    
13184 /**
13185  * Returns the first item in the collection.
13186  * @return {Object} the first item in the collection..
13187  */
13188     first : function(){
13189         return this.items[0]; 
13190     },
13191    
13192 /**
13193  * Returns the last item in the collection.
13194  * @return {Object} the last item in the collection..
13195  */
13196     last : function(){
13197         return this.items[this.length-1];   
13198     },
13199     
13200     _sort : function(property, dir, fn){
13201         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13202         fn = fn || function(a, b){
13203             return a-b;
13204         };
13205         var c = [], k = this.keys, items = this.items;
13206         for(var i = 0, len = items.length; i < len; i++){
13207             c[c.length] = {key: k[i], value: items[i], index: i};
13208         }
13209         c.sort(function(a, b){
13210             var v = fn(a[property], b[property]) * dsc;
13211             if(v == 0){
13212                 v = (a.index < b.index ? -1 : 1);
13213             }
13214             return v;
13215         });
13216         for(var i = 0, len = c.length; i < len; i++){
13217             items[i] = c[i].value;
13218             k[i] = c[i].key;
13219         }
13220         this.fireEvent("sort", this);
13221     },
13222     
13223     /**
13224      * Sorts this collection with the passed comparison function
13225      * @param {String} direction (optional) "ASC" or "DESC"
13226      * @param {Function} fn (optional) comparison function
13227      */
13228     sort : function(dir, fn){
13229         this._sort("value", dir, fn);
13230     },
13231     
13232     /**
13233      * Sorts this collection by keys
13234      * @param {String} direction (optional) "ASC" or "DESC"
13235      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13236      */
13237     keySort : function(dir, fn){
13238         this._sort("key", dir, fn || function(a, b){
13239             return String(a).toUpperCase()-String(b).toUpperCase();
13240         });
13241     },
13242     
13243     /**
13244      * Returns a range of items in this collection
13245      * @param {Number} startIndex (optional) defaults to 0
13246      * @param {Number} endIndex (optional) default to the last item
13247      * @return {Array} An array of items
13248      */
13249     getRange : function(start, end){
13250         var items = this.items;
13251         if(items.length < 1){
13252             return [];
13253         }
13254         start = start || 0;
13255         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13256         var r = [];
13257         if(start <= end){
13258             for(var i = start; i <= end; i++) {
13259                     r[r.length] = items[i];
13260             }
13261         }else{
13262             for(var i = start; i >= end; i--) {
13263                     r[r.length] = items[i];
13264             }
13265         }
13266         return r;
13267     },
13268         
13269     /**
13270      * Filter the <i>objects</i> in this collection by a specific property. 
13271      * Returns a new collection that has been filtered.
13272      * @param {String} property A property on your objects
13273      * @param {String/RegExp} value Either string that the property values 
13274      * should start with or a RegExp to test against the property
13275      * @return {MixedCollection} The new filtered collection
13276      */
13277     filter : function(property, value){
13278         if(!value.exec){ // not a regex
13279             value = String(value);
13280             if(value.length == 0){
13281                 return this.clone();
13282             }
13283             value = new RegExp("^" + Roo.escapeRe(value), "i");
13284         }
13285         return this.filterBy(function(o){
13286             return o && value.test(o[property]);
13287         });
13288         },
13289     
13290     /**
13291      * Filter by a function. * Returns a new collection that has been filtered.
13292      * The passed function will be called with each 
13293      * object in the collection. If the function returns true, the value is included 
13294      * otherwise it is filtered.
13295      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13296      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13297      * @return {MixedCollection} The new filtered collection
13298      */
13299     filterBy : function(fn, scope){
13300         var r = new Roo.util.MixedCollection();
13301         r.getKey = this.getKey;
13302         var k = this.keys, it = this.items;
13303         for(var i = 0, len = it.length; i < len; i++){
13304             if(fn.call(scope||this, it[i], k[i])){
13305                                 r.add(k[i], it[i]);
13306                         }
13307         }
13308         return r;
13309     },
13310     
13311     /**
13312      * Creates a duplicate of this collection
13313      * @return {MixedCollection}
13314      */
13315     clone : function(){
13316         var r = new Roo.util.MixedCollection();
13317         var k = this.keys, it = this.items;
13318         for(var i = 0, len = it.length; i < len; i++){
13319             r.add(k[i], it[i]);
13320         }
13321         r.getKey = this.getKey;
13322         return r;
13323     }
13324 });
13325 /**
13326  * Returns the item associated with the passed key or index.
13327  * @method
13328  * @param {String/Number} key The key or index of the item.
13329  * @return {Object} The item associated with the passed key.
13330  */
13331 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13332  * Based on:
13333  * Ext JS Library 1.1.1
13334  * Copyright(c) 2006-2007, Ext JS, LLC.
13335  *
13336  * Originally Released Under LGPL - original licence link has changed is not relivant.
13337  *
13338  * Fork - LGPL
13339  * <script type="text/javascript">
13340  */
13341 /**
13342  * @class Roo.util.JSON
13343  * Modified version of Douglas Crockford"s json.js that doesn"t
13344  * mess with the Object prototype 
13345  * http://www.json.org/js.html
13346  * @singleton
13347  */
13348 Roo.util.JSON = new (function(){
13349     var useHasOwn = {}.hasOwnProperty ? true : false;
13350     
13351     // crashes Safari in some instances
13352     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13353     
13354     var pad = function(n) {
13355         return n < 10 ? "0" + n : n;
13356     };
13357     
13358     var m = {
13359         "\b": '\\b',
13360         "\t": '\\t',
13361         "\n": '\\n',
13362         "\f": '\\f',
13363         "\r": '\\r',
13364         '"' : '\\"',
13365         "\\": '\\\\'
13366     };
13367
13368     var encodeString = function(s){
13369         if (/["\\\x00-\x1f]/.test(s)) {
13370             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13371                 var c = m[b];
13372                 if(c){
13373                     return c;
13374                 }
13375                 c = b.charCodeAt();
13376                 return "\\u00" +
13377                     Math.floor(c / 16).toString(16) +
13378                     (c % 16).toString(16);
13379             }) + '"';
13380         }
13381         return '"' + s + '"';
13382     };
13383     
13384     var encodeArray = function(o){
13385         var a = ["["], b, i, l = o.length, v;
13386             for (i = 0; i < l; i += 1) {
13387                 v = o[i];
13388                 switch (typeof v) {
13389                     case "undefined":
13390                     case "function":
13391                     case "unknown":
13392                         break;
13393                     default:
13394                         if (b) {
13395                             a.push(',');
13396                         }
13397                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13398                         b = true;
13399                 }
13400             }
13401             a.push("]");
13402             return a.join("");
13403     };
13404     
13405     var encodeDate = function(o){
13406         return '"' + o.getFullYear() + "-" +
13407                 pad(o.getMonth() + 1) + "-" +
13408                 pad(o.getDate()) + "T" +
13409                 pad(o.getHours()) + ":" +
13410                 pad(o.getMinutes()) + ":" +
13411                 pad(o.getSeconds()) + '"';
13412     };
13413     
13414     /**
13415      * Encodes an Object, Array or other value
13416      * @param {Mixed} o The variable to encode
13417      * @return {String} The JSON string
13418      */
13419     this.encode = function(o)
13420     {
13421         // should this be extended to fully wrap stringify..
13422         
13423         if(typeof o == "undefined" || o === null){
13424             return "null";
13425         }else if(o instanceof Array){
13426             return encodeArray(o);
13427         }else if(o instanceof Date){
13428             return encodeDate(o);
13429         }else if(typeof o == "string"){
13430             return encodeString(o);
13431         }else if(typeof o == "number"){
13432             return isFinite(o) ? String(o) : "null";
13433         }else if(typeof o == "boolean"){
13434             return String(o);
13435         }else {
13436             var a = ["{"], b, i, v;
13437             for (i in o) {
13438                 if(!useHasOwn || o.hasOwnProperty(i)) {
13439                     v = o[i];
13440                     switch (typeof v) {
13441                     case "undefined":
13442                     case "function":
13443                     case "unknown":
13444                         break;
13445                     default:
13446                         if(b){
13447                             a.push(',');
13448                         }
13449                         a.push(this.encode(i), ":",
13450                                 v === null ? "null" : this.encode(v));
13451                         b = true;
13452                     }
13453                 }
13454             }
13455             a.push("}");
13456             return a.join("");
13457         }
13458     };
13459     
13460     /**
13461      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13462      * @param {String} json The JSON string
13463      * @return {Object} The resulting object
13464      */
13465     this.decode = function(json){
13466         
13467         return  /** eval:var:json */ eval("(" + json + ')');
13468     };
13469 })();
13470 /** 
13471  * Shorthand for {@link Roo.util.JSON#encode}
13472  * @member Roo encode 
13473  * @method */
13474 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13475 /** 
13476  * Shorthand for {@link Roo.util.JSON#decode}
13477  * @member Roo decode 
13478  * @method */
13479 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13480 /*
13481  * Based on:
13482  * Ext JS Library 1.1.1
13483  * Copyright(c) 2006-2007, Ext JS, LLC.
13484  *
13485  * Originally Released Under LGPL - original licence link has changed is not relivant.
13486  *
13487  * Fork - LGPL
13488  * <script type="text/javascript">
13489  */
13490  
13491 /**
13492  * @class Roo.util.Format
13493  * Reusable data formatting functions
13494  * @singleton
13495  */
13496 Roo.util.Format = function(){
13497     var trimRe = /^\s+|\s+$/g;
13498     return {
13499         /**
13500          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13501          * @param {String} value The string to truncate
13502          * @param {Number} length The maximum length to allow before truncating
13503          * @return {String} The converted text
13504          */
13505         ellipsis : function(value, len){
13506             if(value && value.length > len){
13507                 return value.substr(0, len-3)+"...";
13508             }
13509             return value;
13510         },
13511
13512         /**
13513          * Checks a reference and converts it to empty string if it is undefined
13514          * @param {Mixed} value Reference to check
13515          * @return {Mixed} Empty string if converted, otherwise the original value
13516          */
13517         undef : function(value){
13518             return typeof value != "undefined" ? value : "";
13519         },
13520
13521         /**
13522          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13523          * @param {String} value The string to encode
13524          * @return {String} The encoded text
13525          */
13526         htmlEncode : function(value){
13527             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13528         },
13529
13530         /**
13531          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13532          * @param {String} value The string to decode
13533          * @return {String} The decoded text
13534          */
13535         htmlDecode : function(value){
13536             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13537         },
13538
13539         /**
13540          * Trims any whitespace from either side of a string
13541          * @param {String} value The text to trim
13542          * @return {String} The trimmed text
13543          */
13544         trim : function(value){
13545             return String(value).replace(trimRe, "");
13546         },
13547
13548         /**
13549          * Returns a substring from within an original string
13550          * @param {String} value The original text
13551          * @param {Number} start The start index of the substring
13552          * @param {Number} length The length of the substring
13553          * @return {String} The substring
13554          */
13555         substr : function(value, start, length){
13556             return String(value).substr(start, length);
13557         },
13558
13559         /**
13560          * Converts a string to all lower case letters
13561          * @param {String} value The text to convert
13562          * @return {String} The converted text
13563          */
13564         lowercase : function(value){
13565             return String(value).toLowerCase();
13566         },
13567
13568         /**
13569          * Converts a string to all upper case letters
13570          * @param {String} value The text to convert
13571          * @return {String} The converted text
13572          */
13573         uppercase : function(value){
13574             return String(value).toUpperCase();
13575         },
13576
13577         /**
13578          * Converts the first character only of a string to upper case
13579          * @param {String} value The text to convert
13580          * @return {String} The converted text
13581          */
13582         capitalize : function(value){
13583             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13584         },
13585
13586         // private
13587         call : function(value, fn){
13588             if(arguments.length > 2){
13589                 var args = Array.prototype.slice.call(arguments, 2);
13590                 args.unshift(value);
13591                  
13592                 return /** eval:var:value */  eval(fn).apply(window, args);
13593             }else{
13594                 /** eval:var:value */
13595                 return /** eval:var:value */ eval(fn).call(window, value);
13596             }
13597         },
13598
13599        
13600         /**
13601          * safer version of Math.toFixed..??/
13602          * @param {Number/String} value The numeric value to format
13603          * @param {Number/String} value Decimal places 
13604          * @return {String} The formatted currency string
13605          */
13606         toFixed : function(v, n)
13607         {
13608             // why not use to fixed - precision is buggered???
13609             if (!n) {
13610                 return Math.round(v-0);
13611             }
13612             var fact = Math.pow(10,n+1);
13613             v = (Math.round((v-0)*fact))/fact;
13614             var z = (''+fact).substring(2);
13615             if (v == Math.floor(v)) {
13616                 return Math.floor(v) + '.' + z;
13617             }
13618             
13619             // now just padd decimals..
13620             var ps = String(v).split('.');
13621             var fd = (ps[1] + z);
13622             var r = fd.substring(0,n); 
13623             var rm = fd.substring(n); 
13624             if (rm < 5) {
13625                 return ps[0] + '.' + r;
13626             }
13627             r*=1; // turn it into a number;
13628             r++;
13629             if (String(r).length != n) {
13630                 ps[0]*=1;
13631                 ps[0]++;
13632                 r = String(r).substring(1); // chop the end off.
13633             }
13634             
13635             return ps[0] + '.' + r;
13636              
13637         },
13638         
13639         /**
13640          * Format a number as US currency
13641          * @param {Number/String} value The numeric value to format
13642          * @return {String} The formatted currency string
13643          */
13644         usMoney : function(v){
13645             return '$' + Roo.util.Format.number(v);
13646         },
13647         
13648         /**
13649          * Format a number
13650          * eventually this should probably emulate php's number_format
13651          * @param {Number/String} value The numeric value to format
13652          * @param {Number} decimals number of decimal places
13653          * @return {String} The formatted currency string
13654          */
13655         number : function(v,decimals)
13656         {
13657             // multiply and round.
13658             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13659             var mul = Math.pow(10, decimals);
13660             var zero = String(mul).substring(1);
13661             v = (Math.round((v-0)*mul))/mul;
13662             
13663             // if it's '0' number.. then
13664             
13665             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13666             v = String(v);
13667             var ps = v.split('.');
13668             var whole = ps[0];
13669             
13670             
13671             var r = /(\d+)(\d{3})/;
13672             // add comma's
13673             while (r.test(whole)) {
13674                 whole = whole.replace(r, '$1' + ',' + '$2');
13675             }
13676             
13677             
13678             var sub = ps[1] ?
13679                     // has decimals..
13680                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13681                     // does not have decimals
13682                     (decimals ? ('.' + zero) : '');
13683             
13684             
13685             return whole + sub ;
13686         },
13687         
13688         /**
13689          * Parse a value into a formatted date using the specified format pattern.
13690          * @param {Mixed} value The value to format
13691          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13692          * @return {String} The formatted date string
13693          */
13694         date : function(v, format){
13695             if(!v){
13696                 return "";
13697             }
13698             if(!(v instanceof Date)){
13699                 v = new Date(Date.parse(v));
13700             }
13701             return v.dateFormat(format || Roo.util.Format.defaults.date);
13702         },
13703
13704         /**
13705          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13706          * @param {String} format Any valid date format string
13707          * @return {Function} The date formatting function
13708          */
13709         dateRenderer : function(format){
13710             return function(v){
13711                 return Roo.util.Format.date(v, format);  
13712             };
13713         },
13714
13715         // private
13716         stripTagsRE : /<\/?[^>]+>/gi,
13717         
13718         /**
13719          * Strips all HTML tags
13720          * @param {Mixed} value The text from which to strip tags
13721          * @return {String} The stripped text
13722          */
13723         stripTags : function(v){
13724             return !v ? v : String(v).replace(this.stripTagsRE, "");
13725         }
13726     };
13727 }();
13728 Roo.util.Format.defaults = {
13729     date : 'd/M/Y'
13730 };/*
13731  * Based on:
13732  * Ext JS Library 1.1.1
13733  * Copyright(c) 2006-2007, Ext JS, LLC.
13734  *
13735  * Originally Released Under LGPL - original licence link has changed is not relivant.
13736  *
13737  * Fork - LGPL
13738  * <script type="text/javascript">
13739  */
13740
13741
13742  
13743
13744 /**
13745  * @class Roo.MasterTemplate
13746  * @extends Roo.Template
13747  * Provides a template that can have child templates. The syntax is:
13748 <pre><code>
13749 var t = new Roo.MasterTemplate(
13750         '&lt;select name="{name}"&gt;',
13751                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13752         '&lt;/select&gt;'
13753 );
13754 t.add('options', {value: 'foo', text: 'bar'});
13755 // or you can add multiple child elements in one shot
13756 t.addAll('options', [
13757     {value: 'foo', text: 'bar'},
13758     {value: 'foo2', text: 'bar2'},
13759     {value: 'foo3', text: 'bar3'}
13760 ]);
13761 // then append, applying the master template values
13762 t.append('my-form', {name: 'my-select'});
13763 </code></pre>
13764 * A name attribute for the child template is not required if you have only one child
13765 * template or you want to refer to them by index.
13766  */
13767 Roo.MasterTemplate = function(){
13768     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13769     this.originalHtml = this.html;
13770     var st = {};
13771     var m, re = this.subTemplateRe;
13772     re.lastIndex = 0;
13773     var subIndex = 0;
13774     while(m = re.exec(this.html)){
13775         var name = m[1], content = m[2];
13776         st[subIndex] = {
13777             name: name,
13778             index: subIndex,
13779             buffer: [],
13780             tpl : new Roo.Template(content)
13781         };
13782         if(name){
13783             st[name] = st[subIndex];
13784         }
13785         st[subIndex].tpl.compile();
13786         st[subIndex].tpl.call = this.call.createDelegate(this);
13787         subIndex++;
13788     }
13789     this.subCount = subIndex;
13790     this.subs = st;
13791 };
13792 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13793     /**
13794     * The regular expression used to match sub templates
13795     * @type RegExp
13796     * @property
13797     */
13798     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13799
13800     /**
13801      * Applies the passed values to a child template.
13802      * @param {String/Number} name (optional) The name or index of the child template
13803      * @param {Array/Object} values The values to be applied to the template
13804      * @return {MasterTemplate} this
13805      */
13806      add : function(name, values){
13807         if(arguments.length == 1){
13808             values = arguments[0];
13809             name = 0;
13810         }
13811         var s = this.subs[name];
13812         s.buffer[s.buffer.length] = s.tpl.apply(values);
13813         return this;
13814     },
13815
13816     /**
13817      * Applies all the passed values to a child template.
13818      * @param {String/Number} name (optional) The name or index of the child template
13819      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13820      * @param {Boolean} reset (optional) True to reset the template first
13821      * @return {MasterTemplate} this
13822      */
13823     fill : function(name, values, reset){
13824         var a = arguments;
13825         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13826             values = a[0];
13827             name = 0;
13828             reset = a[1];
13829         }
13830         if(reset){
13831             this.reset();
13832         }
13833         for(var i = 0, len = values.length; i < len; i++){
13834             this.add(name, values[i]);
13835         }
13836         return this;
13837     },
13838
13839     /**
13840      * Resets the template for reuse
13841      * @return {MasterTemplate} this
13842      */
13843      reset : function(){
13844         var s = this.subs;
13845         for(var i = 0; i < this.subCount; i++){
13846             s[i].buffer = [];
13847         }
13848         return this;
13849     },
13850
13851     applyTemplate : function(values){
13852         var s = this.subs;
13853         var replaceIndex = -1;
13854         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13855             return s[++replaceIndex].buffer.join("");
13856         });
13857         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13858     },
13859
13860     apply : function(){
13861         return this.applyTemplate.apply(this, arguments);
13862     },
13863
13864     compile : function(){return this;}
13865 });
13866
13867 /**
13868  * Alias for fill().
13869  * @method
13870  */
13871 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13872  /**
13873  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13874  * var tpl = Roo.MasterTemplate.from('element-id');
13875  * @param {String/HTMLElement} el
13876  * @param {Object} config
13877  * @static
13878  */
13879 Roo.MasterTemplate.from = function(el, config){
13880     el = Roo.getDom(el);
13881     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13882 };/*
13883  * Based on:
13884  * Ext JS Library 1.1.1
13885  * Copyright(c) 2006-2007, Ext JS, LLC.
13886  *
13887  * Originally Released Under LGPL - original licence link has changed is not relivant.
13888  *
13889  * Fork - LGPL
13890  * <script type="text/javascript">
13891  */
13892
13893  
13894 /**
13895  * @class Roo.util.CSS
13896  * Utility class for manipulating CSS rules
13897  * @singleton
13898  */
13899 Roo.util.CSS = function(){
13900         var rules = null;
13901         var doc = document;
13902
13903     var camelRe = /(-[a-z])/gi;
13904     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13905
13906    return {
13907    /**
13908     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13909     * tag and appended to the HEAD of the document.
13910     * @param {String|Object} cssText The text containing the css rules
13911     * @param {String} id An id to add to the stylesheet for later removal
13912     * @return {StyleSheet}
13913     */
13914     createStyleSheet : function(cssText, id){
13915         var ss;
13916         var head = doc.getElementsByTagName("head")[0];
13917         var nrules = doc.createElement("style");
13918         nrules.setAttribute("type", "text/css");
13919         if(id){
13920             nrules.setAttribute("id", id);
13921         }
13922         if (typeof(cssText) != 'string') {
13923             // support object maps..
13924             // not sure if this a good idea.. 
13925             // perhaps it should be merged with the general css handling
13926             // and handle js style props.
13927             var cssTextNew = [];
13928             for(var n in cssText) {
13929                 var citems = [];
13930                 for(var k in cssText[n]) {
13931                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13932                 }
13933                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13934                 
13935             }
13936             cssText = cssTextNew.join("\n");
13937             
13938         }
13939        
13940        
13941        if(Roo.isIE){
13942            head.appendChild(nrules);
13943            ss = nrules.styleSheet;
13944            ss.cssText = cssText;
13945        }else{
13946            try{
13947                 nrules.appendChild(doc.createTextNode(cssText));
13948            }catch(e){
13949                nrules.cssText = cssText; 
13950            }
13951            head.appendChild(nrules);
13952            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13953        }
13954        this.cacheStyleSheet(ss);
13955        return ss;
13956    },
13957
13958    /**
13959     * Removes a style or link tag by id
13960     * @param {String} id The id of the tag
13961     */
13962    removeStyleSheet : function(id){
13963        var existing = doc.getElementById(id);
13964        if(existing){
13965            existing.parentNode.removeChild(existing);
13966        }
13967    },
13968
13969    /**
13970     * Dynamically swaps an existing stylesheet reference for a new one
13971     * @param {String} id The id of an existing link tag to remove
13972     * @param {String} url The href of the new stylesheet to include
13973     */
13974    swapStyleSheet : function(id, url){
13975        this.removeStyleSheet(id);
13976        var ss = doc.createElement("link");
13977        ss.setAttribute("rel", "stylesheet");
13978        ss.setAttribute("type", "text/css");
13979        ss.setAttribute("id", id);
13980        ss.setAttribute("href", url);
13981        doc.getElementsByTagName("head")[0].appendChild(ss);
13982    },
13983    
13984    /**
13985     * Refresh the rule cache if you have dynamically added stylesheets
13986     * @return {Object} An object (hash) of rules indexed by selector
13987     */
13988    refreshCache : function(){
13989        return this.getRules(true);
13990    },
13991
13992    // private
13993    cacheStyleSheet : function(stylesheet){
13994        if(!rules){
13995            rules = {};
13996        }
13997        try{// try catch for cross domain access issue
13998            var ssRules = stylesheet.cssRules || stylesheet.rules;
13999            for(var j = ssRules.length-1; j >= 0; --j){
14000                rules[ssRules[j].selectorText] = ssRules[j];
14001            }
14002        }catch(e){}
14003    },
14004    
14005    /**
14006     * Gets all css rules for the document
14007     * @param {Boolean} refreshCache true to refresh the internal cache
14008     * @return {Object} An object (hash) of rules indexed by selector
14009     */
14010    getRules : function(refreshCache){
14011                 if(rules == null || refreshCache){
14012                         rules = {};
14013                         var ds = doc.styleSheets;
14014                         for(var i =0, len = ds.length; i < len; i++){
14015                             try{
14016                         this.cacheStyleSheet(ds[i]);
14017                     }catch(e){} 
14018                 }
14019                 }
14020                 return rules;
14021         },
14022         
14023         /**
14024     * Gets an an individual CSS rule by selector(s)
14025     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14026     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14027     * @return {CSSRule} The CSS rule or null if one is not found
14028     */
14029    getRule : function(selector, refreshCache){
14030                 var rs = this.getRules(refreshCache);
14031                 if(!(selector instanceof Array)){
14032                     return rs[selector];
14033                 }
14034                 for(var i = 0; i < selector.length; i++){
14035                         if(rs[selector[i]]){
14036                                 return rs[selector[i]];
14037                         }
14038                 }
14039                 return null;
14040         },
14041         
14042         
14043         /**
14044     * Updates a rule property
14045     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14046     * @param {String} property The css property
14047     * @param {String} value The new value for the property
14048     * @return {Boolean} true If a rule was found and updated
14049     */
14050    updateRule : function(selector, property, value){
14051                 if(!(selector instanceof Array)){
14052                         var rule = this.getRule(selector);
14053                         if(rule){
14054                                 rule.style[property.replace(camelRe, camelFn)] = value;
14055                                 return true;
14056                         }
14057                 }else{
14058                         for(var i = 0; i < selector.length; i++){
14059                                 if(this.updateRule(selector[i], property, value)){
14060                                         return true;
14061                                 }
14062                         }
14063                 }
14064                 return false;
14065         }
14066    };   
14067 }();/*
14068  * Based on:
14069  * Ext JS Library 1.1.1
14070  * Copyright(c) 2006-2007, Ext JS, LLC.
14071  *
14072  * Originally Released Under LGPL - original licence link has changed is not relivant.
14073  *
14074  * Fork - LGPL
14075  * <script type="text/javascript">
14076  */
14077
14078  
14079
14080 /**
14081  * @class Roo.util.ClickRepeater
14082  * @extends Roo.util.Observable
14083  * 
14084  * A wrapper class which can be applied to any element. Fires a "click" event while the
14085  * mouse is pressed. The interval between firings may be specified in the config but
14086  * defaults to 10 milliseconds.
14087  * 
14088  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14089  * 
14090  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14091  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14092  * Similar to an autorepeat key delay.
14093  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14094  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14095  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14096  *           "interval" and "delay" are ignored. "immediate" is honored.
14097  * @cfg {Boolean} preventDefault True to prevent the default click event
14098  * @cfg {Boolean} stopDefault True to stop the default click event
14099  * 
14100  * @history
14101  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14102  *     2007-02-02 jvs Renamed to ClickRepeater
14103  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14104  *
14105  *  @constructor
14106  * @param {String/HTMLElement/Element} el The element to listen on
14107  * @param {Object} config
14108  **/
14109 Roo.util.ClickRepeater = function(el, config)
14110 {
14111     this.el = Roo.get(el);
14112     this.el.unselectable();
14113
14114     Roo.apply(this, config);
14115
14116     this.addEvents({
14117     /**
14118      * @event mousedown
14119      * Fires when the mouse button is depressed.
14120      * @param {Roo.util.ClickRepeater} this
14121      */
14122         "mousedown" : true,
14123     /**
14124      * @event click
14125      * Fires on a specified interval during the time the element is pressed.
14126      * @param {Roo.util.ClickRepeater} this
14127      */
14128         "click" : true,
14129     /**
14130      * @event mouseup
14131      * Fires when the mouse key is released.
14132      * @param {Roo.util.ClickRepeater} this
14133      */
14134         "mouseup" : true
14135     });
14136
14137     this.el.on("mousedown", this.handleMouseDown, this);
14138     if(this.preventDefault || this.stopDefault){
14139         this.el.on("click", function(e){
14140             if(this.preventDefault){
14141                 e.preventDefault();
14142             }
14143             if(this.stopDefault){
14144                 e.stopEvent();
14145             }
14146         }, this);
14147     }
14148
14149     // allow inline handler
14150     if(this.handler){
14151         this.on("click", this.handler,  this.scope || this);
14152     }
14153
14154     Roo.util.ClickRepeater.superclass.constructor.call(this);
14155 };
14156
14157 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14158     interval : 20,
14159     delay: 250,
14160     preventDefault : true,
14161     stopDefault : false,
14162     timer : 0,
14163
14164     // private
14165     handleMouseDown : function(){
14166         clearTimeout(this.timer);
14167         this.el.blur();
14168         if(this.pressClass){
14169             this.el.addClass(this.pressClass);
14170         }
14171         this.mousedownTime = new Date();
14172
14173         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14174         this.el.on("mouseout", this.handleMouseOut, this);
14175
14176         this.fireEvent("mousedown", this);
14177         this.fireEvent("click", this);
14178         
14179         this.timer = this.click.defer(this.delay || this.interval, this);
14180     },
14181
14182     // private
14183     click : function(){
14184         this.fireEvent("click", this);
14185         this.timer = this.click.defer(this.getInterval(), this);
14186     },
14187
14188     // private
14189     getInterval: function(){
14190         if(!this.accelerate){
14191             return this.interval;
14192         }
14193         var pressTime = this.mousedownTime.getElapsed();
14194         if(pressTime < 500){
14195             return 400;
14196         }else if(pressTime < 1700){
14197             return 320;
14198         }else if(pressTime < 2600){
14199             return 250;
14200         }else if(pressTime < 3500){
14201             return 180;
14202         }else if(pressTime < 4400){
14203             return 140;
14204         }else if(pressTime < 5300){
14205             return 80;
14206         }else if(pressTime < 6200){
14207             return 50;
14208         }else{
14209             return 10;
14210         }
14211     },
14212
14213     // private
14214     handleMouseOut : function(){
14215         clearTimeout(this.timer);
14216         if(this.pressClass){
14217             this.el.removeClass(this.pressClass);
14218         }
14219         this.el.on("mouseover", this.handleMouseReturn, this);
14220     },
14221
14222     // private
14223     handleMouseReturn : function(){
14224         this.el.un("mouseover", this.handleMouseReturn);
14225         if(this.pressClass){
14226             this.el.addClass(this.pressClass);
14227         }
14228         this.click();
14229     },
14230
14231     // private
14232     handleMouseUp : function(){
14233         clearTimeout(this.timer);
14234         this.el.un("mouseover", this.handleMouseReturn);
14235         this.el.un("mouseout", this.handleMouseOut);
14236         Roo.get(document).un("mouseup", this.handleMouseUp);
14237         this.el.removeClass(this.pressClass);
14238         this.fireEvent("mouseup", this);
14239     }
14240 });/*
14241  * Based on:
14242  * Ext JS Library 1.1.1
14243  * Copyright(c) 2006-2007, Ext JS, LLC.
14244  *
14245  * Originally Released Under LGPL - original licence link has changed is not relivant.
14246  *
14247  * Fork - LGPL
14248  * <script type="text/javascript">
14249  */
14250
14251  
14252 /**
14253  * @class Roo.KeyNav
14254  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14255  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14256  * way to implement custom navigation schemes for any UI component.</p>
14257  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14258  * pageUp, pageDown, del, home, end.  Usage:</p>
14259  <pre><code>
14260 var nav = new Roo.KeyNav("my-element", {
14261     "left" : function(e){
14262         this.moveLeft(e.ctrlKey);
14263     },
14264     "right" : function(e){
14265         this.moveRight(e.ctrlKey);
14266     },
14267     "enter" : function(e){
14268         this.save();
14269     },
14270     scope : this
14271 });
14272 </code></pre>
14273  * @constructor
14274  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14275  * @param {Object} config The config
14276  */
14277 Roo.KeyNav = function(el, config){
14278     this.el = Roo.get(el);
14279     Roo.apply(this, config);
14280     if(!this.disabled){
14281         this.disabled = true;
14282         this.enable();
14283     }
14284 };
14285
14286 Roo.KeyNav.prototype = {
14287     /**
14288      * @cfg {Boolean} disabled
14289      * True to disable this KeyNav instance (defaults to false)
14290      */
14291     disabled : false,
14292     /**
14293      * @cfg {String} defaultEventAction
14294      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14295      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14296      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14297      */
14298     defaultEventAction: "stopEvent",
14299     /**
14300      * @cfg {Boolean} forceKeyDown
14301      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14302      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14303      * handle keydown instead of keypress.
14304      */
14305     forceKeyDown : false,
14306
14307     // private
14308     prepareEvent : function(e){
14309         var k = e.getKey();
14310         var h = this.keyToHandler[k];
14311         //if(h && this[h]){
14312         //    e.stopPropagation();
14313         //}
14314         if(Roo.isSafari && h && k >= 37 && k <= 40){
14315             e.stopEvent();
14316         }
14317     },
14318
14319     // private
14320     relay : function(e){
14321         var k = e.getKey();
14322         var h = this.keyToHandler[k];
14323         if(h && this[h]){
14324             if(this.doRelay(e, this[h], h) !== true){
14325                 e[this.defaultEventAction]();
14326             }
14327         }
14328     },
14329
14330     // private
14331     doRelay : function(e, h, hname){
14332         return h.call(this.scope || this, e);
14333     },
14334
14335     // possible handlers
14336     enter : false,
14337     left : false,
14338     right : false,
14339     up : false,
14340     down : false,
14341     tab : false,
14342     esc : false,
14343     pageUp : false,
14344     pageDown : false,
14345     del : false,
14346     home : false,
14347     end : false,
14348
14349     // quick lookup hash
14350     keyToHandler : {
14351         37 : "left",
14352         39 : "right",
14353         38 : "up",
14354         40 : "down",
14355         33 : "pageUp",
14356         34 : "pageDown",
14357         46 : "del",
14358         36 : "home",
14359         35 : "end",
14360         13 : "enter",
14361         27 : "esc",
14362         9  : "tab"
14363     },
14364
14365         /**
14366          * Enable this KeyNav
14367          */
14368         enable: function(){
14369                 if(this.disabled){
14370             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14371             // the EventObject will normalize Safari automatically
14372             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14373                 this.el.on("keydown", this.relay,  this);
14374             }else{
14375                 this.el.on("keydown", this.prepareEvent,  this);
14376                 this.el.on("keypress", this.relay,  this);
14377             }
14378                     this.disabled = false;
14379                 }
14380         },
14381
14382         /**
14383          * Disable this KeyNav
14384          */
14385         disable: function(){
14386                 if(!this.disabled){
14387                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14388                 this.el.un("keydown", this.relay);
14389             }else{
14390                 this.el.un("keydown", this.prepareEvent);
14391                 this.el.un("keypress", this.relay);
14392             }
14393                     this.disabled = true;
14394                 }
14395         }
14396 };/*
14397  * Based on:
14398  * Ext JS Library 1.1.1
14399  * Copyright(c) 2006-2007, Ext JS, LLC.
14400  *
14401  * Originally Released Under LGPL - original licence link has changed is not relivant.
14402  *
14403  * Fork - LGPL
14404  * <script type="text/javascript">
14405  */
14406
14407  
14408 /**
14409  * @class Roo.KeyMap
14410  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14411  * The constructor accepts the same config object as defined by {@link #addBinding}.
14412  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14413  * combination it will call the function with this signature (if the match is a multi-key
14414  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14415  * A KeyMap can also handle a string representation of keys.<br />
14416  * Usage:
14417  <pre><code>
14418 // map one key by key code
14419 var map = new Roo.KeyMap("my-element", {
14420     key: 13, // or Roo.EventObject.ENTER
14421     fn: myHandler,
14422     scope: myObject
14423 });
14424
14425 // map multiple keys to one action by string
14426 var map = new Roo.KeyMap("my-element", {
14427     key: "a\r\n\t",
14428     fn: myHandler,
14429     scope: myObject
14430 });
14431
14432 // map multiple keys to multiple actions by strings and array of codes
14433 var map = new Roo.KeyMap("my-element", [
14434     {
14435         key: [10,13],
14436         fn: function(){ alert("Return was pressed"); }
14437     }, {
14438         key: "abc",
14439         fn: function(){ alert('a, b or c was pressed'); }
14440     }, {
14441         key: "\t",
14442         ctrl:true,
14443         shift:true,
14444         fn: function(){ alert('Control + shift + tab was pressed.'); }
14445     }
14446 ]);
14447 </code></pre>
14448  * <b>Note: A KeyMap starts enabled</b>
14449  * @constructor
14450  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14451  * @param {Object} config The config (see {@link #addBinding})
14452  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14453  */
14454 Roo.KeyMap = function(el, config, eventName){
14455     this.el  = Roo.get(el);
14456     this.eventName = eventName || "keydown";
14457     this.bindings = [];
14458     if(config){
14459         this.addBinding(config);
14460     }
14461     this.enable();
14462 };
14463
14464 Roo.KeyMap.prototype = {
14465     /**
14466      * True to stop the event from bubbling and prevent the default browser action if the
14467      * key was handled by the KeyMap (defaults to false)
14468      * @type Boolean
14469      */
14470     stopEvent : false,
14471
14472     /**
14473      * Add a new binding to this KeyMap. The following config object properties are supported:
14474      * <pre>
14475 Property    Type             Description
14476 ----------  ---------------  ----------------------------------------------------------------------
14477 key         String/Array     A single keycode or an array of keycodes to handle
14478 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14479 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14480 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14481 fn          Function         The function to call when KeyMap finds the expected key combination
14482 scope       Object           The scope of the callback function
14483 </pre>
14484      *
14485      * Usage:
14486      * <pre><code>
14487 // Create a KeyMap
14488 var map = new Roo.KeyMap(document, {
14489     key: Roo.EventObject.ENTER,
14490     fn: handleKey,
14491     scope: this
14492 });
14493
14494 //Add a new binding to the existing KeyMap later
14495 map.addBinding({
14496     key: 'abc',
14497     shift: true,
14498     fn: handleKey,
14499     scope: this
14500 });
14501 </code></pre>
14502      * @param {Object/Array} config A single KeyMap config or an array of configs
14503      */
14504         addBinding : function(config){
14505         if(config instanceof Array){
14506             for(var i = 0, len = config.length; i < len; i++){
14507                 this.addBinding(config[i]);
14508             }
14509             return;
14510         }
14511         var keyCode = config.key,
14512             shift = config.shift, 
14513             ctrl = config.ctrl, 
14514             alt = config.alt,
14515             fn = config.fn,
14516             scope = config.scope;
14517         if(typeof keyCode == "string"){
14518             var ks = [];
14519             var keyString = keyCode.toUpperCase();
14520             for(var j = 0, len = keyString.length; j < len; j++){
14521                 ks.push(keyString.charCodeAt(j));
14522             }
14523             keyCode = ks;
14524         }
14525         var keyArray = keyCode instanceof Array;
14526         var handler = function(e){
14527             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14528                 var k = e.getKey();
14529                 if(keyArray){
14530                     for(var i = 0, len = keyCode.length; i < len; i++){
14531                         if(keyCode[i] == k){
14532                           if(this.stopEvent){
14533                               e.stopEvent();
14534                           }
14535                           fn.call(scope || window, k, e);
14536                           return;
14537                         }
14538                     }
14539                 }else{
14540                     if(k == keyCode){
14541                         if(this.stopEvent){
14542                            e.stopEvent();
14543                         }
14544                         fn.call(scope || window, k, e);
14545                     }
14546                 }
14547             }
14548         };
14549         this.bindings.push(handler);  
14550         },
14551
14552     /**
14553      * Shorthand for adding a single key listener
14554      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14555      * following options:
14556      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14557      * @param {Function} fn The function to call
14558      * @param {Object} scope (optional) The scope of the function
14559      */
14560     on : function(key, fn, scope){
14561         var keyCode, shift, ctrl, alt;
14562         if(typeof key == "object" && !(key instanceof Array)){
14563             keyCode = key.key;
14564             shift = key.shift;
14565             ctrl = key.ctrl;
14566             alt = key.alt;
14567         }else{
14568             keyCode = key;
14569         }
14570         this.addBinding({
14571             key: keyCode,
14572             shift: shift,
14573             ctrl: ctrl,
14574             alt: alt,
14575             fn: fn,
14576             scope: scope
14577         })
14578     },
14579
14580     // private
14581     handleKeyDown : function(e){
14582             if(this.enabled){ //just in case
14583             var b = this.bindings;
14584             for(var i = 0, len = b.length; i < len; i++){
14585                 b[i].call(this, e);
14586             }
14587             }
14588         },
14589         
14590         /**
14591          * Returns true if this KeyMap is enabled
14592          * @return {Boolean} 
14593          */
14594         isEnabled : function(){
14595             return this.enabled;  
14596         },
14597         
14598         /**
14599          * Enables this KeyMap
14600          */
14601         enable: function(){
14602                 if(!this.enabled){
14603                     this.el.on(this.eventName, this.handleKeyDown, this);
14604                     this.enabled = true;
14605                 }
14606         },
14607
14608         /**
14609          * Disable this KeyMap
14610          */
14611         disable: function(){
14612                 if(this.enabled){
14613                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14614                     this.enabled = false;
14615                 }
14616         }
14617 };/*
14618  * Based on:
14619  * Ext JS Library 1.1.1
14620  * Copyright(c) 2006-2007, Ext JS, LLC.
14621  *
14622  * Originally Released Under LGPL - original licence link has changed is not relivant.
14623  *
14624  * Fork - LGPL
14625  * <script type="text/javascript">
14626  */
14627
14628  
14629 /**
14630  * @class Roo.util.TextMetrics
14631  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14632  * wide, in pixels, a given block of text will be.
14633  * @singleton
14634  */
14635 Roo.util.TextMetrics = function(){
14636     var shared;
14637     return {
14638         /**
14639          * Measures the size of the specified text
14640          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14641          * that can affect the size of the rendered text
14642          * @param {String} text The text to measure
14643          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14644          * in order to accurately measure the text height
14645          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14646          */
14647         measure : function(el, text, fixedWidth){
14648             if(!shared){
14649                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14650             }
14651             shared.bind(el);
14652             shared.setFixedWidth(fixedWidth || 'auto');
14653             return shared.getSize(text);
14654         },
14655
14656         /**
14657          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14658          * the overhead of multiple calls to initialize the style properties on each measurement.
14659          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14660          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14661          * in order to accurately measure the text height
14662          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14663          */
14664         createInstance : function(el, fixedWidth){
14665             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14666         }
14667     };
14668 }();
14669
14670  
14671
14672 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14673     var ml = new Roo.Element(document.createElement('div'));
14674     document.body.appendChild(ml.dom);
14675     ml.position('absolute');
14676     ml.setLeftTop(-1000, -1000);
14677     ml.hide();
14678
14679     if(fixedWidth){
14680         ml.setWidth(fixedWidth);
14681     }
14682      
14683     var instance = {
14684         /**
14685          * Returns the size of the specified text based on the internal element's style and width properties
14686          * @memberOf Roo.util.TextMetrics.Instance#
14687          * @param {String} text The text to measure
14688          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14689          */
14690         getSize : function(text){
14691             ml.update(text);
14692             var s = ml.getSize();
14693             ml.update('');
14694             return s;
14695         },
14696
14697         /**
14698          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14699          * that can affect the size of the rendered text
14700          * @memberOf Roo.util.TextMetrics.Instance#
14701          * @param {String/HTMLElement} el The element, dom node or id
14702          */
14703         bind : function(el){
14704             ml.setStyle(
14705                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14706             );
14707         },
14708
14709         /**
14710          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14711          * to set a fixed width in order to accurately measure the text height.
14712          * @memberOf Roo.util.TextMetrics.Instance#
14713          * @param {Number} width The width to set on the element
14714          */
14715         setFixedWidth : function(width){
14716             ml.setWidth(width);
14717         },
14718
14719         /**
14720          * Returns the measured width of the specified text
14721          * @memberOf Roo.util.TextMetrics.Instance#
14722          * @param {String} text The text to measure
14723          * @return {Number} width The width in pixels
14724          */
14725         getWidth : function(text){
14726             ml.dom.style.width = 'auto';
14727             return this.getSize(text).width;
14728         },
14729
14730         /**
14731          * Returns the measured height of the specified text.  For multiline text, be sure to call
14732          * {@link #setFixedWidth} if necessary.
14733          * @memberOf Roo.util.TextMetrics.Instance#
14734          * @param {String} text The text to measure
14735          * @return {Number} height The height in pixels
14736          */
14737         getHeight : function(text){
14738             return this.getSize(text).height;
14739         }
14740     };
14741
14742     instance.bind(bindTo);
14743
14744     return instance;
14745 };
14746
14747 // backwards compat
14748 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14749  * Based on:
14750  * Ext JS Library 1.1.1
14751  * Copyright(c) 2006-2007, Ext JS, LLC.
14752  *
14753  * Originally Released Under LGPL - original licence link has changed is not relivant.
14754  *
14755  * Fork - LGPL
14756  * <script type="text/javascript">
14757  */
14758
14759 /**
14760  * @class Roo.state.Provider
14761  * Abstract base class for state provider implementations. This class provides methods
14762  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14763  * Provider interface.
14764  */
14765 Roo.state.Provider = function(){
14766     /**
14767      * @event statechange
14768      * Fires when a state change occurs.
14769      * @param {Provider} this This state provider
14770      * @param {String} key The state key which was changed
14771      * @param {String} value The encoded value for the state
14772      */
14773     this.addEvents({
14774         "statechange": true
14775     });
14776     this.state = {};
14777     Roo.state.Provider.superclass.constructor.call(this);
14778 };
14779 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14780     /**
14781      * Returns the current value for a key
14782      * @param {String} name The key name
14783      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14784      * @return {Mixed} The state data
14785      */
14786     get : function(name, defaultValue){
14787         return typeof this.state[name] == "undefined" ?
14788             defaultValue : this.state[name];
14789     },
14790     
14791     /**
14792      * Clears a value from the state
14793      * @param {String} name The key name
14794      */
14795     clear : function(name){
14796         delete this.state[name];
14797         this.fireEvent("statechange", this, name, null);
14798     },
14799     
14800     /**
14801      * Sets the value for a key
14802      * @param {String} name The key name
14803      * @param {Mixed} value The value to set
14804      */
14805     set : function(name, value){
14806         this.state[name] = value;
14807         this.fireEvent("statechange", this, name, value);
14808     },
14809     
14810     /**
14811      * Decodes a string previously encoded with {@link #encodeValue}.
14812      * @param {String} value The value to decode
14813      * @return {Mixed} The decoded value
14814      */
14815     decodeValue : function(cookie){
14816         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14817         var matches = re.exec(unescape(cookie));
14818         if(!matches || !matches[1]) {
14819             return; // non state cookie
14820         }
14821         var type = matches[1];
14822         var v = matches[2];
14823         switch(type){
14824             case "n":
14825                 return parseFloat(v);
14826             case "d":
14827                 return new Date(Date.parse(v));
14828             case "b":
14829                 return (v == "1");
14830             case "a":
14831                 var all = [];
14832                 var values = v.split("^");
14833                 for(var i = 0, len = values.length; i < len; i++){
14834                     all.push(this.decodeValue(values[i]));
14835                 }
14836                 return all;
14837            case "o":
14838                 var all = {};
14839                 var values = v.split("^");
14840                 for(var i = 0, len = values.length; i < len; i++){
14841                     var kv = values[i].split("=");
14842                     all[kv[0]] = this.decodeValue(kv[1]);
14843                 }
14844                 return all;
14845            default:
14846                 return v;
14847         }
14848     },
14849     
14850     /**
14851      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14852      * @param {Mixed} value The value to encode
14853      * @return {String} The encoded value
14854      */
14855     encodeValue : function(v){
14856         var enc;
14857         if(typeof v == "number"){
14858             enc = "n:" + v;
14859         }else if(typeof v == "boolean"){
14860             enc = "b:" + (v ? "1" : "0");
14861         }else if(v instanceof Date){
14862             enc = "d:" + v.toGMTString();
14863         }else if(v instanceof Array){
14864             var flat = "";
14865             for(var i = 0, len = v.length; i < len; i++){
14866                 flat += this.encodeValue(v[i]);
14867                 if(i != len-1) {
14868                     flat += "^";
14869                 }
14870             }
14871             enc = "a:" + flat;
14872         }else if(typeof v == "object"){
14873             var flat = "";
14874             for(var key in v){
14875                 if(typeof v[key] != "function"){
14876                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14877                 }
14878             }
14879             enc = "o:" + flat.substring(0, flat.length-1);
14880         }else{
14881             enc = "s:" + v;
14882         }
14883         return escape(enc);        
14884     }
14885 });
14886
14887 /*
14888  * Based on:
14889  * Ext JS Library 1.1.1
14890  * Copyright(c) 2006-2007, Ext JS, LLC.
14891  *
14892  * Originally Released Under LGPL - original licence link has changed is not relivant.
14893  *
14894  * Fork - LGPL
14895  * <script type="text/javascript">
14896  */
14897 /**
14898  * @class Roo.state.Manager
14899  * This is the global state manager. By default all components that are "state aware" check this class
14900  * for state information if you don't pass them a custom state provider. In order for this class
14901  * to be useful, it must be initialized with a provider when your application initializes.
14902  <pre><code>
14903 // in your initialization function
14904 init : function(){
14905    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14906    ...
14907    // supposed you have a {@link Roo.BorderLayout}
14908    var layout = new Roo.BorderLayout(...);
14909    layout.restoreState();
14910    // or a {Roo.BasicDialog}
14911    var dialog = new Roo.BasicDialog(...);
14912    dialog.restoreState();
14913  </code></pre>
14914  * @singleton
14915  */
14916 Roo.state.Manager = function(){
14917     var provider = new Roo.state.Provider();
14918     
14919     return {
14920         /**
14921          * Configures the default state provider for your application
14922          * @param {Provider} stateProvider The state provider to set
14923          */
14924         setProvider : function(stateProvider){
14925             provider = stateProvider;
14926         },
14927         
14928         /**
14929          * Returns the current value for a key
14930          * @param {String} name The key name
14931          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14932          * @return {Mixed} The state data
14933          */
14934         get : function(key, defaultValue){
14935             return provider.get(key, defaultValue);
14936         },
14937         
14938         /**
14939          * Sets the value for a key
14940          * @param {String} name The key name
14941          * @param {Mixed} value The state data
14942          */
14943          set : function(key, value){
14944             provider.set(key, value);
14945         },
14946         
14947         /**
14948          * Clears a value from the state
14949          * @param {String} name The key name
14950          */
14951         clear : function(key){
14952             provider.clear(key);
14953         },
14954         
14955         /**
14956          * Gets the currently configured state provider
14957          * @return {Provider} The state provider
14958          */
14959         getProvider : function(){
14960             return provider;
14961         }
14962     };
14963 }();
14964 /*
14965  * Based on:
14966  * Ext JS Library 1.1.1
14967  * Copyright(c) 2006-2007, Ext JS, LLC.
14968  *
14969  * Originally Released Under LGPL - original licence link has changed is not relivant.
14970  *
14971  * Fork - LGPL
14972  * <script type="text/javascript">
14973  */
14974 /**
14975  * @class Roo.state.CookieProvider
14976  * @extends Roo.state.Provider
14977  * The default Provider implementation which saves state via cookies.
14978  * <br />Usage:
14979  <pre><code>
14980    var cp = new Roo.state.CookieProvider({
14981        path: "/cgi-bin/",
14982        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14983        domain: "roojs.com"
14984    })
14985    Roo.state.Manager.setProvider(cp);
14986  </code></pre>
14987  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14988  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14989  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14990  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14991  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14992  * domain the page is running on including the 'www' like 'www.roojs.com')
14993  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14994  * @constructor
14995  * Create a new CookieProvider
14996  * @param {Object} config The configuration object
14997  */
14998 Roo.state.CookieProvider = function(config){
14999     Roo.state.CookieProvider.superclass.constructor.call(this);
15000     this.path = "/";
15001     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15002     this.domain = null;
15003     this.secure = false;
15004     Roo.apply(this, config);
15005     this.state = this.readCookies();
15006 };
15007
15008 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15009     // private
15010     set : function(name, value){
15011         if(typeof value == "undefined" || value === null){
15012             this.clear(name);
15013             return;
15014         }
15015         this.setCookie(name, value);
15016         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15017     },
15018
15019     // private
15020     clear : function(name){
15021         this.clearCookie(name);
15022         Roo.state.CookieProvider.superclass.clear.call(this, name);
15023     },
15024
15025     // private
15026     readCookies : function(){
15027         var cookies = {};
15028         var c = document.cookie + ";";
15029         var re = /\s?(.*?)=(.*?);/g;
15030         var matches;
15031         while((matches = re.exec(c)) != null){
15032             var name = matches[1];
15033             var value = matches[2];
15034             if(name && name.substring(0,3) == "ys-"){
15035                 cookies[name.substr(3)] = this.decodeValue(value);
15036             }
15037         }
15038         return cookies;
15039     },
15040
15041     // private
15042     setCookie : function(name, value){
15043         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15044            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15045            ((this.path == null) ? "" : ("; path=" + this.path)) +
15046            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15047            ((this.secure == true) ? "; secure" : "");
15048     },
15049
15050     // private
15051     clearCookie : function(name){
15052         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15053            ((this.path == null) ? "" : ("; path=" + this.path)) +
15054            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15055            ((this.secure == true) ? "; secure" : "");
15056     }
15057 });/*
15058  * Based on:
15059  * Ext JS Library 1.1.1
15060  * Copyright(c) 2006-2007, Ext JS, LLC.
15061  *
15062  * Originally Released Under LGPL - original licence link has changed is not relivant.
15063  *
15064  * Fork - LGPL
15065  * <script type="text/javascript">
15066  */
15067  
15068
15069 /**
15070  * @class Roo.ComponentMgr
15071  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15072  * @singleton
15073  */
15074 Roo.ComponentMgr = function(){
15075     var all = new Roo.util.MixedCollection();
15076
15077     return {
15078         /**
15079          * Registers a component.
15080          * @param {Roo.Component} c The component
15081          */
15082         register : function(c){
15083             all.add(c);
15084         },
15085
15086         /**
15087          * Unregisters a component.
15088          * @param {Roo.Component} c The component
15089          */
15090         unregister : function(c){
15091             all.remove(c);
15092         },
15093
15094         /**
15095          * Returns a component by id
15096          * @param {String} id The component id
15097          */
15098         get : function(id){
15099             return all.get(id);
15100         },
15101
15102         /**
15103          * Registers a function that will be called when a specified component is added to ComponentMgr
15104          * @param {String} id The component id
15105          * @param {Funtction} fn The callback function
15106          * @param {Object} scope The scope of the callback
15107          */
15108         onAvailable : function(id, fn, scope){
15109             all.on("add", function(index, o){
15110                 if(o.id == id){
15111                     fn.call(scope || o, o);
15112                     all.un("add", fn, scope);
15113                 }
15114             });
15115         }
15116     };
15117 }();/*
15118  * Based on:
15119  * Ext JS Library 1.1.1
15120  * Copyright(c) 2006-2007, Ext JS, LLC.
15121  *
15122  * Originally Released Under LGPL - original licence link has changed is not relivant.
15123  *
15124  * Fork - LGPL
15125  * <script type="text/javascript">
15126  */
15127  
15128 /**
15129  * @class Roo.Component
15130  * @extends Roo.util.Observable
15131  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15132  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15133  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15134  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15135  * All visual components (widgets) that require rendering into a layout should subclass Component.
15136  * @constructor
15137  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15138  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
15139  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15140  */
15141 Roo.Component = function(config){
15142     config = config || {};
15143     if(config.tagName || config.dom || typeof config == "string"){ // element object
15144         config = {el: config, id: config.id || config};
15145     }
15146     this.initialConfig = config;
15147
15148     Roo.apply(this, config);
15149     this.addEvents({
15150         /**
15151          * @event disable
15152          * Fires after the component is disabled.
15153              * @param {Roo.Component} this
15154              */
15155         disable : true,
15156         /**
15157          * @event enable
15158          * Fires after the component is enabled.
15159              * @param {Roo.Component} this
15160              */
15161         enable : true,
15162         /**
15163          * @event beforeshow
15164          * Fires before the component is shown.  Return false to stop the show.
15165              * @param {Roo.Component} this
15166              */
15167         beforeshow : true,
15168         /**
15169          * @event show
15170          * Fires after the component is shown.
15171              * @param {Roo.Component} this
15172              */
15173         show : true,
15174         /**
15175          * @event beforehide
15176          * Fires before the component is hidden. Return false to stop the hide.
15177              * @param {Roo.Component} this
15178              */
15179         beforehide : true,
15180         /**
15181          * @event hide
15182          * Fires after the component is hidden.
15183              * @param {Roo.Component} this
15184              */
15185         hide : true,
15186         /**
15187          * @event beforerender
15188          * Fires before the component is rendered. Return false to stop the render.
15189              * @param {Roo.Component} this
15190              */
15191         beforerender : true,
15192         /**
15193          * @event render
15194          * Fires after the component is rendered.
15195              * @param {Roo.Component} this
15196              */
15197         render : true,
15198         /**
15199          * @event beforedestroy
15200          * Fires before the component is destroyed. Return false to stop the destroy.
15201              * @param {Roo.Component} this
15202              */
15203         beforedestroy : true,
15204         /**
15205          * @event destroy
15206          * Fires after the component is destroyed.
15207              * @param {Roo.Component} this
15208              */
15209         destroy : true
15210     });
15211     if(!this.id){
15212         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15213     }
15214     Roo.ComponentMgr.register(this);
15215     Roo.Component.superclass.constructor.call(this);
15216     this.initComponent();
15217     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15218         this.render(this.renderTo);
15219         delete this.renderTo;
15220     }
15221 };
15222
15223 /** @private */
15224 Roo.Component.AUTO_ID = 1000;
15225
15226 Roo.extend(Roo.Component, Roo.util.Observable, {
15227     /**
15228      * @scope Roo.Component.prototype
15229      * @type {Boolean}
15230      * true if this component is hidden. Read-only.
15231      */
15232     hidden : false,
15233     /**
15234      * @type {Boolean}
15235      * true if this component is disabled. Read-only.
15236      */
15237     disabled : false,
15238     /**
15239      * @type {Boolean}
15240      * true if this component has been rendered. Read-only.
15241      */
15242     rendered : false,
15243     
15244     /** @cfg {String} disableClass
15245      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15246      */
15247     disabledClass : "x-item-disabled",
15248         /** @cfg {Boolean} allowDomMove
15249          * Whether the component can move the Dom node when rendering (defaults to true).
15250          */
15251     allowDomMove : true,
15252     /** @cfg {String} hideMode (display|visibility)
15253      * How this component should hidden. Supported values are
15254      * "visibility" (css visibility), "offsets" (negative offset position) and
15255      * "display" (css display) - defaults to "display".
15256      */
15257     hideMode: 'display',
15258
15259     /** @private */
15260     ctype : "Roo.Component",
15261
15262     /**
15263      * @cfg {String} actionMode 
15264      * which property holds the element that used for  hide() / show() / disable() / enable()
15265      * default is 'el' 
15266      */
15267     actionMode : "el",
15268
15269     /** @private */
15270     getActionEl : function(){
15271         return this[this.actionMode];
15272     },
15273
15274     initComponent : Roo.emptyFn,
15275     /**
15276      * If this is a lazy rendering component, render it to its container element.
15277      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
15278      */
15279     render : function(container, position){
15280         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15281             if(!container && this.el){
15282                 this.el = Roo.get(this.el);
15283                 container = this.el.dom.parentNode;
15284                 this.allowDomMove = false;
15285             }
15286             this.container = Roo.get(container);
15287             this.rendered = true;
15288             if(position !== undefined){
15289                 if(typeof position == 'number'){
15290                     position = this.container.dom.childNodes[position];
15291                 }else{
15292                     position = Roo.getDom(position);
15293                 }
15294             }
15295             this.onRender(this.container, position || null);
15296             if(this.cls){
15297                 this.el.addClass(this.cls);
15298                 delete this.cls;
15299             }
15300             if(this.style){
15301                 this.el.applyStyles(this.style);
15302                 delete this.style;
15303             }
15304             this.fireEvent("render", this);
15305             this.afterRender(this.container);
15306             if(this.hidden){
15307                 this.hide();
15308             }
15309             if(this.disabled){
15310                 this.disable();
15311             }
15312         }
15313         return this;
15314     },
15315
15316     /** @private */
15317     // default function is not really useful
15318     onRender : function(ct, position){
15319         if(this.el){
15320             this.el = Roo.get(this.el);
15321             if(this.allowDomMove !== false){
15322                 ct.dom.insertBefore(this.el.dom, position);
15323             }
15324         }
15325     },
15326
15327     /** @private */
15328     getAutoCreate : function(){
15329         var cfg = typeof this.autoCreate == "object" ?
15330                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15331         if(this.id && !cfg.id){
15332             cfg.id = this.id;
15333         }
15334         return cfg;
15335     },
15336
15337     /** @private */
15338     afterRender : Roo.emptyFn,
15339
15340     /**
15341      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15342      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15343      */
15344     destroy : function(){
15345         if(this.fireEvent("beforedestroy", this) !== false){
15346             this.purgeListeners();
15347             this.beforeDestroy();
15348             if(this.rendered){
15349                 this.el.removeAllListeners();
15350                 this.el.remove();
15351                 if(this.actionMode == "container"){
15352                     this.container.remove();
15353                 }
15354             }
15355             this.onDestroy();
15356             Roo.ComponentMgr.unregister(this);
15357             this.fireEvent("destroy", this);
15358         }
15359     },
15360
15361         /** @private */
15362     beforeDestroy : function(){
15363
15364     },
15365
15366         /** @private */
15367         onDestroy : function(){
15368
15369     },
15370
15371     /**
15372      * Returns the underlying {@link Roo.Element}.
15373      * @return {Roo.Element} The element
15374      */
15375     getEl : function(){
15376         return this.el;
15377     },
15378
15379     /**
15380      * Returns the id of this component.
15381      * @return {String}
15382      */
15383     getId : function(){
15384         return this.id;
15385     },
15386
15387     /**
15388      * Try to focus this component.
15389      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15390      * @return {Roo.Component} this
15391      */
15392     focus : function(selectText){
15393         if(this.rendered){
15394             this.el.focus();
15395             if(selectText === true){
15396                 this.el.dom.select();
15397             }
15398         }
15399         return this;
15400     },
15401
15402     /** @private */
15403     blur : function(){
15404         if(this.rendered){
15405             this.el.blur();
15406         }
15407         return this;
15408     },
15409
15410     /**
15411      * Disable this component.
15412      * @return {Roo.Component} this
15413      */
15414     disable : function(){
15415         if(this.rendered){
15416             this.onDisable();
15417         }
15418         this.disabled = true;
15419         this.fireEvent("disable", this);
15420         return this;
15421     },
15422
15423         // private
15424     onDisable : function(){
15425         this.getActionEl().addClass(this.disabledClass);
15426         this.el.dom.disabled = true;
15427     },
15428
15429     /**
15430      * Enable this component.
15431      * @return {Roo.Component} this
15432      */
15433     enable : function(){
15434         if(this.rendered){
15435             this.onEnable();
15436         }
15437         this.disabled = false;
15438         this.fireEvent("enable", this);
15439         return this;
15440     },
15441
15442         // private
15443     onEnable : function(){
15444         this.getActionEl().removeClass(this.disabledClass);
15445         this.el.dom.disabled = false;
15446     },
15447
15448     /**
15449      * Convenience function for setting disabled/enabled by boolean.
15450      * @param {Boolean} disabled
15451      */
15452     setDisabled : function(disabled){
15453         this[disabled ? "disable" : "enable"]();
15454     },
15455
15456     /**
15457      * Show this component.
15458      * @return {Roo.Component} this
15459      */
15460     show: function(){
15461         if(this.fireEvent("beforeshow", this) !== false){
15462             this.hidden = false;
15463             if(this.rendered){
15464                 this.onShow();
15465             }
15466             this.fireEvent("show", this);
15467         }
15468         return this;
15469     },
15470
15471     // private
15472     onShow : function(){
15473         var ae = this.getActionEl();
15474         if(this.hideMode == 'visibility'){
15475             ae.dom.style.visibility = "visible";
15476         }else if(this.hideMode == 'offsets'){
15477             ae.removeClass('x-hidden');
15478         }else{
15479             ae.dom.style.display = "";
15480         }
15481     },
15482
15483     /**
15484      * Hide this component.
15485      * @return {Roo.Component} this
15486      */
15487     hide: function(){
15488         if(this.fireEvent("beforehide", this) !== false){
15489             this.hidden = true;
15490             if(this.rendered){
15491                 this.onHide();
15492             }
15493             this.fireEvent("hide", this);
15494         }
15495         return this;
15496     },
15497
15498     // private
15499     onHide : function(){
15500         var ae = this.getActionEl();
15501         if(this.hideMode == 'visibility'){
15502             ae.dom.style.visibility = "hidden";
15503         }else if(this.hideMode == 'offsets'){
15504             ae.addClass('x-hidden');
15505         }else{
15506             ae.dom.style.display = "none";
15507         }
15508     },
15509
15510     /**
15511      * Convenience function to hide or show this component by boolean.
15512      * @param {Boolean} visible True to show, false to hide
15513      * @return {Roo.Component} this
15514      */
15515     setVisible: function(visible){
15516         if(visible) {
15517             this.show();
15518         }else{
15519             this.hide();
15520         }
15521         return this;
15522     },
15523
15524     /**
15525      * Returns true if this component is visible.
15526      */
15527     isVisible : function(){
15528         return this.getActionEl().isVisible();
15529     },
15530
15531     cloneConfig : function(overrides){
15532         overrides = overrides || {};
15533         var id = overrides.id || Roo.id();
15534         var cfg = Roo.applyIf(overrides, this.initialConfig);
15535         cfg.id = id; // prevent dup id
15536         return new this.constructor(cfg);
15537     }
15538 });/*
15539  * Based on:
15540  * Ext JS Library 1.1.1
15541  * Copyright(c) 2006-2007, Ext JS, LLC.
15542  *
15543  * Originally Released Under LGPL - original licence link has changed is not relivant.
15544  *
15545  * Fork - LGPL
15546  * <script type="text/javascript">
15547  */
15548
15549 /**
15550  * @class Roo.BoxComponent
15551  * @extends Roo.Component
15552  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15553  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15554  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15555  * layout containers.
15556  * @constructor
15557  * @param {Roo.Element/String/Object} config The configuration options.
15558  */
15559 Roo.BoxComponent = function(config){
15560     Roo.Component.call(this, config);
15561     this.addEvents({
15562         /**
15563          * @event resize
15564          * Fires after the component is resized.
15565              * @param {Roo.Component} this
15566              * @param {Number} adjWidth The box-adjusted width that was set
15567              * @param {Number} adjHeight The box-adjusted height that was set
15568              * @param {Number} rawWidth The width that was originally specified
15569              * @param {Number} rawHeight The height that was originally specified
15570              */
15571         resize : true,
15572         /**
15573          * @event move
15574          * Fires after the component is moved.
15575              * @param {Roo.Component} this
15576              * @param {Number} x The new x position
15577              * @param {Number} y The new y position
15578              */
15579         move : true
15580     });
15581 };
15582
15583 Roo.extend(Roo.BoxComponent, Roo.Component, {
15584     // private, set in afterRender to signify that the component has been rendered
15585     boxReady : false,
15586     // private, used to defer height settings to subclasses
15587     deferHeight: false,
15588     /** @cfg {Number} width
15589      * width (optional) size of component
15590      */
15591      /** @cfg {Number} height
15592      * height (optional) size of component
15593      */
15594      
15595     /**
15596      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15597      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15598      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15599      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15600      * @return {Roo.BoxComponent} this
15601      */
15602     setSize : function(w, h){
15603         // support for standard size objects
15604         if(typeof w == 'object'){
15605             h = w.height;
15606             w = w.width;
15607         }
15608         // not rendered
15609         if(!this.boxReady){
15610             this.width = w;
15611             this.height = h;
15612             return this;
15613         }
15614
15615         // prevent recalcs when not needed
15616         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15617             return this;
15618         }
15619         this.lastSize = {width: w, height: h};
15620
15621         var adj = this.adjustSize(w, h);
15622         var aw = adj.width, ah = adj.height;
15623         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15624             var rz = this.getResizeEl();
15625             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15626                 rz.setSize(aw, ah);
15627             }else if(!this.deferHeight && ah !== undefined){
15628                 rz.setHeight(ah);
15629             }else if(aw !== undefined){
15630                 rz.setWidth(aw);
15631             }
15632             this.onResize(aw, ah, w, h);
15633             this.fireEvent('resize', this, aw, ah, w, h);
15634         }
15635         return this;
15636     },
15637
15638     /**
15639      * Gets the current size of the component's underlying element.
15640      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15641      */
15642     getSize : function(){
15643         return this.el.getSize();
15644     },
15645
15646     /**
15647      * Gets the current XY position of the component's underlying element.
15648      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15649      * @return {Array} The XY position of the element (e.g., [100, 200])
15650      */
15651     getPosition : function(local){
15652         if(local === true){
15653             return [this.el.getLeft(true), this.el.getTop(true)];
15654         }
15655         return this.xy || this.el.getXY();
15656     },
15657
15658     /**
15659      * Gets the current box measurements of the component's underlying element.
15660      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15661      * @returns {Object} box An object in the format {x, y, width, height}
15662      */
15663     getBox : function(local){
15664         var s = this.el.getSize();
15665         if(local){
15666             s.x = this.el.getLeft(true);
15667             s.y = this.el.getTop(true);
15668         }else{
15669             var xy = this.xy || this.el.getXY();
15670             s.x = xy[0];
15671             s.y = xy[1];
15672         }
15673         return s;
15674     },
15675
15676     /**
15677      * Sets the current box measurements of the component's underlying element.
15678      * @param {Object} box An object in the format {x, y, width, height}
15679      * @returns {Roo.BoxComponent} this
15680      */
15681     updateBox : function(box){
15682         this.setSize(box.width, box.height);
15683         this.setPagePosition(box.x, box.y);
15684         return this;
15685     },
15686
15687     // protected
15688     getResizeEl : function(){
15689         return this.resizeEl || this.el;
15690     },
15691
15692     // protected
15693     getPositionEl : function(){
15694         return this.positionEl || this.el;
15695     },
15696
15697     /**
15698      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15699      * This method fires the move event.
15700      * @param {Number} left The new left
15701      * @param {Number} top The new top
15702      * @returns {Roo.BoxComponent} this
15703      */
15704     setPosition : function(x, y){
15705         this.x = x;
15706         this.y = y;
15707         if(!this.boxReady){
15708             return this;
15709         }
15710         var adj = this.adjustPosition(x, y);
15711         var ax = adj.x, ay = adj.y;
15712
15713         var el = this.getPositionEl();
15714         if(ax !== undefined || ay !== undefined){
15715             if(ax !== undefined && ay !== undefined){
15716                 el.setLeftTop(ax, ay);
15717             }else if(ax !== undefined){
15718                 el.setLeft(ax);
15719             }else if(ay !== undefined){
15720                 el.setTop(ay);
15721             }
15722             this.onPosition(ax, ay);
15723             this.fireEvent('move', this, ax, ay);
15724         }
15725         return this;
15726     },
15727
15728     /**
15729      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15730      * This method fires the move event.
15731      * @param {Number} x The new x position
15732      * @param {Number} y The new y position
15733      * @returns {Roo.BoxComponent} this
15734      */
15735     setPagePosition : function(x, y){
15736         this.pageX = x;
15737         this.pageY = y;
15738         if(!this.boxReady){
15739             return;
15740         }
15741         if(x === undefined || y === undefined){ // cannot translate undefined points
15742             return;
15743         }
15744         var p = this.el.translatePoints(x, y);
15745         this.setPosition(p.left, p.top);
15746         return this;
15747     },
15748
15749     // private
15750     onRender : function(ct, position){
15751         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15752         if(this.resizeEl){
15753             this.resizeEl = Roo.get(this.resizeEl);
15754         }
15755         if(this.positionEl){
15756             this.positionEl = Roo.get(this.positionEl);
15757         }
15758     },
15759
15760     // private
15761     afterRender : function(){
15762         Roo.BoxComponent.superclass.afterRender.call(this);
15763         this.boxReady = true;
15764         this.setSize(this.width, this.height);
15765         if(this.x || this.y){
15766             this.setPosition(this.x, this.y);
15767         }
15768         if(this.pageX || this.pageY){
15769             this.setPagePosition(this.pageX, this.pageY);
15770         }
15771     },
15772
15773     /**
15774      * Force the component's size to recalculate based on the underlying element's current height and width.
15775      * @returns {Roo.BoxComponent} this
15776      */
15777     syncSize : function(){
15778         delete this.lastSize;
15779         this.setSize(this.el.getWidth(), this.el.getHeight());
15780         return this;
15781     },
15782
15783     /**
15784      * Called after the component is resized, this method is empty by default but can be implemented by any
15785      * subclass that needs to perform custom logic after a resize occurs.
15786      * @param {Number} adjWidth The box-adjusted width that was set
15787      * @param {Number} adjHeight The box-adjusted height that was set
15788      * @param {Number} rawWidth The width that was originally specified
15789      * @param {Number} rawHeight The height that was originally specified
15790      */
15791     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15792
15793     },
15794
15795     /**
15796      * Called after the component is moved, this method is empty by default but can be implemented by any
15797      * subclass that needs to perform custom logic after a move occurs.
15798      * @param {Number} x The new x position
15799      * @param {Number} y The new y position
15800      */
15801     onPosition : function(x, y){
15802
15803     },
15804
15805     // private
15806     adjustSize : function(w, h){
15807         if(this.autoWidth){
15808             w = 'auto';
15809         }
15810         if(this.autoHeight){
15811             h = 'auto';
15812         }
15813         return {width : w, height: h};
15814     },
15815
15816     // private
15817     adjustPosition : function(x, y){
15818         return {x : x, y: y};
15819     }
15820 });/*
15821  * Original code for Roojs - LGPL
15822  * <script type="text/javascript">
15823  */
15824  
15825 /**
15826  * @class Roo.XComponent
15827  * A delayed Element creator...
15828  * Or a way to group chunks of interface together.
15829  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15830  *  used in conjunction with XComponent.build() it will create an instance of each element,
15831  *  then call addxtype() to build the User interface.
15832  * 
15833  * Mypart.xyx = new Roo.XComponent({
15834
15835     parent : 'Mypart.xyz', // empty == document.element.!!
15836     order : '001',
15837     name : 'xxxx'
15838     region : 'xxxx'
15839     disabled : function() {} 
15840      
15841     tree : function() { // return an tree of xtype declared components
15842         var MODULE = this;
15843         return 
15844         {
15845             xtype : 'NestedLayoutPanel',
15846             // technicall
15847         }
15848      ]
15849  *})
15850  *
15851  *
15852  * It can be used to build a big heiracy, with parent etc.
15853  * or you can just use this to render a single compoent to a dom element
15854  * MYPART.render(Roo.Element | String(id) | dom_element )
15855  *
15856  *
15857  * Usage patterns.
15858  *
15859  * Classic Roo
15860  *
15861  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15862  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15863  *
15864  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15865  *
15866  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15867  * - if mulitple topModules exist, the last one is defined as the top module.
15868  *
15869  * Embeded Roo
15870  * 
15871  * When the top level or multiple modules are to embedded into a existing HTML page,
15872  * the parent element can container '#id' of the element where the module will be drawn.
15873  *
15874  * Bootstrap Roo
15875  *
15876  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15877  * it relies more on a include mechanism, where sub modules are included into an outer page.
15878  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15879  * 
15880  * Bootstrap Roo Included elements
15881  *
15882  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15883  * hence confusing the component builder as it thinks there are multiple top level elements. 
15884  *
15885  * 
15886  * 
15887  * @extends Roo.util.Observable
15888  * @constructor
15889  * @param cfg {Object} configuration of component
15890  * 
15891  */
15892 Roo.XComponent = function(cfg) {
15893     Roo.apply(this, cfg);
15894     this.addEvents({ 
15895         /**
15896              * @event built
15897              * Fires when this the componnt is built
15898              * @param {Roo.XComponent} c the component
15899              */
15900         'built' : true
15901         
15902     });
15903     this.region = this.region || 'center'; // default..
15904     Roo.XComponent.register(this);
15905     this.modules = false;
15906     this.el = false; // where the layout goes..
15907     
15908     
15909 }
15910 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15911     /**
15912      * @property el
15913      * The created element (with Roo.factory())
15914      * @type {Roo.Layout}
15915      */
15916     el  : false,
15917     
15918     /**
15919      * @property el
15920      * for BC  - use el in new code
15921      * @type {Roo.Layout}
15922      */
15923     panel : false,
15924     
15925     /**
15926      * @property layout
15927      * for BC  - use el in new code
15928      * @type {Roo.Layout}
15929      */
15930     layout : false,
15931     
15932      /**
15933      * @cfg {Function|boolean} disabled
15934      * If this module is disabled by some rule, return true from the funtion
15935      */
15936     disabled : false,
15937     
15938     /**
15939      * @cfg {String} parent 
15940      * Name of parent element which it get xtype added to..
15941      */
15942     parent: false,
15943     
15944     /**
15945      * @cfg {String} order
15946      * Used to set the order in which elements are created (usefull for multiple tabs)
15947      */
15948     
15949     order : false,
15950     /**
15951      * @cfg {String} name
15952      * String to display while loading.
15953      */
15954     name : false,
15955     /**
15956      * @cfg {String} region
15957      * Region to render component to (defaults to center)
15958      */
15959     region : 'center',
15960     
15961     /**
15962      * @cfg {Array} items
15963      * A single item array - the first element is the root of the tree..
15964      * It's done this way to stay compatible with the Xtype system...
15965      */
15966     items : false,
15967     
15968     /**
15969      * @property _tree
15970      * The method that retuns the tree of parts that make up this compoennt 
15971      * @type {function}
15972      */
15973     _tree  : false,
15974     
15975      /**
15976      * render
15977      * render element to dom or tree
15978      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15979      */
15980     
15981     render : function(el)
15982     {
15983         
15984         el = el || false;
15985         var hp = this.parent ? 1 : 0;
15986         Roo.debug &&  Roo.log(this);
15987         
15988         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15989             // if parent is a '#.....' string, then let's use that..
15990             var ename = this.parent.substr(1);
15991             this.parent = false;
15992             Roo.debug && Roo.log(ename);
15993             switch (ename) {
15994                 case 'bootstrap-body' :
15995                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15996                         this.parent = { el :  new  Roo.bootstrap.Body() };
15997                         Roo.debug && Roo.log("setting el to doc body");
15998                          
15999                     } else {
16000                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16001                     }
16002                     break;
16003                 case 'bootstrap':
16004                     this.parent = { el : true};
16005                     // fall through
16006                 default:
16007                     el = Roo.get(ename);
16008                     break;
16009             }
16010                 
16011             
16012             if (!el && !this.parent) {
16013                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16014                 return;
16015             }
16016         }
16017         Roo.debug && Roo.log("EL:");
16018         Roo.debug && Roo.log(el);
16019         Roo.debug && Roo.log("this.parent.el:");
16020         Roo.debug && Roo.log(this.parent.el);
16021         
16022         var tree = this._tree ? this._tree() : this.tree();
16023
16024         // altertive root elements ??? - we need a better way to indicate these.
16025         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16026                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16027         
16028         if (!this.parent && is_alt) {
16029             //el = Roo.get(document.body);
16030             this.parent = { el : true };
16031         }
16032             
16033             
16034         
16035         if (!this.parent) {
16036             
16037             Roo.debug && Roo.log("no parent - creating one");
16038             
16039             el = el ? Roo.get(el) : false;      
16040             
16041             // it's a top level one..
16042             this.parent =  {
16043                 el : new Roo.BorderLayout(el || document.body, {
16044                 
16045                      center: {
16046                          titlebar: false,
16047                          autoScroll:false,
16048                          closeOnTab: true,
16049                          tabPosition: 'top',
16050                           //resizeTabs: true,
16051                          alwaysShowTabs: el && hp? false :  true,
16052                          hideTabs: el || !hp ? true :  false,
16053                          minTabWidth: 140
16054                      }
16055                  })
16056             }
16057         }
16058         
16059         if (!this.parent.el) {
16060                 // probably an old style ctor, which has been disabled.
16061                 return;
16062
16063         }
16064                 // The 'tree' method is  '_tree now' 
16065             
16066         tree.region = tree.region || this.region;
16067         
16068         if (this.parent.el === true) {
16069             // bootstrap... - body..
16070             this.parent.el = Roo.factory(tree);
16071         }
16072         
16073         this.el = this.parent.el.addxtype(tree);
16074         this.fireEvent('built', this);
16075         
16076         this.panel = this.el;
16077         this.layout = this.panel.layout;
16078         this.parentLayout = this.parent.layout  || false;  
16079          
16080     }
16081     
16082 });
16083
16084 Roo.apply(Roo.XComponent, {
16085     /**
16086      * @property  hideProgress
16087      * true to disable the building progress bar.. usefull on single page renders.
16088      * @type Boolean
16089      */
16090     hideProgress : false,
16091     /**
16092      * @property  buildCompleted
16093      * True when the builder has completed building the interface.
16094      * @type Boolean
16095      */
16096     buildCompleted : false,
16097      
16098     /**
16099      * @property  topModule
16100      * the upper most module - uses document.element as it's constructor.
16101      * @type Object
16102      */
16103      
16104     topModule  : false,
16105       
16106     /**
16107      * @property  modules
16108      * array of modules to be created by registration system.
16109      * @type {Array} of Roo.XComponent
16110      */
16111     
16112     modules : [],
16113     /**
16114      * @property  elmodules
16115      * array of modules to be created by which use #ID 
16116      * @type {Array} of Roo.XComponent
16117      */
16118      
16119     elmodules : [],
16120
16121      /**
16122      * @property  build_from_html
16123      * Build elements from html - used by bootstrap HTML stuff 
16124      *    - this is cleared after build is completed
16125      * @type {boolean} true  (default false)
16126      */
16127      
16128     build_from_html : false,
16129
16130     /**
16131      * Register components to be built later.
16132      *
16133      * This solves the following issues
16134      * - Building is not done on page load, but after an authentication process has occured.
16135      * - Interface elements are registered on page load
16136      * - Parent Interface elements may not be loaded before child, so this handles that..
16137      * 
16138      *
16139      * example:
16140      * 
16141      * MyApp.register({
16142           order : '000001',
16143           module : 'Pman.Tab.projectMgr',
16144           region : 'center',
16145           parent : 'Pman.layout',
16146           disabled : false,  // or use a function..
16147         })
16148      
16149      * * @param {Object} details about module
16150      */
16151     register : function(obj) {
16152                 
16153         Roo.XComponent.event.fireEvent('register', obj);
16154         switch(typeof(obj.disabled) ) {
16155                 
16156             case 'undefined':
16157                 break;
16158             
16159             case 'function':
16160                 if ( obj.disabled() ) {
16161                         return;
16162                 }
16163                 break;
16164             
16165             default:
16166                 if (obj.disabled) {
16167                         return;
16168                 }
16169                 break;
16170         }
16171                 
16172         this.modules.push(obj);
16173          
16174     },
16175     /**
16176      * convert a string to an object..
16177      * eg. 'AAA.BBB' -> finds AAA.BBB
16178
16179      */
16180     
16181     toObject : function(str)
16182     {
16183         if (!str || typeof(str) == 'object') {
16184             return str;
16185         }
16186         if (str.substring(0,1) == '#') {
16187             return str;
16188         }
16189
16190         var ar = str.split('.');
16191         var rt, o;
16192         rt = ar.shift();
16193             /** eval:var:o */
16194         try {
16195             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16196         } catch (e) {
16197             throw "Module not found : " + str;
16198         }
16199         
16200         if (o === false) {
16201             throw "Module not found : " + str;
16202         }
16203         Roo.each(ar, function(e) {
16204             if (typeof(o[e]) == 'undefined') {
16205                 throw "Module not found : " + str;
16206             }
16207             o = o[e];
16208         });
16209         
16210         return o;
16211         
16212     },
16213     
16214     
16215     /**
16216      * move modules into their correct place in the tree..
16217      * 
16218      */
16219     preBuild : function ()
16220     {
16221         var _t = this;
16222         Roo.each(this.modules , function (obj)
16223         {
16224             Roo.XComponent.event.fireEvent('beforebuild', obj);
16225             
16226             var opar = obj.parent;
16227             try { 
16228                 obj.parent = this.toObject(opar);
16229             } catch(e) {
16230                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16231                 return;
16232             }
16233             
16234             if (!obj.parent) {
16235                 Roo.debug && Roo.log("GOT top level module");
16236                 Roo.debug && Roo.log(obj);
16237                 obj.modules = new Roo.util.MixedCollection(false, 
16238                     function(o) { return o.order + '' }
16239                 );
16240                 this.topModule = obj;
16241                 return;
16242             }
16243                         // parent is a string (usually a dom element name..)
16244             if (typeof(obj.parent) == 'string') {
16245                 this.elmodules.push(obj);
16246                 return;
16247             }
16248             if (obj.parent.constructor != Roo.XComponent) {
16249                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16250             }
16251             if (!obj.parent.modules) {
16252                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16253                     function(o) { return o.order + '' }
16254                 );
16255             }
16256             if (obj.parent.disabled) {
16257                 obj.disabled = true;
16258             }
16259             obj.parent.modules.add(obj);
16260         }, this);
16261     },
16262     
16263      /**
16264      * make a list of modules to build.
16265      * @return {Array} list of modules. 
16266      */ 
16267     
16268     buildOrder : function()
16269     {
16270         var _this = this;
16271         var cmp = function(a,b) {   
16272             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16273         };
16274         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16275             throw "No top level modules to build";
16276         }
16277         
16278         // make a flat list in order of modules to build.
16279         var mods = this.topModule ? [ this.topModule ] : [];
16280                 
16281         
16282         // elmodules (is a list of DOM based modules )
16283         Roo.each(this.elmodules, function(e) {
16284             mods.push(e);
16285             if (!this.topModule &&
16286                 typeof(e.parent) == 'string' &&
16287                 e.parent.substring(0,1) == '#' &&
16288                 Roo.get(e.parent.substr(1))
16289                ) {
16290                 
16291                 _this.topModule = e;
16292             }
16293             
16294         });
16295
16296         
16297         // add modules to their parents..
16298         var addMod = function(m) {
16299             Roo.debug && Roo.log("build Order: add: " + m.name);
16300                 
16301             mods.push(m);
16302             if (m.modules && !m.disabled) {
16303                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16304                 m.modules.keySort('ASC',  cmp );
16305                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16306     
16307                 m.modules.each(addMod);
16308             } else {
16309                 Roo.debug && Roo.log("build Order: no child modules");
16310             }
16311             // not sure if this is used any more..
16312             if (m.finalize) {
16313                 m.finalize.name = m.name + " (clean up) ";
16314                 mods.push(m.finalize);
16315             }
16316             
16317         }
16318         if (this.topModule && this.topModule.modules) { 
16319             this.topModule.modules.keySort('ASC',  cmp );
16320             this.topModule.modules.each(addMod);
16321         } 
16322         return mods;
16323     },
16324     
16325      /**
16326      * Build the registered modules.
16327      * @param {Object} parent element.
16328      * @param {Function} optional method to call after module has been added.
16329      * 
16330      */ 
16331    
16332     build : function(opts) 
16333     {
16334         
16335         if (typeof(opts) != 'undefined') {
16336             Roo.apply(this,opts);
16337         }
16338         
16339         this.preBuild();
16340         var mods = this.buildOrder();
16341       
16342         //this.allmods = mods;
16343         //Roo.debug && Roo.log(mods);
16344         //return;
16345         if (!mods.length) { // should not happen
16346             throw "NO modules!!!";
16347         }
16348         
16349         
16350         var msg = "Building Interface...";
16351         // flash it up as modal - so we store the mask!?
16352         if (!this.hideProgress && Roo.MessageBox) {
16353             Roo.MessageBox.show({ title: 'loading' });
16354             Roo.MessageBox.show({
16355                title: "Please wait...",
16356                msg: msg,
16357                width:450,
16358                progress:true,
16359                closable:false,
16360                modal: false
16361               
16362             });
16363         }
16364         var total = mods.length;
16365         
16366         var _this = this;
16367         var progressRun = function() {
16368             if (!mods.length) {
16369                 Roo.debug && Roo.log('hide?');
16370                 if (!this.hideProgress && Roo.MessageBox) {
16371                     Roo.MessageBox.hide();
16372                 }
16373                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16374                 
16375                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16376                 
16377                 // THE END...
16378                 return false;   
16379             }
16380             
16381             var m = mods.shift();
16382             
16383             
16384             Roo.debug && Roo.log(m);
16385             // not sure if this is supported any more.. - modules that are are just function
16386             if (typeof(m) == 'function') { 
16387                 m.call(this);
16388                 return progressRun.defer(10, _this);
16389             } 
16390             
16391             
16392             msg = "Building Interface " + (total  - mods.length) + 
16393                     " of " + total + 
16394                     (m.name ? (' - ' + m.name) : '');
16395                         Roo.debug && Roo.log(msg);
16396             if (!this.hideProgress &&  Roo.MessageBox) { 
16397                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16398             }
16399             
16400          
16401             // is the module disabled?
16402             var disabled = (typeof(m.disabled) == 'function') ?
16403                 m.disabled.call(m.module.disabled) : m.disabled;    
16404             
16405             
16406             if (disabled) {
16407                 return progressRun(); // we do not update the display!
16408             }
16409             
16410             // now build 
16411             
16412                         
16413                         
16414             m.render();
16415             // it's 10 on top level, and 1 on others??? why...
16416             return progressRun.defer(10, _this);
16417              
16418         }
16419         progressRun.defer(1, _this);
16420      
16421         
16422         
16423     },
16424         
16425         
16426         /**
16427          * Event Object.
16428          *
16429          *
16430          */
16431         event: false, 
16432     /**
16433          * wrapper for event.on - aliased later..  
16434          * Typically use to register a event handler for register:
16435          *
16436          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16437          *
16438          */
16439     on : false
16440    
16441     
16442     
16443 });
16444
16445 Roo.XComponent.event = new Roo.util.Observable({
16446                 events : { 
16447                         /**
16448                          * @event register
16449                          * Fires when an Component is registered,
16450                          * set the disable property on the Component to stop registration.
16451                          * @param {Roo.XComponent} c the component being registerd.
16452                          * 
16453                          */
16454                         'register' : true,
16455             /**
16456                          * @event beforebuild
16457                          * Fires before each Component is built
16458                          * can be used to apply permissions.
16459                          * @param {Roo.XComponent} c the component being registerd.
16460                          * 
16461                          */
16462                         'beforebuild' : true,
16463                         /**
16464                          * @event buildcomplete
16465                          * Fires on the top level element when all elements have been built
16466                          * @param {Roo.XComponent} the top level component.
16467                          */
16468                         'buildcomplete' : true
16469                         
16470                 }
16471 });
16472
16473 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16474  /*
16475  * Based on:
16476  * Ext JS Library 1.1.1
16477  * Copyright(c) 2006-2007, Ext JS, LLC.
16478  *
16479  * Originally Released Under LGPL - original licence link has changed is not relivant.
16480  *
16481  * Fork - LGPL
16482  * <script type="text/javascript">
16483  */
16484
16485
16486
16487 /*
16488  * These classes are derivatives of the similarly named classes in the YUI Library.
16489  * The original license:
16490  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16491  * Code licensed under the BSD License:
16492  * http://developer.yahoo.net/yui/license.txt
16493  */
16494
16495 (function() {
16496
16497 var Event=Roo.EventManager;
16498 var Dom=Roo.lib.Dom;
16499
16500 /**
16501  * @class Roo.dd.DragDrop
16502  * @extends Roo.util.Observable
16503  * Defines the interface and base operation of items that that can be
16504  * dragged or can be drop targets.  It was designed to be extended, overriding
16505  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16506  * Up to three html elements can be associated with a DragDrop instance:
16507  * <ul>
16508  * <li>linked element: the element that is passed into the constructor.
16509  * This is the element which defines the boundaries for interaction with
16510  * other DragDrop objects.</li>
16511  * <li>handle element(s): The drag operation only occurs if the element that
16512  * was clicked matches a handle element.  By default this is the linked
16513  * element, but there are times that you will want only a portion of the
16514  * linked element to initiate the drag operation, and the setHandleElId()
16515  * method provides a way to define this.</li>
16516  * <li>drag element: this represents the element that would be moved along
16517  * with the cursor during a drag operation.  By default, this is the linked
16518  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16519  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16520  * </li>
16521  * </ul>
16522  * This class should not be instantiated until the onload event to ensure that
16523  * the associated elements are available.
16524  * The following would define a DragDrop obj that would interact with any
16525  * other DragDrop obj in the "group1" group:
16526  * <pre>
16527  *  dd = new Roo.dd.DragDrop("div1", "group1");
16528  * </pre>
16529  * Since none of the event handlers have been implemented, nothing would
16530  * actually happen if you were to run the code above.  Normally you would
16531  * override this class or one of the default implementations, but you can
16532  * also override the methods you want on an instance of the class...
16533  * <pre>
16534  *  dd.onDragDrop = function(e, id) {
16535  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16536  *  }
16537  * </pre>
16538  * @constructor
16539  * @param {String} id of the element that is linked to this instance
16540  * @param {String} sGroup the group of related DragDrop objects
16541  * @param {object} config an object containing configurable attributes
16542  *                Valid properties for DragDrop:
16543  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16544  */
16545 Roo.dd.DragDrop = function(id, sGroup, config) {
16546     if (id) {
16547         this.init(id, sGroup, config);
16548     }
16549     
16550 };
16551
16552 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16553
16554     /**
16555      * The id of the element associated with this object.  This is what we
16556      * refer to as the "linked element" because the size and position of
16557      * this element is used to determine when the drag and drop objects have
16558      * interacted.
16559      * @property id
16560      * @type String
16561      */
16562     id: null,
16563
16564     /**
16565      * Configuration attributes passed into the constructor
16566      * @property config
16567      * @type object
16568      */
16569     config: null,
16570
16571     /**
16572      * The id of the element that will be dragged.  By default this is same
16573      * as the linked element , but could be changed to another element. Ex:
16574      * Roo.dd.DDProxy
16575      * @property dragElId
16576      * @type String
16577      * @private
16578      */
16579     dragElId: null,
16580
16581     /**
16582      * the id of the element that initiates the drag operation.  By default
16583      * this is the linked element, but could be changed to be a child of this
16584      * element.  This lets us do things like only starting the drag when the
16585      * header element within the linked html element is clicked.
16586      * @property handleElId
16587      * @type String
16588      * @private
16589      */
16590     handleElId: null,
16591
16592     /**
16593      * An associative array of HTML tags that will be ignored if clicked.
16594      * @property invalidHandleTypes
16595      * @type {string: string}
16596      */
16597     invalidHandleTypes: null,
16598
16599     /**
16600      * An associative array of ids for elements that will be ignored if clicked
16601      * @property invalidHandleIds
16602      * @type {string: string}
16603      */
16604     invalidHandleIds: null,
16605
16606     /**
16607      * An indexted array of css class names for elements that will be ignored
16608      * if clicked.
16609      * @property invalidHandleClasses
16610      * @type string[]
16611      */
16612     invalidHandleClasses: null,
16613
16614     /**
16615      * The linked element's absolute X position at the time the drag was
16616      * started
16617      * @property startPageX
16618      * @type int
16619      * @private
16620      */
16621     startPageX: 0,
16622
16623     /**
16624      * The linked element's absolute X position at the time the drag was
16625      * started
16626      * @property startPageY
16627      * @type int
16628      * @private
16629      */
16630     startPageY: 0,
16631
16632     /**
16633      * The group defines a logical collection of DragDrop objects that are
16634      * related.  Instances only get events when interacting with other
16635      * DragDrop object in the same group.  This lets us define multiple
16636      * groups using a single DragDrop subclass if we want.
16637      * @property groups
16638      * @type {string: string}
16639      */
16640     groups: null,
16641
16642     /**
16643      * Individual drag/drop instances can be locked.  This will prevent
16644      * onmousedown start drag.
16645      * @property locked
16646      * @type boolean
16647      * @private
16648      */
16649     locked: false,
16650
16651     /**
16652      * Lock this instance
16653      * @method lock
16654      */
16655     lock: function() { this.locked = true; },
16656
16657     /**
16658      * Unlock this instace
16659      * @method unlock
16660      */
16661     unlock: function() { this.locked = false; },
16662
16663     /**
16664      * By default, all insances can be a drop target.  This can be disabled by
16665      * setting isTarget to false.
16666      * @method isTarget
16667      * @type boolean
16668      */
16669     isTarget: true,
16670
16671     /**
16672      * The padding configured for this drag and drop object for calculating
16673      * the drop zone intersection with this object.
16674      * @method padding
16675      * @type int[]
16676      */
16677     padding: null,
16678
16679     /**
16680      * Cached reference to the linked element
16681      * @property _domRef
16682      * @private
16683      */
16684     _domRef: null,
16685
16686     /**
16687      * Internal typeof flag
16688      * @property __ygDragDrop
16689      * @private
16690      */
16691     __ygDragDrop: true,
16692
16693     /**
16694      * Set to true when horizontal contraints are applied
16695      * @property constrainX
16696      * @type boolean
16697      * @private
16698      */
16699     constrainX: false,
16700
16701     /**
16702      * Set to true when vertical contraints are applied
16703      * @property constrainY
16704      * @type boolean
16705      * @private
16706      */
16707     constrainY: false,
16708
16709     /**
16710      * The left constraint
16711      * @property minX
16712      * @type int
16713      * @private
16714      */
16715     minX: 0,
16716
16717     /**
16718      * The right constraint
16719      * @property maxX
16720      * @type int
16721      * @private
16722      */
16723     maxX: 0,
16724
16725     /**
16726      * The up constraint
16727      * @property minY
16728      * @type int
16729      * @type int
16730      * @private
16731      */
16732     minY: 0,
16733
16734     /**
16735      * The down constraint
16736      * @property maxY
16737      * @type int
16738      * @private
16739      */
16740     maxY: 0,
16741
16742     /**
16743      * Maintain offsets when we resetconstraints.  Set to true when you want
16744      * the position of the element relative to its parent to stay the same
16745      * when the page changes
16746      *
16747      * @property maintainOffset
16748      * @type boolean
16749      */
16750     maintainOffset: false,
16751
16752     /**
16753      * Array of pixel locations the element will snap to if we specified a
16754      * horizontal graduation/interval.  This array is generated automatically
16755      * when you define a tick interval.
16756      * @property xTicks
16757      * @type int[]
16758      */
16759     xTicks: null,
16760
16761     /**
16762      * Array of pixel locations the element will snap to if we specified a
16763      * vertical graduation/interval.  This array is generated automatically
16764      * when you define a tick interval.
16765      * @property yTicks
16766      * @type int[]
16767      */
16768     yTicks: null,
16769
16770     /**
16771      * By default the drag and drop instance will only respond to the primary
16772      * button click (left button for a right-handed mouse).  Set to true to
16773      * allow drag and drop to start with any mouse click that is propogated
16774      * by the browser
16775      * @property primaryButtonOnly
16776      * @type boolean
16777      */
16778     primaryButtonOnly: true,
16779
16780     /**
16781      * The availabe property is false until the linked dom element is accessible.
16782      * @property available
16783      * @type boolean
16784      */
16785     available: false,
16786
16787     /**
16788      * By default, drags can only be initiated if the mousedown occurs in the
16789      * region the linked element is.  This is done in part to work around a
16790      * bug in some browsers that mis-report the mousedown if the previous
16791      * mouseup happened outside of the window.  This property is set to true
16792      * if outer handles are defined.
16793      *
16794      * @property hasOuterHandles
16795      * @type boolean
16796      * @default false
16797      */
16798     hasOuterHandles: false,
16799
16800     /**
16801      * Code that executes immediately before the startDrag event
16802      * @method b4StartDrag
16803      * @private
16804      */
16805     b4StartDrag: function(x, y) { },
16806
16807     /**
16808      * Abstract method called after a drag/drop object is clicked
16809      * and the drag or mousedown time thresholds have beeen met.
16810      * @method startDrag
16811      * @param {int} X click location
16812      * @param {int} Y click location
16813      */
16814     startDrag: function(x, y) { /* override this */ },
16815
16816     /**
16817      * Code that executes immediately before the onDrag event
16818      * @method b4Drag
16819      * @private
16820      */
16821     b4Drag: function(e) { },
16822
16823     /**
16824      * Abstract method called during the onMouseMove event while dragging an
16825      * object.
16826      * @method onDrag
16827      * @param {Event} e the mousemove event
16828      */
16829     onDrag: function(e) { /* override this */ },
16830
16831     /**
16832      * Abstract method called when this element fist begins hovering over
16833      * another DragDrop obj
16834      * @method onDragEnter
16835      * @param {Event} e the mousemove event
16836      * @param {String|DragDrop[]} id In POINT mode, the element
16837      * id this is hovering over.  In INTERSECT mode, an array of one or more
16838      * dragdrop items being hovered over.
16839      */
16840     onDragEnter: function(e, id) { /* override this */ },
16841
16842     /**
16843      * Code that executes immediately before the onDragOver event
16844      * @method b4DragOver
16845      * @private
16846      */
16847     b4DragOver: function(e) { },
16848
16849     /**
16850      * Abstract method called when this element is hovering over another
16851      * DragDrop obj
16852      * @method onDragOver
16853      * @param {Event} e the mousemove event
16854      * @param {String|DragDrop[]} id In POINT mode, the element
16855      * id this is hovering over.  In INTERSECT mode, an array of dd items
16856      * being hovered over.
16857      */
16858     onDragOver: function(e, id) { /* override this */ },
16859
16860     /**
16861      * Code that executes immediately before the onDragOut event
16862      * @method b4DragOut
16863      * @private
16864      */
16865     b4DragOut: function(e) { },
16866
16867     /**
16868      * Abstract method called when we are no longer hovering over an element
16869      * @method onDragOut
16870      * @param {Event} e the mousemove event
16871      * @param {String|DragDrop[]} id In POINT mode, the element
16872      * id this was hovering over.  In INTERSECT mode, an array of dd items
16873      * that the mouse is no longer over.
16874      */
16875     onDragOut: function(e, id) { /* override this */ },
16876
16877     /**
16878      * Code that executes immediately before the onDragDrop event
16879      * @method b4DragDrop
16880      * @private
16881      */
16882     b4DragDrop: function(e) { },
16883
16884     /**
16885      * Abstract method called when this item is dropped on another DragDrop
16886      * obj
16887      * @method onDragDrop
16888      * @param {Event} e the mouseup event
16889      * @param {String|DragDrop[]} id In POINT mode, the element
16890      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16891      * was dropped on.
16892      */
16893     onDragDrop: function(e, id) { /* override this */ },
16894
16895     /**
16896      * Abstract method called when this item is dropped on an area with no
16897      * drop target
16898      * @method onInvalidDrop
16899      * @param {Event} e the mouseup event
16900      */
16901     onInvalidDrop: function(e) { /* override this */ },
16902
16903     /**
16904      * Code that executes immediately before the endDrag event
16905      * @method b4EndDrag
16906      * @private
16907      */
16908     b4EndDrag: function(e) { },
16909
16910     /**
16911      * Fired when we are done dragging the object
16912      * @method endDrag
16913      * @param {Event} e the mouseup event
16914      */
16915     endDrag: function(e) { /* override this */ },
16916
16917     /**
16918      * Code executed immediately before the onMouseDown event
16919      * @method b4MouseDown
16920      * @param {Event} e the mousedown event
16921      * @private
16922      */
16923     b4MouseDown: function(e) {  },
16924
16925     /**
16926      * Event handler that fires when a drag/drop obj gets a mousedown
16927      * @method onMouseDown
16928      * @param {Event} e the mousedown event
16929      */
16930     onMouseDown: function(e) { /* override this */ },
16931
16932     /**
16933      * Event handler that fires when a drag/drop obj gets a mouseup
16934      * @method onMouseUp
16935      * @param {Event} e the mouseup event
16936      */
16937     onMouseUp: function(e) { /* override this */ },
16938
16939     /**
16940      * Override the onAvailable method to do what is needed after the initial
16941      * position was determined.
16942      * @method onAvailable
16943      */
16944     onAvailable: function () {
16945     },
16946
16947     /*
16948      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16949      * @type Object
16950      */
16951     defaultPadding : {left:0, right:0, top:0, bottom:0},
16952
16953     /*
16954      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16955  *
16956  * Usage:
16957  <pre><code>
16958  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16959                 { dragElId: "existingProxyDiv" });
16960  dd.startDrag = function(){
16961      this.constrainTo("parent-id");
16962  };
16963  </code></pre>
16964  * Or you can initalize it using the {@link Roo.Element} object:
16965  <pre><code>
16966  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16967      startDrag : function(){
16968          this.constrainTo("parent-id");
16969      }
16970  });
16971  </code></pre>
16972      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16973      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16974      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16975      * an object containing the sides to pad. For example: {right:10, bottom:10}
16976      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16977      */
16978     constrainTo : function(constrainTo, pad, inContent){
16979         if(typeof pad == "number"){
16980             pad = {left: pad, right:pad, top:pad, bottom:pad};
16981         }
16982         pad = pad || this.defaultPadding;
16983         var b = Roo.get(this.getEl()).getBox();
16984         var ce = Roo.get(constrainTo);
16985         var s = ce.getScroll();
16986         var c, cd = ce.dom;
16987         if(cd == document.body){
16988             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16989         }else{
16990             xy = ce.getXY();
16991             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16992         }
16993
16994
16995         var topSpace = b.y - c.y;
16996         var leftSpace = b.x - c.x;
16997
16998         this.resetConstraints();
16999         this.setXConstraint(leftSpace - (pad.left||0), // left
17000                 c.width - leftSpace - b.width - (pad.right||0) //right
17001         );
17002         this.setYConstraint(topSpace - (pad.top||0), //top
17003                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
17004         );
17005     },
17006
17007     /**
17008      * Returns a reference to the linked element
17009      * @method getEl
17010      * @return {HTMLElement} the html element
17011      */
17012     getEl: function() {
17013         if (!this._domRef) {
17014             this._domRef = Roo.getDom(this.id);
17015         }
17016
17017         return this._domRef;
17018     },
17019
17020     /**
17021      * Returns a reference to the actual element to drag.  By default this is
17022      * the same as the html element, but it can be assigned to another
17023      * element. An example of this can be found in Roo.dd.DDProxy
17024      * @method getDragEl
17025      * @return {HTMLElement} the html element
17026      */
17027     getDragEl: function() {
17028         return Roo.getDom(this.dragElId);
17029     },
17030
17031     /**
17032      * Sets up the DragDrop object.  Must be called in the constructor of any
17033      * Roo.dd.DragDrop subclass
17034      * @method init
17035      * @param id the id of the linked element
17036      * @param {String} sGroup the group of related items
17037      * @param {object} config configuration attributes
17038      */
17039     init: function(id, sGroup, config) {
17040         this.initTarget(id, sGroup, config);
17041         if (!Roo.isTouch) {
17042             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17043         }
17044         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17045         // Event.on(this.id, "selectstart", Event.preventDefault);
17046     },
17047
17048     /**
17049      * Initializes Targeting functionality only... the object does not
17050      * get a mousedown handler.
17051      * @method initTarget
17052      * @param id the id of the linked element
17053      * @param {String} sGroup the group of related items
17054      * @param {object} config configuration attributes
17055      */
17056     initTarget: function(id, sGroup, config) {
17057
17058         // configuration attributes
17059         this.config = config || {};
17060
17061         // create a local reference to the drag and drop manager
17062         this.DDM = Roo.dd.DDM;
17063         // initialize the groups array
17064         this.groups = {};
17065
17066         // assume that we have an element reference instead of an id if the
17067         // parameter is not a string
17068         if (typeof id !== "string") {
17069             id = Roo.id(id);
17070         }
17071
17072         // set the id
17073         this.id = id;
17074
17075         // add to an interaction group
17076         this.addToGroup((sGroup) ? sGroup : "default");
17077
17078         // We don't want to register this as the handle with the manager
17079         // so we just set the id rather than calling the setter.
17080         this.handleElId = id;
17081
17082         // the linked element is the element that gets dragged by default
17083         this.setDragElId(id);
17084
17085         // by default, clicked anchors will not start drag operations.
17086         this.invalidHandleTypes = { A: "A" };
17087         this.invalidHandleIds = {};
17088         this.invalidHandleClasses = [];
17089
17090         this.applyConfig();
17091
17092         this.handleOnAvailable();
17093     },
17094
17095     /**
17096      * Applies the configuration parameters that were passed into the constructor.
17097      * This is supposed to happen at each level through the inheritance chain.  So
17098      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17099      * DragDrop in order to get all of the parameters that are available in
17100      * each object.
17101      * @method applyConfig
17102      */
17103     applyConfig: function() {
17104
17105         // configurable properties:
17106         //    padding, isTarget, maintainOffset, primaryButtonOnly
17107         this.padding           = this.config.padding || [0, 0, 0, 0];
17108         this.isTarget          = (this.config.isTarget !== false);
17109         this.maintainOffset    = (this.config.maintainOffset);
17110         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17111
17112     },
17113
17114     /**
17115      * Executed when the linked element is available
17116      * @method handleOnAvailable
17117      * @private
17118      */
17119     handleOnAvailable: function() {
17120         this.available = true;
17121         this.resetConstraints();
17122         this.onAvailable();
17123     },
17124
17125      /**
17126      * Configures the padding for the target zone in px.  Effectively expands
17127      * (or reduces) the virtual object size for targeting calculations.
17128      * Supports css-style shorthand; if only one parameter is passed, all sides
17129      * will have that padding, and if only two are passed, the top and bottom
17130      * will have the first param, the left and right the second.
17131      * @method setPadding
17132      * @param {int} iTop    Top pad
17133      * @param {int} iRight  Right pad
17134      * @param {int} iBot    Bot pad
17135      * @param {int} iLeft   Left pad
17136      */
17137     setPadding: function(iTop, iRight, iBot, iLeft) {
17138         // this.padding = [iLeft, iRight, iTop, iBot];
17139         if (!iRight && 0 !== iRight) {
17140             this.padding = [iTop, iTop, iTop, iTop];
17141         } else if (!iBot && 0 !== iBot) {
17142             this.padding = [iTop, iRight, iTop, iRight];
17143         } else {
17144             this.padding = [iTop, iRight, iBot, iLeft];
17145         }
17146     },
17147
17148     /**
17149      * Stores the initial placement of the linked element.
17150      * @method setInitialPosition
17151      * @param {int} diffX   the X offset, default 0
17152      * @param {int} diffY   the Y offset, default 0
17153      */
17154     setInitPosition: function(diffX, diffY) {
17155         var el = this.getEl();
17156
17157         if (!this.DDM.verifyEl(el)) {
17158             return;
17159         }
17160
17161         var dx = diffX || 0;
17162         var dy = diffY || 0;
17163
17164         var p = Dom.getXY( el );
17165
17166         this.initPageX = p[0] - dx;
17167         this.initPageY = p[1] - dy;
17168
17169         this.lastPageX = p[0];
17170         this.lastPageY = p[1];
17171
17172
17173         this.setStartPosition(p);
17174     },
17175
17176     /**
17177      * Sets the start position of the element.  This is set when the obj
17178      * is initialized, the reset when a drag is started.
17179      * @method setStartPosition
17180      * @param pos current position (from previous lookup)
17181      * @private
17182      */
17183     setStartPosition: function(pos) {
17184         var p = pos || Dom.getXY( this.getEl() );
17185         this.deltaSetXY = null;
17186
17187         this.startPageX = p[0];
17188         this.startPageY = p[1];
17189     },
17190
17191     /**
17192      * Add this instance to a group of related drag/drop objects.  All
17193      * instances belong to at least one group, and can belong to as many
17194      * groups as needed.
17195      * @method addToGroup
17196      * @param sGroup {string} the name of the group
17197      */
17198     addToGroup: function(sGroup) {
17199         this.groups[sGroup] = true;
17200         this.DDM.regDragDrop(this, sGroup);
17201     },
17202
17203     /**
17204      * Remove's this instance from the supplied interaction group
17205      * @method removeFromGroup
17206      * @param {string}  sGroup  The group to drop
17207      */
17208     removeFromGroup: function(sGroup) {
17209         if (this.groups[sGroup]) {
17210             delete this.groups[sGroup];
17211         }
17212
17213         this.DDM.removeDDFromGroup(this, sGroup);
17214     },
17215
17216     /**
17217      * Allows you to specify that an element other than the linked element
17218      * will be moved with the cursor during a drag
17219      * @method setDragElId
17220      * @param id {string} the id of the element that will be used to initiate the drag
17221      */
17222     setDragElId: function(id) {
17223         this.dragElId = id;
17224     },
17225
17226     /**
17227      * Allows you to specify a child of the linked element that should be
17228      * used to initiate the drag operation.  An example of this would be if
17229      * you have a content div with text and links.  Clicking anywhere in the
17230      * content area would normally start the drag operation.  Use this method
17231      * to specify that an element inside of the content div is the element
17232      * that starts the drag operation.
17233      * @method setHandleElId
17234      * @param id {string} the id of the element that will be used to
17235      * initiate the drag.
17236      */
17237     setHandleElId: function(id) {
17238         if (typeof id !== "string") {
17239             id = Roo.id(id);
17240         }
17241         this.handleElId = id;
17242         this.DDM.regHandle(this.id, id);
17243     },
17244
17245     /**
17246      * Allows you to set an element outside of the linked element as a drag
17247      * handle
17248      * @method setOuterHandleElId
17249      * @param id the id of the element that will be used to initiate the drag
17250      */
17251     setOuterHandleElId: function(id) {
17252         if (typeof id !== "string") {
17253             id = Roo.id(id);
17254         }
17255         Event.on(id, "mousedown",
17256                 this.handleMouseDown, this);
17257         this.setHandleElId(id);
17258
17259         this.hasOuterHandles = true;
17260     },
17261
17262     /**
17263      * Remove all drag and drop hooks for this element
17264      * @method unreg
17265      */
17266     unreg: function() {
17267         Event.un(this.id, "mousedown",
17268                 this.handleMouseDown);
17269         Event.un(this.id, "touchstart",
17270                 this.handleMouseDown);
17271         this._domRef = null;
17272         this.DDM._remove(this);
17273     },
17274
17275     destroy : function(){
17276         this.unreg();
17277     },
17278
17279     /**
17280      * Returns true if this instance is locked, or the drag drop mgr is locked
17281      * (meaning that all drag/drop is disabled on the page.)
17282      * @method isLocked
17283      * @return {boolean} true if this obj or all drag/drop is locked, else
17284      * false
17285      */
17286     isLocked: function() {
17287         return (this.DDM.isLocked() || this.locked);
17288     },
17289
17290     /**
17291      * Fired when this object is clicked
17292      * @method handleMouseDown
17293      * @param {Event} e
17294      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17295      * @private
17296      */
17297     handleMouseDown: function(e, oDD){
17298      
17299         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17300             //Roo.log('not touch/ button !=0');
17301             return;
17302         }
17303         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17304             return; // double touch..
17305         }
17306         
17307
17308         if (this.isLocked()) {
17309             //Roo.log('locked');
17310             return;
17311         }
17312
17313         this.DDM.refreshCache(this.groups);
17314 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17315         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17316         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17317             //Roo.log('no outer handes or not over target');
17318                 // do nothing.
17319         } else {
17320 //            Roo.log('check validator');
17321             if (this.clickValidator(e)) {
17322 //                Roo.log('validate success');
17323                 // set the initial element position
17324                 this.setStartPosition();
17325
17326
17327                 this.b4MouseDown(e);
17328                 this.onMouseDown(e);
17329
17330                 this.DDM.handleMouseDown(e, this);
17331
17332                 this.DDM.stopEvent(e);
17333             } else {
17334
17335
17336             }
17337         }
17338     },
17339
17340     clickValidator: function(e) {
17341         var target = e.getTarget();
17342         return ( this.isValidHandleChild(target) &&
17343                     (this.id == this.handleElId ||
17344                         this.DDM.handleWasClicked(target, this.id)) );
17345     },
17346
17347     /**
17348      * Allows you to specify a tag name that should not start a drag operation
17349      * when clicked.  This is designed to facilitate embedding links within a
17350      * drag handle that do something other than start the drag.
17351      * @method addInvalidHandleType
17352      * @param {string} tagName the type of element to exclude
17353      */
17354     addInvalidHandleType: function(tagName) {
17355         var type = tagName.toUpperCase();
17356         this.invalidHandleTypes[type] = type;
17357     },
17358
17359     /**
17360      * Lets you to specify an element id for a child of a drag handle
17361      * that should not initiate a drag
17362      * @method addInvalidHandleId
17363      * @param {string} id the element id of the element you wish to ignore
17364      */
17365     addInvalidHandleId: function(id) {
17366         if (typeof id !== "string") {
17367             id = Roo.id(id);
17368         }
17369         this.invalidHandleIds[id] = id;
17370     },
17371
17372     /**
17373      * Lets you specify a css class of elements that will not initiate a drag
17374      * @method addInvalidHandleClass
17375      * @param {string} cssClass the class of the elements you wish to ignore
17376      */
17377     addInvalidHandleClass: function(cssClass) {
17378         this.invalidHandleClasses.push(cssClass);
17379     },
17380
17381     /**
17382      * Unsets an excluded tag name set by addInvalidHandleType
17383      * @method removeInvalidHandleType
17384      * @param {string} tagName the type of element to unexclude
17385      */
17386     removeInvalidHandleType: function(tagName) {
17387         var type = tagName.toUpperCase();
17388         // this.invalidHandleTypes[type] = null;
17389         delete this.invalidHandleTypes[type];
17390     },
17391
17392     /**
17393      * Unsets an invalid handle id
17394      * @method removeInvalidHandleId
17395      * @param {string} id the id of the element to re-enable
17396      */
17397     removeInvalidHandleId: function(id) {
17398         if (typeof id !== "string") {
17399             id = Roo.id(id);
17400         }
17401         delete this.invalidHandleIds[id];
17402     },
17403
17404     /**
17405      * Unsets an invalid css class
17406      * @method removeInvalidHandleClass
17407      * @param {string} cssClass the class of the element(s) you wish to
17408      * re-enable
17409      */
17410     removeInvalidHandleClass: function(cssClass) {
17411         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17412             if (this.invalidHandleClasses[i] == cssClass) {
17413                 delete this.invalidHandleClasses[i];
17414             }
17415         }
17416     },
17417
17418     /**
17419      * Checks the tag exclusion list to see if this click should be ignored
17420      * @method isValidHandleChild
17421      * @param {HTMLElement} node the HTMLElement to evaluate
17422      * @return {boolean} true if this is a valid tag type, false if not
17423      */
17424     isValidHandleChild: function(node) {
17425
17426         var valid = true;
17427         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17428         var nodeName;
17429         try {
17430             nodeName = node.nodeName.toUpperCase();
17431         } catch(e) {
17432             nodeName = node.nodeName;
17433         }
17434         valid = valid && !this.invalidHandleTypes[nodeName];
17435         valid = valid && !this.invalidHandleIds[node.id];
17436
17437         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17438             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17439         }
17440
17441
17442         return valid;
17443
17444     },
17445
17446     /**
17447      * Create the array of horizontal tick marks if an interval was specified
17448      * in setXConstraint().
17449      * @method setXTicks
17450      * @private
17451      */
17452     setXTicks: function(iStartX, iTickSize) {
17453         this.xTicks = [];
17454         this.xTickSize = iTickSize;
17455
17456         var tickMap = {};
17457
17458         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17459             if (!tickMap[i]) {
17460                 this.xTicks[this.xTicks.length] = i;
17461                 tickMap[i] = true;
17462             }
17463         }
17464
17465         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17466             if (!tickMap[i]) {
17467                 this.xTicks[this.xTicks.length] = i;
17468                 tickMap[i] = true;
17469             }
17470         }
17471
17472         this.xTicks.sort(this.DDM.numericSort) ;
17473     },
17474
17475     /**
17476      * Create the array of vertical tick marks if an interval was specified in
17477      * setYConstraint().
17478      * @method setYTicks
17479      * @private
17480      */
17481     setYTicks: function(iStartY, iTickSize) {
17482         this.yTicks = [];
17483         this.yTickSize = iTickSize;
17484
17485         var tickMap = {};
17486
17487         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17488             if (!tickMap[i]) {
17489                 this.yTicks[this.yTicks.length] = i;
17490                 tickMap[i] = true;
17491             }
17492         }
17493
17494         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17495             if (!tickMap[i]) {
17496                 this.yTicks[this.yTicks.length] = i;
17497                 tickMap[i] = true;
17498             }
17499         }
17500
17501         this.yTicks.sort(this.DDM.numericSort) ;
17502     },
17503
17504     /**
17505      * By default, the element can be dragged any place on the screen.  Use
17506      * this method to limit the horizontal travel of the element.  Pass in
17507      * 0,0 for the parameters if you want to lock the drag to the y axis.
17508      * @method setXConstraint
17509      * @param {int} iLeft the number of pixels the element can move to the left
17510      * @param {int} iRight the number of pixels the element can move to the
17511      * right
17512      * @param {int} iTickSize optional parameter for specifying that the
17513      * element
17514      * should move iTickSize pixels at a time.
17515      */
17516     setXConstraint: function(iLeft, iRight, iTickSize) {
17517         this.leftConstraint = iLeft;
17518         this.rightConstraint = iRight;
17519
17520         this.minX = this.initPageX - iLeft;
17521         this.maxX = this.initPageX + iRight;
17522         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17523
17524         this.constrainX = true;
17525     },
17526
17527     /**
17528      * Clears any constraints applied to this instance.  Also clears ticks
17529      * since they can't exist independent of a constraint at this time.
17530      * @method clearConstraints
17531      */
17532     clearConstraints: function() {
17533         this.constrainX = false;
17534         this.constrainY = false;
17535         this.clearTicks();
17536     },
17537
17538     /**
17539      * Clears any tick interval defined for this instance
17540      * @method clearTicks
17541      */
17542     clearTicks: function() {
17543         this.xTicks = null;
17544         this.yTicks = null;
17545         this.xTickSize = 0;
17546         this.yTickSize = 0;
17547     },
17548
17549     /**
17550      * By default, the element can be dragged any place on the screen.  Set
17551      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17552      * parameters if you want to lock the drag to the x axis.
17553      * @method setYConstraint
17554      * @param {int} iUp the number of pixels the element can move up
17555      * @param {int} iDown the number of pixels the element can move down
17556      * @param {int} iTickSize optional parameter for specifying that the
17557      * element should move iTickSize pixels at a time.
17558      */
17559     setYConstraint: function(iUp, iDown, iTickSize) {
17560         this.topConstraint = iUp;
17561         this.bottomConstraint = iDown;
17562
17563         this.minY = this.initPageY - iUp;
17564         this.maxY = this.initPageY + iDown;
17565         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17566
17567         this.constrainY = true;
17568
17569     },
17570
17571     /**
17572      * resetConstraints must be called if you manually reposition a dd element.
17573      * @method resetConstraints
17574      * @param {boolean} maintainOffset
17575      */
17576     resetConstraints: function() {
17577
17578
17579         // Maintain offsets if necessary
17580         if (this.initPageX || this.initPageX === 0) {
17581             // figure out how much this thing has moved
17582             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17583             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17584
17585             this.setInitPosition(dx, dy);
17586
17587         // This is the first time we have detected the element's position
17588         } else {
17589             this.setInitPosition();
17590         }
17591
17592         if (this.constrainX) {
17593             this.setXConstraint( this.leftConstraint,
17594                                  this.rightConstraint,
17595                                  this.xTickSize        );
17596         }
17597
17598         if (this.constrainY) {
17599             this.setYConstraint( this.topConstraint,
17600                                  this.bottomConstraint,
17601                                  this.yTickSize         );
17602         }
17603     },
17604
17605     /**
17606      * Normally the drag element is moved pixel by pixel, but we can specify
17607      * that it move a number of pixels at a time.  This method resolves the
17608      * location when we have it set up like this.
17609      * @method getTick
17610      * @param {int} val where we want to place the object
17611      * @param {int[]} tickArray sorted array of valid points
17612      * @return {int} the closest tick
17613      * @private
17614      */
17615     getTick: function(val, tickArray) {
17616
17617         if (!tickArray) {
17618             // If tick interval is not defined, it is effectively 1 pixel,
17619             // so we return the value passed to us.
17620             return val;
17621         } else if (tickArray[0] >= val) {
17622             // The value is lower than the first tick, so we return the first
17623             // tick.
17624             return tickArray[0];
17625         } else {
17626             for (var i=0, len=tickArray.length; i<len; ++i) {
17627                 var next = i + 1;
17628                 if (tickArray[next] && tickArray[next] >= val) {
17629                     var diff1 = val - tickArray[i];
17630                     var diff2 = tickArray[next] - val;
17631                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17632                 }
17633             }
17634
17635             // The value is larger than the last tick, so we return the last
17636             // tick.
17637             return tickArray[tickArray.length - 1];
17638         }
17639     },
17640
17641     /**
17642      * toString method
17643      * @method toString
17644      * @return {string} string representation of the dd obj
17645      */
17646     toString: function() {
17647         return ("DragDrop " + this.id);
17648     }
17649
17650 });
17651
17652 })();
17653 /*
17654  * Based on:
17655  * Ext JS Library 1.1.1
17656  * Copyright(c) 2006-2007, Ext JS, LLC.
17657  *
17658  * Originally Released Under LGPL - original licence link has changed is not relivant.
17659  *
17660  * Fork - LGPL
17661  * <script type="text/javascript">
17662  */
17663
17664
17665 /**
17666  * The drag and drop utility provides a framework for building drag and drop
17667  * applications.  In addition to enabling drag and drop for specific elements,
17668  * the drag and drop elements are tracked by the manager class, and the
17669  * interactions between the various elements are tracked during the drag and
17670  * the implementing code is notified about these important moments.
17671  */
17672
17673 // Only load the library once.  Rewriting the manager class would orphan
17674 // existing drag and drop instances.
17675 if (!Roo.dd.DragDropMgr) {
17676
17677 /**
17678  * @class Roo.dd.DragDropMgr
17679  * DragDropMgr is a singleton that tracks the element interaction for
17680  * all DragDrop items in the window.  Generally, you will not call
17681  * this class directly, but it does have helper methods that could
17682  * be useful in your DragDrop implementations.
17683  * @singleton
17684  */
17685 Roo.dd.DragDropMgr = function() {
17686
17687     var Event = Roo.EventManager;
17688
17689     return {
17690
17691         /**
17692          * Two dimensional Array of registered DragDrop objects.  The first
17693          * dimension is the DragDrop item group, the second the DragDrop
17694          * object.
17695          * @property ids
17696          * @type {string: string}
17697          * @private
17698          * @static
17699          */
17700         ids: {},
17701
17702         /**
17703          * Array of element ids defined as drag handles.  Used to determine
17704          * if the element that generated the mousedown event is actually the
17705          * handle and not the html element itself.
17706          * @property handleIds
17707          * @type {string: string}
17708          * @private
17709          * @static
17710          */
17711         handleIds: {},
17712
17713         /**
17714          * the DragDrop object that is currently being dragged
17715          * @property dragCurrent
17716          * @type DragDrop
17717          * @private
17718          * @static
17719          **/
17720         dragCurrent: null,
17721
17722         /**
17723          * the DragDrop object(s) that are being hovered over
17724          * @property dragOvers
17725          * @type Array
17726          * @private
17727          * @static
17728          */
17729         dragOvers: {},
17730
17731         /**
17732          * the X distance between the cursor and the object being dragged
17733          * @property deltaX
17734          * @type int
17735          * @private
17736          * @static
17737          */
17738         deltaX: 0,
17739
17740         /**
17741          * the Y distance between the cursor and the object being dragged
17742          * @property deltaY
17743          * @type int
17744          * @private
17745          * @static
17746          */
17747         deltaY: 0,
17748
17749         /**
17750          * Flag to determine if we should prevent the default behavior of the
17751          * events we define. By default this is true, but this can be set to
17752          * false if you need the default behavior (not recommended)
17753          * @property preventDefault
17754          * @type boolean
17755          * @static
17756          */
17757         preventDefault: true,
17758
17759         /**
17760          * Flag to determine if we should stop the propagation of the events
17761          * we generate. This is true by default but you may want to set it to
17762          * false if the html element contains other features that require the
17763          * mouse click.
17764          * @property stopPropagation
17765          * @type boolean
17766          * @static
17767          */
17768         stopPropagation: true,
17769
17770         /**
17771          * Internal flag that is set to true when drag and drop has been
17772          * intialized
17773          * @property initialized
17774          * @private
17775          * @static
17776          */
17777         initalized: false,
17778
17779         /**
17780          * All drag and drop can be disabled.
17781          * @property locked
17782          * @private
17783          * @static
17784          */
17785         locked: false,
17786
17787         /**
17788          * Called the first time an element is registered.
17789          * @method init
17790          * @private
17791          * @static
17792          */
17793         init: function() {
17794             this.initialized = true;
17795         },
17796
17797         /**
17798          * In point mode, drag and drop interaction is defined by the
17799          * location of the cursor during the drag/drop
17800          * @property POINT
17801          * @type int
17802          * @static
17803          */
17804         POINT: 0,
17805
17806         /**
17807          * In intersect mode, drag and drop interactio nis defined by the
17808          * overlap of two or more drag and drop objects.
17809          * @property INTERSECT
17810          * @type int
17811          * @static
17812          */
17813         INTERSECT: 1,
17814
17815         /**
17816          * The current drag and drop mode.  Default: POINT
17817          * @property mode
17818          * @type int
17819          * @static
17820          */
17821         mode: 0,
17822
17823         /**
17824          * Runs method on all drag and drop objects
17825          * @method _execOnAll
17826          * @private
17827          * @static
17828          */
17829         _execOnAll: function(sMethod, args) {
17830             for (var i in this.ids) {
17831                 for (var j in this.ids[i]) {
17832                     var oDD = this.ids[i][j];
17833                     if (! this.isTypeOfDD(oDD)) {
17834                         continue;
17835                     }
17836                     oDD[sMethod].apply(oDD, args);
17837                 }
17838             }
17839         },
17840
17841         /**
17842          * Drag and drop initialization.  Sets up the global event handlers
17843          * @method _onLoad
17844          * @private
17845          * @static
17846          */
17847         _onLoad: function() {
17848
17849             this.init();
17850
17851             if (!Roo.isTouch) {
17852                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17853                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17854             }
17855             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17856             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17857             
17858             Event.on(window,   "unload",    this._onUnload, this, true);
17859             Event.on(window,   "resize",    this._onResize, this, true);
17860             // Event.on(window,   "mouseout",    this._test);
17861
17862         },
17863
17864         /**
17865          * Reset constraints on all drag and drop objs
17866          * @method _onResize
17867          * @private
17868          * @static
17869          */
17870         _onResize: function(e) {
17871             this._execOnAll("resetConstraints", []);
17872         },
17873
17874         /**
17875          * Lock all drag and drop functionality
17876          * @method lock
17877          * @static
17878          */
17879         lock: function() { this.locked = true; },
17880
17881         /**
17882          * Unlock all drag and drop functionality
17883          * @method unlock
17884          * @static
17885          */
17886         unlock: function() { this.locked = false; },
17887
17888         /**
17889          * Is drag and drop locked?
17890          * @method isLocked
17891          * @return {boolean} True if drag and drop is locked, false otherwise.
17892          * @static
17893          */
17894         isLocked: function() { return this.locked; },
17895
17896         /**
17897          * Location cache that is set for all drag drop objects when a drag is
17898          * initiated, cleared when the drag is finished.
17899          * @property locationCache
17900          * @private
17901          * @static
17902          */
17903         locationCache: {},
17904
17905         /**
17906          * Set useCache to false if you want to force object the lookup of each
17907          * drag and drop linked element constantly during a drag.
17908          * @property useCache
17909          * @type boolean
17910          * @static
17911          */
17912         useCache: true,
17913
17914         /**
17915          * The number of pixels that the mouse needs to move after the
17916          * mousedown before the drag is initiated.  Default=3;
17917          * @property clickPixelThresh
17918          * @type int
17919          * @static
17920          */
17921         clickPixelThresh: 3,
17922
17923         /**
17924          * The number of milliseconds after the mousedown event to initiate the
17925          * drag if we don't get a mouseup event. Default=1000
17926          * @property clickTimeThresh
17927          * @type int
17928          * @static
17929          */
17930         clickTimeThresh: 350,
17931
17932         /**
17933          * Flag that indicates that either the drag pixel threshold or the
17934          * mousdown time threshold has been met
17935          * @property dragThreshMet
17936          * @type boolean
17937          * @private
17938          * @static
17939          */
17940         dragThreshMet: false,
17941
17942         /**
17943          * Timeout used for the click time threshold
17944          * @property clickTimeout
17945          * @type Object
17946          * @private
17947          * @static
17948          */
17949         clickTimeout: null,
17950
17951         /**
17952          * The X position of the mousedown event stored for later use when a
17953          * drag threshold is met.
17954          * @property startX
17955          * @type int
17956          * @private
17957          * @static
17958          */
17959         startX: 0,
17960
17961         /**
17962          * The Y position of the mousedown event stored for later use when a
17963          * drag threshold is met.
17964          * @property startY
17965          * @type int
17966          * @private
17967          * @static
17968          */
17969         startY: 0,
17970
17971         /**
17972          * Each DragDrop instance must be registered with the DragDropMgr.
17973          * This is executed in DragDrop.init()
17974          * @method regDragDrop
17975          * @param {DragDrop} oDD the DragDrop object to register
17976          * @param {String} sGroup the name of the group this element belongs to
17977          * @static
17978          */
17979         regDragDrop: function(oDD, sGroup) {
17980             if (!this.initialized) { this.init(); }
17981
17982             if (!this.ids[sGroup]) {
17983                 this.ids[sGroup] = {};
17984             }
17985             this.ids[sGroup][oDD.id] = oDD;
17986         },
17987
17988         /**
17989          * Removes the supplied dd instance from the supplied group. Executed
17990          * by DragDrop.removeFromGroup, so don't call this function directly.
17991          * @method removeDDFromGroup
17992          * @private
17993          * @static
17994          */
17995         removeDDFromGroup: function(oDD, sGroup) {
17996             if (!this.ids[sGroup]) {
17997                 this.ids[sGroup] = {};
17998             }
17999
18000             var obj = this.ids[sGroup];
18001             if (obj && obj[oDD.id]) {
18002                 delete obj[oDD.id];
18003             }
18004         },
18005
18006         /**
18007          * Unregisters a drag and drop item.  This is executed in
18008          * DragDrop.unreg, use that method instead of calling this directly.
18009          * @method _remove
18010          * @private
18011          * @static
18012          */
18013         _remove: function(oDD) {
18014             for (var g in oDD.groups) {
18015                 if (g && this.ids[g][oDD.id]) {
18016                     delete this.ids[g][oDD.id];
18017                 }
18018             }
18019             delete this.handleIds[oDD.id];
18020         },
18021
18022         /**
18023          * Each DragDrop handle element must be registered.  This is done
18024          * automatically when executing DragDrop.setHandleElId()
18025          * @method regHandle
18026          * @param {String} sDDId the DragDrop id this element is a handle for
18027          * @param {String} sHandleId the id of the element that is the drag
18028          * handle
18029          * @static
18030          */
18031         regHandle: function(sDDId, sHandleId) {
18032             if (!this.handleIds[sDDId]) {
18033                 this.handleIds[sDDId] = {};
18034             }
18035             this.handleIds[sDDId][sHandleId] = sHandleId;
18036         },
18037
18038         /**
18039          * Utility function to determine if a given element has been
18040          * registered as a drag drop item.
18041          * @method isDragDrop
18042          * @param {String} id the element id to check
18043          * @return {boolean} true if this element is a DragDrop item,
18044          * false otherwise
18045          * @static
18046          */
18047         isDragDrop: function(id) {
18048             return ( this.getDDById(id) ) ? true : false;
18049         },
18050
18051         /**
18052          * Returns the drag and drop instances that are in all groups the
18053          * passed in instance belongs to.
18054          * @method getRelated
18055          * @param {DragDrop} p_oDD the obj to get related data for
18056          * @param {boolean} bTargetsOnly if true, only return targetable objs
18057          * @return {DragDrop[]} the related instances
18058          * @static
18059          */
18060         getRelated: function(p_oDD, bTargetsOnly) {
18061             var oDDs = [];
18062             for (var i in p_oDD.groups) {
18063                 for (j in this.ids[i]) {
18064                     var dd = this.ids[i][j];
18065                     if (! this.isTypeOfDD(dd)) {
18066                         continue;
18067                     }
18068                     if (!bTargetsOnly || dd.isTarget) {
18069                         oDDs[oDDs.length] = dd;
18070                     }
18071                 }
18072             }
18073
18074             return oDDs;
18075         },
18076
18077         /**
18078          * Returns true if the specified dd target is a legal target for
18079          * the specifice drag obj
18080          * @method isLegalTarget
18081          * @param {DragDrop} the drag obj
18082          * @param {DragDrop} the target
18083          * @return {boolean} true if the target is a legal target for the
18084          * dd obj
18085          * @static
18086          */
18087         isLegalTarget: function (oDD, oTargetDD) {
18088             var targets = this.getRelated(oDD, true);
18089             for (var i=0, len=targets.length;i<len;++i) {
18090                 if (targets[i].id == oTargetDD.id) {
18091                     return true;
18092                 }
18093             }
18094
18095             return false;
18096         },
18097
18098         /**
18099          * My goal is to be able to transparently determine if an object is
18100          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18101          * returns "object", oDD.constructor.toString() always returns
18102          * "DragDrop" and not the name of the subclass.  So for now it just
18103          * evaluates a well-known variable in DragDrop.
18104          * @method isTypeOfDD
18105          * @param {Object} the object to evaluate
18106          * @return {boolean} true if typeof oDD = DragDrop
18107          * @static
18108          */
18109         isTypeOfDD: function (oDD) {
18110             return (oDD && oDD.__ygDragDrop);
18111         },
18112
18113         /**
18114          * Utility function to determine if a given element has been
18115          * registered as a drag drop handle for the given Drag Drop object.
18116          * @method isHandle
18117          * @param {String} id the element id to check
18118          * @return {boolean} true if this element is a DragDrop handle, false
18119          * otherwise
18120          * @static
18121          */
18122         isHandle: function(sDDId, sHandleId) {
18123             return ( this.handleIds[sDDId] &&
18124                             this.handleIds[sDDId][sHandleId] );
18125         },
18126
18127         /**
18128          * Returns the DragDrop instance for a given id
18129          * @method getDDById
18130          * @param {String} id the id of the DragDrop object
18131          * @return {DragDrop} the drag drop object, null if it is not found
18132          * @static
18133          */
18134         getDDById: function(id) {
18135             for (var i in this.ids) {
18136                 if (this.ids[i][id]) {
18137                     return this.ids[i][id];
18138                 }
18139             }
18140             return null;
18141         },
18142
18143         /**
18144          * Fired after a registered DragDrop object gets the mousedown event.
18145          * Sets up the events required to track the object being dragged
18146          * @method handleMouseDown
18147          * @param {Event} e the event
18148          * @param oDD the DragDrop object being dragged
18149          * @private
18150          * @static
18151          */
18152         handleMouseDown: function(e, oDD) {
18153             if(Roo.QuickTips){
18154                 Roo.QuickTips.disable();
18155             }
18156             this.currentTarget = e.getTarget();
18157
18158             this.dragCurrent = oDD;
18159
18160             var el = oDD.getEl();
18161
18162             // track start position
18163             this.startX = e.getPageX();
18164             this.startY = e.getPageY();
18165
18166             this.deltaX = this.startX - el.offsetLeft;
18167             this.deltaY = this.startY - el.offsetTop;
18168
18169             this.dragThreshMet = false;
18170
18171             this.clickTimeout = setTimeout(
18172                     function() {
18173                         var DDM = Roo.dd.DDM;
18174                         DDM.startDrag(DDM.startX, DDM.startY);
18175                     },
18176                     this.clickTimeThresh );
18177         },
18178
18179         /**
18180          * Fired when either the drag pixel threshol or the mousedown hold
18181          * time threshold has been met.
18182          * @method startDrag
18183          * @param x {int} the X position of the original mousedown
18184          * @param y {int} the Y position of the original mousedown
18185          * @static
18186          */
18187         startDrag: function(x, y) {
18188             clearTimeout(this.clickTimeout);
18189             if (this.dragCurrent) {
18190                 this.dragCurrent.b4StartDrag(x, y);
18191                 this.dragCurrent.startDrag(x, y);
18192             }
18193             this.dragThreshMet = true;
18194         },
18195
18196         /**
18197          * Internal function to handle the mouseup event.  Will be invoked
18198          * from the context of the document.
18199          * @method handleMouseUp
18200          * @param {Event} e the event
18201          * @private
18202          * @static
18203          */
18204         handleMouseUp: function(e) {
18205
18206             if(Roo.QuickTips){
18207                 Roo.QuickTips.enable();
18208             }
18209             if (! this.dragCurrent) {
18210                 return;
18211             }
18212
18213             clearTimeout(this.clickTimeout);
18214
18215             if (this.dragThreshMet) {
18216                 this.fireEvents(e, true);
18217             } else {
18218             }
18219
18220             this.stopDrag(e);
18221
18222             this.stopEvent(e);
18223         },
18224
18225         /**
18226          * Utility to stop event propagation and event default, if these
18227          * features are turned on.
18228          * @method stopEvent
18229          * @param {Event} e the event as returned by this.getEvent()
18230          * @static
18231          */
18232         stopEvent: function(e){
18233             if(this.stopPropagation) {
18234                 e.stopPropagation();
18235             }
18236
18237             if (this.preventDefault) {
18238                 e.preventDefault();
18239             }
18240         },
18241
18242         /**
18243          * Internal function to clean up event handlers after the drag
18244          * operation is complete
18245          * @method stopDrag
18246          * @param {Event} e the event
18247          * @private
18248          * @static
18249          */
18250         stopDrag: function(e) {
18251             // Fire the drag end event for the item that was dragged
18252             if (this.dragCurrent) {
18253                 if (this.dragThreshMet) {
18254                     this.dragCurrent.b4EndDrag(e);
18255                     this.dragCurrent.endDrag(e);
18256                 }
18257
18258                 this.dragCurrent.onMouseUp(e);
18259             }
18260
18261             this.dragCurrent = null;
18262             this.dragOvers = {};
18263         },
18264
18265         /**
18266          * Internal function to handle the mousemove event.  Will be invoked
18267          * from the context of the html element.
18268          *
18269          * @TODO figure out what we can do about mouse events lost when the
18270          * user drags objects beyond the window boundary.  Currently we can
18271          * detect this in internet explorer by verifying that the mouse is
18272          * down during the mousemove event.  Firefox doesn't give us the
18273          * button state on the mousemove event.
18274          * @method handleMouseMove
18275          * @param {Event} e the event
18276          * @private
18277          * @static
18278          */
18279         handleMouseMove: function(e) {
18280             if (! this.dragCurrent) {
18281                 return true;
18282             }
18283
18284             // var button = e.which || e.button;
18285
18286             // check for IE mouseup outside of page boundary
18287             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18288                 this.stopEvent(e);
18289                 return this.handleMouseUp(e);
18290             }
18291
18292             if (!this.dragThreshMet) {
18293                 var diffX = Math.abs(this.startX - e.getPageX());
18294                 var diffY = Math.abs(this.startY - e.getPageY());
18295                 if (diffX > this.clickPixelThresh ||
18296                             diffY > this.clickPixelThresh) {
18297                     this.startDrag(this.startX, this.startY);
18298                 }
18299             }
18300
18301             if (this.dragThreshMet) {
18302                 this.dragCurrent.b4Drag(e);
18303                 this.dragCurrent.onDrag(e);
18304                 if(!this.dragCurrent.moveOnly){
18305                     this.fireEvents(e, false);
18306                 }
18307             }
18308
18309             this.stopEvent(e);
18310
18311             return true;
18312         },
18313
18314         /**
18315          * Iterates over all of the DragDrop elements to find ones we are
18316          * hovering over or dropping on
18317          * @method fireEvents
18318          * @param {Event} e the event
18319          * @param {boolean} isDrop is this a drop op or a mouseover op?
18320          * @private
18321          * @static
18322          */
18323         fireEvents: function(e, isDrop) {
18324             var dc = this.dragCurrent;
18325
18326             // If the user did the mouse up outside of the window, we could
18327             // get here even though we have ended the drag.
18328             if (!dc || dc.isLocked()) {
18329                 return;
18330             }
18331
18332             var pt = e.getPoint();
18333
18334             // cache the previous dragOver array
18335             var oldOvers = [];
18336
18337             var outEvts   = [];
18338             var overEvts  = [];
18339             var dropEvts  = [];
18340             var enterEvts = [];
18341
18342             // Check to see if the object(s) we were hovering over is no longer
18343             // being hovered over so we can fire the onDragOut event
18344             for (var i in this.dragOvers) {
18345
18346                 var ddo = this.dragOvers[i];
18347
18348                 if (! this.isTypeOfDD(ddo)) {
18349                     continue;
18350                 }
18351
18352                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18353                     outEvts.push( ddo );
18354                 }
18355
18356                 oldOvers[i] = true;
18357                 delete this.dragOvers[i];
18358             }
18359
18360             for (var sGroup in dc.groups) {
18361
18362                 if ("string" != typeof sGroup) {
18363                     continue;
18364                 }
18365
18366                 for (i in this.ids[sGroup]) {
18367                     var oDD = this.ids[sGroup][i];
18368                     if (! this.isTypeOfDD(oDD)) {
18369                         continue;
18370                     }
18371
18372                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18373                         if (this.isOverTarget(pt, oDD, this.mode)) {
18374                             // look for drop interactions
18375                             if (isDrop) {
18376                                 dropEvts.push( oDD );
18377                             // look for drag enter and drag over interactions
18378                             } else {
18379
18380                                 // initial drag over: dragEnter fires
18381                                 if (!oldOvers[oDD.id]) {
18382                                     enterEvts.push( oDD );
18383                                 // subsequent drag overs: dragOver fires
18384                                 } else {
18385                                     overEvts.push( oDD );
18386                                 }
18387
18388                                 this.dragOvers[oDD.id] = oDD;
18389                             }
18390                         }
18391                     }
18392                 }
18393             }
18394
18395             if (this.mode) {
18396                 if (outEvts.length) {
18397                     dc.b4DragOut(e, outEvts);
18398                     dc.onDragOut(e, outEvts);
18399                 }
18400
18401                 if (enterEvts.length) {
18402                     dc.onDragEnter(e, enterEvts);
18403                 }
18404
18405                 if (overEvts.length) {
18406                     dc.b4DragOver(e, overEvts);
18407                     dc.onDragOver(e, overEvts);
18408                 }
18409
18410                 if (dropEvts.length) {
18411                     dc.b4DragDrop(e, dropEvts);
18412                     dc.onDragDrop(e, dropEvts);
18413                 }
18414
18415             } else {
18416                 // fire dragout events
18417                 var len = 0;
18418                 for (i=0, len=outEvts.length; i<len; ++i) {
18419                     dc.b4DragOut(e, outEvts[i].id);
18420                     dc.onDragOut(e, outEvts[i].id);
18421                 }
18422
18423                 // fire enter events
18424                 for (i=0,len=enterEvts.length; i<len; ++i) {
18425                     // dc.b4DragEnter(e, oDD.id);
18426                     dc.onDragEnter(e, enterEvts[i].id);
18427                 }
18428
18429                 // fire over events
18430                 for (i=0,len=overEvts.length; i<len; ++i) {
18431                     dc.b4DragOver(e, overEvts[i].id);
18432                     dc.onDragOver(e, overEvts[i].id);
18433                 }
18434
18435                 // fire drop events
18436                 for (i=0, len=dropEvts.length; i<len; ++i) {
18437                     dc.b4DragDrop(e, dropEvts[i].id);
18438                     dc.onDragDrop(e, dropEvts[i].id);
18439                 }
18440
18441             }
18442
18443             // notify about a drop that did not find a target
18444             if (isDrop && !dropEvts.length) {
18445                 dc.onInvalidDrop(e);
18446             }
18447
18448         },
18449
18450         /**
18451          * Helper function for getting the best match from the list of drag
18452          * and drop objects returned by the drag and drop events when we are
18453          * in INTERSECT mode.  It returns either the first object that the
18454          * cursor is over, or the object that has the greatest overlap with
18455          * the dragged element.
18456          * @method getBestMatch
18457          * @param  {DragDrop[]} dds The array of drag and drop objects
18458          * targeted
18459          * @return {DragDrop}       The best single match
18460          * @static
18461          */
18462         getBestMatch: function(dds) {
18463             var winner = null;
18464             // Return null if the input is not what we expect
18465             //if (!dds || !dds.length || dds.length == 0) {
18466                // winner = null;
18467             // If there is only one item, it wins
18468             //} else if (dds.length == 1) {
18469
18470             var len = dds.length;
18471
18472             if (len == 1) {
18473                 winner = dds[0];
18474             } else {
18475                 // Loop through the targeted items
18476                 for (var i=0; i<len; ++i) {
18477                     var dd = dds[i];
18478                     // If the cursor is over the object, it wins.  If the
18479                     // cursor is over multiple matches, the first one we come
18480                     // to wins.
18481                     if (dd.cursorIsOver) {
18482                         winner = dd;
18483                         break;
18484                     // Otherwise the object with the most overlap wins
18485                     } else {
18486                         if (!winner ||
18487                             winner.overlap.getArea() < dd.overlap.getArea()) {
18488                             winner = dd;
18489                         }
18490                     }
18491                 }
18492             }
18493
18494             return winner;
18495         },
18496
18497         /**
18498          * Refreshes the cache of the top-left and bottom-right points of the
18499          * drag and drop objects in the specified group(s).  This is in the
18500          * format that is stored in the drag and drop instance, so typical
18501          * usage is:
18502          * <code>
18503          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18504          * </code>
18505          * Alternatively:
18506          * <code>
18507          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18508          * </code>
18509          * @TODO this really should be an indexed array.  Alternatively this
18510          * method could accept both.
18511          * @method refreshCache
18512          * @param {Object} groups an associative array of groups to refresh
18513          * @static
18514          */
18515         refreshCache: function(groups) {
18516             for (var sGroup in groups) {
18517                 if ("string" != typeof sGroup) {
18518                     continue;
18519                 }
18520                 for (var i in this.ids[sGroup]) {
18521                     var oDD = this.ids[sGroup][i];
18522
18523                     if (this.isTypeOfDD(oDD)) {
18524                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18525                         var loc = this.getLocation(oDD);
18526                         if (loc) {
18527                             this.locationCache[oDD.id] = loc;
18528                         } else {
18529                             delete this.locationCache[oDD.id];
18530                             // this will unregister the drag and drop object if
18531                             // the element is not in a usable state
18532                             // oDD.unreg();
18533                         }
18534                     }
18535                 }
18536             }
18537         },
18538
18539         /**
18540          * This checks to make sure an element exists and is in the DOM.  The
18541          * main purpose is to handle cases where innerHTML is used to remove
18542          * drag and drop objects from the DOM.  IE provides an 'unspecified
18543          * error' when trying to access the offsetParent of such an element
18544          * @method verifyEl
18545          * @param {HTMLElement} el the element to check
18546          * @return {boolean} true if the element looks usable
18547          * @static
18548          */
18549         verifyEl: function(el) {
18550             if (el) {
18551                 var parent;
18552                 if(Roo.isIE){
18553                     try{
18554                         parent = el.offsetParent;
18555                     }catch(e){}
18556                 }else{
18557                     parent = el.offsetParent;
18558                 }
18559                 if (parent) {
18560                     return true;
18561                 }
18562             }
18563
18564             return false;
18565         },
18566
18567         /**
18568          * Returns a Region object containing the drag and drop element's position
18569          * and size, including the padding configured for it
18570          * @method getLocation
18571          * @param {DragDrop} oDD the drag and drop object to get the
18572          *                       location for
18573          * @return {Roo.lib.Region} a Region object representing the total area
18574          *                             the element occupies, including any padding
18575          *                             the instance is configured for.
18576          * @static
18577          */
18578         getLocation: function(oDD) {
18579             if (! this.isTypeOfDD(oDD)) {
18580                 return null;
18581             }
18582
18583             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18584
18585             try {
18586                 pos= Roo.lib.Dom.getXY(el);
18587             } catch (e) { }
18588
18589             if (!pos) {
18590                 return null;
18591             }
18592
18593             x1 = pos[0];
18594             x2 = x1 + el.offsetWidth;
18595             y1 = pos[1];
18596             y2 = y1 + el.offsetHeight;
18597
18598             t = y1 - oDD.padding[0];
18599             r = x2 + oDD.padding[1];
18600             b = y2 + oDD.padding[2];
18601             l = x1 - oDD.padding[3];
18602
18603             return new Roo.lib.Region( t, r, b, l );
18604         },
18605
18606         /**
18607          * Checks the cursor location to see if it over the target
18608          * @method isOverTarget
18609          * @param {Roo.lib.Point} pt The point to evaluate
18610          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18611          * @return {boolean} true if the mouse is over the target
18612          * @private
18613          * @static
18614          */
18615         isOverTarget: function(pt, oTarget, intersect) {
18616             // use cache if available
18617             var loc = this.locationCache[oTarget.id];
18618             if (!loc || !this.useCache) {
18619                 loc = this.getLocation(oTarget);
18620                 this.locationCache[oTarget.id] = loc;
18621
18622             }
18623
18624             if (!loc) {
18625                 return false;
18626             }
18627
18628             oTarget.cursorIsOver = loc.contains( pt );
18629
18630             // DragDrop is using this as a sanity check for the initial mousedown
18631             // in this case we are done.  In POINT mode, if the drag obj has no
18632             // contraints, we are also done. Otherwise we need to evaluate the
18633             // location of the target as related to the actual location of the
18634             // dragged element.
18635             var dc = this.dragCurrent;
18636             if (!dc || !dc.getTargetCoord ||
18637                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18638                 return oTarget.cursorIsOver;
18639             }
18640
18641             oTarget.overlap = null;
18642
18643             // Get the current location of the drag element, this is the
18644             // location of the mouse event less the delta that represents
18645             // where the original mousedown happened on the element.  We
18646             // need to consider constraints and ticks as well.
18647             var pos = dc.getTargetCoord(pt.x, pt.y);
18648
18649             var el = dc.getDragEl();
18650             var curRegion = new Roo.lib.Region( pos.y,
18651                                                    pos.x + el.offsetWidth,
18652                                                    pos.y + el.offsetHeight,
18653                                                    pos.x );
18654
18655             var overlap = curRegion.intersect(loc);
18656
18657             if (overlap) {
18658                 oTarget.overlap = overlap;
18659                 return (intersect) ? true : oTarget.cursorIsOver;
18660             } else {
18661                 return false;
18662             }
18663         },
18664
18665         /**
18666          * unload event handler
18667          * @method _onUnload
18668          * @private
18669          * @static
18670          */
18671         _onUnload: function(e, me) {
18672             Roo.dd.DragDropMgr.unregAll();
18673         },
18674
18675         /**
18676          * Cleans up the drag and drop events and objects.
18677          * @method unregAll
18678          * @private
18679          * @static
18680          */
18681         unregAll: function() {
18682
18683             if (this.dragCurrent) {
18684                 this.stopDrag();
18685                 this.dragCurrent = null;
18686             }
18687
18688             this._execOnAll("unreg", []);
18689
18690             for (i in this.elementCache) {
18691                 delete this.elementCache[i];
18692             }
18693
18694             this.elementCache = {};
18695             this.ids = {};
18696         },
18697
18698         /**
18699          * A cache of DOM elements
18700          * @property elementCache
18701          * @private
18702          * @static
18703          */
18704         elementCache: {},
18705
18706         /**
18707          * Get the wrapper for the DOM element specified
18708          * @method getElWrapper
18709          * @param {String} id the id of the element to get
18710          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18711          * @private
18712          * @deprecated This wrapper isn't that useful
18713          * @static
18714          */
18715         getElWrapper: function(id) {
18716             var oWrapper = this.elementCache[id];
18717             if (!oWrapper || !oWrapper.el) {
18718                 oWrapper = this.elementCache[id] =
18719                     new this.ElementWrapper(Roo.getDom(id));
18720             }
18721             return oWrapper;
18722         },
18723
18724         /**
18725          * Returns the actual DOM element
18726          * @method getElement
18727          * @param {String} id the id of the elment to get
18728          * @return {Object} The element
18729          * @deprecated use Roo.getDom instead
18730          * @static
18731          */
18732         getElement: function(id) {
18733             return Roo.getDom(id);
18734         },
18735
18736         /**
18737          * Returns the style property for the DOM element (i.e.,
18738          * document.getElById(id).style)
18739          * @method getCss
18740          * @param {String} id the id of the elment to get
18741          * @return {Object} The style property of the element
18742          * @deprecated use Roo.getDom instead
18743          * @static
18744          */
18745         getCss: function(id) {
18746             var el = Roo.getDom(id);
18747             return (el) ? el.style : null;
18748         },
18749
18750         /**
18751          * Inner class for cached elements
18752          * @class DragDropMgr.ElementWrapper
18753          * @for DragDropMgr
18754          * @private
18755          * @deprecated
18756          */
18757         ElementWrapper: function(el) {
18758                 /**
18759                  * The element
18760                  * @property el
18761                  */
18762                 this.el = el || null;
18763                 /**
18764                  * The element id
18765                  * @property id
18766                  */
18767                 this.id = this.el && el.id;
18768                 /**
18769                  * A reference to the style property
18770                  * @property css
18771                  */
18772                 this.css = this.el && el.style;
18773             },
18774
18775         /**
18776          * Returns the X position of an html element
18777          * @method getPosX
18778          * @param el the element for which to get the position
18779          * @return {int} the X coordinate
18780          * @for DragDropMgr
18781          * @deprecated use Roo.lib.Dom.getX instead
18782          * @static
18783          */
18784         getPosX: function(el) {
18785             return Roo.lib.Dom.getX(el);
18786         },
18787
18788         /**
18789          * Returns the Y position of an html element
18790          * @method getPosY
18791          * @param el the element for which to get the position
18792          * @return {int} the Y coordinate
18793          * @deprecated use Roo.lib.Dom.getY instead
18794          * @static
18795          */
18796         getPosY: function(el) {
18797             return Roo.lib.Dom.getY(el);
18798         },
18799
18800         /**
18801          * Swap two nodes.  In IE, we use the native method, for others we
18802          * emulate the IE behavior
18803          * @method swapNode
18804          * @param n1 the first node to swap
18805          * @param n2 the other node to swap
18806          * @static
18807          */
18808         swapNode: function(n1, n2) {
18809             if (n1.swapNode) {
18810                 n1.swapNode(n2);
18811             } else {
18812                 var p = n2.parentNode;
18813                 var s = n2.nextSibling;
18814
18815                 if (s == n1) {
18816                     p.insertBefore(n1, n2);
18817                 } else if (n2 == n1.nextSibling) {
18818                     p.insertBefore(n2, n1);
18819                 } else {
18820                     n1.parentNode.replaceChild(n2, n1);
18821                     p.insertBefore(n1, s);
18822                 }
18823             }
18824         },
18825
18826         /**
18827          * Returns the current scroll position
18828          * @method getScroll
18829          * @private
18830          * @static
18831          */
18832         getScroll: function () {
18833             var t, l, dde=document.documentElement, db=document.body;
18834             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18835                 t = dde.scrollTop;
18836                 l = dde.scrollLeft;
18837             } else if (db) {
18838                 t = db.scrollTop;
18839                 l = db.scrollLeft;
18840             } else {
18841
18842             }
18843             return { top: t, left: l };
18844         },
18845
18846         /**
18847          * Returns the specified element style property
18848          * @method getStyle
18849          * @param {HTMLElement} el          the element
18850          * @param {string}      styleProp   the style property
18851          * @return {string} The value of the style property
18852          * @deprecated use Roo.lib.Dom.getStyle
18853          * @static
18854          */
18855         getStyle: function(el, styleProp) {
18856             return Roo.fly(el).getStyle(styleProp);
18857         },
18858
18859         /**
18860          * Gets the scrollTop
18861          * @method getScrollTop
18862          * @return {int} the document's scrollTop
18863          * @static
18864          */
18865         getScrollTop: function () { return this.getScroll().top; },
18866
18867         /**
18868          * Gets the scrollLeft
18869          * @method getScrollLeft
18870          * @return {int} the document's scrollTop
18871          * @static
18872          */
18873         getScrollLeft: function () { return this.getScroll().left; },
18874
18875         /**
18876          * Sets the x/y position of an element to the location of the
18877          * target element.
18878          * @method moveToEl
18879          * @param {HTMLElement} moveEl      The element to move
18880          * @param {HTMLElement} targetEl    The position reference element
18881          * @static
18882          */
18883         moveToEl: function (moveEl, targetEl) {
18884             var aCoord = Roo.lib.Dom.getXY(targetEl);
18885             Roo.lib.Dom.setXY(moveEl, aCoord);
18886         },
18887
18888         /**
18889          * Numeric array sort function
18890          * @method numericSort
18891          * @static
18892          */
18893         numericSort: function(a, b) { return (a - b); },
18894
18895         /**
18896          * Internal counter
18897          * @property _timeoutCount
18898          * @private
18899          * @static
18900          */
18901         _timeoutCount: 0,
18902
18903         /**
18904          * Trying to make the load order less important.  Without this we get
18905          * an error if this file is loaded before the Event Utility.
18906          * @method _addListeners
18907          * @private
18908          * @static
18909          */
18910         _addListeners: function() {
18911             var DDM = Roo.dd.DDM;
18912             if ( Roo.lib.Event && document ) {
18913                 DDM._onLoad();
18914             } else {
18915                 if (DDM._timeoutCount > 2000) {
18916                 } else {
18917                     setTimeout(DDM._addListeners, 10);
18918                     if (document && document.body) {
18919                         DDM._timeoutCount += 1;
18920                     }
18921                 }
18922             }
18923         },
18924
18925         /**
18926          * Recursively searches the immediate parent and all child nodes for
18927          * the handle element in order to determine wheter or not it was
18928          * clicked.
18929          * @method handleWasClicked
18930          * @param node the html element to inspect
18931          * @static
18932          */
18933         handleWasClicked: function(node, id) {
18934             if (this.isHandle(id, node.id)) {
18935                 return true;
18936             } else {
18937                 // check to see if this is a text node child of the one we want
18938                 var p = node.parentNode;
18939
18940                 while (p) {
18941                     if (this.isHandle(id, p.id)) {
18942                         return true;
18943                     } else {
18944                         p = p.parentNode;
18945                     }
18946                 }
18947             }
18948
18949             return false;
18950         }
18951
18952     };
18953
18954 }();
18955
18956 // shorter alias, save a few bytes
18957 Roo.dd.DDM = Roo.dd.DragDropMgr;
18958 Roo.dd.DDM._addListeners();
18959
18960 }/*
18961  * Based on:
18962  * Ext JS Library 1.1.1
18963  * Copyright(c) 2006-2007, Ext JS, LLC.
18964  *
18965  * Originally Released Under LGPL - original licence link has changed is not relivant.
18966  *
18967  * Fork - LGPL
18968  * <script type="text/javascript">
18969  */
18970
18971 /**
18972  * @class Roo.dd.DD
18973  * A DragDrop implementation where the linked element follows the
18974  * mouse cursor during a drag.
18975  * @extends Roo.dd.DragDrop
18976  * @constructor
18977  * @param {String} id the id of the linked element
18978  * @param {String} sGroup the group of related DragDrop items
18979  * @param {object} config an object containing configurable attributes
18980  *                Valid properties for DD:
18981  *                    scroll
18982  */
18983 Roo.dd.DD = function(id, sGroup, config) {
18984     if (id) {
18985         this.init(id, sGroup, config);
18986     }
18987 };
18988
18989 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18990
18991     /**
18992      * When set to true, the utility automatically tries to scroll the browser
18993      * window wehn a drag and drop element is dragged near the viewport boundary.
18994      * Defaults to true.
18995      * @property scroll
18996      * @type boolean
18997      */
18998     scroll: true,
18999
19000     /**
19001      * Sets the pointer offset to the distance between the linked element's top
19002      * left corner and the location the element was clicked
19003      * @method autoOffset
19004      * @param {int} iPageX the X coordinate of the click
19005      * @param {int} iPageY the Y coordinate of the click
19006      */
19007     autoOffset: function(iPageX, iPageY) {
19008         var x = iPageX - this.startPageX;
19009         var y = iPageY - this.startPageY;
19010         this.setDelta(x, y);
19011     },
19012
19013     /**
19014      * Sets the pointer offset.  You can call this directly to force the
19015      * offset to be in a particular location (e.g., pass in 0,0 to set it
19016      * to the center of the object)
19017      * @method setDelta
19018      * @param {int} iDeltaX the distance from the left
19019      * @param {int} iDeltaY the distance from the top
19020      */
19021     setDelta: function(iDeltaX, iDeltaY) {
19022         this.deltaX = iDeltaX;
19023         this.deltaY = iDeltaY;
19024     },
19025
19026     /**
19027      * Sets the drag element to the location of the mousedown or click event,
19028      * maintaining the cursor location relative to the location on the element
19029      * that was clicked.  Override this if you want to place the element in a
19030      * location other than where the cursor is.
19031      * @method setDragElPos
19032      * @param {int} iPageX the X coordinate of the mousedown or drag event
19033      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19034      */
19035     setDragElPos: function(iPageX, iPageY) {
19036         // the first time we do this, we are going to check to make sure
19037         // the element has css positioning
19038
19039         var el = this.getDragEl();
19040         this.alignElWithMouse(el, iPageX, iPageY);
19041     },
19042
19043     /**
19044      * Sets the element to the location of the mousedown or click event,
19045      * maintaining the cursor location relative to the location on the element
19046      * that was clicked.  Override this if you want to place the element in a
19047      * location other than where the cursor is.
19048      * @method alignElWithMouse
19049      * @param {HTMLElement} el the element to move
19050      * @param {int} iPageX the X coordinate of the mousedown or drag event
19051      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19052      */
19053     alignElWithMouse: function(el, iPageX, iPageY) {
19054         var oCoord = this.getTargetCoord(iPageX, iPageY);
19055         var fly = el.dom ? el : Roo.fly(el);
19056         if (!this.deltaSetXY) {
19057             var aCoord = [oCoord.x, oCoord.y];
19058             fly.setXY(aCoord);
19059             var newLeft = fly.getLeft(true);
19060             var newTop  = fly.getTop(true);
19061             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19062         } else {
19063             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19064         }
19065
19066         this.cachePosition(oCoord.x, oCoord.y);
19067         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19068         return oCoord;
19069     },
19070
19071     /**
19072      * Saves the most recent position so that we can reset the constraints and
19073      * tick marks on-demand.  We need to know this so that we can calculate the
19074      * number of pixels the element is offset from its original position.
19075      * @method cachePosition
19076      * @param iPageX the current x position (optional, this just makes it so we
19077      * don't have to look it up again)
19078      * @param iPageY the current y position (optional, this just makes it so we
19079      * don't have to look it up again)
19080      */
19081     cachePosition: function(iPageX, iPageY) {
19082         if (iPageX) {
19083             this.lastPageX = iPageX;
19084             this.lastPageY = iPageY;
19085         } else {
19086             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19087             this.lastPageX = aCoord[0];
19088             this.lastPageY = aCoord[1];
19089         }
19090     },
19091
19092     /**
19093      * Auto-scroll the window if the dragged object has been moved beyond the
19094      * visible window boundary.
19095      * @method autoScroll
19096      * @param {int} x the drag element's x position
19097      * @param {int} y the drag element's y position
19098      * @param {int} h the height of the drag element
19099      * @param {int} w the width of the drag element
19100      * @private
19101      */
19102     autoScroll: function(x, y, h, w) {
19103
19104         if (this.scroll) {
19105             // The client height
19106             var clientH = Roo.lib.Dom.getViewWidth();
19107
19108             // The client width
19109             var clientW = Roo.lib.Dom.getViewHeight();
19110
19111             // The amt scrolled down
19112             var st = this.DDM.getScrollTop();
19113
19114             // The amt scrolled right
19115             var sl = this.DDM.getScrollLeft();
19116
19117             // Location of the bottom of the element
19118             var bot = h + y;
19119
19120             // Location of the right of the element
19121             var right = w + x;
19122
19123             // The distance from the cursor to the bottom of the visible area,
19124             // adjusted so that we don't scroll if the cursor is beyond the
19125             // element drag constraints
19126             var toBot = (clientH + st - y - this.deltaY);
19127
19128             // The distance from the cursor to the right of the visible area
19129             var toRight = (clientW + sl - x - this.deltaX);
19130
19131
19132             // How close to the edge the cursor must be before we scroll
19133             // var thresh = (document.all) ? 100 : 40;
19134             var thresh = 40;
19135
19136             // How many pixels to scroll per autoscroll op.  This helps to reduce
19137             // clunky scrolling. IE is more sensitive about this ... it needs this
19138             // value to be higher.
19139             var scrAmt = (document.all) ? 80 : 30;
19140
19141             // Scroll down if we are near the bottom of the visible page and the
19142             // obj extends below the crease
19143             if ( bot > clientH && toBot < thresh ) {
19144                 window.scrollTo(sl, st + scrAmt);
19145             }
19146
19147             // Scroll up if the window is scrolled down and the top of the object
19148             // goes above the top border
19149             if ( y < st && st > 0 && y - st < thresh ) {
19150                 window.scrollTo(sl, st - scrAmt);
19151             }
19152
19153             // Scroll right if the obj is beyond the right border and the cursor is
19154             // near the border.
19155             if ( right > clientW && toRight < thresh ) {
19156                 window.scrollTo(sl + scrAmt, st);
19157             }
19158
19159             // Scroll left if the window has been scrolled to the right and the obj
19160             // extends past the left border
19161             if ( x < sl && sl > 0 && x - sl < thresh ) {
19162                 window.scrollTo(sl - scrAmt, st);
19163             }
19164         }
19165     },
19166
19167     /**
19168      * Finds the location the element should be placed if we want to move
19169      * it to where the mouse location less the click offset would place us.
19170      * @method getTargetCoord
19171      * @param {int} iPageX the X coordinate of the click
19172      * @param {int} iPageY the Y coordinate of the click
19173      * @return an object that contains the coordinates (Object.x and Object.y)
19174      * @private
19175      */
19176     getTargetCoord: function(iPageX, iPageY) {
19177
19178
19179         var x = iPageX - this.deltaX;
19180         var y = iPageY - this.deltaY;
19181
19182         if (this.constrainX) {
19183             if (x < this.minX) { x = this.minX; }
19184             if (x > this.maxX) { x = this.maxX; }
19185         }
19186
19187         if (this.constrainY) {
19188             if (y < this.minY) { y = this.minY; }
19189             if (y > this.maxY) { y = this.maxY; }
19190         }
19191
19192         x = this.getTick(x, this.xTicks);
19193         y = this.getTick(y, this.yTicks);
19194
19195
19196         return {x:x, y:y};
19197     },
19198
19199     /*
19200      * Sets up config options specific to this class. Overrides
19201      * Roo.dd.DragDrop, but all versions of this method through the
19202      * inheritance chain are called
19203      */
19204     applyConfig: function() {
19205         Roo.dd.DD.superclass.applyConfig.call(this);
19206         this.scroll = (this.config.scroll !== false);
19207     },
19208
19209     /*
19210      * Event that fires prior to the onMouseDown event.  Overrides
19211      * Roo.dd.DragDrop.
19212      */
19213     b4MouseDown: function(e) {
19214         // this.resetConstraints();
19215         this.autoOffset(e.getPageX(),
19216                             e.getPageY());
19217     },
19218
19219     /*
19220      * Event that fires prior to the onDrag event.  Overrides
19221      * Roo.dd.DragDrop.
19222      */
19223     b4Drag: function(e) {
19224         this.setDragElPos(e.getPageX(),
19225                             e.getPageY());
19226     },
19227
19228     toString: function() {
19229         return ("DD " + this.id);
19230     }
19231
19232     //////////////////////////////////////////////////////////////////////////
19233     // Debugging ygDragDrop events that can be overridden
19234     //////////////////////////////////////////////////////////////////////////
19235     /*
19236     startDrag: function(x, y) {
19237     },
19238
19239     onDrag: function(e) {
19240     },
19241
19242     onDragEnter: function(e, id) {
19243     },
19244
19245     onDragOver: function(e, id) {
19246     },
19247
19248     onDragOut: function(e, id) {
19249     },
19250
19251     onDragDrop: function(e, id) {
19252     },
19253
19254     endDrag: function(e) {
19255     }
19256
19257     */
19258
19259 });/*
19260  * Based on:
19261  * Ext JS Library 1.1.1
19262  * Copyright(c) 2006-2007, Ext JS, LLC.
19263  *
19264  * Originally Released Under LGPL - original licence link has changed is not relivant.
19265  *
19266  * Fork - LGPL
19267  * <script type="text/javascript">
19268  */
19269
19270 /**
19271  * @class Roo.dd.DDProxy
19272  * A DragDrop implementation that inserts an empty, bordered div into
19273  * the document that follows the cursor during drag operations.  At the time of
19274  * the click, the frame div is resized to the dimensions of the linked html
19275  * element, and moved to the exact location of the linked element.
19276  *
19277  * References to the "frame" element refer to the single proxy element that
19278  * was created to be dragged in place of all DDProxy elements on the
19279  * page.
19280  *
19281  * @extends Roo.dd.DD
19282  * @constructor
19283  * @param {String} id the id of the linked html element
19284  * @param {String} sGroup the group of related DragDrop objects
19285  * @param {object} config an object containing configurable attributes
19286  *                Valid properties for DDProxy in addition to those in DragDrop:
19287  *                   resizeFrame, centerFrame, dragElId
19288  */
19289 Roo.dd.DDProxy = function(id, sGroup, config) {
19290     if (id) {
19291         this.init(id, sGroup, config);
19292         this.initFrame();
19293     }
19294 };
19295
19296 /**
19297  * The default drag frame div id
19298  * @property Roo.dd.DDProxy.dragElId
19299  * @type String
19300  * @static
19301  */
19302 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19303
19304 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19305
19306     /**
19307      * By default we resize the drag frame to be the same size as the element
19308      * we want to drag (this is to get the frame effect).  We can turn it off
19309      * if we want a different behavior.
19310      * @property resizeFrame
19311      * @type boolean
19312      */
19313     resizeFrame: true,
19314
19315     /**
19316      * By default the frame is positioned exactly where the drag element is, so
19317      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19318      * you do not have constraints on the obj is to have the drag frame centered
19319      * around the cursor.  Set centerFrame to true for this effect.
19320      * @property centerFrame
19321      * @type boolean
19322      */
19323     centerFrame: false,
19324
19325     /**
19326      * Creates the proxy element if it does not yet exist
19327      * @method createFrame
19328      */
19329     createFrame: function() {
19330         var self = this;
19331         var body = document.body;
19332
19333         if (!body || !body.firstChild) {
19334             setTimeout( function() { self.createFrame(); }, 50 );
19335             return;
19336         }
19337
19338         var div = this.getDragEl();
19339
19340         if (!div) {
19341             div    = document.createElement("div");
19342             div.id = this.dragElId;
19343             var s  = div.style;
19344
19345             s.position   = "absolute";
19346             s.visibility = "hidden";
19347             s.cursor     = "move";
19348             s.border     = "2px solid #aaa";
19349             s.zIndex     = 999;
19350
19351             // appendChild can blow up IE if invoked prior to the window load event
19352             // while rendering a table.  It is possible there are other scenarios
19353             // that would cause this to happen as well.
19354             body.insertBefore(div, body.firstChild);
19355         }
19356     },
19357
19358     /**
19359      * Initialization for the drag frame element.  Must be called in the
19360      * constructor of all subclasses
19361      * @method initFrame
19362      */
19363     initFrame: function() {
19364         this.createFrame();
19365     },
19366
19367     applyConfig: function() {
19368         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19369
19370         this.resizeFrame = (this.config.resizeFrame !== false);
19371         this.centerFrame = (this.config.centerFrame);
19372         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19373     },
19374
19375     /**
19376      * Resizes the drag frame to the dimensions of the clicked object, positions
19377      * it over the object, and finally displays it
19378      * @method showFrame
19379      * @param {int} iPageX X click position
19380      * @param {int} iPageY Y click position
19381      * @private
19382      */
19383     showFrame: function(iPageX, iPageY) {
19384         var el = this.getEl();
19385         var dragEl = this.getDragEl();
19386         var s = dragEl.style;
19387
19388         this._resizeProxy();
19389
19390         if (this.centerFrame) {
19391             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19392                            Math.round(parseInt(s.height, 10)/2) );
19393         }
19394
19395         this.setDragElPos(iPageX, iPageY);
19396
19397         Roo.fly(dragEl).show();
19398     },
19399
19400     /**
19401      * The proxy is automatically resized to the dimensions of the linked
19402      * element when a drag is initiated, unless resizeFrame is set to false
19403      * @method _resizeProxy
19404      * @private
19405      */
19406     _resizeProxy: function() {
19407         if (this.resizeFrame) {
19408             var el = this.getEl();
19409             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19410         }
19411     },
19412
19413     // overrides Roo.dd.DragDrop
19414     b4MouseDown: function(e) {
19415         var x = e.getPageX();
19416         var y = e.getPageY();
19417         this.autoOffset(x, y);
19418         this.setDragElPos(x, y);
19419     },
19420
19421     // overrides Roo.dd.DragDrop
19422     b4StartDrag: function(x, y) {
19423         // show the drag frame
19424         this.showFrame(x, y);
19425     },
19426
19427     // overrides Roo.dd.DragDrop
19428     b4EndDrag: function(e) {
19429         Roo.fly(this.getDragEl()).hide();
19430     },
19431
19432     // overrides Roo.dd.DragDrop
19433     // By default we try to move the element to the last location of the frame.
19434     // This is so that the default behavior mirrors that of Roo.dd.DD.
19435     endDrag: function(e) {
19436
19437         var lel = this.getEl();
19438         var del = this.getDragEl();
19439
19440         // Show the drag frame briefly so we can get its position
19441         del.style.visibility = "";
19442
19443         this.beforeMove();
19444         // Hide the linked element before the move to get around a Safari
19445         // rendering bug.
19446         lel.style.visibility = "hidden";
19447         Roo.dd.DDM.moveToEl(lel, del);
19448         del.style.visibility = "hidden";
19449         lel.style.visibility = "";
19450
19451         this.afterDrag();
19452     },
19453
19454     beforeMove : function(){
19455
19456     },
19457
19458     afterDrag : function(){
19459
19460     },
19461
19462     toString: function() {
19463         return ("DDProxy " + this.id);
19464     }
19465
19466 });
19467 /*
19468  * Based on:
19469  * Ext JS Library 1.1.1
19470  * Copyright(c) 2006-2007, Ext JS, LLC.
19471  *
19472  * Originally Released Under LGPL - original licence link has changed is not relivant.
19473  *
19474  * Fork - LGPL
19475  * <script type="text/javascript">
19476  */
19477
19478  /**
19479  * @class Roo.dd.DDTarget
19480  * A DragDrop implementation that does not move, but can be a drop
19481  * target.  You would get the same result by simply omitting implementation
19482  * for the event callbacks, but this way we reduce the processing cost of the
19483  * event listener and the callbacks.
19484  * @extends Roo.dd.DragDrop
19485  * @constructor
19486  * @param {String} id the id of the element that is a drop target
19487  * @param {String} sGroup the group of related DragDrop objects
19488  * @param {object} config an object containing configurable attributes
19489  *                 Valid properties for DDTarget in addition to those in
19490  *                 DragDrop:
19491  *                    none
19492  */
19493 Roo.dd.DDTarget = function(id, sGroup, config) {
19494     if (id) {
19495         this.initTarget(id, sGroup, config);
19496     }
19497     if (config.listeners || config.events) { 
19498        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19499             listeners : config.listeners || {}, 
19500             events : config.events || {} 
19501         });    
19502     }
19503 };
19504
19505 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19506 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19507     toString: function() {
19508         return ("DDTarget " + this.id);
19509     }
19510 });
19511 /*
19512  * Based on:
19513  * Ext JS Library 1.1.1
19514  * Copyright(c) 2006-2007, Ext JS, LLC.
19515  *
19516  * Originally Released Under LGPL - original licence link has changed is not relivant.
19517  *
19518  * Fork - LGPL
19519  * <script type="text/javascript">
19520  */
19521  
19522
19523 /**
19524  * @class Roo.dd.ScrollManager
19525  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19526  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19527  * @singleton
19528  */
19529 Roo.dd.ScrollManager = function(){
19530     var ddm = Roo.dd.DragDropMgr;
19531     var els = {};
19532     var dragEl = null;
19533     var proc = {};
19534     
19535     
19536     
19537     var onStop = function(e){
19538         dragEl = null;
19539         clearProc();
19540     };
19541     
19542     var triggerRefresh = function(){
19543         if(ddm.dragCurrent){
19544              ddm.refreshCache(ddm.dragCurrent.groups);
19545         }
19546     };
19547     
19548     var doScroll = function(){
19549         if(ddm.dragCurrent){
19550             var dds = Roo.dd.ScrollManager;
19551             if(!dds.animate){
19552                 if(proc.el.scroll(proc.dir, dds.increment)){
19553                     triggerRefresh();
19554                 }
19555             }else{
19556                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19557             }
19558         }
19559     };
19560     
19561     var clearProc = function(){
19562         if(proc.id){
19563             clearInterval(proc.id);
19564         }
19565         proc.id = 0;
19566         proc.el = null;
19567         proc.dir = "";
19568     };
19569     
19570     var startProc = function(el, dir){
19571          Roo.log('scroll startproc');
19572         clearProc();
19573         proc.el = el;
19574         proc.dir = dir;
19575         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19576     };
19577     
19578     var onFire = function(e, isDrop){
19579        
19580         if(isDrop || !ddm.dragCurrent){ return; }
19581         var dds = Roo.dd.ScrollManager;
19582         if(!dragEl || dragEl != ddm.dragCurrent){
19583             dragEl = ddm.dragCurrent;
19584             // refresh regions on drag start
19585             dds.refreshCache();
19586         }
19587         
19588         var xy = Roo.lib.Event.getXY(e);
19589         var pt = new Roo.lib.Point(xy[0], xy[1]);
19590         for(var id in els){
19591             var el = els[id], r = el._region;
19592             if(r && r.contains(pt) && el.isScrollable()){
19593                 if(r.bottom - pt.y <= dds.thresh){
19594                     if(proc.el != el){
19595                         startProc(el, "down");
19596                     }
19597                     return;
19598                 }else if(r.right - pt.x <= dds.thresh){
19599                     if(proc.el != el){
19600                         startProc(el, "left");
19601                     }
19602                     return;
19603                 }else if(pt.y - r.top <= dds.thresh){
19604                     if(proc.el != el){
19605                         startProc(el, "up");
19606                     }
19607                     return;
19608                 }else if(pt.x - r.left <= dds.thresh){
19609                     if(proc.el != el){
19610                         startProc(el, "right");
19611                     }
19612                     return;
19613                 }
19614             }
19615         }
19616         clearProc();
19617     };
19618     
19619     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19620     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19621     
19622     return {
19623         /**
19624          * Registers new overflow element(s) to auto scroll
19625          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19626          */
19627         register : function(el){
19628             if(el instanceof Array){
19629                 for(var i = 0, len = el.length; i < len; i++) {
19630                         this.register(el[i]);
19631                 }
19632             }else{
19633                 el = Roo.get(el);
19634                 els[el.id] = el;
19635             }
19636             Roo.dd.ScrollManager.els = els;
19637         },
19638         
19639         /**
19640          * Unregisters overflow element(s) so they are no longer scrolled
19641          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19642          */
19643         unregister : function(el){
19644             if(el instanceof Array){
19645                 for(var i = 0, len = el.length; i < len; i++) {
19646                         this.unregister(el[i]);
19647                 }
19648             }else{
19649                 el = Roo.get(el);
19650                 delete els[el.id];
19651             }
19652         },
19653         
19654         /**
19655          * The number of pixels from the edge of a container the pointer needs to be to 
19656          * trigger scrolling (defaults to 25)
19657          * @type Number
19658          */
19659         thresh : 25,
19660         
19661         /**
19662          * The number of pixels to scroll in each scroll increment (defaults to 50)
19663          * @type Number
19664          */
19665         increment : 100,
19666         
19667         /**
19668          * The frequency of scrolls in milliseconds (defaults to 500)
19669          * @type Number
19670          */
19671         frequency : 500,
19672         
19673         /**
19674          * True to animate the scroll (defaults to true)
19675          * @type Boolean
19676          */
19677         animate: true,
19678         
19679         /**
19680          * The animation duration in seconds - 
19681          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19682          * @type Number
19683          */
19684         animDuration: .4,
19685         
19686         /**
19687          * Manually trigger a cache refresh.
19688          */
19689         refreshCache : function(){
19690             for(var id in els){
19691                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19692                     els[id]._region = els[id].getRegion();
19693                 }
19694             }
19695         }
19696     };
19697 }();/*
19698  * Based on:
19699  * Ext JS Library 1.1.1
19700  * Copyright(c) 2006-2007, Ext JS, LLC.
19701  *
19702  * Originally Released Under LGPL - original licence link has changed is not relivant.
19703  *
19704  * Fork - LGPL
19705  * <script type="text/javascript">
19706  */
19707  
19708
19709 /**
19710  * @class Roo.dd.Registry
19711  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19712  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19713  * @singleton
19714  */
19715 Roo.dd.Registry = function(){
19716     var elements = {}; 
19717     var handles = {}; 
19718     var autoIdSeed = 0;
19719
19720     var getId = function(el, autogen){
19721         if(typeof el == "string"){
19722             return el;
19723         }
19724         var id = el.id;
19725         if(!id && autogen !== false){
19726             id = "roodd-" + (++autoIdSeed);
19727             el.id = id;
19728         }
19729         return id;
19730     };
19731     
19732     return {
19733     /**
19734      * Register a drag drop element
19735      * @param {String|HTMLElement} element The id or DOM node to register
19736      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19737      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19738      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19739      * populated in the data object (if applicable):
19740      * <pre>
19741 Value      Description<br />
19742 ---------  ------------------------------------------<br />
19743 handles    Array of DOM nodes that trigger dragging<br />
19744            for the element being registered<br />
19745 isHandle   True if the element passed in triggers<br />
19746            dragging itself, else false
19747 </pre>
19748      */
19749         register : function(el, data){
19750             data = data || {};
19751             if(typeof el == "string"){
19752                 el = document.getElementById(el);
19753             }
19754             data.ddel = el;
19755             elements[getId(el)] = data;
19756             if(data.isHandle !== false){
19757                 handles[data.ddel.id] = data;
19758             }
19759             if(data.handles){
19760                 var hs = data.handles;
19761                 for(var i = 0, len = hs.length; i < len; i++){
19762                         handles[getId(hs[i])] = data;
19763                 }
19764             }
19765         },
19766
19767     /**
19768      * Unregister a drag drop element
19769      * @param {String|HTMLElement}  element The id or DOM node to unregister
19770      */
19771         unregister : function(el){
19772             var id = getId(el, false);
19773             var data = elements[id];
19774             if(data){
19775                 delete elements[id];
19776                 if(data.handles){
19777                     var hs = data.handles;
19778                     for(var i = 0, len = hs.length; i < len; i++){
19779                         delete handles[getId(hs[i], false)];
19780                     }
19781                 }
19782             }
19783         },
19784
19785     /**
19786      * Returns the handle registered for a DOM Node by id
19787      * @param {String|HTMLElement} id The DOM node or id to look up
19788      * @return {Object} handle The custom handle data
19789      */
19790         getHandle : function(id){
19791             if(typeof id != "string"){ // must be element?
19792                 id = id.id;
19793             }
19794             return handles[id];
19795         },
19796
19797     /**
19798      * Returns the handle that is registered for the DOM node that is the target of the event
19799      * @param {Event} e The event
19800      * @return {Object} handle The custom handle data
19801      */
19802         getHandleFromEvent : function(e){
19803             var t = Roo.lib.Event.getTarget(e);
19804             return t ? handles[t.id] : null;
19805         },
19806
19807     /**
19808      * Returns a custom data object that is registered for a DOM node by id
19809      * @param {String|HTMLElement} id The DOM node or id to look up
19810      * @return {Object} data The custom data
19811      */
19812         getTarget : function(id){
19813             if(typeof id != "string"){ // must be element?
19814                 id = id.id;
19815             }
19816             return elements[id];
19817         },
19818
19819     /**
19820      * Returns a custom data object that is registered for the DOM node that is the target of the event
19821      * @param {Event} e The event
19822      * @return {Object} data The custom data
19823      */
19824         getTargetFromEvent : function(e){
19825             var t = Roo.lib.Event.getTarget(e);
19826             return t ? elements[t.id] || handles[t.id] : null;
19827         }
19828     };
19829 }();/*
19830  * Based on:
19831  * Ext JS Library 1.1.1
19832  * Copyright(c) 2006-2007, Ext JS, LLC.
19833  *
19834  * Originally Released Under LGPL - original licence link has changed is not relivant.
19835  *
19836  * Fork - LGPL
19837  * <script type="text/javascript">
19838  */
19839  
19840
19841 /**
19842  * @class Roo.dd.StatusProxy
19843  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19844  * default drag proxy used by all Roo.dd components.
19845  * @constructor
19846  * @param {Object} config
19847  */
19848 Roo.dd.StatusProxy = function(config){
19849     Roo.apply(this, config);
19850     this.id = this.id || Roo.id();
19851     this.el = new Roo.Layer({
19852         dh: {
19853             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19854                 {tag: "div", cls: "x-dd-drop-icon"},
19855                 {tag: "div", cls: "x-dd-drag-ghost"}
19856             ]
19857         }, 
19858         shadow: !config || config.shadow !== false
19859     });
19860     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19861     this.dropStatus = this.dropNotAllowed;
19862 };
19863
19864 Roo.dd.StatusProxy.prototype = {
19865     /**
19866      * @cfg {String} dropAllowed
19867      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19868      */
19869     dropAllowed : "x-dd-drop-ok",
19870     /**
19871      * @cfg {String} dropNotAllowed
19872      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19873      */
19874     dropNotAllowed : "x-dd-drop-nodrop",
19875
19876     /**
19877      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19878      * over the current target element.
19879      * @param {String} cssClass The css class for the new drop status indicator image
19880      */
19881     setStatus : function(cssClass){
19882         cssClass = cssClass || this.dropNotAllowed;
19883         if(this.dropStatus != cssClass){
19884             this.el.replaceClass(this.dropStatus, cssClass);
19885             this.dropStatus = cssClass;
19886         }
19887     },
19888
19889     /**
19890      * Resets the status indicator to the default dropNotAllowed value
19891      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19892      */
19893     reset : function(clearGhost){
19894         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19895         this.dropStatus = this.dropNotAllowed;
19896         if(clearGhost){
19897             this.ghost.update("");
19898         }
19899     },
19900
19901     /**
19902      * Updates the contents of the ghost element
19903      * @param {String} html The html that will replace the current innerHTML of the ghost element
19904      */
19905     update : function(html){
19906         if(typeof html == "string"){
19907             this.ghost.update(html);
19908         }else{
19909             this.ghost.update("");
19910             html.style.margin = "0";
19911             this.ghost.dom.appendChild(html);
19912         }
19913         // ensure float = none set?? cant remember why though.
19914         var el = this.ghost.dom.firstChild;
19915                 if(el){
19916                         Roo.fly(el).setStyle('float', 'none');
19917                 }
19918     },
19919     
19920     /**
19921      * Returns the underlying proxy {@link Roo.Layer}
19922      * @return {Roo.Layer} el
19923     */
19924     getEl : function(){
19925         return this.el;
19926     },
19927
19928     /**
19929      * Returns the ghost element
19930      * @return {Roo.Element} el
19931      */
19932     getGhost : function(){
19933         return this.ghost;
19934     },
19935
19936     /**
19937      * Hides the proxy
19938      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19939      */
19940     hide : function(clear){
19941         this.el.hide();
19942         if(clear){
19943             this.reset(true);
19944         }
19945     },
19946
19947     /**
19948      * Stops the repair animation if it's currently running
19949      */
19950     stop : function(){
19951         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19952             this.anim.stop();
19953         }
19954     },
19955
19956     /**
19957      * Displays this proxy
19958      */
19959     show : function(){
19960         this.el.show();
19961     },
19962
19963     /**
19964      * Force the Layer to sync its shadow and shim positions to the element
19965      */
19966     sync : function(){
19967         this.el.sync();
19968     },
19969
19970     /**
19971      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19972      * invalid drop operation by the item being dragged.
19973      * @param {Array} xy The XY position of the element ([x, y])
19974      * @param {Function} callback The function to call after the repair is complete
19975      * @param {Object} scope The scope in which to execute the callback
19976      */
19977     repair : function(xy, callback, scope){
19978         this.callback = callback;
19979         this.scope = scope;
19980         if(xy && this.animRepair !== false){
19981             this.el.addClass("x-dd-drag-repair");
19982             this.el.hideUnders(true);
19983             this.anim = this.el.shift({
19984                 duration: this.repairDuration || .5,
19985                 easing: 'easeOut',
19986                 xy: xy,
19987                 stopFx: true,
19988                 callback: this.afterRepair,
19989                 scope: this
19990             });
19991         }else{
19992             this.afterRepair();
19993         }
19994     },
19995
19996     // private
19997     afterRepair : function(){
19998         this.hide(true);
19999         if(typeof this.callback == "function"){
20000             this.callback.call(this.scope || this);
20001         }
20002         this.callback = null;
20003         this.scope = null;
20004     }
20005 };/*
20006  * Based on:
20007  * Ext JS Library 1.1.1
20008  * Copyright(c) 2006-2007, Ext JS, LLC.
20009  *
20010  * Originally Released Under LGPL - original licence link has changed is not relivant.
20011  *
20012  * Fork - LGPL
20013  * <script type="text/javascript">
20014  */
20015
20016 /**
20017  * @class Roo.dd.DragSource
20018  * @extends Roo.dd.DDProxy
20019  * A simple class that provides the basic implementation needed to make any element draggable.
20020  * @constructor
20021  * @param {String/HTMLElement/Element} el The container element
20022  * @param {Object} config
20023  */
20024 Roo.dd.DragSource = function(el, config){
20025     this.el = Roo.get(el);
20026     this.dragData = {};
20027     
20028     Roo.apply(this, config);
20029     
20030     if(!this.proxy){
20031         this.proxy = new Roo.dd.StatusProxy();
20032     }
20033
20034     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
20035           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
20036     
20037     this.dragging = false;
20038 };
20039
20040 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
20041     /**
20042      * @cfg {String} dropAllowed
20043      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20044      */
20045     dropAllowed : "x-dd-drop-ok",
20046     /**
20047      * @cfg {String} dropNotAllowed
20048      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20049      */
20050     dropNotAllowed : "x-dd-drop-nodrop",
20051
20052     /**
20053      * Returns the data object associated with this drag source
20054      * @return {Object} data An object containing arbitrary data
20055      */
20056     getDragData : function(e){
20057         return this.dragData;
20058     },
20059
20060     // private
20061     onDragEnter : function(e, id){
20062         var target = Roo.dd.DragDropMgr.getDDById(id);
20063         this.cachedTarget = target;
20064         if(this.beforeDragEnter(target, e, id) !== false){
20065             if(target.isNotifyTarget){
20066                 var status = target.notifyEnter(this, e, this.dragData);
20067                 this.proxy.setStatus(status);
20068             }else{
20069                 this.proxy.setStatus(this.dropAllowed);
20070             }
20071             
20072             if(this.afterDragEnter){
20073                 /**
20074                  * An empty function by default, but provided so that you can perform a custom action
20075                  * when the dragged item enters the drop target by providing an implementation.
20076                  * @param {Roo.dd.DragDrop} target The drop target
20077                  * @param {Event} e The event object
20078                  * @param {String} id The id of the dragged element
20079                  * @method afterDragEnter
20080                  */
20081                 this.afterDragEnter(target, e, id);
20082             }
20083         }
20084     },
20085
20086     /**
20087      * An empty function by default, but provided so that you can perform a custom action
20088      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20089      * @param {Roo.dd.DragDrop} target The drop target
20090      * @param {Event} e The event object
20091      * @param {String} id The id of the dragged element
20092      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20093      */
20094     beforeDragEnter : function(target, e, id){
20095         return true;
20096     },
20097
20098     // private
20099     alignElWithMouse: function() {
20100         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20101         this.proxy.sync();
20102     },
20103
20104     // private
20105     onDragOver : function(e, id){
20106         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20107         if(this.beforeDragOver(target, e, id) !== false){
20108             if(target.isNotifyTarget){
20109                 var status = target.notifyOver(this, e, this.dragData);
20110                 this.proxy.setStatus(status);
20111             }
20112
20113             if(this.afterDragOver){
20114                 /**
20115                  * An empty function by default, but provided so that you can perform a custom action
20116                  * while the dragged item is over the drop target by providing an implementation.
20117                  * @param {Roo.dd.DragDrop} target The drop target
20118                  * @param {Event} e The event object
20119                  * @param {String} id The id of the dragged element
20120                  * @method afterDragOver
20121                  */
20122                 this.afterDragOver(target, e, id);
20123             }
20124         }
20125     },
20126
20127     /**
20128      * An empty function by default, but provided so that you can perform a custom action
20129      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20130      * @param {Roo.dd.DragDrop} target The drop target
20131      * @param {Event} e The event object
20132      * @param {String} id The id of the dragged element
20133      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20134      */
20135     beforeDragOver : function(target, e, id){
20136         return true;
20137     },
20138
20139     // private
20140     onDragOut : function(e, id){
20141         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20142         if(this.beforeDragOut(target, e, id) !== false){
20143             if(target.isNotifyTarget){
20144                 target.notifyOut(this, e, this.dragData);
20145             }
20146             this.proxy.reset();
20147             if(this.afterDragOut){
20148                 /**
20149                  * An empty function by default, but provided so that you can perform a custom action
20150                  * after the dragged item is dragged out of the target without dropping.
20151                  * @param {Roo.dd.DragDrop} target The drop target
20152                  * @param {Event} e The event object
20153                  * @param {String} id The id of the dragged element
20154                  * @method afterDragOut
20155                  */
20156                 this.afterDragOut(target, e, id);
20157             }
20158         }
20159         this.cachedTarget = null;
20160     },
20161
20162     /**
20163      * An empty function by default, but provided so that you can perform a custom action before the dragged
20164      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20165      * @param {Roo.dd.DragDrop} target The drop target
20166      * @param {Event} e The event object
20167      * @param {String} id The id of the dragged element
20168      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20169      */
20170     beforeDragOut : function(target, e, id){
20171         return true;
20172     },
20173     
20174     // private
20175     onDragDrop : function(e, id){
20176         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20177         if(this.beforeDragDrop(target, e, id) !== false){
20178             if(target.isNotifyTarget){
20179                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20180                     this.onValidDrop(target, e, id);
20181                 }else{
20182                     this.onInvalidDrop(target, e, id);
20183                 }
20184             }else{
20185                 this.onValidDrop(target, e, id);
20186             }
20187             
20188             if(this.afterDragDrop){
20189                 /**
20190                  * An empty function by default, but provided so that you can perform a custom action
20191                  * after a valid drag drop has occurred by providing an implementation.
20192                  * @param {Roo.dd.DragDrop} target The drop target
20193                  * @param {Event} e The event object
20194                  * @param {String} id The id of the dropped element
20195                  * @method afterDragDrop
20196                  */
20197                 this.afterDragDrop(target, e, id);
20198             }
20199         }
20200         delete this.cachedTarget;
20201     },
20202
20203     /**
20204      * An empty function by default, but provided so that you can perform a custom action before the dragged
20205      * item is dropped onto the target and optionally cancel the onDragDrop.
20206      * @param {Roo.dd.DragDrop} target The drop target
20207      * @param {Event} e The event object
20208      * @param {String} id The id of the dragged element
20209      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20210      */
20211     beforeDragDrop : function(target, e, id){
20212         return true;
20213     },
20214
20215     // private
20216     onValidDrop : function(target, e, id){
20217         this.hideProxy();
20218         if(this.afterValidDrop){
20219             /**
20220              * An empty function by default, but provided so that you can perform a custom action
20221              * after a valid drop has occurred by providing an implementation.
20222              * @param {Object} target The target DD 
20223              * @param {Event} e The event object
20224              * @param {String} id The id of the dropped element
20225              * @method afterInvalidDrop
20226              */
20227             this.afterValidDrop(target, e, id);
20228         }
20229     },
20230
20231     // private
20232     getRepairXY : function(e, data){
20233         return this.el.getXY();  
20234     },
20235
20236     // private
20237     onInvalidDrop : function(target, e, id){
20238         this.beforeInvalidDrop(target, e, id);
20239         if(this.cachedTarget){
20240             if(this.cachedTarget.isNotifyTarget){
20241                 this.cachedTarget.notifyOut(this, e, this.dragData);
20242             }
20243             this.cacheTarget = null;
20244         }
20245         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20246
20247         if(this.afterInvalidDrop){
20248             /**
20249              * An empty function by default, but provided so that you can perform a custom action
20250              * after an invalid drop has occurred by providing an implementation.
20251              * @param {Event} e The event object
20252              * @param {String} id The id of the dropped element
20253              * @method afterInvalidDrop
20254              */
20255             this.afterInvalidDrop(e, id);
20256         }
20257     },
20258
20259     // private
20260     afterRepair : function(){
20261         if(Roo.enableFx){
20262             this.el.highlight(this.hlColor || "c3daf9");
20263         }
20264         this.dragging = false;
20265     },
20266
20267     /**
20268      * An empty function by default, but provided so that you can perform a custom action after an invalid
20269      * drop has occurred.
20270      * @param {Roo.dd.DragDrop} target The drop target
20271      * @param {Event} e The event object
20272      * @param {String} id The id of the dragged element
20273      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20274      */
20275     beforeInvalidDrop : function(target, e, id){
20276         return true;
20277     },
20278
20279     // private
20280     handleMouseDown : function(e){
20281         if(this.dragging) {
20282             return;
20283         }
20284         var data = this.getDragData(e);
20285         if(data && this.onBeforeDrag(data, e) !== false){
20286             this.dragData = data;
20287             this.proxy.stop();
20288             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20289         } 
20290     },
20291
20292     /**
20293      * An empty function by default, but provided so that you can perform a custom action before the initial
20294      * drag event begins and optionally cancel it.
20295      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20296      * @param {Event} e The event object
20297      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20298      */
20299     onBeforeDrag : function(data, e){
20300         return true;
20301     },
20302
20303     /**
20304      * An empty function by default, but provided so that you can perform a custom action once the initial
20305      * drag event has begun.  The drag cannot be canceled from this function.
20306      * @param {Number} x The x position of the click on the dragged object
20307      * @param {Number} y The y position of the click on the dragged object
20308      */
20309     onStartDrag : Roo.emptyFn,
20310
20311     // private - YUI override
20312     startDrag : function(x, y){
20313         this.proxy.reset();
20314         this.dragging = true;
20315         this.proxy.update("");
20316         this.onInitDrag(x, y);
20317         this.proxy.show();
20318     },
20319
20320     // private
20321     onInitDrag : function(x, y){
20322         var clone = this.el.dom.cloneNode(true);
20323         clone.id = Roo.id(); // prevent duplicate ids
20324         this.proxy.update(clone);
20325         this.onStartDrag(x, y);
20326         return true;
20327     },
20328
20329     /**
20330      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20331      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20332      */
20333     getProxy : function(){
20334         return this.proxy;  
20335     },
20336
20337     /**
20338      * Hides the drag source's {@link Roo.dd.StatusProxy}
20339      */
20340     hideProxy : function(){
20341         this.proxy.hide();  
20342         this.proxy.reset(true);
20343         this.dragging = false;
20344     },
20345
20346     // private
20347     triggerCacheRefresh : function(){
20348         Roo.dd.DDM.refreshCache(this.groups);
20349     },
20350
20351     // private - override to prevent hiding
20352     b4EndDrag: function(e) {
20353     },
20354
20355     // private - override to prevent moving
20356     endDrag : function(e){
20357         this.onEndDrag(this.dragData, e);
20358     },
20359
20360     // private
20361     onEndDrag : function(data, e){
20362     },
20363     
20364     // private - pin to cursor
20365     autoOffset : function(x, y) {
20366         this.setDelta(-12, -20);
20367     }    
20368 });/*
20369  * Based on:
20370  * Ext JS Library 1.1.1
20371  * Copyright(c) 2006-2007, Ext JS, LLC.
20372  *
20373  * Originally Released Under LGPL - original licence link has changed is not relivant.
20374  *
20375  * Fork - LGPL
20376  * <script type="text/javascript">
20377  */
20378
20379
20380 /**
20381  * @class Roo.dd.DropTarget
20382  * @extends Roo.dd.DDTarget
20383  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20384  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20385  * @constructor
20386  * @param {String/HTMLElement/Element} el The container element
20387  * @param {Object} config
20388  */
20389 Roo.dd.DropTarget = function(el, config){
20390     this.el = Roo.get(el);
20391     
20392     var listeners = false; ;
20393     if (config && config.listeners) {
20394         listeners= config.listeners;
20395         delete config.listeners;
20396     }
20397     Roo.apply(this, config);
20398     
20399     if(this.containerScroll){
20400         Roo.dd.ScrollManager.register(this.el);
20401     }
20402     this.addEvents( {
20403          /**
20404          * @scope Roo.dd.DropTarget
20405          */
20406          
20407          /**
20408          * @event enter
20409          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20410          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20411          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20412          * 
20413          * IMPORTANT : it should set this.overClass and this.dropAllowed
20414          * 
20415          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20416          * @param {Event} e The event
20417          * @param {Object} data An object containing arbitrary data supplied by the drag source
20418          */
20419         "enter" : true,
20420         
20421          /**
20422          * @event over
20423          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20424          * This method will be called on every mouse movement while the drag source is over the drop target.
20425          * This default implementation simply returns the dropAllowed config value.
20426          * 
20427          * IMPORTANT : it should set this.dropAllowed
20428          * 
20429          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20430          * @param {Event} e The event
20431          * @param {Object} data An object containing arbitrary data supplied by the drag source
20432          
20433          */
20434         "over" : true,
20435         /**
20436          * @event out
20437          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20438          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20439          * overClass (if any) from the drop element.
20440          * 
20441          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20442          * @param {Event} e The event
20443          * @param {Object} data An object containing arbitrary data supplied by the drag source
20444          */
20445          "out" : true,
20446          
20447         /**
20448          * @event drop
20449          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20450          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20451          * implementation that does something to process the drop event and returns true so that the drag source's
20452          * repair action does not run.
20453          * 
20454          * IMPORTANT : it should set this.success
20455          * 
20456          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20457          * @param {Event} e The event
20458          * @param {Object} data An object containing arbitrary data supplied by the drag source
20459         */
20460          "drop" : true
20461     });
20462             
20463      
20464     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20465         this.el.dom, 
20466         this.ddGroup || this.group,
20467         {
20468             isTarget: true,
20469             listeners : listeners || {} 
20470            
20471         
20472         }
20473     );
20474
20475 };
20476
20477 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20478     /**
20479      * @cfg {String} overClass
20480      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20481      */
20482      /**
20483      * @cfg {String} ddGroup
20484      * The drag drop group to handle drop events for
20485      */
20486      
20487     /**
20488      * @cfg {String} dropAllowed
20489      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20490      */
20491     dropAllowed : "x-dd-drop-ok",
20492     /**
20493      * @cfg {String} dropNotAllowed
20494      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20495      */
20496     dropNotAllowed : "x-dd-drop-nodrop",
20497     /**
20498      * @cfg {boolean} success
20499      * set this after drop listener.. 
20500      */
20501     success : false,
20502     /**
20503      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20504      * if the drop point is valid for over/enter..
20505      */
20506     valid : false,
20507     // private
20508     isTarget : true,
20509
20510     // private
20511     isNotifyTarget : true,
20512     
20513     /**
20514      * @hide
20515      */
20516     notifyEnter : function(dd, e, data)
20517     {
20518         this.valid = true;
20519         this.fireEvent('enter', dd, e, data);
20520         if(this.overClass){
20521             this.el.addClass(this.overClass);
20522         }
20523         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20524             this.valid ? this.dropAllowed : this.dropNotAllowed
20525         );
20526     },
20527
20528     /**
20529      * @hide
20530      */
20531     notifyOver : function(dd, e, data)
20532     {
20533         this.valid = true;
20534         this.fireEvent('over', dd, e, data);
20535         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20536             this.valid ? this.dropAllowed : this.dropNotAllowed
20537         );
20538     },
20539
20540     /**
20541      * @hide
20542      */
20543     notifyOut : function(dd, e, data)
20544     {
20545         this.fireEvent('out', dd, e, data);
20546         if(this.overClass){
20547             this.el.removeClass(this.overClass);
20548         }
20549     },
20550
20551     /**
20552      * @hide
20553      */
20554     notifyDrop : function(dd, e, data)
20555     {
20556         this.success = false;
20557         this.fireEvent('drop', dd, e, data);
20558         return this.success;
20559     }
20560 });/*
20561  * Based on:
20562  * Ext JS Library 1.1.1
20563  * Copyright(c) 2006-2007, Ext JS, LLC.
20564  *
20565  * Originally Released Under LGPL - original licence link has changed is not relivant.
20566  *
20567  * Fork - LGPL
20568  * <script type="text/javascript">
20569  */
20570
20571
20572 /**
20573  * @class Roo.dd.DragZone
20574  * @extends Roo.dd.DragSource
20575  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20576  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20577  * @constructor
20578  * @param {String/HTMLElement/Element} el The container element
20579  * @param {Object} config
20580  */
20581 Roo.dd.DragZone = function(el, config){
20582     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20583     if(this.containerScroll){
20584         Roo.dd.ScrollManager.register(this.el);
20585     }
20586 };
20587
20588 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20589     /**
20590      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20591      * for auto scrolling during drag operations.
20592      */
20593     /**
20594      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20595      * method after a failed drop (defaults to "c3daf9" - light blue)
20596      */
20597
20598     /**
20599      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20600      * for a valid target to drag based on the mouse down. Override this method
20601      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20602      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20603      * @param {EventObject} e The mouse down event
20604      * @return {Object} The dragData
20605      */
20606     getDragData : function(e){
20607         return Roo.dd.Registry.getHandleFromEvent(e);
20608     },
20609     
20610     /**
20611      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20612      * this.dragData.ddel
20613      * @param {Number} x The x position of the click on the dragged object
20614      * @param {Number} y The y position of the click on the dragged object
20615      * @return {Boolean} true to continue the drag, false to cancel
20616      */
20617     onInitDrag : function(x, y){
20618         this.proxy.update(this.dragData.ddel.cloneNode(true));
20619         this.onStartDrag(x, y);
20620         return true;
20621     },
20622     
20623     /**
20624      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20625      */
20626     afterRepair : function(){
20627         if(Roo.enableFx){
20628             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20629         }
20630         this.dragging = false;
20631     },
20632
20633     /**
20634      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20635      * the XY of this.dragData.ddel
20636      * @param {EventObject} e The mouse up event
20637      * @return {Array} The xy location (e.g. [100, 200])
20638      */
20639     getRepairXY : function(e){
20640         return Roo.Element.fly(this.dragData.ddel).getXY();  
20641     }
20642 });/*
20643  * Based on:
20644  * Ext JS Library 1.1.1
20645  * Copyright(c) 2006-2007, Ext JS, LLC.
20646  *
20647  * Originally Released Under LGPL - original licence link has changed is not relivant.
20648  *
20649  * Fork - LGPL
20650  * <script type="text/javascript">
20651  */
20652 /**
20653  * @class Roo.dd.DropZone
20654  * @extends Roo.dd.DropTarget
20655  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20656  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20657  * @constructor
20658  * @param {String/HTMLElement/Element} el The container element
20659  * @param {Object} config
20660  */
20661 Roo.dd.DropZone = function(el, config){
20662     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20663 };
20664
20665 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20666     /**
20667      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20668      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20669      * provide your own custom lookup.
20670      * @param {Event} e The event
20671      * @return {Object} data The custom data
20672      */
20673     getTargetFromEvent : function(e){
20674         return Roo.dd.Registry.getTargetFromEvent(e);
20675     },
20676
20677     /**
20678      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20679      * that it has registered.  This method has no default implementation and should be overridden to provide
20680      * node-specific processing if necessary.
20681      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20682      * {@link #getTargetFromEvent} for this node)
20683      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20684      * @param {Event} e The event
20685      * @param {Object} data An object containing arbitrary data supplied by the drag source
20686      */
20687     onNodeEnter : function(n, dd, e, data){
20688         
20689     },
20690
20691     /**
20692      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20693      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20694      * overridden to provide the proper feedback.
20695      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20696      * {@link #getTargetFromEvent} for this node)
20697      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20698      * @param {Event} e The event
20699      * @param {Object} data An object containing arbitrary data supplied by the drag source
20700      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20701      * underlying {@link Roo.dd.StatusProxy} can be updated
20702      */
20703     onNodeOver : function(n, dd, e, data){
20704         return this.dropAllowed;
20705     },
20706
20707     /**
20708      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20709      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20710      * node-specific processing if necessary.
20711      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20712      * {@link #getTargetFromEvent} for this node)
20713      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20714      * @param {Event} e The event
20715      * @param {Object} data An object containing arbitrary data supplied by the drag source
20716      */
20717     onNodeOut : function(n, dd, e, data){
20718         
20719     },
20720
20721     /**
20722      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20723      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20724      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20725      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20726      * {@link #getTargetFromEvent} for this node)
20727      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20728      * @param {Event} e The event
20729      * @param {Object} data An object containing arbitrary data supplied by the drag source
20730      * @return {Boolean} True if the drop was valid, else false
20731      */
20732     onNodeDrop : function(n, dd, e, data){
20733         return false;
20734     },
20735
20736     /**
20737      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20738      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20739      * it should be overridden to provide the proper feedback if necessary.
20740      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20741      * @param {Event} e The event
20742      * @param {Object} data An object containing arbitrary data supplied by the drag source
20743      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20744      * underlying {@link Roo.dd.StatusProxy} can be updated
20745      */
20746     onContainerOver : function(dd, e, data){
20747         return this.dropNotAllowed;
20748     },
20749
20750     /**
20751      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20752      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20753      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20754      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20755      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20756      * @param {Event} e The event
20757      * @param {Object} data An object containing arbitrary data supplied by the drag source
20758      * @return {Boolean} True if the drop was valid, else false
20759      */
20760     onContainerDrop : function(dd, e, data){
20761         return false;
20762     },
20763
20764     /**
20765      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20766      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20767      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20768      * you should override this method and provide a custom implementation.
20769      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20770      * @param {Event} e The event
20771      * @param {Object} data An object containing arbitrary data supplied by the drag source
20772      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20773      * underlying {@link Roo.dd.StatusProxy} can be updated
20774      */
20775     notifyEnter : function(dd, e, data){
20776         return this.dropNotAllowed;
20777     },
20778
20779     /**
20780      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20781      * This method will be called on every mouse movement while the drag source is over the drop zone.
20782      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20783      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20784      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20785      * registered node, it will call {@link #onContainerOver}.
20786      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20787      * @param {Event} e The event
20788      * @param {Object} data An object containing arbitrary data supplied by the drag source
20789      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20790      * underlying {@link Roo.dd.StatusProxy} can be updated
20791      */
20792     notifyOver : function(dd, e, data){
20793         var n = this.getTargetFromEvent(e);
20794         if(!n){ // not over valid drop target
20795             if(this.lastOverNode){
20796                 this.onNodeOut(this.lastOverNode, dd, e, data);
20797                 this.lastOverNode = null;
20798             }
20799             return this.onContainerOver(dd, e, data);
20800         }
20801         if(this.lastOverNode != n){
20802             if(this.lastOverNode){
20803                 this.onNodeOut(this.lastOverNode, dd, e, data);
20804             }
20805             this.onNodeEnter(n, dd, e, data);
20806             this.lastOverNode = n;
20807         }
20808         return this.onNodeOver(n, dd, e, data);
20809     },
20810
20811     /**
20812      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20813      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20814      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20815      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20816      * @param {Event} e The event
20817      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20818      */
20819     notifyOut : function(dd, e, data){
20820         if(this.lastOverNode){
20821             this.onNodeOut(this.lastOverNode, dd, e, data);
20822             this.lastOverNode = null;
20823         }
20824     },
20825
20826     /**
20827      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20828      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20829      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20830      * otherwise it will call {@link #onContainerDrop}.
20831      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20832      * @param {Event} e The event
20833      * @param {Object} data An object containing arbitrary data supplied by the drag source
20834      * @return {Boolean} True if the drop was valid, else false
20835      */
20836     notifyDrop : function(dd, e, data){
20837         if(this.lastOverNode){
20838             this.onNodeOut(this.lastOverNode, dd, e, data);
20839             this.lastOverNode = null;
20840         }
20841         var n = this.getTargetFromEvent(e);
20842         return n ?
20843             this.onNodeDrop(n, dd, e, data) :
20844             this.onContainerDrop(dd, e, data);
20845     },
20846
20847     // private
20848     triggerCacheRefresh : function(){
20849         Roo.dd.DDM.refreshCache(this.groups);
20850     }  
20851 });/*
20852  * Based on:
20853  * Ext JS Library 1.1.1
20854  * Copyright(c) 2006-2007, Ext JS, LLC.
20855  *
20856  * Originally Released Under LGPL - original licence link has changed is not relivant.
20857  *
20858  * Fork - LGPL
20859  * <script type="text/javascript">
20860  */
20861
20862
20863 /**
20864  * @class Roo.data.SortTypes
20865  * @singleton
20866  * Defines the default sorting (casting?) comparison functions used when sorting data.
20867  */
20868 Roo.data.SortTypes = {
20869     /**
20870      * Default sort that does nothing
20871      * @param {Mixed} s The value being converted
20872      * @return {Mixed} The comparison value
20873      */
20874     none : function(s){
20875         return s;
20876     },
20877     
20878     /**
20879      * The regular expression used to strip tags
20880      * @type {RegExp}
20881      * @property
20882      */
20883     stripTagsRE : /<\/?[^>]+>/gi,
20884     
20885     /**
20886      * Strips all HTML tags to sort on text only
20887      * @param {Mixed} s The value being converted
20888      * @return {String} The comparison value
20889      */
20890     asText : function(s){
20891         return String(s).replace(this.stripTagsRE, "");
20892     },
20893     
20894     /**
20895      * Strips all HTML tags to sort on text only - Case insensitive
20896      * @param {Mixed} s The value being converted
20897      * @return {String} The comparison value
20898      */
20899     asUCText : function(s){
20900         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20901     },
20902     
20903     /**
20904      * Case insensitive string
20905      * @param {Mixed} s The value being converted
20906      * @return {String} The comparison value
20907      */
20908     asUCString : function(s) {
20909         return String(s).toUpperCase();
20910     },
20911     
20912     /**
20913      * Date sorting
20914      * @param {Mixed} s The value being converted
20915      * @return {Number} The comparison value
20916      */
20917     asDate : function(s) {
20918         if(!s){
20919             return 0;
20920         }
20921         if(s instanceof Date){
20922             return s.getTime();
20923         }
20924         return Date.parse(String(s));
20925     },
20926     
20927     /**
20928      * Float sorting
20929      * @param {Mixed} s The value being converted
20930      * @return {Float} The comparison value
20931      */
20932     asFloat : function(s) {
20933         var val = parseFloat(String(s).replace(/,/g, ""));
20934         if(isNaN(val)) {
20935             val = 0;
20936         }
20937         return val;
20938     },
20939     
20940     /**
20941      * Integer sorting
20942      * @param {Mixed} s The value being converted
20943      * @return {Number} The comparison value
20944      */
20945     asInt : function(s) {
20946         var val = parseInt(String(s).replace(/,/g, ""));
20947         if(isNaN(val)) {
20948             val = 0;
20949         }
20950         return val;
20951     }
20952 };/*
20953  * Based on:
20954  * Ext JS Library 1.1.1
20955  * Copyright(c) 2006-2007, Ext JS, LLC.
20956  *
20957  * Originally Released Under LGPL - original licence link has changed is not relivant.
20958  *
20959  * Fork - LGPL
20960  * <script type="text/javascript">
20961  */
20962
20963 /**
20964 * @class Roo.data.Record
20965  * Instances of this class encapsulate both record <em>definition</em> information, and record
20966  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20967  * to access Records cached in an {@link Roo.data.Store} object.<br>
20968  * <p>
20969  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20970  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20971  * objects.<br>
20972  * <p>
20973  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20974  * @constructor
20975  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20976  * {@link #create}. The parameters are the same.
20977  * @param {Array} data An associative Array of data values keyed by the field name.
20978  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20979  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20980  * not specified an integer id is generated.
20981  */
20982 Roo.data.Record = function(data, id){
20983     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20984     this.data = data;
20985 };
20986
20987 /**
20988  * Generate a constructor for a specific record layout.
20989  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20990  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20991  * Each field definition object may contain the following properties: <ul>
20992  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
20993  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20994  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20995  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20996  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20997  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20998  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20999  * this may be omitted.</p></li>
21000  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
21001  * <ul><li>auto (Default, implies no conversion)</li>
21002  * <li>string</li>
21003  * <li>int</li>
21004  * <li>float</li>
21005  * <li>boolean</li>
21006  * <li>date</li></ul></p></li>
21007  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
21008  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
21009  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
21010  * by the Reader into an object that will be stored in the Record. It is passed the
21011  * following parameters:<ul>
21012  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
21013  * </ul></p></li>
21014  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
21015  * </ul>
21016  * <br>usage:<br><pre><code>
21017 var TopicRecord = Roo.data.Record.create(
21018     {name: 'title', mapping: 'topic_title'},
21019     {name: 'author', mapping: 'username'},
21020     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
21021     {name: 'lastPost', mapping: 'post_time', type: 'date'},
21022     {name: 'lastPoster', mapping: 'user2'},
21023     {name: 'excerpt', mapping: 'post_text'}
21024 );
21025
21026 var myNewRecord = new TopicRecord({
21027     title: 'Do my job please',
21028     author: 'noobie',
21029     totalPosts: 1,
21030     lastPost: new Date(),
21031     lastPoster: 'Animal',
21032     excerpt: 'No way dude!'
21033 });
21034 myStore.add(myNewRecord);
21035 </code></pre>
21036  * @method create
21037  * @static
21038  */
21039 Roo.data.Record.create = function(o){
21040     var f = function(){
21041         f.superclass.constructor.apply(this, arguments);
21042     };
21043     Roo.extend(f, Roo.data.Record);
21044     var p = f.prototype;
21045     p.fields = new Roo.util.MixedCollection(false, function(field){
21046         return field.name;
21047     });
21048     for(var i = 0, len = o.length; i < len; i++){
21049         p.fields.add(new Roo.data.Field(o[i]));
21050     }
21051     f.getField = function(name){
21052         return p.fields.get(name);  
21053     };
21054     return f;
21055 };
21056
21057 Roo.data.Record.AUTO_ID = 1000;
21058 Roo.data.Record.EDIT = 'edit';
21059 Roo.data.Record.REJECT = 'reject';
21060 Roo.data.Record.COMMIT = 'commit';
21061
21062 Roo.data.Record.prototype = {
21063     /**
21064      * Readonly flag - true if this record has been modified.
21065      * @type Boolean
21066      */
21067     dirty : false,
21068     editing : false,
21069     error: null,
21070     modified: null,
21071
21072     // private
21073     join : function(store){
21074         this.store = store;
21075     },
21076
21077     /**
21078      * Set the named field to the specified value.
21079      * @param {String} name The name of the field to set.
21080      * @param {Object} value The value to set the field to.
21081      */
21082     set : function(name, value){
21083         if(this.data[name] == value){
21084             return;
21085         }
21086         this.dirty = true;
21087         if(!this.modified){
21088             this.modified = {};
21089         }
21090         if(typeof this.modified[name] == 'undefined'){
21091             this.modified[name] = this.data[name];
21092         }
21093         this.data[name] = value;
21094         if(!this.editing && this.store){
21095             this.store.afterEdit(this);
21096         }       
21097     },
21098
21099     /**
21100      * Get the value of the named field.
21101      * @param {String} name The name of the field to get the value of.
21102      * @return {Object} The value of the field.
21103      */
21104     get : function(name){
21105         return this.data[name]; 
21106     },
21107
21108     // private
21109     beginEdit : function(){
21110         this.editing = true;
21111         this.modified = {}; 
21112     },
21113
21114     // private
21115     cancelEdit : function(){
21116         this.editing = false;
21117         delete this.modified;
21118     },
21119
21120     // private
21121     endEdit : function(){
21122         this.editing = false;
21123         if(this.dirty && this.store){
21124             this.store.afterEdit(this);
21125         }
21126     },
21127
21128     /**
21129      * Usually called by the {@link Roo.data.Store} which owns the Record.
21130      * Rejects all changes made to the Record since either creation, or the last commit operation.
21131      * Modified fields are reverted to their original values.
21132      * <p>
21133      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21134      * of reject operations.
21135      */
21136     reject : function(){
21137         var m = this.modified;
21138         for(var n in m){
21139             if(typeof m[n] != "function"){
21140                 this.data[n] = m[n];
21141             }
21142         }
21143         this.dirty = false;
21144         delete this.modified;
21145         this.editing = false;
21146         if(this.store){
21147             this.store.afterReject(this);
21148         }
21149     },
21150
21151     /**
21152      * Usually called by the {@link Roo.data.Store} which owns the Record.
21153      * Commits all changes made to the Record since either creation, or the last commit operation.
21154      * <p>
21155      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21156      * of commit operations.
21157      */
21158     commit : function(){
21159         this.dirty = false;
21160         delete this.modified;
21161         this.editing = false;
21162         if(this.store){
21163             this.store.afterCommit(this);
21164         }
21165     },
21166
21167     // private
21168     hasError : function(){
21169         return this.error != null;
21170     },
21171
21172     // private
21173     clearError : function(){
21174         this.error = null;
21175     },
21176
21177     /**
21178      * Creates a copy of this record.
21179      * @param {String} id (optional) A new record id if you don't want to use this record's id
21180      * @return {Record}
21181      */
21182     copy : function(newId) {
21183         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21184     }
21185 };/*
21186  * Based on:
21187  * Ext JS Library 1.1.1
21188  * Copyright(c) 2006-2007, Ext JS, LLC.
21189  *
21190  * Originally Released Under LGPL - original licence link has changed is not relivant.
21191  *
21192  * Fork - LGPL
21193  * <script type="text/javascript">
21194  */
21195
21196
21197
21198 /**
21199  * @class Roo.data.Store
21200  * @extends Roo.util.Observable
21201  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21202  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21203  * <p>
21204  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
21205  * has no knowledge of the format of the data returned by the Proxy.<br>
21206  * <p>
21207  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21208  * instances from the data object. These records are cached and made available through accessor functions.
21209  * @constructor
21210  * Creates a new Store.
21211  * @param {Object} config A config object containing the objects needed for the Store to access data,
21212  * and read the data into Records.
21213  */
21214 Roo.data.Store = function(config){
21215     this.data = new Roo.util.MixedCollection(false);
21216     this.data.getKey = function(o){
21217         return o.id;
21218     };
21219     this.baseParams = {};
21220     // private
21221     this.paramNames = {
21222         "start" : "start",
21223         "limit" : "limit",
21224         "sort" : "sort",
21225         "dir" : "dir",
21226         "multisort" : "_multisort"
21227     };
21228
21229     if(config && config.data){
21230         this.inlineData = config.data;
21231         delete config.data;
21232     }
21233
21234     Roo.apply(this, config);
21235     
21236     if(this.reader){ // reader passed
21237         this.reader = Roo.factory(this.reader, Roo.data);
21238         this.reader.xmodule = this.xmodule || false;
21239         if(!this.recordType){
21240             this.recordType = this.reader.recordType;
21241         }
21242         if(this.reader.onMetaChange){
21243             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21244         }
21245     }
21246
21247     if(this.recordType){
21248         this.fields = this.recordType.prototype.fields;
21249     }
21250     this.modified = [];
21251
21252     this.addEvents({
21253         /**
21254          * @event datachanged
21255          * Fires when the data cache has changed, and a widget which is using this Store
21256          * as a Record cache should refresh its view.
21257          * @param {Store} this
21258          */
21259         datachanged : true,
21260         /**
21261          * @event metachange
21262          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21263          * @param {Store} this
21264          * @param {Object} meta The JSON metadata
21265          */
21266         metachange : true,
21267         /**
21268          * @event add
21269          * Fires when Records have been added to the Store
21270          * @param {Store} this
21271          * @param {Roo.data.Record[]} records The array of Records added
21272          * @param {Number} index The index at which the record(s) were added
21273          */
21274         add : true,
21275         /**
21276          * @event remove
21277          * Fires when a Record has been removed from the Store
21278          * @param {Store} this
21279          * @param {Roo.data.Record} record The Record that was removed
21280          * @param {Number} index The index at which the record was removed
21281          */
21282         remove : true,
21283         /**
21284          * @event update
21285          * Fires when a Record has been updated
21286          * @param {Store} this
21287          * @param {Roo.data.Record} record The Record that was updated
21288          * @param {String} operation The update operation being performed.  Value may be one of:
21289          * <pre><code>
21290  Roo.data.Record.EDIT
21291  Roo.data.Record.REJECT
21292  Roo.data.Record.COMMIT
21293          * </code></pre>
21294          */
21295         update : true,
21296         /**
21297          * @event clear
21298          * Fires when the data cache has been cleared.
21299          * @param {Store} this
21300          */
21301         clear : true,
21302         /**
21303          * @event beforeload
21304          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21305          * the load action will be canceled.
21306          * @param {Store} this
21307          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21308          */
21309         beforeload : true,
21310         /**
21311          * @event beforeloadadd
21312          * Fires after a new set of Records has been loaded.
21313          * @param {Store} this
21314          * @param {Roo.data.Record[]} records The Records that were loaded
21315          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21316          */
21317         beforeloadadd : true,
21318         /**
21319          * @event load
21320          * Fires after a new set of Records has been loaded, before they are added to the store.
21321          * @param {Store} this
21322          * @param {Roo.data.Record[]} records The Records that were loaded
21323          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21324          * @params {Object} return from reader
21325          */
21326         load : true,
21327         /**
21328          * @event loadexception
21329          * Fires if an exception occurs in the Proxy during loading.
21330          * Called with the signature of the Proxy's "loadexception" event.
21331          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21332          * 
21333          * @param {Proxy} 
21334          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21335          * @param {Object} load options 
21336          * @param {Object} jsonData from your request (normally this contains the Exception)
21337          */
21338         loadexception : true
21339     });
21340     
21341     if(this.proxy){
21342         this.proxy = Roo.factory(this.proxy, Roo.data);
21343         this.proxy.xmodule = this.xmodule || false;
21344         this.relayEvents(this.proxy,  ["loadexception"]);
21345     }
21346     this.sortToggle = {};
21347     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21348
21349     Roo.data.Store.superclass.constructor.call(this);
21350
21351     if(this.inlineData){
21352         this.loadData(this.inlineData);
21353         delete this.inlineData;
21354     }
21355 };
21356
21357 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21358      /**
21359     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21360     * without a remote query - used by combo/forms at present.
21361     */
21362     
21363     /**
21364     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21365     */
21366     /**
21367     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21368     */
21369     /**
21370     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21371     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21372     */
21373     /**
21374     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21375     * on any HTTP request
21376     */
21377     /**
21378     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21379     */
21380     /**
21381     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21382     */
21383     multiSort: false,
21384     /**
21385     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21386     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21387     */
21388     remoteSort : false,
21389
21390     /**
21391     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21392      * loaded or when a record is removed. (defaults to false).
21393     */
21394     pruneModifiedRecords : false,
21395
21396     // private
21397     lastOptions : null,
21398
21399     /**
21400      * Add Records to the Store and fires the add event.
21401      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21402      */
21403     add : function(records){
21404         records = [].concat(records);
21405         for(var i = 0, len = records.length; i < len; i++){
21406             records[i].join(this);
21407         }
21408         var index = this.data.length;
21409         this.data.addAll(records);
21410         this.fireEvent("add", this, records, index);
21411     },
21412
21413     /**
21414      * Remove a Record from the Store and fires the remove event.
21415      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21416      */
21417     remove : function(record){
21418         var index = this.data.indexOf(record);
21419         this.data.removeAt(index);
21420         if(this.pruneModifiedRecords){
21421             this.modified.remove(record);
21422         }
21423         this.fireEvent("remove", this, record, index);
21424     },
21425
21426     /**
21427      * Remove all Records from the Store and fires the clear event.
21428      */
21429     removeAll : function(){
21430         this.data.clear();
21431         if(this.pruneModifiedRecords){
21432             this.modified = [];
21433         }
21434         this.fireEvent("clear", this);
21435     },
21436
21437     /**
21438      * Inserts Records to the Store at the given index and fires the add event.
21439      * @param {Number} index The start index at which to insert the passed Records.
21440      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21441      */
21442     insert : function(index, records){
21443         records = [].concat(records);
21444         for(var i = 0, len = records.length; i < len; i++){
21445             this.data.insert(index, records[i]);
21446             records[i].join(this);
21447         }
21448         this.fireEvent("add", this, records, index);
21449     },
21450
21451     /**
21452      * Get the index within the cache of the passed Record.
21453      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21454      * @return {Number} The index of the passed Record. Returns -1 if not found.
21455      */
21456     indexOf : function(record){
21457         return this.data.indexOf(record);
21458     },
21459
21460     /**
21461      * Get the index within the cache of the Record with the passed id.
21462      * @param {String} id The id of the Record to find.
21463      * @return {Number} The index of the Record. Returns -1 if not found.
21464      */
21465     indexOfId : function(id){
21466         return this.data.indexOfKey(id);
21467     },
21468
21469     /**
21470      * Get the Record with the specified id.
21471      * @param {String} id The id of the Record to find.
21472      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21473      */
21474     getById : function(id){
21475         return this.data.key(id);
21476     },
21477
21478     /**
21479      * Get the Record at the specified index.
21480      * @param {Number} index The index of the Record to find.
21481      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21482      */
21483     getAt : function(index){
21484         return this.data.itemAt(index);
21485     },
21486
21487     /**
21488      * Returns a range of Records between specified indices.
21489      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21490      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21491      * @return {Roo.data.Record[]} An array of Records
21492      */
21493     getRange : function(start, end){
21494         return this.data.getRange(start, end);
21495     },
21496
21497     // private
21498     storeOptions : function(o){
21499         o = Roo.apply({}, o);
21500         delete o.callback;
21501         delete o.scope;
21502         this.lastOptions = o;
21503     },
21504
21505     /**
21506      * Loads the Record cache from the configured Proxy using the configured Reader.
21507      * <p>
21508      * If using remote paging, then the first load call must specify the <em>start</em>
21509      * and <em>limit</em> properties in the options.params property to establish the initial
21510      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21511      * <p>
21512      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21513      * and this call will return before the new data has been loaded. Perform any post-processing
21514      * in a callback function, or in a "load" event handler.</strong>
21515      * <p>
21516      * @param {Object} options An object containing properties which control loading options:<ul>
21517      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21518      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21519      * passed the following arguments:<ul>
21520      * <li>r : Roo.data.Record[]</li>
21521      * <li>options: Options object from the load call</li>
21522      * <li>success: Boolean success indicator</li></ul></li>
21523      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21524      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21525      * </ul>
21526      */
21527     load : function(options){
21528         options = options || {};
21529         if(this.fireEvent("beforeload", this, options) !== false){
21530             this.storeOptions(options);
21531             var p = Roo.apply(options.params || {}, this.baseParams);
21532             // if meta was not loaded from remote source.. try requesting it.
21533             if (!this.reader.metaFromRemote) {
21534                 p._requestMeta = 1;
21535             }
21536             if(this.sortInfo && this.remoteSort){
21537                 var pn = this.paramNames;
21538                 p[pn["sort"]] = this.sortInfo.field;
21539                 p[pn["dir"]] = this.sortInfo.direction;
21540             }
21541             if (this.multiSort) {
21542                 var pn = this.paramNames;
21543                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21544             }
21545             
21546             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21547         }
21548     },
21549
21550     /**
21551      * Reloads the Record cache from the configured Proxy using the configured Reader and
21552      * the options from the last load operation performed.
21553      * @param {Object} options (optional) An object containing properties which may override the options
21554      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21555      * the most recently used options are reused).
21556      */
21557     reload : function(options){
21558         this.load(Roo.applyIf(options||{}, this.lastOptions));
21559     },
21560
21561     // private
21562     // Called as a callback by the Reader during a load operation.
21563     loadRecords : function(o, options, success){
21564         if(!o || success === false){
21565             if(success !== false){
21566                 this.fireEvent("load", this, [], options, o);
21567             }
21568             if(options.callback){
21569                 options.callback.call(options.scope || this, [], options, false);
21570             }
21571             return;
21572         }
21573         // if data returned failure - throw an exception.
21574         if (o.success === false) {
21575             // show a message if no listener is registered.
21576             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21577                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21578             }
21579             // loadmask wil be hooked into this..
21580             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21581             return;
21582         }
21583         var r = o.records, t = o.totalRecords || r.length;
21584         
21585         this.fireEvent("beforeloadadd", this, r, options, o);
21586         
21587         if(!options || options.add !== true){
21588             if(this.pruneModifiedRecords){
21589                 this.modified = [];
21590             }
21591             for(var i = 0, len = r.length; i < len; i++){
21592                 r[i].join(this);
21593             }
21594             if(this.snapshot){
21595                 this.data = this.snapshot;
21596                 delete this.snapshot;
21597             }
21598             this.data.clear();
21599             this.data.addAll(r);
21600             this.totalLength = t;
21601             this.applySort();
21602             this.fireEvent("datachanged", this);
21603         }else{
21604             this.totalLength = Math.max(t, this.data.length+r.length);
21605             this.add(r);
21606         }
21607         this.fireEvent("load", this, r, options, o);
21608         if(options.callback){
21609             options.callback.call(options.scope || this, r, options, true);
21610         }
21611     },
21612
21613
21614     /**
21615      * Loads data from a passed data block. A Reader which understands the format of the data
21616      * must have been configured in the constructor.
21617      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21618      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21619      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21620      */
21621     loadData : function(o, append){
21622         var r = this.reader.readRecords(o);
21623         this.loadRecords(r, {add: append}, true);
21624     },
21625
21626     /**
21627      * Gets the number of cached records.
21628      * <p>
21629      * <em>If using paging, this may not be the total size of the dataset. If the data object
21630      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21631      * the data set size</em>
21632      */
21633     getCount : function(){
21634         return this.data.length || 0;
21635     },
21636
21637     /**
21638      * Gets the total number of records in the dataset as returned by the server.
21639      * <p>
21640      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21641      * the dataset size</em>
21642      */
21643     getTotalCount : function(){
21644         return this.totalLength || 0;
21645     },
21646
21647     /**
21648      * Returns the sort state of the Store as an object with two properties:
21649      * <pre><code>
21650  field {String} The name of the field by which the Records are sorted
21651  direction {String} The sort order, "ASC" or "DESC"
21652      * </code></pre>
21653      */
21654     getSortState : function(){
21655         return this.sortInfo;
21656     },
21657
21658     // private
21659     applySort : function(){
21660         if(this.sortInfo && !this.remoteSort){
21661             var s = this.sortInfo, f = s.field;
21662             var st = this.fields.get(f).sortType;
21663             var fn = function(r1, r2){
21664                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21665                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21666             };
21667             this.data.sort(s.direction, fn);
21668             if(this.snapshot && this.snapshot != this.data){
21669                 this.snapshot.sort(s.direction, fn);
21670             }
21671         }
21672     },
21673
21674     /**
21675      * Sets the default sort column and order to be used by the next load operation.
21676      * @param {String} fieldName The name of the field to sort by.
21677      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21678      */
21679     setDefaultSort : function(field, dir){
21680         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21681     },
21682
21683     /**
21684      * Sort the Records.
21685      * If remote sorting is used, the sort is performed on the server, and the cache is
21686      * reloaded. If local sorting is used, the cache is sorted internally.
21687      * @param {String} fieldName The name of the field to sort by.
21688      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21689      */
21690     sort : function(fieldName, dir){
21691         var f = this.fields.get(fieldName);
21692         if(!dir){
21693             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21694             
21695             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21696                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21697             }else{
21698                 dir = f.sortDir;
21699             }
21700         }
21701         this.sortToggle[f.name] = dir;
21702         this.sortInfo = {field: f.name, direction: dir};
21703         if(!this.remoteSort){
21704             this.applySort();
21705             this.fireEvent("datachanged", this);
21706         }else{
21707             this.load(this.lastOptions);
21708         }
21709     },
21710
21711     /**
21712      * Calls the specified function for each of the Records in the cache.
21713      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21714      * Returning <em>false</em> aborts and exits the iteration.
21715      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21716      */
21717     each : function(fn, scope){
21718         this.data.each(fn, scope);
21719     },
21720
21721     /**
21722      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21723      * (e.g., during paging).
21724      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21725      */
21726     getModifiedRecords : function(){
21727         return this.modified;
21728     },
21729
21730     // private
21731     createFilterFn : function(property, value, anyMatch){
21732         if(!value.exec){ // not a regex
21733             value = String(value);
21734             if(value.length == 0){
21735                 return false;
21736             }
21737             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21738         }
21739         return function(r){
21740             return value.test(r.data[property]);
21741         };
21742     },
21743
21744     /**
21745      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21746      * @param {String} property A field on your records
21747      * @param {Number} start The record index to start at (defaults to 0)
21748      * @param {Number} end The last record index to include (defaults to length - 1)
21749      * @return {Number} The sum
21750      */
21751     sum : function(property, start, end){
21752         var rs = this.data.items, v = 0;
21753         start = start || 0;
21754         end = (end || end === 0) ? end : rs.length-1;
21755
21756         for(var i = start; i <= end; i++){
21757             v += (rs[i].data[property] || 0);
21758         }
21759         return v;
21760     },
21761
21762     /**
21763      * Filter the records by a specified property.
21764      * @param {String} field A field on your records
21765      * @param {String/RegExp} value Either a string that the field
21766      * should start with or a RegExp to test against the field
21767      * @param {Boolean} anyMatch True to match any part not just the beginning
21768      */
21769     filter : function(property, value, anyMatch){
21770         var fn = this.createFilterFn(property, value, anyMatch);
21771         return fn ? this.filterBy(fn) : this.clearFilter();
21772     },
21773
21774     /**
21775      * Filter by a function. The specified function will be called with each
21776      * record in this data source. If the function returns true the record is included,
21777      * otherwise it is filtered.
21778      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21779      * @param {Object} scope (optional) The scope of the function (defaults to this)
21780      */
21781     filterBy : function(fn, scope){
21782         this.snapshot = this.snapshot || this.data;
21783         this.data = this.queryBy(fn, scope||this);
21784         this.fireEvent("datachanged", this);
21785     },
21786
21787     /**
21788      * Query the records by a specified property.
21789      * @param {String} field A field on your records
21790      * @param {String/RegExp} value Either a string that the field
21791      * should start with or a RegExp to test against the field
21792      * @param {Boolean} anyMatch True to match any part not just the beginning
21793      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21794      */
21795     query : function(property, value, anyMatch){
21796         var fn = this.createFilterFn(property, value, anyMatch);
21797         return fn ? this.queryBy(fn) : this.data.clone();
21798     },
21799
21800     /**
21801      * Query by a function. The specified function will be called with each
21802      * record in this data source. If the function returns true the record is included
21803      * in the results.
21804      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21805      * @param {Object} scope (optional) The scope of the function (defaults to this)
21806       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21807      **/
21808     queryBy : function(fn, scope){
21809         var data = this.snapshot || this.data;
21810         return data.filterBy(fn, scope||this);
21811     },
21812
21813     /**
21814      * Collects unique values for a particular dataIndex from this store.
21815      * @param {String} dataIndex The property to collect
21816      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21817      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21818      * @return {Array} An array of the unique values
21819      **/
21820     collect : function(dataIndex, allowNull, bypassFilter){
21821         var d = (bypassFilter === true && this.snapshot) ?
21822                 this.snapshot.items : this.data.items;
21823         var v, sv, r = [], l = {};
21824         for(var i = 0, len = d.length; i < len; i++){
21825             v = d[i].data[dataIndex];
21826             sv = String(v);
21827             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21828                 l[sv] = true;
21829                 r[r.length] = v;
21830             }
21831         }
21832         return r;
21833     },
21834
21835     /**
21836      * Revert to a view of the Record cache with no filtering applied.
21837      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21838      */
21839     clearFilter : function(suppressEvent){
21840         if(this.snapshot && this.snapshot != this.data){
21841             this.data = this.snapshot;
21842             delete this.snapshot;
21843             if(suppressEvent !== true){
21844                 this.fireEvent("datachanged", this);
21845             }
21846         }
21847     },
21848
21849     // private
21850     afterEdit : function(record){
21851         if(this.modified.indexOf(record) == -1){
21852             this.modified.push(record);
21853         }
21854         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21855     },
21856     
21857     // private
21858     afterReject : function(record){
21859         this.modified.remove(record);
21860         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21861     },
21862
21863     // private
21864     afterCommit : function(record){
21865         this.modified.remove(record);
21866         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21867     },
21868
21869     /**
21870      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21871      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21872      */
21873     commitChanges : function(){
21874         var m = this.modified.slice(0);
21875         this.modified = [];
21876         for(var i = 0, len = m.length; i < len; i++){
21877             m[i].commit();
21878         }
21879     },
21880
21881     /**
21882      * Cancel outstanding changes on all changed records.
21883      */
21884     rejectChanges : function(){
21885         var m = this.modified.slice(0);
21886         this.modified = [];
21887         for(var i = 0, len = m.length; i < len; i++){
21888             m[i].reject();
21889         }
21890     },
21891
21892     onMetaChange : function(meta, rtype, o){
21893         this.recordType = rtype;
21894         this.fields = rtype.prototype.fields;
21895         delete this.snapshot;
21896         this.sortInfo = meta.sortInfo || this.sortInfo;
21897         this.modified = [];
21898         this.fireEvent('metachange', this, this.reader.meta);
21899     },
21900     
21901     moveIndex : function(data, type)
21902     {
21903         var index = this.indexOf(data);
21904         
21905         var newIndex = index + type;
21906         
21907         this.remove(data);
21908         
21909         this.insert(newIndex, data);
21910         
21911     }
21912 });/*
21913  * Based on:
21914  * Ext JS Library 1.1.1
21915  * Copyright(c) 2006-2007, Ext JS, LLC.
21916  *
21917  * Originally Released Under LGPL - original licence link has changed is not relivant.
21918  *
21919  * Fork - LGPL
21920  * <script type="text/javascript">
21921  */
21922
21923 /**
21924  * @class Roo.data.SimpleStore
21925  * @extends Roo.data.Store
21926  * Small helper class to make creating Stores from Array data easier.
21927  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21928  * @cfg {Array} fields An array of field definition objects, or field name strings.
21929  * @cfg {Array} data The multi-dimensional array of data
21930  * @constructor
21931  * @param {Object} config
21932  */
21933 Roo.data.SimpleStore = function(config){
21934     Roo.data.SimpleStore.superclass.constructor.call(this, {
21935         isLocal : true,
21936         reader: new Roo.data.ArrayReader({
21937                 id: config.id
21938             },
21939             Roo.data.Record.create(config.fields)
21940         ),
21941         proxy : new Roo.data.MemoryProxy(config.data)
21942     });
21943     this.load();
21944 };
21945 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21946  * Based on:
21947  * Ext JS Library 1.1.1
21948  * Copyright(c) 2006-2007, Ext JS, LLC.
21949  *
21950  * Originally Released Under LGPL - original licence link has changed is not relivant.
21951  *
21952  * Fork - LGPL
21953  * <script type="text/javascript">
21954  */
21955
21956 /**
21957 /**
21958  * @extends Roo.data.Store
21959  * @class Roo.data.JsonStore
21960  * Small helper class to make creating Stores for JSON data easier. <br/>
21961 <pre><code>
21962 var store = new Roo.data.JsonStore({
21963     url: 'get-images.php',
21964     root: 'images',
21965     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21966 });
21967 </code></pre>
21968  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21969  * JsonReader and HttpProxy (unless inline data is provided).</b>
21970  * @cfg {Array} fields An array of field definition objects, or field name strings.
21971  * @constructor
21972  * @param {Object} config
21973  */
21974 Roo.data.JsonStore = function(c){
21975     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21976         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21977         reader: new Roo.data.JsonReader(c, c.fields)
21978     }));
21979 };
21980 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21981  * Based on:
21982  * Ext JS Library 1.1.1
21983  * Copyright(c) 2006-2007, Ext JS, LLC.
21984  *
21985  * Originally Released Under LGPL - original licence link has changed is not relivant.
21986  *
21987  * Fork - LGPL
21988  * <script type="text/javascript">
21989  */
21990
21991  
21992 Roo.data.Field = function(config){
21993     if(typeof config == "string"){
21994         config = {name: config};
21995     }
21996     Roo.apply(this, config);
21997     
21998     if(!this.type){
21999         this.type = "auto";
22000     }
22001     
22002     var st = Roo.data.SortTypes;
22003     // named sortTypes are supported, here we look them up
22004     if(typeof this.sortType == "string"){
22005         this.sortType = st[this.sortType];
22006     }
22007     
22008     // set default sortType for strings and dates
22009     if(!this.sortType){
22010         switch(this.type){
22011             case "string":
22012                 this.sortType = st.asUCString;
22013                 break;
22014             case "date":
22015                 this.sortType = st.asDate;
22016                 break;
22017             default:
22018                 this.sortType = st.none;
22019         }
22020     }
22021
22022     // define once
22023     var stripRe = /[\$,%]/g;
22024
22025     // prebuilt conversion function for this field, instead of
22026     // switching every time we're reading a value
22027     if(!this.convert){
22028         var cv, dateFormat = this.dateFormat;
22029         switch(this.type){
22030             case "":
22031             case "auto":
22032             case undefined:
22033                 cv = function(v){ return v; };
22034                 break;
22035             case "string":
22036                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22037                 break;
22038             case "int":
22039                 cv = function(v){
22040                     return v !== undefined && v !== null && v !== '' ?
22041                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22042                     };
22043                 break;
22044             case "float":
22045                 cv = function(v){
22046                     return v !== undefined && v !== null && v !== '' ?
22047                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22048                     };
22049                 break;
22050             case "bool":
22051             case "boolean":
22052                 cv = function(v){ return v === true || v === "true" || v == 1; };
22053                 break;
22054             case "date":
22055                 cv = function(v){
22056                     if(!v){
22057                         return '';
22058                     }
22059                     if(v instanceof Date){
22060                         return v;
22061                     }
22062                     if(dateFormat){
22063                         if(dateFormat == "timestamp"){
22064                             return new Date(v*1000);
22065                         }
22066                         return Date.parseDate(v, dateFormat);
22067                     }
22068                     var parsed = Date.parse(v);
22069                     return parsed ? new Date(parsed) : null;
22070                 };
22071              break;
22072             
22073         }
22074         this.convert = cv;
22075     }
22076 };
22077
22078 Roo.data.Field.prototype = {
22079     dateFormat: null,
22080     defaultValue: "",
22081     mapping: null,
22082     sortType : null,
22083     sortDir : "ASC"
22084 };/*
22085  * Based on:
22086  * Ext JS Library 1.1.1
22087  * Copyright(c) 2006-2007, Ext JS, LLC.
22088  *
22089  * Originally Released Under LGPL - original licence link has changed is not relivant.
22090  *
22091  * Fork - LGPL
22092  * <script type="text/javascript">
22093  */
22094  
22095 // Base class for reading structured data from a data source.  This class is intended to be
22096 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22097
22098 /**
22099  * @class Roo.data.DataReader
22100  * Base class for reading structured data from a data source.  This class is intended to be
22101  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22102  */
22103
22104 Roo.data.DataReader = function(meta, recordType){
22105     
22106     this.meta = meta;
22107     
22108     this.recordType = recordType instanceof Array ? 
22109         Roo.data.Record.create(recordType) : recordType;
22110 };
22111
22112 Roo.data.DataReader.prototype = {
22113      /**
22114      * Create an empty record
22115      * @param {Object} data (optional) - overlay some values
22116      * @return {Roo.data.Record} record created.
22117      */
22118     newRow :  function(d) {
22119         var da =  {};
22120         this.recordType.prototype.fields.each(function(c) {
22121             switch( c.type) {
22122                 case 'int' : da[c.name] = 0; break;
22123                 case 'date' : da[c.name] = new Date(); break;
22124                 case 'float' : da[c.name] = 0.0; break;
22125                 case 'boolean' : da[c.name] = false; break;
22126                 default : da[c.name] = ""; break;
22127             }
22128             
22129         });
22130         return new this.recordType(Roo.apply(da, d));
22131     }
22132     
22133 };/*
22134  * Based on:
22135  * Ext JS Library 1.1.1
22136  * Copyright(c) 2006-2007, Ext JS, LLC.
22137  *
22138  * Originally Released Under LGPL - original licence link has changed is not relivant.
22139  *
22140  * Fork - LGPL
22141  * <script type="text/javascript">
22142  */
22143
22144 /**
22145  * @class Roo.data.DataProxy
22146  * @extends Roo.data.Observable
22147  * This class is an abstract base class for implementations which provide retrieval of
22148  * unformatted data objects.<br>
22149  * <p>
22150  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22151  * (of the appropriate type which knows how to parse the data object) to provide a block of
22152  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22153  * <p>
22154  * Custom implementations must implement the load method as described in
22155  * {@link Roo.data.HttpProxy#load}.
22156  */
22157 Roo.data.DataProxy = function(){
22158     this.addEvents({
22159         /**
22160          * @event beforeload
22161          * Fires before a network request is made to retrieve a data object.
22162          * @param {Object} This DataProxy object.
22163          * @param {Object} params The params parameter to the load function.
22164          */
22165         beforeload : true,
22166         /**
22167          * @event load
22168          * Fires before the load method's callback is called.
22169          * @param {Object} This DataProxy object.
22170          * @param {Object} o The data object.
22171          * @param {Object} arg The callback argument object passed to the load function.
22172          */
22173         load : true,
22174         /**
22175          * @event loadexception
22176          * Fires if an Exception occurs during data retrieval.
22177          * @param {Object} This DataProxy object.
22178          * @param {Object} o The data object.
22179          * @param {Object} arg The callback argument object passed to the load function.
22180          * @param {Object} e The Exception.
22181          */
22182         loadexception : true
22183     });
22184     Roo.data.DataProxy.superclass.constructor.call(this);
22185 };
22186
22187 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22188
22189     /**
22190      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22191      */
22192 /*
22193  * Based on:
22194  * Ext JS Library 1.1.1
22195  * Copyright(c) 2006-2007, Ext JS, LLC.
22196  *
22197  * Originally Released Under LGPL - original licence link has changed is not relivant.
22198  *
22199  * Fork - LGPL
22200  * <script type="text/javascript">
22201  */
22202 /**
22203  * @class Roo.data.MemoryProxy
22204  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22205  * to the Reader when its load method is called.
22206  * @constructor
22207  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22208  */
22209 Roo.data.MemoryProxy = function(data){
22210     if (data.data) {
22211         data = data.data;
22212     }
22213     Roo.data.MemoryProxy.superclass.constructor.call(this);
22214     this.data = data;
22215 };
22216
22217 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22218     /**
22219      * Load data from the requested source (in this case an in-memory
22220      * data object passed to the constructor), read the data object into
22221      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22222      * process that block using the passed callback.
22223      * @param {Object} params This parameter is not used by the MemoryProxy class.
22224      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22225      * object into a block of Roo.data.Records.
22226      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22227      * The function must be passed <ul>
22228      * <li>The Record block object</li>
22229      * <li>The "arg" argument from the load function</li>
22230      * <li>A boolean success indicator</li>
22231      * </ul>
22232      * @param {Object} scope The scope in which to call the callback
22233      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22234      */
22235     load : function(params, reader, callback, scope, arg){
22236         params = params || {};
22237         var result;
22238         try {
22239             result = reader.readRecords(this.data);
22240         }catch(e){
22241             this.fireEvent("loadexception", this, arg, null, e);
22242             callback.call(scope, null, arg, false);
22243             return;
22244         }
22245         callback.call(scope, result, arg, true);
22246     },
22247     
22248     // private
22249     update : function(params, records){
22250         
22251     }
22252 });/*
22253  * Based on:
22254  * Ext JS Library 1.1.1
22255  * Copyright(c) 2006-2007, Ext JS, LLC.
22256  *
22257  * Originally Released Under LGPL - original licence link has changed is not relivant.
22258  *
22259  * Fork - LGPL
22260  * <script type="text/javascript">
22261  */
22262 /**
22263  * @class Roo.data.HttpProxy
22264  * @extends Roo.data.DataProxy
22265  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22266  * configured to reference a certain URL.<br><br>
22267  * <p>
22268  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22269  * from which the running page was served.<br><br>
22270  * <p>
22271  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22272  * <p>
22273  * Be aware that to enable the browser to parse an XML document, the server must set
22274  * the Content-Type header in the HTTP response to "text/xml".
22275  * @constructor
22276  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22277  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22278  * will be used to make the request.
22279  */
22280 Roo.data.HttpProxy = function(conn){
22281     Roo.data.HttpProxy.superclass.constructor.call(this);
22282     // is conn a conn config or a real conn?
22283     this.conn = conn;
22284     this.useAjax = !conn || !conn.events;
22285   
22286 };
22287
22288 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22289     // thse are take from connection...
22290     
22291     /**
22292      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22293      */
22294     /**
22295      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22296      * extra parameters to each request made by this object. (defaults to undefined)
22297      */
22298     /**
22299      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22300      *  to each request made by this object. (defaults to undefined)
22301      */
22302     /**
22303      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
22304      */
22305     /**
22306      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22307      */
22308      /**
22309      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22310      * @type Boolean
22311      */
22312   
22313
22314     /**
22315      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22316      * @type Boolean
22317      */
22318     /**
22319      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22320      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22321      * a finer-grained basis than the DataProxy events.
22322      */
22323     getConnection : function(){
22324         return this.useAjax ? Roo.Ajax : this.conn;
22325     },
22326
22327     /**
22328      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22329      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22330      * process that block using the passed callback.
22331      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22332      * for the request to the remote server.
22333      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22334      * object into a block of Roo.data.Records.
22335      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22336      * The function must be passed <ul>
22337      * <li>The Record block object</li>
22338      * <li>The "arg" argument from the load function</li>
22339      * <li>A boolean success indicator</li>
22340      * </ul>
22341      * @param {Object} scope The scope in which to call the callback
22342      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22343      */
22344     load : function(params, reader, callback, scope, arg){
22345         if(this.fireEvent("beforeload", this, params) !== false){
22346             var  o = {
22347                 params : params || {},
22348                 request: {
22349                     callback : callback,
22350                     scope : scope,
22351                     arg : arg
22352                 },
22353                 reader: reader,
22354                 callback : this.loadResponse,
22355                 scope: this
22356             };
22357             if(this.useAjax){
22358                 Roo.applyIf(o, this.conn);
22359                 if(this.activeRequest){
22360                     Roo.Ajax.abort(this.activeRequest);
22361                 }
22362                 this.activeRequest = Roo.Ajax.request(o);
22363             }else{
22364                 this.conn.request(o);
22365             }
22366         }else{
22367             callback.call(scope||this, null, arg, false);
22368         }
22369     },
22370
22371     // private
22372     loadResponse : function(o, success, response){
22373         delete this.activeRequest;
22374         if(!success){
22375             this.fireEvent("loadexception", this, o, response);
22376             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22377             return;
22378         }
22379         var result;
22380         try {
22381             result = o.reader.read(response);
22382         }catch(e){
22383             this.fireEvent("loadexception", this, o, response, e);
22384             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22385             return;
22386         }
22387         
22388         this.fireEvent("load", this, o, o.request.arg);
22389         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22390     },
22391
22392     // private
22393     update : function(dataSet){
22394
22395     },
22396
22397     // private
22398     updateResponse : function(dataSet){
22399
22400     }
22401 });/*
22402  * Based on:
22403  * Ext JS Library 1.1.1
22404  * Copyright(c) 2006-2007, Ext JS, LLC.
22405  *
22406  * Originally Released Under LGPL - original licence link has changed is not relivant.
22407  *
22408  * Fork - LGPL
22409  * <script type="text/javascript">
22410  */
22411
22412 /**
22413  * @class Roo.data.ScriptTagProxy
22414  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22415  * other than the originating domain of the running page.<br><br>
22416  * <p>
22417  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
22418  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22419  * <p>
22420  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22421  * source code that is used as the source inside a &lt;script> tag.<br><br>
22422  * <p>
22423  * In order for the browser to process the returned data, the server must wrap the data object
22424  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22425  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22426  * depending on whether the callback name was passed:
22427  * <p>
22428  * <pre><code>
22429 boolean scriptTag = false;
22430 String cb = request.getParameter("callback");
22431 if (cb != null) {
22432     scriptTag = true;
22433     response.setContentType("text/javascript");
22434 } else {
22435     response.setContentType("application/x-json");
22436 }
22437 Writer out = response.getWriter();
22438 if (scriptTag) {
22439     out.write(cb + "(");
22440 }
22441 out.print(dataBlock.toJsonString());
22442 if (scriptTag) {
22443     out.write(");");
22444 }
22445 </pre></code>
22446  *
22447  * @constructor
22448  * @param {Object} config A configuration object.
22449  */
22450 Roo.data.ScriptTagProxy = function(config){
22451     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22452     Roo.apply(this, config);
22453     this.head = document.getElementsByTagName("head")[0];
22454 };
22455
22456 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22457
22458 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22459     /**
22460      * @cfg {String} url The URL from which to request the data object.
22461      */
22462     /**
22463      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22464      */
22465     timeout : 30000,
22466     /**
22467      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22468      * the server the name of the callback function set up by the load call to process the returned data object.
22469      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22470      * javascript output which calls this named function passing the data object as its only parameter.
22471      */
22472     callbackParam : "callback",
22473     /**
22474      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22475      * name to the request.
22476      */
22477     nocache : true,
22478
22479     /**
22480      * Load data from the configured URL, read the data object into
22481      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22482      * process that block using the passed callback.
22483      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22484      * for the request to the remote server.
22485      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22486      * object into a block of Roo.data.Records.
22487      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22488      * The function must be passed <ul>
22489      * <li>The Record block object</li>
22490      * <li>The "arg" argument from the load function</li>
22491      * <li>A boolean success indicator</li>
22492      * </ul>
22493      * @param {Object} scope The scope in which to call the callback
22494      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22495      */
22496     load : function(params, reader, callback, scope, arg){
22497         if(this.fireEvent("beforeload", this, params) !== false){
22498
22499             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22500
22501             var url = this.url;
22502             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22503             if(this.nocache){
22504                 url += "&_dc=" + (new Date().getTime());
22505             }
22506             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22507             var trans = {
22508                 id : transId,
22509                 cb : "stcCallback"+transId,
22510                 scriptId : "stcScript"+transId,
22511                 params : params,
22512                 arg : arg,
22513                 url : url,
22514                 callback : callback,
22515                 scope : scope,
22516                 reader : reader
22517             };
22518             var conn = this;
22519
22520             window[trans.cb] = function(o){
22521                 conn.handleResponse(o, trans);
22522             };
22523
22524             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22525
22526             if(this.autoAbort !== false){
22527                 this.abort();
22528             }
22529
22530             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22531
22532             var script = document.createElement("script");
22533             script.setAttribute("src", url);
22534             script.setAttribute("type", "text/javascript");
22535             script.setAttribute("id", trans.scriptId);
22536             this.head.appendChild(script);
22537
22538             this.trans = trans;
22539         }else{
22540             callback.call(scope||this, null, arg, false);
22541         }
22542     },
22543
22544     // private
22545     isLoading : function(){
22546         return this.trans ? true : false;
22547     },
22548
22549     /**
22550      * Abort the current server request.
22551      */
22552     abort : function(){
22553         if(this.isLoading()){
22554             this.destroyTrans(this.trans);
22555         }
22556     },
22557
22558     // private
22559     destroyTrans : function(trans, isLoaded){
22560         this.head.removeChild(document.getElementById(trans.scriptId));
22561         clearTimeout(trans.timeoutId);
22562         if(isLoaded){
22563             window[trans.cb] = undefined;
22564             try{
22565                 delete window[trans.cb];
22566             }catch(e){}
22567         }else{
22568             // if hasn't been loaded, wait for load to remove it to prevent script error
22569             window[trans.cb] = function(){
22570                 window[trans.cb] = undefined;
22571                 try{
22572                     delete window[trans.cb];
22573                 }catch(e){}
22574             };
22575         }
22576     },
22577
22578     // private
22579     handleResponse : function(o, trans){
22580         this.trans = false;
22581         this.destroyTrans(trans, true);
22582         var result;
22583         try {
22584             result = trans.reader.readRecords(o);
22585         }catch(e){
22586             this.fireEvent("loadexception", this, o, trans.arg, e);
22587             trans.callback.call(trans.scope||window, null, trans.arg, false);
22588             return;
22589         }
22590         this.fireEvent("load", this, o, trans.arg);
22591         trans.callback.call(trans.scope||window, result, trans.arg, true);
22592     },
22593
22594     // private
22595     handleFailure : function(trans){
22596         this.trans = false;
22597         this.destroyTrans(trans, false);
22598         this.fireEvent("loadexception", this, null, trans.arg);
22599         trans.callback.call(trans.scope||window, null, trans.arg, false);
22600     }
22601 });/*
22602  * Based on:
22603  * Ext JS Library 1.1.1
22604  * Copyright(c) 2006-2007, Ext JS, LLC.
22605  *
22606  * Originally Released Under LGPL - original licence link has changed is not relivant.
22607  *
22608  * Fork - LGPL
22609  * <script type="text/javascript">
22610  */
22611
22612 /**
22613  * @class Roo.data.JsonReader
22614  * @extends Roo.data.DataReader
22615  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22616  * based on mappings in a provided Roo.data.Record constructor.
22617  * 
22618  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22619  * in the reply previously. 
22620  * 
22621  * <p>
22622  * Example code:
22623  * <pre><code>
22624 var RecordDef = Roo.data.Record.create([
22625     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22626     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22627 ]);
22628 var myReader = new Roo.data.JsonReader({
22629     totalProperty: "results",    // The property which contains the total dataset size (optional)
22630     root: "rows",                // The property which contains an Array of row objects
22631     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22632 }, RecordDef);
22633 </code></pre>
22634  * <p>
22635  * This would consume a JSON file like this:
22636  * <pre><code>
22637 { 'results': 2, 'rows': [
22638     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22639     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22640 }
22641 </code></pre>
22642  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22643  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22644  * paged from the remote server.
22645  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22646  * @cfg {String} root name of the property which contains the Array of row objects.
22647  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22648  * @cfg {Array} fields Array of field definition objects
22649  * @constructor
22650  * Create a new JsonReader
22651  * @param {Object} meta Metadata configuration options
22652  * @param {Object} recordType Either an Array of field definition objects,
22653  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22654  */
22655 Roo.data.JsonReader = function(meta, recordType){
22656     
22657     meta = meta || {};
22658     // set some defaults:
22659     Roo.applyIf(meta, {
22660         totalProperty: 'total',
22661         successProperty : 'success',
22662         root : 'data',
22663         id : 'id'
22664     });
22665     
22666     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22667 };
22668 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22669     
22670     /**
22671      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22672      * Used by Store query builder to append _requestMeta to params.
22673      * 
22674      */
22675     metaFromRemote : false,
22676     /**
22677      * This method is only used by a DataProxy which has retrieved data from a remote server.
22678      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22679      * @return {Object} data A data block which is used by an Roo.data.Store object as
22680      * a cache of Roo.data.Records.
22681      */
22682     read : function(response){
22683         var json = response.responseText;
22684        
22685         var o = /* eval:var:o */ eval("("+json+")");
22686         if(!o) {
22687             throw {message: "JsonReader.read: Json object not found"};
22688         }
22689         
22690         if(o.metaData){
22691             
22692             delete this.ef;
22693             this.metaFromRemote = true;
22694             this.meta = o.metaData;
22695             this.recordType = Roo.data.Record.create(o.metaData.fields);
22696             this.onMetaChange(this.meta, this.recordType, o);
22697         }
22698         return this.readRecords(o);
22699     },
22700
22701     // private function a store will implement
22702     onMetaChange : function(meta, recordType, o){
22703
22704     },
22705
22706     /**
22707          * @ignore
22708          */
22709     simpleAccess: function(obj, subsc) {
22710         return obj[subsc];
22711     },
22712
22713         /**
22714          * @ignore
22715          */
22716     getJsonAccessor: function(){
22717         var re = /[\[\.]/;
22718         return function(expr) {
22719             try {
22720                 return(re.test(expr))
22721                     ? new Function("obj", "return obj." + expr)
22722                     : function(obj){
22723                         return obj[expr];
22724                     };
22725             } catch(e){}
22726             return Roo.emptyFn;
22727         };
22728     }(),
22729
22730     /**
22731      * Create a data block containing Roo.data.Records from an XML document.
22732      * @param {Object} o An object which contains an Array of row objects in the property specified
22733      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22734      * which contains the total size of the dataset.
22735      * @return {Object} data A data block which is used by an Roo.data.Store object as
22736      * a cache of Roo.data.Records.
22737      */
22738     readRecords : function(o){
22739         /**
22740          * After any data loads, the raw JSON data is available for further custom processing.
22741          * @type Object
22742          */
22743         this.o = o;
22744         var s = this.meta, Record = this.recordType,
22745             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22746
22747 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22748         if (!this.ef) {
22749             if(s.totalProperty) {
22750                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22751                 }
22752                 if(s.successProperty) {
22753                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22754                 }
22755                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22756                 if (s.id) {
22757                         var g = this.getJsonAccessor(s.id);
22758                         this.getId = function(rec) {
22759                                 var r = g(rec);  
22760                                 return (r === undefined || r === "") ? null : r;
22761                         };
22762                 } else {
22763                         this.getId = function(){return null;};
22764                 }
22765             this.ef = [];
22766             for(var jj = 0; jj < fl; jj++){
22767                 f = fi[jj];
22768                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22769                 this.ef[jj] = this.getJsonAccessor(map);
22770             }
22771         }
22772
22773         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22774         if(s.totalProperty){
22775             var vt = parseInt(this.getTotal(o), 10);
22776             if(!isNaN(vt)){
22777                 totalRecords = vt;
22778             }
22779         }
22780         if(s.successProperty){
22781             var vs = this.getSuccess(o);
22782             if(vs === false || vs === 'false'){
22783                 success = false;
22784             }
22785         }
22786         var records = [];
22787         for(var i = 0; i < c; i++){
22788                 var n = root[i];
22789             var values = {};
22790             var id = this.getId(n);
22791             for(var j = 0; j < fl; j++){
22792                 f = fi[j];
22793             var v = this.ef[j](n);
22794             if (!f.convert) {
22795                 Roo.log('missing convert for ' + f.name);
22796                 Roo.log(f);
22797                 continue;
22798             }
22799             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22800             }
22801             var record = new Record(values, id);
22802             record.json = n;
22803             records[i] = record;
22804         }
22805         return {
22806             raw : o,
22807             success : success,
22808             records : records,
22809             totalRecords : totalRecords
22810         };
22811     }
22812 });/*
22813  * Based on:
22814  * Ext JS Library 1.1.1
22815  * Copyright(c) 2006-2007, Ext JS, LLC.
22816  *
22817  * Originally Released Under LGPL - original licence link has changed is not relivant.
22818  *
22819  * Fork - LGPL
22820  * <script type="text/javascript">
22821  */
22822
22823 /**
22824  * @class Roo.data.XmlReader
22825  * @extends Roo.data.DataReader
22826  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22827  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22828  * <p>
22829  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22830  * header in the HTTP response must be set to "text/xml".</em>
22831  * <p>
22832  * Example code:
22833  * <pre><code>
22834 var RecordDef = Roo.data.Record.create([
22835    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22836    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22837 ]);
22838 var myReader = new Roo.data.XmlReader({
22839    totalRecords: "results", // The element which contains the total dataset size (optional)
22840    record: "row",           // The repeated element which contains row information
22841    id: "id"                 // The element within the row that provides an ID for the record (optional)
22842 }, RecordDef);
22843 </code></pre>
22844  * <p>
22845  * This would consume an XML file like this:
22846  * <pre><code>
22847 &lt;?xml?>
22848 &lt;dataset>
22849  &lt;results>2&lt;/results>
22850  &lt;row>
22851    &lt;id>1&lt;/id>
22852    &lt;name>Bill&lt;/name>
22853    &lt;occupation>Gardener&lt;/occupation>
22854  &lt;/row>
22855  &lt;row>
22856    &lt;id>2&lt;/id>
22857    &lt;name>Ben&lt;/name>
22858    &lt;occupation>Horticulturalist&lt;/occupation>
22859  &lt;/row>
22860 &lt;/dataset>
22861 </code></pre>
22862  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22863  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22864  * paged from the remote server.
22865  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22866  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22867  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22868  * a record identifier value.
22869  * @constructor
22870  * Create a new XmlReader
22871  * @param {Object} meta Metadata configuration options
22872  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22873  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22874  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22875  */
22876 Roo.data.XmlReader = function(meta, recordType){
22877     meta = meta || {};
22878     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22879 };
22880 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22881     /**
22882      * This method is only used by a DataProxy which has retrieved data from a remote server.
22883          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22884          * to contain a method called 'responseXML' that returns an XML document object.
22885      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22886      * a cache of Roo.data.Records.
22887      */
22888     read : function(response){
22889         var doc = response.responseXML;
22890         if(!doc) {
22891             throw {message: "XmlReader.read: XML Document not available"};
22892         }
22893         return this.readRecords(doc);
22894     },
22895
22896     /**
22897      * Create a data block containing Roo.data.Records from an XML document.
22898          * @param {Object} doc A parsed XML document.
22899      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22900      * a cache of Roo.data.Records.
22901      */
22902     readRecords : function(doc){
22903         /**
22904          * After any data loads/reads, the raw XML Document is available for further custom processing.
22905          * @type XMLDocument
22906          */
22907         this.xmlData = doc;
22908         var root = doc.documentElement || doc;
22909         var q = Roo.DomQuery;
22910         var recordType = this.recordType, fields = recordType.prototype.fields;
22911         var sid = this.meta.id;
22912         var totalRecords = 0, success = true;
22913         if(this.meta.totalRecords){
22914             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22915         }
22916         
22917         if(this.meta.success){
22918             var sv = q.selectValue(this.meta.success, root, true);
22919             success = sv !== false && sv !== 'false';
22920         }
22921         var records = [];
22922         var ns = q.select(this.meta.record, root);
22923         for(var i = 0, len = ns.length; i < len; i++) {
22924                 var n = ns[i];
22925                 var values = {};
22926                 var id = sid ? q.selectValue(sid, n) : undefined;
22927                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22928                     var f = fields.items[j];
22929                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22930                     v = f.convert(v);
22931                     values[f.name] = v;
22932                 }
22933                 var record = new recordType(values, id);
22934                 record.node = n;
22935                 records[records.length] = record;
22936             }
22937
22938             return {
22939                 success : success,
22940                 records : records,
22941                 totalRecords : totalRecords || records.length
22942             };
22943     }
22944 });/*
22945  * Based on:
22946  * Ext JS Library 1.1.1
22947  * Copyright(c) 2006-2007, Ext JS, LLC.
22948  *
22949  * Originally Released Under LGPL - original licence link has changed is not relivant.
22950  *
22951  * Fork - LGPL
22952  * <script type="text/javascript">
22953  */
22954
22955 /**
22956  * @class Roo.data.ArrayReader
22957  * @extends Roo.data.DataReader
22958  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22959  * Each element of that Array represents a row of data fields. The
22960  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22961  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22962  * <p>
22963  * Example code:.
22964  * <pre><code>
22965 var RecordDef = Roo.data.Record.create([
22966     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22967     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22968 ]);
22969 var myReader = new Roo.data.ArrayReader({
22970     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22971 }, RecordDef);
22972 </code></pre>
22973  * <p>
22974  * This would consume an Array like this:
22975  * <pre><code>
22976 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22977   </code></pre>
22978  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22979  * @constructor
22980  * Create a new JsonReader
22981  * @param {Object} meta Metadata configuration options.
22982  * @param {Object} recordType Either an Array of field definition objects
22983  * as specified to {@link Roo.data.Record#create},
22984  * or an {@link Roo.data.Record} object
22985  * created using {@link Roo.data.Record#create}.
22986  */
22987 Roo.data.ArrayReader = function(meta, recordType){
22988     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22989 };
22990
22991 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22992     /**
22993      * Create a data block containing Roo.data.Records from an XML document.
22994      * @param {Object} o An Array of row objects which represents the dataset.
22995      * @return {Object} data A data block which is used by an Roo.data.Store object as
22996      * a cache of Roo.data.Records.
22997      */
22998     readRecords : function(o){
22999         var sid = this.meta ? this.meta.id : null;
23000         var recordType = this.recordType, fields = recordType.prototype.fields;
23001         var records = [];
23002         var root = o;
23003             for(var i = 0; i < root.length; i++){
23004                     var n = root[i];
23005                 var values = {};
23006                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
23007                 for(var j = 0, jlen = fields.length; j < jlen; j++){
23008                 var f = fields.items[j];
23009                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
23010                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
23011                 v = f.convert(v);
23012                 values[f.name] = v;
23013             }
23014                 var record = new recordType(values, id);
23015                 record.json = n;
23016                 records[records.length] = record;
23017             }
23018             return {
23019                 records : records,
23020                 totalRecords : records.length
23021             };
23022     }
23023 });/*
23024  * Based on:
23025  * Ext JS Library 1.1.1
23026  * Copyright(c) 2006-2007, Ext JS, LLC.
23027  *
23028  * Originally Released Under LGPL - original licence link has changed is not relivant.
23029  *
23030  * Fork - LGPL
23031  * <script type="text/javascript">
23032  */
23033
23034
23035 /**
23036  * @class Roo.data.Tree
23037  * @extends Roo.util.Observable
23038  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23039  * in the tree have most standard DOM functionality.
23040  * @constructor
23041  * @param {Node} root (optional) The root node
23042  */
23043 Roo.data.Tree = function(root){
23044    this.nodeHash = {};
23045    /**
23046     * The root node for this tree
23047     * @type Node
23048     */
23049    this.root = null;
23050    if(root){
23051        this.setRootNode(root);
23052    }
23053    this.addEvents({
23054        /**
23055         * @event append
23056         * Fires when a new child node is appended to a node in this tree.
23057         * @param {Tree} tree The owner tree
23058         * @param {Node} parent The parent node
23059         * @param {Node} node The newly appended node
23060         * @param {Number} index The index of the newly appended node
23061         */
23062        "append" : true,
23063        /**
23064         * @event remove
23065         * Fires when a child node is removed from a node in this tree.
23066         * @param {Tree} tree The owner tree
23067         * @param {Node} parent The parent node
23068         * @param {Node} node The child node removed
23069         */
23070        "remove" : true,
23071        /**
23072         * @event move
23073         * Fires when a node is moved to a new location in the tree
23074         * @param {Tree} tree The owner tree
23075         * @param {Node} node The node moved
23076         * @param {Node} oldParent The old parent of this node
23077         * @param {Node} newParent The new parent of this node
23078         * @param {Number} index The index it was moved to
23079         */
23080        "move" : true,
23081        /**
23082         * @event insert
23083         * Fires when a new child node is inserted in a node in this tree.
23084         * @param {Tree} tree The owner tree
23085         * @param {Node} parent The parent node
23086         * @param {Node} node The child node inserted
23087         * @param {Node} refNode The child node the node was inserted before
23088         */
23089        "insert" : true,
23090        /**
23091         * @event beforeappend
23092         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23093         * @param {Tree} tree The owner tree
23094         * @param {Node} parent The parent node
23095         * @param {Node} node The child node to be appended
23096         */
23097        "beforeappend" : true,
23098        /**
23099         * @event beforeremove
23100         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23101         * @param {Tree} tree The owner tree
23102         * @param {Node} parent The parent node
23103         * @param {Node} node The child node to be removed
23104         */
23105        "beforeremove" : true,
23106        /**
23107         * @event beforemove
23108         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23109         * @param {Tree} tree The owner tree
23110         * @param {Node} node The node being moved
23111         * @param {Node} oldParent The parent of the node
23112         * @param {Node} newParent The new parent the node is moving to
23113         * @param {Number} index The index it is being moved to
23114         */
23115        "beforemove" : true,
23116        /**
23117         * @event beforeinsert
23118         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23119         * @param {Tree} tree The owner tree
23120         * @param {Node} parent The parent node
23121         * @param {Node} node The child node to be inserted
23122         * @param {Node} refNode The child node the node is being inserted before
23123         */
23124        "beforeinsert" : true
23125    });
23126
23127     Roo.data.Tree.superclass.constructor.call(this);
23128 };
23129
23130 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23131     pathSeparator: "/",
23132
23133     proxyNodeEvent : function(){
23134         return this.fireEvent.apply(this, arguments);
23135     },
23136
23137     /**
23138      * Returns the root node for this tree.
23139      * @return {Node}
23140      */
23141     getRootNode : function(){
23142         return this.root;
23143     },
23144
23145     /**
23146      * Sets the root node for this tree.
23147      * @param {Node} node
23148      * @return {Node}
23149      */
23150     setRootNode : function(node){
23151         this.root = node;
23152         node.ownerTree = this;
23153         node.isRoot = true;
23154         this.registerNode(node);
23155         return node;
23156     },
23157
23158     /**
23159      * Gets a node in this tree by its id.
23160      * @param {String} id
23161      * @return {Node}
23162      */
23163     getNodeById : function(id){
23164         return this.nodeHash[id];
23165     },
23166
23167     registerNode : function(node){
23168         this.nodeHash[node.id] = node;
23169     },
23170
23171     unregisterNode : function(node){
23172         delete this.nodeHash[node.id];
23173     },
23174
23175     toString : function(){
23176         return "[Tree"+(this.id?" "+this.id:"")+"]";
23177     }
23178 });
23179
23180 /**
23181  * @class Roo.data.Node
23182  * @extends Roo.util.Observable
23183  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23184  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23185  * @constructor
23186  * @param {Object} attributes The attributes/config for the node
23187  */
23188 Roo.data.Node = function(attributes){
23189     /**
23190      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23191      * @type {Object}
23192      */
23193     this.attributes = attributes || {};
23194     this.leaf = this.attributes.leaf;
23195     /**
23196      * The node id. @type String
23197      */
23198     this.id = this.attributes.id;
23199     if(!this.id){
23200         this.id = Roo.id(null, "ynode-");
23201         this.attributes.id = this.id;
23202     }
23203      
23204     
23205     /**
23206      * All child nodes of this node. @type Array
23207      */
23208     this.childNodes = [];
23209     if(!this.childNodes.indexOf){ // indexOf is a must
23210         this.childNodes.indexOf = function(o){
23211             for(var i = 0, len = this.length; i < len; i++){
23212                 if(this[i] == o) {
23213                     return i;
23214                 }
23215             }
23216             return -1;
23217         };
23218     }
23219     /**
23220      * The parent node for this node. @type Node
23221      */
23222     this.parentNode = null;
23223     /**
23224      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23225      */
23226     this.firstChild = null;
23227     /**
23228      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23229      */
23230     this.lastChild = null;
23231     /**
23232      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23233      */
23234     this.previousSibling = null;
23235     /**
23236      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23237      */
23238     this.nextSibling = null;
23239
23240     this.addEvents({
23241        /**
23242         * @event append
23243         * Fires when a new child node is appended
23244         * @param {Tree} tree The owner tree
23245         * @param {Node} this This node
23246         * @param {Node} node The newly appended node
23247         * @param {Number} index The index of the newly appended node
23248         */
23249        "append" : true,
23250        /**
23251         * @event remove
23252         * Fires when a child node is removed
23253         * @param {Tree} tree The owner tree
23254         * @param {Node} this This node
23255         * @param {Node} node The removed node
23256         */
23257        "remove" : true,
23258        /**
23259         * @event move
23260         * Fires when this node is moved to a new location in the tree
23261         * @param {Tree} tree The owner tree
23262         * @param {Node} this This node
23263         * @param {Node} oldParent The old parent of this node
23264         * @param {Node} newParent The new parent of this node
23265         * @param {Number} index The index it was moved to
23266         */
23267        "move" : true,
23268        /**
23269         * @event insert
23270         * Fires when a new child node is inserted.
23271         * @param {Tree} tree The owner tree
23272         * @param {Node} this This node
23273         * @param {Node} node The child node inserted
23274         * @param {Node} refNode The child node the node was inserted before
23275         */
23276        "insert" : true,
23277        /**
23278         * @event beforeappend
23279         * Fires before a new child is appended, return false to cancel the append.
23280         * @param {Tree} tree The owner tree
23281         * @param {Node} this This node
23282         * @param {Node} node The child node to be appended
23283         */
23284        "beforeappend" : true,
23285        /**
23286         * @event beforeremove
23287         * Fires before a child is removed, return false to cancel the remove.
23288         * @param {Tree} tree The owner tree
23289         * @param {Node} this This node
23290         * @param {Node} node The child node to be removed
23291         */
23292        "beforeremove" : true,
23293        /**
23294         * @event beforemove
23295         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23296         * @param {Tree} tree The owner tree
23297         * @param {Node} this This node
23298         * @param {Node} oldParent The parent of this node
23299         * @param {Node} newParent The new parent this node is moving to
23300         * @param {Number} index The index it is being moved to
23301         */
23302        "beforemove" : true,
23303        /**
23304         * @event beforeinsert
23305         * Fires before a new child is inserted, return false to cancel the insert.
23306         * @param {Tree} tree The owner tree
23307         * @param {Node} this This node
23308         * @param {Node} node The child node to be inserted
23309         * @param {Node} refNode The child node the node is being inserted before
23310         */
23311        "beforeinsert" : true
23312    });
23313     this.listeners = this.attributes.listeners;
23314     Roo.data.Node.superclass.constructor.call(this);
23315 };
23316
23317 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23318     fireEvent : function(evtName){
23319         // first do standard event for this node
23320         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23321             return false;
23322         }
23323         // then bubble it up to the tree if the event wasn't cancelled
23324         var ot = this.getOwnerTree();
23325         if(ot){
23326             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23327                 return false;
23328             }
23329         }
23330         return true;
23331     },
23332
23333     /**
23334      * Returns true if this node is a leaf
23335      * @return {Boolean}
23336      */
23337     isLeaf : function(){
23338         return this.leaf === true;
23339     },
23340
23341     // private
23342     setFirstChild : function(node){
23343         this.firstChild = node;
23344     },
23345
23346     //private
23347     setLastChild : function(node){
23348         this.lastChild = node;
23349     },
23350
23351
23352     /**
23353      * Returns true if this node is the last child of its parent
23354      * @return {Boolean}
23355      */
23356     isLast : function(){
23357        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23358     },
23359
23360     /**
23361      * Returns true if this node is the first child of its parent
23362      * @return {Boolean}
23363      */
23364     isFirst : function(){
23365        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23366     },
23367
23368     hasChildNodes : function(){
23369         return !this.isLeaf() && this.childNodes.length > 0;
23370     },
23371
23372     /**
23373      * Insert node(s) as the last child node of this node.
23374      * @param {Node/Array} node The node or Array of nodes to append
23375      * @return {Node} The appended node if single append, or null if an array was passed
23376      */
23377     appendChild : function(node){
23378         var multi = false;
23379         if(node instanceof Array){
23380             multi = node;
23381         }else if(arguments.length > 1){
23382             multi = arguments;
23383         }
23384         // if passed an array or multiple args do them one by one
23385         if(multi){
23386             for(var i = 0, len = multi.length; i < len; i++) {
23387                 this.appendChild(multi[i]);
23388             }
23389         }else{
23390             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23391                 return false;
23392             }
23393             var index = this.childNodes.length;
23394             var oldParent = node.parentNode;
23395             // it's a move, make sure we move it cleanly
23396             if(oldParent){
23397                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23398                     return false;
23399                 }
23400                 oldParent.removeChild(node);
23401             }
23402             index = this.childNodes.length;
23403             if(index == 0){
23404                 this.setFirstChild(node);
23405             }
23406             this.childNodes.push(node);
23407             node.parentNode = this;
23408             var ps = this.childNodes[index-1];
23409             if(ps){
23410                 node.previousSibling = ps;
23411                 ps.nextSibling = node;
23412             }else{
23413                 node.previousSibling = null;
23414             }
23415             node.nextSibling = null;
23416             this.setLastChild(node);
23417             node.setOwnerTree(this.getOwnerTree());
23418             this.fireEvent("append", this.ownerTree, this, node, index);
23419             if(oldParent){
23420                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23421             }
23422             return node;
23423         }
23424     },
23425
23426     /**
23427      * Removes a child node from this node.
23428      * @param {Node} node The node to remove
23429      * @return {Node} The removed node
23430      */
23431     removeChild : function(node){
23432         var index = this.childNodes.indexOf(node);
23433         if(index == -1){
23434             return false;
23435         }
23436         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23437             return false;
23438         }
23439
23440         // remove it from childNodes collection
23441         this.childNodes.splice(index, 1);
23442
23443         // update siblings
23444         if(node.previousSibling){
23445             node.previousSibling.nextSibling = node.nextSibling;
23446         }
23447         if(node.nextSibling){
23448             node.nextSibling.previousSibling = node.previousSibling;
23449         }
23450
23451         // update child refs
23452         if(this.firstChild == node){
23453             this.setFirstChild(node.nextSibling);
23454         }
23455         if(this.lastChild == node){
23456             this.setLastChild(node.previousSibling);
23457         }
23458
23459         node.setOwnerTree(null);
23460         // clear any references from the node
23461         node.parentNode = null;
23462         node.previousSibling = null;
23463         node.nextSibling = null;
23464         this.fireEvent("remove", this.ownerTree, this, node);
23465         return node;
23466     },
23467
23468     /**
23469      * Inserts the first node before the second node in this nodes childNodes collection.
23470      * @param {Node} node The node to insert
23471      * @param {Node} refNode The node to insert before (if null the node is appended)
23472      * @return {Node} The inserted node
23473      */
23474     insertBefore : function(node, refNode){
23475         if(!refNode){ // like standard Dom, refNode can be null for append
23476             return this.appendChild(node);
23477         }
23478         // nothing to do
23479         if(node == refNode){
23480             return false;
23481         }
23482
23483         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23484             return false;
23485         }
23486         var index = this.childNodes.indexOf(refNode);
23487         var oldParent = node.parentNode;
23488         var refIndex = index;
23489
23490         // when moving internally, indexes will change after remove
23491         if(oldParent == this && this.childNodes.indexOf(node) < index){
23492             refIndex--;
23493         }
23494
23495         // it's a move, make sure we move it cleanly
23496         if(oldParent){
23497             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23498                 return false;
23499             }
23500             oldParent.removeChild(node);
23501         }
23502         if(refIndex == 0){
23503             this.setFirstChild(node);
23504         }
23505         this.childNodes.splice(refIndex, 0, node);
23506         node.parentNode = this;
23507         var ps = this.childNodes[refIndex-1];
23508         if(ps){
23509             node.previousSibling = ps;
23510             ps.nextSibling = node;
23511         }else{
23512             node.previousSibling = null;
23513         }
23514         node.nextSibling = refNode;
23515         refNode.previousSibling = node;
23516         node.setOwnerTree(this.getOwnerTree());
23517         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23518         if(oldParent){
23519             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23520         }
23521         return node;
23522     },
23523
23524     /**
23525      * Returns the child node at the specified index.
23526      * @param {Number} index
23527      * @return {Node}
23528      */
23529     item : function(index){
23530         return this.childNodes[index];
23531     },
23532
23533     /**
23534      * Replaces one child node in this node with another.
23535      * @param {Node} newChild The replacement node
23536      * @param {Node} oldChild The node to replace
23537      * @return {Node} The replaced node
23538      */
23539     replaceChild : function(newChild, oldChild){
23540         this.insertBefore(newChild, oldChild);
23541         this.removeChild(oldChild);
23542         return oldChild;
23543     },
23544
23545     /**
23546      * Returns the index of a child node
23547      * @param {Node} node
23548      * @return {Number} The index of the node or -1 if it was not found
23549      */
23550     indexOf : function(child){
23551         return this.childNodes.indexOf(child);
23552     },
23553
23554     /**
23555      * Returns the tree this node is in.
23556      * @return {Tree}
23557      */
23558     getOwnerTree : function(){
23559         // if it doesn't have one, look for one
23560         if(!this.ownerTree){
23561             var p = this;
23562             while(p){
23563                 if(p.ownerTree){
23564                     this.ownerTree = p.ownerTree;
23565                     break;
23566                 }
23567                 p = p.parentNode;
23568             }
23569         }
23570         return this.ownerTree;
23571     },
23572
23573     /**
23574      * Returns depth of this node (the root node has a depth of 0)
23575      * @return {Number}
23576      */
23577     getDepth : function(){
23578         var depth = 0;
23579         var p = this;
23580         while(p.parentNode){
23581             ++depth;
23582             p = p.parentNode;
23583         }
23584         return depth;
23585     },
23586
23587     // private
23588     setOwnerTree : function(tree){
23589         // if it's move, we need to update everyone
23590         if(tree != this.ownerTree){
23591             if(this.ownerTree){
23592                 this.ownerTree.unregisterNode(this);
23593             }
23594             this.ownerTree = tree;
23595             var cs = this.childNodes;
23596             for(var i = 0, len = cs.length; i < len; i++) {
23597                 cs[i].setOwnerTree(tree);
23598             }
23599             if(tree){
23600                 tree.registerNode(this);
23601             }
23602         }
23603     },
23604
23605     /**
23606      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23607      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23608      * @return {String} The path
23609      */
23610     getPath : function(attr){
23611         attr = attr || "id";
23612         var p = this.parentNode;
23613         var b = [this.attributes[attr]];
23614         while(p){
23615             b.unshift(p.attributes[attr]);
23616             p = p.parentNode;
23617         }
23618         var sep = this.getOwnerTree().pathSeparator;
23619         return sep + b.join(sep);
23620     },
23621
23622     /**
23623      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23624      * function call will be the scope provided or the current node. The arguments to the function
23625      * will be the args provided or the current node. If the function returns false at any point,
23626      * the bubble is stopped.
23627      * @param {Function} fn The function to call
23628      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23629      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23630      */
23631     bubble : function(fn, scope, args){
23632         var p = this;
23633         while(p){
23634             if(fn.call(scope || p, args || p) === false){
23635                 break;
23636             }
23637             p = p.parentNode;
23638         }
23639     },
23640
23641     /**
23642      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23643      * function call will be the scope provided or the current node. The arguments to the function
23644      * will be the args provided or the current node. If the function returns false at any point,
23645      * the cascade is stopped on that branch.
23646      * @param {Function} fn The function to call
23647      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23648      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23649      */
23650     cascade : function(fn, scope, args){
23651         if(fn.call(scope || this, args || this) !== false){
23652             var cs = this.childNodes;
23653             for(var i = 0, len = cs.length; i < len; i++) {
23654                 cs[i].cascade(fn, scope, args);
23655             }
23656         }
23657     },
23658
23659     /**
23660      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23661      * function call will be the scope provided or the current node. The arguments to the function
23662      * will be the args provided or the current node. If the function returns false at any point,
23663      * the iteration stops.
23664      * @param {Function} fn The function to call
23665      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23666      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23667      */
23668     eachChild : function(fn, scope, args){
23669         var cs = this.childNodes;
23670         for(var i = 0, len = cs.length; i < len; i++) {
23671                 if(fn.call(scope || this, args || cs[i]) === false){
23672                     break;
23673                 }
23674         }
23675     },
23676
23677     /**
23678      * Finds the first child that has the attribute with the specified value.
23679      * @param {String} attribute The attribute name
23680      * @param {Mixed} value The value to search for
23681      * @return {Node} The found child or null if none was found
23682      */
23683     findChild : function(attribute, value){
23684         var cs = this.childNodes;
23685         for(var i = 0, len = cs.length; i < len; i++) {
23686                 if(cs[i].attributes[attribute] == value){
23687                     return cs[i];
23688                 }
23689         }
23690         return null;
23691     },
23692
23693     /**
23694      * Finds the first child by a custom function. The child matches if the function passed
23695      * returns true.
23696      * @param {Function} fn
23697      * @param {Object} scope (optional)
23698      * @return {Node} The found child or null if none was found
23699      */
23700     findChildBy : function(fn, scope){
23701         var cs = this.childNodes;
23702         for(var i = 0, len = cs.length; i < len; i++) {
23703                 if(fn.call(scope||cs[i], cs[i]) === true){
23704                     return cs[i];
23705                 }
23706         }
23707         return null;
23708     },
23709
23710     /**
23711      * Sorts this nodes children using the supplied sort function
23712      * @param {Function} fn
23713      * @param {Object} scope (optional)
23714      */
23715     sort : function(fn, scope){
23716         var cs = this.childNodes;
23717         var len = cs.length;
23718         if(len > 0){
23719             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23720             cs.sort(sortFn);
23721             for(var i = 0; i < len; i++){
23722                 var n = cs[i];
23723                 n.previousSibling = cs[i-1];
23724                 n.nextSibling = cs[i+1];
23725                 if(i == 0){
23726                     this.setFirstChild(n);
23727                 }
23728                 if(i == len-1){
23729                     this.setLastChild(n);
23730                 }
23731             }
23732         }
23733     },
23734
23735     /**
23736      * Returns true if this node is an ancestor (at any point) of the passed node.
23737      * @param {Node} node
23738      * @return {Boolean}
23739      */
23740     contains : function(node){
23741         return node.isAncestor(this);
23742     },
23743
23744     /**
23745      * Returns true if the passed node is an ancestor (at any point) of this node.
23746      * @param {Node} node
23747      * @return {Boolean}
23748      */
23749     isAncestor : function(node){
23750         var p = this.parentNode;
23751         while(p){
23752             if(p == node){
23753                 return true;
23754             }
23755             p = p.parentNode;
23756         }
23757         return false;
23758     },
23759
23760     toString : function(){
23761         return "[Node"+(this.id?" "+this.id:"")+"]";
23762     }
23763 });/*
23764  * Based on:
23765  * Ext JS Library 1.1.1
23766  * Copyright(c) 2006-2007, Ext JS, LLC.
23767  *
23768  * Originally Released Under LGPL - original licence link has changed is not relivant.
23769  *
23770  * Fork - LGPL
23771  * <script type="text/javascript">
23772  */
23773  (function(){ 
23774 /**
23775  * @class Roo.Layer
23776  * @extends Roo.Element
23777  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23778  * automatic maintaining of shadow/shim positions.
23779  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23780  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23781  * you can pass a string with a CSS class name. False turns off the shadow.
23782  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23783  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23784  * @cfg {String} cls CSS class to add to the element
23785  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23786  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23787  * @constructor
23788  * @param {Object} config An object with config options.
23789  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23790  */
23791
23792 Roo.Layer = function(config, existingEl){
23793     config = config || {};
23794     var dh = Roo.DomHelper;
23795     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23796     if(existingEl){
23797         this.dom = Roo.getDom(existingEl);
23798     }
23799     if(!this.dom){
23800         var o = config.dh || {tag: "div", cls: "x-layer"};
23801         this.dom = dh.append(pel, o);
23802     }
23803     if(config.cls){
23804         this.addClass(config.cls);
23805     }
23806     this.constrain = config.constrain !== false;
23807     this.visibilityMode = Roo.Element.VISIBILITY;
23808     if(config.id){
23809         this.id = this.dom.id = config.id;
23810     }else{
23811         this.id = Roo.id(this.dom);
23812     }
23813     this.zindex = config.zindex || this.getZIndex();
23814     this.position("absolute", this.zindex);
23815     if(config.shadow){
23816         this.shadowOffset = config.shadowOffset || 4;
23817         this.shadow = new Roo.Shadow({
23818             offset : this.shadowOffset,
23819             mode : config.shadow
23820         });
23821     }else{
23822         this.shadowOffset = 0;
23823     }
23824     this.useShim = config.shim !== false && Roo.useShims;
23825     this.useDisplay = config.useDisplay;
23826     this.hide();
23827 };
23828
23829 var supr = Roo.Element.prototype;
23830
23831 // shims are shared among layer to keep from having 100 iframes
23832 var shims = [];
23833
23834 Roo.extend(Roo.Layer, Roo.Element, {
23835
23836     getZIndex : function(){
23837         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23838     },
23839
23840     getShim : function(){
23841         if(!this.useShim){
23842             return null;
23843         }
23844         if(this.shim){
23845             return this.shim;
23846         }
23847         var shim = shims.shift();
23848         if(!shim){
23849             shim = this.createShim();
23850             shim.enableDisplayMode('block');
23851             shim.dom.style.display = 'none';
23852             shim.dom.style.visibility = 'visible';
23853         }
23854         var pn = this.dom.parentNode;
23855         if(shim.dom.parentNode != pn){
23856             pn.insertBefore(shim.dom, this.dom);
23857         }
23858         shim.setStyle('z-index', this.getZIndex()-2);
23859         this.shim = shim;
23860         return shim;
23861     },
23862
23863     hideShim : function(){
23864         if(this.shim){
23865             this.shim.setDisplayed(false);
23866             shims.push(this.shim);
23867             delete this.shim;
23868         }
23869     },
23870
23871     disableShadow : function(){
23872         if(this.shadow){
23873             this.shadowDisabled = true;
23874             this.shadow.hide();
23875             this.lastShadowOffset = this.shadowOffset;
23876             this.shadowOffset = 0;
23877         }
23878     },
23879
23880     enableShadow : function(show){
23881         if(this.shadow){
23882             this.shadowDisabled = false;
23883             this.shadowOffset = this.lastShadowOffset;
23884             delete this.lastShadowOffset;
23885             if(show){
23886                 this.sync(true);
23887             }
23888         }
23889     },
23890
23891     // private
23892     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23893     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23894     sync : function(doShow){
23895         var sw = this.shadow;
23896         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23897             var sh = this.getShim();
23898
23899             var w = this.getWidth(),
23900                 h = this.getHeight();
23901
23902             var l = this.getLeft(true),
23903                 t = this.getTop(true);
23904
23905             if(sw && !this.shadowDisabled){
23906                 if(doShow && !sw.isVisible()){
23907                     sw.show(this);
23908                 }else{
23909                     sw.realign(l, t, w, h);
23910                 }
23911                 if(sh){
23912                     if(doShow){
23913                        sh.show();
23914                     }
23915                     // fit the shim behind the shadow, so it is shimmed too
23916                     var a = sw.adjusts, s = sh.dom.style;
23917                     s.left = (Math.min(l, l+a.l))+"px";
23918                     s.top = (Math.min(t, t+a.t))+"px";
23919                     s.width = (w+a.w)+"px";
23920                     s.height = (h+a.h)+"px";
23921                 }
23922             }else if(sh){
23923                 if(doShow){
23924                    sh.show();
23925                 }
23926                 sh.setSize(w, h);
23927                 sh.setLeftTop(l, t);
23928             }
23929             
23930         }
23931     },
23932
23933     // private
23934     destroy : function(){
23935         this.hideShim();
23936         if(this.shadow){
23937             this.shadow.hide();
23938         }
23939         this.removeAllListeners();
23940         var pn = this.dom.parentNode;
23941         if(pn){
23942             pn.removeChild(this.dom);
23943         }
23944         Roo.Element.uncache(this.id);
23945     },
23946
23947     remove : function(){
23948         this.destroy();
23949     },
23950
23951     // private
23952     beginUpdate : function(){
23953         this.updating = true;
23954     },
23955
23956     // private
23957     endUpdate : function(){
23958         this.updating = false;
23959         this.sync(true);
23960     },
23961
23962     // private
23963     hideUnders : function(negOffset){
23964         if(this.shadow){
23965             this.shadow.hide();
23966         }
23967         this.hideShim();
23968     },
23969
23970     // private
23971     constrainXY : function(){
23972         if(this.constrain){
23973             var vw = Roo.lib.Dom.getViewWidth(),
23974                 vh = Roo.lib.Dom.getViewHeight();
23975             var s = Roo.get(document).getScroll();
23976
23977             var xy = this.getXY();
23978             var x = xy[0], y = xy[1];   
23979             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23980             // only move it if it needs it
23981             var moved = false;
23982             // first validate right/bottom
23983             if((x + w) > vw+s.left){
23984                 x = vw - w - this.shadowOffset;
23985                 moved = true;
23986             }
23987             if((y + h) > vh+s.top){
23988                 y = vh - h - this.shadowOffset;
23989                 moved = true;
23990             }
23991             // then make sure top/left isn't negative
23992             if(x < s.left){
23993                 x = s.left;
23994                 moved = true;
23995             }
23996             if(y < s.top){
23997                 y = s.top;
23998                 moved = true;
23999             }
24000             if(moved){
24001                 if(this.avoidY){
24002                     var ay = this.avoidY;
24003                     if(y <= ay && (y+h) >= ay){
24004                         y = ay-h-5;   
24005                     }
24006                 }
24007                 xy = [x, y];
24008                 this.storeXY(xy);
24009                 supr.setXY.call(this, xy);
24010                 this.sync();
24011             }
24012         }
24013     },
24014
24015     isVisible : function(){
24016         return this.visible;    
24017     },
24018
24019     // private
24020     showAction : function(){
24021         this.visible = true; // track visibility to prevent getStyle calls
24022         if(this.useDisplay === true){
24023             this.setDisplayed("");
24024         }else if(this.lastXY){
24025             supr.setXY.call(this, this.lastXY);
24026         }else if(this.lastLT){
24027             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
24028         }
24029     },
24030
24031     // private
24032     hideAction : function(){
24033         this.visible = false;
24034         if(this.useDisplay === true){
24035             this.setDisplayed(false);
24036         }else{
24037             this.setLeftTop(-10000,-10000);
24038         }
24039     },
24040
24041     // overridden Element method
24042     setVisible : function(v, a, d, c, e){
24043         if(v){
24044             this.showAction();
24045         }
24046         if(a && v){
24047             var cb = function(){
24048                 this.sync(true);
24049                 if(c){
24050                     c();
24051                 }
24052             }.createDelegate(this);
24053             supr.setVisible.call(this, true, true, d, cb, e);
24054         }else{
24055             if(!v){
24056                 this.hideUnders(true);
24057             }
24058             var cb = c;
24059             if(a){
24060                 cb = function(){
24061                     this.hideAction();
24062                     if(c){
24063                         c();
24064                     }
24065                 }.createDelegate(this);
24066             }
24067             supr.setVisible.call(this, v, a, d, cb, e);
24068             if(v){
24069                 this.sync(true);
24070             }else if(!a){
24071                 this.hideAction();
24072             }
24073         }
24074     },
24075
24076     storeXY : function(xy){
24077         delete this.lastLT;
24078         this.lastXY = xy;
24079     },
24080
24081     storeLeftTop : function(left, top){
24082         delete this.lastXY;
24083         this.lastLT = [left, top];
24084     },
24085
24086     // private
24087     beforeFx : function(){
24088         this.beforeAction();
24089         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24090     },
24091
24092     // private
24093     afterFx : function(){
24094         Roo.Layer.superclass.afterFx.apply(this, arguments);
24095         this.sync(this.isVisible());
24096     },
24097
24098     // private
24099     beforeAction : function(){
24100         if(!this.updating && this.shadow){
24101             this.shadow.hide();
24102         }
24103     },
24104
24105     // overridden Element method
24106     setLeft : function(left){
24107         this.storeLeftTop(left, this.getTop(true));
24108         supr.setLeft.apply(this, arguments);
24109         this.sync();
24110     },
24111
24112     setTop : function(top){
24113         this.storeLeftTop(this.getLeft(true), top);
24114         supr.setTop.apply(this, arguments);
24115         this.sync();
24116     },
24117
24118     setLeftTop : function(left, top){
24119         this.storeLeftTop(left, top);
24120         supr.setLeftTop.apply(this, arguments);
24121         this.sync();
24122     },
24123
24124     setXY : function(xy, a, d, c, e){
24125         this.fixDisplay();
24126         this.beforeAction();
24127         this.storeXY(xy);
24128         var cb = this.createCB(c);
24129         supr.setXY.call(this, xy, a, d, cb, e);
24130         if(!a){
24131             cb();
24132         }
24133     },
24134
24135     // private
24136     createCB : function(c){
24137         var el = this;
24138         return function(){
24139             el.constrainXY();
24140             el.sync(true);
24141             if(c){
24142                 c();
24143             }
24144         };
24145     },
24146
24147     // overridden Element method
24148     setX : function(x, a, d, c, e){
24149         this.setXY([x, this.getY()], a, d, c, e);
24150     },
24151
24152     // overridden Element method
24153     setY : function(y, a, d, c, e){
24154         this.setXY([this.getX(), y], a, d, c, e);
24155     },
24156
24157     // overridden Element method
24158     setSize : function(w, h, a, d, c, e){
24159         this.beforeAction();
24160         var cb = this.createCB(c);
24161         supr.setSize.call(this, w, h, a, d, cb, e);
24162         if(!a){
24163             cb();
24164         }
24165     },
24166
24167     // overridden Element method
24168     setWidth : function(w, a, d, c, e){
24169         this.beforeAction();
24170         var cb = this.createCB(c);
24171         supr.setWidth.call(this, w, a, d, cb, e);
24172         if(!a){
24173             cb();
24174         }
24175     },
24176
24177     // overridden Element method
24178     setHeight : function(h, a, d, c, e){
24179         this.beforeAction();
24180         var cb = this.createCB(c);
24181         supr.setHeight.call(this, h, a, d, cb, e);
24182         if(!a){
24183             cb();
24184         }
24185     },
24186
24187     // overridden Element method
24188     setBounds : function(x, y, w, h, a, d, c, e){
24189         this.beforeAction();
24190         var cb = this.createCB(c);
24191         if(!a){
24192             this.storeXY([x, y]);
24193             supr.setXY.call(this, [x, y]);
24194             supr.setSize.call(this, w, h, a, d, cb, e);
24195             cb();
24196         }else{
24197             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24198         }
24199         return this;
24200     },
24201     
24202     /**
24203      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24204      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24205      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24206      * @param {Number} zindex The new z-index to set
24207      * @return {this} The Layer
24208      */
24209     setZIndex : function(zindex){
24210         this.zindex = zindex;
24211         this.setStyle("z-index", zindex + 2);
24212         if(this.shadow){
24213             this.shadow.setZIndex(zindex + 1);
24214         }
24215         if(this.shim){
24216             this.shim.setStyle("z-index", zindex);
24217         }
24218     }
24219 });
24220 })();/*
24221  * Based on:
24222  * Ext JS Library 1.1.1
24223  * Copyright(c) 2006-2007, Ext JS, LLC.
24224  *
24225  * Originally Released Under LGPL - original licence link has changed is not relivant.
24226  *
24227  * Fork - LGPL
24228  * <script type="text/javascript">
24229  */
24230
24231
24232 /**
24233  * @class Roo.Shadow
24234  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24235  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24236  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24237  * @constructor
24238  * Create a new Shadow
24239  * @param {Object} config The config object
24240  */
24241 Roo.Shadow = function(config){
24242     Roo.apply(this, config);
24243     if(typeof this.mode != "string"){
24244         this.mode = this.defaultMode;
24245     }
24246     var o = this.offset, a = {h: 0};
24247     var rad = Math.floor(this.offset/2);
24248     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24249         case "drop":
24250             a.w = 0;
24251             a.l = a.t = o;
24252             a.t -= 1;
24253             if(Roo.isIE){
24254                 a.l -= this.offset + rad;
24255                 a.t -= this.offset + rad;
24256                 a.w -= rad;
24257                 a.h -= rad;
24258                 a.t += 1;
24259             }
24260         break;
24261         case "sides":
24262             a.w = (o*2);
24263             a.l = -o;
24264             a.t = o-1;
24265             if(Roo.isIE){
24266                 a.l -= (this.offset - rad);
24267                 a.t -= this.offset + rad;
24268                 a.l += 1;
24269                 a.w -= (this.offset - rad)*2;
24270                 a.w -= rad + 1;
24271                 a.h -= 1;
24272             }
24273         break;
24274         case "frame":
24275             a.w = a.h = (o*2);
24276             a.l = a.t = -o;
24277             a.t += 1;
24278             a.h -= 2;
24279             if(Roo.isIE){
24280                 a.l -= (this.offset - rad);
24281                 a.t -= (this.offset - rad);
24282                 a.l += 1;
24283                 a.w -= (this.offset + rad + 1);
24284                 a.h -= (this.offset + rad);
24285                 a.h += 1;
24286             }
24287         break;
24288     };
24289
24290     this.adjusts = a;
24291 };
24292
24293 Roo.Shadow.prototype = {
24294     /**
24295      * @cfg {String} mode
24296      * The shadow display mode.  Supports the following options:<br />
24297      * sides: Shadow displays on both sides and bottom only<br />
24298      * frame: Shadow displays equally on all four sides<br />
24299      * drop: Traditional bottom-right drop shadow (default)
24300      */
24301     /**
24302      * @cfg {String} offset
24303      * The number of pixels to offset the shadow from the element (defaults to 4)
24304      */
24305     offset: 4,
24306
24307     // private
24308     defaultMode: "drop",
24309
24310     /**
24311      * Displays the shadow under the target element
24312      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24313      */
24314     show : function(target){
24315         target = Roo.get(target);
24316         if(!this.el){
24317             this.el = Roo.Shadow.Pool.pull();
24318             if(this.el.dom.nextSibling != target.dom){
24319                 this.el.insertBefore(target);
24320             }
24321         }
24322         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24323         if(Roo.isIE){
24324             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24325         }
24326         this.realign(
24327             target.getLeft(true),
24328             target.getTop(true),
24329             target.getWidth(),
24330             target.getHeight()
24331         );
24332         this.el.dom.style.display = "block";
24333     },
24334
24335     /**
24336      * Returns true if the shadow is visible, else false
24337      */
24338     isVisible : function(){
24339         return this.el ? true : false;  
24340     },
24341
24342     /**
24343      * Direct alignment when values are already available. Show must be called at least once before
24344      * calling this method to ensure it is initialized.
24345      * @param {Number} left The target element left position
24346      * @param {Number} top The target element top position
24347      * @param {Number} width The target element width
24348      * @param {Number} height The target element height
24349      */
24350     realign : function(l, t, w, h){
24351         if(!this.el){
24352             return;
24353         }
24354         var a = this.adjusts, d = this.el.dom, s = d.style;
24355         var iea = 0;
24356         s.left = (l+a.l)+"px";
24357         s.top = (t+a.t)+"px";
24358         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24359  
24360         if(s.width != sws || s.height != shs){
24361             s.width = sws;
24362             s.height = shs;
24363             if(!Roo.isIE){
24364                 var cn = d.childNodes;
24365                 var sww = Math.max(0, (sw-12))+"px";
24366                 cn[0].childNodes[1].style.width = sww;
24367                 cn[1].childNodes[1].style.width = sww;
24368                 cn[2].childNodes[1].style.width = sww;
24369                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24370             }
24371         }
24372     },
24373
24374     /**
24375      * Hides this shadow
24376      */
24377     hide : function(){
24378         if(this.el){
24379             this.el.dom.style.display = "none";
24380             Roo.Shadow.Pool.push(this.el);
24381             delete this.el;
24382         }
24383     },
24384
24385     /**
24386      * Adjust the z-index of this shadow
24387      * @param {Number} zindex The new z-index
24388      */
24389     setZIndex : function(z){
24390         this.zIndex = z;
24391         if(this.el){
24392             this.el.setStyle("z-index", z);
24393         }
24394     }
24395 };
24396
24397 // Private utility class that manages the internal Shadow cache
24398 Roo.Shadow.Pool = function(){
24399     var p = [];
24400     var markup = Roo.isIE ?
24401                  '<div class="x-ie-shadow"></div>' :
24402                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
24403     return {
24404         pull : function(){
24405             var sh = p.shift();
24406             if(!sh){
24407                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24408                 sh.autoBoxAdjust = false;
24409             }
24410             return sh;
24411         },
24412
24413         push : function(sh){
24414             p.push(sh);
24415         }
24416     };
24417 }();/*
24418  * Based on:
24419  * Ext JS Library 1.1.1
24420  * Copyright(c) 2006-2007, Ext JS, LLC.
24421  *
24422  * Originally Released Under LGPL - original licence link has changed is not relivant.
24423  *
24424  * Fork - LGPL
24425  * <script type="text/javascript">
24426  */
24427
24428
24429 /**
24430  * @class Roo.SplitBar
24431  * @extends Roo.util.Observable
24432  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24433  * <br><br>
24434  * Usage:
24435  * <pre><code>
24436 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24437                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24438 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24439 split.minSize = 100;
24440 split.maxSize = 600;
24441 split.animate = true;
24442 split.on('moved', splitterMoved);
24443 </code></pre>
24444  * @constructor
24445  * Create a new SplitBar
24446  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24447  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24448  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24449  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24450                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24451                         position of the SplitBar).
24452  */
24453 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24454     
24455     /** @private */
24456     this.el = Roo.get(dragElement, true);
24457     this.el.dom.unselectable = "on";
24458     /** @private */
24459     this.resizingEl = Roo.get(resizingElement, true);
24460
24461     /**
24462      * @private
24463      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24464      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24465      * @type Number
24466      */
24467     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24468     
24469     /**
24470      * The minimum size of the resizing element. (Defaults to 0)
24471      * @type Number
24472      */
24473     this.minSize = 0;
24474     
24475     /**
24476      * The maximum size of the resizing element. (Defaults to 2000)
24477      * @type Number
24478      */
24479     this.maxSize = 2000;
24480     
24481     /**
24482      * Whether to animate the transition to the new size
24483      * @type Boolean
24484      */
24485     this.animate = false;
24486     
24487     /**
24488      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24489      * @type Boolean
24490      */
24491     this.useShim = false;
24492     
24493     /** @private */
24494     this.shim = null;
24495     
24496     if(!existingProxy){
24497         /** @private */
24498         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24499     }else{
24500         this.proxy = Roo.get(existingProxy).dom;
24501     }
24502     /** @private */
24503     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24504     
24505     /** @private */
24506     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24507     
24508     /** @private */
24509     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24510     
24511     /** @private */
24512     this.dragSpecs = {};
24513     
24514     /**
24515      * @private The adapter to use to positon and resize elements
24516      */
24517     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24518     this.adapter.init(this);
24519     
24520     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24521         /** @private */
24522         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24523         this.el.addClass("x-splitbar-h");
24524     }else{
24525         /** @private */
24526         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24527         this.el.addClass("x-splitbar-v");
24528     }
24529     
24530     this.addEvents({
24531         /**
24532          * @event resize
24533          * Fires when the splitter is moved (alias for {@link #event-moved})
24534          * @param {Roo.SplitBar} this
24535          * @param {Number} newSize the new width or height
24536          */
24537         "resize" : true,
24538         /**
24539          * @event moved
24540          * Fires when the splitter is moved
24541          * @param {Roo.SplitBar} this
24542          * @param {Number} newSize the new width or height
24543          */
24544         "moved" : true,
24545         /**
24546          * @event beforeresize
24547          * Fires before the splitter is dragged
24548          * @param {Roo.SplitBar} this
24549          */
24550         "beforeresize" : true,
24551
24552         "beforeapply" : true
24553     });
24554
24555     Roo.util.Observable.call(this);
24556 };
24557
24558 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24559     onStartProxyDrag : function(x, y){
24560         this.fireEvent("beforeresize", this);
24561         if(!this.overlay){
24562             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24563             o.unselectable();
24564             o.enableDisplayMode("block");
24565             // all splitbars share the same overlay
24566             Roo.SplitBar.prototype.overlay = o;
24567         }
24568         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24569         this.overlay.show();
24570         Roo.get(this.proxy).setDisplayed("block");
24571         var size = this.adapter.getElementSize(this);
24572         this.activeMinSize = this.getMinimumSize();;
24573         this.activeMaxSize = this.getMaximumSize();;
24574         var c1 = size - this.activeMinSize;
24575         var c2 = Math.max(this.activeMaxSize - size, 0);
24576         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24577             this.dd.resetConstraints();
24578             this.dd.setXConstraint(
24579                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24580                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24581             );
24582             this.dd.setYConstraint(0, 0);
24583         }else{
24584             this.dd.resetConstraints();
24585             this.dd.setXConstraint(0, 0);
24586             this.dd.setYConstraint(
24587                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24588                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24589             );
24590          }
24591         this.dragSpecs.startSize = size;
24592         this.dragSpecs.startPoint = [x, y];
24593         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24594     },
24595     
24596     /** 
24597      * @private Called after the drag operation by the DDProxy
24598      */
24599     onEndProxyDrag : function(e){
24600         Roo.get(this.proxy).setDisplayed(false);
24601         var endPoint = Roo.lib.Event.getXY(e);
24602         if(this.overlay){
24603             this.overlay.hide();
24604         }
24605         var newSize;
24606         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24607             newSize = this.dragSpecs.startSize + 
24608                 (this.placement == Roo.SplitBar.LEFT ?
24609                     endPoint[0] - this.dragSpecs.startPoint[0] :
24610                     this.dragSpecs.startPoint[0] - endPoint[0]
24611                 );
24612         }else{
24613             newSize = this.dragSpecs.startSize + 
24614                 (this.placement == Roo.SplitBar.TOP ?
24615                     endPoint[1] - this.dragSpecs.startPoint[1] :
24616                     this.dragSpecs.startPoint[1] - endPoint[1]
24617                 );
24618         }
24619         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24620         if(newSize != this.dragSpecs.startSize){
24621             if(this.fireEvent('beforeapply', this, newSize) !== false){
24622                 this.adapter.setElementSize(this, newSize);
24623                 this.fireEvent("moved", this, newSize);
24624                 this.fireEvent("resize", this, newSize);
24625             }
24626         }
24627     },
24628     
24629     /**
24630      * Get the adapter this SplitBar uses
24631      * @return The adapter object
24632      */
24633     getAdapter : function(){
24634         return this.adapter;
24635     },
24636     
24637     /**
24638      * Set the adapter this SplitBar uses
24639      * @param {Object} adapter A SplitBar adapter object
24640      */
24641     setAdapter : function(adapter){
24642         this.adapter = adapter;
24643         this.adapter.init(this);
24644     },
24645     
24646     /**
24647      * Gets the minimum size for the resizing element
24648      * @return {Number} The minimum size
24649      */
24650     getMinimumSize : function(){
24651         return this.minSize;
24652     },
24653     
24654     /**
24655      * Sets the minimum size for the resizing element
24656      * @param {Number} minSize The minimum size
24657      */
24658     setMinimumSize : function(minSize){
24659         this.minSize = minSize;
24660     },
24661     
24662     /**
24663      * Gets the maximum size for the resizing element
24664      * @return {Number} The maximum size
24665      */
24666     getMaximumSize : function(){
24667         return this.maxSize;
24668     },
24669     
24670     /**
24671      * Sets the maximum size for the resizing element
24672      * @param {Number} maxSize The maximum size
24673      */
24674     setMaximumSize : function(maxSize){
24675         this.maxSize = maxSize;
24676     },
24677     
24678     /**
24679      * Sets the initialize size for the resizing element
24680      * @param {Number} size The initial size
24681      */
24682     setCurrentSize : function(size){
24683         var oldAnimate = this.animate;
24684         this.animate = false;
24685         this.adapter.setElementSize(this, size);
24686         this.animate = oldAnimate;
24687     },
24688     
24689     /**
24690      * Destroy this splitbar. 
24691      * @param {Boolean} removeEl True to remove the element
24692      */
24693     destroy : function(removeEl){
24694         if(this.shim){
24695             this.shim.remove();
24696         }
24697         this.dd.unreg();
24698         this.proxy.parentNode.removeChild(this.proxy);
24699         if(removeEl){
24700             this.el.remove();
24701         }
24702     }
24703 });
24704
24705 /**
24706  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
24707  */
24708 Roo.SplitBar.createProxy = function(dir){
24709     var proxy = new Roo.Element(document.createElement("div"));
24710     proxy.unselectable();
24711     var cls = 'x-splitbar-proxy';
24712     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24713     document.body.appendChild(proxy.dom);
24714     return proxy.dom;
24715 };
24716
24717 /** 
24718  * @class Roo.SplitBar.BasicLayoutAdapter
24719  * Default Adapter. It assumes the splitter and resizing element are not positioned
24720  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24721  */
24722 Roo.SplitBar.BasicLayoutAdapter = function(){
24723 };
24724
24725 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24726     // do nothing for now
24727     init : function(s){
24728     
24729     },
24730     /**
24731      * Called before drag operations to get the current size of the resizing element. 
24732      * @param {Roo.SplitBar} s The SplitBar using this adapter
24733      */
24734      getElementSize : function(s){
24735         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24736             return s.resizingEl.getWidth();
24737         }else{
24738             return s.resizingEl.getHeight();
24739         }
24740     },
24741     
24742     /**
24743      * Called after drag operations to set the size of the resizing element.
24744      * @param {Roo.SplitBar} s The SplitBar using this adapter
24745      * @param {Number} newSize The new size to set
24746      * @param {Function} onComplete A function to be invoked when resizing is complete
24747      */
24748     setElementSize : function(s, newSize, onComplete){
24749         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24750             if(!s.animate){
24751                 s.resizingEl.setWidth(newSize);
24752                 if(onComplete){
24753                     onComplete(s, newSize);
24754                 }
24755             }else{
24756                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24757             }
24758         }else{
24759             
24760             if(!s.animate){
24761                 s.resizingEl.setHeight(newSize);
24762                 if(onComplete){
24763                     onComplete(s, newSize);
24764                 }
24765             }else{
24766                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24767             }
24768         }
24769     }
24770 };
24771
24772 /** 
24773  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24774  * @extends Roo.SplitBar.BasicLayoutAdapter
24775  * Adapter that  moves the splitter element to align with the resized sizing element. 
24776  * Used with an absolute positioned SplitBar.
24777  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24778  * document.body, make sure you assign an id to the body element.
24779  */
24780 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24781     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24782     this.container = Roo.get(container);
24783 };
24784
24785 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24786     init : function(s){
24787         this.basic.init(s);
24788     },
24789     
24790     getElementSize : function(s){
24791         return this.basic.getElementSize(s);
24792     },
24793     
24794     setElementSize : function(s, newSize, onComplete){
24795         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24796     },
24797     
24798     moveSplitter : function(s){
24799         var yes = Roo.SplitBar;
24800         switch(s.placement){
24801             case yes.LEFT:
24802                 s.el.setX(s.resizingEl.getRight());
24803                 break;
24804             case yes.RIGHT:
24805                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24806                 break;
24807             case yes.TOP:
24808                 s.el.setY(s.resizingEl.getBottom());
24809                 break;
24810             case yes.BOTTOM:
24811                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24812                 break;
24813         }
24814     }
24815 };
24816
24817 /**
24818  * Orientation constant - Create a vertical SplitBar
24819  * @static
24820  * @type Number
24821  */
24822 Roo.SplitBar.VERTICAL = 1;
24823
24824 /**
24825  * Orientation constant - Create a horizontal SplitBar
24826  * @static
24827  * @type Number
24828  */
24829 Roo.SplitBar.HORIZONTAL = 2;
24830
24831 /**
24832  * Placement constant - The resizing element is to the left of the splitter element
24833  * @static
24834  * @type Number
24835  */
24836 Roo.SplitBar.LEFT = 1;
24837
24838 /**
24839  * Placement constant - The resizing element is to the right of the splitter element
24840  * @static
24841  * @type Number
24842  */
24843 Roo.SplitBar.RIGHT = 2;
24844
24845 /**
24846  * Placement constant - The resizing element is positioned above the splitter element
24847  * @static
24848  * @type Number
24849  */
24850 Roo.SplitBar.TOP = 3;
24851
24852 /**
24853  * Placement constant - The resizing element is positioned under splitter element
24854  * @static
24855  * @type Number
24856  */
24857 Roo.SplitBar.BOTTOM = 4;
24858 /*
24859  * Based on:
24860  * Ext JS Library 1.1.1
24861  * Copyright(c) 2006-2007, Ext JS, LLC.
24862  *
24863  * Originally Released Under LGPL - original licence link has changed is not relivant.
24864  *
24865  * Fork - LGPL
24866  * <script type="text/javascript">
24867  */
24868
24869 /**
24870  * @class Roo.View
24871  * @extends Roo.util.Observable
24872  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24873  * This class also supports single and multi selection modes. <br>
24874  * Create a data model bound view:
24875  <pre><code>
24876  var store = new Roo.data.Store(...);
24877
24878  var view = new Roo.View({
24879     el : "my-element",
24880     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24881  
24882     singleSelect: true,
24883     selectedClass: "ydataview-selected",
24884     store: store
24885  });
24886
24887  // listen for node click?
24888  view.on("click", function(vw, index, node, e){
24889  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24890  });
24891
24892  // load XML data
24893  dataModel.load("foobar.xml");
24894  </code></pre>
24895  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24896  * <br><br>
24897  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24898  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24899  * 
24900  * Note: old style constructor is still suported (container, template, config)
24901  * 
24902  * @constructor
24903  * Create a new View
24904  * @param {Object} config The config object
24905  * 
24906  */
24907 Roo.View = function(config, depreciated_tpl, depreciated_config){
24908     
24909     this.parent = false;
24910     
24911     if (typeof(depreciated_tpl) == 'undefined') {
24912         // new way.. - universal constructor.
24913         Roo.apply(this, config);
24914         this.el  = Roo.get(this.el);
24915     } else {
24916         // old format..
24917         this.el  = Roo.get(config);
24918         this.tpl = depreciated_tpl;
24919         Roo.apply(this, depreciated_config);
24920     }
24921     this.wrapEl  = this.el.wrap().wrap();
24922     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24923     
24924     
24925     if(typeof(this.tpl) == "string"){
24926         this.tpl = new Roo.Template(this.tpl);
24927     } else {
24928         // support xtype ctors..
24929         this.tpl = new Roo.factory(this.tpl, Roo);
24930     }
24931     
24932     
24933     this.tpl.compile();
24934     
24935     /** @private */
24936     this.addEvents({
24937         /**
24938          * @event beforeclick
24939          * Fires before a click is processed. Returns false to cancel the default action.
24940          * @param {Roo.View} this
24941          * @param {Number} index The index of the target node
24942          * @param {HTMLElement} node The target node
24943          * @param {Roo.EventObject} e The raw event object
24944          */
24945             "beforeclick" : true,
24946         /**
24947          * @event click
24948          * Fires when a template node is clicked.
24949          * @param {Roo.View} this
24950          * @param {Number} index The index of the target node
24951          * @param {HTMLElement} node The target node
24952          * @param {Roo.EventObject} e The raw event object
24953          */
24954             "click" : true,
24955         /**
24956          * @event dblclick
24957          * Fires when a template node is double clicked.
24958          * @param {Roo.View} this
24959          * @param {Number} index The index of the target node
24960          * @param {HTMLElement} node The target node
24961          * @param {Roo.EventObject} e The raw event object
24962          */
24963             "dblclick" : true,
24964         /**
24965          * @event contextmenu
24966          * Fires when a template node is right clicked.
24967          * @param {Roo.View} this
24968          * @param {Number} index The index of the target node
24969          * @param {HTMLElement} node The target node
24970          * @param {Roo.EventObject} e The raw event object
24971          */
24972             "contextmenu" : true,
24973         /**
24974          * @event selectionchange
24975          * Fires when the selected nodes change.
24976          * @param {Roo.View} this
24977          * @param {Array} selections Array of the selected nodes
24978          */
24979             "selectionchange" : true,
24980     
24981         /**
24982          * @event beforeselect
24983          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24984          * @param {Roo.View} this
24985          * @param {HTMLElement} node The node to be selected
24986          * @param {Array} selections Array of currently selected nodes
24987          */
24988             "beforeselect" : true,
24989         /**
24990          * @event preparedata
24991          * Fires on every row to render, to allow you to change the data.
24992          * @param {Roo.View} this
24993          * @param {Object} data to be rendered (change this)
24994          */
24995           "preparedata" : true
24996           
24997           
24998         });
24999
25000
25001
25002     this.el.on({
25003         "click": this.onClick,
25004         "dblclick": this.onDblClick,
25005         "contextmenu": this.onContextMenu,
25006         scope:this
25007     });
25008
25009     this.selections = [];
25010     this.nodes = [];
25011     this.cmp = new Roo.CompositeElementLite([]);
25012     if(this.store){
25013         this.store = Roo.factory(this.store, Roo.data);
25014         this.setStore(this.store, true);
25015     }
25016     
25017     if ( this.footer && this.footer.xtype) {
25018            
25019          var fctr = this.wrapEl.appendChild(document.createElement("div"));
25020         
25021         this.footer.dataSource = this.store;
25022         this.footer.container = fctr;
25023         this.footer = Roo.factory(this.footer, Roo);
25024         fctr.insertFirst(this.el);
25025         
25026         // this is a bit insane - as the paging toolbar seems to detach the el..
25027 //        dom.parentNode.parentNode.parentNode
25028          // they get detached?
25029     }
25030     
25031     
25032     Roo.View.superclass.constructor.call(this);
25033     
25034     
25035 };
25036
25037 Roo.extend(Roo.View, Roo.util.Observable, {
25038     
25039      /**
25040      * @cfg {Roo.data.Store} store Data store to load data from.
25041      */
25042     store : false,
25043     
25044     /**
25045      * @cfg {String|Roo.Element} el The container element.
25046      */
25047     el : '',
25048     
25049     /**
25050      * @cfg {String|Roo.Template} tpl The template used by this View 
25051      */
25052     tpl : false,
25053     /**
25054      * @cfg {String} dataName the named area of the template to use as the data area
25055      *                          Works with domtemplates roo-name="name"
25056      */
25057     dataName: false,
25058     /**
25059      * @cfg {String} selectedClass The css class to add to selected nodes
25060      */
25061     selectedClass : "x-view-selected",
25062      /**
25063      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25064      */
25065     emptyText : "",
25066     
25067     /**
25068      * @cfg {String} text to display on mask (default Loading)
25069      */
25070     mask : false,
25071     /**
25072      * @cfg {Boolean} multiSelect Allow multiple selection
25073      */
25074     multiSelect : false,
25075     /**
25076      * @cfg {Boolean} singleSelect Allow single selection
25077      */
25078     singleSelect:  false,
25079     
25080     /**
25081      * @cfg {Boolean} toggleSelect - selecting 
25082      */
25083     toggleSelect : false,
25084     
25085     /**
25086      * @cfg {Boolean} tickable - selecting 
25087      */
25088     tickable : false,
25089     
25090     /**
25091      * Returns the element this view is bound to.
25092      * @return {Roo.Element}
25093      */
25094     getEl : function(){
25095         return this.wrapEl;
25096     },
25097     
25098     
25099
25100     /**
25101      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25102      */
25103     refresh : function(){
25104         //Roo.log('refresh');
25105         var t = this.tpl;
25106         
25107         // if we are using something like 'domtemplate', then
25108         // the what gets used is:
25109         // t.applySubtemplate(NAME, data, wrapping data..)
25110         // the outer template then get' applied with
25111         //     the store 'extra data'
25112         // and the body get's added to the
25113         //      roo-name="data" node?
25114         //      <span class='roo-tpl-{name}'></span> ?????
25115         
25116         
25117         
25118         this.clearSelections();
25119         this.el.update("");
25120         var html = [];
25121         var records = this.store.getRange();
25122         if(records.length < 1) {
25123             
25124             // is this valid??  = should it render a template??
25125             
25126             this.el.update(this.emptyText);
25127             return;
25128         }
25129         var el = this.el;
25130         if (this.dataName) {
25131             this.el.update(t.apply(this.store.meta)); //????
25132             el = this.el.child('.roo-tpl-' + this.dataName);
25133         }
25134         
25135         for(var i = 0, len = records.length; i < len; i++){
25136             var data = this.prepareData(records[i].data, i, records[i]);
25137             this.fireEvent("preparedata", this, data, i, records[i]);
25138             
25139             var d = Roo.apply({}, data);
25140             
25141             if(this.tickable){
25142                 Roo.apply(d, {'roo-id' : Roo.id()});
25143                 
25144                 var _this = this;
25145             
25146                 Roo.each(this.parent.item, function(item){
25147                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25148                         return;
25149                     }
25150                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25151                 });
25152             }
25153             
25154             html[html.length] = Roo.util.Format.trim(
25155                 this.dataName ?
25156                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25157                     t.apply(d)
25158             );
25159         }
25160         
25161         
25162         
25163         el.update(html.join(""));
25164         this.nodes = el.dom.childNodes;
25165         this.updateIndexes(0);
25166     },
25167     
25168
25169     /**
25170      * Function to override to reformat the data that is sent to
25171      * the template for each node.
25172      * DEPRICATED - use the preparedata event handler.
25173      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25174      * a JSON object for an UpdateManager bound view).
25175      */
25176     prepareData : function(data, index, record)
25177     {
25178         this.fireEvent("preparedata", this, data, index, record);
25179         return data;
25180     },
25181
25182     onUpdate : function(ds, record){
25183         // Roo.log('on update');   
25184         this.clearSelections();
25185         var index = this.store.indexOf(record);
25186         var n = this.nodes[index];
25187         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25188         n.parentNode.removeChild(n);
25189         this.updateIndexes(index, index);
25190     },
25191
25192     
25193     
25194 // --------- FIXME     
25195     onAdd : function(ds, records, index)
25196     {
25197         //Roo.log(['on Add', ds, records, index] );        
25198         this.clearSelections();
25199         if(this.nodes.length == 0){
25200             this.refresh();
25201             return;
25202         }
25203         var n = this.nodes[index];
25204         for(var i = 0, len = records.length; i < len; i++){
25205             var d = this.prepareData(records[i].data, i, records[i]);
25206             if(n){
25207                 this.tpl.insertBefore(n, d);
25208             }else{
25209                 
25210                 this.tpl.append(this.el, d);
25211             }
25212         }
25213         this.updateIndexes(index);
25214     },
25215
25216     onRemove : function(ds, record, index){
25217        // Roo.log('onRemove');
25218         this.clearSelections();
25219         var el = this.dataName  ?
25220             this.el.child('.roo-tpl-' + this.dataName) :
25221             this.el; 
25222         
25223         el.dom.removeChild(this.nodes[index]);
25224         this.updateIndexes(index);
25225     },
25226
25227     /**
25228      * Refresh an individual node.
25229      * @param {Number} index
25230      */
25231     refreshNode : function(index){
25232         this.onUpdate(this.store, this.store.getAt(index));
25233     },
25234
25235     updateIndexes : function(startIndex, endIndex){
25236         var ns = this.nodes;
25237         startIndex = startIndex || 0;
25238         endIndex = endIndex || ns.length - 1;
25239         for(var i = startIndex; i <= endIndex; i++){
25240             ns[i].nodeIndex = i;
25241         }
25242     },
25243
25244     /**
25245      * Changes the data store this view uses and refresh the view.
25246      * @param {Store} store
25247      */
25248     setStore : function(store, initial){
25249         if(!initial && this.store){
25250             this.store.un("datachanged", this.refresh);
25251             this.store.un("add", this.onAdd);
25252             this.store.un("remove", this.onRemove);
25253             this.store.un("update", this.onUpdate);
25254             this.store.un("clear", this.refresh);
25255             this.store.un("beforeload", this.onBeforeLoad);
25256             this.store.un("load", this.onLoad);
25257             this.store.un("loadexception", this.onLoad);
25258         }
25259         if(store){
25260           
25261             store.on("datachanged", this.refresh, this);
25262             store.on("add", this.onAdd, this);
25263             store.on("remove", this.onRemove, this);
25264             store.on("update", this.onUpdate, this);
25265             store.on("clear", this.refresh, this);
25266             store.on("beforeload", this.onBeforeLoad, this);
25267             store.on("load", this.onLoad, this);
25268             store.on("loadexception", this.onLoad, this);
25269         }
25270         
25271         if(store){
25272             this.refresh();
25273         }
25274     },
25275     /**
25276      * onbeforeLoad - masks the loading area.
25277      *
25278      */
25279     onBeforeLoad : function(store,opts)
25280     {
25281          //Roo.log('onBeforeLoad');   
25282         if (!opts.add) {
25283             this.el.update("");
25284         }
25285         this.el.mask(this.mask ? this.mask : "Loading" ); 
25286     },
25287     onLoad : function ()
25288     {
25289         this.el.unmask();
25290     },
25291     
25292
25293     /**
25294      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25295      * @param {HTMLElement} node
25296      * @return {HTMLElement} The template node
25297      */
25298     findItemFromChild : function(node){
25299         var el = this.dataName  ?
25300             this.el.child('.roo-tpl-' + this.dataName,true) :
25301             this.el.dom; 
25302         
25303         if(!node || node.parentNode == el){
25304                     return node;
25305             }
25306             var p = node.parentNode;
25307             while(p && p != el){
25308             if(p.parentNode == el){
25309                 return p;
25310             }
25311             p = p.parentNode;
25312         }
25313             return null;
25314     },
25315
25316     /** @ignore */
25317     onClick : function(e){
25318         var item = this.findItemFromChild(e.getTarget());
25319         if(item){
25320             var index = this.indexOf(item);
25321             if(this.onItemClick(item, index, e) !== false){
25322                 this.fireEvent("click", this, index, item, e);
25323             }
25324         }else{
25325             this.clearSelections();
25326         }
25327     },
25328
25329     /** @ignore */
25330     onContextMenu : function(e){
25331         var item = this.findItemFromChild(e.getTarget());
25332         if(item){
25333             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25334         }
25335     },
25336
25337     /** @ignore */
25338     onDblClick : function(e){
25339         var item = this.findItemFromChild(e.getTarget());
25340         if(item){
25341             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25342         }
25343     },
25344
25345     onItemClick : function(item, index, e)
25346     {
25347         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25348             return false;
25349         }
25350         if (this.toggleSelect) {
25351             var m = this.isSelected(item) ? 'unselect' : 'select';
25352             //Roo.log(m);
25353             var _t = this;
25354             _t[m](item, true, false);
25355             return true;
25356         }
25357         if(this.multiSelect || this.singleSelect){
25358             if(this.multiSelect && e.shiftKey && this.lastSelection){
25359                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25360             }else{
25361                 this.select(item, this.multiSelect && e.ctrlKey);
25362                 this.lastSelection = item;
25363             }
25364             
25365             if(!this.tickable){
25366                 e.preventDefault();
25367             }
25368             
25369         }
25370         return true;
25371     },
25372
25373     /**
25374      * Get the number of selected nodes.
25375      * @return {Number}
25376      */
25377     getSelectionCount : function(){
25378         return this.selections.length;
25379     },
25380
25381     /**
25382      * Get the currently selected nodes.
25383      * @return {Array} An array of HTMLElements
25384      */
25385     getSelectedNodes : function(){
25386         return this.selections;
25387     },
25388
25389     /**
25390      * Get the indexes of the selected nodes.
25391      * @return {Array}
25392      */
25393     getSelectedIndexes : function(){
25394         var indexes = [], s = this.selections;
25395         for(var i = 0, len = s.length; i < len; i++){
25396             indexes.push(s[i].nodeIndex);
25397         }
25398         return indexes;
25399     },
25400
25401     /**
25402      * Clear all selections
25403      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25404      */
25405     clearSelections : function(suppressEvent){
25406         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25407             this.cmp.elements = this.selections;
25408             this.cmp.removeClass(this.selectedClass);
25409             this.selections = [];
25410             if(!suppressEvent){
25411                 this.fireEvent("selectionchange", this, this.selections);
25412             }
25413         }
25414     },
25415
25416     /**
25417      * Returns true if the passed node is selected
25418      * @param {HTMLElement/Number} node The node or node index
25419      * @return {Boolean}
25420      */
25421     isSelected : function(node){
25422         var s = this.selections;
25423         if(s.length < 1){
25424             return false;
25425         }
25426         node = this.getNode(node);
25427         return s.indexOf(node) !== -1;
25428     },
25429
25430     /**
25431      * Selects nodes.
25432      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
25433      * @param {Boolean} keepExisting (optional) true to keep existing selections
25434      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25435      */
25436     select : function(nodeInfo, keepExisting, suppressEvent){
25437         if(nodeInfo instanceof Array){
25438             if(!keepExisting){
25439                 this.clearSelections(true);
25440             }
25441             for(var i = 0, len = nodeInfo.length; i < len; i++){
25442                 this.select(nodeInfo[i], true, true);
25443             }
25444             return;
25445         } 
25446         var node = this.getNode(nodeInfo);
25447         if(!node || this.isSelected(node)){
25448             return; // already selected.
25449         }
25450         if(!keepExisting){
25451             this.clearSelections(true);
25452         }
25453         
25454         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25455             Roo.fly(node).addClass(this.selectedClass);
25456             this.selections.push(node);
25457             if(!suppressEvent){
25458                 this.fireEvent("selectionchange", this, this.selections);
25459             }
25460         }
25461         
25462         
25463     },
25464       /**
25465      * Unselects nodes.
25466      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
25467      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25468      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25469      */
25470     unselect : function(nodeInfo, keepExisting, suppressEvent)
25471     {
25472         if(nodeInfo instanceof Array){
25473             Roo.each(this.selections, function(s) {
25474                 this.unselect(s, nodeInfo);
25475             }, this);
25476             return;
25477         }
25478         var node = this.getNode(nodeInfo);
25479         if(!node || !this.isSelected(node)){
25480             //Roo.log("not selected");
25481             return; // not selected.
25482         }
25483         // fireevent???
25484         var ns = [];
25485         Roo.each(this.selections, function(s) {
25486             if (s == node ) {
25487                 Roo.fly(node).removeClass(this.selectedClass);
25488
25489                 return;
25490             }
25491             ns.push(s);
25492         },this);
25493         
25494         this.selections= ns;
25495         this.fireEvent("selectionchange", this, this.selections);
25496     },
25497
25498     /**
25499      * Gets a template node.
25500      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25501      * @return {HTMLElement} The node or null if it wasn't found
25502      */
25503     getNode : function(nodeInfo){
25504         if(typeof nodeInfo == "string"){
25505             return document.getElementById(nodeInfo);
25506         }else if(typeof nodeInfo == "number"){
25507             return this.nodes[nodeInfo];
25508         }
25509         return nodeInfo;
25510     },
25511
25512     /**
25513      * Gets a range template nodes.
25514      * @param {Number} startIndex
25515      * @param {Number} endIndex
25516      * @return {Array} An array of nodes
25517      */
25518     getNodes : function(start, end){
25519         var ns = this.nodes;
25520         start = start || 0;
25521         end = typeof end == "undefined" ? ns.length - 1 : end;
25522         var nodes = [];
25523         if(start <= end){
25524             for(var i = start; i <= end; i++){
25525                 nodes.push(ns[i]);
25526             }
25527         } else{
25528             for(var i = start; i >= end; i--){
25529                 nodes.push(ns[i]);
25530             }
25531         }
25532         return nodes;
25533     },
25534
25535     /**
25536      * Finds the index of the passed node
25537      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25538      * @return {Number} The index of the node or -1
25539      */
25540     indexOf : function(node){
25541         node = this.getNode(node);
25542         if(typeof node.nodeIndex == "number"){
25543             return node.nodeIndex;
25544         }
25545         var ns = this.nodes;
25546         for(var i = 0, len = ns.length; i < len; i++){
25547             if(ns[i] == node){
25548                 return i;
25549             }
25550         }
25551         return -1;
25552     }
25553 });
25554 /*
25555  * Based on:
25556  * Ext JS Library 1.1.1
25557  * Copyright(c) 2006-2007, Ext JS, LLC.
25558  *
25559  * Originally Released Under LGPL - original licence link has changed is not relivant.
25560  *
25561  * Fork - LGPL
25562  * <script type="text/javascript">
25563  */
25564
25565 /**
25566  * @class Roo.JsonView
25567  * @extends Roo.View
25568  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25569 <pre><code>
25570 var view = new Roo.JsonView({
25571     container: "my-element",
25572     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25573     multiSelect: true, 
25574     jsonRoot: "data" 
25575 });
25576
25577 // listen for node click?
25578 view.on("click", function(vw, index, node, e){
25579     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25580 });
25581
25582 // direct load of JSON data
25583 view.load("foobar.php");
25584
25585 // Example from my blog list
25586 var tpl = new Roo.Template(
25587     '&lt;div class="entry"&gt;' +
25588     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25589     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25590     "&lt;/div&gt;&lt;hr /&gt;"
25591 );
25592
25593 var moreView = new Roo.JsonView({
25594     container :  "entry-list", 
25595     template : tpl,
25596     jsonRoot: "posts"
25597 });
25598 moreView.on("beforerender", this.sortEntries, this);
25599 moreView.load({
25600     url: "/blog/get-posts.php",
25601     params: "allposts=true",
25602     text: "Loading Blog Entries..."
25603 });
25604 </code></pre>
25605
25606 * Note: old code is supported with arguments : (container, template, config)
25607
25608
25609  * @constructor
25610  * Create a new JsonView
25611  * 
25612  * @param {Object} config The config object
25613  * 
25614  */
25615 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25616     
25617     
25618     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25619
25620     var um = this.el.getUpdateManager();
25621     um.setRenderer(this);
25622     um.on("update", this.onLoad, this);
25623     um.on("failure", this.onLoadException, this);
25624
25625     /**
25626      * @event beforerender
25627      * Fires before rendering of the downloaded JSON data.
25628      * @param {Roo.JsonView} this
25629      * @param {Object} data The JSON data loaded
25630      */
25631     /**
25632      * @event load
25633      * Fires when data is loaded.
25634      * @param {Roo.JsonView} this
25635      * @param {Object} data The JSON data loaded
25636      * @param {Object} response The raw Connect response object
25637      */
25638     /**
25639      * @event loadexception
25640      * Fires when loading fails.
25641      * @param {Roo.JsonView} this
25642      * @param {Object} response The raw Connect response object
25643      */
25644     this.addEvents({
25645         'beforerender' : true,
25646         'load' : true,
25647         'loadexception' : true
25648     });
25649 };
25650 Roo.extend(Roo.JsonView, Roo.View, {
25651     /**
25652      * @type {String} The root property in the loaded JSON object that contains the data
25653      */
25654     jsonRoot : "",
25655
25656     /**
25657      * Refreshes the view.
25658      */
25659     refresh : function(){
25660         this.clearSelections();
25661         this.el.update("");
25662         var html = [];
25663         var o = this.jsonData;
25664         if(o && o.length > 0){
25665             for(var i = 0, len = o.length; i < len; i++){
25666                 var data = this.prepareData(o[i], i, o);
25667                 html[html.length] = this.tpl.apply(data);
25668             }
25669         }else{
25670             html.push(this.emptyText);
25671         }
25672         this.el.update(html.join(""));
25673         this.nodes = this.el.dom.childNodes;
25674         this.updateIndexes(0);
25675     },
25676
25677     /**
25678      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
25679      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
25680      <pre><code>
25681      view.load({
25682          url: "your-url.php",
25683          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25684          callback: yourFunction,
25685          scope: yourObject, //(optional scope)
25686          discardUrl: false,
25687          nocache: false,
25688          text: "Loading...",
25689          timeout: 30,
25690          scripts: false
25691      });
25692      </code></pre>
25693      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25694      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
25695      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
25696      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25697      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
25698      */
25699     load : function(){
25700         var um = this.el.getUpdateManager();
25701         um.update.apply(um, arguments);
25702     },
25703
25704     render : function(el, response){
25705         this.clearSelections();
25706         this.el.update("");
25707         var o;
25708         try{
25709             o = Roo.util.JSON.decode(response.responseText);
25710             if(this.jsonRoot){
25711                 
25712                 o = o[this.jsonRoot];
25713             }
25714         } catch(e){
25715         }
25716         /**
25717          * The current JSON data or null
25718          */
25719         this.jsonData = o;
25720         this.beforeRender();
25721         this.refresh();
25722     },
25723
25724 /**
25725  * Get the number of records in the current JSON dataset
25726  * @return {Number}
25727  */
25728     getCount : function(){
25729         return this.jsonData ? this.jsonData.length : 0;
25730     },
25731
25732 /**
25733  * Returns the JSON object for the specified node(s)
25734  * @param {HTMLElement/Array} node The node or an array of nodes
25735  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25736  * you get the JSON object for the node
25737  */
25738     getNodeData : function(node){
25739         if(node instanceof Array){
25740             var data = [];
25741             for(var i = 0, len = node.length; i < len; i++){
25742                 data.push(this.getNodeData(node[i]));
25743             }
25744             return data;
25745         }
25746         return this.jsonData[this.indexOf(node)] || null;
25747     },
25748
25749     beforeRender : function(){
25750         this.snapshot = this.jsonData;
25751         if(this.sortInfo){
25752             this.sort.apply(this, this.sortInfo);
25753         }
25754         this.fireEvent("beforerender", this, this.jsonData);
25755     },
25756
25757     onLoad : function(el, o){
25758         this.fireEvent("load", this, this.jsonData, o);
25759     },
25760
25761     onLoadException : function(el, o){
25762         this.fireEvent("loadexception", this, o);
25763     },
25764
25765 /**
25766  * Filter the data by a specific property.
25767  * @param {String} property A property on your JSON objects
25768  * @param {String/RegExp} value Either string that the property values
25769  * should start with, or a RegExp to test against the property
25770  */
25771     filter : function(property, value){
25772         if(this.jsonData){
25773             var data = [];
25774             var ss = this.snapshot;
25775             if(typeof value == "string"){
25776                 var vlen = value.length;
25777                 if(vlen == 0){
25778                     this.clearFilter();
25779                     return;
25780                 }
25781                 value = value.toLowerCase();
25782                 for(var i = 0, len = ss.length; i < len; i++){
25783                     var o = ss[i];
25784                     if(o[property].substr(0, vlen).toLowerCase() == value){
25785                         data.push(o);
25786                     }
25787                 }
25788             } else if(value.exec){ // regex?
25789                 for(var i = 0, len = ss.length; i < len; i++){
25790                     var o = ss[i];
25791                     if(value.test(o[property])){
25792                         data.push(o);
25793                     }
25794                 }
25795             } else{
25796                 return;
25797             }
25798             this.jsonData = data;
25799             this.refresh();
25800         }
25801     },
25802
25803 /**
25804  * Filter by a function. The passed function will be called with each
25805  * object in the current dataset. If the function returns true the value is kept,
25806  * otherwise it is filtered.
25807  * @param {Function} fn
25808  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25809  */
25810     filterBy : function(fn, scope){
25811         if(this.jsonData){
25812             var data = [];
25813             var ss = this.snapshot;
25814             for(var i = 0, len = ss.length; i < len; i++){
25815                 var o = ss[i];
25816                 if(fn.call(scope || this, o)){
25817                     data.push(o);
25818                 }
25819             }
25820             this.jsonData = data;
25821             this.refresh();
25822         }
25823     },
25824
25825 /**
25826  * Clears the current filter.
25827  */
25828     clearFilter : function(){
25829         if(this.snapshot && this.jsonData != this.snapshot){
25830             this.jsonData = this.snapshot;
25831             this.refresh();
25832         }
25833     },
25834
25835
25836 /**
25837  * Sorts the data for this view and refreshes it.
25838  * @param {String} property A property on your JSON objects to sort on
25839  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25840  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25841  */
25842     sort : function(property, dir, sortType){
25843         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25844         if(this.jsonData){
25845             var p = property;
25846             var dsc = dir && dir.toLowerCase() == "desc";
25847             var f = function(o1, o2){
25848                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25849                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25850                 ;
25851                 if(v1 < v2){
25852                     return dsc ? +1 : -1;
25853                 } else if(v1 > v2){
25854                     return dsc ? -1 : +1;
25855                 } else{
25856                     return 0;
25857                 }
25858             };
25859             this.jsonData.sort(f);
25860             this.refresh();
25861             if(this.jsonData != this.snapshot){
25862                 this.snapshot.sort(f);
25863             }
25864         }
25865     }
25866 });/*
25867  * Based on:
25868  * Ext JS Library 1.1.1
25869  * Copyright(c) 2006-2007, Ext JS, LLC.
25870  *
25871  * Originally Released Under LGPL - original licence link has changed is not relivant.
25872  *
25873  * Fork - LGPL
25874  * <script type="text/javascript">
25875  */
25876  
25877
25878 /**
25879  * @class Roo.ColorPalette
25880  * @extends Roo.Component
25881  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25882  * Here's an example of typical usage:
25883  * <pre><code>
25884 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25885 cp.render('my-div');
25886
25887 cp.on('select', function(palette, selColor){
25888     // do something with selColor
25889 });
25890 </code></pre>
25891  * @constructor
25892  * Create a new ColorPalette
25893  * @param {Object} config The config object
25894  */
25895 Roo.ColorPalette = function(config){
25896     Roo.ColorPalette.superclass.constructor.call(this, config);
25897     this.addEvents({
25898         /**
25899              * @event select
25900              * Fires when a color is selected
25901              * @param {ColorPalette} this
25902              * @param {String} color The 6-digit color hex code (without the # symbol)
25903              */
25904         select: true
25905     });
25906
25907     if(this.handler){
25908         this.on("select", this.handler, this.scope, true);
25909     }
25910 };
25911 Roo.extend(Roo.ColorPalette, Roo.Component, {
25912     /**
25913      * @cfg {String} itemCls
25914      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25915      */
25916     itemCls : "x-color-palette",
25917     /**
25918      * @cfg {String} value
25919      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25920      * the hex codes are case-sensitive.
25921      */
25922     value : null,
25923     clickEvent:'click',
25924     // private
25925     ctype: "Roo.ColorPalette",
25926
25927     /**
25928      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25929      */
25930     allowReselect : false,
25931
25932     /**
25933      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25934      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25935      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25936      * of colors with the width setting until the box is symmetrical.</p>
25937      * <p>You can override individual colors if needed:</p>
25938      * <pre><code>
25939 var cp = new Roo.ColorPalette();
25940 cp.colors[0] = "FF0000";  // change the first box to red
25941 </code></pre>
25942
25943 Or you can provide a custom array of your own for complete control:
25944 <pre><code>
25945 var cp = new Roo.ColorPalette();
25946 cp.colors = ["000000", "993300", "333300"];
25947 </code></pre>
25948      * @type Array
25949      */
25950     colors : [
25951         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25952         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25953         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25954         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25955         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25956     ],
25957
25958     // private
25959     onRender : function(container, position){
25960         var t = new Roo.MasterTemplate(
25961             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25962         );
25963         var c = this.colors;
25964         for(var i = 0, len = c.length; i < len; i++){
25965             t.add([c[i]]);
25966         }
25967         var el = document.createElement("div");
25968         el.className = this.itemCls;
25969         t.overwrite(el);
25970         container.dom.insertBefore(el, position);
25971         this.el = Roo.get(el);
25972         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25973         if(this.clickEvent != 'click'){
25974             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25975         }
25976     },
25977
25978     // private
25979     afterRender : function(){
25980         Roo.ColorPalette.superclass.afterRender.call(this);
25981         if(this.value){
25982             var s = this.value;
25983             this.value = null;
25984             this.select(s);
25985         }
25986     },
25987
25988     // private
25989     handleClick : function(e, t){
25990         e.preventDefault();
25991         if(!this.disabled){
25992             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25993             this.select(c.toUpperCase());
25994         }
25995     },
25996
25997     /**
25998      * Selects the specified color in the palette (fires the select event)
25999      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
26000      */
26001     select : function(color){
26002         color = color.replace("#", "");
26003         if(color != this.value || this.allowReselect){
26004             var el = this.el;
26005             if(this.value){
26006                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
26007             }
26008             el.child("a.color-"+color).addClass("x-color-palette-sel");
26009             this.value = color;
26010             this.fireEvent("select", this, color);
26011         }
26012     }
26013 });/*
26014  * Based on:
26015  * Ext JS Library 1.1.1
26016  * Copyright(c) 2006-2007, Ext JS, LLC.
26017  *
26018  * Originally Released Under LGPL - original licence link has changed is not relivant.
26019  *
26020  * Fork - LGPL
26021  * <script type="text/javascript">
26022  */
26023  
26024 /**
26025  * @class Roo.DatePicker
26026  * @extends Roo.Component
26027  * Simple date picker class.
26028  * @constructor
26029  * Create a new DatePicker
26030  * @param {Object} config The config object
26031  */
26032 Roo.DatePicker = function(config){
26033     Roo.DatePicker.superclass.constructor.call(this, config);
26034
26035     this.value = config && config.value ?
26036                  config.value.clearTime() : new Date().clearTime();
26037
26038     this.addEvents({
26039         /**
26040              * @event select
26041              * Fires when a date is selected
26042              * @param {DatePicker} this
26043              * @param {Date} date The selected date
26044              */
26045         'select': true,
26046         /**
26047              * @event monthchange
26048              * Fires when the displayed month changes 
26049              * @param {DatePicker} this
26050              * @param {Date} date The selected month
26051              */
26052         'monthchange': true
26053     });
26054
26055     if(this.handler){
26056         this.on("select", this.handler,  this.scope || this);
26057     }
26058     // build the disabledDatesRE
26059     if(!this.disabledDatesRE && this.disabledDates){
26060         var dd = this.disabledDates;
26061         var re = "(?:";
26062         for(var i = 0; i < dd.length; i++){
26063             re += dd[i];
26064             if(i != dd.length-1) {
26065                 re += "|";
26066             }
26067         }
26068         this.disabledDatesRE = new RegExp(re + ")");
26069     }
26070 };
26071
26072 Roo.extend(Roo.DatePicker, Roo.Component, {
26073     /**
26074      * @cfg {String} todayText
26075      * The text to display on the button that selects the current date (defaults to "Today")
26076      */
26077     todayText : "Today",
26078     /**
26079      * @cfg {String} okText
26080      * The text to display on the ok button
26081      */
26082     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26083     /**
26084      * @cfg {String} cancelText
26085      * The text to display on the cancel button
26086      */
26087     cancelText : "Cancel",
26088     /**
26089      * @cfg {String} todayTip
26090      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26091      */
26092     todayTip : "{0} (Spacebar)",
26093     /**
26094      * @cfg {Date} minDate
26095      * Minimum allowable date (JavaScript date object, defaults to null)
26096      */
26097     minDate : null,
26098     /**
26099      * @cfg {Date} maxDate
26100      * Maximum allowable date (JavaScript date object, defaults to null)
26101      */
26102     maxDate : null,
26103     /**
26104      * @cfg {String} minText
26105      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26106      */
26107     minText : "This date is before the minimum date",
26108     /**
26109      * @cfg {String} maxText
26110      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26111      */
26112     maxText : "This date is after the maximum date",
26113     /**
26114      * @cfg {String} format
26115      * The default date format string which can be overriden for localization support.  The format must be
26116      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26117      */
26118     format : "m/d/y",
26119     /**
26120      * @cfg {Array} disabledDays
26121      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26122      */
26123     disabledDays : null,
26124     /**
26125      * @cfg {String} disabledDaysText
26126      * The tooltip to display when the date falls on a disabled day (defaults to "")
26127      */
26128     disabledDaysText : "",
26129     /**
26130      * @cfg {RegExp} disabledDatesRE
26131      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26132      */
26133     disabledDatesRE : null,
26134     /**
26135      * @cfg {String} disabledDatesText
26136      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26137      */
26138     disabledDatesText : "",
26139     /**
26140      * @cfg {Boolean} constrainToViewport
26141      * True to constrain the date picker to the viewport (defaults to true)
26142      */
26143     constrainToViewport : true,
26144     /**
26145      * @cfg {Array} monthNames
26146      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26147      */
26148     monthNames : Date.monthNames,
26149     /**
26150      * @cfg {Array} dayNames
26151      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26152      */
26153     dayNames : Date.dayNames,
26154     /**
26155      * @cfg {String} nextText
26156      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26157      */
26158     nextText: 'Next Month (Control+Right)',
26159     /**
26160      * @cfg {String} prevText
26161      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26162      */
26163     prevText: 'Previous Month (Control+Left)',
26164     /**
26165      * @cfg {String} monthYearText
26166      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26167      */
26168     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26169     /**
26170      * @cfg {Number} startDay
26171      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26172      */
26173     startDay : 0,
26174     /**
26175      * @cfg {Bool} showClear
26176      * Show a clear button (usefull for date form elements that can be blank.)
26177      */
26178     
26179     showClear: false,
26180     
26181     /**
26182      * Sets the value of the date field
26183      * @param {Date} value The date to set
26184      */
26185     setValue : function(value){
26186         var old = this.value;
26187         
26188         if (typeof(value) == 'string') {
26189          
26190             value = Date.parseDate(value, this.format);
26191         }
26192         if (!value) {
26193             value = new Date();
26194         }
26195         
26196         this.value = value.clearTime(true);
26197         if(this.el){
26198             this.update(this.value);
26199         }
26200     },
26201
26202     /**
26203      * Gets the current selected value of the date field
26204      * @return {Date} The selected date
26205      */
26206     getValue : function(){
26207         return this.value;
26208     },
26209
26210     // private
26211     focus : function(){
26212         if(this.el){
26213             this.update(this.activeDate);
26214         }
26215     },
26216
26217     // privateval
26218     onRender : function(container, position){
26219         
26220         var m = [
26221              '<table cellspacing="0">',
26222                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
26223                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26224         var dn = this.dayNames;
26225         for(var i = 0; i < 7; i++){
26226             var d = this.startDay+i;
26227             if(d > 6){
26228                 d = d-7;
26229             }
26230             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26231         }
26232         m[m.length] = "</tr></thead><tbody><tr>";
26233         for(var i = 0; i < 42; i++) {
26234             if(i % 7 == 0 && i != 0){
26235                 m[m.length] = "</tr><tr>";
26236             }
26237             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26238         }
26239         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26240             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26241
26242         var el = document.createElement("div");
26243         el.className = "x-date-picker";
26244         el.innerHTML = m.join("");
26245
26246         container.dom.insertBefore(el, position);
26247
26248         this.el = Roo.get(el);
26249         this.eventEl = Roo.get(el.firstChild);
26250
26251         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26252             handler: this.showPrevMonth,
26253             scope: this,
26254             preventDefault:true,
26255             stopDefault:true
26256         });
26257
26258         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26259             handler: this.showNextMonth,
26260             scope: this,
26261             preventDefault:true,
26262             stopDefault:true
26263         });
26264
26265         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26266
26267         this.monthPicker = this.el.down('div.x-date-mp');
26268         this.monthPicker.enableDisplayMode('block');
26269         
26270         var kn = new Roo.KeyNav(this.eventEl, {
26271             "left" : function(e){
26272                 e.ctrlKey ?
26273                     this.showPrevMonth() :
26274                     this.update(this.activeDate.add("d", -1));
26275             },
26276
26277             "right" : function(e){
26278                 e.ctrlKey ?
26279                     this.showNextMonth() :
26280                     this.update(this.activeDate.add("d", 1));
26281             },
26282
26283             "up" : function(e){
26284                 e.ctrlKey ?
26285                     this.showNextYear() :
26286                     this.update(this.activeDate.add("d", -7));
26287             },
26288
26289             "down" : function(e){
26290                 e.ctrlKey ?
26291                     this.showPrevYear() :
26292                     this.update(this.activeDate.add("d", 7));
26293             },
26294
26295             "pageUp" : function(e){
26296                 this.showNextMonth();
26297             },
26298
26299             "pageDown" : function(e){
26300                 this.showPrevMonth();
26301             },
26302
26303             "enter" : function(e){
26304                 e.stopPropagation();
26305                 return true;
26306             },
26307
26308             scope : this
26309         });
26310
26311         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26312
26313         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26314
26315         this.el.unselectable();
26316         
26317         this.cells = this.el.select("table.x-date-inner tbody td");
26318         this.textNodes = this.el.query("table.x-date-inner tbody span");
26319
26320         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26321             text: "&#160;",
26322             tooltip: this.monthYearText
26323         });
26324
26325         this.mbtn.on('click', this.showMonthPicker, this);
26326         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26327
26328
26329         var today = (new Date()).dateFormat(this.format);
26330         
26331         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26332         if (this.showClear) {
26333             baseTb.add( new Roo.Toolbar.Fill());
26334         }
26335         baseTb.add({
26336             text: String.format(this.todayText, today),
26337             tooltip: String.format(this.todayTip, today),
26338             handler: this.selectToday,
26339             scope: this
26340         });
26341         
26342         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26343             
26344         //});
26345         if (this.showClear) {
26346             
26347             baseTb.add( new Roo.Toolbar.Fill());
26348             baseTb.add({
26349                 text: '&#160;',
26350                 cls: 'x-btn-icon x-btn-clear',
26351                 handler: function() {
26352                     //this.value = '';
26353                     this.fireEvent("select", this, '');
26354                 },
26355                 scope: this
26356             });
26357         }
26358         
26359         
26360         if(Roo.isIE){
26361             this.el.repaint();
26362         }
26363         this.update(this.value);
26364     },
26365
26366     createMonthPicker : function(){
26367         if(!this.monthPicker.dom.firstChild){
26368             var buf = ['<table border="0" cellspacing="0">'];
26369             for(var i = 0; i < 6; i++){
26370                 buf.push(
26371                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26372                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26373                     i == 0 ?
26374                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
26375                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26376                 );
26377             }
26378             buf.push(
26379                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26380                     this.okText,
26381                     '</button><button type="button" class="x-date-mp-cancel">',
26382                     this.cancelText,
26383                     '</button></td></tr>',
26384                 '</table>'
26385             );
26386             this.monthPicker.update(buf.join(''));
26387             this.monthPicker.on('click', this.onMonthClick, this);
26388             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26389
26390             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26391             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26392
26393             this.mpMonths.each(function(m, a, i){
26394                 i += 1;
26395                 if((i%2) == 0){
26396                     m.dom.xmonth = 5 + Math.round(i * .5);
26397                 }else{
26398                     m.dom.xmonth = Math.round((i-1) * .5);
26399                 }
26400             });
26401         }
26402     },
26403
26404     showMonthPicker : function(){
26405         this.createMonthPicker();
26406         var size = this.el.getSize();
26407         this.monthPicker.setSize(size);
26408         this.monthPicker.child('table').setSize(size);
26409
26410         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26411         this.updateMPMonth(this.mpSelMonth);
26412         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26413         this.updateMPYear(this.mpSelYear);
26414
26415         this.monthPicker.slideIn('t', {duration:.2});
26416     },
26417
26418     updateMPYear : function(y){
26419         this.mpyear = y;
26420         var ys = this.mpYears.elements;
26421         for(var i = 1; i <= 10; i++){
26422             var td = ys[i-1], y2;
26423             if((i%2) == 0){
26424                 y2 = y + Math.round(i * .5);
26425                 td.firstChild.innerHTML = y2;
26426                 td.xyear = y2;
26427             }else{
26428                 y2 = y - (5-Math.round(i * .5));
26429                 td.firstChild.innerHTML = y2;
26430                 td.xyear = y2;
26431             }
26432             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26433         }
26434     },
26435
26436     updateMPMonth : function(sm){
26437         this.mpMonths.each(function(m, a, i){
26438             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26439         });
26440     },
26441
26442     selectMPMonth: function(m){
26443         
26444     },
26445
26446     onMonthClick : function(e, t){
26447         e.stopEvent();
26448         var el = new Roo.Element(t), pn;
26449         if(el.is('button.x-date-mp-cancel')){
26450             this.hideMonthPicker();
26451         }
26452         else if(el.is('button.x-date-mp-ok')){
26453             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26454             this.hideMonthPicker();
26455         }
26456         else if(pn = el.up('td.x-date-mp-month', 2)){
26457             this.mpMonths.removeClass('x-date-mp-sel');
26458             pn.addClass('x-date-mp-sel');
26459             this.mpSelMonth = pn.dom.xmonth;
26460         }
26461         else if(pn = el.up('td.x-date-mp-year', 2)){
26462             this.mpYears.removeClass('x-date-mp-sel');
26463             pn.addClass('x-date-mp-sel');
26464             this.mpSelYear = pn.dom.xyear;
26465         }
26466         else if(el.is('a.x-date-mp-prev')){
26467             this.updateMPYear(this.mpyear-10);
26468         }
26469         else if(el.is('a.x-date-mp-next')){
26470             this.updateMPYear(this.mpyear+10);
26471         }
26472     },
26473
26474     onMonthDblClick : function(e, t){
26475         e.stopEvent();
26476         var el = new Roo.Element(t), pn;
26477         if(pn = el.up('td.x-date-mp-month', 2)){
26478             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26479             this.hideMonthPicker();
26480         }
26481         else if(pn = el.up('td.x-date-mp-year', 2)){
26482             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26483             this.hideMonthPicker();
26484         }
26485     },
26486
26487     hideMonthPicker : function(disableAnim){
26488         if(this.monthPicker){
26489             if(disableAnim === true){
26490                 this.monthPicker.hide();
26491             }else{
26492                 this.monthPicker.slideOut('t', {duration:.2});
26493             }
26494         }
26495     },
26496
26497     // private
26498     showPrevMonth : function(e){
26499         this.update(this.activeDate.add("mo", -1));
26500     },
26501
26502     // private
26503     showNextMonth : function(e){
26504         this.update(this.activeDate.add("mo", 1));
26505     },
26506
26507     // private
26508     showPrevYear : function(){
26509         this.update(this.activeDate.add("y", -1));
26510     },
26511
26512     // private
26513     showNextYear : function(){
26514         this.update(this.activeDate.add("y", 1));
26515     },
26516
26517     // private
26518     handleMouseWheel : function(e){
26519         var delta = e.getWheelDelta();
26520         if(delta > 0){
26521             this.showPrevMonth();
26522             e.stopEvent();
26523         } else if(delta < 0){
26524             this.showNextMonth();
26525             e.stopEvent();
26526         }
26527     },
26528
26529     // private
26530     handleDateClick : function(e, t){
26531         e.stopEvent();
26532         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26533             this.setValue(new Date(t.dateValue));
26534             this.fireEvent("select", this, this.value);
26535         }
26536     },
26537
26538     // private
26539     selectToday : function(){
26540         this.setValue(new Date().clearTime());
26541         this.fireEvent("select", this, this.value);
26542     },
26543
26544     // private
26545     update : function(date)
26546     {
26547         var vd = this.activeDate;
26548         this.activeDate = date;
26549         if(vd && this.el){
26550             var t = date.getTime();
26551             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26552                 this.cells.removeClass("x-date-selected");
26553                 this.cells.each(function(c){
26554                    if(c.dom.firstChild.dateValue == t){
26555                        c.addClass("x-date-selected");
26556                        setTimeout(function(){
26557                             try{c.dom.firstChild.focus();}catch(e){}
26558                        }, 50);
26559                        return false;
26560                    }
26561                 });
26562                 return;
26563             }
26564         }
26565         
26566         var days = date.getDaysInMonth();
26567         var firstOfMonth = date.getFirstDateOfMonth();
26568         var startingPos = firstOfMonth.getDay()-this.startDay;
26569
26570         if(startingPos <= this.startDay){
26571             startingPos += 7;
26572         }
26573
26574         var pm = date.add("mo", -1);
26575         var prevStart = pm.getDaysInMonth()-startingPos;
26576
26577         var cells = this.cells.elements;
26578         var textEls = this.textNodes;
26579         days += startingPos;
26580
26581         // convert everything to numbers so it's fast
26582         var day = 86400000;
26583         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26584         var today = new Date().clearTime().getTime();
26585         var sel = date.clearTime().getTime();
26586         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26587         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26588         var ddMatch = this.disabledDatesRE;
26589         var ddText = this.disabledDatesText;
26590         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26591         var ddaysText = this.disabledDaysText;
26592         var format = this.format;
26593
26594         var setCellClass = function(cal, cell){
26595             cell.title = "";
26596             var t = d.getTime();
26597             cell.firstChild.dateValue = t;
26598             if(t == today){
26599                 cell.className += " x-date-today";
26600                 cell.title = cal.todayText;
26601             }
26602             if(t == sel){
26603                 cell.className += " x-date-selected";
26604                 setTimeout(function(){
26605                     try{cell.firstChild.focus();}catch(e){}
26606                 }, 50);
26607             }
26608             // disabling
26609             if(t < min) {
26610                 cell.className = " x-date-disabled";
26611                 cell.title = cal.minText;
26612                 return;
26613             }
26614             if(t > max) {
26615                 cell.className = " x-date-disabled";
26616                 cell.title = cal.maxText;
26617                 return;
26618             }
26619             if(ddays){
26620                 if(ddays.indexOf(d.getDay()) != -1){
26621                     cell.title = ddaysText;
26622                     cell.className = " x-date-disabled";
26623                 }
26624             }
26625             if(ddMatch && format){
26626                 var fvalue = d.dateFormat(format);
26627                 if(ddMatch.test(fvalue)){
26628                     cell.title = ddText.replace("%0", fvalue);
26629                     cell.className = " x-date-disabled";
26630                 }
26631             }
26632         };
26633
26634         var i = 0;
26635         for(; i < startingPos; i++) {
26636             textEls[i].innerHTML = (++prevStart);
26637             d.setDate(d.getDate()+1);
26638             cells[i].className = "x-date-prevday";
26639             setCellClass(this, cells[i]);
26640         }
26641         for(; i < days; i++){
26642             intDay = i - startingPos + 1;
26643             textEls[i].innerHTML = (intDay);
26644             d.setDate(d.getDate()+1);
26645             cells[i].className = "x-date-active";
26646             setCellClass(this, cells[i]);
26647         }
26648         var extraDays = 0;
26649         for(; i < 42; i++) {
26650              textEls[i].innerHTML = (++extraDays);
26651              d.setDate(d.getDate()+1);
26652              cells[i].className = "x-date-nextday";
26653              setCellClass(this, cells[i]);
26654         }
26655
26656         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26657         this.fireEvent('monthchange', this, date);
26658         
26659         if(!this.internalRender){
26660             var main = this.el.dom.firstChild;
26661             var w = main.offsetWidth;
26662             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26663             Roo.fly(main).setWidth(w);
26664             this.internalRender = true;
26665             // opera does not respect the auto grow header center column
26666             // then, after it gets a width opera refuses to recalculate
26667             // without a second pass
26668             if(Roo.isOpera && !this.secondPass){
26669                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26670                 this.secondPass = true;
26671                 this.update.defer(10, this, [date]);
26672             }
26673         }
26674         
26675         
26676     }
26677 });        /*
26678  * Based on:
26679  * Ext JS Library 1.1.1
26680  * Copyright(c) 2006-2007, Ext JS, LLC.
26681  *
26682  * Originally Released Under LGPL - original licence link has changed is not relivant.
26683  *
26684  * Fork - LGPL
26685  * <script type="text/javascript">
26686  */
26687 /**
26688  * @class Roo.TabPanel
26689  * @extends Roo.util.Observable
26690  * A lightweight tab container.
26691  * <br><br>
26692  * Usage:
26693  * <pre><code>
26694 // basic tabs 1, built from existing content
26695 var tabs = new Roo.TabPanel("tabs1");
26696 tabs.addTab("script", "View Script");
26697 tabs.addTab("markup", "View Markup");
26698 tabs.activate("script");
26699
26700 // more advanced tabs, built from javascript
26701 var jtabs = new Roo.TabPanel("jtabs");
26702 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26703
26704 // set up the UpdateManager
26705 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26706 var updater = tab2.getUpdateManager();
26707 updater.setDefaultUrl("ajax1.htm");
26708 tab2.on('activate', updater.refresh, updater, true);
26709
26710 // Use setUrl for Ajax loading
26711 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26712 tab3.setUrl("ajax2.htm", null, true);
26713
26714 // Disabled tab
26715 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26716 tab4.disable();
26717
26718 jtabs.activate("jtabs-1");
26719  * </code></pre>
26720  * @constructor
26721  * Create a new TabPanel.
26722  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26723  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26724  */
26725 Roo.TabPanel = function(container, config){
26726     /**
26727     * The container element for this TabPanel.
26728     * @type Roo.Element
26729     */
26730     this.el = Roo.get(container, true);
26731     if(config){
26732         if(typeof config == "boolean"){
26733             this.tabPosition = config ? "bottom" : "top";
26734         }else{
26735             Roo.apply(this, config);
26736         }
26737     }
26738     if(this.tabPosition == "bottom"){
26739         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26740         this.el.addClass("x-tabs-bottom");
26741     }
26742     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26743     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26744     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26745     if(Roo.isIE){
26746         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26747     }
26748     if(this.tabPosition != "bottom"){
26749         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26750          * @type Roo.Element
26751          */
26752         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26753         this.el.addClass("x-tabs-top");
26754     }
26755     this.items = [];
26756
26757     this.bodyEl.setStyle("position", "relative");
26758
26759     this.active = null;
26760     this.activateDelegate = this.activate.createDelegate(this);
26761
26762     this.addEvents({
26763         /**
26764          * @event tabchange
26765          * Fires when the active tab changes
26766          * @param {Roo.TabPanel} this
26767          * @param {Roo.TabPanelItem} activePanel The new active tab
26768          */
26769         "tabchange": true,
26770         /**
26771          * @event beforetabchange
26772          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26773          * @param {Roo.TabPanel} this
26774          * @param {Object} e Set cancel to true on this object to cancel the tab change
26775          * @param {Roo.TabPanelItem} tab The tab being changed to
26776          */
26777         "beforetabchange" : true
26778     });
26779
26780     Roo.EventManager.onWindowResize(this.onResize, this);
26781     this.cpad = this.el.getPadding("lr");
26782     this.hiddenCount = 0;
26783
26784
26785     // toolbar on the tabbar support...
26786     if (this.toolbar) {
26787         var tcfg = this.toolbar;
26788         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26789         this.toolbar = new Roo.Toolbar(tcfg);
26790         if (Roo.isSafari) {
26791             var tbl = tcfg.container.child('table', true);
26792             tbl.setAttribute('width', '100%');
26793         }
26794         
26795     }
26796    
26797
26798
26799     Roo.TabPanel.superclass.constructor.call(this);
26800 };
26801
26802 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26803     /*
26804      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26805      */
26806     tabPosition : "top",
26807     /*
26808      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26809      */
26810     currentTabWidth : 0,
26811     /*
26812      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26813      */
26814     minTabWidth : 40,
26815     /*
26816      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26817      */
26818     maxTabWidth : 250,
26819     /*
26820      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26821      */
26822     preferredTabWidth : 175,
26823     /*
26824      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26825      */
26826     resizeTabs : false,
26827     /*
26828      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26829      */
26830     monitorResize : true,
26831     /*
26832      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26833      */
26834     toolbar : false,
26835
26836     /**
26837      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26838      * @param {String} id The id of the div to use <b>or create</b>
26839      * @param {String} text The text for the tab
26840      * @param {String} content (optional) Content to put in the TabPanelItem body
26841      * @param {Boolean} closable (optional) True to create a close icon on the tab
26842      * @return {Roo.TabPanelItem} The created TabPanelItem
26843      */
26844     addTab : function(id, text, content, closable){
26845         var item = new Roo.TabPanelItem(this, id, text, closable);
26846         this.addTabItem(item);
26847         if(content){
26848             item.setContent(content);
26849         }
26850         return item;
26851     },
26852
26853     /**
26854      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26855      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26856      * @return {Roo.TabPanelItem}
26857      */
26858     getTab : function(id){
26859         return this.items[id];
26860     },
26861
26862     /**
26863      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26864      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26865      */
26866     hideTab : function(id){
26867         var t = this.items[id];
26868         if(!t.isHidden()){
26869            t.setHidden(true);
26870            this.hiddenCount++;
26871            this.autoSizeTabs();
26872         }
26873     },
26874
26875     /**
26876      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26877      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26878      */
26879     unhideTab : function(id){
26880         var t = this.items[id];
26881         if(t.isHidden()){
26882            t.setHidden(false);
26883            this.hiddenCount--;
26884            this.autoSizeTabs();
26885         }
26886     },
26887
26888     /**
26889      * Adds an existing {@link Roo.TabPanelItem}.
26890      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26891      */
26892     addTabItem : function(item){
26893         this.items[item.id] = item;
26894         this.items.push(item);
26895         if(this.resizeTabs){
26896            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26897            this.autoSizeTabs();
26898         }else{
26899             item.autoSize();
26900         }
26901     },
26902
26903     /**
26904      * Removes a {@link Roo.TabPanelItem}.
26905      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26906      */
26907     removeTab : function(id){
26908         var items = this.items;
26909         var tab = items[id];
26910         if(!tab) { return; }
26911         var index = items.indexOf(tab);
26912         if(this.active == tab && items.length > 1){
26913             var newTab = this.getNextAvailable(index);
26914             if(newTab) {
26915                 newTab.activate();
26916             }
26917         }
26918         this.stripEl.dom.removeChild(tab.pnode.dom);
26919         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26920             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26921         }
26922         items.splice(index, 1);
26923         delete this.items[tab.id];
26924         tab.fireEvent("close", tab);
26925         tab.purgeListeners();
26926         this.autoSizeTabs();
26927     },
26928
26929     getNextAvailable : function(start){
26930         var items = this.items;
26931         var index = start;
26932         // look for a next tab that will slide over to
26933         // replace the one being removed
26934         while(index < items.length){
26935             var item = items[++index];
26936             if(item && !item.isHidden()){
26937                 return item;
26938             }
26939         }
26940         // if one isn't found select the previous tab (on the left)
26941         index = start;
26942         while(index >= 0){
26943             var item = items[--index];
26944             if(item && !item.isHidden()){
26945                 return item;
26946             }
26947         }
26948         return null;
26949     },
26950
26951     /**
26952      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26953      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26954      */
26955     disableTab : function(id){
26956         var tab = this.items[id];
26957         if(tab && this.active != tab){
26958             tab.disable();
26959         }
26960     },
26961
26962     /**
26963      * Enables a {@link Roo.TabPanelItem} that is disabled.
26964      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26965      */
26966     enableTab : function(id){
26967         var tab = this.items[id];
26968         tab.enable();
26969     },
26970
26971     /**
26972      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26973      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26974      * @return {Roo.TabPanelItem} The TabPanelItem.
26975      */
26976     activate : function(id){
26977         var tab = this.items[id];
26978         if(!tab){
26979             return null;
26980         }
26981         if(tab == this.active || tab.disabled){
26982             return tab;
26983         }
26984         var e = {};
26985         this.fireEvent("beforetabchange", this, e, tab);
26986         if(e.cancel !== true && !tab.disabled){
26987             if(this.active){
26988                 this.active.hide();
26989             }
26990             this.active = this.items[id];
26991             this.active.show();
26992             this.fireEvent("tabchange", this, this.active);
26993         }
26994         return tab;
26995     },
26996
26997     /**
26998      * Gets the active {@link Roo.TabPanelItem}.
26999      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
27000      */
27001     getActiveTab : function(){
27002         return this.active;
27003     },
27004
27005     /**
27006      * Updates the tab body element to fit the height of the container element
27007      * for overflow scrolling
27008      * @param {Number} targetHeight (optional) Override the starting height from the elements height
27009      */
27010     syncHeight : function(targetHeight){
27011         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
27012         var bm = this.bodyEl.getMargins();
27013         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
27014         this.bodyEl.setHeight(newHeight);
27015         return newHeight;
27016     },
27017
27018     onResize : function(){
27019         if(this.monitorResize){
27020             this.autoSizeTabs();
27021         }
27022     },
27023
27024     /**
27025      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
27026      */
27027     beginUpdate : function(){
27028         this.updating = true;
27029     },
27030
27031     /**
27032      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
27033      */
27034     endUpdate : function(){
27035         this.updating = false;
27036         this.autoSizeTabs();
27037     },
27038
27039     /**
27040      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27041      */
27042     autoSizeTabs : function(){
27043         var count = this.items.length;
27044         var vcount = count - this.hiddenCount;
27045         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
27046             return;
27047         }
27048         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27049         var availWidth = Math.floor(w / vcount);
27050         var b = this.stripBody;
27051         if(b.getWidth() > w){
27052             var tabs = this.items;
27053             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27054             if(availWidth < this.minTabWidth){
27055                 /*if(!this.sleft){    // incomplete scrolling code
27056                     this.createScrollButtons();
27057                 }
27058                 this.showScroll();
27059                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27060             }
27061         }else{
27062             if(this.currentTabWidth < this.preferredTabWidth){
27063                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27064             }
27065         }
27066     },
27067
27068     /**
27069      * Returns the number of tabs in this TabPanel.
27070      * @return {Number}
27071      */
27072      getCount : function(){
27073          return this.items.length;
27074      },
27075
27076     /**
27077      * Resizes all the tabs to the passed width
27078      * @param {Number} The new width
27079      */
27080     setTabWidth : function(width){
27081         this.currentTabWidth = width;
27082         for(var i = 0, len = this.items.length; i < len; i++) {
27083                 if(!this.items[i].isHidden()) {
27084                 this.items[i].setWidth(width);
27085             }
27086         }
27087     },
27088
27089     /**
27090      * Destroys this TabPanel
27091      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27092      */
27093     destroy : function(removeEl){
27094         Roo.EventManager.removeResizeListener(this.onResize, this);
27095         for(var i = 0, len = this.items.length; i < len; i++){
27096             this.items[i].purgeListeners();
27097         }
27098         if(removeEl === true){
27099             this.el.update("");
27100             this.el.remove();
27101         }
27102     }
27103 });
27104
27105 /**
27106  * @class Roo.TabPanelItem
27107  * @extends Roo.util.Observable
27108  * Represents an individual item (tab plus body) in a TabPanel.
27109  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27110  * @param {String} id The id of this TabPanelItem
27111  * @param {String} text The text for the tab of this TabPanelItem
27112  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27113  */
27114 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27115     /**
27116      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27117      * @type Roo.TabPanel
27118      */
27119     this.tabPanel = tabPanel;
27120     /**
27121      * The id for this TabPanelItem
27122      * @type String
27123      */
27124     this.id = id;
27125     /** @private */
27126     this.disabled = false;
27127     /** @private */
27128     this.text = text;
27129     /** @private */
27130     this.loaded = false;
27131     this.closable = closable;
27132
27133     /**
27134      * The body element for this TabPanelItem.
27135      * @type Roo.Element
27136      */
27137     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27138     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27139     this.bodyEl.setStyle("display", "block");
27140     this.bodyEl.setStyle("zoom", "1");
27141     this.hideAction();
27142
27143     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27144     /** @private */
27145     this.el = Roo.get(els.el, true);
27146     this.inner = Roo.get(els.inner, true);
27147     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27148     this.pnode = Roo.get(els.el.parentNode, true);
27149     this.el.on("mousedown", this.onTabMouseDown, this);
27150     this.el.on("click", this.onTabClick, this);
27151     /** @private */
27152     if(closable){
27153         var c = Roo.get(els.close, true);
27154         c.dom.title = this.closeText;
27155         c.addClassOnOver("close-over");
27156         c.on("click", this.closeClick, this);
27157      }
27158
27159     this.addEvents({
27160          /**
27161          * @event activate
27162          * Fires when this tab becomes the active tab.
27163          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27164          * @param {Roo.TabPanelItem} this
27165          */
27166         "activate": true,
27167         /**
27168          * @event beforeclose
27169          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27170          * @param {Roo.TabPanelItem} this
27171          * @param {Object} e Set cancel to true on this object to cancel the close.
27172          */
27173         "beforeclose": true,
27174         /**
27175          * @event close
27176          * Fires when this tab is closed.
27177          * @param {Roo.TabPanelItem} this
27178          */
27179          "close": true,
27180         /**
27181          * @event deactivate
27182          * Fires when this tab is no longer the active tab.
27183          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27184          * @param {Roo.TabPanelItem} this
27185          */
27186          "deactivate" : true
27187     });
27188     this.hidden = false;
27189
27190     Roo.TabPanelItem.superclass.constructor.call(this);
27191 };
27192
27193 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27194     purgeListeners : function(){
27195        Roo.util.Observable.prototype.purgeListeners.call(this);
27196        this.el.removeAllListeners();
27197     },
27198     /**
27199      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27200      */
27201     show : function(){
27202         this.pnode.addClass("on");
27203         this.showAction();
27204         if(Roo.isOpera){
27205             this.tabPanel.stripWrap.repaint();
27206         }
27207         this.fireEvent("activate", this.tabPanel, this);
27208     },
27209
27210     /**
27211      * Returns true if this tab is the active tab.
27212      * @return {Boolean}
27213      */
27214     isActive : function(){
27215         return this.tabPanel.getActiveTab() == this;
27216     },
27217
27218     /**
27219      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27220      */
27221     hide : function(){
27222         this.pnode.removeClass("on");
27223         this.hideAction();
27224         this.fireEvent("deactivate", this.tabPanel, this);
27225     },
27226
27227     hideAction : function(){
27228         this.bodyEl.hide();
27229         this.bodyEl.setStyle("position", "absolute");
27230         this.bodyEl.setLeft("-20000px");
27231         this.bodyEl.setTop("-20000px");
27232     },
27233
27234     showAction : function(){
27235         this.bodyEl.setStyle("position", "relative");
27236         this.bodyEl.setTop("");
27237         this.bodyEl.setLeft("");
27238         this.bodyEl.show();
27239     },
27240
27241     /**
27242      * Set the tooltip for the tab.
27243      * @param {String} tooltip The tab's tooltip
27244      */
27245     setTooltip : function(text){
27246         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27247             this.textEl.dom.qtip = text;
27248             this.textEl.dom.removeAttribute('title');
27249         }else{
27250             this.textEl.dom.title = text;
27251         }
27252     },
27253
27254     onTabClick : function(e){
27255         e.preventDefault();
27256         this.tabPanel.activate(this.id);
27257     },
27258
27259     onTabMouseDown : function(e){
27260         e.preventDefault();
27261         this.tabPanel.activate(this.id);
27262     },
27263
27264     getWidth : function(){
27265         return this.inner.getWidth();
27266     },
27267
27268     setWidth : function(width){
27269         var iwidth = width - this.pnode.getPadding("lr");
27270         this.inner.setWidth(iwidth);
27271         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27272         this.pnode.setWidth(width);
27273     },
27274
27275     /**
27276      * Show or hide the tab
27277      * @param {Boolean} hidden True to hide or false to show.
27278      */
27279     setHidden : function(hidden){
27280         this.hidden = hidden;
27281         this.pnode.setStyle("display", hidden ? "none" : "");
27282     },
27283
27284     /**
27285      * Returns true if this tab is "hidden"
27286      * @return {Boolean}
27287      */
27288     isHidden : function(){
27289         return this.hidden;
27290     },
27291
27292     /**
27293      * Returns the text for this tab
27294      * @return {String}
27295      */
27296     getText : function(){
27297         return this.text;
27298     },
27299
27300     autoSize : function(){
27301         //this.el.beginMeasure();
27302         this.textEl.setWidth(1);
27303         /*
27304          *  #2804 [new] Tabs in Roojs
27305          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27306          */
27307         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27308         //this.el.endMeasure();
27309     },
27310
27311     /**
27312      * Sets the text for the tab (Note: this also sets the tooltip text)
27313      * @param {String} text The tab's text and tooltip
27314      */
27315     setText : function(text){
27316         this.text = text;
27317         this.textEl.update(text);
27318         this.setTooltip(text);
27319         if(!this.tabPanel.resizeTabs){
27320             this.autoSize();
27321         }
27322     },
27323     /**
27324      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27325      */
27326     activate : function(){
27327         this.tabPanel.activate(this.id);
27328     },
27329
27330     /**
27331      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27332      */
27333     disable : function(){
27334         if(this.tabPanel.active != this){
27335             this.disabled = true;
27336             this.pnode.addClass("disabled");
27337         }
27338     },
27339
27340     /**
27341      * Enables this TabPanelItem if it was previously disabled.
27342      */
27343     enable : function(){
27344         this.disabled = false;
27345         this.pnode.removeClass("disabled");
27346     },
27347
27348     /**
27349      * Sets the content for this TabPanelItem.
27350      * @param {String} content The content
27351      * @param {Boolean} loadScripts true to look for and load scripts
27352      */
27353     setContent : function(content, loadScripts){
27354         this.bodyEl.update(content, loadScripts);
27355     },
27356
27357     /**
27358      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27359      * @return {Roo.UpdateManager} The UpdateManager
27360      */
27361     getUpdateManager : function(){
27362         return this.bodyEl.getUpdateManager();
27363     },
27364
27365     /**
27366      * Set a URL to be used to load the content for this TabPanelItem.
27367      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27368      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
27369      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
27370      * @return {Roo.UpdateManager} The UpdateManager
27371      */
27372     setUrl : function(url, params, loadOnce){
27373         if(this.refreshDelegate){
27374             this.un('activate', this.refreshDelegate);
27375         }
27376         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27377         this.on("activate", this.refreshDelegate);
27378         return this.bodyEl.getUpdateManager();
27379     },
27380
27381     /** @private */
27382     _handleRefresh : function(url, params, loadOnce){
27383         if(!loadOnce || !this.loaded){
27384             var updater = this.bodyEl.getUpdateManager();
27385             updater.update(url, params, this._setLoaded.createDelegate(this));
27386         }
27387     },
27388
27389     /**
27390      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27391      *   Will fail silently if the setUrl method has not been called.
27392      *   This does not activate the panel, just updates its content.
27393      */
27394     refresh : function(){
27395         if(this.refreshDelegate){
27396            this.loaded = false;
27397            this.refreshDelegate();
27398         }
27399     },
27400
27401     /** @private */
27402     _setLoaded : function(){
27403         this.loaded = true;
27404     },
27405
27406     /** @private */
27407     closeClick : function(e){
27408         var o = {};
27409         e.stopEvent();
27410         this.fireEvent("beforeclose", this, o);
27411         if(o.cancel !== true){
27412             this.tabPanel.removeTab(this.id);
27413         }
27414     },
27415     /**
27416      * The text displayed in the tooltip for the close icon.
27417      * @type String
27418      */
27419     closeText : "Close this tab"
27420 });
27421
27422 /** @private */
27423 Roo.TabPanel.prototype.createStrip = function(container){
27424     var strip = document.createElement("div");
27425     strip.className = "x-tabs-wrap";
27426     container.appendChild(strip);
27427     return strip;
27428 };
27429 /** @private */
27430 Roo.TabPanel.prototype.createStripList = function(strip){
27431     // div wrapper for retard IE
27432     // returns the "tr" element.
27433     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27434         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27435         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27436     return strip.firstChild.firstChild.firstChild.firstChild;
27437 };
27438 /** @private */
27439 Roo.TabPanel.prototype.createBody = function(container){
27440     var body = document.createElement("div");
27441     Roo.id(body, "tab-body");
27442     Roo.fly(body).addClass("x-tabs-body");
27443     container.appendChild(body);
27444     return body;
27445 };
27446 /** @private */
27447 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27448     var body = Roo.getDom(id);
27449     if(!body){
27450         body = document.createElement("div");
27451         body.id = id;
27452     }
27453     Roo.fly(body).addClass("x-tabs-item-body");
27454     bodyEl.insertBefore(body, bodyEl.firstChild);
27455     return body;
27456 };
27457 /** @private */
27458 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27459     var td = document.createElement("td");
27460     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27461     //stripEl.appendChild(td);
27462     if(closable){
27463         td.className = "x-tabs-closable";
27464         if(!this.closeTpl){
27465             this.closeTpl = new Roo.Template(
27466                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27467                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27468                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27469             );
27470         }
27471         var el = this.closeTpl.overwrite(td, {"text": text});
27472         var close = el.getElementsByTagName("div")[0];
27473         var inner = el.getElementsByTagName("em")[0];
27474         return {"el": el, "close": close, "inner": inner};
27475     } else {
27476         if(!this.tabTpl){
27477             this.tabTpl = new Roo.Template(
27478                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27479                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27480             );
27481         }
27482         var el = this.tabTpl.overwrite(td, {"text": text});
27483         var inner = el.getElementsByTagName("em")[0];
27484         return {"el": el, "inner": inner};
27485     }
27486 };/*
27487  * Based on:
27488  * Ext JS Library 1.1.1
27489  * Copyright(c) 2006-2007, Ext JS, LLC.
27490  *
27491  * Originally Released Under LGPL - original licence link has changed is not relivant.
27492  *
27493  * Fork - LGPL
27494  * <script type="text/javascript">
27495  */
27496
27497 /**
27498  * @class Roo.Button
27499  * @extends Roo.util.Observable
27500  * Simple Button class
27501  * @cfg {String} text The button text
27502  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27503  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27504  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27505  * @cfg {Object} scope The scope of the handler
27506  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27507  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27508  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27509  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27510  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27511  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27512    applies if enableToggle = true)
27513  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27514  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27515   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27516  * @constructor
27517  * Create a new button
27518  * @param {Object} config The config object
27519  */
27520 Roo.Button = function(renderTo, config)
27521 {
27522     if (!config) {
27523         config = renderTo;
27524         renderTo = config.renderTo || false;
27525     }
27526     
27527     Roo.apply(this, config);
27528     this.addEvents({
27529         /**
27530              * @event click
27531              * Fires when this button is clicked
27532              * @param {Button} this
27533              * @param {EventObject} e The click event
27534              */
27535             "click" : true,
27536         /**
27537              * @event toggle
27538              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27539              * @param {Button} this
27540              * @param {Boolean} pressed
27541              */
27542             "toggle" : true,
27543         /**
27544              * @event mouseover
27545              * Fires when the mouse hovers over the button
27546              * @param {Button} this
27547              * @param {Event} e The event object
27548              */
27549         'mouseover' : true,
27550         /**
27551              * @event mouseout
27552              * Fires when the mouse exits the button
27553              * @param {Button} this
27554              * @param {Event} e The event object
27555              */
27556         'mouseout': true,
27557          /**
27558              * @event render
27559              * Fires when the button is rendered
27560              * @param {Button} this
27561              */
27562         'render': true
27563     });
27564     if(this.menu){
27565         this.menu = Roo.menu.MenuMgr.get(this.menu);
27566     }
27567     // register listeners first!!  - so render can be captured..
27568     Roo.util.Observable.call(this);
27569     if(renderTo){
27570         this.render(renderTo);
27571     }
27572     
27573   
27574 };
27575
27576 Roo.extend(Roo.Button, Roo.util.Observable, {
27577     /**
27578      * 
27579      */
27580     
27581     /**
27582      * Read-only. True if this button is hidden
27583      * @type Boolean
27584      */
27585     hidden : false,
27586     /**
27587      * Read-only. True if this button is disabled
27588      * @type Boolean
27589      */
27590     disabled : false,
27591     /**
27592      * Read-only. True if this button is pressed (only if enableToggle = true)
27593      * @type Boolean
27594      */
27595     pressed : false,
27596
27597     /**
27598      * @cfg {Number} tabIndex 
27599      * The DOM tabIndex for this button (defaults to undefined)
27600      */
27601     tabIndex : undefined,
27602
27603     /**
27604      * @cfg {Boolean} enableToggle
27605      * True to enable pressed/not pressed toggling (defaults to false)
27606      */
27607     enableToggle: false,
27608     /**
27609      * @cfg {Mixed} menu
27610      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27611      */
27612     menu : undefined,
27613     /**
27614      * @cfg {String} menuAlign
27615      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27616      */
27617     menuAlign : "tl-bl?",
27618
27619     /**
27620      * @cfg {String} iconCls
27621      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27622      */
27623     iconCls : undefined,
27624     /**
27625      * @cfg {String} type
27626      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27627      */
27628     type : 'button',
27629
27630     // private
27631     menuClassTarget: 'tr',
27632
27633     /**
27634      * @cfg {String} clickEvent
27635      * The type of event to map to the button's event handler (defaults to 'click')
27636      */
27637     clickEvent : 'click',
27638
27639     /**
27640      * @cfg {Boolean} handleMouseEvents
27641      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27642      */
27643     handleMouseEvents : true,
27644
27645     /**
27646      * @cfg {String} tooltipType
27647      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27648      */
27649     tooltipType : 'qtip',
27650
27651     /**
27652      * @cfg {String} cls
27653      * A CSS class to apply to the button's main element.
27654      */
27655     
27656     /**
27657      * @cfg {Roo.Template} template (Optional)
27658      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27659      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27660      * require code modifications if required elements (e.g. a button) aren't present.
27661      */
27662
27663     // private
27664     render : function(renderTo){
27665         var btn;
27666         if(this.hideParent){
27667             this.parentEl = Roo.get(renderTo);
27668         }
27669         if(!this.dhconfig){
27670             if(!this.template){
27671                 if(!Roo.Button.buttonTemplate){
27672                     // hideous table template
27673                     Roo.Button.buttonTemplate = new Roo.Template(
27674                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27675                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
27676                         "</tr></tbody></table>");
27677                 }
27678                 this.template = Roo.Button.buttonTemplate;
27679             }
27680             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27681             var btnEl = btn.child("button:first");
27682             btnEl.on('focus', this.onFocus, this);
27683             btnEl.on('blur', this.onBlur, this);
27684             if(this.cls){
27685                 btn.addClass(this.cls);
27686             }
27687             if(this.icon){
27688                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27689             }
27690             if(this.iconCls){
27691                 btnEl.addClass(this.iconCls);
27692                 if(!this.cls){
27693                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27694                 }
27695             }
27696             if(this.tabIndex !== undefined){
27697                 btnEl.dom.tabIndex = this.tabIndex;
27698             }
27699             if(this.tooltip){
27700                 if(typeof this.tooltip == 'object'){
27701                     Roo.QuickTips.tips(Roo.apply({
27702                           target: btnEl.id
27703                     }, this.tooltip));
27704                 } else {
27705                     btnEl.dom[this.tooltipType] = this.tooltip;
27706                 }
27707             }
27708         }else{
27709             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27710         }
27711         this.el = btn;
27712         if(this.id){
27713             this.el.dom.id = this.el.id = this.id;
27714         }
27715         if(this.menu){
27716             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27717             this.menu.on("show", this.onMenuShow, this);
27718             this.menu.on("hide", this.onMenuHide, this);
27719         }
27720         btn.addClass("x-btn");
27721         if(Roo.isIE && !Roo.isIE7){
27722             this.autoWidth.defer(1, this);
27723         }else{
27724             this.autoWidth();
27725         }
27726         if(this.handleMouseEvents){
27727             btn.on("mouseover", this.onMouseOver, this);
27728             btn.on("mouseout", this.onMouseOut, this);
27729             btn.on("mousedown", this.onMouseDown, this);
27730         }
27731         btn.on(this.clickEvent, this.onClick, this);
27732         //btn.on("mouseup", this.onMouseUp, this);
27733         if(this.hidden){
27734             this.hide();
27735         }
27736         if(this.disabled){
27737             this.disable();
27738         }
27739         Roo.ButtonToggleMgr.register(this);
27740         if(this.pressed){
27741             this.el.addClass("x-btn-pressed");
27742         }
27743         if(this.repeat){
27744             var repeater = new Roo.util.ClickRepeater(btn,
27745                 typeof this.repeat == "object" ? this.repeat : {}
27746             );
27747             repeater.on("click", this.onClick,  this);
27748         }
27749         
27750         this.fireEvent('render', this);
27751         
27752     },
27753     /**
27754      * Returns the button's underlying element
27755      * @return {Roo.Element} The element
27756      */
27757     getEl : function(){
27758         return this.el;  
27759     },
27760     
27761     /**
27762      * Destroys this Button and removes any listeners.
27763      */
27764     destroy : function(){
27765         Roo.ButtonToggleMgr.unregister(this);
27766         this.el.removeAllListeners();
27767         this.purgeListeners();
27768         this.el.remove();
27769     },
27770
27771     // private
27772     autoWidth : function(){
27773         if(this.el){
27774             this.el.setWidth("auto");
27775             if(Roo.isIE7 && Roo.isStrict){
27776                 var ib = this.el.child('button');
27777                 if(ib && ib.getWidth() > 20){
27778                     ib.clip();
27779                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27780                 }
27781             }
27782             if(this.minWidth){
27783                 if(this.hidden){
27784                     this.el.beginMeasure();
27785                 }
27786                 if(this.el.getWidth() < this.minWidth){
27787                     this.el.setWidth(this.minWidth);
27788                 }
27789                 if(this.hidden){
27790                     this.el.endMeasure();
27791                 }
27792             }
27793         }
27794     },
27795
27796     /**
27797      * Assigns this button's click handler
27798      * @param {Function} handler The function to call when the button is clicked
27799      * @param {Object} scope (optional) Scope for the function passed in
27800      */
27801     setHandler : function(handler, scope){
27802         this.handler = handler;
27803         this.scope = scope;  
27804     },
27805     
27806     /**
27807      * Sets this button's text
27808      * @param {String} text The button text
27809      */
27810     setText : function(text){
27811         this.text = text;
27812         if(this.el){
27813             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27814         }
27815         this.autoWidth();
27816     },
27817     
27818     /**
27819      * Gets the text for this button
27820      * @return {String} The button text
27821      */
27822     getText : function(){
27823         return this.text;  
27824     },
27825     
27826     /**
27827      * Show this button
27828      */
27829     show: function(){
27830         this.hidden = false;
27831         if(this.el){
27832             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27833         }
27834     },
27835     
27836     /**
27837      * Hide this button
27838      */
27839     hide: function(){
27840         this.hidden = true;
27841         if(this.el){
27842             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27843         }
27844     },
27845     
27846     /**
27847      * Convenience function for boolean show/hide
27848      * @param {Boolean} visible True to show, false to hide
27849      */
27850     setVisible: function(visible){
27851         if(visible) {
27852             this.show();
27853         }else{
27854             this.hide();
27855         }
27856     },
27857     
27858     /**
27859      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27860      * @param {Boolean} state (optional) Force a particular state
27861      */
27862     toggle : function(state){
27863         state = state === undefined ? !this.pressed : state;
27864         if(state != this.pressed){
27865             if(state){
27866                 this.el.addClass("x-btn-pressed");
27867                 this.pressed = true;
27868                 this.fireEvent("toggle", this, true);
27869             }else{
27870                 this.el.removeClass("x-btn-pressed");
27871                 this.pressed = false;
27872                 this.fireEvent("toggle", this, false);
27873             }
27874             if(this.toggleHandler){
27875                 this.toggleHandler.call(this.scope || this, this, state);
27876             }
27877         }
27878     },
27879     
27880     /**
27881      * Focus the button
27882      */
27883     focus : function(){
27884         this.el.child('button:first').focus();
27885     },
27886     
27887     /**
27888      * Disable this button
27889      */
27890     disable : function(){
27891         if(this.el){
27892             this.el.addClass("x-btn-disabled");
27893         }
27894         this.disabled = true;
27895     },
27896     
27897     /**
27898      * Enable this button
27899      */
27900     enable : function(){
27901         if(this.el){
27902             this.el.removeClass("x-btn-disabled");
27903         }
27904         this.disabled = false;
27905     },
27906
27907     /**
27908      * Convenience function for boolean enable/disable
27909      * @param {Boolean} enabled True to enable, false to disable
27910      */
27911     setDisabled : function(v){
27912         this[v !== true ? "enable" : "disable"]();
27913     },
27914
27915     // private
27916     onClick : function(e)
27917     {
27918         if(e){
27919             e.preventDefault();
27920         }
27921         if(e.button != 0){
27922             return;
27923         }
27924         if(!this.disabled){
27925             if(this.enableToggle){
27926                 this.toggle();
27927             }
27928             if(this.menu && !this.menu.isVisible()){
27929                 this.menu.show(this.el, this.menuAlign);
27930             }
27931             this.fireEvent("click", this, e);
27932             if(this.handler){
27933                 this.el.removeClass("x-btn-over");
27934                 this.handler.call(this.scope || this, this, e);
27935             }
27936         }
27937     },
27938     // private
27939     onMouseOver : function(e){
27940         if(!this.disabled){
27941             this.el.addClass("x-btn-over");
27942             this.fireEvent('mouseover', this, e);
27943         }
27944     },
27945     // private
27946     onMouseOut : function(e){
27947         if(!e.within(this.el,  true)){
27948             this.el.removeClass("x-btn-over");
27949             this.fireEvent('mouseout', this, e);
27950         }
27951     },
27952     // private
27953     onFocus : function(e){
27954         if(!this.disabled){
27955             this.el.addClass("x-btn-focus");
27956         }
27957     },
27958     // private
27959     onBlur : function(e){
27960         this.el.removeClass("x-btn-focus");
27961     },
27962     // private
27963     onMouseDown : function(e){
27964         if(!this.disabled && e.button == 0){
27965             this.el.addClass("x-btn-click");
27966             Roo.get(document).on('mouseup', this.onMouseUp, this);
27967         }
27968     },
27969     // private
27970     onMouseUp : function(e){
27971         if(e.button == 0){
27972             this.el.removeClass("x-btn-click");
27973             Roo.get(document).un('mouseup', this.onMouseUp, this);
27974         }
27975     },
27976     // private
27977     onMenuShow : function(e){
27978         this.el.addClass("x-btn-menu-active");
27979     },
27980     // private
27981     onMenuHide : function(e){
27982         this.el.removeClass("x-btn-menu-active");
27983     }   
27984 });
27985
27986 // Private utility class used by Button
27987 Roo.ButtonToggleMgr = function(){
27988    var groups = {};
27989    
27990    function toggleGroup(btn, state){
27991        if(state){
27992            var g = groups[btn.toggleGroup];
27993            for(var i = 0, l = g.length; i < l; i++){
27994                if(g[i] != btn){
27995                    g[i].toggle(false);
27996                }
27997            }
27998        }
27999    }
28000    
28001    return {
28002        register : function(btn){
28003            if(!btn.toggleGroup){
28004                return;
28005            }
28006            var g = groups[btn.toggleGroup];
28007            if(!g){
28008                g = groups[btn.toggleGroup] = [];
28009            }
28010            g.push(btn);
28011            btn.on("toggle", toggleGroup);
28012        },
28013        
28014        unregister : function(btn){
28015            if(!btn.toggleGroup){
28016                return;
28017            }
28018            var g = groups[btn.toggleGroup];
28019            if(g){
28020                g.remove(btn);
28021                btn.un("toggle", toggleGroup);
28022            }
28023        }
28024    };
28025 }();/*
28026  * Based on:
28027  * Ext JS Library 1.1.1
28028  * Copyright(c) 2006-2007, Ext JS, LLC.
28029  *
28030  * Originally Released Under LGPL - original licence link has changed is not relivant.
28031  *
28032  * Fork - LGPL
28033  * <script type="text/javascript">
28034  */
28035  
28036 /**
28037  * @class Roo.SplitButton
28038  * @extends Roo.Button
28039  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
28040  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28041  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28042  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28043  * @cfg {String} arrowTooltip The title attribute of the arrow
28044  * @constructor
28045  * Create a new menu button
28046  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28047  * @param {Object} config The config object
28048  */
28049 Roo.SplitButton = function(renderTo, config){
28050     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28051     /**
28052      * @event arrowclick
28053      * Fires when this button's arrow is clicked
28054      * @param {SplitButton} this
28055      * @param {EventObject} e The click event
28056      */
28057     this.addEvents({"arrowclick":true});
28058 };
28059
28060 Roo.extend(Roo.SplitButton, Roo.Button, {
28061     render : function(renderTo){
28062         // this is one sweet looking template!
28063         var tpl = new Roo.Template(
28064             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28065             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28066             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
28067             "</tbody></table></td><td>",
28068             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28069             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
28070             "</tbody></table></td></tr></table>"
28071         );
28072         var btn = tpl.append(renderTo, [this.text, this.type], true);
28073         var btnEl = btn.child("button");
28074         if(this.cls){
28075             btn.addClass(this.cls);
28076         }
28077         if(this.icon){
28078             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28079         }
28080         if(this.iconCls){
28081             btnEl.addClass(this.iconCls);
28082             if(!this.cls){
28083                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28084             }
28085         }
28086         this.el = btn;
28087         if(this.handleMouseEvents){
28088             btn.on("mouseover", this.onMouseOver, this);
28089             btn.on("mouseout", this.onMouseOut, this);
28090             btn.on("mousedown", this.onMouseDown, this);
28091             btn.on("mouseup", this.onMouseUp, this);
28092         }
28093         btn.on(this.clickEvent, this.onClick, this);
28094         if(this.tooltip){
28095             if(typeof this.tooltip == 'object'){
28096                 Roo.QuickTips.tips(Roo.apply({
28097                       target: btnEl.id
28098                 }, this.tooltip));
28099             } else {
28100                 btnEl.dom[this.tooltipType] = this.tooltip;
28101             }
28102         }
28103         if(this.arrowTooltip){
28104             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28105         }
28106         if(this.hidden){
28107             this.hide();
28108         }
28109         if(this.disabled){
28110             this.disable();
28111         }
28112         if(this.pressed){
28113             this.el.addClass("x-btn-pressed");
28114         }
28115         if(Roo.isIE && !Roo.isIE7){
28116             this.autoWidth.defer(1, this);
28117         }else{
28118             this.autoWidth();
28119         }
28120         if(this.menu){
28121             this.menu.on("show", this.onMenuShow, this);
28122             this.menu.on("hide", this.onMenuHide, this);
28123         }
28124         this.fireEvent('render', this);
28125     },
28126
28127     // private
28128     autoWidth : function(){
28129         if(this.el){
28130             var tbl = this.el.child("table:first");
28131             var tbl2 = this.el.child("table:last");
28132             this.el.setWidth("auto");
28133             tbl.setWidth("auto");
28134             if(Roo.isIE7 && Roo.isStrict){
28135                 var ib = this.el.child('button:first');
28136                 if(ib && ib.getWidth() > 20){
28137                     ib.clip();
28138                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28139                 }
28140             }
28141             if(this.minWidth){
28142                 if(this.hidden){
28143                     this.el.beginMeasure();
28144                 }
28145                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28146                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28147                 }
28148                 if(this.hidden){
28149                     this.el.endMeasure();
28150                 }
28151             }
28152             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28153         } 
28154     },
28155     /**
28156      * Sets this button's click handler
28157      * @param {Function} handler The function to call when the button is clicked
28158      * @param {Object} scope (optional) Scope for the function passed above
28159      */
28160     setHandler : function(handler, scope){
28161         this.handler = handler;
28162         this.scope = scope;  
28163     },
28164     
28165     /**
28166      * Sets this button's arrow click handler
28167      * @param {Function} handler The function to call when the arrow is clicked
28168      * @param {Object} scope (optional) Scope for the function passed above
28169      */
28170     setArrowHandler : function(handler, scope){
28171         this.arrowHandler = handler;
28172         this.scope = scope;  
28173     },
28174     
28175     /**
28176      * Focus the button
28177      */
28178     focus : function(){
28179         if(this.el){
28180             this.el.child("button:first").focus();
28181         }
28182     },
28183
28184     // private
28185     onClick : function(e){
28186         e.preventDefault();
28187         if(!this.disabled){
28188             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28189                 if(this.menu && !this.menu.isVisible()){
28190                     this.menu.show(this.el, this.menuAlign);
28191                 }
28192                 this.fireEvent("arrowclick", this, e);
28193                 if(this.arrowHandler){
28194                     this.arrowHandler.call(this.scope || this, this, e);
28195                 }
28196             }else{
28197                 this.fireEvent("click", this, e);
28198                 if(this.handler){
28199                     this.handler.call(this.scope || this, this, e);
28200                 }
28201             }
28202         }
28203     },
28204     // private
28205     onMouseDown : function(e){
28206         if(!this.disabled){
28207             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28208         }
28209     },
28210     // private
28211     onMouseUp : function(e){
28212         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28213     }   
28214 });
28215
28216
28217 // backwards compat
28218 Roo.MenuButton = Roo.SplitButton;/*
28219  * Based on:
28220  * Ext JS Library 1.1.1
28221  * Copyright(c) 2006-2007, Ext JS, LLC.
28222  *
28223  * Originally Released Under LGPL - original licence link has changed is not relivant.
28224  *
28225  * Fork - LGPL
28226  * <script type="text/javascript">
28227  */
28228
28229 /**
28230  * @class Roo.Toolbar
28231  * Basic Toolbar class.
28232  * @constructor
28233  * Creates a new Toolbar
28234  * @param {Object} container The config object
28235  */ 
28236 Roo.Toolbar = function(container, buttons, config)
28237 {
28238     /// old consturctor format still supported..
28239     if(container instanceof Array){ // omit the container for later rendering
28240         buttons = container;
28241         config = buttons;
28242         container = null;
28243     }
28244     if (typeof(container) == 'object' && container.xtype) {
28245         config = container;
28246         container = config.container;
28247         buttons = config.buttons || []; // not really - use items!!
28248     }
28249     var xitems = [];
28250     if (config && config.items) {
28251         xitems = config.items;
28252         delete config.items;
28253     }
28254     Roo.apply(this, config);
28255     this.buttons = buttons;
28256     
28257     if(container){
28258         this.render(container);
28259     }
28260     this.xitems = xitems;
28261     Roo.each(xitems, function(b) {
28262         this.add(b);
28263     }, this);
28264     
28265 };
28266
28267 Roo.Toolbar.prototype = {
28268     /**
28269      * @cfg {Array} items
28270      * array of button configs or elements to add (will be converted to a MixedCollection)
28271      */
28272     
28273     /**
28274      * @cfg {String/HTMLElement/Element} container
28275      * The id or element that will contain the toolbar
28276      */
28277     // private
28278     render : function(ct){
28279         this.el = Roo.get(ct);
28280         if(this.cls){
28281             this.el.addClass(this.cls);
28282         }
28283         // using a table allows for vertical alignment
28284         // 100% width is needed by Safari...
28285         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28286         this.tr = this.el.child("tr", true);
28287         var autoId = 0;
28288         this.items = new Roo.util.MixedCollection(false, function(o){
28289             return o.id || ("item" + (++autoId));
28290         });
28291         if(this.buttons){
28292             this.add.apply(this, this.buttons);
28293             delete this.buttons;
28294         }
28295     },
28296
28297     /**
28298      * Adds element(s) to the toolbar -- this function takes a variable number of 
28299      * arguments of mixed type and adds them to the toolbar.
28300      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28301      * <ul>
28302      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28303      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28304      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28305      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28306      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28307      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28308      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28309      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28310      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28311      * </ul>
28312      * @param {Mixed} arg2
28313      * @param {Mixed} etc.
28314      */
28315     add : function(){
28316         var a = arguments, l = a.length;
28317         for(var i = 0; i < l; i++){
28318             this._add(a[i]);
28319         }
28320     },
28321     // private..
28322     _add : function(el) {
28323         
28324         if (el.xtype) {
28325             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28326         }
28327         
28328         if (el.applyTo){ // some kind of form field
28329             return this.addField(el);
28330         } 
28331         if (el.render){ // some kind of Toolbar.Item
28332             return this.addItem(el);
28333         }
28334         if (typeof el == "string"){ // string
28335             if(el == "separator" || el == "-"){
28336                 return this.addSeparator();
28337             }
28338             if (el == " "){
28339                 return this.addSpacer();
28340             }
28341             if(el == "->"){
28342                 return this.addFill();
28343             }
28344             return this.addText(el);
28345             
28346         }
28347         if(el.tagName){ // element
28348             return this.addElement(el);
28349         }
28350         if(typeof el == "object"){ // must be button config?
28351             return this.addButton(el);
28352         }
28353         // and now what?!?!
28354         return false;
28355         
28356     },
28357     
28358     /**
28359      * Add an Xtype element
28360      * @param {Object} xtype Xtype Object
28361      * @return {Object} created Object
28362      */
28363     addxtype : function(e){
28364         return this.add(e);  
28365     },
28366     
28367     /**
28368      * Returns the Element for this toolbar.
28369      * @return {Roo.Element}
28370      */
28371     getEl : function(){
28372         return this.el;  
28373     },
28374     
28375     /**
28376      * Adds a separator
28377      * @return {Roo.Toolbar.Item} The separator item
28378      */
28379     addSeparator : function(){
28380         return this.addItem(new Roo.Toolbar.Separator());
28381     },
28382
28383     /**
28384      * Adds a spacer element
28385      * @return {Roo.Toolbar.Spacer} The spacer item
28386      */
28387     addSpacer : function(){
28388         return this.addItem(new Roo.Toolbar.Spacer());
28389     },
28390
28391     /**
28392      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28393      * @return {Roo.Toolbar.Fill} The fill item
28394      */
28395     addFill : function(){
28396         return this.addItem(new Roo.Toolbar.Fill());
28397     },
28398
28399     /**
28400      * Adds any standard HTML element to the toolbar
28401      * @param {String/HTMLElement/Element} el The element or id of the element to add
28402      * @return {Roo.Toolbar.Item} The element's item
28403      */
28404     addElement : function(el){
28405         return this.addItem(new Roo.Toolbar.Item(el));
28406     },
28407     /**
28408      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28409      * @type Roo.util.MixedCollection  
28410      */
28411     items : false,
28412      
28413     /**
28414      * Adds any Toolbar.Item or subclass
28415      * @param {Roo.Toolbar.Item} item
28416      * @return {Roo.Toolbar.Item} The item
28417      */
28418     addItem : function(item){
28419         var td = this.nextBlock();
28420         item.render(td);
28421         this.items.add(item);
28422         return item;
28423     },
28424     
28425     /**
28426      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28427      * @param {Object/Array} config A button config or array of configs
28428      * @return {Roo.Toolbar.Button/Array}
28429      */
28430     addButton : function(config){
28431         if(config instanceof Array){
28432             var buttons = [];
28433             for(var i = 0, len = config.length; i < len; i++) {
28434                 buttons.push(this.addButton(config[i]));
28435             }
28436             return buttons;
28437         }
28438         var b = config;
28439         if(!(config instanceof Roo.Toolbar.Button)){
28440             b = config.split ?
28441                 new Roo.Toolbar.SplitButton(config) :
28442                 new Roo.Toolbar.Button(config);
28443         }
28444         var td = this.nextBlock();
28445         b.render(td);
28446         this.items.add(b);
28447         return b;
28448     },
28449     
28450     /**
28451      * Adds text to the toolbar
28452      * @param {String} text The text to add
28453      * @return {Roo.Toolbar.Item} The element's item
28454      */
28455     addText : function(text){
28456         return this.addItem(new Roo.Toolbar.TextItem(text));
28457     },
28458     
28459     /**
28460      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28461      * @param {Number} index The index where the item is to be inserted
28462      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28463      * @return {Roo.Toolbar.Button/Item}
28464      */
28465     insertButton : function(index, item){
28466         if(item instanceof Array){
28467             var buttons = [];
28468             for(var i = 0, len = item.length; i < len; i++) {
28469                buttons.push(this.insertButton(index + i, item[i]));
28470             }
28471             return buttons;
28472         }
28473         if (!(item instanceof Roo.Toolbar.Button)){
28474            item = new Roo.Toolbar.Button(item);
28475         }
28476         var td = document.createElement("td");
28477         this.tr.insertBefore(td, this.tr.childNodes[index]);
28478         item.render(td);
28479         this.items.insert(index, item);
28480         return item;
28481     },
28482     
28483     /**
28484      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28485      * @param {Object} config
28486      * @return {Roo.Toolbar.Item} The element's item
28487      */
28488     addDom : function(config, returnEl){
28489         var td = this.nextBlock();
28490         Roo.DomHelper.overwrite(td, config);
28491         var ti = new Roo.Toolbar.Item(td.firstChild);
28492         ti.render(td);
28493         this.items.add(ti);
28494         return ti;
28495     },
28496
28497     /**
28498      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28499      * @type Roo.util.MixedCollection  
28500      */
28501     fields : false,
28502     
28503     /**
28504      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28505      * Note: the field should not have been rendered yet. For a field that has already been
28506      * rendered, use {@link #addElement}.
28507      * @param {Roo.form.Field} field
28508      * @return {Roo.ToolbarItem}
28509      */
28510      
28511       
28512     addField : function(field) {
28513         if (!this.fields) {
28514             var autoId = 0;
28515             this.fields = new Roo.util.MixedCollection(false, function(o){
28516                 return o.id || ("item" + (++autoId));
28517             });
28518
28519         }
28520         
28521         var td = this.nextBlock();
28522         field.render(td);
28523         var ti = new Roo.Toolbar.Item(td.firstChild);
28524         ti.render(td);
28525         this.items.add(ti);
28526         this.fields.add(field);
28527         return ti;
28528     },
28529     /**
28530      * Hide the toolbar
28531      * @method hide
28532      */
28533      
28534       
28535     hide : function()
28536     {
28537         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28538         this.el.child('div').hide();
28539     },
28540     /**
28541      * Show the toolbar
28542      * @method show
28543      */
28544     show : function()
28545     {
28546         this.el.child('div').show();
28547     },
28548       
28549     // private
28550     nextBlock : function(){
28551         var td = document.createElement("td");
28552         this.tr.appendChild(td);
28553         return td;
28554     },
28555
28556     // private
28557     destroy : function(){
28558         if(this.items){ // rendered?
28559             Roo.destroy.apply(Roo, this.items.items);
28560         }
28561         if(this.fields){ // rendered?
28562             Roo.destroy.apply(Roo, this.fields.items);
28563         }
28564         Roo.Element.uncache(this.el, this.tr);
28565     }
28566 };
28567
28568 /**
28569  * @class Roo.Toolbar.Item
28570  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28571  * @constructor
28572  * Creates a new Item
28573  * @param {HTMLElement} el 
28574  */
28575 Roo.Toolbar.Item = function(el){
28576     var cfg = {};
28577     if (typeof (el.xtype) != 'undefined') {
28578         cfg = el;
28579         el = cfg.el;
28580     }
28581     
28582     this.el = Roo.getDom(el);
28583     this.id = Roo.id(this.el);
28584     this.hidden = false;
28585     
28586     this.addEvents({
28587          /**
28588              * @event render
28589              * Fires when the button is rendered
28590              * @param {Button} this
28591              */
28592         'render': true
28593     });
28594     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28595 };
28596 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28597 //Roo.Toolbar.Item.prototype = {
28598     
28599     /**
28600      * Get this item's HTML Element
28601      * @return {HTMLElement}
28602      */
28603     getEl : function(){
28604        return this.el;  
28605     },
28606
28607     // private
28608     render : function(td){
28609         
28610          this.td = td;
28611         td.appendChild(this.el);
28612         
28613         this.fireEvent('render', this);
28614     },
28615     
28616     /**
28617      * Removes and destroys this item.
28618      */
28619     destroy : function(){
28620         this.td.parentNode.removeChild(this.td);
28621     },
28622     
28623     /**
28624      * Shows this item.
28625      */
28626     show: function(){
28627         this.hidden = false;
28628         this.td.style.display = "";
28629     },
28630     
28631     /**
28632      * Hides this item.
28633      */
28634     hide: function(){
28635         this.hidden = true;
28636         this.td.style.display = "none";
28637     },
28638     
28639     /**
28640      * Convenience function for boolean show/hide.
28641      * @param {Boolean} visible true to show/false to hide
28642      */
28643     setVisible: function(visible){
28644         if(visible) {
28645             this.show();
28646         }else{
28647             this.hide();
28648         }
28649     },
28650     
28651     /**
28652      * Try to focus this item.
28653      */
28654     focus : function(){
28655         Roo.fly(this.el).focus();
28656     },
28657     
28658     /**
28659      * Disables this item.
28660      */
28661     disable : function(){
28662         Roo.fly(this.td).addClass("x-item-disabled");
28663         this.disabled = true;
28664         this.el.disabled = true;
28665     },
28666     
28667     /**
28668      * Enables this item.
28669      */
28670     enable : function(){
28671         Roo.fly(this.td).removeClass("x-item-disabled");
28672         this.disabled = false;
28673         this.el.disabled = false;
28674     }
28675 });
28676
28677
28678 /**
28679  * @class Roo.Toolbar.Separator
28680  * @extends Roo.Toolbar.Item
28681  * A simple toolbar separator class
28682  * @constructor
28683  * Creates a new Separator
28684  */
28685 Roo.Toolbar.Separator = function(cfg){
28686     
28687     var s = document.createElement("span");
28688     s.className = "ytb-sep";
28689     if (cfg) {
28690         cfg.el = s;
28691     }
28692     
28693     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28694 };
28695 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28696     enable:Roo.emptyFn,
28697     disable:Roo.emptyFn,
28698     focus:Roo.emptyFn
28699 });
28700
28701 /**
28702  * @class Roo.Toolbar.Spacer
28703  * @extends Roo.Toolbar.Item
28704  * A simple element that adds extra horizontal space to a toolbar.
28705  * @constructor
28706  * Creates a new Spacer
28707  */
28708 Roo.Toolbar.Spacer = function(cfg){
28709     var s = document.createElement("div");
28710     s.className = "ytb-spacer";
28711     if (cfg) {
28712         cfg.el = s;
28713     }
28714     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28715 };
28716 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28717     enable:Roo.emptyFn,
28718     disable:Roo.emptyFn,
28719     focus:Roo.emptyFn
28720 });
28721
28722 /**
28723  * @class Roo.Toolbar.Fill
28724  * @extends Roo.Toolbar.Spacer
28725  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28726  * @constructor
28727  * Creates a new Spacer
28728  */
28729 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28730     // private
28731     render : function(td){
28732         td.style.width = '100%';
28733         Roo.Toolbar.Fill.superclass.render.call(this, td);
28734     }
28735 });
28736
28737 /**
28738  * @class Roo.Toolbar.TextItem
28739  * @extends Roo.Toolbar.Item
28740  * A simple class that renders text directly into a toolbar.
28741  * @constructor
28742  * Creates a new TextItem
28743  * @param {String} text
28744  */
28745 Roo.Toolbar.TextItem = function(cfg){
28746     var  text = cfg || "";
28747     if (typeof(cfg) == 'object') {
28748         text = cfg.text || "";
28749     }  else {
28750         cfg = null;
28751     }
28752     var s = document.createElement("span");
28753     s.className = "ytb-text";
28754     s.innerHTML = text;
28755     if (cfg) {
28756         cfg.el  = s;
28757     }
28758     
28759     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28760 };
28761 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28762     
28763      
28764     enable:Roo.emptyFn,
28765     disable:Roo.emptyFn,
28766     focus:Roo.emptyFn
28767 });
28768
28769 /**
28770  * @class Roo.Toolbar.Button
28771  * @extends Roo.Button
28772  * A button that renders into a toolbar.
28773  * @constructor
28774  * Creates a new Button
28775  * @param {Object} config A standard {@link Roo.Button} config object
28776  */
28777 Roo.Toolbar.Button = function(config){
28778     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28779 };
28780 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28781     render : function(td){
28782         this.td = td;
28783         Roo.Toolbar.Button.superclass.render.call(this, td);
28784     },
28785     
28786     /**
28787      * Removes and destroys this button
28788      */
28789     destroy : function(){
28790         Roo.Toolbar.Button.superclass.destroy.call(this);
28791         this.td.parentNode.removeChild(this.td);
28792     },
28793     
28794     /**
28795      * Shows this button
28796      */
28797     show: function(){
28798         this.hidden = false;
28799         this.td.style.display = "";
28800     },
28801     
28802     /**
28803      * Hides this button
28804      */
28805     hide: function(){
28806         this.hidden = true;
28807         this.td.style.display = "none";
28808     },
28809
28810     /**
28811      * Disables this item
28812      */
28813     disable : function(){
28814         Roo.fly(this.td).addClass("x-item-disabled");
28815         this.disabled = true;
28816     },
28817
28818     /**
28819      * Enables this item
28820      */
28821     enable : function(){
28822         Roo.fly(this.td).removeClass("x-item-disabled");
28823         this.disabled = false;
28824     }
28825 });
28826 // backwards compat
28827 Roo.ToolbarButton = Roo.Toolbar.Button;
28828
28829 /**
28830  * @class Roo.Toolbar.SplitButton
28831  * @extends Roo.SplitButton
28832  * A menu button that renders into a toolbar.
28833  * @constructor
28834  * Creates a new SplitButton
28835  * @param {Object} config A standard {@link Roo.SplitButton} config object
28836  */
28837 Roo.Toolbar.SplitButton = function(config){
28838     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28839 };
28840 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28841     render : function(td){
28842         this.td = td;
28843         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28844     },
28845     
28846     /**
28847      * Removes and destroys this button
28848      */
28849     destroy : function(){
28850         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28851         this.td.parentNode.removeChild(this.td);
28852     },
28853     
28854     /**
28855      * Shows this button
28856      */
28857     show: function(){
28858         this.hidden = false;
28859         this.td.style.display = "";
28860     },
28861     
28862     /**
28863      * Hides this button
28864      */
28865     hide: function(){
28866         this.hidden = true;
28867         this.td.style.display = "none";
28868     }
28869 });
28870
28871 // backwards compat
28872 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28873  * Based on:
28874  * Ext JS Library 1.1.1
28875  * Copyright(c) 2006-2007, Ext JS, LLC.
28876  *
28877  * Originally Released Under LGPL - original licence link has changed is not relivant.
28878  *
28879  * Fork - LGPL
28880  * <script type="text/javascript">
28881  */
28882  
28883 /**
28884  * @class Roo.PagingToolbar
28885  * @extends Roo.Toolbar
28886  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28887  * @constructor
28888  * Create a new PagingToolbar
28889  * @param {Object} config The config object
28890  */
28891 Roo.PagingToolbar = function(el, ds, config)
28892 {
28893     // old args format still supported... - xtype is prefered..
28894     if (typeof(el) == 'object' && el.xtype) {
28895         // created from xtype...
28896         config = el;
28897         ds = el.dataSource;
28898         el = config.container;
28899     }
28900     var items = [];
28901     if (config.items) {
28902         items = config.items;
28903         config.items = [];
28904     }
28905     
28906     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28907     this.ds = ds;
28908     this.cursor = 0;
28909     this.renderButtons(this.el);
28910     this.bind(ds);
28911     
28912     // supprot items array.
28913    
28914     Roo.each(items, function(e) {
28915         this.add(Roo.factory(e));
28916     },this);
28917     
28918 };
28919
28920 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28921     /**
28922      * @cfg {Roo.data.Store} dataSource
28923      * The underlying data store providing the paged data
28924      */
28925     /**
28926      * @cfg {String/HTMLElement/Element} container
28927      * container The id or element that will contain the toolbar
28928      */
28929     /**
28930      * @cfg {Boolean} displayInfo
28931      * True to display the displayMsg (defaults to false)
28932      */
28933     /**
28934      * @cfg {Number} pageSize
28935      * The number of records to display per page (defaults to 20)
28936      */
28937     pageSize: 20,
28938     /**
28939      * @cfg {String} displayMsg
28940      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28941      */
28942     displayMsg : 'Displaying {0} - {1} of {2}',
28943     /**
28944      * @cfg {String} emptyMsg
28945      * The message to display when no records are found (defaults to "No data to display")
28946      */
28947     emptyMsg : 'No data to display',
28948     /**
28949      * Customizable piece of the default paging text (defaults to "Page")
28950      * @type String
28951      */
28952     beforePageText : "Page",
28953     /**
28954      * Customizable piece of the default paging text (defaults to "of %0")
28955      * @type String
28956      */
28957     afterPageText : "of {0}",
28958     /**
28959      * Customizable piece of the default paging text (defaults to "First Page")
28960      * @type String
28961      */
28962     firstText : "First Page",
28963     /**
28964      * Customizable piece of the default paging text (defaults to "Previous Page")
28965      * @type String
28966      */
28967     prevText : "Previous Page",
28968     /**
28969      * Customizable piece of the default paging text (defaults to "Next Page")
28970      * @type String
28971      */
28972     nextText : "Next Page",
28973     /**
28974      * Customizable piece of the default paging text (defaults to "Last Page")
28975      * @type String
28976      */
28977     lastText : "Last Page",
28978     /**
28979      * Customizable piece of the default paging text (defaults to "Refresh")
28980      * @type String
28981      */
28982     refreshText : "Refresh",
28983
28984     // private
28985     renderButtons : function(el){
28986         Roo.PagingToolbar.superclass.render.call(this, el);
28987         this.first = this.addButton({
28988             tooltip: this.firstText,
28989             cls: "x-btn-icon x-grid-page-first",
28990             disabled: true,
28991             handler: this.onClick.createDelegate(this, ["first"])
28992         });
28993         this.prev = this.addButton({
28994             tooltip: this.prevText,
28995             cls: "x-btn-icon x-grid-page-prev",
28996             disabled: true,
28997             handler: this.onClick.createDelegate(this, ["prev"])
28998         });
28999         //this.addSeparator();
29000         this.add(this.beforePageText);
29001         this.field = Roo.get(this.addDom({
29002            tag: "input",
29003            type: "text",
29004            size: "3",
29005            value: "1",
29006            cls: "x-grid-page-number"
29007         }).el);
29008         this.field.on("keydown", this.onPagingKeydown, this);
29009         this.field.on("focus", function(){this.dom.select();});
29010         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
29011         this.field.setHeight(18);
29012         //this.addSeparator();
29013         this.next = this.addButton({
29014             tooltip: this.nextText,
29015             cls: "x-btn-icon x-grid-page-next",
29016             disabled: true,
29017             handler: this.onClick.createDelegate(this, ["next"])
29018         });
29019         this.last = this.addButton({
29020             tooltip: this.lastText,
29021             cls: "x-btn-icon x-grid-page-last",
29022             disabled: true,
29023             handler: this.onClick.createDelegate(this, ["last"])
29024         });
29025         //this.addSeparator();
29026         this.loading = this.addButton({
29027             tooltip: this.refreshText,
29028             cls: "x-btn-icon x-grid-loading",
29029             handler: this.onClick.createDelegate(this, ["refresh"])
29030         });
29031
29032         if(this.displayInfo){
29033             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
29034         }
29035     },
29036
29037     // private
29038     updateInfo : function(){
29039         if(this.displayEl){
29040             var count = this.ds.getCount();
29041             var msg = count == 0 ?
29042                 this.emptyMsg :
29043                 String.format(
29044                     this.displayMsg,
29045                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29046                 );
29047             this.displayEl.update(msg);
29048         }
29049     },
29050
29051     // private
29052     onLoad : function(ds, r, o){
29053        this.cursor = o.params ? o.params.start : 0;
29054        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29055
29056        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29057        this.field.dom.value = ap;
29058        this.first.setDisabled(ap == 1);
29059        this.prev.setDisabled(ap == 1);
29060        this.next.setDisabled(ap == ps);
29061        this.last.setDisabled(ap == ps);
29062        this.loading.enable();
29063        this.updateInfo();
29064     },
29065
29066     // private
29067     getPageData : function(){
29068         var total = this.ds.getTotalCount();
29069         return {
29070             total : total,
29071             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29072             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29073         };
29074     },
29075
29076     // private
29077     onLoadError : function(){
29078         this.loading.enable();
29079     },
29080
29081     // private
29082     onPagingKeydown : function(e){
29083         var k = e.getKey();
29084         var d = this.getPageData();
29085         if(k == e.RETURN){
29086             var v = this.field.dom.value, pageNum;
29087             if(!v || isNaN(pageNum = parseInt(v, 10))){
29088                 this.field.dom.value = d.activePage;
29089                 return;
29090             }
29091             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29092             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29093             e.stopEvent();
29094         }
29095         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
29096         {
29097           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29098           this.field.dom.value = pageNum;
29099           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29100           e.stopEvent();
29101         }
29102         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29103         {
29104           var v = this.field.dom.value, pageNum; 
29105           var increment = (e.shiftKey) ? 10 : 1;
29106           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
29107             increment *= -1;
29108           }
29109           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29110             this.field.dom.value = d.activePage;
29111             return;
29112           }
29113           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29114           {
29115             this.field.dom.value = parseInt(v, 10) + increment;
29116             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29117             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29118           }
29119           e.stopEvent();
29120         }
29121     },
29122
29123     // private
29124     beforeLoad : function(){
29125         if(this.loading){
29126             this.loading.disable();
29127         }
29128     },
29129
29130     // private
29131     onClick : function(which){
29132         var ds = this.ds;
29133         switch(which){
29134             case "first":
29135                 ds.load({params:{start: 0, limit: this.pageSize}});
29136             break;
29137             case "prev":
29138                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29139             break;
29140             case "next":
29141                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29142             break;
29143             case "last":
29144                 var total = ds.getTotalCount();
29145                 var extra = total % this.pageSize;
29146                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29147                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29148             break;
29149             case "refresh":
29150                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29151             break;
29152         }
29153     },
29154
29155     /**
29156      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29157      * @param {Roo.data.Store} store The data store to unbind
29158      */
29159     unbind : function(ds){
29160         ds.un("beforeload", this.beforeLoad, this);
29161         ds.un("load", this.onLoad, this);
29162         ds.un("loadexception", this.onLoadError, this);
29163         ds.un("remove", this.updateInfo, this);
29164         ds.un("add", this.updateInfo, this);
29165         this.ds = undefined;
29166     },
29167
29168     /**
29169      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29170      * @param {Roo.data.Store} store The data store to bind
29171      */
29172     bind : function(ds){
29173         ds.on("beforeload", this.beforeLoad, this);
29174         ds.on("load", this.onLoad, this);
29175         ds.on("loadexception", this.onLoadError, this);
29176         ds.on("remove", this.updateInfo, this);
29177         ds.on("add", this.updateInfo, this);
29178         this.ds = ds;
29179     }
29180 });/*
29181  * Based on:
29182  * Ext JS Library 1.1.1
29183  * Copyright(c) 2006-2007, Ext JS, LLC.
29184  *
29185  * Originally Released Under LGPL - original licence link has changed is not relivant.
29186  *
29187  * Fork - LGPL
29188  * <script type="text/javascript">
29189  */
29190
29191 /**
29192  * @class Roo.Resizable
29193  * @extends Roo.util.Observable
29194  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29195  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29196  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
29197  * the element will be wrapped for you automatically.</p>
29198  * <p>Here is the list of valid resize handles:</p>
29199  * <pre>
29200 Value   Description
29201 ------  -------------------
29202  'n'     north
29203  's'     south
29204  'e'     east
29205  'w'     west
29206  'nw'    northwest
29207  'sw'    southwest
29208  'se'    southeast
29209  'ne'    northeast
29210  'hd'    horizontal drag
29211  'all'   all
29212 </pre>
29213  * <p>Here's an example showing the creation of a typical Resizable:</p>
29214  * <pre><code>
29215 var resizer = new Roo.Resizable("element-id", {
29216     handles: 'all',
29217     minWidth: 200,
29218     minHeight: 100,
29219     maxWidth: 500,
29220     maxHeight: 400,
29221     pinned: true
29222 });
29223 resizer.on("resize", myHandler);
29224 </code></pre>
29225  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29226  * resizer.east.setDisplayed(false);</p>
29227  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29228  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29229  * resize operation's new size (defaults to [0, 0])
29230  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29231  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29232  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29233  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29234  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29235  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29236  * @cfg {Number} width The width of the element in pixels (defaults to null)
29237  * @cfg {Number} height The height of the element in pixels (defaults to null)
29238  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29239  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29240  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29241  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29242  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29243  * in favor of the handles config option (defaults to false)
29244  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29245  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29246  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29247  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29248  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29249  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29250  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29251  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29252  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29253  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29254  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29255  * @constructor
29256  * Create a new resizable component
29257  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29258  * @param {Object} config configuration options
29259   */
29260 Roo.Resizable = function(el, config)
29261 {
29262     this.el = Roo.get(el);
29263
29264     if(config && config.wrap){
29265         config.resizeChild = this.el;
29266         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29267         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29268         this.el.setStyle("overflow", "hidden");
29269         this.el.setPositioning(config.resizeChild.getPositioning());
29270         config.resizeChild.clearPositioning();
29271         if(!config.width || !config.height){
29272             var csize = config.resizeChild.getSize();
29273             this.el.setSize(csize.width, csize.height);
29274         }
29275         if(config.pinned && !config.adjustments){
29276             config.adjustments = "auto";
29277         }
29278     }
29279
29280     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29281     this.proxy.unselectable();
29282     this.proxy.enableDisplayMode('block');
29283
29284     Roo.apply(this, config);
29285
29286     if(this.pinned){
29287         this.disableTrackOver = true;
29288         this.el.addClass("x-resizable-pinned");
29289     }
29290     // if the element isn't positioned, make it relative
29291     var position = this.el.getStyle("position");
29292     if(position != "absolute" && position != "fixed"){
29293         this.el.setStyle("position", "relative");
29294     }
29295     if(!this.handles){ // no handles passed, must be legacy style
29296         this.handles = 's,e,se';
29297         if(this.multiDirectional){
29298             this.handles += ',n,w';
29299         }
29300     }
29301     if(this.handles == "all"){
29302         this.handles = "n s e w ne nw se sw";
29303     }
29304     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29305     var ps = Roo.Resizable.positions;
29306     for(var i = 0, len = hs.length; i < len; i++){
29307         if(hs[i] && ps[hs[i]]){
29308             var pos = ps[hs[i]];
29309             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29310         }
29311     }
29312     // legacy
29313     this.corner = this.southeast;
29314     
29315     // updateBox = the box can move..
29316     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29317         this.updateBox = true;
29318     }
29319
29320     this.activeHandle = null;
29321
29322     if(this.resizeChild){
29323         if(typeof this.resizeChild == "boolean"){
29324             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29325         }else{
29326             this.resizeChild = Roo.get(this.resizeChild, true);
29327         }
29328     }
29329     
29330     if(this.adjustments == "auto"){
29331         var rc = this.resizeChild;
29332         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29333         if(rc && (hw || hn)){
29334             rc.position("relative");
29335             rc.setLeft(hw ? hw.el.getWidth() : 0);
29336             rc.setTop(hn ? hn.el.getHeight() : 0);
29337         }
29338         this.adjustments = [
29339             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29340             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29341         ];
29342     }
29343
29344     if(this.draggable){
29345         this.dd = this.dynamic ?
29346             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29347         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29348     }
29349
29350     // public events
29351     this.addEvents({
29352         /**
29353          * @event beforeresize
29354          * Fired before resize is allowed. Set enabled to false to cancel resize.
29355          * @param {Roo.Resizable} this
29356          * @param {Roo.EventObject} e The mousedown event
29357          */
29358         "beforeresize" : true,
29359         /**
29360          * @event resizing
29361          * Fired a resizing.
29362          * @param {Roo.Resizable} this
29363          * @param {Number} x The new x position
29364          * @param {Number} y The new y position
29365          * @param {Number} w The new w width
29366          * @param {Number} h The new h hight
29367          * @param {Roo.EventObject} e The mouseup event
29368          */
29369         "resizing" : true,
29370         /**
29371          * @event resize
29372          * Fired after a resize.
29373          * @param {Roo.Resizable} this
29374          * @param {Number} width The new width
29375          * @param {Number} height The new height
29376          * @param {Roo.EventObject} e The mouseup event
29377          */
29378         "resize" : true
29379     });
29380
29381     if(this.width !== null && this.height !== null){
29382         this.resizeTo(this.width, this.height);
29383     }else{
29384         this.updateChildSize();
29385     }
29386     if(Roo.isIE){
29387         this.el.dom.style.zoom = 1;
29388     }
29389     Roo.Resizable.superclass.constructor.call(this);
29390 };
29391
29392 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29393         resizeChild : false,
29394         adjustments : [0, 0],
29395         minWidth : 5,
29396         minHeight : 5,
29397         maxWidth : 10000,
29398         maxHeight : 10000,
29399         enabled : true,
29400         animate : false,
29401         duration : .35,
29402         dynamic : false,
29403         handles : false,
29404         multiDirectional : false,
29405         disableTrackOver : false,
29406         easing : 'easeOutStrong',
29407         widthIncrement : 0,
29408         heightIncrement : 0,
29409         pinned : false,
29410         width : null,
29411         height : null,
29412         preserveRatio : false,
29413         transparent: false,
29414         minX: 0,
29415         minY: 0,
29416         draggable: false,
29417
29418         /**
29419          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29420          */
29421         constrainTo: undefined,
29422         /**
29423          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29424          */
29425         resizeRegion: undefined,
29426
29427
29428     /**
29429      * Perform a manual resize
29430      * @param {Number} width
29431      * @param {Number} height
29432      */
29433     resizeTo : function(width, height){
29434         this.el.setSize(width, height);
29435         this.updateChildSize();
29436         this.fireEvent("resize", this, width, height, null);
29437     },
29438
29439     // private
29440     startSizing : function(e, handle){
29441         this.fireEvent("beforeresize", this, e);
29442         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29443
29444             if(!this.overlay){
29445                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29446                 this.overlay.unselectable();
29447                 this.overlay.enableDisplayMode("block");
29448                 this.overlay.on("mousemove", this.onMouseMove, this);
29449                 this.overlay.on("mouseup", this.onMouseUp, this);
29450             }
29451             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29452
29453             this.resizing = true;
29454             this.startBox = this.el.getBox();
29455             this.startPoint = e.getXY();
29456             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29457                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29458
29459             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29460             this.overlay.show();
29461
29462             if(this.constrainTo) {
29463                 var ct = Roo.get(this.constrainTo);
29464                 this.resizeRegion = ct.getRegion().adjust(
29465                     ct.getFrameWidth('t'),
29466                     ct.getFrameWidth('l'),
29467                     -ct.getFrameWidth('b'),
29468                     -ct.getFrameWidth('r')
29469                 );
29470             }
29471
29472             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29473             this.proxy.show();
29474             this.proxy.setBox(this.startBox);
29475             if(!this.dynamic){
29476                 this.proxy.setStyle('visibility', 'visible');
29477             }
29478         }
29479     },
29480
29481     // private
29482     onMouseDown : function(handle, e){
29483         if(this.enabled){
29484             e.stopEvent();
29485             this.activeHandle = handle;
29486             this.startSizing(e, handle);
29487         }
29488     },
29489
29490     // private
29491     onMouseUp : function(e){
29492         var size = this.resizeElement();
29493         this.resizing = false;
29494         this.handleOut();
29495         this.overlay.hide();
29496         this.proxy.hide();
29497         this.fireEvent("resize", this, size.width, size.height, e);
29498     },
29499
29500     // private
29501     updateChildSize : function(){
29502         
29503         if(this.resizeChild){
29504             var el = this.el;
29505             var child = this.resizeChild;
29506             var adj = this.adjustments;
29507             if(el.dom.offsetWidth){
29508                 var b = el.getSize(true);
29509                 child.setSize(b.width+adj[0], b.height+adj[1]);
29510             }
29511             // Second call here for IE
29512             // The first call enables instant resizing and
29513             // the second call corrects scroll bars if they
29514             // exist
29515             if(Roo.isIE){
29516                 setTimeout(function(){
29517                     if(el.dom.offsetWidth){
29518                         var b = el.getSize(true);
29519                         child.setSize(b.width+adj[0], b.height+adj[1]);
29520                     }
29521                 }, 10);
29522             }
29523         }
29524     },
29525
29526     // private
29527     snap : function(value, inc, min){
29528         if(!inc || !value) {
29529             return value;
29530         }
29531         var newValue = value;
29532         var m = value % inc;
29533         if(m > 0){
29534             if(m > (inc/2)){
29535                 newValue = value + (inc-m);
29536             }else{
29537                 newValue = value - m;
29538             }
29539         }
29540         return Math.max(min, newValue);
29541     },
29542
29543     // private
29544     resizeElement : function(){
29545         var box = this.proxy.getBox();
29546         if(this.updateBox){
29547             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29548         }else{
29549             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29550         }
29551         this.updateChildSize();
29552         if(!this.dynamic){
29553             this.proxy.hide();
29554         }
29555         return box;
29556     },
29557
29558     // private
29559     constrain : function(v, diff, m, mx){
29560         if(v - diff < m){
29561             diff = v - m;
29562         }else if(v - diff > mx){
29563             diff = mx - v;
29564         }
29565         return diff;
29566     },
29567
29568     // private
29569     onMouseMove : function(e){
29570         
29571         if(this.enabled){
29572             try{// try catch so if something goes wrong the user doesn't get hung
29573
29574             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29575                 return;
29576             }
29577
29578             //var curXY = this.startPoint;
29579             var curSize = this.curSize || this.startBox;
29580             var x = this.startBox.x, y = this.startBox.y;
29581             var ox = x, oy = y;
29582             var w = curSize.width, h = curSize.height;
29583             var ow = w, oh = h;
29584             var mw = this.minWidth, mh = this.minHeight;
29585             var mxw = this.maxWidth, mxh = this.maxHeight;
29586             var wi = this.widthIncrement;
29587             var hi = this.heightIncrement;
29588
29589             var eventXY = e.getXY();
29590             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29591             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29592
29593             var pos = this.activeHandle.position;
29594
29595             switch(pos){
29596                 case "east":
29597                     w += diffX;
29598                     w = Math.min(Math.max(mw, w), mxw);
29599                     break;
29600              
29601                 case "south":
29602                     h += diffY;
29603                     h = Math.min(Math.max(mh, h), mxh);
29604                     break;
29605                 case "southeast":
29606                     w += diffX;
29607                     h += diffY;
29608                     w = Math.min(Math.max(mw, w), mxw);
29609                     h = Math.min(Math.max(mh, h), mxh);
29610                     break;
29611                 case "north":
29612                     diffY = this.constrain(h, diffY, mh, mxh);
29613                     y += diffY;
29614                     h -= diffY;
29615                     break;
29616                 case "hdrag":
29617                     
29618                     if (wi) {
29619                         var adiffX = Math.abs(diffX);
29620                         var sub = (adiffX % wi); // how much 
29621                         if (sub > (wi/2)) { // far enough to snap
29622                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29623                         } else {
29624                             // remove difference.. 
29625                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29626                         }
29627                     }
29628                     x += diffX;
29629                     x = Math.max(this.minX, x);
29630                     break;
29631                 case "west":
29632                     diffX = this.constrain(w, diffX, mw, mxw);
29633                     x += diffX;
29634                     w -= diffX;
29635                     break;
29636                 case "northeast":
29637                     w += diffX;
29638                     w = Math.min(Math.max(mw, w), mxw);
29639                     diffY = this.constrain(h, diffY, mh, mxh);
29640                     y += diffY;
29641                     h -= diffY;
29642                     break;
29643                 case "northwest":
29644                     diffX = this.constrain(w, diffX, mw, mxw);
29645                     diffY = this.constrain(h, diffY, mh, mxh);
29646                     y += diffY;
29647                     h -= diffY;
29648                     x += diffX;
29649                     w -= diffX;
29650                     break;
29651                case "southwest":
29652                     diffX = this.constrain(w, diffX, mw, mxw);
29653                     h += diffY;
29654                     h = Math.min(Math.max(mh, h), mxh);
29655                     x += diffX;
29656                     w -= diffX;
29657                     break;
29658             }
29659
29660             var sw = this.snap(w, wi, mw);
29661             var sh = this.snap(h, hi, mh);
29662             if(sw != w || sh != h){
29663                 switch(pos){
29664                     case "northeast":
29665                         y -= sh - h;
29666                     break;
29667                     case "north":
29668                         y -= sh - h;
29669                         break;
29670                     case "southwest":
29671                         x -= sw - w;
29672                     break;
29673                     case "west":
29674                         x -= sw - w;
29675                         break;
29676                     case "northwest":
29677                         x -= sw - w;
29678                         y -= sh - h;
29679                     break;
29680                 }
29681                 w = sw;
29682                 h = sh;
29683             }
29684
29685             if(this.preserveRatio){
29686                 switch(pos){
29687                     case "southeast":
29688                     case "east":
29689                         h = oh * (w/ow);
29690                         h = Math.min(Math.max(mh, h), mxh);
29691                         w = ow * (h/oh);
29692                        break;
29693                     case "south":
29694                         w = ow * (h/oh);
29695                         w = Math.min(Math.max(mw, w), mxw);
29696                         h = oh * (w/ow);
29697                         break;
29698                     case "northeast":
29699                         w = ow * (h/oh);
29700                         w = Math.min(Math.max(mw, w), mxw);
29701                         h = oh * (w/ow);
29702                     break;
29703                     case "north":
29704                         var tw = w;
29705                         w = ow * (h/oh);
29706                         w = Math.min(Math.max(mw, w), mxw);
29707                         h = oh * (w/ow);
29708                         x += (tw - w) / 2;
29709                         break;
29710                     case "southwest":
29711                         h = oh * (w/ow);
29712                         h = Math.min(Math.max(mh, h), mxh);
29713                         var tw = w;
29714                         w = ow * (h/oh);
29715                         x += tw - w;
29716                         break;
29717                     case "west":
29718                         var th = h;
29719                         h = oh * (w/ow);
29720                         h = Math.min(Math.max(mh, h), mxh);
29721                         y += (th - h) / 2;
29722                         var tw = w;
29723                         w = ow * (h/oh);
29724                         x += tw - w;
29725                        break;
29726                     case "northwest":
29727                         var tw = w;
29728                         var th = h;
29729                         h = oh * (w/ow);
29730                         h = Math.min(Math.max(mh, h), mxh);
29731                         w = ow * (h/oh);
29732                         y += th - h;
29733                         x += tw - w;
29734                        break;
29735
29736                 }
29737             }
29738             if (pos == 'hdrag') {
29739                 w = ow;
29740             }
29741             this.proxy.setBounds(x, y, w, h);
29742             if(this.dynamic){
29743                 this.resizeElement();
29744             }
29745             }catch(e){}
29746         }
29747         this.fireEvent("resizing", this, x, y, w, h, e);
29748     },
29749
29750     // private
29751     handleOver : function(){
29752         if(this.enabled){
29753             this.el.addClass("x-resizable-over");
29754         }
29755     },
29756
29757     // private
29758     handleOut : function(){
29759         if(!this.resizing){
29760             this.el.removeClass("x-resizable-over");
29761         }
29762     },
29763
29764     /**
29765      * Returns the element this component is bound to.
29766      * @return {Roo.Element}
29767      */
29768     getEl : function(){
29769         return this.el;
29770     },
29771
29772     /**
29773      * Returns the resizeChild element (or null).
29774      * @return {Roo.Element}
29775      */
29776     getResizeChild : function(){
29777         return this.resizeChild;
29778     },
29779     groupHandler : function()
29780     {
29781         
29782     },
29783     /**
29784      * Destroys this resizable. If the element was wrapped and
29785      * removeEl is not true then the element remains.
29786      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29787      */
29788     destroy : function(removeEl){
29789         this.proxy.remove();
29790         if(this.overlay){
29791             this.overlay.removeAllListeners();
29792             this.overlay.remove();
29793         }
29794         var ps = Roo.Resizable.positions;
29795         for(var k in ps){
29796             if(typeof ps[k] != "function" && this[ps[k]]){
29797                 var h = this[ps[k]];
29798                 h.el.removeAllListeners();
29799                 h.el.remove();
29800             }
29801         }
29802         if(removeEl){
29803             this.el.update("");
29804             this.el.remove();
29805         }
29806     }
29807 });
29808
29809 // private
29810 // hash to map config positions to true positions
29811 Roo.Resizable.positions = {
29812     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29813     hd: "hdrag"
29814 };
29815
29816 // private
29817 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29818     if(!this.tpl){
29819         // only initialize the template if resizable is used
29820         var tpl = Roo.DomHelper.createTemplate(
29821             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29822         );
29823         tpl.compile();
29824         Roo.Resizable.Handle.prototype.tpl = tpl;
29825     }
29826     this.position = pos;
29827     this.rz = rz;
29828     // show north drag fro topdra
29829     var handlepos = pos == 'hdrag' ? 'north' : pos;
29830     
29831     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29832     if (pos == 'hdrag') {
29833         this.el.setStyle('cursor', 'pointer');
29834     }
29835     this.el.unselectable();
29836     if(transparent){
29837         this.el.setOpacity(0);
29838     }
29839     this.el.on("mousedown", this.onMouseDown, this);
29840     if(!disableTrackOver){
29841         this.el.on("mouseover", this.onMouseOver, this);
29842         this.el.on("mouseout", this.onMouseOut, this);
29843     }
29844 };
29845
29846 // private
29847 Roo.Resizable.Handle.prototype = {
29848     afterResize : function(rz){
29849         Roo.log('after?');
29850         // do nothing
29851     },
29852     // private
29853     onMouseDown : function(e){
29854         this.rz.onMouseDown(this, e);
29855     },
29856     // private
29857     onMouseOver : function(e){
29858         this.rz.handleOver(this, e);
29859     },
29860     // private
29861     onMouseOut : function(e){
29862         this.rz.handleOut(this, e);
29863     }
29864 };/*
29865  * Based on:
29866  * Ext JS Library 1.1.1
29867  * Copyright(c) 2006-2007, Ext JS, LLC.
29868  *
29869  * Originally Released Under LGPL - original licence link has changed is not relivant.
29870  *
29871  * Fork - LGPL
29872  * <script type="text/javascript">
29873  */
29874
29875 /**
29876  * @class Roo.Editor
29877  * @extends Roo.Component
29878  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29879  * @constructor
29880  * Create a new Editor
29881  * @param {Roo.form.Field} field The Field object (or descendant)
29882  * @param {Object} config The config object
29883  */
29884 Roo.Editor = function(field, config){
29885     Roo.Editor.superclass.constructor.call(this, config);
29886     this.field = field;
29887     this.addEvents({
29888         /**
29889              * @event beforestartedit
29890              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29891              * false from the handler of this event.
29892              * @param {Editor} this
29893              * @param {Roo.Element} boundEl The underlying element bound to this editor
29894              * @param {Mixed} value The field value being set
29895              */
29896         "beforestartedit" : true,
29897         /**
29898              * @event startedit
29899              * Fires when this editor is displayed
29900              * @param {Roo.Element} boundEl The underlying element bound to this editor
29901              * @param {Mixed} value The starting field value
29902              */
29903         "startedit" : true,
29904         /**
29905              * @event beforecomplete
29906              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29907              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29908              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29909              * event will not fire since no edit actually occurred.
29910              * @param {Editor} this
29911              * @param {Mixed} value The current field value
29912              * @param {Mixed} startValue The original field value
29913              */
29914         "beforecomplete" : true,
29915         /**
29916              * @event complete
29917              * Fires after editing is complete and any changed value has been written to the underlying field.
29918              * @param {Editor} this
29919              * @param {Mixed} value The current field value
29920              * @param {Mixed} startValue The original field value
29921              */
29922         "complete" : true,
29923         /**
29924          * @event specialkey
29925          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29926          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29927          * @param {Roo.form.Field} this
29928          * @param {Roo.EventObject} e The event object
29929          */
29930         "specialkey" : true
29931     });
29932 };
29933
29934 Roo.extend(Roo.Editor, Roo.Component, {
29935     /**
29936      * @cfg {Boolean/String} autosize
29937      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29938      * or "height" to adopt the height only (defaults to false)
29939      */
29940     /**
29941      * @cfg {Boolean} revertInvalid
29942      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29943      * validation fails (defaults to true)
29944      */
29945     /**
29946      * @cfg {Boolean} ignoreNoChange
29947      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29948      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29949      * will never be ignored.
29950      */
29951     /**
29952      * @cfg {Boolean} hideEl
29953      * False to keep the bound element visible while the editor is displayed (defaults to true)
29954      */
29955     /**
29956      * @cfg {Mixed} value
29957      * The data value of the underlying field (defaults to "")
29958      */
29959     value : "",
29960     /**
29961      * @cfg {String} alignment
29962      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29963      */
29964     alignment: "c-c?",
29965     /**
29966      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29967      * for bottom-right shadow (defaults to "frame")
29968      */
29969     shadow : "frame",
29970     /**
29971      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29972      */
29973     constrain : false,
29974     /**
29975      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29976      */
29977     completeOnEnter : false,
29978     /**
29979      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29980      */
29981     cancelOnEsc : false,
29982     /**
29983      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29984      */
29985     updateEl : false,
29986
29987     // private
29988     onRender : function(ct, position){
29989         this.el = new Roo.Layer({
29990             shadow: this.shadow,
29991             cls: "x-editor",
29992             parentEl : ct,
29993             shim : this.shim,
29994             shadowOffset:4,
29995             id: this.id,
29996             constrain: this.constrain
29997         });
29998         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29999         if(this.field.msgTarget != 'title'){
30000             this.field.msgTarget = 'qtip';
30001         }
30002         this.field.render(this.el);
30003         if(Roo.isGecko){
30004             this.field.el.dom.setAttribute('autocomplete', 'off');
30005         }
30006         this.field.on("specialkey", this.onSpecialKey, this);
30007         if(this.swallowKeys){
30008             this.field.el.swallowEvent(['keydown','keypress']);
30009         }
30010         this.field.show();
30011         this.field.on("blur", this.onBlur, this);
30012         if(this.field.grow){
30013             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
30014         }
30015     },
30016
30017     onSpecialKey : function(field, e)
30018     {
30019         //Roo.log('editor onSpecialKey');
30020         if(this.completeOnEnter && e.getKey() == e.ENTER){
30021             e.stopEvent();
30022             this.completeEdit();
30023             return;
30024         }
30025         // do not fire special key otherwise it might hide close the editor...
30026         if(e.getKey() == e.ENTER){    
30027             return;
30028         }
30029         if(this.cancelOnEsc && e.getKey() == e.ESC){
30030             this.cancelEdit();
30031             return;
30032         } 
30033         this.fireEvent('specialkey', field, e);
30034     
30035     },
30036
30037     /**
30038      * Starts the editing process and shows the editor.
30039      * @param {String/HTMLElement/Element} el The element to edit
30040      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
30041       * to the innerHTML of el.
30042      */
30043     startEdit : function(el, value){
30044         if(this.editing){
30045             this.completeEdit();
30046         }
30047         this.boundEl = Roo.get(el);
30048         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30049         if(!this.rendered){
30050             this.render(this.parentEl || document.body);
30051         }
30052         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30053             return;
30054         }
30055         this.startValue = v;
30056         this.field.setValue(v);
30057         if(this.autoSize){
30058             var sz = this.boundEl.getSize();
30059             switch(this.autoSize){
30060                 case "width":
30061                 this.setSize(sz.width,  "");
30062                 break;
30063                 case "height":
30064                 this.setSize("",  sz.height);
30065                 break;
30066                 default:
30067                 this.setSize(sz.width,  sz.height);
30068             }
30069         }
30070         this.el.alignTo(this.boundEl, this.alignment);
30071         this.editing = true;
30072         if(Roo.QuickTips){
30073             Roo.QuickTips.disable();
30074         }
30075         this.show();
30076     },
30077
30078     /**
30079      * Sets the height and width of this editor.
30080      * @param {Number} width The new width
30081      * @param {Number} height The new height
30082      */
30083     setSize : function(w, h){
30084         this.field.setSize(w, h);
30085         if(this.el){
30086             this.el.sync();
30087         }
30088     },
30089
30090     /**
30091      * Realigns the editor to the bound field based on the current alignment config value.
30092      */
30093     realign : function(){
30094         this.el.alignTo(this.boundEl, this.alignment);
30095     },
30096
30097     /**
30098      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30099      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30100      */
30101     completeEdit : function(remainVisible){
30102         if(!this.editing){
30103             return;
30104         }
30105         var v = this.getValue();
30106         if(this.revertInvalid !== false && !this.field.isValid()){
30107             v = this.startValue;
30108             this.cancelEdit(true);
30109         }
30110         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30111             this.editing = false;
30112             this.hide();
30113             return;
30114         }
30115         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30116             this.editing = false;
30117             if(this.updateEl && this.boundEl){
30118                 this.boundEl.update(v);
30119             }
30120             if(remainVisible !== true){
30121                 this.hide();
30122             }
30123             this.fireEvent("complete", this, v, this.startValue);
30124         }
30125     },
30126
30127     // private
30128     onShow : function(){
30129         this.el.show();
30130         if(this.hideEl !== false){
30131             this.boundEl.hide();
30132         }
30133         this.field.show();
30134         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30135             this.fixIEFocus = true;
30136             this.deferredFocus.defer(50, this);
30137         }else{
30138             this.field.focus();
30139         }
30140         this.fireEvent("startedit", this.boundEl, this.startValue);
30141     },
30142
30143     deferredFocus : function(){
30144         if(this.editing){
30145             this.field.focus();
30146         }
30147     },
30148
30149     /**
30150      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30151      * reverted to the original starting value.
30152      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30153      * cancel (defaults to false)
30154      */
30155     cancelEdit : function(remainVisible){
30156         if(this.editing){
30157             this.setValue(this.startValue);
30158             if(remainVisible !== true){
30159                 this.hide();
30160             }
30161         }
30162     },
30163
30164     // private
30165     onBlur : function(){
30166         if(this.allowBlur !== true && this.editing){
30167             this.completeEdit();
30168         }
30169     },
30170
30171     // private
30172     onHide : function(){
30173         if(this.editing){
30174             this.completeEdit();
30175             return;
30176         }
30177         this.field.blur();
30178         if(this.field.collapse){
30179             this.field.collapse();
30180         }
30181         this.el.hide();
30182         if(this.hideEl !== false){
30183             this.boundEl.show();
30184         }
30185         if(Roo.QuickTips){
30186             Roo.QuickTips.enable();
30187         }
30188     },
30189
30190     /**
30191      * Sets the data value of the editor
30192      * @param {Mixed} value Any valid value supported by the underlying field
30193      */
30194     setValue : function(v){
30195         this.field.setValue(v);
30196     },
30197
30198     /**
30199      * Gets the data value of the editor
30200      * @return {Mixed} The data value
30201      */
30202     getValue : function(){
30203         return this.field.getValue();
30204     }
30205 });/*
30206  * Based on:
30207  * Ext JS Library 1.1.1
30208  * Copyright(c) 2006-2007, Ext JS, LLC.
30209  *
30210  * Originally Released Under LGPL - original licence link has changed is not relivant.
30211  *
30212  * Fork - LGPL
30213  * <script type="text/javascript">
30214  */
30215  
30216 /**
30217  * @class Roo.BasicDialog
30218  * @extends Roo.util.Observable
30219  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30220  * <pre><code>
30221 var dlg = new Roo.BasicDialog("my-dlg", {
30222     height: 200,
30223     width: 300,
30224     minHeight: 100,
30225     minWidth: 150,
30226     modal: true,
30227     proxyDrag: true,
30228     shadow: true
30229 });
30230 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30231 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30232 dlg.addButton('Cancel', dlg.hide, dlg);
30233 dlg.show();
30234 </code></pre>
30235   <b>A Dialog should always be a direct child of the body element.</b>
30236  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30237  * @cfg {String} title Default text to display in the title bar (defaults to null)
30238  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30239  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30240  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30241  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30242  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30243  * (defaults to null with no animation)
30244  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30245  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30246  * property for valid values (defaults to 'all')
30247  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30248  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30249  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30250  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30251  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30252  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30253  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30254  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30255  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30256  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30257  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30258  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30259  * draggable = true (defaults to false)
30260  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30261  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30262  * shadow (defaults to false)
30263  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30264  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30265  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30266  * @cfg {Array} buttons Array of buttons
30267  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30268  * @constructor
30269  * Create a new BasicDialog.
30270  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30271  * @param {Object} config Configuration options
30272  */
30273 Roo.BasicDialog = function(el, config){
30274     this.el = Roo.get(el);
30275     var dh = Roo.DomHelper;
30276     if(!this.el && config && config.autoCreate){
30277         if(typeof config.autoCreate == "object"){
30278             if(!config.autoCreate.id){
30279                 config.autoCreate.id = el;
30280             }
30281             this.el = dh.append(document.body,
30282                         config.autoCreate, true);
30283         }else{
30284             this.el = dh.append(document.body,
30285                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30286         }
30287     }
30288     el = this.el;
30289     el.setDisplayed(true);
30290     el.hide = this.hideAction;
30291     this.id = el.id;
30292     el.addClass("x-dlg");
30293
30294     Roo.apply(this, config);
30295
30296     this.proxy = el.createProxy("x-dlg-proxy");
30297     this.proxy.hide = this.hideAction;
30298     this.proxy.setOpacity(.5);
30299     this.proxy.hide();
30300
30301     if(config.width){
30302         el.setWidth(config.width);
30303     }
30304     if(config.height){
30305         el.setHeight(config.height);
30306     }
30307     this.size = el.getSize();
30308     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30309         this.xy = [config.x,config.y];
30310     }else{
30311         this.xy = el.getCenterXY(true);
30312     }
30313     /** The header element @type Roo.Element */
30314     this.header = el.child("> .x-dlg-hd");
30315     /** The body element @type Roo.Element */
30316     this.body = el.child("> .x-dlg-bd");
30317     /** The footer element @type Roo.Element */
30318     this.footer = el.child("> .x-dlg-ft");
30319
30320     if(!this.header){
30321         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30322     }
30323     if(!this.body){
30324         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30325     }
30326
30327     this.header.unselectable();
30328     if(this.title){
30329         this.header.update(this.title);
30330     }
30331     // this element allows the dialog to be focused for keyboard event
30332     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30333     this.focusEl.swallowEvent("click", true);
30334
30335     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30336
30337     // wrap the body and footer for special rendering
30338     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30339     if(this.footer){
30340         this.bwrap.dom.appendChild(this.footer.dom);
30341     }
30342
30343     this.bg = this.el.createChild({
30344         tag: "div", cls:"x-dlg-bg",
30345         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30346     });
30347     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30348
30349
30350     if(this.autoScroll !== false && !this.autoTabs){
30351         this.body.setStyle("overflow", "auto");
30352     }
30353
30354     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30355
30356     if(this.closable !== false){
30357         this.el.addClass("x-dlg-closable");
30358         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30359         this.close.on("click", this.closeClick, this);
30360         this.close.addClassOnOver("x-dlg-close-over");
30361     }
30362     if(this.collapsible !== false){
30363         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30364         this.collapseBtn.on("click", this.collapseClick, this);
30365         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30366         this.header.on("dblclick", this.collapseClick, this);
30367     }
30368     if(this.resizable !== false){
30369         this.el.addClass("x-dlg-resizable");
30370         this.resizer = new Roo.Resizable(el, {
30371             minWidth: this.minWidth || 80,
30372             minHeight:this.minHeight || 80,
30373             handles: this.resizeHandles || "all",
30374             pinned: true
30375         });
30376         this.resizer.on("beforeresize", this.beforeResize, this);
30377         this.resizer.on("resize", this.onResize, this);
30378     }
30379     if(this.draggable !== false){
30380         el.addClass("x-dlg-draggable");
30381         if (!this.proxyDrag) {
30382             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30383         }
30384         else {
30385             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30386         }
30387         dd.setHandleElId(this.header.id);
30388         dd.endDrag = this.endMove.createDelegate(this);
30389         dd.startDrag = this.startMove.createDelegate(this);
30390         dd.onDrag = this.onDrag.createDelegate(this);
30391         dd.scroll = false;
30392         this.dd = dd;
30393     }
30394     if(this.modal){
30395         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30396         this.mask.enableDisplayMode("block");
30397         this.mask.hide();
30398         this.el.addClass("x-dlg-modal");
30399     }
30400     if(this.shadow){
30401         this.shadow = new Roo.Shadow({
30402             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30403             offset : this.shadowOffset
30404         });
30405     }else{
30406         this.shadowOffset = 0;
30407     }
30408     if(Roo.useShims && this.shim !== false){
30409         this.shim = this.el.createShim();
30410         this.shim.hide = this.hideAction;
30411         this.shim.hide();
30412     }else{
30413         this.shim = false;
30414     }
30415     if(this.autoTabs){
30416         this.initTabs();
30417     }
30418     if (this.buttons) { 
30419         var bts= this.buttons;
30420         this.buttons = [];
30421         Roo.each(bts, function(b) {
30422             this.addButton(b);
30423         }, this);
30424     }
30425     
30426     
30427     this.addEvents({
30428         /**
30429          * @event keydown
30430          * Fires when a key is pressed
30431          * @param {Roo.BasicDialog} this
30432          * @param {Roo.EventObject} e
30433          */
30434         "keydown" : true,
30435         /**
30436          * @event move
30437          * Fires when this dialog is moved by the user.
30438          * @param {Roo.BasicDialog} this
30439          * @param {Number} x The new page X
30440          * @param {Number} y The new page Y
30441          */
30442         "move" : true,
30443         /**
30444          * @event resize
30445          * Fires when this dialog is resized by the user.
30446          * @param {Roo.BasicDialog} this
30447          * @param {Number} width The new width
30448          * @param {Number} height The new height
30449          */
30450         "resize" : true,
30451         /**
30452          * @event beforehide
30453          * Fires before this dialog is hidden.
30454          * @param {Roo.BasicDialog} this
30455          */
30456         "beforehide" : true,
30457         /**
30458          * @event hide
30459          * Fires when this dialog is hidden.
30460          * @param {Roo.BasicDialog} this
30461          */
30462         "hide" : true,
30463         /**
30464          * @event beforeshow
30465          * Fires before this dialog is shown.
30466          * @param {Roo.BasicDialog} this
30467          */
30468         "beforeshow" : true,
30469         /**
30470          * @event show
30471          * Fires when this dialog is shown.
30472          * @param {Roo.BasicDialog} this
30473          */
30474         "show" : true
30475     });
30476     el.on("keydown", this.onKeyDown, this);
30477     el.on("mousedown", this.toFront, this);
30478     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30479     this.el.hide();
30480     Roo.DialogManager.register(this);
30481     Roo.BasicDialog.superclass.constructor.call(this);
30482 };
30483
30484 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30485     shadowOffset: Roo.isIE ? 6 : 5,
30486     minHeight: 80,
30487     minWidth: 200,
30488     minButtonWidth: 75,
30489     defaultButton: null,
30490     buttonAlign: "right",
30491     tabTag: 'div',
30492     firstShow: true,
30493
30494     /**
30495      * Sets the dialog title text
30496      * @param {String} text The title text to display
30497      * @return {Roo.BasicDialog} this
30498      */
30499     setTitle : function(text){
30500         this.header.update(text);
30501         return this;
30502     },
30503
30504     // private
30505     closeClick : function(){
30506         this.hide();
30507     },
30508
30509     // private
30510     collapseClick : function(){
30511         this[this.collapsed ? "expand" : "collapse"]();
30512     },
30513
30514     /**
30515      * Collapses the dialog to its minimized state (only the title bar is visible).
30516      * Equivalent to the user clicking the collapse dialog button.
30517      */
30518     collapse : function(){
30519         if(!this.collapsed){
30520             this.collapsed = true;
30521             this.el.addClass("x-dlg-collapsed");
30522             this.restoreHeight = this.el.getHeight();
30523             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30524         }
30525     },
30526
30527     /**
30528      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30529      * clicking the expand dialog button.
30530      */
30531     expand : function(){
30532         if(this.collapsed){
30533             this.collapsed = false;
30534             this.el.removeClass("x-dlg-collapsed");
30535             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30536         }
30537     },
30538
30539     /**
30540      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30541      * @return {Roo.TabPanel} The tabs component
30542      */
30543     initTabs : function(){
30544         var tabs = this.getTabs();
30545         while(tabs.getTab(0)){
30546             tabs.removeTab(0);
30547         }
30548         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30549             var dom = el.dom;
30550             tabs.addTab(Roo.id(dom), dom.title);
30551             dom.title = "";
30552         });
30553         tabs.activate(0);
30554         return tabs;
30555     },
30556
30557     // private
30558     beforeResize : function(){
30559         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30560     },
30561
30562     // private
30563     onResize : function(){
30564         this.refreshSize();
30565         this.syncBodyHeight();
30566         this.adjustAssets();
30567         this.focus();
30568         this.fireEvent("resize", this, this.size.width, this.size.height);
30569     },
30570
30571     // private
30572     onKeyDown : function(e){
30573         if(this.isVisible()){
30574             this.fireEvent("keydown", this, e);
30575         }
30576     },
30577
30578     /**
30579      * Resizes the dialog.
30580      * @param {Number} width
30581      * @param {Number} height
30582      * @return {Roo.BasicDialog} this
30583      */
30584     resizeTo : function(width, height){
30585         this.el.setSize(width, height);
30586         this.size = {width: width, height: height};
30587         this.syncBodyHeight();
30588         if(this.fixedcenter){
30589             this.center();
30590         }
30591         if(this.isVisible()){
30592             this.constrainXY();
30593             this.adjustAssets();
30594         }
30595         this.fireEvent("resize", this, width, height);
30596         return this;
30597     },
30598
30599
30600     /**
30601      * Resizes the dialog to fit the specified content size.
30602      * @param {Number} width
30603      * @param {Number} height
30604      * @return {Roo.BasicDialog} this
30605      */
30606     setContentSize : function(w, h){
30607         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30608         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30609         //if(!this.el.isBorderBox()){
30610             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30611             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30612         //}
30613         if(this.tabs){
30614             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30615             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30616         }
30617         this.resizeTo(w, h);
30618         return this;
30619     },
30620
30621     /**
30622      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30623      * executed in response to a particular key being pressed while the dialog is active.
30624      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30625      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30626      * @param {Function} fn The function to call
30627      * @param {Object} scope (optional) The scope of the function
30628      * @return {Roo.BasicDialog} this
30629      */
30630     addKeyListener : function(key, fn, scope){
30631         var keyCode, shift, ctrl, alt;
30632         if(typeof key == "object" && !(key instanceof Array)){
30633             keyCode = key["key"];
30634             shift = key["shift"];
30635             ctrl = key["ctrl"];
30636             alt = key["alt"];
30637         }else{
30638             keyCode = key;
30639         }
30640         var handler = function(dlg, e){
30641             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30642                 var k = e.getKey();
30643                 if(keyCode instanceof Array){
30644                     for(var i = 0, len = keyCode.length; i < len; i++){
30645                         if(keyCode[i] == k){
30646                           fn.call(scope || window, dlg, k, e);
30647                           return;
30648                         }
30649                     }
30650                 }else{
30651                     if(k == keyCode){
30652                         fn.call(scope || window, dlg, k, e);
30653                     }
30654                 }
30655             }
30656         };
30657         this.on("keydown", handler);
30658         return this;
30659     },
30660
30661     /**
30662      * Returns the TabPanel component (creates it if it doesn't exist).
30663      * Note: If you wish to simply check for the existence of tabs without creating them,
30664      * check for a null 'tabs' property.
30665      * @return {Roo.TabPanel} The tabs component
30666      */
30667     getTabs : function(){
30668         if(!this.tabs){
30669             this.el.addClass("x-dlg-auto-tabs");
30670             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30671             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30672         }
30673         return this.tabs;
30674     },
30675
30676     /**
30677      * Adds a button to the footer section of the dialog.
30678      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30679      * object or a valid Roo.DomHelper element config
30680      * @param {Function} handler The function called when the button is clicked
30681      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30682      * @return {Roo.Button} The new button
30683      */
30684     addButton : function(config, handler, scope){
30685         var dh = Roo.DomHelper;
30686         if(!this.footer){
30687             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30688         }
30689         if(!this.btnContainer){
30690             var tb = this.footer.createChild({
30691
30692                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30693                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30694             }, null, true);
30695             this.btnContainer = tb.firstChild.firstChild.firstChild;
30696         }
30697         var bconfig = {
30698             handler: handler,
30699             scope: scope,
30700             minWidth: this.minButtonWidth,
30701             hideParent:true
30702         };
30703         if(typeof config == "string"){
30704             bconfig.text = config;
30705         }else{
30706             if(config.tag){
30707                 bconfig.dhconfig = config;
30708             }else{
30709                 Roo.apply(bconfig, config);
30710             }
30711         }
30712         var fc = false;
30713         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30714             bconfig.position = Math.max(0, bconfig.position);
30715             fc = this.btnContainer.childNodes[bconfig.position];
30716         }
30717          
30718         var btn = new Roo.Button(
30719             fc ? 
30720                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30721                 : this.btnContainer.appendChild(document.createElement("td")),
30722             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30723             bconfig
30724         );
30725         this.syncBodyHeight();
30726         if(!this.buttons){
30727             /**
30728              * Array of all the buttons that have been added to this dialog via addButton
30729              * @type Array
30730              */
30731             this.buttons = [];
30732         }
30733         this.buttons.push(btn);
30734         return btn;
30735     },
30736
30737     /**
30738      * Sets the default button to be focused when the dialog is displayed.
30739      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30740      * @return {Roo.BasicDialog} this
30741      */
30742     setDefaultButton : function(btn){
30743         this.defaultButton = btn;
30744         return this;
30745     },
30746
30747     // private
30748     getHeaderFooterHeight : function(safe){
30749         var height = 0;
30750         if(this.header){
30751            height += this.header.getHeight();
30752         }
30753         if(this.footer){
30754            var fm = this.footer.getMargins();
30755             height += (this.footer.getHeight()+fm.top+fm.bottom);
30756         }
30757         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30758         height += this.centerBg.getPadding("tb");
30759         return height;
30760     },
30761
30762     // private
30763     syncBodyHeight : function()
30764     {
30765         var bd = this.body, // the text
30766             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30767             bw = this.bwrap;
30768         var height = this.size.height - this.getHeaderFooterHeight(false);
30769         bd.setHeight(height-bd.getMargins("tb"));
30770         var hh = this.header.getHeight();
30771         var h = this.size.height-hh;
30772         cb.setHeight(h);
30773         
30774         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30775         bw.setHeight(h-cb.getPadding("tb"));
30776         
30777         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30778         bd.setWidth(bw.getWidth(true));
30779         if(this.tabs){
30780             this.tabs.syncHeight();
30781             if(Roo.isIE){
30782                 this.tabs.el.repaint();
30783             }
30784         }
30785     },
30786
30787     /**
30788      * Restores the previous state of the dialog if Roo.state is configured.
30789      * @return {Roo.BasicDialog} this
30790      */
30791     restoreState : function(){
30792         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30793         if(box && box.width){
30794             this.xy = [box.x, box.y];
30795             this.resizeTo(box.width, box.height);
30796         }
30797         return this;
30798     },
30799
30800     // private
30801     beforeShow : function(){
30802         this.expand();
30803         if(this.fixedcenter){
30804             this.xy = this.el.getCenterXY(true);
30805         }
30806         if(this.modal){
30807             Roo.get(document.body).addClass("x-body-masked");
30808             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30809             this.mask.show();
30810         }
30811         this.constrainXY();
30812     },
30813
30814     // private
30815     animShow : function(){
30816         var b = Roo.get(this.animateTarget).getBox();
30817         this.proxy.setSize(b.width, b.height);
30818         this.proxy.setLocation(b.x, b.y);
30819         this.proxy.show();
30820         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30821                     true, .35, this.showEl.createDelegate(this));
30822     },
30823
30824     /**
30825      * Shows the dialog.
30826      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30827      * @return {Roo.BasicDialog} this
30828      */
30829     show : function(animateTarget){
30830         if (this.fireEvent("beforeshow", this) === false){
30831             return;
30832         }
30833         if(this.syncHeightBeforeShow){
30834             this.syncBodyHeight();
30835         }else if(this.firstShow){
30836             this.firstShow = false;
30837             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30838         }
30839         this.animateTarget = animateTarget || this.animateTarget;
30840         if(!this.el.isVisible()){
30841             this.beforeShow();
30842             if(this.animateTarget && Roo.get(this.animateTarget)){
30843                 this.animShow();
30844             }else{
30845                 this.showEl();
30846             }
30847         }
30848         return this;
30849     },
30850
30851     // private
30852     showEl : function(){
30853         this.proxy.hide();
30854         this.el.setXY(this.xy);
30855         this.el.show();
30856         this.adjustAssets(true);
30857         this.toFront();
30858         this.focus();
30859         // IE peekaboo bug - fix found by Dave Fenwick
30860         if(Roo.isIE){
30861             this.el.repaint();
30862         }
30863         this.fireEvent("show", this);
30864     },
30865
30866     /**
30867      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30868      * dialog itself will receive focus.
30869      */
30870     focus : function(){
30871         if(this.defaultButton){
30872             this.defaultButton.focus();
30873         }else{
30874             this.focusEl.focus();
30875         }
30876     },
30877
30878     // private
30879     constrainXY : function(){
30880         if(this.constraintoviewport !== false){
30881             if(!this.viewSize){
30882                 if(this.container){
30883                     var s = this.container.getSize();
30884                     this.viewSize = [s.width, s.height];
30885                 }else{
30886                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30887                 }
30888             }
30889             var s = Roo.get(this.container||document).getScroll();
30890
30891             var x = this.xy[0], y = this.xy[1];
30892             var w = this.size.width, h = this.size.height;
30893             var vw = this.viewSize[0], vh = this.viewSize[1];
30894             // only move it if it needs it
30895             var moved = false;
30896             // first validate right/bottom
30897             if(x + w > vw+s.left){
30898                 x = vw - w;
30899                 moved = true;
30900             }
30901             if(y + h > vh+s.top){
30902                 y = vh - h;
30903                 moved = true;
30904             }
30905             // then make sure top/left isn't negative
30906             if(x < s.left){
30907                 x = s.left;
30908                 moved = true;
30909             }
30910             if(y < s.top){
30911                 y = s.top;
30912                 moved = true;
30913             }
30914             if(moved){
30915                 // cache xy
30916                 this.xy = [x, y];
30917                 if(this.isVisible()){
30918                     this.el.setLocation(x, y);
30919                     this.adjustAssets();
30920                 }
30921             }
30922         }
30923     },
30924
30925     // private
30926     onDrag : function(){
30927         if(!this.proxyDrag){
30928             this.xy = this.el.getXY();
30929             this.adjustAssets();
30930         }
30931     },
30932
30933     // private
30934     adjustAssets : function(doShow){
30935         var x = this.xy[0], y = this.xy[1];
30936         var w = this.size.width, h = this.size.height;
30937         if(doShow === true){
30938             if(this.shadow){
30939                 this.shadow.show(this.el);
30940             }
30941             if(this.shim){
30942                 this.shim.show();
30943             }
30944         }
30945         if(this.shadow && this.shadow.isVisible()){
30946             this.shadow.show(this.el);
30947         }
30948         if(this.shim && this.shim.isVisible()){
30949             this.shim.setBounds(x, y, w, h);
30950         }
30951     },
30952
30953     // private
30954     adjustViewport : function(w, h){
30955         if(!w || !h){
30956             w = Roo.lib.Dom.getViewWidth();
30957             h = Roo.lib.Dom.getViewHeight();
30958         }
30959         // cache the size
30960         this.viewSize = [w, h];
30961         if(this.modal && this.mask.isVisible()){
30962             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30963             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30964         }
30965         if(this.isVisible()){
30966             this.constrainXY();
30967         }
30968     },
30969
30970     /**
30971      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30972      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30973      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30974      */
30975     destroy : function(removeEl){
30976         if(this.isVisible()){
30977             this.animateTarget = null;
30978             this.hide();
30979         }
30980         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30981         if(this.tabs){
30982             this.tabs.destroy(removeEl);
30983         }
30984         Roo.destroy(
30985              this.shim,
30986              this.proxy,
30987              this.resizer,
30988              this.close,
30989              this.mask
30990         );
30991         if(this.dd){
30992             this.dd.unreg();
30993         }
30994         if(this.buttons){
30995            for(var i = 0, len = this.buttons.length; i < len; i++){
30996                this.buttons[i].destroy();
30997            }
30998         }
30999         this.el.removeAllListeners();
31000         if(removeEl === true){
31001             this.el.update("");
31002             this.el.remove();
31003         }
31004         Roo.DialogManager.unregister(this);
31005     },
31006
31007     // private
31008     startMove : function(){
31009         if(this.proxyDrag){
31010             this.proxy.show();
31011         }
31012         if(this.constraintoviewport !== false){
31013             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
31014         }
31015     },
31016
31017     // private
31018     endMove : function(){
31019         if(!this.proxyDrag){
31020             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
31021         }else{
31022             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
31023             this.proxy.hide();
31024         }
31025         this.refreshSize();
31026         this.adjustAssets();
31027         this.focus();
31028         this.fireEvent("move", this, this.xy[0], this.xy[1]);
31029     },
31030
31031     /**
31032      * Brings this dialog to the front of any other visible dialogs
31033      * @return {Roo.BasicDialog} this
31034      */
31035     toFront : function(){
31036         Roo.DialogManager.bringToFront(this);
31037         return this;
31038     },
31039
31040     /**
31041      * Sends this dialog to the back (under) of any other visible dialogs
31042      * @return {Roo.BasicDialog} this
31043      */
31044     toBack : function(){
31045         Roo.DialogManager.sendToBack(this);
31046         return this;
31047     },
31048
31049     /**
31050      * Centers this dialog in the viewport
31051      * @return {Roo.BasicDialog} this
31052      */
31053     center : function(){
31054         var xy = this.el.getCenterXY(true);
31055         this.moveTo(xy[0], xy[1]);
31056         return this;
31057     },
31058
31059     /**
31060      * Moves the dialog's top-left corner to the specified point
31061      * @param {Number} x
31062      * @param {Number} y
31063      * @return {Roo.BasicDialog} this
31064      */
31065     moveTo : function(x, y){
31066         this.xy = [x,y];
31067         if(this.isVisible()){
31068             this.el.setXY(this.xy);
31069             this.adjustAssets();
31070         }
31071         return this;
31072     },
31073
31074     /**
31075      * Aligns the dialog to the specified element
31076      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31077      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31078      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31079      * @return {Roo.BasicDialog} this
31080      */
31081     alignTo : function(element, position, offsets){
31082         this.xy = this.el.getAlignToXY(element, position, offsets);
31083         if(this.isVisible()){
31084             this.el.setXY(this.xy);
31085             this.adjustAssets();
31086         }
31087         return this;
31088     },
31089
31090     /**
31091      * Anchors an element to another element and realigns it when the window is resized.
31092      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31093      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31094      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31095      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31096      * is a number, it is used as the buffer delay (defaults to 50ms).
31097      * @return {Roo.BasicDialog} this
31098      */
31099     anchorTo : function(el, alignment, offsets, monitorScroll){
31100         var action = function(){
31101             this.alignTo(el, alignment, offsets);
31102         };
31103         Roo.EventManager.onWindowResize(action, this);
31104         var tm = typeof monitorScroll;
31105         if(tm != 'undefined'){
31106             Roo.EventManager.on(window, 'scroll', action, this,
31107                 {buffer: tm == 'number' ? monitorScroll : 50});
31108         }
31109         action.call(this);
31110         return this;
31111     },
31112
31113     /**
31114      * Returns true if the dialog is visible
31115      * @return {Boolean}
31116      */
31117     isVisible : function(){
31118         return this.el.isVisible();
31119     },
31120
31121     // private
31122     animHide : function(callback){
31123         var b = Roo.get(this.animateTarget).getBox();
31124         this.proxy.show();
31125         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31126         this.el.hide();
31127         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31128                     this.hideEl.createDelegate(this, [callback]));
31129     },
31130
31131     /**
31132      * Hides the dialog.
31133      * @param {Function} callback (optional) Function to call when the dialog is hidden
31134      * @return {Roo.BasicDialog} this
31135      */
31136     hide : function(callback){
31137         if (this.fireEvent("beforehide", this) === false){
31138             return;
31139         }
31140         if(this.shadow){
31141             this.shadow.hide();
31142         }
31143         if(this.shim) {
31144           this.shim.hide();
31145         }
31146         // sometimes animateTarget seems to get set.. causing problems...
31147         // this just double checks..
31148         if(this.animateTarget && Roo.get(this.animateTarget)) {
31149            this.animHide(callback);
31150         }else{
31151             this.el.hide();
31152             this.hideEl(callback);
31153         }
31154         return this;
31155     },
31156
31157     // private
31158     hideEl : function(callback){
31159         this.proxy.hide();
31160         if(this.modal){
31161             this.mask.hide();
31162             Roo.get(document.body).removeClass("x-body-masked");
31163         }
31164         this.fireEvent("hide", this);
31165         if(typeof callback == "function"){
31166             callback();
31167         }
31168     },
31169
31170     // private
31171     hideAction : function(){
31172         this.setLeft("-10000px");
31173         this.setTop("-10000px");
31174         this.setStyle("visibility", "hidden");
31175     },
31176
31177     // private
31178     refreshSize : function(){
31179         this.size = this.el.getSize();
31180         this.xy = this.el.getXY();
31181         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31182     },
31183
31184     // private
31185     // z-index is managed by the DialogManager and may be overwritten at any time
31186     setZIndex : function(index){
31187         if(this.modal){
31188             this.mask.setStyle("z-index", index);
31189         }
31190         if(this.shim){
31191             this.shim.setStyle("z-index", ++index);
31192         }
31193         if(this.shadow){
31194             this.shadow.setZIndex(++index);
31195         }
31196         this.el.setStyle("z-index", ++index);
31197         if(this.proxy){
31198             this.proxy.setStyle("z-index", ++index);
31199         }
31200         if(this.resizer){
31201             this.resizer.proxy.setStyle("z-index", ++index);
31202         }
31203
31204         this.lastZIndex = index;
31205     },
31206
31207     /**
31208      * Returns the element for this dialog
31209      * @return {Roo.Element} The underlying dialog Element
31210      */
31211     getEl : function(){
31212         return this.el;
31213     }
31214 });
31215
31216 /**
31217  * @class Roo.DialogManager
31218  * Provides global access to BasicDialogs that have been created and
31219  * support for z-indexing (layering) multiple open dialogs.
31220  */
31221 Roo.DialogManager = function(){
31222     var list = {};
31223     var accessList = [];
31224     var front = null;
31225
31226     // private
31227     var sortDialogs = function(d1, d2){
31228         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31229     };
31230
31231     // private
31232     var orderDialogs = function(){
31233         accessList.sort(sortDialogs);
31234         var seed = Roo.DialogManager.zseed;
31235         for(var i = 0, len = accessList.length; i < len; i++){
31236             var dlg = accessList[i];
31237             if(dlg){
31238                 dlg.setZIndex(seed + (i*10));
31239             }
31240         }
31241     };
31242
31243     return {
31244         /**
31245          * The starting z-index for BasicDialogs (defaults to 9000)
31246          * @type Number The z-index value
31247          */
31248         zseed : 9000,
31249
31250         // private
31251         register : function(dlg){
31252             list[dlg.id] = dlg;
31253             accessList.push(dlg);
31254         },
31255
31256         // private
31257         unregister : function(dlg){
31258             delete list[dlg.id];
31259             var i=0;
31260             var len=0;
31261             if(!accessList.indexOf){
31262                 for(  i = 0, len = accessList.length; i < len; i++){
31263                     if(accessList[i] == dlg){
31264                         accessList.splice(i, 1);
31265                         return;
31266                     }
31267                 }
31268             }else{
31269                  i = accessList.indexOf(dlg);
31270                 if(i != -1){
31271                     accessList.splice(i, 1);
31272                 }
31273             }
31274         },
31275
31276         /**
31277          * Gets a registered dialog by id
31278          * @param {String/Object} id The id of the dialog or a dialog
31279          * @return {Roo.BasicDialog} this
31280          */
31281         get : function(id){
31282             return typeof id == "object" ? id : list[id];
31283         },
31284
31285         /**
31286          * Brings the specified dialog to the front
31287          * @param {String/Object} dlg The id of the dialog or a dialog
31288          * @return {Roo.BasicDialog} this
31289          */
31290         bringToFront : function(dlg){
31291             dlg = this.get(dlg);
31292             if(dlg != front){
31293                 front = dlg;
31294                 dlg._lastAccess = new Date().getTime();
31295                 orderDialogs();
31296             }
31297             return dlg;
31298         },
31299
31300         /**
31301          * Sends the specified dialog to the back
31302          * @param {String/Object} dlg The id of the dialog or a dialog
31303          * @return {Roo.BasicDialog} this
31304          */
31305         sendToBack : function(dlg){
31306             dlg = this.get(dlg);
31307             dlg._lastAccess = -(new Date().getTime());
31308             orderDialogs();
31309             return dlg;
31310         },
31311
31312         /**
31313          * Hides all dialogs
31314          */
31315         hideAll : function(){
31316             for(var id in list){
31317                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31318                     list[id].hide();
31319                 }
31320             }
31321         }
31322     };
31323 }();
31324
31325 /**
31326  * @class Roo.LayoutDialog
31327  * @extends Roo.BasicDialog
31328  * Dialog which provides adjustments for working with a layout in a Dialog.
31329  * Add your necessary layout config options to the dialog's config.<br>
31330  * Example usage (including a nested layout):
31331  * <pre><code>
31332 if(!dialog){
31333     dialog = new Roo.LayoutDialog("download-dlg", {
31334         modal: true,
31335         width:600,
31336         height:450,
31337         shadow:true,
31338         minWidth:500,
31339         minHeight:350,
31340         autoTabs:true,
31341         proxyDrag:true,
31342         // layout config merges with the dialog config
31343         center:{
31344             tabPosition: "top",
31345             alwaysShowTabs: true
31346         }
31347     });
31348     dialog.addKeyListener(27, dialog.hide, dialog);
31349     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31350     dialog.addButton("Build It!", this.getDownload, this);
31351
31352     // we can even add nested layouts
31353     var innerLayout = new Roo.BorderLayout("dl-inner", {
31354         east: {
31355             initialSize: 200,
31356             autoScroll:true,
31357             split:true
31358         },
31359         center: {
31360             autoScroll:true
31361         }
31362     });
31363     innerLayout.beginUpdate();
31364     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31365     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31366     innerLayout.endUpdate(true);
31367
31368     var layout = dialog.getLayout();
31369     layout.beginUpdate();
31370     layout.add("center", new Roo.ContentPanel("standard-panel",
31371                         {title: "Download the Source", fitToFrame:true}));
31372     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31373                {title: "Build your own roo.js"}));
31374     layout.getRegion("center").showPanel(sp);
31375     layout.endUpdate();
31376 }
31377 </code></pre>
31378     * @constructor
31379     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31380     * @param {Object} config configuration options
31381   */
31382 Roo.LayoutDialog = function(el, cfg){
31383     
31384     var config=  cfg;
31385     if (typeof(cfg) == 'undefined') {
31386         config = Roo.apply({}, el);
31387         // not sure why we use documentElement here.. - it should always be body.
31388         // IE7 borks horribly if we use documentElement.
31389         // webkit also does not like documentElement - it creates a body element...
31390         el = Roo.get( document.body || document.documentElement ).createChild();
31391         //config.autoCreate = true;
31392     }
31393     
31394     
31395     config.autoTabs = false;
31396     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31397     this.body.setStyle({overflow:"hidden", position:"relative"});
31398     this.layout = new Roo.BorderLayout(this.body.dom, config);
31399     this.layout.monitorWindowResize = false;
31400     this.el.addClass("x-dlg-auto-layout");
31401     // fix case when center region overwrites center function
31402     this.center = Roo.BasicDialog.prototype.center;
31403     this.on("show", this.layout.layout, this.layout, true);
31404     if (config.items) {
31405         var xitems = config.items;
31406         delete config.items;
31407         Roo.each(xitems, this.addxtype, this);
31408     }
31409     
31410     
31411 };
31412 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31413     /**
31414      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31415      * @deprecated
31416      */
31417     endUpdate : function(){
31418         this.layout.endUpdate();
31419     },
31420
31421     /**
31422      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31423      *  @deprecated
31424      */
31425     beginUpdate : function(){
31426         this.layout.beginUpdate();
31427     },
31428
31429     /**
31430      * Get the BorderLayout for this dialog
31431      * @return {Roo.BorderLayout}
31432      */
31433     getLayout : function(){
31434         return this.layout;
31435     },
31436
31437     showEl : function(){
31438         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31439         if(Roo.isIE7){
31440             this.layout.layout();
31441         }
31442     },
31443
31444     // private
31445     // Use the syncHeightBeforeShow config option to control this automatically
31446     syncBodyHeight : function(){
31447         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31448         if(this.layout){this.layout.layout();}
31449     },
31450     
31451       /**
31452      * Add an xtype element (actually adds to the layout.)
31453      * @return {Object} xdata xtype object data.
31454      */
31455     
31456     addxtype : function(c) {
31457         return this.layout.addxtype(c);
31458     }
31459 });/*
31460  * Based on:
31461  * Ext JS Library 1.1.1
31462  * Copyright(c) 2006-2007, Ext JS, LLC.
31463  *
31464  * Originally Released Under LGPL - original licence link has changed is not relivant.
31465  *
31466  * Fork - LGPL
31467  * <script type="text/javascript">
31468  */
31469  
31470 /**
31471  * @class Roo.MessageBox
31472  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31473  * Example usage:
31474  *<pre><code>
31475 // Basic alert:
31476 Roo.Msg.alert('Status', 'Changes saved successfully.');
31477
31478 // Prompt for user data:
31479 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31480     if (btn == 'ok'){
31481         // process text value...
31482     }
31483 });
31484
31485 // Show a dialog using config options:
31486 Roo.Msg.show({
31487    title:'Save Changes?',
31488    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31489    buttons: Roo.Msg.YESNOCANCEL,
31490    fn: processResult,
31491    animEl: 'elId'
31492 });
31493 </code></pre>
31494  * @singleton
31495  */
31496 Roo.MessageBox = function(){
31497     var dlg, opt, mask, waitTimer;
31498     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31499     var buttons, activeTextEl, bwidth;
31500
31501     // private
31502     var handleButton = function(button){
31503         dlg.hide();
31504         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31505     };
31506
31507     // private
31508     var handleHide = function(){
31509         if(opt && opt.cls){
31510             dlg.el.removeClass(opt.cls);
31511         }
31512         if(waitTimer){
31513             Roo.TaskMgr.stop(waitTimer);
31514             waitTimer = null;
31515         }
31516     };
31517
31518     // private
31519     var updateButtons = function(b){
31520         var width = 0;
31521         if(!b){
31522             buttons["ok"].hide();
31523             buttons["cancel"].hide();
31524             buttons["yes"].hide();
31525             buttons["no"].hide();
31526             dlg.footer.dom.style.display = 'none';
31527             return width;
31528         }
31529         dlg.footer.dom.style.display = '';
31530         for(var k in buttons){
31531             if(typeof buttons[k] != "function"){
31532                 if(b[k]){
31533                     buttons[k].show();
31534                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31535                     width += buttons[k].el.getWidth()+15;
31536                 }else{
31537                     buttons[k].hide();
31538                 }
31539             }
31540         }
31541         return width;
31542     };
31543
31544     // private
31545     var handleEsc = function(d, k, e){
31546         if(opt && opt.closable !== false){
31547             dlg.hide();
31548         }
31549         if(e){
31550             e.stopEvent();
31551         }
31552     };
31553
31554     return {
31555         /**
31556          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31557          * @return {Roo.BasicDialog} The BasicDialog element
31558          */
31559         getDialog : function(){
31560            if(!dlg){
31561                 dlg = new Roo.BasicDialog("x-msg-box", {
31562                     autoCreate : true,
31563                     shadow: true,
31564                     draggable: true,
31565                     resizable:false,
31566                     constraintoviewport:false,
31567                     fixedcenter:true,
31568                     collapsible : false,
31569                     shim:true,
31570                     modal: true,
31571                     width:400, height:100,
31572                     buttonAlign:"center",
31573                     closeClick : function(){
31574                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31575                             handleButton("no");
31576                         }else{
31577                             handleButton("cancel");
31578                         }
31579                     }
31580                 });
31581                 dlg.on("hide", handleHide);
31582                 mask = dlg.mask;
31583                 dlg.addKeyListener(27, handleEsc);
31584                 buttons = {};
31585                 var bt = this.buttonText;
31586                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31587                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31588                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31589                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31590                 bodyEl = dlg.body.createChild({
31591
31592                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
31593                 });
31594                 msgEl = bodyEl.dom.firstChild;
31595                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31596                 textboxEl.enableDisplayMode();
31597                 textboxEl.addKeyListener([10,13], function(){
31598                     if(dlg.isVisible() && opt && opt.buttons){
31599                         if(opt.buttons.ok){
31600                             handleButton("ok");
31601                         }else if(opt.buttons.yes){
31602                             handleButton("yes");
31603                         }
31604                     }
31605                 });
31606                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31607                 textareaEl.enableDisplayMode();
31608                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31609                 progressEl.enableDisplayMode();
31610                 var pf = progressEl.dom.firstChild;
31611                 if (pf) {
31612                     pp = Roo.get(pf.firstChild);
31613                     pp.setHeight(pf.offsetHeight);
31614                 }
31615                 
31616             }
31617             return dlg;
31618         },
31619
31620         /**
31621          * Updates the message box body text
31622          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31623          * the XHTML-compliant non-breaking space character '&amp;#160;')
31624          * @return {Roo.MessageBox} This message box
31625          */
31626         updateText : function(text){
31627             if(!dlg.isVisible() && !opt.width){
31628                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31629             }
31630             msgEl.innerHTML = text || '&#160;';
31631       
31632             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31633             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31634             var w = Math.max(
31635                     Math.min(opt.width || cw , this.maxWidth), 
31636                     Math.max(opt.minWidth || this.minWidth, bwidth)
31637             );
31638             if(opt.prompt){
31639                 activeTextEl.setWidth(w);
31640             }
31641             if(dlg.isVisible()){
31642                 dlg.fixedcenter = false;
31643             }
31644             // to big, make it scroll. = But as usual stupid IE does not support
31645             // !important..
31646             
31647             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31648                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31649                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31650             } else {
31651                 bodyEl.dom.style.height = '';
31652                 bodyEl.dom.style.overflowY = '';
31653             }
31654             if (cw > w) {
31655                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31656             } else {
31657                 bodyEl.dom.style.overflowX = '';
31658             }
31659             
31660             dlg.setContentSize(w, bodyEl.getHeight());
31661             if(dlg.isVisible()){
31662                 dlg.fixedcenter = true;
31663             }
31664             return this;
31665         },
31666
31667         /**
31668          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31669          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31670          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31671          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31672          * @return {Roo.MessageBox} This message box
31673          */
31674         updateProgress : function(value, text){
31675             if(text){
31676                 this.updateText(text);
31677             }
31678             if (pp) { // weird bug on my firefox - for some reason this is not defined
31679                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31680             }
31681             return this;
31682         },        
31683
31684         /**
31685          * Returns true if the message box is currently displayed
31686          * @return {Boolean} True if the message box is visible, else false
31687          */
31688         isVisible : function(){
31689             return dlg && dlg.isVisible();  
31690         },
31691
31692         /**
31693          * Hides the message box if it is displayed
31694          */
31695         hide : function(){
31696             if(this.isVisible()){
31697                 dlg.hide();
31698             }  
31699         },
31700
31701         /**
31702          * Displays a new message box, or reinitializes an existing message box, based on the config options
31703          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31704          * The following config object properties are supported:
31705          * <pre>
31706 Property    Type             Description
31707 ----------  ---------------  ------------------------------------------------------------------------------------
31708 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31709                                    closes (defaults to undefined)
31710 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31711                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31712 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31713                                    progress and wait dialogs will ignore this property and always hide the
31714                                    close button as they can only be closed programmatically.
31715 cls               String           A custom CSS class to apply to the message box element
31716 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31717                                    displayed (defaults to 75)
31718 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31719                                    function will be btn (the name of the button that was clicked, if applicable,
31720                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31721                                    Progress and wait dialogs will ignore this option since they do not respond to
31722                                    user actions and can only be closed programmatically, so any required function
31723                                    should be called by the same code after it closes the dialog.
31724 icon              String           A CSS class that provides a background image to be used as an icon for
31725                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31726 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31727 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31728 modal             Boolean          False to allow user interaction with the page while the message box is
31729                                    displayed (defaults to true)
31730 msg               String           A string that will replace the existing message box body text (defaults
31731                                    to the XHTML-compliant non-breaking space character '&#160;')
31732 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31733 progress          Boolean          True to display a progress bar (defaults to false)
31734 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31735 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31736 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31737 title             String           The title text
31738 value             String           The string value to set into the active textbox element if displayed
31739 wait              Boolean          True to display a progress bar (defaults to false)
31740 width             Number           The width of the dialog in pixels
31741 </pre>
31742          *
31743          * Example usage:
31744          * <pre><code>
31745 Roo.Msg.show({
31746    title: 'Address',
31747    msg: 'Please enter your address:',
31748    width: 300,
31749    buttons: Roo.MessageBox.OKCANCEL,
31750    multiline: true,
31751    fn: saveAddress,
31752    animEl: 'addAddressBtn'
31753 });
31754 </code></pre>
31755          * @param {Object} config Configuration options
31756          * @return {Roo.MessageBox} This message box
31757          */
31758         show : function(options)
31759         {
31760             
31761             // this causes nightmares if you show one dialog after another
31762             // especially on callbacks..
31763              
31764             if(this.isVisible()){
31765                 
31766                 this.hide();
31767                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31768                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31769                 Roo.log("New Dialog Message:" +  options.msg )
31770                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31771                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31772                 
31773             }
31774             var d = this.getDialog();
31775             opt = options;
31776             d.setTitle(opt.title || "&#160;");
31777             d.close.setDisplayed(opt.closable !== false);
31778             activeTextEl = textboxEl;
31779             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31780             if(opt.prompt){
31781                 if(opt.multiline){
31782                     textboxEl.hide();
31783                     textareaEl.show();
31784                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31785                         opt.multiline : this.defaultTextHeight);
31786                     activeTextEl = textareaEl;
31787                 }else{
31788                     textboxEl.show();
31789                     textareaEl.hide();
31790                 }
31791             }else{
31792                 textboxEl.hide();
31793                 textareaEl.hide();
31794             }
31795             progressEl.setDisplayed(opt.progress === true);
31796             this.updateProgress(0);
31797             activeTextEl.dom.value = opt.value || "";
31798             if(opt.prompt){
31799                 dlg.setDefaultButton(activeTextEl);
31800             }else{
31801                 var bs = opt.buttons;
31802                 var db = null;
31803                 if(bs && bs.ok){
31804                     db = buttons["ok"];
31805                 }else if(bs && bs.yes){
31806                     db = buttons["yes"];
31807                 }
31808                 dlg.setDefaultButton(db);
31809             }
31810             bwidth = updateButtons(opt.buttons);
31811             this.updateText(opt.msg);
31812             if(opt.cls){
31813                 d.el.addClass(opt.cls);
31814             }
31815             d.proxyDrag = opt.proxyDrag === true;
31816             d.modal = opt.modal !== false;
31817             d.mask = opt.modal !== false ? mask : false;
31818             if(!d.isVisible()){
31819                 // force it to the end of the z-index stack so it gets a cursor in FF
31820                 document.body.appendChild(dlg.el.dom);
31821                 d.animateTarget = null;
31822                 d.show(options.animEl);
31823             }
31824             return this;
31825         },
31826
31827         /**
31828          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31829          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31830          * and closing the message box when the process is complete.
31831          * @param {String} title The title bar text
31832          * @param {String} msg The message box body text
31833          * @return {Roo.MessageBox} This message box
31834          */
31835         progress : function(title, msg){
31836             this.show({
31837                 title : title,
31838                 msg : msg,
31839                 buttons: false,
31840                 progress:true,
31841                 closable:false,
31842                 minWidth: this.minProgressWidth,
31843                 modal : true
31844             });
31845             return this;
31846         },
31847
31848         /**
31849          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31850          * If a callback function is passed it will be called after the user clicks the button, and the
31851          * id of the button that was clicked will be passed as the only parameter to the callback
31852          * (could also be the top-right close button).
31853          * @param {String} title The title bar text
31854          * @param {String} msg The message box body text
31855          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31856          * @param {Object} scope (optional) The scope of the callback function
31857          * @return {Roo.MessageBox} This message box
31858          */
31859         alert : function(title, msg, fn, scope){
31860             this.show({
31861                 title : title,
31862                 msg : msg,
31863                 buttons: this.OK,
31864                 fn: fn,
31865                 scope : scope,
31866                 modal : true
31867             });
31868             return this;
31869         },
31870
31871         /**
31872          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31873          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31874          * You are responsible for closing the message box when the process is complete.
31875          * @param {String} msg The message box body text
31876          * @param {String} title (optional) The title bar text
31877          * @return {Roo.MessageBox} This message box
31878          */
31879         wait : function(msg, title){
31880             this.show({
31881                 title : title,
31882                 msg : msg,
31883                 buttons: false,
31884                 closable:false,
31885                 progress:true,
31886                 modal:true,
31887                 width:300,
31888                 wait:true
31889             });
31890             waitTimer = Roo.TaskMgr.start({
31891                 run: function(i){
31892                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31893                 },
31894                 interval: 1000
31895             });
31896             return this;
31897         },
31898
31899         /**
31900          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31901          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31902          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31903          * @param {String} title The title bar text
31904          * @param {String} msg The message box body text
31905          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31906          * @param {Object} scope (optional) The scope of the callback function
31907          * @return {Roo.MessageBox} This message box
31908          */
31909         confirm : function(title, msg, fn, scope){
31910             this.show({
31911                 title : title,
31912                 msg : msg,
31913                 buttons: this.YESNO,
31914                 fn: fn,
31915                 scope : scope,
31916                 modal : true
31917             });
31918             return this;
31919         },
31920
31921         /**
31922          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31923          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31924          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31925          * (could also be the top-right close button) and the text that was entered will be passed as the two
31926          * parameters to the callback.
31927          * @param {String} title The title bar text
31928          * @param {String} msg The message box body text
31929          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31930          * @param {Object} scope (optional) The scope of the callback function
31931          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31932          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31933          * @return {Roo.MessageBox} This message box
31934          */
31935         prompt : function(title, msg, fn, scope, multiline){
31936             this.show({
31937                 title : title,
31938                 msg : msg,
31939                 buttons: this.OKCANCEL,
31940                 fn: fn,
31941                 minWidth:250,
31942                 scope : scope,
31943                 prompt:true,
31944                 multiline: multiline,
31945                 modal : true
31946             });
31947             return this;
31948         },
31949
31950         /**
31951          * Button config that displays a single OK button
31952          * @type Object
31953          */
31954         OK : {ok:true},
31955         /**
31956          * Button config that displays Yes and No buttons
31957          * @type Object
31958          */
31959         YESNO : {yes:true, no:true},
31960         /**
31961          * Button config that displays OK and Cancel buttons
31962          * @type Object
31963          */
31964         OKCANCEL : {ok:true, cancel:true},
31965         /**
31966          * Button config that displays Yes, No and Cancel buttons
31967          * @type Object
31968          */
31969         YESNOCANCEL : {yes:true, no:true, cancel:true},
31970
31971         /**
31972          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31973          * @type Number
31974          */
31975         defaultTextHeight : 75,
31976         /**
31977          * The maximum width in pixels of the message box (defaults to 600)
31978          * @type Number
31979          */
31980         maxWidth : 600,
31981         /**
31982          * The minimum width in pixels of the message box (defaults to 100)
31983          * @type Number
31984          */
31985         minWidth : 100,
31986         /**
31987          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31988          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31989          * @type Number
31990          */
31991         minProgressWidth : 250,
31992         /**
31993          * An object containing the default button text strings that can be overriden for localized language support.
31994          * Supported properties are: ok, cancel, yes and no.
31995          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31996          * @type Object
31997          */
31998         buttonText : {
31999             ok : "OK",
32000             cancel : "Cancel",
32001             yes : "Yes",
32002             no : "No"
32003         }
32004     };
32005 }();
32006
32007 /**
32008  * Shorthand for {@link Roo.MessageBox}
32009  */
32010 Roo.Msg = Roo.MessageBox;/*
32011  * Based on:
32012  * Ext JS Library 1.1.1
32013  * Copyright(c) 2006-2007, Ext JS, LLC.
32014  *
32015  * Originally Released Under LGPL - original licence link has changed is not relivant.
32016  *
32017  * Fork - LGPL
32018  * <script type="text/javascript">
32019  */
32020 /**
32021  * @class Roo.QuickTips
32022  * Provides attractive and customizable tooltips for any element.
32023  * @singleton
32024  */
32025 Roo.QuickTips = function(){
32026     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
32027     var ce, bd, xy, dd;
32028     var visible = false, disabled = true, inited = false;
32029     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
32030     
32031     var onOver = function(e){
32032         if(disabled){
32033             return;
32034         }
32035         var t = e.getTarget();
32036         if(!t || t.nodeType !== 1 || t == document || t == document.body){
32037             return;
32038         }
32039         if(ce && t == ce.el){
32040             clearTimeout(hideProc);
32041             return;
32042         }
32043         if(t && tagEls[t.id]){
32044             tagEls[t.id].el = t;
32045             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32046             return;
32047         }
32048         var ttp, et = Roo.fly(t);
32049         var ns = cfg.namespace;
32050         if(tm.interceptTitles && t.title){
32051             ttp = t.title;
32052             t.qtip = ttp;
32053             t.removeAttribute("title");
32054             e.preventDefault();
32055         }else{
32056             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
32057         }
32058         if(ttp){
32059             showProc = show.defer(tm.showDelay, tm, [{
32060                 el: t, 
32061                 text: ttp, 
32062                 width: et.getAttributeNS(ns, cfg.width),
32063                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32064                 title: et.getAttributeNS(ns, cfg.title),
32065                     cls: et.getAttributeNS(ns, cfg.cls)
32066             }]);
32067         }
32068     };
32069     
32070     var onOut = function(e){
32071         clearTimeout(showProc);
32072         var t = e.getTarget();
32073         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32074             hideProc = setTimeout(hide, tm.hideDelay);
32075         }
32076     };
32077     
32078     var onMove = function(e){
32079         if(disabled){
32080             return;
32081         }
32082         xy = e.getXY();
32083         xy[1] += 18;
32084         if(tm.trackMouse && ce){
32085             el.setXY(xy);
32086         }
32087     };
32088     
32089     var onDown = function(e){
32090         clearTimeout(showProc);
32091         clearTimeout(hideProc);
32092         if(!e.within(el)){
32093             if(tm.hideOnClick){
32094                 hide();
32095                 tm.disable();
32096                 tm.enable.defer(100, tm);
32097             }
32098         }
32099     };
32100     
32101     var getPad = function(){
32102         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32103     };
32104
32105     var show = function(o){
32106         if(disabled){
32107             return;
32108         }
32109         clearTimeout(dismissProc);
32110         ce = o;
32111         if(removeCls){ // in case manually hidden
32112             el.removeClass(removeCls);
32113             removeCls = null;
32114         }
32115         if(ce.cls){
32116             el.addClass(ce.cls);
32117             removeCls = ce.cls;
32118         }
32119         if(ce.title){
32120             tipTitle.update(ce.title);
32121             tipTitle.show();
32122         }else{
32123             tipTitle.update('');
32124             tipTitle.hide();
32125         }
32126         el.dom.style.width  = tm.maxWidth+'px';
32127         //tipBody.dom.style.width = '';
32128         tipBodyText.update(o.text);
32129         var p = getPad(), w = ce.width;
32130         if(!w){
32131             var td = tipBodyText.dom;
32132             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32133             if(aw > tm.maxWidth){
32134                 w = tm.maxWidth;
32135             }else if(aw < tm.minWidth){
32136                 w = tm.minWidth;
32137             }else{
32138                 w = aw;
32139             }
32140         }
32141         //tipBody.setWidth(w);
32142         el.setWidth(parseInt(w, 10) + p);
32143         if(ce.autoHide === false){
32144             close.setDisplayed(true);
32145             if(dd){
32146                 dd.unlock();
32147             }
32148         }else{
32149             close.setDisplayed(false);
32150             if(dd){
32151                 dd.lock();
32152             }
32153         }
32154         if(xy){
32155             el.avoidY = xy[1]-18;
32156             el.setXY(xy);
32157         }
32158         if(tm.animate){
32159             el.setOpacity(.1);
32160             el.setStyle("visibility", "visible");
32161             el.fadeIn({callback: afterShow});
32162         }else{
32163             afterShow();
32164         }
32165     };
32166     
32167     var afterShow = function(){
32168         if(ce){
32169             el.show();
32170             esc.enable();
32171             if(tm.autoDismiss && ce.autoHide !== false){
32172                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32173             }
32174         }
32175     };
32176     
32177     var hide = function(noanim){
32178         clearTimeout(dismissProc);
32179         clearTimeout(hideProc);
32180         ce = null;
32181         if(el.isVisible()){
32182             esc.disable();
32183             if(noanim !== true && tm.animate){
32184                 el.fadeOut({callback: afterHide});
32185             }else{
32186                 afterHide();
32187             } 
32188         }
32189     };
32190     
32191     var afterHide = function(){
32192         el.hide();
32193         if(removeCls){
32194             el.removeClass(removeCls);
32195             removeCls = null;
32196         }
32197     };
32198     
32199     return {
32200         /**
32201         * @cfg {Number} minWidth
32202         * The minimum width of the quick tip (defaults to 40)
32203         */
32204        minWidth : 40,
32205         /**
32206         * @cfg {Number} maxWidth
32207         * The maximum width of the quick tip (defaults to 300)
32208         */
32209        maxWidth : 300,
32210         /**
32211         * @cfg {Boolean} interceptTitles
32212         * True to automatically use the element's DOM title value if available (defaults to false)
32213         */
32214        interceptTitles : false,
32215         /**
32216         * @cfg {Boolean} trackMouse
32217         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32218         */
32219        trackMouse : false,
32220         /**
32221         * @cfg {Boolean} hideOnClick
32222         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32223         */
32224        hideOnClick : true,
32225         /**
32226         * @cfg {Number} showDelay
32227         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32228         */
32229        showDelay : 500,
32230         /**
32231         * @cfg {Number} hideDelay
32232         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32233         */
32234        hideDelay : 200,
32235         /**
32236         * @cfg {Boolean} autoHide
32237         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32238         * Used in conjunction with hideDelay.
32239         */
32240        autoHide : true,
32241         /**
32242         * @cfg {Boolean}
32243         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32244         * (defaults to true).  Used in conjunction with autoDismissDelay.
32245         */
32246        autoDismiss : true,
32247         /**
32248         * @cfg {Number}
32249         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32250         */
32251        autoDismissDelay : 5000,
32252        /**
32253         * @cfg {Boolean} animate
32254         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32255         */
32256        animate : false,
32257
32258        /**
32259         * @cfg {String} title
32260         * Title text to display (defaults to '').  This can be any valid HTML markup.
32261         */
32262         title: '',
32263        /**
32264         * @cfg {String} text
32265         * Body text to display (defaults to '').  This can be any valid HTML markup.
32266         */
32267         text : '',
32268        /**
32269         * @cfg {String} cls
32270         * A CSS class to apply to the base quick tip element (defaults to '').
32271         */
32272         cls : '',
32273        /**
32274         * @cfg {Number} width
32275         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32276         * minWidth or maxWidth.
32277         */
32278         width : null,
32279
32280     /**
32281      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32282      * or display QuickTips in a page.
32283      */
32284        init : function(){
32285           tm = Roo.QuickTips;
32286           cfg = tm.tagConfig;
32287           if(!inited){
32288               if(!Roo.isReady){ // allow calling of init() before onReady
32289                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32290                   return;
32291               }
32292               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32293               el.fxDefaults = {stopFx: true};
32294               // maximum custom styling
32295               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
32296               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
32297               tipTitle = el.child('h3');
32298               tipTitle.enableDisplayMode("block");
32299               tipBody = el.child('div.x-tip-bd');
32300               tipBodyText = el.child('div.x-tip-bd-inner');
32301               //bdLeft = el.child('div.x-tip-bd-left');
32302               //bdRight = el.child('div.x-tip-bd-right');
32303               close = el.child('div.x-tip-close');
32304               close.enableDisplayMode("block");
32305               close.on("click", hide);
32306               var d = Roo.get(document);
32307               d.on("mousedown", onDown);
32308               d.on("mouseover", onOver);
32309               d.on("mouseout", onOut);
32310               d.on("mousemove", onMove);
32311               esc = d.addKeyListener(27, hide);
32312               esc.disable();
32313               if(Roo.dd.DD){
32314                   dd = el.initDD("default", null, {
32315                       onDrag : function(){
32316                           el.sync();  
32317                       }
32318                   });
32319                   dd.setHandleElId(tipTitle.id);
32320                   dd.lock();
32321               }
32322               inited = true;
32323           }
32324           this.enable(); 
32325        },
32326
32327     /**
32328      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32329      * are supported:
32330      * <pre>
32331 Property    Type                   Description
32332 ----------  ---------------------  ------------------------------------------------------------------------
32333 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32334      * </ul>
32335      * @param {Object} config The config object
32336      */
32337        register : function(config){
32338            var cs = config instanceof Array ? config : arguments;
32339            for(var i = 0, len = cs.length; i < len; i++) {
32340                var c = cs[i];
32341                var target = c.target;
32342                if(target){
32343                    if(target instanceof Array){
32344                        for(var j = 0, jlen = target.length; j < jlen; j++){
32345                            tagEls[target[j]] = c;
32346                        }
32347                    }else{
32348                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32349                    }
32350                }
32351            }
32352        },
32353
32354     /**
32355      * Removes this quick tip from its element and destroys it.
32356      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32357      */
32358        unregister : function(el){
32359            delete tagEls[Roo.id(el)];
32360        },
32361
32362     /**
32363      * Enable this quick tip.
32364      */
32365        enable : function(){
32366            if(inited && disabled){
32367                locks.pop();
32368                if(locks.length < 1){
32369                    disabled = false;
32370                }
32371            }
32372        },
32373
32374     /**
32375      * Disable this quick tip.
32376      */
32377        disable : function(){
32378           disabled = true;
32379           clearTimeout(showProc);
32380           clearTimeout(hideProc);
32381           clearTimeout(dismissProc);
32382           if(ce){
32383               hide(true);
32384           }
32385           locks.push(1);
32386        },
32387
32388     /**
32389      * Returns true if the quick tip is enabled, else false.
32390      */
32391        isEnabled : function(){
32392             return !disabled;
32393        },
32394
32395         // private
32396        tagConfig : {
32397            namespace : "roo", // was ext?? this may break..
32398            alt_namespace : "ext",
32399            attribute : "qtip",
32400            width : "width",
32401            target : "target",
32402            title : "qtitle",
32403            hide : "hide",
32404            cls : "qclass"
32405        }
32406    };
32407 }();
32408
32409 // backwards compat
32410 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32411  * Based on:
32412  * Ext JS Library 1.1.1
32413  * Copyright(c) 2006-2007, Ext JS, LLC.
32414  *
32415  * Originally Released Under LGPL - original licence link has changed is not relivant.
32416  *
32417  * Fork - LGPL
32418  * <script type="text/javascript">
32419  */
32420  
32421
32422 /**
32423  * @class Roo.tree.TreePanel
32424  * @extends Roo.data.Tree
32425
32426  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32427  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32428  * @cfg {Boolean} enableDD true to enable drag and drop
32429  * @cfg {Boolean} enableDrag true to enable just drag
32430  * @cfg {Boolean} enableDrop true to enable just drop
32431  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32432  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32433  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32434  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32435  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32436  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32437  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32438  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32439  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32440  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32441  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32442  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32443  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32444  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32445  * @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>
32446  * @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>
32447  * 
32448  * @constructor
32449  * @param {String/HTMLElement/Element} el The container element
32450  * @param {Object} config
32451  */
32452 Roo.tree.TreePanel = function(el, config){
32453     var root = false;
32454     var loader = false;
32455     if (config.root) {
32456         root = config.root;
32457         delete config.root;
32458     }
32459     if (config.loader) {
32460         loader = config.loader;
32461         delete config.loader;
32462     }
32463     
32464     Roo.apply(this, config);
32465     Roo.tree.TreePanel.superclass.constructor.call(this);
32466     this.el = Roo.get(el);
32467     this.el.addClass('x-tree');
32468     //console.log(root);
32469     if (root) {
32470         this.setRootNode( Roo.factory(root, Roo.tree));
32471     }
32472     if (loader) {
32473         this.loader = Roo.factory(loader, Roo.tree);
32474     }
32475    /**
32476     * Read-only. The id of the container element becomes this TreePanel's id.
32477     */
32478     this.id = this.el.id;
32479     this.addEvents({
32480         /**
32481         * @event beforeload
32482         * Fires before a node is loaded, return false to cancel
32483         * @param {Node} node The node being loaded
32484         */
32485         "beforeload" : true,
32486         /**
32487         * @event load
32488         * Fires when a node is loaded
32489         * @param {Node} node The node that was loaded
32490         */
32491         "load" : true,
32492         /**
32493         * @event textchange
32494         * Fires when the text for a node is changed
32495         * @param {Node} node The node
32496         * @param {String} text The new text
32497         * @param {String} oldText The old text
32498         */
32499         "textchange" : true,
32500         /**
32501         * @event beforeexpand
32502         * Fires before a node is expanded, return false to cancel.
32503         * @param {Node} node The node
32504         * @param {Boolean} deep
32505         * @param {Boolean} anim
32506         */
32507         "beforeexpand" : true,
32508         /**
32509         * @event beforecollapse
32510         * Fires before a node is collapsed, return false to cancel.
32511         * @param {Node} node The node
32512         * @param {Boolean} deep
32513         * @param {Boolean} anim
32514         */
32515         "beforecollapse" : true,
32516         /**
32517         * @event expand
32518         * Fires when a node is expanded
32519         * @param {Node} node The node
32520         */
32521         "expand" : true,
32522         /**
32523         * @event disabledchange
32524         * Fires when the disabled status of a node changes
32525         * @param {Node} node The node
32526         * @param {Boolean} disabled
32527         */
32528         "disabledchange" : true,
32529         /**
32530         * @event collapse
32531         * Fires when a node is collapsed
32532         * @param {Node} node The node
32533         */
32534         "collapse" : true,
32535         /**
32536         * @event beforeclick
32537         * Fires before click processing on a node. Return false to cancel the default action.
32538         * @param {Node} node The node
32539         * @param {Roo.EventObject} e The event object
32540         */
32541         "beforeclick":true,
32542         /**
32543         * @event checkchange
32544         * Fires when a node with a checkbox's checked property changes
32545         * @param {Node} this This node
32546         * @param {Boolean} checked
32547         */
32548         "checkchange":true,
32549         /**
32550         * @event click
32551         * Fires when a node is clicked
32552         * @param {Node} node The node
32553         * @param {Roo.EventObject} e The event object
32554         */
32555         "click":true,
32556         /**
32557         * @event dblclick
32558         * Fires when a node is double clicked
32559         * @param {Node} node The node
32560         * @param {Roo.EventObject} e The event object
32561         */
32562         "dblclick":true,
32563         /**
32564         * @event contextmenu
32565         * Fires when a node is right clicked
32566         * @param {Node} node The node
32567         * @param {Roo.EventObject} e The event object
32568         */
32569         "contextmenu":true,
32570         /**
32571         * @event beforechildrenrendered
32572         * Fires right before the child nodes for a node are rendered
32573         * @param {Node} node The node
32574         */
32575         "beforechildrenrendered":true,
32576         /**
32577         * @event startdrag
32578         * Fires when a node starts being dragged
32579         * @param {Roo.tree.TreePanel} this
32580         * @param {Roo.tree.TreeNode} node
32581         * @param {event} e The raw browser event
32582         */ 
32583        "startdrag" : true,
32584        /**
32585         * @event enddrag
32586         * Fires when a drag operation is complete
32587         * @param {Roo.tree.TreePanel} this
32588         * @param {Roo.tree.TreeNode} node
32589         * @param {event} e The raw browser event
32590         */
32591        "enddrag" : true,
32592        /**
32593         * @event dragdrop
32594         * Fires when a dragged node is dropped on a valid DD target
32595         * @param {Roo.tree.TreePanel} this
32596         * @param {Roo.tree.TreeNode} node
32597         * @param {DD} dd The dd it was dropped on
32598         * @param {event} e The raw browser event
32599         */
32600        "dragdrop" : true,
32601        /**
32602         * @event beforenodedrop
32603         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32604         * passed to handlers has the following properties:<br />
32605         * <ul style="padding:5px;padding-left:16px;">
32606         * <li>tree - The TreePanel</li>
32607         * <li>target - The node being targeted for the drop</li>
32608         * <li>data - The drag data from the drag source</li>
32609         * <li>point - The point of the drop - append, above or below</li>
32610         * <li>source - The drag source</li>
32611         * <li>rawEvent - Raw mouse event</li>
32612         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32613         * to be inserted by setting them on this object.</li>
32614         * <li>cancel - Set this to true to cancel the drop.</li>
32615         * </ul>
32616         * @param {Object} dropEvent
32617         */
32618        "beforenodedrop" : true,
32619        /**
32620         * @event nodedrop
32621         * Fires after a DD object is dropped on a node in this tree. The dropEvent
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 - Dropped node(s).</li>
32631         * </ul>
32632         * @param {Object} dropEvent
32633         */
32634        "nodedrop" : true,
32635         /**
32636         * @event nodedragover
32637         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32638         * passed to handlers has the following properties:<br />
32639         * <ul style="padding:5px;padding-left:16px;">
32640         * <li>tree - The TreePanel</li>
32641         * <li>target - The node being targeted for the drop</li>
32642         * <li>data - The drag data from the drag source</li>
32643         * <li>point - The point of the drop - append, above or below</li>
32644         * <li>source - The drag source</li>
32645         * <li>rawEvent - Raw mouse event</li>
32646         * <li>dropNode - Drop node(s) provided by the source.</li>
32647         * <li>cancel - Set this to true to signal drop not allowed.</li>
32648         * </ul>
32649         * @param {Object} dragOverEvent
32650         */
32651        "nodedragover" : true
32652         
32653     });
32654     if(this.singleExpand){
32655        this.on("beforeexpand", this.restrictExpand, this);
32656     }
32657     if (this.editor) {
32658         this.editor.tree = this;
32659         this.editor = Roo.factory(this.editor, Roo.tree);
32660     }
32661     
32662     if (this.selModel) {
32663         this.selModel = Roo.factory(this.selModel, Roo.tree);
32664     }
32665    
32666 };
32667 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32668     rootVisible : true,
32669     animate: Roo.enableFx,
32670     lines : true,
32671     enableDD : false,
32672     hlDrop : Roo.enableFx,
32673   
32674     renderer: false,
32675     
32676     rendererTip: false,
32677     // private
32678     restrictExpand : function(node){
32679         var p = node.parentNode;
32680         if(p){
32681             if(p.expandedChild && p.expandedChild.parentNode == p){
32682                 p.expandedChild.collapse();
32683             }
32684             p.expandedChild = node;
32685         }
32686     },
32687
32688     // private override
32689     setRootNode : function(node){
32690         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32691         if(!this.rootVisible){
32692             node.ui = new Roo.tree.RootTreeNodeUI(node);
32693         }
32694         return node;
32695     },
32696
32697     /**
32698      * Returns the container element for this TreePanel
32699      */
32700     getEl : function(){
32701         return this.el;
32702     },
32703
32704     /**
32705      * Returns the default TreeLoader for this TreePanel
32706      */
32707     getLoader : function(){
32708         return this.loader;
32709     },
32710
32711     /**
32712      * Expand all nodes
32713      */
32714     expandAll : function(){
32715         this.root.expand(true);
32716     },
32717
32718     /**
32719      * Collapse all nodes
32720      */
32721     collapseAll : function(){
32722         this.root.collapse(true);
32723     },
32724
32725     /**
32726      * Returns the selection model used by this TreePanel
32727      */
32728     getSelectionModel : function(){
32729         if(!this.selModel){
32730             this.selModel = new Roo.tree.DefaultSelectionModel();
32731         }
32732         return this.selModel;
32733     },
32734
32735     /**
32736      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32737      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32738      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32739      * @return {Array}
32740      */
32741     getChecked : function(a, startNode){
32742         startNode = startNode || this.root;
32743         var r = [];
32744         var f = function(){
32745             if(this.attributes.checked){
32746                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32747             }
32748         }
32749         startNode.cascade(f);
32750         return r;
32751     },
32752
32753     /**
32754      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32755      * @param {String} path
32756      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32757      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32758      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32759      */
32760     expandPath : function(path, attr, callback){
32761         attr = attr || "id";
32762         var keys = path.split(this.pathSeparator);
32763         var curNode = this.root;
32764         if(curNode.attributes[attr] != keys[1]){ // invalid root
32765             if(callback){
32766                 callback(false, null);
32767             }
32768             return;
32769         }
32770         var index = 1;
32771         var f = function(){
32772             if(++index == keys.length){
32773                 if(callback){
32774                     callback(true, curNode);
32775                 }
32776                 return;
32777             }
32778             var c = curNode.findChild(attr, keys[index]);
32779             if(!c){
32780                 if(callback){
32781                     callback(false, curNode);
32782                 }
32783                 return;
32784             }
32785             curNode = c;
32786             c.expand(false, false, f);
32787         };
32788         curNode.expand(false, false, f);
32789     },
32790
32791     /**
32792      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32793      * @param {String} path
32794      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32795      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32796      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32797      */
32798     selectPath : function(path, attr, callback){
32799         attr = attr || "id";
32800         var keys = path.split(this.pathSeparator);
32801         var v = keys.pop();
32802         if(keys.length > 0){
32803             var f = function(success, node){
32804                 if(success && node){
32805                     var n = node.findChild(attr, v);
32806                     if(n){
32807                         n.select();
32808                         if(callback){
32809                             callback(true, n);
32810                         }
32811                     }else if(callback){
32812                         callback(false, n);
32813                     }
32814                 }else{
32815                     if(callback){
32816                         callback(false, n);
32817                     }
32818                 }
32819             };
32820             this.expandPath(keys.join(this.pathSeparator), attr, f);
32821         }else{
32822             this.root.select();
32823             if(callback){
32824                 callback(true, this.root);
32825             }
32826         }
32827     },
32828
32829     getTreeEl : function(){
32830         return this.el;
32831     },
32832
32833     /**
32834      * Trigger rendering of this TreePanel
32835      */
32836     render : function(){
32837         if (this.innerCt) {
32838             return this; // stop it rendering more than once!!
32839         }
32840         
32841         this.innerCt = this.el.createChild({tag:"ul",
32842                cls:"x-tree-root-ct " +
32843                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32844
32845         if(this.containerScroll){
32846             Roo.dd.ScrollManager.register(this.el);
32847         }
32848         if((this.enableDD || this.enableDrop) && !this.dropZone){
32849            /**
32850             * The dropZone used by this tree if drop is enabled
32851             * @type Roo.tree.TreeDropZone
32852             */
32853              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32854                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32855            });
32856         }
32857         if((this.enableDD || this.enableDrag) && !this.dragZone){
32858            /**
32859             * The dragZone used by this tree if drag is enabled
32860             * @type Roo.tree.TreeDragZone
32861             */
32862             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32863                ddGroup: this.ddGroup || "TreeDD",
32864                scroll: this.ddScroll
32865            });
32866         }
32867         this.getSelectionModel().init(this);
32868         if (!this.root) {
32869             Roo.log("ROOT not set in tree");
32870             return this;
32871         }
32872         this.root.render();
32873         if(!this.rootVisible){
32874             this.root.renderChildren();
32875         }
32876         return this;
32877     }
32878 });/*
32879  * Based on:
32880  * Ext JS Library 1.1.1
32881  * Copyright(c) 2006-2007, Ext JS, LLC.
32882  *
32883  * Originally Released Under LGPL - original licence link has changed is not relivant.
32884  *
32885  * Fork - LGPL
32886  * <script type="text/javascript">
32887  */
32888  
32889
32890 /**
32891  * @class Roo.tree.DefaultSelectionModel
32892  * @extends Roo.util.Observable
32893  * The default single selection for a TreePanel.
32894  * @param {Object} cfg Configuration
32895  */
32896 Roo.tree.DefaultSelectionModel = function(cfg){
32897    this.selNode = null;
32898    
32899    
32900    
32901    this.addEvents({
32902        /**
32903         * @event selectionchange
32904         * Fires when the selected node changes
32905         * @param {DefaultSelectionModel} this
32906         * @param {TreeNode} node the new selection
32907         */
32908        "selectionchange" : true,
32909
32910        /**
32911         * @event beforeselect
32912         * Fires before the selected node changes, return false to cancel the change
32913         * @param {DefaultSelectionModel} this
32914         * @param {TreeNode} node the new selection
32915         * @param {TreeNode} node the old selection
32916         */
32917        "beforeselect" : true
32918    });
32919    
32920     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32921 };
32922
32923 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32924     init : function(tree){
32925         this.tree = tree;
32926         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32927         tree.on("click", this.onNodeClick, this);
32928     },
32929     
32930     onNodeClick : function(node, e){
32931         if (e.ctrlKey && this.selNode == node)  {
32932             this.unselect(node);
32933             return;
32934         }
32935         this.select(node);
32936     },
32937     
32938     /**
32939      * Select a node.
32940      * @param {TreeNode} node The node to select
32941      * @return {TreeNode} The selected node
32942      */
32943     select : function(node){
32944         var last = this.selNode;
32945         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32946             if(last){
32947                 last.ui.onSelectedChange(false);
32948             }
32949             this.selNode = node;
32950             node.ui.onSelectedChange(true);
32951             this.fireEvent("selectionchange", this, node, last);
32952         }
32953         return node;
32954     },
32955     
32956     /**
32957      * Deselect a node.
32958      * @param {TreeNode} node The node to unselect
32959      */
32960     unselect : function(node){
32961         if(this.selNode == node){
32962             this.clearSelections();
32963         }    
32964     },
32965     
32966     /**
32967      * Clear all selections
32968      */
32969     clearSelections : function(){
32970         var n = this.selNode;
32971         if(n){
32972             n.ui.onSelectedChange(false);
32973             this.selNode = null;
32974             this.fireEvent("selectionchange", this, null);
32975         }
32976         return n;
32977     },
32978     
32979     /**
32980      * Get the selected node
32981      * @return {TreeNode} The selected node
32982      */
32983     getSelectedNode : function(){
32984         return this.selNode;    
32985     },
32986     
32987     /**
32988      * Returns true if the node is selected
32989      * @param {TreeNode} node The node to check
32990      * @return {Boolean}
32991      */
32992     isSelected : function(node){
32993         return this.selNode == node;  
32994     },
32995
32996     /**
32997      * Selects the node above the selected node in the tree, intelligently walking the nodes
32998      * @return TreeNode The new selection
32999      */
33000     selectPrevious : function(){
33001         var s = this.selNode || this.lastSelNode;
33002         if(!s){
33003             return null;
33004         }
33005         var ps = s.previousSibling;
33006         if(ps){
33007             if(!ps.isExpanded() || ps.childNodes.length < 1){
33008                 return this.select(ps);
33009             } else{
33010                 var lc = ps.lastChild;
33011                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
33012                     lc = lc.lastChild;
33013                 }
33014                 return this.select(lc);
33015             }
33016         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
33017             return this.select(s.parentNode);
33018         }
33019         return null;
33020     },
33021
33022     /**
33023      * Selects the node above the selected node in the tree, intelligently walking the nodes
33024      * @return TreeNode The new selection
33025      */
33026     selectNext : function(){
33027         var s = this.selNode || this.lastSelNode;
33028         if(!s){
33029             return null;
33030         }
33031         if(s.firstChild && s.isExpanded()){
33032              return this.select(s.firstChild);
33033          }else if(s.nextSibling){
33034              return this.select(s.nextSibling);
33035          }else if(s.parentNode){
33036             var newS = null;
33037             s.parentNode.bubble(function(){
33038                 if(this.nextSibling){
33039                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
33040                     return false;
33041                 }
33042             });
33043             return newS;
33044          }
33045         return null;
33046     },
33047
33048     onKeyDown : function(e){
33049         var s = this.selNode || this.lastSelNode;
33050         // undesirable, but required
33051         var sm = this;
33052         if(!s){
33053             return;
33054         }
33055         var k = e.getKey();
33056         switch(k){
33057              case e.DOWN:
33058                  e.stopEvent();
33059                  this.selectNext();
33060              break;
33061              case e.UP:
33062                  e.stopEvent();
33063                  this.selectPrevious();
33064              break;
33065              case e.RIGHT:
33066                  e.preventDefault();
33067                  if(s.hasChildNodes()){
33068                      if(!s.isExpanded()){
33069                          s.expand();
33070                      }else if(s.firstChild){
33071                          this.select(s.firstChild, e);
33072                      }
33073                  }
33074              break;
33075              case e.LEFT:
33076                  e.preventDefault();
33077                  if(s.hasChildNodes() && s.isExpanded()){
33078                      s.collapse();
33079                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33080                      this.select(s.parentNode, e);
33081                  }
33082              break;
33083         };
33084     }
33085 });
33086
33087 /**
33088  * @class Roo.tree.MultiSelectionModel
33089  * @extends Roo.util.Observable
33090  * Multi selection for a TreePanel.
33091  * @param {Object} cfg Configuration
33092  */
33093 Roo.tree.MultiSelectionModel = function(){
33094    this.selNodes = [];
33095    this.selMap = {};
33096    this.addEvents({
33097        /**
33098         * @event selectionchange
33099         * Fires when the selected nodes change
33100         * @param {MultiSelectionModel} this
33101         * @param {Array} nodes Array of the selected nodes
33102         */
33103        "selectionchange" : true
33104    });
33105    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33106    
33107 };
33108
33109 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33110     init : function(tree){
33111         this.tree = tree;
33112         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33113         tree.on("click", this.onNodeClick, this);
33114     },
33115     
33116     onNodeClick : function(node, e){
33117         this.select(node, e, e.ctrlKey);
33118     },
33119     
33120     /**
33121      * Select a node.
33122      * @param {TreeNode} node The node to select
33123      * @param {EventObject} e (optional) An event associated with the selection
33124      * @param {Boolean} keepExisting True to retain existing selections
33125      * @return {TreeNode} The selected node
33126      */
33127     select : function(node, e, keepExisting){
33128         if(keepExisting !== true){
33129             this.clearSelections(true);
33130         }
33131         if(this.isSelected(node)){
33132             this.lastSelNode = node;
33133             return node;
33134         }
33135         this.selNodes.push(node);
33136         this.selMap[node.id] = node;
33137         this.lastSelNode = node;
33138         node.ui.onSelectedChange(true);
33139         this.fireEvent("selectionchange", this, this.selNodes);
33140         return node;
33141     },
33142     
33143     /**
33144      * Deselect a node.
33145      * @param {TreeNode} node The node to unselect
33146      */
33147     unselect : function(node){
33148         if(this.selMap[node.id]){
33149             node.ui.onSelectedChange(false);
33150             var sn = this.selNodes;
33151             var index = -1;
33152             if(sn.indexOf){
33153                 index = sn.indexOf(node);
33154             }else{
33155                 for(var i = 0, len = sn.length; i < len; i++){
33156                     if(sn[i] == node){
33157                         index = i;
33158                         break;
33159                     }
33160                 }
33161             }
33162             if(index != -1){
33163                 this.selNodes.splice(index, 1);
33164             }
33165             delete this.selMap[node.id];
33166             this.fireEvent("selectionchange", this, this.selNodes);
33167         }
33168     },
33169     
33170     /**
33171      * Clear all selections
33172      */
33173     clearSelections : function(suppressEvent){
33174         var sn = this.selNodes;
33175         if(sn.length > 0){
33176             for(var i = 0, len = sn.length; i < len; i++){
33177                 sn[i].ui.onSelectedChange(false);
33178             }
33179             this.selNodes = [];
33180             this.selMap = {};
33181             if(suppressEvent !== true){
33182                 this.fireEvent("selectionchange", this, this.selNodes);
33183             }
33184         }
33185     },
33186     
33187     /**
33188      * Returns true if the node is selected
33189      * @param {TreeNode} node The node to check
33190      * @return {Boolean}
33191      */
33192     isSelected : function(node){
33193         return this.selMap[node.id] ? true : false;  
33194     },
33195     
33196     /**
33197      * Returns an array of the selected nodes
33198      * @return {Array}
33199      */
33200     getSelectedNodes : function(){
33201         return this.selNodes;    
33202     },
33203
33204     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33205
33206     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33207
33208     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33209 });/*
33210  * Based on:
33211  * Ext JS Library 1.1.1
33212  * Copyright(c) 2006-2007, Ext JS, LLC.
33213  *
33214  * Originally Released Under LGPL - original licence link has changed is not relivant.
33215  *
33216  * Fork - LGPL
33217  * <script type="text/javascript">
33218  */
33219  
33220 /**
33221  * @class Roo.tree.TreeNode
33222  * @extends Roo.data.Node
33223  * @cfg {String} text The text for this node
33224  * @cfg {Boolean} expanded true to start the node expanded
33225  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33226  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33227  * @cfg {Boolean} disabled true to start the node disabled
33228  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33229  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33230  * @cfg {String} cls A css class to be added to the node
33231  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33232  * @cfg {String} href URL of the link used for the node (defaults to #)
33233  * @cfg {String} hrefTarget target frame for the link
33234  * @cfg {String} qtip An Ext QuickTip for the node
33235  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33236  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33237  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33238  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33239  * (defaults to undefined with no checkbox rendered)
33240  * @constructor
33241  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33242  */
33243 Roo.tree.TreeNode = function(attributes){
33244     attributes = attributes || {};
33245     if(typeof attributes == "string"){
33246         attributes = {text: attributes};
33247     }
33248     this.childrenRendered = false;
33249     this.rendered = false;
33250     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33251     this.expanded = attributes.expanded === true;
33252     this.isTarget = attributes.isTarget !== false;
33253     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33254     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33255
33256     /**
33257      * Read-only. The text for this node. To change it use setText().
33258      * @type String
33259      */
33260     this.text = attributes.text;
33261     /**
33262      * True if this node is disabled.
33263      * @type Boolean
33264      */
33265     this.disabled = attributes.disabled === true;
33266
33267     this.addEvents({
33268         /**
33269         * @event textchange
33270         * Fires when the text for this node is changed
33271         * @param {Node} this This node
33272         * @param {String} text The new text
33273         * @param {String} oldText The old text
33274         */
33275         "textchange" : true,
33276         /**
33277         * @event beforeexpand
33278         * Fires before this node is expanded, return false to cancel.
33279         * @param {Node} this This node
33280         * @param {Boolean} deep
33281         * @param {Boolean} anim
33282         */
33283         "beforeexpand" : true,
33284         /**
33285         * @event beforecollapse
33286         * Fires before this node is collapsed, return false to cancel.
33287         * @param {Node} this This node
33288         * @param {Boolean} deep
33289         * @param {Boolean} anim
33290         */
33291         "beforecollapse" : true,
33292         /**
33293         * @event expand
33294         * Fires when this node is expanded
33295         * @param {Node} this This node
33296         */
33297         "expand" : true,
33298         /**
33299         * @event disabledchange
33300         * Fires when the disabled status of this node changes
33301         * @param {Node} this This node
33302         * @param {Boolean} disabled
33303         */
33304         "disabledchange" : true,
33305         /**
33306         * @event collapse
33307         * Fires when this node is collapsed
33308         * @param {Node} this This node
33309         */
33310         "collapse" : true,
33311         /**
33312         * @event beforeclick
33313         * Fires before click processing. Return false to cancel the default action.
33314         * @param {Node} this This node
33315         * @param {Roo.EventObject} e The event object
33316         */
33317         "beforeclick":true,
33318         /**
33319         * @event checkchange
33320         * Fires when a node with a checkbox's checked property changes
33321         * @param {Node} this This node
33322         * @param {Boolean} checked
33323         */
33324         "checkchange":true,
33325         /**
33326         * @event click
33327         * Fires when this node is clicked
33328         * @param {Node} this This node
33329         * @param {Roo.EventObject} e The event object
33330         */
33331         "click":true,
33332         /**
33333         * @event dblclick
33334         * Fires when this node is double clicked
33335         * @param {Node} this This node
33336         * @param {Roo.EventObject} e The event object
33337         */
33338         "dblclick":true,
33339         /**
33340         * @event contextmenu
33341         * Fires when this node is right clicked
33342         * @param {Node} this This node
33343         * @param {Roo.EventObject} e The event object
33344         */
33345         "contextmenu":true,
33346         /**
33347         * @event beforechildrenrendered
33348         * Fires right before the child nodes for this node are rendered
33349         * @param {Node} this This node
33350         */
33351         "beforechildrenrendered":true
33352     });
33353
33354     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33355
33356     /**
33357      * Read-only. The UI for this node
33358      * @type TreeNodeUI
33359      */
33360     this.ui = new uiClass(this);
33361     
33362     // finally support items[]
33363     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33364         return;
33365     }
33366     
33367     
33368     Roo.each(this.attributes.items, function(c) {
33369         this.appendChild(Roo.factory(c,Roo.Tree));
33370     }, this);
33371     delete this.attributes.items;
33372     
33373     
33374     
33375 };
33376 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33377     preventHScroll: true,
33378     /**
33379      * Returns true if this node is expanded
33380      * @return {Boolean}
33381      */
33382     isExpanded : function(){
33383         return this.expanded;
33384     },
33385
33386     /**
33387      * Returns the UI object for this node
33388      * @return {TreeNodeUI}
33389      */
33390     getUI : function(){
33391         return this.ui;
33392     },
33393
33394     // private override
33395     setFirstChild : function(node){
33396         var of = this.firstChild;
33397         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33398         if(this.childrenRendered && of && node != of){
33399             of.renderIndent(true, true);
33400         }
33401         if(this.rendered){
33402             this.renderIndent(true, true);
33403         }
33404     },
33405
33406     // private override
33407     setLastChild : function(node){
33408         var ol = this.lastChild;
33409         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33410         if(this.childrenRendered && ol && node != ol){
33411             ol.renderIndent(true, true);
33412         }
33413         if(this.rendered){
33414             this.renderIndent(true, true);
33415         }
33416     },
33417
33418     // these methods are overridden to provide lazy rendering support
33419     // private override
33420     appendChild : function()
33421     {
33422         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33423         if(node && this.childrenRendered){
33424             node.render();
33425         }
33426         this.ui.updateExpandIcon();
33427         return node;
33428     },
33429
33430     // private override
33431     removeChild : function(node){
33432         this.ownerTree.getSelectionModel().unselect(node);
33433         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33434         // if it's been rendered remove dom node
33435         if(this.childrenRendered){
33436             node.ui.remove();
33437         }
33438         if(this.childNodes.length < 1){
33439             this.collapse(false, false);
33440         }else{
33441             this.ui.updateExpandIcon();
33442         }
33443         if(!this.firstChild) {
33444             this.childrenRendered = false;
33445         }
33446         return node;
33447     },
33448
33449     // private override
33450     insertBefore : function(node, refNode){
33451         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33452         if(newNode && refNode && this.childrenRendered){
33453             node.render();
33454         }
33455         this.ui.updateExpandIcon();
33456         return newNode;
33457     },
33458
33459     /**
33460      * Sets the text for this node
33461      * @param {String} text
33462      */
33463     setText : function(text){
33464         var oldText = this.text;
33465         this.text = text;
33466         this.attributes.text = text;
33467         if(this.rendered){ // event without subscribing
33468             this.ui.onTextChange(this, text, oldText);
33469         }
33470         this.fireEvent("textchange", this, text, oldText);
33471     },
33472
33473     /**
33474      * Triggers selection of this node
33475      */
33476     select : function(){
33477         this.getOwnerTree().getSelectionModel().select(this);
33478     },
33479
33480     /**
33481      * Triggers deselection of this node
33482      */
33483     unselect : function(){
33484         this.getOwnerTree().getSelectionModel().unselect(this);
33485     },
33486
33487     /**
33488      * Returns true if this node is selected
33489      * @return {Boolean}
33490      */
33491     isSelected : function(){
33492         return this.getOwnerTree().getSelectionModel().isSelected(this);
33493     },
33494
33495     /**
33496      * Expand this node.
33497      * @param {Boolean} deep (optional) True to expand all children as well
33498      * @param {Boolean} anim (optional) false to cancel the default animation
33499      * @param {Function} callback (optional) A callback to be called when
33500      * expanding this node completes (does not wait for deep expand to complete).
33501      * Called with 1 parameter, this node.
33502      */
33503     expand : function(deep, anim, callback){
33504         if(!this.expanded){
33505             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33506                 return;
33507             }
33508             if(!this.childrenRendered){
33509                 this.renderChildren();
33510             }
33511             this.expanded = true;
33512             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33513                 this.ui.animExpand(function(){
33514                     this.fireEvent("expand", this);
33515                     if(typeof callback == "function"){
33516                         callback(this);
33517                     }
33518                     if(deep === true){
33519                         this.expandChildNodes(true);
33520                     }
33521                 }.createDelegate(this));
33522                 return;
33523             }else{
33524                 this.ui.expand();
33525                 this.fireEvent("expand", this);
33526                 if(typeof callback == "function"){
33527                     callback(this);
33528                 }
33529             }
33530         }else{
33531            if(typeof callback == "function"){
33532                callback(this);
33533            }
33534         }
33535         if(deep === true){
33536             this.expandChildNodes(true);
33537         }
33538     },
33539
33540     isHiddenRoot : function(){
33541         return this.isRoot && !this.getOwnerTree().rootVisible;
33542     },
33543
33544     /**
33545      * Collapse this node.
33546      * @param {Boolean} deep (optional) True to collapse all children as well
33547      * @param {Boolean} anim (optional) false to cancel the default animation
33548      */
33549     collapse : function(deep, anim){
33550         if(this.expanded && !this.isHiddenRoot()){
33551             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33552                 return;
33553             }
33554             this.expanded = false;
33555             if((this.getOwnerTree().animate && anim !== false) || anim){
33556                 this.ui.animCollapse(function(){
33557                     this.fireEvent("collapse", this);
33558                     if(deep === true){
33559                         this.collapseChildNodes(true);
33560                     }
33561                 }.createDelegate(this));
33562                 return;
33563             }else{
33564                 this.ui.collapse();
33565                 this.fireEvent("collapse", this);
33566             }
33567         }
33568         if(deep === true){
33569             var cs = this.childNodes;
33570             for(var i = 0, len = cs.length; i < len; i++) {
33571                 cs[i].collapse(true, false);
33572             }
33573         }
33574     },
33575
33576     // private
33577     delayedExpand : function(delay){
33578         if(!this.expandProcId){
33579             this.expandProcId = this.expand.defer(delay, this);
33580         }
33581     },
33582
33583     // private
33584     cancelExpand : function(){
33585         if(this.expandProcId){
33586             clearTimeout(this.expandProcId);
33587         }
33588         this.expandProcId = false;
33589     },
33590
33591     /**
33592      * Toggles expanded/collapsed state of the node
33593      */
33594     toggle : function(){
33595         if(this.expanded){
33596             this.collapse();
33597         }else{
33598             this.expand();
33599         }
33600     },
33601
33602     /**
33603      * Ensures all parent nodes are expanded
33604      */
33605     ensureVisible : function(callback){
33606         var tree = this.getOwnerTree();
33607         tree.expandPath(this.parentNode.getPath(), false, function(){
33608             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33609             Roo.callback(callback);
33610         }.createDelegate(this));
33611     },
33612
33613     /**
33614      * Expand all child nodes
33615      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33616      */
33617     expandChildNodes : function(deep){
33618         var cs = this.childNodes;
33619         for(var i = 0, len = cs.length; i < len; i++) {
33620                 cs[i].expand(deep);
33621         }
33622     },
33623
33624     /**
33625      * Collapse all child nodes
33626      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33627      */
33628     collapseChildNodes : function(deep){
33629         var cs = this.childNodes;
33630         for(var i = 0, len = cs.length; i < len; i++) {
33631                 cs[i].collapse(deep);
33632         }
33633     },
33634
33635     /**
33636      * Disables this node
33637      */
33638     disable : function(){
33639         this.disabled = true;
33640         this.unselect();
33641         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33642             this.ui.onDisableChange(this, true);
33643         }
33644         this.fireEvent("disabledchange", this, true);
33645     },
33646
33647     /**
33648      * Enables this node
33649      */
33650     enable : function(){
33651         this.disabled = false;
33652         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33653             this.ui.onDisableChange(this, false);
33654         }
33655         this.fireEvent("disabledchange", this, false);
33656     },
33657
33658     // private
33659     renderChildren : function(suppressEvent){
33660         if(suppressEvent !== false){
33661             this.fireEvent("beforechildrenrendered", this);
33662         }
33663         var cs = this.childNodes;
33664         for(var i = 0, len = cs.length; i < len; i++){
33665             cs[i].render(true);
33666         }
33667         this.childrenRendered = true;
33668     },
33669
33670     // private
33671     sort : function(fn, scope){
33672         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33673         if(this.childrenRendered){
33674             var cs = this.childNodes;
33675             for(var i = 0, len = cs.length; i < len; i++){
33676                 cs[i].render(true);
33677             }
33678         }
33679     },
33680
33681     // private
33682     render : function(bulkRender){
33683         this.ui.render(bulkRender);
33684         if(!this.rendered){
33685             this.rendered = true;
33686             if(this.expanded){
33687                 this.expanded = false;
33688                 this.expand(false, false);
33689             }
33690         }
33691     },
33692
33693     // private
33694     renderIndent : function(deep, refresh){
33695         if(refresh){
33696             this.ui.childIndent = null;
33697         }
33698         this.ui.renderIndent();
33699         if(deep === true && this.childrenRendered){
33700             var cs = this.childNodes;
33701             for(var i = 0, len = cs.length; i < len; i++){
33702                 cs[i].renderIndent(true, refresh);
33703             }
33704         }
33705     }
33706 });/*
33707  * Based on:
33708  * Ext JS Library 1.1.1
33709  * Copyright(c) 2006-2007, Ext JS, LLC.
33710  *
33711  * Originally Released Under LGPL - original licence link has changed is not relivant.
33712  *
33713  * Fork - LGPL
33714  * <script type="text/javascript">
33715  */
33716  
33717 /**
33718  * @class Roo.tree.AsyncTreeNode
33719  * @extends Roo.tree.TreeNode
33720  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33721  * @constructor
33722  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33723  */
33724  Roo.tree.AsyncTreeNode = function(config){
33725     this.loaded = false;
33726     this.loading = false;
33727     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33728     /**
33729     * @event beforeload
33730     * Fires before this node is loaded, return false to cancel
33731     * @param {Node} this This node
33732     */
33733     this.addEvents({'beforeload':true, 'load': true});
33734     /**
33735     * @event load
33736     * Fires when this node is loaded
33737     * @param {Node} this This node
33738     */
33739     /**
33740      * The loader used by this node (defaults to using the tree's defined loader)
33741      * @type TreeLoader
33742      * @property loader
33743      */
33744 };
33745 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33746     expand : function(deep, anim, callback){
33747         if(this.loading){ // if an async load is already running, waiting til it's done
33748             var timer;
33749             var f = function(){
33750                 if(!this.loading){ // done loading
33751                     clearInterval(timer);
33752                     this.expand(deep, anim, callback);
33753                 }
33754             }.createDelegate(this);
33755             timer = setInterval(f, 200);
33756             return;
33757         }
33758         if(!this.loaded){
33759             if(this.fireEvent("beforeload", this) === false){
33760                 return;
33761             }
33762             this.loading = true;
33763             this.ui.beforeLoad(this);
33764             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33765             if(loader){
33766                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33767                 return;
33768             }
33769         }
33770         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33771     },
33772     
33773     /**
33774      * Returns true if this node is currently loading
33775      * @return {Boolean}
33776      */
33777     isLoading : function(){
33778         return this.loading;  
33779     },
33780     
33781     loadComplete : function(deep, anim, callback){
33782         this.loading = false;
33783         this.loaded = true;
33784         this.ui.afterLoad(this);
33785         this.fireEvent("load", this);
33786         this.expand(deep, anim, callback);
33787     },
33788     
33789     /**
33790      * Returns true if this node has been loaded
33791      * @return {Boolean}
33792      */
33793     isLoaded : function(){
33794         return this.loaded;
33795     },
33796     
33797     hasChildNodes : function(){
33798         if(!this.isLeaf() && !this.loaded){
33799             return true;
33800         }else{
33801             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33802         }
33803     },
33804
33805     /**
33806      * Trigger a reload for this node
33807      * @param {Function} callback
33808      */
33809     reload : function(callback){
33810         this.collapse(false, false);
33811         while(this.firstChild){
33812             this.removeChild(this.firstChild);
33813         }
33814         this.childrenRendered = false;
33815         this.loaded = false;
33816         if(this.isHiddenRoot()){
33817             this.expanded = false;
33818         }
33819         this.expand(false, false, callback);
33820     }
33821 });/*
33822  * Based on:
33823  * Ext JS Library 1.1.1
33824  * Copyright(c) 2006-2007, Ext JS, LLC.
33825  *
33826  * Originally Released Under LGPL - original licence link has changed is not relivant.
33827  *
33828  * Fork - LGPL
33829  * <script type="text/javascript">
33830  */
33831  
33832 /**
33833  * @class Roo.tree.TreeNodeUI
33834  * @constructor
33835  * @param {Object} node The node to render
33836  * The TreeNode UI implementation is separate from the
33837  * tree implementation. Unless you are customizing the tree UI,
33838  * you should never have to use this directly.
33839  */
33840 Roo.tree.TreeNodeUI = function(node){
33841     this.node = node;
33842     this.rendered = false;
33843     this.animating = false;
33844     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33845 };
33846
33847 Roo.tree.TreeNodeUI.prototype = {
33848     removeChild : function(node){
33849         if(this.rendered){
33850             this.ctNode.removeChild(node.ui.getEl());
33851         }
33852     },
33853
33854     beforeLoad : function(){
33855          this.addClass("x-tree-node-loading");
33856     },
33857
33858     afterLoad : function(){
33859          this.removeClass("x-tree-node-loading");
33860     },
33861
33862     onTextChange : function(node, text, oldText){
33863         if(this.rendered){
33864             this.textNode.innerHTML = text;
33865         }
33866     },
33867
33868     onDisableChange : function(node, state){
33869         this.disabled = state;
33870         if(state){
33871             this.addClass("x-tree-node-disabled");
33872         }else{
33873             this.removeClass("x-tree-node-disabled");
33874         }
33875     },
33876
33877     onSelectedChange : function(state){
33878         if(state){
33879             this.focus();
33880             this.addClass("x-tree-selected");
33881         }else{
33882             //this.blur();
33883             this.removeClass("x-tree-selected");
33884         }
33885     },
33886
33887     onMove : function(tree, node, oldParent, newParent, index, refNode){
33888         this.childIndent = null;
33889         if(this.rendered){
33890             var targetNode = newParent.ui.getContainer();
33891             if(!targetNode){//target not rendered
33892                 this.holder = document.createElement("div");
33893                 this.holder.appendChild(this.wrap);
33894                 return;
33895             }
33896             var insertBefore = refNode ? refNode.ui.getEl() : null;
33897             if(insertBefore){
33898                 targetNode.insertBefore(this.wrap, insertBefore);
33899             }else{
33900                 targetNode.appendChild(this.wrap);
33901             }
33902             this.node.renderIndent(true);
33903         }
33904     },
33905
33906     addClass : function(cls){
33907         if(this.elNode){
33908             Roo.fly(this.elNode).addClass(cls);
33909         }
33910     },
33911
33912     removeClass : function(cls){
33913         if(this.elNode){
33914             Roo.fly(this.elNode).removeClass(cls);
33915         }
33916     },
33917
33918     remove : function(){
33919         if(this.rendered){
33920             this.holder = document.createElement("div");
33921             this.holder.appendChild(this.wrap);
33922         }
33923     },
33924
33925     fireEvent : function(){
33926         return this.node.fireEvent.apply(this.node, arguments);
33927     },
33928
33929     initEvents : function(){
33930         this.node.on("move", this.onMove, this);
33931         var E = Roo.EventManager;
33932         var a = this.anchor;
33933
33934         var el = Roo.fly(a, '_treeui');
33935
33936         if(Roo.isOpera){ // opera render bug ignores the CSS
33937             el.setStyle("text-decoration", "none");
33938         }
33939
33940         el.on("click", this.onClick, this);
33941         el.on("dblclick", this.onDblClick, this);
33942
33943         if(this.checkbox){
33944             Roo.EventManager.on(this.checkbox,
33945                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33946         }
33947
33948         el.on("contextmenu", this.onContextMenu, this);
33949
33950         var icon = Roo.fly(this.iconNode);
33951         icon.on("click", this.onClick, this);
33952         icon.on("dblclick", this.onDblClick, this);
33953         icon.on("contextmenu", this.onContextMenu, this);
33954         E.on(this.ecNode, "click", this.ecClick, this, true);
33955
33956         if(this.node.disabled){
33957             this.addClass("x-tree-node-disabled");
33958         }
33959         if(this.node.hidden){
33960             this.addClass("x-tree-node-disabled");
33961         }
33962         var ot = this.node.getOwnerTree();
33963         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33964         if(dd && (!this.node.isRoot || ot.rootVisible)){
33965             Roo.dd.Registry.register(this.elNode, {
33966                 node: this.node,
33967                 handles: this.getDDHandles(),
33968                 isHandle: false
33969             });
33970         }
33971     },
33972
33973     getDDHandles : function(){
33974         return [this.iconNode, this.textNode];
33975     },
33976
33977     hide : function(){
33978         if(this.rendered){
33979             this.wrap.style.display = "none";
33980         }
33981     },
33982
33983     show : function(){
33984         if(this.rendered){
33985             this.wrap.style.display = "";
33986         }
33987     },
33988
33989     onContextMenu : function(e){
33990         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33991             e.preventDefault();
33992             this.focus();
33993             this.fireEvent("contextmenu", this.node, e);
33994         }
33995     },
33996
33997     onClick : function(e){
33998         if(this.dropping){
33999             e.stopEvent();
34000             return;
34001         }
34002         if(this.fireEvent("beforeclick", this.node, e) !== false){
34003             if(!this.disabled && this.node.attributes.href){
34004                 this.fireEvent("click", this.node, e);
34005                 return;
34006             }
34007             e.preventDefault();
34008             if(this.disabled){
34009                 return;
34010             }
34011
34012             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
34013                 this.node.toggle();
34014             }
34015
34016             this.fireEvent("click", this.node, e);
34017         }else{
34018             e.stopEvent();
34019         }
34020     },
34021
34022     onDblClick : function(e){
34023         e.preventDefault();
34024         if(this.disabled){
34025             return;
34026         }
34027         if(this.checkbox){
34028             this.toggleCheck();
34029         }
34030         if(!this.animating && this.node.hasChildNodes()){
34031             this.node.toggle();
34032         }
34033         this.fireEvent("dblclick", this.node, e);
34034     },
34035
34036     onCheckChange : function(){
34037         var checked = this.checkbox.checked;
34038         this.node.attributes.checked = checked;
34039         this.fireEvent('checkchange', this.node, checked);
34040     },
34041
34042     ecClick : function(e){
34043         if(!this.animating && this.node.hasChildNodes()){
34044             this.node.toggle();
34045         }
34046     },
34047
34048     startDrop : function(){
34049         this.dropping = true;
34050     },
34051
34052     // delayed drop so the click event doesn't get fired on a drop
34053     endDrop : function(){
34054        setTimeout(function(){
34055            this.dropping = false;
34056        }.createDelegate(this), 50);
34057     },
34058
34059     expand : function(){
34060         this.updateExpandIcon();
34061         this.ctNode.style.display = "";
34062     },
34063
34064     focus : function(){
34065         if(!this.node.preventHScroll){
34066             try{this.anchor.focus();
34067             }catch(e){}
34068         }else if(!Roo.isIE){
34069             try{
34070                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34071                 var l = noscroll.scrollLeft;
34072                 this.anchor.focus();
34073                 noscroll.scrollLeft = l;
34074             }catch(e){}
34075         }
34076     },
34077
34078     toggleCheck : function(value){
34079         var cb = this.checkbox;
34080         if(cb){
34081             cb.checked = (value === undefined ? !cb.checked : value);
34082         }
34083     },
34084
34085     blur : function(){
34086         try{
34087             this.anchor.blur();
34088         }catch(e){}
34089     },
34090
34091     animExpand : function(callback){
34092         var ct = Roo.get(this.ctNode);
34093         ct.stopFx();
34094         if(!this.node.hasChildNodes()){
34095             this.updateExpandIcon();
34096             this.ctNode.style.display = "";
34097             Roo.callback(callback);
34098             return;
34099         }
34100         this.animating = true;
34101         this.updateExpandIcon();
34102
34103         ct.slideIn('t', {
34104            callback : function(){
34105                this.animating = false;
34106                Roo.callback(callback);
34107             },
34108             scope: this,
34109             duration: this.node.ownerTree.duration || .25
34110         });
34111     },
34112
34113     highlight : function(){
34114         var tree = this.node.getOwnerTree();
34115         Roo.fly(this.wrap).highlight(
34116             tree.hlColor || "C3DAF9",
34117             {endColor: tree.hlBaseColor}
34118         );
34119     },
34120
34121     collapse : function(){
34122         this.updateExpandIcon();
34123         this.ctNode.style.display = "none";
34124     },
34125
34126     animCollapse : function(callback){
34127         var ct = Roo.get(this.ctNode);
34128         ct.enableDisplayMode('block');
34129         ct.stopFx();
34130
34131         this.animating = true;
34132         this.updateExpandIcon();
34133
34134         ct.slideOut('t', {
34135             callback : function(){
34136                this.animating = false;
34137                Roo.callback(callback);
34138             },
34139             scope: this,
34140             duration: this.node.ownerTree.duration || .25
34141         });
34142     },
34143
34144     getContainer : function(){
34145         return this.ctNode;
34146     },
34147
34148     getEl : function(){
34149         return this.wrap;
34150     },
34151
34152     appendDDGhost : function(ghostNode){
34153         ghostNode.appendChild(this.elNode.cloneNode(true));
34154     },
34155
34156     getDDRepairXY : function(){
34157         return Roo.lib.Dom.getXY(this.iconNode);
34158     },
34159
34160     onRender : function(){
34161         this.render();
34162     },
34163
34164     render : function(bulkRender){
34165         var n = this.node, a = n.attributes;
34166         var targetNode = n.parentNode ?
34167               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34168
34169         if(!this.rendered){
34170             this.rendered = true;
34171
34172             this.renderElements(n, a, targetNode, bulkRender);
34173
34174             if(a.qtip){
34175                if(this.textNode.setAttributeNS){
34176                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34177                    if(a.qtipTitle){
34178                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34179                    }
34180                }else{
34181                    this.textNode.setAttribute("ext:qtip", a.qtip);
34182                    if(a.qtipTitle){
34183                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34184                    }
34185                }
34186             }else if(a.qtipCfg){
34187                 a.qtipCfg.target = Roo.id(this.textNode);
34188                 Roo.QuickTips.register(a.qtipCfg);
34189             }
34190             this.initEvents();
34191             if(!this.node.expanded){
34192                 this.updateExpandIcon();
34193             }
34194         }else{
34195             if(bulkRender === true) {
34196                 targetNode.appendChild(this.wrap);
34197             }
34198         }
34199     },
34200
34201     renderElements : function(n, a, targetNode, bulkRender)
34202     {
34203         // add some indent caching, this helps performance when rendering a large tree
34204         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34205         var t = n.getOwnerTree();
34206         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34207         if (typeof(n.attributes.html) != 'undefined') {
34208             txt = n.attributes.html;
34209         }
34210         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34211         var cb = typeof a.checked == 'boolean';
34212         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34213         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34214             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34215             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34216             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34217             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34218             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34219              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34220                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34221             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34222             "</li>"];
34223
34224         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34225             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34226                                 n.nextSibling.ui.getEl(), buf.join(""));
34227         }else{
34228             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34229         }
34230
34231         this.elNode = this.wrap.childNodes[0];
34232         this.ctNode = this.wrap.childNodes[1];
34233         var cs = this.elNode.childNodes;
34234         this.indentNode = cs[0];
34235         this.ecNode = cs[1];
34236         this.iconNode = cs[2];
34237         var index = 3;
34238         if(cb){
34239             this.checkbox = cs[3];
34240             index++;
34241         }
34242         this.anchor = cs[index];
34243         this.textNode = cs[index].firstChild;
34244     },
34245
34246     getAnchor : function(){
34247         return this.anchor;
34248     },
34249
34250     getTextEl : function(){
34251         return this.textNode;
34252     },
34253
34254     getIconEl : function(){
34255         return this.iconNode;
34256     },
34257
34258     isChecked : function(){
34259         return this.checkbox ? this.checkbox.checked : false;
34260     },
34261
34262     updateExpandIcon : function(){
34263         if(this.rendered){
34264             var n = this.node, c1, c2;
34265             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34266             var hasChild = n.hasChildNodes();
34267             if(hasChild){
34268                 if(n.expanded){
34269                     cls += "-minus";
34270                     c1 = "x-tree-node-collapsed";
34271                     c2 = "x-tree-node-expanded";
34272                 }else{
34273                     cls += "-plus";
34274                     c1 = "x-tree-node-expanded";
34275                     c2 = "x-tree-node-collapsed";
34276                 }
34277                 if(this.wasLeaf){
34278                     this.removeClass("x-tree-node-leaf");
34279                     this.wasLeaf = false;
34280                 }
34281                 if(this.c1 != c1 || this.c2 != c2){
34282                     Roo.fly(this.elNode).replaceClass(c1, c2);
34283                     this.c1 = c1; this.c2 = c2;
34284                 }
34285             }else{
34286                 // this changes non-leafs into leafs if they have no children.
34287                 // it's not very rational behaviour..
34288                 
34289                 if(!this.wasLeaf && this.node.leaf){
34290                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34291                     delete this.c1;
34292                     delete this.c2;
34293                     this.wasLeaf = true;
34294                 }
34295             }
34296             var ecc = "x-tree-ec-icon "+cls;
34297             if(this.ecc != ecc){
34298                 this.ecNode.className = ecc;
34299                 this.ecc = ecc;
34300             }
34301         }
34302     },
34303
34304     getChildIndent : function(){
34305         if(!this.childIndent){
34306             var buf = [];
34307             var p = this.node;
34308             while(p){
34309                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34310                     if(!p.isLast()) {
34311                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34312                     } else {
34313                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34314                     }
34315                 }
34316                 p = p.parentNode;
34317             }
34318             this.childIndent = buf.join("");
34319         }
34320         return this.childIndent;
34321     },
34322
34323     renderIndent : function(){
34324         if(this.rendered){
34325             var indent = "";
34326             var p = this.node.parentNode;
34327             if(p){
34328                 indent = p.ui.getChildIndent();
34329             }
34330             if(this.indentMarkup != indent){ // don't rerender if not required
34331                 this.indentNode.innerHTML = indent;
34332                 this.indentMarkup = indent;
34333             }
34334             this.updateExpandIcon();
34335         }
34336     }
34337 };
34338
34339 Roo.tree.RootTreeNodeUI = function(){
34340     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34341 };
34342 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34343     render : function(){
34344         if(!this.rendered){
34345             var targetNode = this.node.ownerTree.innerCt.dom;
34346             this.node.expanded = true;
34347             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34348             this.wrap = this.ctNode = targetNode.firstChild;
34349         }
34350     },
34351     collapse : function(){
34352     },
34353     expand : function(){
34354     }
34355 });/*
34356  * Based on:
34357  * Ext JS Library 1.1.1
34358  * Copyright(c) 2006-2007, Ext JS, LLC.
34359  *
34360  * Originally Released Under LGPL - original licence link has changed is not relivant.
34361  *
34362  * Fork - LGPL
34363  * <script type="text/javascript">
34364  */
34365 /**
34366  * @class Roo.tree.TreeLoader
34367  * @extends Roo.util.Observable
34368  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34369  * nodes from a specified URL. The response must be a javascript Array definition
34370  * who's elements are node definition objects. eg:
34371  * <pre><code>
34372 {  success : true,
34373    data :      [
34374    
34375     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34376     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34377     ]
34378 }
34379
34380
34381 </code></pre>
34382  * <br><br>
34383  * The old style respose with just an array is still supported, but not recommended.
34384  * <br><br>
34385  *
34386  * A server request is sent, and child nodes are loaded only when a node is expanded.
34387  * The loading node's id is passed to the server under the parameter name "node" to
34388  * enable the server to produce the correct child nodes.
34389  * <br><br>
34390  * To pass extra parameters, an event handler may be attached to the "beforeload"
34391  * event, and the parameters specified in the TreeLoader's baseParams property:
34392  * <pre><code>
34393     myTreeLoader.on("beforeload", function(treeLoader, node) {
34394         this.baseParams.category = node.attributes.category;
34395     }, this);
34396 </code></pre><
34397  * This would pass an HTTP parameter called "category" to the server containing
34398  * the value of the Node's "category" attribute.
34399  * @constructor
34400  * Creates a new Treeloader.
34401  * @param {Object} config A config object containing config properties.
34402  */
34403 Roo.tree.TreeLoader = function(config){
34404     this.baseParams = {};
34405     this.requestMethod = "POST";
34406     Roo.apply(this, config);
34407
34408     this.addEvents({
34409     
34410         /**
34411          * @event beforeload
34412          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34413          * @param {Object} This TreeLoader object.
34414          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34415          * @param {Object} callback The callback function specified in the {@link #load} call.
34416          */
34417         beforeload : true,
34418         /**
34419          * @event load
34420          * Fires when the node has been successfuly loaded.
34421          * @param {Object} This TreeLoader object.
34422          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34423          * @param {Object} response The response object containing the data from the server.
34424          */
34425         load : true,
34426         /**
34427          * @event loadexception
34428          * Fires if the network request failed.
34429          * @param {Object} This TreeLoader object.
34430          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34431          * @param {Object} response The response object containing the data from the server.
34432          */
34433         loadexception : true,
34434         /**
34435          * @event create
34436          * Fires before a node is created, enabling you to return custom Node types 
34437          * @param {Object} This TreeLoader object.
34438          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34439          */
34440         create : true
34441     });
34442
34443     Roo.tree.TreeLoader.superclass.constructor.call(this);
34444 };
34445
34446 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34447     /**
34448     * @cfg {String} dataUrl The URL from which to request a Json string which
34449     * specifies an array of node definition object representing the child nodes
34450     * to be loaded.
34451     */
34452     /**
34453     * @cfg {String} requestMethod either GET or POST
34454     * defaults to POST (due to BC)
34455     * to be loaded.
34456     */
34457     /**
34458     * @cfg {Object} baseParams (optional) An object containing properties which
34459     * specify HTTP parameters to be passed to each request for child nodes.
34460     */
34461     /**
34462     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34463     * created by this loader. If the attributes sent by the server have an attribute in this object,
34464     * they take priority.
34465     */
34466     /**
34467     * @cfg {Object} uiProviders (optional) An object containing properties which
34468     * 
34469     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34470     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34471     * <i>uiProvider</i> attribute of a returned child node is a string rather
34472     * than a reference to a TreeNodeUI implementation, this that string value
34473     * is used as a property name in the uiProviders object. You can define the provider named
34474     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34475     */
34476     uiProviders : {},
34477
34478     /**
34479     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34480     * child nodes before loading.
34481     */
34482     clearOnLoad : true,
34483
34484     /**
34485     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34486     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34487     * Grid query { data : [ .....] }
34488     */
34489     
34490     root : false,
34491      /**
34492     * @cfg {String} queryParam (optional) 
34493     * Name of the query as it will be passed on the querystring (defaults to 'node')
34494     * eg. the request will be ?node=[id]
34495     */
34496     
34497     
34498     queryParam: false,
34499     
34500     /**
34501      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34502      * This is called automatically when a node is expanded, but may be used to reload
34503      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34504      * @param {Roo.tree.TreeNode} node
34505      * @param {Function} callback
34506      */
34507     load : function(node, callback){
34508         if(this.clearOnLoad){
34509             while(node.firstChild){
34510                 node.removeChild(node.firstChild);
34511             }
34512         }
34513         if(node.attributes.children){ // preloaded json children
34514             var cs = node.attributes.children;
34515             for(var i = 0, len = cs.length; i < len; i++){
34516                 node.appendChild(this.createNode(cs[i]));
34517             }
34518             if(typeof callback == "function"){
34519                 callback();
34520             }
34521         }else if(this.dataUrl){
34522             this.requestData(node, callback);
34523         }
34524     },
34525
34526     getParams: function(node){
34527         var buf = [], bp = this.baseParams;
34528         for(var key in bp){
34529             if(typeof bp[key] != "function"){
34530                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34531             }
34532         }
34533         var n = this.queryParam === false ? 'node' : this.queryParam;
34534         buf.push(n + "=", encodeURIComponent(node.id));
34535         return buf.join("");
34536     },
34537
34538     requestData : function(node, callback){
34539         if(this.fireEvent("beforeload", this, node, callback) !== false){
34540             this.transId = Roo.Ajax.request({
34541                 method:this.requestMethod,
34542                 url: this.dataUrl||this.url,
34543                 success: this.handleResponse,
34544                 failure: this.handleFailure,
34545                 scope: this,
34546                 argument: {callback: callback, node: node},
34547                 params: this.getParams(node)
34548             });
34549         }else{
34550             // if the load is cancelled, make sure we notify
34551             // the node that we are done
34552             if(typeof callback == "function"){
34553                 callback();
34554             }
34555         }
34556     },
34557
34558     isLoading : function(){
34559         return this.transId ? true : false;
34560     },
34561
34562     abort : function(){
34563         if(this.isLoading()){
34564             Roo.Ajax.abort(this.transId);
34565         }
34566     },
34567
34568     // private
34569     createNode : function(attr)
34570     {
34571         // apply baseAttrs, nice idea Corey!
34572         if(this.baseAttrs){
34573             Roo.applyIf(attr, this.baseAttrs);
34574         }
34575         if(this.applyLoader !== false){
34576             attr.loader = this;
34577         }
34578         // uiProvider = depreciated..
34579         
34580         if(typeof(attr.uiProvider) == 'string'){
34581            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34582                 /**  eval:var:attr */ eval(attr.uiProvider);
34583         }
34584         if(typeof(this.uiProviders['default']) != 'undefined') {
34585             attr.uiProvider = this.uiProviders['default'];
34586         }
34587         
34588         this.fireEvent('create', this, attr);
34589         
34590         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34591         return(attr.leaf ?
34592                         new Roo.tree.TreeNode(attr) :
34593                         new Roo.tree.AsyncTreeNode(attr));
34594     },
34595
34596     processResponse : function(response, node, callback)
34597     {
34598         var json = response.responseText;
34599         try {
34600             
34601             var o = Roo.decode(json);
34602             
34603             if (this.root === false && typeof(o.success) != undefined) {
34604                 this.root = 'data'; // the default behaviour for list like data..
34605                 }
34606                 
34607             if (this.root !== false &&  !o.success) {
34608                 // it's a failure condition.
34609                 var a = response.argument;
34610                 this.fireEvent("loadexception", this, a.node, response);
34611                 Roo.log("Load failed - should have a handler really");
34612                 return;
34613             }
34614             
34615             
34616             
34617             if (this.root !== false) {
34618                  o = o[this.root];
34619             }
34620             
34621             for(var i = 0, len = o.length; i < len; i++){
34622                 var n = this.createNode(o[i]);
34623                 if(n){
34624                     node.appendChild(n);
34625                 }
34626             }
34627             if(typeof callback == "function"){
34628                 callback(this, node);
34629             }
34630         }catch(e){
34631             this.handleFailure(response);
34632         }
34633     },
34634
34635     handleResponse : function(response){
34636         this.transId = false;
34637         var a = response.argument;
34638         this.processResponse(response, a.node, a.callback);
34639         this.fireEvent("load", this, a.node, response);
34640     },
34641
34642     handleFailure : function(response)
34643     {
34644         // should handle failure better..
34645         this.transId = false;
34646         var a = response.argument;
34647         this.fireEvent("loadexception", this, a.node, response);
34648         if(typeof a.callback == "function"){
34649             a.callback(this, a.node);
34650         }
34651     }
34652 });/*
34653  * Based on:
34654  * Ext JS Library 1.1.1
34655  * Copyright(c) 2006-2007, Ext JS, LLC.
34656  *
34657  * Originally Released Under LGPL - original licence link has changed is not relivant.
34658  *
34659  * Fork - LGPL
34660  * <script type="text/javascript">
34661  */
34662
34663 /**
34664 * @class Roo.tree.TreeFilter
34665 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34666 * @param {TreePanel} tree
34667 * @param {Object} config (optional)
34668  */
34669 Roo.tree.TreeFilter = function(tree, config){
34670     this.tree = tree;
34671     this.filtered = {};
34672     Roo.apply(this, config);
34673 };
34674
34675 Roo.tree.TreeFilter.prototype = {
34676     clearBlank:false,
34677     reverse:false,
34678     autoClear:false,
34679     remove:false,
34680
34681      /**
34682      * Filter the data by a specific attribute.
34683      * @param {String/RegExp} value Either string that the attribute value
34684      * should start with or a RegExp to test against the attribute
34685      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34686      * @param {TreeNode} startNode (optional) The node to start the filter at.
34687      */
34688     filter : function(value, attr, startNode){
34689         attr = attr || "text";
34690         var f;
34691         if(typeof value == "string"){
34692             var vlen = value.length;
34693             // auto clear empty filter
34694             if(vlen == 0 && this.clearBlank){
34695                 this.clear();
34696                 return;
34697             }
34698             value = value.toLowerCase();
34699             f = function(n){
34700                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34701             };
34702         }else if(value.exec){ // regex?
34703             f = function(n){
34704                 return value.test(n.attributes[attr]);
34705             };
34706         }else{
34707             throw 'Illegal filter type, must be string or regex';
34708         }
34709         this.filterBy(f, null, startNode);
34710         },
34711
34712     /**
34713      * Filter by a function. The passed function will be called with each
34714      * node in the tree (or from the startNode). If the function returns true, the node is kept
34715      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34716      * @param {Function} fn The filter function
34717      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34718      */
34719     filterBy : function(fn, scope, startNode){
34720         startNode = startNode || this.tree.root;
34721         if(this.autoClear){
34722             this.clear();
34723         }
34724         var af = this.filtered, rv = this.reverse;
34725         var f = function(n){
34726             if(n == startNode){
34727                 return true;
34728             }
34729             if(af[n.id]){
34730                 return false;
34731             }
34732             var m = fn.call(scope || n, n);
34733             if(!m || rv){
34734                 af[n.id] = n;
34735                 n.ui.hide();
34736                 return false;
34737             }
34738             return true;
34739         };
34740         startNode.cascade(f);
34741         if(this.remove){
34742            for(var id in af){
34743                if(typeof id != "function"){
34744                    var n = af[id];
34745                    if(n && n.parentNode){
34746                        n.parentNode.removeChild(n);
34747                    }
34748                }
34749            }
34750         }
34751     },
34752
34753     /**
34754      * Clears the current filter. Note: with the "remove" option
34755      * set a filter cannot be cleared.
34756      */
34757     clear : function(){
34758         var t = this.tree;
34759         var af = this.filtered;
34760         for(var id in af){
34761             if(typeof id != "function"){
34762                 var n = af[id];
34763                 if(n){
34764                     n.ui.show();
34765                 }
34766             }
34767         }
34768         this.filtered = {};
34769     }
34770 };
34771 /*
34772  * Based on:
34773  * Ext JS Library 1.1.1
34774  * Copyright(c) 2006-2007, Ext JS, LLC.
34775  *
34776  * Originally Released Under LGPL - original licence link has changed is not relivant.
34777  *
34778  * Fork - LGPL
34779  * <script type="text/javascript">
34780  */
34781  
34782
34783 /**
34784  * @class Roo.tree.TreeSorter
34785  * Provides sorting of nodes in a TreePanel
34786  * 
34787  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34788  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34789  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34790  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34791  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34792  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34793  * @constructor
34794  * @param {TreePanel} tree
34795  * @param {Object} config
34796  */
34797 Roo.tree.TreeSorter = function(tree, config){
34798     Roo.apply(this, config);
34799     tree.on("beforechildrenrendered", this.doSort, this);
34800     tree.on("append", this.updateSort, this);
34801     tree.on("insert", this.updateSort, this);
34802     
34803     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34804     var p = this.property || "text";
34805     var sortType = this.sortType;
34806     var fs = this.folderSort;
34807     var cs = this.caseSensitive === true;
34808     var leafAttr = this.leafAttr || 'leaf';
34809
34810     this.sortFn = function(n1, n2){
34811         if(fs){
34812             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34813                 return 1;
34814             }
34815             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34816                 return -1;
34817             }
34818         }
34819         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34820         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34821         if(v1 < v2){
34822                         return dsc ? +1 : -1;
34823                 }else if(v1 > v2){
34824                         return dsc ? -1 : +1;
34825         }else{
34826                 return 0;
34827         }
34828     };
34829 };
34830
34831 Roo.tree.TreeSorter.prototype = {
34832     doSort : function(node){
34833         node.sort(this.sortFn);
34834     },
34835     
34836     compareNodes : function(n1, n2){
34837         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34838     },
34839     
34840     updateSort : function(tree, node){
34841         if(node.childrenRendered){
34842             this.doSort.defer(1, this, [node]);
34843         }
34844     }
34845 };/*
34846  * Based on:
34847  * Ext JS Library 1.1.1
34848  * Copyright(c) 2006-2007, Ext JS, LLC.
34849  *
34850  * Originally Released Under LGPL - original licence link has changed is not relivant.
34851  *
34852  * Fork - LGPL
34853  * <script type="text/javascript">
34854  */
34855
34856 if(Roo.dd.DropZone){
34857     
34858 Roo.tree.TreeDropZone = function(tree, config){
34859     this.allowParentInsert = false;
34860     this.allowContainerDrop = false;
34861     this.appendOnly = false;
34862     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34863     this.tree = tree;
34864     this.lastInsertClass = "x-tree-no-status";
34865     this.dragOverData = {};
34866 };
34867
34868 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34869     ddGroup : "TreeDD",
34870     scroll:  true,
34871     
34872     expandDelay : 1000,
34873     
34874     expandNode : function(node){
34875         if(node.hasChildNodes() && !node.isExpanded()){
34876             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34877         }
34878     },
34879     
34880     queueExpand : function(node){
34881         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34882     },
34883     
34884     cancelExpand : function(){
34885         if(this.expandProcId){
34886             clearTimeout(this.expandProcId);
34887             this.expandProcId = false;
34888         }
34889     },
34890     
34891     isValidDropPoint : function(n, pt, dd, e, data){
34892         if(!n || !data){ return false; }
34893         var targetNode = n.node;
34894         var dropNode = data.node;
34895         // default drop rules
34896         if(!(targetNode && targetNode.isTarget && pt)){
34897             return false;
34898         }
34899         if(pt == "append" && targetNode.allowChildren === false){
34900             return false;
34901         }
34902         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34903             return false;
34904         }
34905         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34906             return false;
34907         }
34908         // reuse the object
34909         var overEvent = this.dragOverData;
34910         overEvent.tree = this.tree;
34911         overEvent.target = targetNode;
34912         overEvent.data = data;
34913         overEvent.point = pt;
34914         overEvent.source = dd;
34915         overEvent.rawEvent = e;
34916         overEvent.dropNode = dropNode;
34917         overEvent.cancel = false;  
34918         var result = this.tree.fireEvent("nodedragover", overEvent);
34919         return overEvent.cancel === false && result !== false;
34920     },
34921     
34922     getDropPoint : function(e, n, dd)
34923     {
34924         var tn = n.node;
34925         if(tn.isRoot){
34926             return tn.allowChildren !== false ? "append" : false; // always append for root
34927         }
34928         var dragEl = n.ddel;
34929         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34930         var y = Roo.lib.Event.getPageY(e);
34931         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34932         
34933         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34934         var noAppend = tn.allowChildren === false;
34935         if(this.appendOnly || tn.parentNode.allowChildren === false){
34936             return noAppend ? false : "append";
34937         }
34938         var noBelow = false;
34939         if(!this.allowParentInsert){
34940             noBelow = tn.hasChildNodes() && tn.isExpanded();
34941         }
34942         var q = (b - t) / (noAppend ? 2 : 3);
34943         if(y >= t && y < (t + q)){
34944             return "above";
34945         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34946             return "below";
34947         }else{
34948             return "append";
34949         }
34950     },
34951     
34952     onNodeEnter : function(n, dd, e, data)
34953     {
34954         this.cancelExpand();
34955     },
34956     
34957     onNodeOver : function(n, dd, e, data)
34958     {
34959        
34960         var pt = this.getDropPoint(e, n, dd);
34961         var node = n.node;
34962         
34963         // auto node expand check
34964         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34965             this.queueExpand(node);
34966         }else if(pt != "append"){
34967             this.cancelExpand();
34968         }
34969         
34970         // set the insert point style on the target node
34971         var returnCls = this.dropNotAllowed;
34972         if(this.isValidDropPoint(n, pt, dd, e, data)){
34973            if(pt){
34974                var el = n.ddel;
34975                var cls;
34976                if(pt == "above"){
34977                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34978                    cls = "x-tree-drag-insert-above";
34979                }else if(pt == "below"){
34980                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34981                    cls = "x-tree-drag-insert-below";
34982                }else{
34983                    returnCls = "x-tree-drop-ok-append";
34984                    cls = "x-tree-drag-append";
34985                }
34986                if(this.lastInsertClass != cls){
34987                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34988                    this.lastInsertClass = cls;
34989                }
34990            }
34991        }
34992        return returnCls;
34993     },
34994     
34995     onNodeOut : function(n, dd, e, data){
34996         
34997         this.cancelExpand();
34998         this.removeDropIndicators(n);
34999     },
35000     
35001     onNodeDrop : function(n, dd, e, data){
35002         var point = this.getDropPoint(e, n, dd);
35003         var targetNode = n.node;
35004         targetNode.ui.startDrop();
35005         if(!this.isValidDropPoint(n, point, dd, e, data)){
35006             targetNode.ui.endDrop();
35007             return false;
35008         }
35009         // first try to find the drop node
35010         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
35011         var dropEvent = {
35012             tree : this.tree,
35013             target: targetNode,
35014             data: data,
35015             point: point,
35016             source: dd,
35017             rawEvent: e,
35018             dropNode: dropNode,
35019             cancel: !dropNode   
35020         };
35021         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
35022         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
35023             targetNode.ui.endDrop();
35024             return false;
35025         }
35026         // allow target changing
35027         targetNode = dropEvent.target;
35028         if(point == "append" && !targetNode.isExpanded()){
35029             targetNode.expand(false, null, function(){
35030                 this.completeDrop(dropEvent);
35031             }.createDelegate(this));
35032         }else{
35033             this.completeDrop(dropEvent);
35034         }
35035         return true;
35036     },
35037     
35038     completeDrop : function(de){
35039         var ns = de.dropNode, p = de.point, t = de.target;
35040         if(!(ns instanceof Array)){
35041             ns = [ns];
35042         }
35043         var n;
35044         for(var i = 0, len = ns.length; i < len; i++){
35045             n = ns[i];
35046             if(p == "above"){
35047                 t.parentNode.insertBefore(n, t);
35048             }else if(p == "below"){
35049                 t.parentNode.insertBefore(n, t.nextSibling);
35050             }else{
35051                 t.appendChild(n);
35052             }
35053         }
35054         n.ui.focus();
35055         if(this.tree.hlDrop){
35056             n.ui.highlight();
35057         }
35058         t.ui.endDrop();
35059         this.tree.fireEvent("nodedrop", de);
35060     },
35061     
35062     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35063         if(this.tree.hlDrop){
35064             dropNode.ui.focus();
35065             dropNode.ui.highlight();
35066         }
35067         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35068     },
35069     
35070     getTree : function(){
35071         return this.tree;
35072     },
35073     
35074     removeDropIndicators : function(n){
35075         if(n && n.ddel){
35076             var el = n.ddel;
35077             Roo.fly(el).removeClass([
35078                     "x-tree-drag-insert-above",
35079                     "x-tree-drag-insert-below",
35080                     "x-tree-drag-append"]);
35081             this.lastInsertClass = "_noclass";
35082         }
35083     },
35084     
35085     beforeDragDrop : function(target, e, id){
35086         this.cancelExpand();
35087         return true;
35088     },
35089     
35090     afterRepair : function(data){
35091         if(data && Roo.enableFx){
35092             data.node.ui.highlight();
35093         }
35094         this.hideProxy();
35095     } 
35096     
35097 });
35098
35099 }
35100 /*
35101  * Based on:
35102  * Ext JS Library 1.1.1
35103  * Copyright(c) 2006-2007, Ext JS, LLC.
35104  *
35105  * Originally Released Under LGPL - original licence link has changed is not relivant.
35106  *
35107  * Fork - LGPL
35108  * <script type="text/javascript">
35109  */
35110  
35111
35112 if(Roo.dd.DragZone){
35113 Roo.tree.TreeDragZone = function(tree, config){
35114     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35115     this.tree = tree;
35116 };
35117
35118 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35119     ddGroup : "TreeDD",
35120    
35121     onBeforeDrag : function(data, e){
35122         var n = data.node;
35123         return n && n.draggable && !n.disabled;
35124     },
35125      
35126     
35127     onInitDrag : function(e){
35128         var data = this.dragData;
35129         this.tree.getSelectionModel().select(data.node);
35130         this.proxy.update("");
35131         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35132         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35133     },
35134     
35135     getRepairXY : function(e, data){
35136         return data.node.ui.getDDRepairXY();
35137     },
35138     
35139     onEndDrag : function(data, e){
35140         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35141         
35142         
35143     },
35144     
35145     onValidDrop : function(dd, e, id){
35146         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35147         this.hideProxy();
35148     },
35149     
35150     beforeInvalidDrop : function(e, id){
35151         // this scrolls the original position back into view
35152         var sm = this.tree.getSelectionModel();
35153         sm.clearSelections();
35154         sm.select(this.dragData.node);
35155     }
35156 });
35157 }/*
35158  * Based on:
35159  * Ext JS Library 1.1.1
35160  * Copyright(c) 2006-2007, Ext JS, LLC.
35161  *
35162  * Originally Released Under LGPL - original licence link has changed is not relivant.
35163  *
35164  * Fork - LGPL
35165  * <script type="text/javascript">
35166  */
35167 /**
35168  * @class Roo.tree.TreeEditor
35169  * @extends Roo.Editor
35170  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35171  * as the editor field.
35172  * @constructor
35173  * @param {Object} config (used to be the tree panel.)
35174  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35175  * 
35176  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35177  * @cfg {Roo.form.TextField|Object} field The field configuration
35178  *
35179  * 
35180  */
35181 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35182     var tree = config;
35183     var field;
35184     if (oldconfig) { // old style..
35185         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35186     } else {
35187         // new style..
35188         tree = config.tree;
35189         config.field = config.field  || {};
35190         config.field.xtype = 'TextField';
35191         field = Roo.factory(config.field, Roo.form);
35192     }
35193     config = config || {};
35194     
35195     
35196     this.addEvents({
35197         /**
35198          * @event beforenodeedit
35199          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35200          * false from the handler of this event.
35201          * @param {Editor} this
35202          * @param {Roo.tree.Node} node 
35203          */
35204         "beforenodeedit" : true
35205     });
35206     
35207     //Roo.log(config);
35208     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35209
35210     this.tree = tree;
35211
35212     tree.on('beforeclick', this.beforeNodeClick, this);
35213     tree.getTreeEl().on('mousedown', this.hide, this);
35214     this.on('complete', this.updateNode, this);
35215     this.on('beforestartedit', this.fitToTree, this);
35216     this.on('startedit', this.bindScroll, this, {delay:10});
35217     this.on('specialkey', this.onSpecialKey, this);
35218 };
35219
35220 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35221     /**
35222      * @cfg {String} alignment
35223      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35224      */
35225     alignment: "l-l",
35226     // inherit
35227     autoSize: false,
35228     /**
35229      * @cfg {Boolean} hideEl
35230      * True to hide the bound element while the editor is displayed (defaults to false)
35231      */
35232     hideEl : false,
35233     /**
35234      * @cfg {String} cls
35235      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35236      */
35237     cls: "x-small-editor x-tree-editor",
35238     /**
35239      * @cfg {Boolean} shim
35240      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35241      */
35242     shim:false,
35243     // inherit
35244     shadow:"frame",
35245     /**
35246      * @cfg {Number} maxWidth
35247      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35248      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35249      * scroll and client offsets into account prior to each edit.
35250      */
35251     maxWidth: 250,
35252
35253     editDelay : 350,
35254
35255     // private
35256     fitToTree : function(ed, el){
35257         var td = this.tree.getTreeEl().dom, nd = el.dom;
35258         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35259             td.scrollLeft = nd.offsetLeft;
35260         }
35261         var w = Math.min(
35262                 this.maxWidth,
35263                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35264         this.setSize(w, '');
35265         
35266         return this.fireEvent('beforenodeedit', this, this.editNode);
35267         
35268     },
35269
35270     // private
35271     triggerEdit : function(node){
35272         this.completeEdit();
35273         this.editNode = node;
35274         this.startEdit(node.ui.textNode, node.text);
35275     },
35276
35277     // private
35278     bindScroll : function(){
35279         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35280     },
35281
35282     // private
35283     beforeNodeClick : function(node, e){
35284         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35285         this.lastClick = new Date();
35286         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35287             e.stopEvent();
35288             this.triggerEdit(node);
35289             return false;
35290         }
35291         return true;
35292     },
35293
35294     // private
35295     updateNode : function(ed, value){
35296         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35297         this.editNode.setText(value);
35298     },
35299
35300     // private
35301     onHide : function(){
35302         Roo.tree.TreeEditor.superclass.onHide.call(this);
35303         if(this.editNode){
35304             this.editNode.ui.focus();
35305         }
35306     },
35307
35308     // private
35309     onSpecialKey : function(field, e){
35310         var k = e.getKey();
35311         if(k == e.ESC){
35312             e.stopEvent();
35313             this.cancelEdit();
35314         }else if(k == e.ENTER && !e.hasModifier()){
35315             e.stopEvent();
35316             this.completeEdit();
35317         }
35318     }
35319 });//<Script type="text/javascript">
35320 /*
35321  * Based on:
35322  * Ext JS Library 1.1.1
35323  * Copyright(c) 2006-2007, Ext JS, LLC.
35324  *
35325  * Originally Released Under LGPL - original licence link has changed is not relivant.
35326  *
35327  * Fork - LGPL
35328  * <script type="text/javascript">
35329  */
35330  
35331 /**
35332  * Not documented??? - probably should be...
35333  */
35334
35335 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35336     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35337     
35338     renderElements : function(n, a, targetNode, bulkRender){
35339         //consel.log("renderElements?");
35340         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35341
35342         var t = n.getOwnerTree();
35343         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35344         
35345         var cols = t.columns;
35346         var bw = t.borderWidth;
35347         var c = cols[0];
35348         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35349          var cb = typeof a.checked == "boolean";
35350         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35351         var colcls = 'x-t-' + tid + '-c0';
35352         var buf = [
35353             '<li class="x-tree-node">',
35354             
35355                 
35356                 '<div class="x-tree-node-el ', a.cls,'">',
35357                     // extran...
35358                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35359                 
35360                 
35361                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35362                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35363                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35364                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35365                            (a.iconCls ? ' '+a.iconCls : ''),
35366                            '" unselectable="on" />',
35367                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35368                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35369                              
35370                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35371                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35372                             '<span unselectable="on" qtip="' + tx + '">',
35373                              tx,
35374                              '</span></a>' ,
35375                     '</div>',
35376                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35377                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35378                  ];
35379         for(var i = 1, len = cols.length; i < len; i++){
35380             c = cols[i];
35381             colcls = 'x-t-' + tid + '-c' +i;
35382             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35383             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35384                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35385                       "</div>");
35386          }
35387          
35388          buf.push(
35389             '</a>',
35390             '<div class="x-clear"></div></div>',
35391             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35392             "</li>");
35393         
35394         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35395             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35396                                 n.nextSibling.ui.getEl(), buf.join(""));
35397         }else{
35398             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35399         }
35400         var el = this.wrap.firstChild;
35401         this.elRow = el;
35402         this.elNode = el.firstChild;
35403         this.ranchor = el.childNodes[1];
35404         this.ctNode = this.wrap.childNodes[1];
35405         var cs = el.firstChild.childNodes;
35406         this.indentNode = cs[0];
35407         this.ecNode = cs[1];
35408         this.iconNode = cs[2];
35409         var index = 3;
35410         if(cb){
35411             this.checkbox = cs[3];
35412             index++;
35413         }
35414         this.anchor = cs[index];
35415         
35416         this.textNode = cs[index].firstChild;
35417         
35418         //el.on("click", this.onClick, this);
35419         //el.on("dblclick", this.onDblClick, this);
35420         
35421         
35422        // console.log(this);
35423     },
35424     initEvents : function(){
35425         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35426         
35427             
35428         var a = this.ranchor;
35429
35430         var el = Roo.get(a);
35431
35432         if(Roo.isOpera){ // opera render bug ignores the CSS
35433             el.setStyle("text-decoration", "none");
35434         }
35435
35436         el.on("click", this.onClick, this);
35437         el.on("dblclick", this.onDblClick, this);
35438         el.on("contextmenu", this.onContextMenu, this);
35439         
35440     },
35441     
35442     /*onSelectedChange : function(state){
35443         if(state){
35444             this.focus();
35445             this.addClass("x-tree-selected");
35446         }else{
35447             //this.blur();
35448             this.removeClass("x-tree-selected");
35449         }
35450     },*/
35451     addClass : function(cls){
35452         if(this.elRow){
35453             Roo.fly(this.elRow).addClass(cls);
35454         }
35455         
35456     },
35457     
35458     
35459     removeClass : function(cls){
35460         if(this.elRow){
35461             Roo.fly(this.elRow).removeClass(cls);
35462         }
35463     }
35464
35465     
35466     
35467 });//<Script type="text/javascript">
35468
35469 /*
35470  * Based on:
35471  * Ext JS Library 1.1.1
35472  * Copyright(c) 2006-2007, Ext JS, LLC.
35473  *
35474  * Originally Released Under LGPL - original licence link has changed is not relivant.
35475  *
35476  * Fork - LGPL
35477  * <script type="text/javascript">
35478  */
35479  
35480
35481 /**
35482  * @class Roo.tree.ColumnTree
35483  * @extends Roo.data.TreePanel
35484  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35485  * @cfg {int} borderWidth  compined right/left border allowance
35486  * @constructor
35487  * @param {String/HTMLElement/Element} el The container element
35488  * @param {Object} config
35489  */
35490 Roo.tree.ColumnTree =  function(el, config)
35491 {
35492    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35493    this.addEvents({
35494         /**
35495         * @event resize
35496         * Fire this event on a container when it resizes
35497         * @param {int} w Width
35498         * @param {int} h Height
35499         */
35500        "resize" : true
35501     });
35502     this.on('resize', this.onResize, this);
35503 };
35504
35505 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35506     //lines:false,
35507     
35508     
35509     borderWidth: Roo.isBorderBox ? 0 : 2, 
35510     headEls : false,
35511     
35512     render : function(){
35513         // add the header.....
35514        
35515         Roo.tree.ColumnTree.superclass.render.apply(this);
35516         
35517         this.el.addClass('x-column-tree');
35518         
35519         this.headers = this.el.createChild(
35520             {cls:'x-tree-headers'},this.innerCt.dom);
35521    
35522         var cols = this.columns, c;
35523         var totalWidth = 0;
35524         this.headEls = [];
35525         var  len = cols.length;
35526         for(var i = 0; i < len; i++){
35527              c = cols[i];
35528              totalWidth += c.width;
35529             this.headEls.push(this.headers.createChild({
35530                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35531                  cn: {
35532                      cls:'x-tree-hd-text',
35533                      html: c.header
35534                  },
35535                  style:'width:'+(c.width-this.borderWidth)+'px;'
35536              }));
35537         }
35538         this.headers.createChild({cls:'x-clear'});
35539         // prevent floats from wrapping when clipped
35540         this.headers.setWidth(totalWidth);
35541         //this.innerCt.setWidth(totalWidth);
35542         this.innerCt.setStyle({ overflow: 'auto' });
35543         this.onResize(this.width, this.height);
35544              
35545         
35546     },
35547     onResize : function(w,h)
35548     {
35549         this.height = h;
35550         this.width = w;
35551         // resize cols..
35552         this.innerCt.setWidth(this.width);
35553         this.innerCt.setHeight(this.height-20);
35554         
35555         // headers...
35556         var cols = this.columns, c;
35557         var totalWidth = 0;
35558         var expEl = false;
35559         var len = cols.length;
35560         for(var i = 0; i < len; i++){
35561             c = cols[i];
35562             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35563                 // it's the expander..
35564                 expEl  = this.headEls[i];
35565                 continue;
35566             }
35567             totalWidth += c.width;
35568             
35569         }
35570         if (expEl) {
35571             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35572         }
35573         this.headers.setWidth(w-20);
35574
35575         
35576         
35577         
35578     }
35579 });
35580 /*
35581  * Based on:
35582  * Ext JS Library 1.1.1
35583  * Copyright(c) 2006-2007, Ext JS, LLC.
35584  *
35585  * Originally Released Under LGPL - original licence link has changed is not relivant.
35586  *
35587  * Fork - LGPL
35588  * <script type="text/javascript">
35589  */
35590  
35591 /**
35592  * @class Roo.menu.Menu
35593  * @extends Roo.util.Observable
35594  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35595  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35596  * @constructor
35597  * Creates a new Menu
35598  * @param {Object} config Configuration options
35599  */
35600 Roo.menu.Menu = function(config){
35601     Roo.apply(this, config);
35602     this.id = this.id || Roo.id();
35603     this.addEvents({
35604         /**
35605          * @event beforeshow
35606          * Fires before this menu is displayed
35607          * @param {Roo.menu.Menu} this
35608          */
35609         beforeshow : true,
35610         /**
35611          * @event beforehide
35612          * Fires before this menu is hidden
35613          * @param {Roo.menu.Menu} this
35614          */
35615         beforehide : true,
35616         /**
35617          * @event show
35618          * Fires after this menu is displayed
35619          * @param {Roo.menu.Menu} this
35620          */
35621         show : true,
35622         /**
35623          * @event hide
35624          * Fires after this menu is hidden
35625          * @param {Roo.menu.Menu} this
35626          */
35627         hide : true,
35628         /**
35629          * @event click
35630          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35631          * @param {Roo.menu.Menu} this
35632          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35633          * @param {Roo.EventObject} e
35634          */
35635         click : true,
35636         /**
35637          * @event mouseover
35638          * Fires when the mouse is hovering over this menu
35639          * @param {Roo.menu.Menu} this
35640          * @param {Roo.EventObject} e
35641          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35642          */
35643         mouseover : true,
35644         /**
35645          * @event mouseout
35646          * Fires when the mouse exits this menu
35647          * @param {Roo.menu.Menu} this
35648          * @param {Roo.EventObject} e
35649          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35650          */
35651         mouseout : true,
35652         /**
35653          * @event itemclick
35654          * Fires when a menu item contained in this menu is clicked
35655          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35656          * @param {Roo.EventObject} e
35657          */
35658         itemclick: true
35659     });
35660     if (this.registerMenu) {
35661         Roo.menu.MenuMgr.register(this);
35662     }
35663     
35664     var mis = this.items;
35665     this.items = new Roo.util.MixedCollection();
35666     if(mis){
35667         this.add.apply(this, mis);
35668     }
35669 };
35670
35671 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35672     /**
35673      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35674      */
35675     minWidth : 120,
35676     /**
35677      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35678      * for bottom-right shadow (defaults to "sides")
35679      */
35680     shadow : "sides",
35681     /**
35682      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35683      * this menu (defaults to "tl-tr?")
35684      */
35685     subMenuAlign : "tl-tr?",
35686     /**
35687      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35688      * relative to its element of origin (defaults to "tl-bl?")
35689      */
35690     defaultAlign : "tl-bl?",
35691     /**
35692      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35693      */
35694     allowOtherMenus : false,
35695     /**
35696      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35697      */
35698     registerMenu : true,
35699
35700     hidden:true,
35701
35702     // private
35703     render : function(){
35704         if(this.el){
35705             return;
35706         }
35707         var el = this.el = new Roo.Layer({
35708             cls: "x-menu",
35709             shadow:this.shadow,
35710             constrain: false,
35711             parentEl: this.parentEl || document.body,
35712             zindex:15000
35713         });
35714
35715         this.keyNav = new Roo.menu.MenuNav(this);
35716
35717         if(this.plain){
35718             el.addClass("x-menu-plain");
35719         }
35720         if(this.cls){
35721             el.addClass(this.cls);
35722         }
35723         // generic focus element
35724         this.focusEl = el.createChild({
35725             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35726         });
35727         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35728         //disabling touch- as it's causing issues ..
35729         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35730         ul.on('click'   , this.onClick, this);
35731         
35732         
35733         ul.on("mouseover", this.onMouseOver, this);
35734         ul.on("mouseout", this.onMouseOut, this);
35735         this.items.each(function(item){
35736             if (item.hidden) {
35737                 return;
35738             }
35739             
35740             var li = document.createElement("li");
35741             li.className = "x-menu-list-item";
35742             ul.dom.appendChild(li);
35743             item.render(li, this);
35744         }, this);
35745         this.ul = ul;
35746         this.autoWidth();
35747     },
35748
35749     // private
35750     autoWidth : function(){
35751         var el = this.el, ul = this.ul;
35752         if(!el){
35753             return;
35754         }
35755         var w = this.width;
35756         if(w){
35757             el.setWidth(w);
35758         }else if(Roo.isIE){
35759             el.setWidth(this.minWidth);
35760             var t = el.dom.offsetWidth; // force recalc
35761             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35762         }
35763     },
35764
35765     // private
35766     delayAutoWidth : function(){
35767         if(this.rendered){
35768             if(!this.awTask){
35769                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35770             }
35771             this.awTask.delay(20);
35772         }
35773     },
35774
35775     // private
35776     findTargetItem : function(e){
35777         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35778         if(t && t.menuItemId){
35779             return this.items.get(t.menuItemId);
35780         }
35781     },
35782
35783     // private
35784     onClick : function(e){
35785         Roo.log("menu.onClick");
35786         var t = this.findTargetItem(e);
35787         if(!t){
35788             return;
35789         }
35790         Roo.log(e);
35791         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35792             if(t == this.activeItem && t.shouldDeactivate(e)){
35793                 this.activeItem.deactivate();
35794                 delete this.activeItem;
35795                 return;
35796             }
35797             if(t.canActivate){
35798                 this.setActiveItem(t, true);
35799             }
35800             return;
35801             
35802             
35803         }
35804         
35805         t.onClick(e);
35806         this.fireEvent("click", this, t, e);
35807     },
35808
35809     // private
35810     setActiveItem : function(item, autoExpand){
35811         if(item != this.activeItem){
35812             if(this.activeItem){
35813                 this.activeItem.deactivate();
35814             }
35815             this.activeItem = item;
35816             item.activate(autoExpand);
35817         }else if(autoExpand){
35818             item.expandMenu();
35819         }
35820     },
35821
35822     // private
35823     tryActivate : function(start, step){
35824         var items = this.items;
35825         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35826             var item = items.get(i);
35827             if(!item.disabled && item.canActivate){
35828                 this.setActiveItem(item, false);
35829                 return item;
35830             }
35831         }
35832         return false;
35833     },
35834
35835     // private
35836     onMouseOver : function(e){
35837         var t;
35838         if(t = this.findTargetItem(e)){
35839             if(t.canActivate && !t.disabled){
35840                 this.setActiveItem(t, true);
35841             }
35842         }
35843         this.fireEvent("mouseover", this, e, t);
35844     },
35845
35846     // private
35847     onMouseOut : function(e){
35848         var t;
35849         if(t = this.findTargetItem(e)){
35850             if(t == this.activeItem && t.shouldDeactivate(e)){
35851                 this.activeItem.deactivate();
35852                 delete this.activeItem;
35853             }
35854         }
35855         this.fireEvent("mouseout", this, e, t);
35856     },
35857
35858     /**
35859      * Read-only.  Returns true if the menu is currently displayed, else false.
35860      * @type Boolean
35861      */
35862     isVisible : function(){
35863         return this.el && !this.hidden;
35864     },
35865
35866     /**
35867      * Displays this menu relative to another element
35868      * @param {String/HTMLElement/Roo.Element} element The element to align to
35869      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35870      * the element (defaults to this.defaultAlign)
35871      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35872      */
35873     show : function(el, pos, parentMenu){
35874         this.parentMenu = parentMenu;
35875         if(!this.el){
35876             this.render();
35877         }
35878         this.fireEvent("beforeshow", this);
35879         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35880     },
35881
35882     /**
35883      * Displays this menu at a specific xy position
35884      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35885      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35886      */
35887     showAt : function(xy, parentMenu, /* private: */_e){
35888         this.parentMenu = parentMenu;
35889         if(!this.el){
35890             this.render();
35891         }
35892         if(_e !== false){
35893             this.fireEvent("beforeshow", this);
35894             xy = this.el.adjustForConstraints(xy);
35895         }
35896         this.el.setXY(xy);
35897         this.el.show();
35898         this.hidden = false;
35899         this.focus();
35900         this.fireEvent("show", this);
35901     },
35902
35903     focus : function(){
35904         if(!this.hidden){
35905             this.doFocus.defer(50, this);
35906         }
35907     },
35908
35909     doFocus : function(){
35910         if(!this.hidden){
35911             this.focusEl.focus();
35912         }
35913     },
35914
35915     /**
35916      * Hides this menu and optionally all parent menus
35917      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35918      */
35919     hide : function(deep){
35920         if(this.el && this.isVisible()){
35921             this.fireEvent("beforehide", this);
35922             if(this.activeItem){
35923                 this.activeItem.deactivate();
35924                 this.activeItem = null;
35925             }
35926             this.el.hide();
35927             this.hidden = true;
35928             this.fireEvent("hide", this);
35929         }
35930         if(deep === true && this.parentMenu){
35931             this.parentMenu.hide(true);
35932         }
35933     },
35934
35935     /**
35936      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35937      * Any of the following are valid:
35938      * <ul>
35939      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35940      * <li>An HTMLElement object which will be converted to a menu item</li>
35941      * <li>A menu item config object that will be created as a new menu item</li>
35942      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35943      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35944      * </ul>
35945      * Usage:
35946      * <pre><code>
35947 // Create the menu
35948 var menu = new Roo.menu.Menu();
35949
35950 // Create a menu item to add by reference
35951 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35952
35953 // Add a bunch of items at once using different methods.
35954 // Only the last item added will be returned.
35955 var item = menu.add(
35956     menuItem,                // add existing item by ref
35957     'Dynamic Item',          // new TextItem
35958     '-',                     // new separator
35959     { text: 'Config Item' }  // new item by config
35960 );
35961 </code></pre>
35962      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35963      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35964      */
35965     add : function(){
35966         var a = arguments, l = a.length, item;
35967         for(var i = 0; i < l; i++){
35968             var el = a[i];
35969             if ((typeof(el) == "object") && el.xtype && el.xns) {
35970                 el = Roo.factory(el, Roo.menu);
35971             }
35972             
35973             if(el.render){ // some kind of Item
35974                 item = this.addItem(el);
35975             }else if(typeof el == "string"){ // string
35976                 if(el == "separator" || el == "-"){
35977                     item = this.addSeparator();
35978                 }else{
35979                     item = this.addText(el);
35980                 }
35981             }else if(el.tagName || el.el){ // element
35982                 item = this.addElement(el);
35983             }else if(typeof el == "object"){ // must be menu item config?
35984                 item = this.addMenuItem(el);
35985             }
35986         }
35987         return item;
35988     },
35989
35990     /**
35991      * Returns this menu's underlying {@link Roo.Element} object
35992      * @return {Roo.Element} The element
35993      */
35994     getEl : function(){
35995         if(!this.el){
35996             this.render();
35997         }
35998         return this.el;
35999     },
36000
36001     /**
36002      * Adds a separator bar to the menu
36003      * @return {Roo.menu.Item} The menu item that was added
36004      */
36005     addSeparator : function(){
36006         return this.addItem(new Roo.menu.Separator());
36007     },
36008
36009     /**
36010      * Adds an {@link Roo.Element} object to the menu
36011      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
36012      * @return {Roo.menu.Item} The menu item that was added
36013      */
36014     addElement : function(el){
36015         return this.addItem(new Roo.menu.BaseItem(el));
36016     },
36017
36018     /**
36019      * Adds an existing object based on {@link Roo.menu.Item} to the menu
36020      * @param {Roo.menu.Item} item The menu item to add
36021      * @return {Roo.menu.Item} The menu item that was added
36022      */
36023     addItem : function(item){
36024         this.items.add(item);
36025         if(this.ul){
36026             var li = document.createElement("li");
36027             li.className = "x-menu-list-item";
36028             this.ul.dom.appendChild(li);
36029             item.render(li, this);
36030             this.delayAutoWidth();
36031         }
36032         return item;
36033     },
36034
36035     /**
36036      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
36037      * @param {Object} config A MenuItem config object
36038      * @return {Roo.menu.Item} The menu item that was added
36039      */
36040     addMenuItem : function(config){
36041         if(!(config instanceof Roo.menu.Item)){
36042             if(typeof config.checked == "boolean"){ // must be check menu item config?
36043                 config = new Roo.menu.CheckItem(config);
36044             }else{
36045                 config = new Roo.menu.Item(config);
36046             }
36047         }
36048         return this.addItem(config);
36049     },
36050
36051     /**
36052      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36053      * @param {String} text The text to display in the menu item
36054      * @return {Roo.menu.Item} The menu item that was added
36055      */
36056     addText : function(text){
36057         return this.addItem(new Roo.menu.TextItem({ text : text }));
36058     },
36059
36060     /**
36061      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36062      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36063      * @param {Roo.menu.Item} item The menu item to add
36064      * @return {Roo.menu.Item} The menu item that was added
36065      */
36066     insert : function(index, item){
36067         this.items.insert(index, item);
36068         if(this.ul){
36069             var li = document.createElement("li");
36070             li.className = "x-menu-list-item";
36071             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36072             item.render(li, this);
36073             this.delayAutoWidth();
36074         }
36075         return item;
36076     },
36077
36078     /**
36079      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36080      * @param {Roo.menu.Item} item The menu item to remove
36081      */
36082     remove : function(item){
36083         this.items.removeKey(item.id);
36084         item.destroy();
36085     },
36086
36087     /**
36088      * Removes and destroys all items in the menu
36089      */
36090     removeAll : function(){
36091         var f;
36092         while(f = this.items.first()){
36093             this.remove(f);
36094         }
36095     }
36096 });
36097
36098 // MenuNav is a private utility class used internally by the Menu
36099 Roo.menu.MenuNav = function(menu){
36100     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36101     this.scope = this.menu = menu;
36102 };
36103
36104 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36105     doRelay : function(e, h){
36106         var k = e.getKey();
36107         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36108             this.menu.tryActivate(0, 1);
36109             return false;
36110         }
36111         return h.call(this.scope || this, e, this.menu);
36112     },
36113
36114     up : function(e, m){
36115         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36116             m.tryActivate(m.items.length-1, -1);
36117         }
36118     },
36119
36120     down : function(e, m){
36121         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36122             m.tryActivate(0, 1);
36123         }
36124     },
36125
36126     right : function(e, m){
36127         if(m.activeItem){
36128             m.activeItem.expandMenu(true);
36129         }
36130     },
36131
36132     left : function(e, m){
36133         m.hide();
36134         if(m.parentMenu && m.parentMenu.activeItem){
36135             m.parentMenu.activeItem.activate();
36136         }
36137     },
36138
36139     enter : function(e, m){
36140         if(m.activeItem){
36141             e.stopPropagation();
36142             m.activeItem.onClick(e);
36143             m.fireEvent("click", this, m.activeItem);
36144             return true;
36145         }
36146     }
36147 });/*
36148  * Based on:
36149  * Ext JS Library 1.1.1
36150  * Copyright(c) 2006-2007, Ext JS, LLC.
36151  *
36152  * Originally Released Under LGPL - original licence link has changed is not relivant.
36153  *
36154  * Fork - LGPL
36155  * <script type="text/javascript">
36156  */
36157  
36158 /**
36159  * @class Roo.menu.MenuMgr
36160  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36161  * @singleton
36162  */
36163 Roo.menu.MenuMgr = function(){
36164    var menus, active, groups = {}, attached = false, lastShow = new Date();
36165
36166    // private - called when first menu is created
36167    function init(){
36168        menus = {};
36169        active = new Roo.util.MixedCollection();
36170        Roo.get(document).addKeyListener(27, function(){
36171            if(active.length > 0){
36172                hideAll();
36173            }
36174        });
36175    }
36176
36177    // private
36178    function hideAll(){
36179        if(active && active.length > 0){
36180            var c = active.clone();
36181            c.each(function(m){
36182                m.hide();
36183            });
36184        }
36185    }
36186
36187    // private
36188    function onHide(m){
36189        active.remove(m);
36190        if(active.length < 1){
36191            Roo.get(document).un("mousedown", onMouseDown);
36192            attached = false;
36193        }
36194    }
36195
36196    // private
36197    function onShow(m){
36198        var last = active.last();
36199        lastShow = new Date();
36200        active.add(m);
36201        if(!attached){
36202            Roo.get(document).on("mousedown", onMouseDown);
36203            attached = true;
36204        }
36205        if(m.parentMenu){
36206           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36207           m.parentMenu.activeChild = m;
36208        }else if(last && last.isVisible()){
36209           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36210        }
36211    }
36212
36213    // private
36214    function onBeforeHide(m){
36215        if(m.activeChild){
36216            m.activeChild.hide();
36217        }
36218        if(m.autoHideTimer){
36219            clearTimeout(m.autoHideTimer);
36220            delete m.autoHideTimer;
36221        }
36222    }
36223
36224    // private
36225    function onBeforeShow(m){
36226        var pm = m.parentMenu;
36227        if(!pm && !m.allowOtherMenus){
36228            hideAll();
36229        }else if(pm && pm.activeChild && active != m){
36230            pm.activeChild.hide();
36231        }
36232    }
36233
36234    // private
36235    function onMouseDown(e){
36236        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36237            hideAll();
36238        }
36239    }
36240
36241    // private
36242    function onBeforeCheck(mi, state){
36243        if(state){
36244            var g = groups[mi.group];
36245            for(var i = 0, l = g.length; i < l; i++){
36246                if(g[i] != mi){
36247                    g[i].setChecked(false);
36248                }
36249            }
36250        }
36251    }
36252
36253    return {
36254
36255        /**
36256         * Hides all menus that are currently visible
36257         */
36258        hideAll : function(){
36259             hideAll();  
36260        },
36261
36262        // private
36263        register : function(menu){
36264            if(!menus){
36265                init();
36266            }
36267            menus[menu.id] = menu;
36268            menu.on("beforehide", onBeforeHide);
36269            menu.on("hide", onHide);
36270            menu.on("beforeshow", onBeforeShow);
36271            menu.on("show", onShow);
36272            var g = menu.group;
36273            if(g && menu.events["checkchange"]){
36274                if(!groups[g]){
36275                    groups[g] = [];
36276                }
36277                groups[g].push(menu);
36278                menu.on("checkchange", onCheck);
36279            }
36280        },
36281
36282         /**
36283          * Returns a {@link Roo.menu.Menu} object
36284          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36285          * be used to generate and return a new Menu instance.
36286          */
36287        get : function(menu){
36288            if(typeof menu == "string"){ // menu id
36289                return menus[menu];
36290            }else if(menu.events){  // menu instance
36291                return menu;
36292            }else if(typeof menu.length == 'number'){ // array of menu items?
36293                return new Roo.menu.Menu({items:menu});
36294            }else{ // otherwise, must be a config
36295                return new Roo.menu.Menu(menu);
36296            }
36297        },
36298
36299        // private
36300        unregister : function(menu){
36301            delete menus[menu.id];
36302            menu.un("beforehide", onBeforeHide);
36303            menu.un("hide", onHide);
36304            menu.un("beforeshow", onBeforeShow);
36305            menu.un("show", onShow);
36306            var g = menu.group;
36307            if(g && menu.events["checkchange"]){
36308                groups[g].remove(menu);
36309                menu.un("checkchange", onCheck);
36310            }
36311        },
36312
36313        // private
36314        registerCheckable : function(menuItem){
36315            var g = menuItem.group;
36316            if(g){
36317                if(!groups[g]){
36318                    groups[g] = [];
36319                }
36320                groups[g].push(menuItem);
36321                menuItem.on("beforecheckchange", onBeforeCheck);
36322            }
36323        },
36324
36325        // private
36326        unregisterCheckable : function(menuItem){
36327            var g = menuItem.group;
36328            if(g){
36329                groups[g].remove(menuItem);
36330                menuItem.un("beforecheckchange", onBeforeCheck);
36331            }
36332        }
36333    };
36334 }();/*
36335  * Based on:
36336  * Ext JS Library 1.1.1
36337  * Copyright(c) 2006-2007, Ext JS, LLC.
36338  *
36339  * Originally Released Under LGPL - original licence link has changed is not relivant.
36340  *
36341  * Fork - LGPL
36342  * <script type="text/javascript">
36343  */
36344  
36345
36346 /**
36347  * @class Roo.menu.BaseItem
36348  * @extends Roo.Component
36349  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36350  * management and base configuration options shared by all menu components.
36351  * @constructor
36352  * Creates a new BaseItem
36353  * @param {Object} config Configuration options
36354  */
36355 Roo.menu.BaseItem = function(config){
36356     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36357
36358     this.addEvents({
36359         /**
36360          * @event click
36361          * Fires when this item is clicked
36362          * @param {Roo.menu.BaseItem} this
36363          * @param {Roo.EventObject} e
36364          */
36365         click: true,
36366         /**
36367          * @event activate
36368          * Fires when this item is activated
36369          * @param {Roo.menu.BaseItem} this
36370          */
36371         activate : true,
36372         /**
36373          * @event deactivate
36374          * Fires when this item is deactivated
36375          * @param {Roo.menu.BaseItem} this
36376          */
36377         deactivate : true
36378     });
36379
36380     if(this.handler){
36381         this.on("click", this.handler, this.scope, true);
36382     }
36383 };
36384
36385 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36386     /**
36387      * @cfg {Function} handler
36388      * A function that will handle the click event of this menu item (defaults to undefined)
36389      */
36390     /**
36391      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36392      */
36393     canActivate : false,
36394     
36395      /**
36396      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36397      */
36398     hidden: false,
36399     
36400     /**
36401      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36402      */
36403     activeClass : "x-menu-item-active",
36404     /**
36405      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36406      */
36407     hideOnClick : true,
36408     /**
36409      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36410      */
36411     hideDelay : 100,
36412
36413     // private
36414     ctype: "Roo.menu.BaseItem",
36415
36416     // private
36417     actionMode : "container",
36418
36419     // private
36420     render : function(container, parentMenu){
36421         this.parentMenu = parentMenu;
36422         Roo.menu.BaseItem.superclass.render.call(this, container);
36423         this.container.menuItemId = this.id;
36424     },
36425
36426     // private
36427     onRender : function(container, position){
36428         this.el = Roo.get(this.el);
36429         container.dom.appendChild(this.el.dom);
36430     },
36431
36432     // private
36433     onClick : function(e){
36434         if(!this.disabled && this.fireEvent("click", this, e) !== false
36435                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36436             this.handleClick(e);
36437         }else{
36438             e.stopEvent();
36439         }
36440     },
36441
36442     // private
36443     activate : function(){
36444         if(this.disabled){
36445             return false;
36446         }
36447         var li = this.container;
36448         li.addClass(this.activeClass);
36449         this.region = li.getRegion().adjust(2, 2, -2, -2);
36450         this.fireEvent("activate", this);
36451         return true;
36452     },
36453
36454     // private
36455     deactivate : function(){
36456         this.container.removeClass(this.activeClass);
36457         this.fireEvent("deactivate", this);
36458     },
36459
36460     // private
36461     shouldDeactivate : function(e){
36462         return !this.region || !this.region.contains(e.getPoint());
36463     },
36464
36465     // private
36466     handleClick : function(e){
36467         if(this.hideOnClick){
36468             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36469         }
36470     },
36471
36472     // private
36473     expandMenu : function(autoActivate){
36474         // do nothing
36475     },
36476
36477     // private
36478     hideMenu : function(){
36479         // do nothing
36480     }
36481 });/*
36482  * Based on:
36483  * Ext JS Library 1.1.1
36484  * Copyright(c) 2006-2007, Ext JS, LLC.
36485  *
36486  * Originally Released Under LGPL - original licence link has changed is not relivant.
36487  *
36488  * Fork - LGPL
36489  * <script type="text/javascript">
36490  */
36491  
36492 /**
36493  * @class Roo.menu.Adapter
36494  * @extends Roo.menu.BaseItem
36495  * 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.
36496  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36497  * @constructor
36498  * Creates a new Adapter
36499  * @param {Object} config Configuration options
36500  */
36501 Roo.menu.Adapter = function(component, config){
36502     Roo.menu.Adapter.superclass.constructor.call(this, config);
36503     this.component = component;
36504 };
36505 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36506     // private
36507     canActivate : true,
36508
36509     // private
36510     onRender : function(container, position){
36511         this.component.render(container);
36512         this.el = this.component.getEl();
36513     },
36514
36515     // private
36516     activate : function(){
36517         if(this.disabled){
36518             return false;
36519         }
36520         this.component.focus();
36521         this.fireEvent("activate", this);
36522         return true;
36523     },
36524
36525     // private
36526     deactivate : function(){
36527         this.fireEvent("deactivate", this);
36528     },
36529
36530     // private
36531     disable : function(){
36532         this.component.disable();
36533         Roo.menu.Adapter.superclass.disable.call(this);
36534     },
36535
36536     // private
36537     enable : function(){
36538         this.component.enable();
36539         Roo.menu.Adapter.superclass.enable.call(this);
36540     }
36541 });/*
36542  * Based on:
36543  * Ext JS Library 1.1.1
36544  * Copyright(c) 2006-2007, Ext JS, LLC.
36545  *
36546  * Originally Released Under LGPL - original licence link has changed is not relivant.
36547  *
36548  * Fork - LGPL
36549  * <script type="text/javascript">
36550  */
36551
36552 /**
36553  * @class Roo.menu.TextItem
36554  * @extends Roo.menu.BaseItem
36555  * Adds a static text string to a menu, usually used as either a heading or group separator.
36556  * Note: old style constructor with text is still supported.
36557  * 
36558  * @constructor
36559  * Creates a new TextItem
36560  * @param {Object} cfg Configuration
36561  */
36562 Roo.menu.TextItem = function(cfg){
36563     if (typeof(cfg) == 'string') {
36564         this.text = cfg;
36565     } else {
36566         Roo.apply(this,cfg);
36567     }
36568     
36569     Roo.menu.TextItem.superclass.constructor.call(this);
36570 };
36571
36572 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36573     /**
36574      * @cfg {Boolean} text Text to show on item.
36575      */
36576     text : '',
36577     
36578     /**
36579      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36580      */
36581     hideOnClick : false,
36582     /**
36583      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36584      */
36585     itemCls : "x-menu-text",
36586
36587     // private
36588     onRender : function(){
36589         var s = document.createElement("span");
36590         s.className = this.itemCls;
36591         s.innerHTML = this.text;
36592         this.el = s;
36593         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36594     }
36595 });/*
36596  * Based on:
36597  * Ext JS Library 1.1.1
36598  * Copyright(c) 2006-2007, Ext JS, LLC.
36599  *
36600  * Originally Released Under LGPL - original licence link has changed is not relivant.
36601  *
36602  * Fork - LGPL
36603  * <script type="text/javascript">
36604  */
36605
36606 /**
36607  * @class Roo.menu.Separator
36608  * @extends Roo.menu.BaseItem
36609  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36610  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36611  * @constructor
36612  * @param {Object} config Configuration options
36613  */
36614 Roo.menu.Separator = function(config){
36615     Roo.menu.Separator.superclass.constructor.call(this, config);
36616 };
36617
36618 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36619     /**
36620      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36621      */
36622     itemCls : "x-menu-sep",
36623     /**
36624      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36625      */
36626     hideOnClick : false,
36627
36628     // private
36629     onRender : function(li){
36630         var s = document.createElement("span");
36631         s.className = this.itemCls;
36632         s.innerHTML = "&#160;";
36633         this.el = s;
36634         li.addClass("x-menu-sep-li");
36635         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36636     }
36637 });/*
36638  * Based on:
36639  * Ext JS Library 1.1.1
36640  * Copyright(c) 2006-2007, Ext JS, LLC.
36641  *
36642  * Originally Released Under LGPL - original licence link has changed is not relivant.
36643  *
36644  * Fork - LGPL
36645  * <script type="text/javascript">
36646  */
36647 /**
36648  * @class Roo.menu.Item
36649  * @extends Roo.menu.BaseItem
36650  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36651  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36652  * activation and click handling.
36653  * @constructor
36654  * Creates a new Item
36655  * @param {Object} config Configuration options
36656  */
36657 Roo.menu.Item = function(config){
36658     Roo.menu.Item.superclass.constructor.call(this, config);
36659     if(this.menu){
36660         this.menu = Roo.menu.MenuMgr.get(this.menu);
36661     }
36662 };
36663 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36664     
36665     /**
36666      * @cfg {String} text
36667      * The text to show on the menu item.
36668      */
36669     text: '',
36670      /**
36671      * @cfg {String} HTML to render in menu
36672      * The text to show on the menu item (HTML version).
36673      */
36674     html: '',
36675     /**
36676      * @cfg {String} icon
36677      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36678      */
36679     icon: undefined,
36680     /**
36681      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36682      */
36683     itemCls : "x-menu-item",
36684     /**
36685      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36686      */
36687     canActivate : true,
36688     /**
36689      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36690      */
36691     showDelay: 200,
36692     // doc'd in BaseItem
36693     hideDelay: 200,
36694
36695     // private
36696     ctype: "Roo.menu.Item",
36697     
36698     // private
36699     onRender : function(container, position){
36700         var el = document.createElement("a");
36701         el.hideFocus = true;
36702         el.unselectable = "on";
36703         el.href = this.href || "#";
36704         if(this.hrefTarget){
36705             el.target = this.hrefTarget;
36706         }
36707         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36708         
36709         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36710         
36711         el.innerHTML = String.format(
36712                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36713                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36714         this.el = el;
36715         Roo.menu.Item.superclass.onRender.call(this, container, position);
36716     },
36717
36718     /**
36719      * Sets the text to display in this menu item
36720      * @param {String} text The text to display
36721      * @param {Boolean} isHTML true to indicate text is pure html.
36722      */
36723     setText : function(text, isHTML){
36724         if (isHTML) {
36725             this.html = text;
36726         } else {
36727             this.text = text;
36728             this.html = '';
36729         }
36730         if(this.rendered){
36731             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36732      
36733             this.el.update(String.format(
36734                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36735                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36736             this.parentMenu.autoWidth();
36737         }
36738     },
36739
36740     // private
36741     handleClick : function(e){
36742         if(!this.href){ // if no link defined, stop the event automatically
36743             e.stopEvent();
36744         }
36745         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36746     },
36747
36748     // private
36749     activate : function(autoExpand){
36750         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36751             this.focus();
36752             if(autoExpand){
36753                 this.expandMenu();
36754             }
36755         }
36756         return true;
36757     },
36758
36759     // private
36760     shouldDeactivate : function(e){
36761         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36762             if(this.menu && this.menu.isVisible()){
36763                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36764             }
36765             return true;
36766         }
36767         return false;
36768     },
36769
36770     // private
36771     deactivate : function(){
36772         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36773         this.hideMenu();
36774     },
36775
36776     // private
36777     expandMenu : function(autoActivate){
36778         if(!this.disabled && this.menu){
36779             clearTimeout(this.hideTimer);
36780             delete this.hideTimer;
36781             if(!this.menu.isVisible() && !this.showTimer){
36782                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36783             }else if (this.menu.isVisible() && autoActivate){
36784                 this.menu.tryActivate(0, 1);
36785             }
36786         }
36787     },
36788
36789     // private
36790     deferExpand : function(autoActivate){
36791         delete this.showTimer;
36792         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36793         if(autoActivate){
36794             this.menu.tryActivate(0, 1);
36795         }
36796     },
36797
36798     // private
36799     hideMenu : function(){
36800         clearTimeout(this.showTimer);
36801         delete this.showTimer;
36802         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36803             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36804         }
36805     },
36806
36807     // private
36808     deferHide : function(){
36809         delete this.hideTimer;
36810         this.menu.hide();
36811     }
36812 });/*
36813  * Based on:
36814  * Ext JS Library 1.1.1
36815  * Copyright(c) 2006-2007, Ext JS, LLC.
36816  *
36817  * Originally Released Under LGPL - original licence link has changed is not relivant.
36818  *
36819  * Fork - LGPL
36820  * <script type="text/javascript">
36821  */
36822  
36823 /**
36824  * @class Roo.menu.CheckItem
36825  * @extends Roo.menu.Item
36826  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36827  * @constructor
36828  * Creates a new CheckItem
36829  * @param {Object} config Configuration options
36830  */
36831 Roo.menu.CheckItem = function(config){
36832     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36833     this.addEvents({
36834         /**
36835          * @event beforecheckchange
36836          * Fires before the checked value is set, providing an opportunity to cancel if needed
36837          * @param {Roo.menu.CheckItem} this
36838          * @param {Boolean} checked The new checked value that will be set
36839          */
36840         "beforecheckchange" : true,
36841         /**
36842          * @event checkchange
36843          * Fires after the checked value has been set
36844          * @param {Roo.menu.CheckItem} this
36845          * @param {Boolean} checked The checked value that was set
36846          */
36847         "checkchange" : true
36848     });
36849     if(this.checkHandler){
36850         this.on('checkchange', this.checkHandler, this.scope);
36851     }
36852 };
36853 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36854     /**
36855      * @cfg {String} group
36856      * All check items with the same group name will automatically be grouped into a single-select
36857      * radio button group (defaults to '')
36858      */
36859     /**
36860      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36861      */
36862     itemCls : "x-menu-item x-menu-check-item",
36863     /**
36864      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36865      */
36866     groupClass : "x-menu-group-item",
36867
36868     /**
36869      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36870      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36871      * initialized with checked = true will be rendered as checked.
36872      */
36873     checked: false,
36874
36875     // private
36876     ctype: "Roo.menu.CheckItem",
36877
36878     // private
36879     onRender : function(c){
36880         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36881         if(this.group){
36882             this.el.addClass(this.groupClass);
36883         }
36884         Roo.menu.MenuMgr.registerCheckable(this);
36885         if(this.checked){
36886             this.checked = false;
36887             this.setChecked(true, true);
36888         }
36889     },
36890
36891     // private
36892     destroy : function(){
36893         if(this.rendered){
36894             Roo.menu.MenuMgr.unregisterCheckable(this);
36895         }
36896         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36897     },
36898
36899     /**
36900      * Set the checked state of this item
36901      * @param {Boolean} checked The new checked value
36902      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36903      */
36904     setChecked : function(state, suppressEvent){
36905         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36906             if(this.container){
36907                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36908             }
36909             this.checked = state;
36910             if(suppressEvent !== true){
36911                 this.fireEvent("checkchange", this, state);
36912             }
36913         }
36914     },
36915
36916     // private
36917     handleClick : function(e){
36918        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36919            this.setChecked(!this.checked);
36920        }
36921        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36922     }
36923 });/*
36924  * Based on:
36925  * Ext JS Library 1.1.1
36926  * Copyright(c) 2006-2007, Ext JS, LLC.
36927  *
36928  * Originally Released Under LGPL - original licence link has changed is not relivant.
36929  *
36930  * Fork - LGPL
36931  * <script type="text/javascript">
36932  */
36933  
36934 /**
36935  * @class Roo.menu.DateItem
36936  * @extends Roo.menu.Adapter
36937  * A menu item that wraps the {@link Roo.DatPicker} component.
36938  * @constructor
36939  * Creates a new DateItem
36940  * @param {Object} config Configuration options
36941  */
36942 Roo.menu.DateItem = function(config){
36943     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36944     /** The Roo.DatePicker object @type Roo.DatePicker */
36945     this.picker = this.component;
36946     this.addEvents({select: true});
36947     
36948     this.picker.on("render", function(picker){
36949         picker.getEl().swallowEvent("click");
36950         picker.container.addClass("x-menu-date-item");
36951     });
36952
36953     this.picker.on("select", this.onSelect, this);
36954 };
36955
36956 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36957     // private
36958     onSelect : function(picker, date){
36959         this.fireEvent("select", this, date, picker);
36960         Roo.menu.DateItem.superclass.handleClick.call(this);
36961     }
36962 });/*
36963  * Based on:
36964  * Ext JS Library 1.1.1
36965  * Copyright(c) 2006-2007, Ext JS, LLC.
36966  *
36967  * Originally Released Under LGPL - original licence link has changed is not relivant.
36968  *
36969  * Fork - LGPL
36970  * <script type="text/javascript">
36971  */
36972  
36973 /**
36974  * @class Roo.menu.ColorItem
36975  * @extends Roo.menu.Adapter
36976  * A menu item that wraps the {@link Roo.ColorPalette} component.
36977  * @constructor
36978  * Creates a new ColorItem
36979  * @param {Object} config Configuration options
36980  */
36981 Roo.menu.ColorItem = function(config){
36982     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36983     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36984     this.palette = this.component;
36985     this.relayEvents(this.palette, ["select"]);
36986     if(this.selectHandler){
36987         this.on('select', this.selectHandler, this.scope);
36988     }
36989 };
36990 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36991  * Based on:
36992  * Ext JS Library 1.1.1
36993  * Copyright(c) 2006-2007, Ext JS, LLC.
36994  *
36995  * Originally Released Under LGPL - original licence link has changed is not relivant.
36996  *
36997  * Fork - LGPL
36998  * <script type="text/javascript">
36999  */
37000  
37001
37002 /**
37003  * @class Roo.menu.DateMenu
37004  * @extends Roo.menu.Menu
37005  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
37006  * @constructor
37007  * Creates a new DateMenu
37008  * @param {Object} config Configuration options
37009  */
37010 Roo.menu.DateMenu = function(config){
37011     Roo.menu.DateMenu.superclass.constructor.call(this, config);
37012     this.plain = true;
37013     var di = new Roo.menu.DateItem(config);
37014     this.add(di);
37015     /**
37016      * The {@link Roo.DatePicker} instance for this DateMenu
37017      * @type DatePicker
37018      */
37019     this.picker = di.picker;
37020     /**
37021      * @event select
37022      * @param {DatePicker} picker
37023      * @param {Date} date
37024      */
37025     this.relayEvents(di, ["select"]);
37026     this.on('beforeshow', function(){
37027         if(this.picker){
37028             this.picker.hideMonthPicker(false);
37029         }
37030     }, this);
37031 };
37032 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
37033     cls:'x-date-menu'
37034 });/*
37035  * Based on:
37036  * Ext JS Library 1.1.1
37037  * Copyright(c) 2006-2007, Ext JS, LLC.
37038  *
37039  * Originally Released Under LGPL - original licence link has changed is not relivant.
37040  *
37041  * Fork - LGPL
37042  * <script type="text/javascript">
37043  */
37044  
37045
37046 /**
37047  * @class Roo.menu.ColorMenu
37048  * @extends Roo.menu.Menu
37049  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37050  * @constructor
37051  * Creates a new ColorMenu
37052  * @param {Object} config Configuration options
37053  */
37054 Roo.menu.ColorMenu = function(config){
37055     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37056     this.plain = true;
37057     var ci = new Roo.menu.ColorItem(config);
37058     this.add(ci);
37059     /**
37060      * The {@link Roo.ColorPalette} instance for this ColorMenu
37061      * @type ColorPalette
37062      */
37063     this.palette = ci.palette;
37064     /**
37065      * @event select
37066      * @param {ColorPalette} palette
37067      * @param {String} color
37068      */
37069     this.relayEvents(ci, ["select"]);
37070 };
37071 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37072  * Based on:
37073  * Ext JS Library 1.1.1
37074  * Copyright(c) 2006-2007, Ext JS, LLC.
37075  *
37076  * Originally Released Under LGPL - original licence link has changed is not relivant.
37077  *
37078  * Fork - LGPL
37079  * <script type="text/javascript">
37080  */
37081  
37082 /**
37083  * @class Roo.form.Field
37084  * @extends Roo.BoxComponent
37085  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37086  * @constructor
37087  * Creates a new Field
37088  * @param {Object} config Configuration options
37089  */
37090 Roo.form.Field = function(config){
37091     Roo.form.Field.superclass.constructor.call(this, config);
37092 };
37093
37094 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37095     /**
37096      * @cfg {String} fieldLabel Label to use when rendering a form.
37097      */
37098        /**
37099      * @cfg {String} qtip Mouse over tip
37100      */
37101      
37102     /**
37103      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37104      */
37105     invalidClass : "x-form-invalid",
37106     /**
37107      * @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")
37108      */
37109     invalidText : "The value in this field is invalid",
37110     /**
37111      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37112      */
37113     focusClass : "x-form-focus",
37114     /**
37115      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37116       automatic validation (defaults to "keyup").
37117      */
37118     validationEvent : "keyup",
37119     /**
37120      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37121      */
37122     validateOnBlur : true,
37123     /**
37124      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37125      */
37126     validationDelay : 250,
37127     /**
37128      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37129      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37130      */
37131     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37132     /**
37133      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37134      */
37135     fieldClass : "x-form-field",
37136     /**
37137      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37138      *<pre>
37139 Value         Description
37140 -----------   ----------------------------------------------------------------------
37141 qtip          Display a quick tip when the user hovers over the field
37142 title         Display a default browser title attribute popup
37143 under         Add a block div beneath the field containing the error text
37144 side          Add an error icon to the right of the field with a popup on hover
37145 [element id]  Add the error text directly to the innerHTML of the specified element
37146 </pre>
37147      */
37148     msgTarget : 'qtip',
37149     /**
37150      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37151      */
37152     msgFx : 'normal',
37153
37154     /**
37155      * @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.
37156      */
37157     readOnly : false,
37158
37159     /**
37160      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37161      */
37162     disabled : false,
37163
37164     /**
37165      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37166      */
37167     inputType : undefined,
37168     
37169     /**
37170      * @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).
37171          */
37172         tabIndex : undefined,
37173         
37174     // private
37175     isFormField : true,
37176
37177     // private
37178     hasFocus : false,
37179     /**
37180      * @property {Roo.Element} fieldEl
37181      * Element Containing the rendered Field (with label etc.)
37182      */
37183     /**
37184      * @cfg {Mixed} value A value to initialize this field with.
37185      */
37186     value : undefined,
37187
37188     /**
37189      * @cfg {String} name The field's HTML name attribute.
37190      */
37191     /**
37192      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37193      */
37194     // private
37195     loadedValue : false,
37196      
37197      
37198         // private ??
37199         initComponent : function(){
37200         Roo.form.Field.superclass.initComponent.call(this);
37201         this.addEvents({
37202             /**
37203              * @event focus
37204              * Fires when this field receives input focus.
37205              * @param {Roo.form.Field} this
37206              */
37207             focus : true,
37208             /**
37209              * @event blur
37210              * Fires when this field loses input focus.
37211              * @param {Roo.form.Field} this
37212              */
37213             blur : true,
37214             /**
37215              * @event specialkey
37216              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37217              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37218              * @param {Roo.form.Field} this
37219              * @param {Roo.EventObject} e The event object
37220              */
37221             specialkey : true,
37222             /**
37223              * @event change
37224              * Fires just before the field blurs if the field value has changed.
37225              * @param {Roo.form.Field} this
37226              * @param {Mixed} newValue The new value
37227              * @param {Mixed} oldValue The original value
37228              */
37229             change : true,
37230             /**
37231              * @event invalid
37232              * Fires after the field has been marked as invalid.
37233              * @param {Roo.form.Field} this
37234              * @param {String} msg The validation message
37235              */
37236             invalid : true,
37237             /**
37238              * @event valid
37239              * Fires after the field has been validated with no errors.
37240              * @param {Roo.form.Field} this
37241              */
37242             valid : true,
37243              /**
37244              * @event keyup
37245              * Fires after the key up
37246              * @param {Roo.form.Field} this
37247              * @param {Roo.EventObject}  e The event Object
37248              */
37249             keyup : true
37250         });
37251     },
37252
37253     /**
37254      * Returns the name attribute of the field if available
37255      * @return {String} name The field name
37256      */
37257     getName: function(){
37258          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37259     },
37260
37261     // private
37262     onRender : function(ct, position){
37263         Roo.form.Field.superclass.onRender.call(this, ct, position);
37264         if(!this.el){
37265             var cfg = this.getAutoCreate();
37266             if(!cfg.name){
37267                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37268             }
37269             if (!cfg.name.length) {
37270                 delete cfg.name;
37271             }
37272             if(this.inputType){
37273                 cfg.type = this.inputType;
37274             }
37275             this.el = ct.createChild(cfg, position);
37276         }
37277         var type = this.el.dom.type;
37278         if(type){
37279             if(type == 'password'){
37280                 type = 'text';
37281             }
37282             this.el.addClass('x-form-'+type);
37283         }
37284         if(this.readOnly){
37285             this.el.dom.readOnly = true;
37286         }
37287         if(this.tabIndex !== undefined){
37288             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37289         }
37290
37291         this.el.addClass([this.fieldClass, this.cls]);
37292         this.initValue();
37293     },
37294
37295     /**
37296      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37297      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37298      * @return {Roo.form.Field} this
37299      */
37300     applyTo : function(target){
37301         this.allowDomMove = false;
37302         this.el = Roo.get(target);
37303         this.render(this.el.dom.parentNode);
37304         return this;
37305     },
37306
37307     // private
37308     initValue : function(){
37309         if(this.value !== undefined){
37310             this.setValue(this.value);
37311         }else if(this.el.dom.value.length > 0){
37312             this.setValue(this.el.dom.value);
37313         }
37314     },
37315
37316     /**
37317      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37318      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
37319      */
37320     isDirty : function() {
37321         if(this.disabled) {
37322             return false;
37323         }
37324         return String(this.getValue()) !== String(this.originalValue);
37325     },
37326
37327     /**
37328      * stores the current value in loadedValue
37329      */
37330     resetHasChanged : function()
37331     {
37332         this.loadedValue = String(this.getValue());
37333     },
37334     /**
37335      * checks the current value against the 'loaded' value.
37336      * Note - will return false if 'resetHasChanged' has not been called first.
37337      */
37338     hasChanged : function()
37339     {
37340         if(this.disabled || this.readOnly) {
37341             return false;
37342         }
37343         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
37344     },
37345     
37346     
37347     
37348     // private
37349     afterRender : function(){
37350         Roo.form.Field.superclass.afterRender.call(this);
37351         this.initEvents();
37352     },
37353
37354     // private
37355     fireKey : function(e){
37356         //Roo.log('field ' + e.getKey());
37357         if(e.isNavKeyPress()){
37358             this.fireEvent("specialkey", this, e);
37359         }
37360     },
37361
37362     /**
37363      * Resets the current field value to the originally loaded value and clears any validation messages
37364      */
37365     reset : function(){
37366         this.setValue(this.resetValue);
37367         this.clearInvalid();
37368     },
37369
37370     // private
37371     initEvents : function(){
37372         // safari killled keypress - so keydown is now used..
37373         this.el.on("keydown" , this.fireKey,  this);
37374         this.el.on("focus", this.onFocus,  this);
37375         this.el.on("blur", this.onBlur,  this);
37376         this.el.relayEvent('keyup', this);
37377
37378         // reference to original value for reset
37379         this.originalValue = this.getValue();
37380         this.resetValue =  this.getValue();
37381     },
37382
37383     // private
37384     onFocus : function(){
37385         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37386             this.el.addClass(this.focusClass);
37387         }
37388         if(!this.hasFocus){
37389             this.hasFocus = true;
37390             this.startValue = this.getValue();
37391             this.fireEvent("focus", this);
37392         }
37393     },
37394
37395     beforeBlur : Roo.emptyFn,
37396
37397     // private
37398     onBlur : function(){
37399         this.beforeBlur();
37400         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37401             this.el.removeClass(this.focusClass);
37402         }
37403         this.hasFocus = false;
37404         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37405             this.validate();
37406         }
37407         var v = this.getValue();
37408         if(String(v) !== String(this.startValue)){
37409             this.fireEvent('change', this, v, this.startValue);
37410         }
37411         this.fireEvent("blur", this);
37412     },
37413
37414     /**
37415      * Returns whether or not the field value is currently valid
37416      * @param {Boolean} preventMark True to disable marking the field invalid
37417      * @return {Boolean} True if the value is valid, else false
37418      */
37419     isValid : function(preventMark){
37420         if(this.disabled){
37421             return true;
37422         }
37423         var restore = this.preventMark;
37424         this.preventMark = preventMark === true;
37425         var v = this.validateValue(this.processValue(this.getRawValue()));
37426         this.preventMark = restore;
37427         return v;
37428     },
37429
37430     /**
37431      * Validates the field value
37432      * @return {Boolean} True if the value is valid, else false
37433      */
37434     validate : function(){
37435         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37436             this.clearInvalid();
37437             return true;
37438         }
37439         return false;
37440     },
37441
37442     processValue : function(value){
37443         return value;
37444     },
37445
37446     // private
37447     // Subclasses should provide the validation implementation by overriding this
37448     validateValue : function(value){
37449         return true;
37450     },
37451
37452     /**
37453      * Mark this field as invalid
37454      * @param {String} msg The validation message
37455      */
37456     markInvalid : function(msg){
37457         if(!this.rendered || this.preventMark){ // not rendered
37458             return;
37459         }
37460         
37461         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37462         
37463         obj.el.addClass(this.invalidClass);
37464         msg = msg || this.invalidText;
37465         switch(this.msgTarget){
37466             case 'qtip':
37467                 obj.el.dom.qtip = msg;
37468                 obj.el.dom.qclass = 'x-form-invalid-tip';
37469                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37470                     Roo.QuickTips.enable();
37471                 }
37472                 break;
37473             case 'title':
37474                 this.el.dom.title = msg;
37475                 break;
37476             case 'under':
37477                 if(!this.errorEl){
37478                     var elp = this.el.findParent('.x-form-element', 5, true);
37479                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37480                     this.errorEl.setWidth(elp.getWidth(true)-20);
37481                 }
37482                 this.errorEl.update(msg);
37483                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37484                 break;
37485             case 'side':
37486                 if(!this.errorIcon){
37487                     var elp = this.el.findParent('.x-form-element', 5, true);
37488                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37489                 }
37490                 this.alignErrorIcon();
37491                 this.errorIcon.dom.qtip = msg;
37492                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37493                 this.errorIcon.show();
37494                 this.on('resize', this.alignErrorIcon, this);
37495                 break;
37496             default:
37497                 var t = Roo.getDom(this.msgTarget);
37498                 t.innerHTML = msg;
37499                 t.style.display = this.msgDisplay;
37500                 break;
37501         }
37502         this.fireEvent('invalid', this, msg);
37503     },
37504
37505     // private
37506     alignErrorIcon : function(){
37507         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37508     },
37509
37510     /**
37511      * Clear any invalid styles/messages for this field
37512      */
37513     clearInvalid : function(){
37514         if(!this.rendered || this.preventMark){ // not rendered
37515             return;
37516         }
37517         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37518         
37519         obj.el.removeClass(this.invalidClass);
37520         switch(this.msgTarget){
37521             case 'qtip':
37522                 obj.el.dom.qtip = '';
37523                 break;
37524             case 'title':
37525                 this.el.dom.title = '';
37526                 break;
37527             case 'under':
37528                 if(this.errorEl){
37529                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37530                 }
37531                 break;
37532             case 'side':
37533                 if(this.errorIcon){
37534                     this.errorIcon.dom.qtip = '';
37535                     this.errorIcon.hide();
37536                     this.un('resize', this.alignErrorIcon, this);
37537                 }
37538                 break;
37539             default:
37540                 var t = Roo.getDom(this.msgTarget);
37541                 t.innerHTML = '';
37542                 t.style.display = 'none';
37543                 break;
37544         }
37545         this.fireEvent('valid', this);
37546     },
37547
37548     /**
37549      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37550      * @return {Mixed} value The field value
37551      */
37552     getRawValue : function(){
37553         var v = this.el.getValue();
37554         
37555         return v;
37556     },
37557
37558     /**
37559      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37560      * @return {Mixed} value The field value
37561      */
37562     getValue : function(){
37563         var v = this.el.getValue();
37564          
37565         return v;
37566     },
37567
37568     /**
37569      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37570      * @param {Mixed} value The value to set
37571      */
37572     setRawValue : function(v){
37573         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37574     },
37575
37576     /**
37577      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37578      * @param {Mixed} value The value to set
37579      */
37580     setValue : function(v){
37581         this.value = v;
37582         if(this.rendered){
37583             this.el.dom.value = (v === null || v === undefined ? '' : v);
37584              this.validate();
37585         }
37586     },
37587
37588     adjustSize : function(w, h){
37589         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37590         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37591         return s;
37592     },
37593
37594     adjustWidth : function(tag, w){
37595         tag = tag.toLowerCase();
37596         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37597             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37598                 if(tag == 'input'){
37599                     return w + 2;
37600                 }
37601                 if(tag == 'textarea'){
37602                     return w-2;
37603                 }
37604             }else if(Roo.isOpera){
37605                 if(tag == 'input'){
37606                     return w + 2;
37607                 }
37608                 if(tag == 'textarea'){
37609                     return w-2;
37610                 }
37611             }
37612         }
37613         return w;
37614     }
37615 });
37616
37617
37618 // anything other than normal should be considered experimental
37619 Roo.form.Field.msgFx = {
37620     normal : {
37621         show: function(msgEl, f){
37622             msgEl.setDisplayed('block');
37623         },
37624
37625         hide : function(msgEl, f){
37626             msgEl.setDisplayed(false).update('');
37627         }
37628     },
37629
37630     slide : {
37631         show: function(msgEl, f){
37632             msgEl.slideIn('t', {stopFx:true});
37633         },
37634
37635         hide : function(msgEl, f){
37636             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37637         }
37638     },
37639
37640     slideRight : {
37641         show: function(msgEl, f){
37642             msgEl.fixDisplay();
37643             msgEl.alignTo(f.el, 'tl-tr');
37644             msgEl.slideIn('l', {stopFx:true});
37645         },
37646
37647         hide : function(msgEl, f){
37648             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37649         }
37650     }
37651 };/*
37652  * Based on:
37653  * Ext JS Library 1.1.1
37654  * Copyright(c) 2006-2007, Ext JS, LLC.
37655  *
37656  * Originally Released Under LGPL - original licence link has changed is not relivant.
37657  *
37658  * Fork - LGPL
37659  * <script type="text/javascript">
37660  */
37661  
37662
37663 /**
37664  * @class Roo.form.TextField
37665  * @extends Roo.form.Field
37666  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37667  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37668  * @constructor
37669  * Creates a new TextField
37670  * @param {Object} config Configuration options
37671  */
37672 Roo.form.TextField = function(config){
37673     Roo.form.TextField.superclass.constructor.call(this, config);
37674     this.addEvents({
37675         /**
37676          * @event autosize
37677          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37678          * according to the default logic, but this event provides a hook for the developer to apply additional
37679          * logic at runtime to resize the field if needed.
37680              * @param {Roo.form.Field} this This text field
37681              * @param {Number} width The new field width
37682              */
37683         autosize : true
37684     });
37685 };
37686
37687 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37688     /**
37689      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37690      */
37691     grow : false,
37692     /**
37693      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37694      */
37695     growMin : 30,
37696     /**
37697      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37698      */
37699     growMax : 800,
37700     /**
37701      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37702      */
37703     vtype : null,
37704     /**
37705      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37706      */
37707     maskRe : null,
37708     /**
37709      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37710      */
37711     disableKeyFilter : false,
37712     /**
37713      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37714      */
37715     allowBlank : true,
37716     /**
37717      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37718      */
37719     minLength : 0,
37720     /**
37721      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37722      */
37723     maxLength : Number.MAX_VALUE,
37724     /**
37725      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37726      */
37727     minLengthText : "The minimum length for this field is {0}",
37728     /**
37729      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37730      */
37731     maxLengthText : "The maximum length for this field is {0}",
37732     /**
37733      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37734      */
37735     selectOnFocus : false,
37736     /**
37737      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37738      */
37739     blankText : "This field is required",
37740     /**
37741      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37742      * If available, this function will be called only after the basic validators all return true, and will be passed the
37743      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37744      */
37745     validator : null,
37746     /**
37747      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37748      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37749      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37750      */
37751     regex : null,
37752     /**
37753      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37754      */
37755     regexText : "",
37756     /**
37757      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37758      */
37759     emptyText : null,
37760    
37761
37762     // private
37763     initEvents : function()
37764     {
37765         if (this.emptyText) {
37766             this.el.attr('placeholder', this.emptyText);
37767         }
37768         
37769         Roo.form.TextField.superclass.initEvents.call(this);
37770         if(this.validationEvent == 'keyup'){
37771             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37772             this.el.on('keyup', this.filterValidation, this);
37773         }
37774         else if(this.validationEvent !== false){
37775             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37776         }
37777         
37778         if(this.selectOnFocus){
37779             this.on("focus", this.preFocus, this);
37780             
37781         }
37782         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37783             this.el.on("keypress", this.filterKeys, this);
37784         }
37785         if(this.grow){
37786             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37787             this.el.on("click", this.autoSize,  this);
37788         }
37789         if(this.el.is('input[type=password]') && Roo.isSafari){
37790             this.el.on('keydown', this.SafariOnKeyDown, this);
37791         }
37792     },
37793
37794     processValue : function(value){
37795         if(this.stripCharsRe){
37796             var newValue = value.replace(this.stripCharsRe, '');
37797             if(newValue !== value){
37798                 this.setRawValue(newValue);
37799                 return newValue;
37800             }
37801         }
37802         return value;
37803     },
37804
37805     filterValidation : function(e){
37806         if(!e.isNavKeyPress()){
37807             this.validationTask.delay(this.validationDelay);
37808         }
37809     },
37810
37811     // private
37812     onKeyUp : function(e){
37813         if(!e.isNavKeyPress()){
37814             this.autoSize();
37815         }
37816     },
37817
37818     /**
37819      * Resets the current field value to the originally-loaded value and clears any validation messages.
37820      *  
37821      */
37822     reset : function(){
37823         Roo.form.TextField.superclass.reset.call(this);
37824        
37825     },
37826
37827     
37828     // private
37829     preFocus : function(){
37830         
37831         if(this.selectOnFocus){
37832             this.el.dom.select();
37833         }
37834     },
37835
37836     
37837     // private
37838     filterKeys : function(e){
37839         var k = e.getKey();
37840         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37841             return;
37842         }
37843         var c = e.getCharCode(), cc = String.fromCharCode(c);
37844         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37845             return;
37846         }
37847         if(!this.maskRe.test(cc)){
37848             e.stopEvent();
37849         }
37850     },
37851
37852     setValue : function(v){
37853         
37854         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37855         
37856         this.autoSize();
37857     },
37858
37859     /**
37860      * Validates a value according to the field's validation rules and marks the field as invalid
37861      * if the validation fails
37862      * @param {Mixed} value The value to validate
37863      * @return {Boolean} True if the value is valid, else false
37864      */
37865     validateValue : function(value){
37866         if(value.length < 1)  { // if it's blank
37867              if(this.allowBlank){
37868                 this.clearInvalid();
37869                 return true;
37870              }else{
37871                 this.markInvalid(this.blankText);
37872                 return false;
37873              }
37874         }
37875         if(value.length < this.minLength){
37876             this.markInvalid(String.format(this.minLengthText, this.minLength));
37877             return false;
37878         }
37879         if(value.length > this.maxLength){
37880             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37881             return false;
37882         }
37883         if(this.vtype){
37884             var vt = Roo.form.VTypes;
37885             if(!vt[this.vtype](value, this)){
37886                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37887                 return false;
37888             }
37889         }
37890         if(typeof this.validator == "function"){
37891             var msg = this.validator(value);
37892             if(msg !== true){
37893                 this.markInvalid(msg);
37894                 return false;
37895             }
37896         }
37897         if(this.regex && !this.regex.test(value)){
37898             this.markInvalid(this.regexText);
37899             return false;
37900         }
37901         return true;
37902     },
37903
37904     /**
37905      * Selects text in this field
37906      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37907      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37908      */
37909     selectText : function(start, end){
37910         var v = this.getRawValue();
37911         if(v.length > 0){
37912             start = start === undefined ? 0 : start;
37913             end = end === undefined ? v.length : end;
37914             var d = this.el.dom;
37915             if(d.setSelectionRange){
37916                 d.setSelectionRange(start, end);
37917             }else if(d.createTextRange){
37918                 var range = d.createTextRange();
37919                 range.moveStart("character", start);
37920                 range.moveEnd("character", v.length-end);
37921                 range.select();
37922             }
37923         }
37924     },
37925
37926     /**
37927      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37928      * This only takes effect if grow = true, and fires the autosize event.
37929      */
37930     autoSize : function(){
37931         if(!this.grow || !this.rendered){
37932             return;
37933         }
37934         if(!this.metrics){
37935             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37936         }
37937         var el = this.el;
37938         var v = el.dom.value;
37939         var d = document.createElement('div');
37940         d.appendChild(document.createTextNode(v));
37941         v = d.innerHTML;
37942         d = null;
37943         v += "&#160;";
37944         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37945         this.el.setWidth(w);
37946         this.fireEvent("autosize", this, w);
37947     },
37948     
37949     // private
37950     SafariOnKeyDown : function(event)
37951     {
37952         // this is a workaround for a password hang bug on chrome/ webkit.
37953         
37954         var isSelectAll = false;
37955         
37956         if(this.el.dom.selectionEnd > 0){
37957             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37958         }
37959         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37960             event.preventDefault();
37961             this.setValue('');
37962             return;
37963         }
37964         
37965         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37966             
37967             event.preventDefault();
37968             // this is very hacky as keydown always get's upper case.
37969             
37970             var cc = String.fromCharCode(event.getCharCode());
37971             
37972             
37973             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37974             
37975         }
37976         
37977         
37978     }
37979 });/*
37980  * Based on:
37981  * Ext JS Library 1.1.1
37982  * Copyright(c) 2006-2007, Ext JS, LLC.
37983  *
37984  * Originally Released Under LGPL - original licence link has changed is not relivant.
37985  *
37986  * Fork - LGPL
37987  * <script type="text/javascript">
37988  */
37989  
37990 /**
37991  * @class Roo.form.Hidden
37992  * @extends Roo.form.TextField
37993  * Simple Hidden element used on forms 
37994  * 
37995  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37996  * 
37997  * @constructor
37998  * Creates a new Hidden form element.
37999  * @param {Object} config Configuration options
38000  */
38001
38002
38003
38004 // easy hidden field...
38005 Roo.form.Hidden = function(config){
38006     Roo.form.Hidden.superclass.constructor.call(this, config);
38007 };
38008   
38009 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
38010     fieldLabel:      '',
38011     inputType:      'hidden',
38012     width:          50,
38013     allowBlank:     true,
38014     labelSeparator: '',
38015     hidden:         true,
38016     itemCls :       'x-form-item-display-none'
38017
38018
38019 });
38020
38021
38022 /*
38023  * Based on:
38024  * Ext JS Library 1.1.1
38025  * Copyright(c) 2006-2007, Ext JS, LLC.
38026  *
38027  * Originally Released Under LGPL - original licence link has changed is not relivant.
38028  *
38029  * Fork - LGPL
38030  * <script type="text/javascript">
38031  */
38032  
38033 /**
38034  * @class Roo.form.TriggerField
38035  * @extends Roo.form.TextField
38036  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
38037  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
38038  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
38039  * for which you can provide a custom implementation.  For example:
38040  * <pre><code>
38041 var trigger = new Roo.form.TriggerField();
38042 trigger.onTriggerClick = myTriggerFn;
38043 trigger.applyTo('my-field');
38044 </code></pre>
38045  *
38046  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
38047  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
38048  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
38049  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
38050  * @constructor
38051  * Create a new TriggerField.
38052  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
38053  * to the base TextField)
38054  */
38055 Roo.form.TriggerField = function(config){
38056     this.mimicing = false;
38057     Roo.form.TriggerField.superclass.constructor.call(this, config);
38058 };
38059
38060 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
38061     /**
38062      * @cfg {String} triggerClass A CSS class to apply to the trigger
38063      */
38064     /**
38065      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38066      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
38067      */
38068     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
38069     /**
38070      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38071      */
38072     hideTrigger:false,
38073
38074     /** @cfg {Boolean} grow @hide */
38075     /** @cfg {Number} growMin @hide */
38076     /** @cfg {Number} growMax @hide */
38077
38078     /**
38079      * @hide 
38080      * @method
38081      */
38082     autoSize: Roo.emptyFn,
38083     // private
38084     monitorTab : true,
38085     // private
38086     deferHeight : true,
38087
38088     
38089     actionMode : 'wrap',
38090     // private
38091     onResize : function(w, h){
38092         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38093         if(typeof w == 'number'){
38094             var x = w - this.trigger.getWidth();
38095             this.el.setWidth(this.adjustWidth('input', x));
38096             this.trigger.setStyle('left', x+'px');
38097         }
38098     },
38099
38100     // private
38101     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38102
38103     // private
38104     getResizeEl : function(){
38105         return this.wrap;
38106     },
38107
38108     // private
38109     getPositionEl : function(){
38110         return this.wrap;
38111     },
38112
38113     // private
38114     alignErrorIcon : function(){
38115         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38116     },
38117
38118     // private
38119     onRender : function(ct, position){
38120         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38121         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38122         this.trigger = this.wrap.createChild(this.triggerConfig ||
38123                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38124         if(this.hideTrigger){
38125             this.trigger.setDisplayed(false);
38126         }
38127         this.initTrigger();
38128         if(!this.width){
38129             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38130         }
38131     },
38132
38133     // private
38134     initTrigger : function(){
38135         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38136         this.trigger.addClassOnOver('x-form-trigger-over');
38137         this.trigger.addClassOnClick('x-form-trigger-click');
38138     },
38139
38140     // private
38141     onDestroy : function(){
38142         if(this.trigger){
38143             this.trigger.removeAllListeners();
38144             this.trigger.remove();
38145         }
38146         if(this.wrap){
38147             this.wrap.remove();
38148         }
38149         Roo.form.TriggerField.superclass.onDestroy.call(this);
38150     },
38151
38152     // private
38153     onFocus : function(){
38154         Roo.form.TriggerField.superclass.onFocus.call(this);
38155         if(!this.mimicing){
38156             this.wrap.addClass('x-trigger-wrap-focus');
38157             this.mimicing = true;
38158             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38159             if(this.monitorTab){
38160                 this.el.on("keydown", this.checkTab, this);
38161             }
38162         }
38163     },
38164
38165     // private
38166     checkTab : function(e){
38167         if(e.getKey() == e.TAB){
38168             this.triggerBlur();
38169         }
38170     },
38171
38172     // private
38173     onBlur : function(){
38174         // do nothing
38175     },
38176
38177     // private
38178     mimicBlur : function(e, t){
38179         if(!this.wrap.contains(t) && this.validateBlur()){
38180             this.triggerBlur();
38181         }
38182     },
38183
38184     // private
38185     triggerBlur : function(){
38186         this.mimicing = false;
38187         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38188         if(this.monitorTab){
38189             this.el.un("keydown", this.checkTab, this);
38190         }
38191         this.wrap.removeClass('x-trigger-wrap-focus');
38192         Roo.form.TriggerField.superclass.onBlur.call(this);
38193     },
38194
38195     // private
38196     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38197     validateBlur : function(e, t){
38198         return true;
38199     },
38200
38201     // private
38202     onDisable : function(){
38203         Roo.form.TriggerField.superclass.onDisable.call(this);
38204         if(this.wrap){
38205             this.wrap.addClass('x-item-disabled');
38206         }
38207     },
38208
38209     // private
38210     onEnable : function(){
38211         Roo.form.TriggerField.superclass.onEnable.call(this);
38212         if(this.wrap){
38213             this.wrap.removeClass('x-item-disabled');
38214         }
38215     },
38216
38217     // private
38218     onShow : function(){
38219         var ae = this.getActionEl();
38220         
38221         if(ae){
38222             ae.dom.style.display = '';
38223             ae.dom.style.visibility = 'visible';
38224         }
38225     },
38226
38227     // private
38228     
38229     onHide : function(){
38230         var ae = this.getActionEl();
38231         ae.dom.style.display = 'none';
38232     },
38233
38234     /**
38235      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38236      * by an implementing function.
38237      * @method
38238      * @param {EventObject} e
38239      */
38240     onTriggerClick : Roo.emptyFn
38241 });
38242
38243 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38244 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38245 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38246 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38247     initComponent : function(){
38248         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38249
38250         this.triggerConfig = {
38251             tag:'span', cls:'x-form-twin-triggers', cn:[
38252             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38253             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38254         ]};
38255     },
38256
38257     getTrigger : function(index){
38258         return this.triggers[index];
38259     },
38260
38261     initTrigger : function(){
38262         var ts = this.trigger.select('.x-form-trigger', true);
38263         this.wrap.setStyle('overflow', 'hidden');
38264         var triggerField = this;
38265         ts.each(function(t, all, index){
38266             t.hide = function(){
38267                 var w = triggerField.wrap.getWidth();
38268                 this.dom.style.display = 'none';
38269                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38270             };
38271             t.show = function(){
38272                 var w = triggerField.wrap.getWidth();
38273                 this.dom.style.display = '';
38274                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38275             };
38276             var triggerIndex = 'Trigger'+(index+1);
38277
38278             if(this['hide'+triggerIndex]){
38279                 t.dom.style.display = 'none';
38280             }
38281             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38282             t.addClassOnOver('x-form-trigger-over');
38283             t.addClassOnClick('x-form-trigger-click');
38284         }, this);
38285         this.triggers = ts.elements;
38286     },
38287
38288     onTrigger1Click : Roo.emptyFn,
38289     onTrigger2Click : Roo.emptyFn
38290 });/*
38291  * Based on:
38292  * Ext JS Library 1.1.1
38293  * Copyright(c) 2006-2007, Ext JS, LLC.
38294  *
38295  * Originally Released Under LGPL - original licence link has changed is not relivant.
38296  *
38297  * Fork - LGPL
38298  * <script type="text/javascript">
38299  */
38300  
38301 /**
38302  * @class Roo.form.TextArea
38303  * @extends Roo.form.TextField
38304  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38305  * support for auto-sizing.
38306  * @constructor
38307  * Creates a new TextArea
38308  * @param {Object} config Configuration options
38309  */
38310 Roo.form.TextArea = function(config){
38311     Roo.form.TextArea.superclass.constructor.call(this, config);
38312     // these are provided exchanges for backwards compat
38313     // minHeight/maxHeight were replaced by growMin/growMax to be
38314     // compatible with TextField growing config values
38315     if(this.minHeight !== undefined){
38316         this.growMin = this.minHeight;
38317     }
38318     if(this.maxHeight !== undefined){
38319         this.growMax = this.maxHeight;
38320     }
38321 };
38322
38323 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38324     /**
38325      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38326      */
38327     growMin : 60,
38328     /**
38329      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38330      */
38331     growMax: 1000,
38332     /**
38333      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38334      * in the field (equivalent to setting overflow: hidden, defaults to false)
38335      */
38336     preventScrollbars: false,
38337     /**
38338      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38339      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38340      */
38341
38342     // private
38343     onRender : function(ct, position){
38344         if(!this.el){
38345             this.defaultAutoCreate = {
38346                 tag: "textarea",
38347                 style:"width:300px;height:60px;",
38348                 autocomplete: "new-password"
38349             };
38350         }
38351         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38352         if(this.grow){
38353             this.textSizeEl = Roo.DomHelper.append(document.body, {
38354                 tag: "pre", cls: "x-form-grow-sizer"
38355             });
38356             if(this.preventScrollbars){
38357                 this.el.setStyle("overflow", "hidden");
38358             }
38359             this.el.setHeight(this.growMin);
38360         }
38361     },
38362
38363     onDestroy : function(){
38364         if(this.textSizeEl){
38365             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38366         }
38367         Roo.form.TextArea.superclass.onDestroy.call(this);
38368     },
38369
38370     // private
38371     onKeyUp : function(e){
38372         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38373             this.autoSize();
38374         }
38375     },
38376
38377     /**
38378      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38379      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38380      */
38381     autoSize : function(){
38382         if(!this.grow || !this.textSizeEl){
38383             return;
38384         }
38385         var el = this.el;
38386         var v = el.dom.value;
38387         var ts = this.textSizeEl;
38388
38389         ts.innerHTML = '';
38390         ts.appendChild(document.createTextNode(v));
38391         v = ts.innerHTML;
38392
38393         Roo.fly(ts).setWidth(this.el.getWidth());
38394         if(v.length < 1){
38395             v = "&#160;&#160;";
38396         }else{
38397             if(Roo.isIE){
38398                 v = v.replace(/\n/g, '<p>&#160;</p>');
38399             }
38400             v += "&#160;\n&#160;";
38401         }
38402         ts.innerHTML = v;
38403         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38404         if(h != this.lastHeight){
38405             this.lastHeight = h;
38406             this.el.setHeight(h);
38407             this.fireEvent("autosize", this, h);
38408         }
38409     }
38410 });/*
38411  * Based on:
38412  * Ext JS Library 1.1.1
38413  * Copyright(c) 2006-2007, Ext JS, LLC.
38414  *
38415  * Originally Released Under LGPL - original licence link has changed is not relivant.
38416  *
38417  * Fork - LGPL
38418  * <script type="text/javascript">
38419  */
38420  
38421
38422 /**
38423  * @class Roo.form.NumberField
38424  * @extends Roo.form.TextField
38425  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38426  * @constructor
38427  * Creates a new NumberField
38428  * @param {Object} config Configuration options
38429  */
38430 Roo.form.NumberField = function(config){
38431     Roo.form.NumberField.superclass.constructor.call(this, config);
38432 };
38433
38434 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38435     /**
38436      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38437      */
38438     fieldClass: "x-form-field x-form-num-field",
38439     /**
38440      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38441      */
38442     allowDecimals : true,
38443     /**
38444      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38445      */
38446     decimalSeparator : ".",
38447     /**
38448      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38449      */
38450     decimalPrecision : 2,
38451     /**
38452      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38453      */
38454     allowNegative : true,
38455     /**
38456      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38457      */
38458     minValue : Number.NEGATIVE_INFINITY,
38459     /**
38460      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38461      */
38462     maxValue : Number.MAX_VALUE,
38463     /**
38464      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38465      */
38466     minText : "The minimum value for this field is {0}",
38467     /**
38468      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38469      */
38470     maxText : "The maximum value for this field is {0}",
38471     /**
38472      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38473      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38474      */
38475     nanText : "{0} is not a valid number",
38476
38477     // private
38478     initEvents : function(){
38479         Roo.form.NumberField.superclass.initEvents.call(this);
38480         var allowed = "0123456789";
38481         if(this.allowDecimals){
38482             allowed += this.decimalSeparator;
38483         }
38484         if(this.allowNegative){
38485             allowed += "-";
38486         }
38487         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38488         var keyPress = function(e){
38489             var k = e.getKey();
38490             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38491                 return;
38492             }
38493             var c = e.getCharCode();
38494             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38495                 e.stopEvent();
38496             }
38497         };
38498         this.el.on("keypress", keyPress, this);
38499     },
38500
38501     // private
38502     validateValue : function(value){
38503         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38504             return false;
38505         }
38506         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38507              return true;
38508         }
38509         var num = this.parseValue(value);
38510         if(isNaN(num)){
38511             this.markInvalid(String.format(this.nanText, value));
38512             return false;
38513         }
38514         if(num < this.minValue){
38515             this.markInvalid(String.format(this.minText, this.minValue));
38516             return false;
38517         }
38518         if(num > this.maxValue){
38519             this.markInvalid(String.format(this.maxText, this.maxValue));
38520             return false;
38521         }
38522         return true;
38523     },
38524
38525     getValue : function(){
38526         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38527     },
38528
38529     // private
38530     parseValue : function(value){
38531         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38532         return isNaN(value) ? '' : value;
38533     },
38534
38535     // private
38536     fixPrecision : function(value){
38537         var nan = isNaN(value);
38538         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38539             return nan ? '' : value;
38540         }
38541         return parseFloat(value).toFixed(this.decimalPrecision);
38542     },
38543
38544     setValue : function(v){
38545         v = this.fixPrecision(v);
38546         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38547     },
38548
38549     // private
38550     decimalPrecisionFcn : function(v){
38551         return Math.floor(v);
38552     },
38553
38554     beforeBlur : function(){
38555         var v = this.parseValue(this.getRawValue());
38556         if(v){
38557             this.setValue(v);
38558         }
38559     }
38560 });/*
38561  * Based on:
38562  * Ext JS Library 1.1.1
38563  * Copyright(c) 2006-2007, Ext JS, LLC.
38564  *
38565  * Originally Released Under LGPL - original licence link has changed is not relivant.
38566  *
38567  * Fork - LGPL
38568  * <script type="text/javascript">
38569  */
38570  
38571 /**
38572  * @class Roo.form.DateField
38573  * @extends Roo.form.TriggerField
38574  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38575 * @constructor
38576 * Create a new DateField
38577 * @param {Object} config
38578  */
38579 Roo.form.DateField = function(config){
38580     Roo.form.DateField.superclass.constructor.call(this, config);
38581     
38582       this.addEvents({
38583          
38584         /**
38585          * @event select
38586          * Fires when a date is selected
38587              * @param {Roo.form.DateField} combo This combo box
38588              * @param {Date} date The date selected
38589              */
38590         'select' : true
38591          
38592     });
38593     
38594     
38595     if(typeof this.minValue == "string") {
38596         this.minValue = this.parseDate(this.minValue);
38597     }
38598     if(typeof this.maxValue == "string") {
38599         this.maxValue = this.parseDate(this.maxValue);
38600     }
38601     this.ddMatch = null;
38602     if(this.disabledDates){
38603         var dd = this.disabledDates;
38604         var re = "(?:";
38605         for(var i = 0; i < dd.length; i++){
38606             re += dd[i];
38607             if(i != dd.length-1) {
38608                 re += "|";
38609             }
38610         }
38611         this.ddMatch = new RegExp(re + ")");
38612     }
38613 };
38614
38615 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38616     /**
38617      * @cfg {String} format
38618      * The default date format string which can be overriden for localization support.  The format must be
38619      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38620      */
38621     format : "m/d/y",
38622     /**
38623      * @cfg {String} altFormats
38624      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38625      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38626      */
38627     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38628     /**
38629      * @cfg {Array} disabledDays
38630      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38631      */
38632     disabledDays : null,
38633     /**
38634      * @cfg {String} disabledDaysText
38635      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38636      */
38637     disabledDaysText : "Disabled",
38638     /**
38639      * @cfg {Array} disabledDates
38640      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38641      * expression so they are very powerful. Some examples:
38642      * <ul>
38643      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38644      * <li>["03/08", "09/16"] would disable those days for every year</li>
38645      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38646      * <li>["03/../2006"] would disable every day in March 2006</li>
38647      * <li>["^03"] would disable every day in every March</li>
38648      * </ul>
38649      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38650      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38651      */
38652     disabledDates : null,
38653     /**
38654      * @cfg {String} disabledDatesText
38655      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38656      */
38657     disabledDatesText : "Disabled",
38658     /**
38659      * @cfg {Date/String} minValue
38660      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38661      * valid format (defaults to null).
38662      */
38663     minValue : null,
38664     /**
38665      * @cfg {Date/String} maxValue
38666      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38667      * valid format (defaults to null).
38668      */
38669     maxValue : null,
38670     /**
38671      * @cfg {String} minText
38672      * The error text to display when the date in the cell is before minValue (defaults to
38673      * 'The date in this field must be after {minValue}').
38674      */
38675     minText : "The date in this field must be equal to or after {0}",
38676     /**
38677      * @cfg {String} maxText
38678      * The error text to display when the date in the cell is after maxValue (defaults to
38679      * 'The date in this field must be before {maxValue}').
38680      */
38681     maxText : "The date in this field must be equal to or before {0}",
38682     /**
38683      * @cfg {String} invalidText
38684      * The error text to display when the date in the field is invalid (defaults to
38685      * '{value} is not a valid date - it must be in the format {format}').
38686      */
38687     invalidText : "{0} is not a valid date - it must be in the format {1}",
38688     /**
38689      * @cfg {String} triggerClass
38690      * An additional CSS class used to style the trigger button.  The trigger will always get the
38691      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38692      * which displays a calendar icon).
38693      */
38694     triggerClass : 'x-form-date-trigger',
38695     
38696
38697     /**
38698      * @cfg {Boolean} useIso
38699      * if enabled, then the date field will use a hidden field to store the 
38700      * real value as iso formated date. default (false)
38701      */ 
38702     useIso : false,
38703     /**
38704      * @cfg {String/Object} autoCreate
38705      * A DomHelper element spec, or true for a default element spec (defaults to
38706      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38707      */ 
38708     // private
38709     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38710     
38711     // private
38712     hiddenField: false,
38713     
38714     onRender : function(ct, position)
38715     {
38716         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38717         if (this.useIso) {
38718             //this.el.dom.removeAttribute('name'); 
38719             Roo.log("Changing name?");
38720             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38721             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38722                     'before', true);
38723             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38724             // prevent input submission
38725             this.hiddenName = this.name;
38726         }
38727             
38728             
38729     },
38730     
38731     // private
38732     validateValue : function(value)
38733     {
38734         value = this.formatDate(value);
38735         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38736             Roo.log('super failed');
38737             return false;
38738         }
38739         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38740              return true;
38741         }
38742         var svalue = value;
38743         value = this.parseDate(value);
38744         if(!value){
38745             Roo.log('parse date failed' + svalue);
38746             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38747             return false;
38748         }
38749         var time = value.getTime();
38750         if(this.minValue && time < this.minValue.getTime()){
38751             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38752             return false;
38753         }
38754         if(this.maxValue && time > this.maxValue.getTime()){
38755             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38756             return false;
38757         }
38758         if(this.disabledDays){
38759             var day = value.getDay();
38760             for(var i = 0; i < this.disabledDays.length; i++) {
38761                 if(day === this.disabledDays[i]){
38762                     this.markInvalid(this.disabledDaysText);
38763                     return false;
38764                 }
38765             }
38766         }
38767         var fvalue = this.formatDate(value);
38768         if(this.ddMatch && this.ddMatch.test(fvalue)){
38769             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38770             return false;
38771         }
38772         return true;
38773     },
38774
38775     // private
38776     // Provides logic to override the default TriggerField.validateBlur which just returns true
38777     validateBlur : function(){
38778         return !this.menu || !this.menu.isVisible();
38779     },
38780     
38781     getName: function()
38782     {
38783         // returns hidden if it's set..
38784         if (!this.rendered) {return ''};
38785         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38786         
38787     },
38788
38789     /**
38790      * Returns the current date value of the date field.
38791      * @return {Date} The date value
38792      */
38793     getValue : function(){
38794         
38795         return  this.hiddenField ?
38796                 this.hiddenField.value :
38797                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38798     },
38799
38800     /**
38801      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38802      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38803      * (the default format used is "m/d/y").
38804      * <br />Usage:
38805      * <pre><code>
38806 //All of these calls set the same date value (May 4, 2006)
38807
38808 //Pass a date object:
38809 var dt = new Date('5/4/06');
38810 dateField.setValue(dt);
38811
38812 //Pass a date string (default format):
38813 dateField.setValue('5/4/06');
38814
38815 //Pass a date string (custom format):
38816 dateField.format = 'Y-m-d';
38817 dateField.setValue('2006-5-4');
38818 </code></pre>
38819      * @param {String/Date} date The date or valid date string
38820      */
38821     setValue : function(date){
38822         if (this.hiddenField) {
38823             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38824         }
38825         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38826         // make sure the value field is always stored as a date..
38827         this.value = this.parseDate(date);
38828         
38829         
38830     },
38831
38832     // private
38833     parseDate : function(value){
38834         if(!value || value instanceof Date){
38835             return value;
38836         }
38837         var v = Date.parseDate(value, this.format);
38838          if (!v && this.useIso) {
38839             v = Date.parseDate(value, 'Y-m-d');
38840         }
38841         if(!v && this.altFormats){
38842             if(!this.altFormatsArray){
38843                 this.altFormatsArray = this.altFormats.split("|");
38844             }
38845             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38846                 v = Date.parseDate(value, this.altFormatsArray[i]);
38847             }
38848         }
38849         return v;
38850     },
38851
38852     // private
38853     formatDate : function(date, fmt){
38854         return (!date || !(date instanceof Date)) ?
38855                date : date.dateFormat(fmt || this.format);
38856     },
38857
38858     // private
38859     menuListeners : {
38860         select: function(m, d){
38861             
38862             this.setValue(d);
38863             this.fireEvent('select', this, d);
38864         },
38865         show : function(){ // retain focus styling
38866             this.onFocus();
38867         },
38868         hide : function(){
38869             this.focus.defer(10, this);
38870             var ml = this.menuListeners;
38871             this.menu.un("select", ml.select,  this);
38872             this.menu.un("show", ml.show,  this);
38873             this.menu.un("hide", ml.hide,  this);
38874         }
38875     },
38876
38877     // private
38878     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38879     onTriggerClick : function(){
38880         if(this.disabled){
38881             return;
38882         }
38883         if(this.menu == null){
38884             this.menu = new Roo.menu.DateMenu();
38885         }
38886         Roo.apply(this.menu.picker,  {
38887             showClear: this.allowBlank,
38888             minDate : this.minValue,
38889             maxDate : this.maxValue,
38890             disabledDatesRE : this.ddMatch,
38891             disabledDatesText : this.disabledDatesText,
38892             disabledDays : this.disabledDays,
38893             disabledDaysText : this.disabledDaysText,
38894             format : this.useIso ? 'Y-m-d' : this.format,
38895             minText : String.format(this.minText, this.formatDate(this.minValue)),
38896             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38897         });
38898         this.menu.on(Roo.apply({}, this.menuListeners, {
38899             scope:this
38900         }));
38901         this.menu.picker.setValue(this.getValue() || new Date());
38902         this.menu.show(this.el, "tl-bl?");
38903     },
38904
38905     beforeBlur : function(){
38906         var v = this.parseDate(this.getRawValue());
38907         if(v){
38908             this.setValue(v);
38909         }
38910     },
38911
38912     /*@
38913      * overide
38914      * 
38915      */
38916     isDirty : function() {
38917         if(this.disabled) {
38918             return false;
38919         }
38920         
38921         if(typeof(this.startValue) === 'undefined'){
38922             return false;
38923         }
38924         
38925         return String(this.getValue()) !== String(this.startValue);
38926         
38927     }
38928 });/*
38929  * Based on:
38930  * Ext JS Library 1.1.1
38931  * Copyright(c) 2006-2007, Ext JS, LLC.
38932  *
38933  * Originally Released Under LGPL - original licence link has changed is not relivant.
38934  *
38935  * Fork - LGPL
38936  * <script type="text/javascript">
38937  */
38938  
38939 /**
38940  * @class Roo.form.MonthField
38941  * @extends Roo.form.TriggerField
38942  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38943 * @constructor
38944 * Create a new MonthField
38945 * @param {Object} config
38946  */
38947 Roo.form.MonthField = function(config){
38948     
38949     Roo.form.MonthField.superclass.constructor.call(this, config);
38950     
38951       this.addEvents({
38952          
38953         /**
38954          * @event select
38955          * Fires when a date is selected
38956              * @param {Roo.form.MonthFieeld} combo This combo box
38957              * @param {Date} date The date selected
38958              */
38959         'select' : true
38960          
38961     });
38962     
38963     
38964     if(typeof this.minValue == "string") {
38965         this.minValue = this.parseDate(this.minValue);
38966     }
38967     if(typeof this.maxValue == "string") {
38968         this.maxValue = this.parseDate(this.maxValue);
38969     }
38970     this.ddMatch = null;
38971     if(this.disabledDates){
38972         var dd = this.disabledDates;
38973         var re = "(?:";
38974         for(var i = 0; i < dd.length; i++){
38975             re += dd[i];
38976             if(i != dd.length-1) {
38977                 re += "|";
38978             }
38979         }
38980         this.ddMatch = new RegExp(re + ")");
38981     }
38982 };
38983
38984 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38985     /**
38986      * @cfg {String} format
38987      * The default date format string which can be overriden for localization support.  The format must be
38988      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38989      */
38990     format : "M Y",
38991     /**
38992      * @cfg {String} altFormats
38993      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38994      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38995      */
38996     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38997     /**
38998      * @cfg {Array} disabledDays
38999      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
39000      */
39001     disabledDays : [0,1,2,3,4,5,6],
39002     /**
39003      * @cfg {String} disabledDaysText
39004      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
39005      */
39006     disabledDaysText : "Disabled",
39007     /**
39008      * @cfg {Array} disabledDates
39009      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
39010      * expression so they are very powerful. Some examples:
39011      * <ul>
39012      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
39013      * <li>["03/08", "09/16"] would disable those days for every year</li>
39014      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
39015      * <li>["03/../2006"] would disable every day in March 2006</li>
39016      * <li>["^03"] would disable every day in every March</li>
39017      * </ul>
39018      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
39019      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
39020      */
39021     disabledDates : null,
39022     /**
39023      * @cfg {String} disabledDatesText
39024      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
39025      */
39026     disabledDatesText : "Disabled",
39027     /**
39028      * @cfg {Date/String} minValue
39029      * The minimum allowed date. Can be either a Javascript date object or a string date in a
39030      * valid format (defaults to null).
39031      */
39032     minValue : null,
39033     /**
39034      * @cfg {Date/String} maxValue
39035      * The maximum allowed date. Can be either a Javascript date object or a string date in a
39036      * valid format (defaults to null).
39037      */
39038     maxValue : null,
39039     /**
39040      * @cfg {String} minText
39041      * The error text to display when the date in the cell is before minValue (defaults to
39042      * 'The date in this field must be after {minValue}').
39043      */
39044     minText : "The date in this field must be equal to or after {0}",
39045     /**
39046      * @cfg {String} maxTextf
39047      * The error text to display when the date in the cell is after maxValue (defaults to
39048      * 'The date in this field must be before {maxValue}').
39049      */
39050     maxText : "The date in this field must be equal to or before {0}",
39051     /**
39052      * @cfg {String} invalidText
39053      * The error text to display when the date in the field is invalid (defaults to
39054      * '{value} is not a valid date - it must be in the format {format}').
39055      */
39056     invalidText : "{0} is not a valid date - it must be in the format {1}",
39057     /**
39058      * @cfg {String} triggerClass
39059      * An additional CSS class used to style the trigger button.  The trigger will always get the
39060      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
39061      * which displays a calendar icon).
39062      */
39063     triggerClass : 'x-form-date-trigger',
39064     
39065
39066     /**
39067      * @cfg {Boolean} useIso
39068      * if enabled, then the date field will use a hidden field to store the 
39069      * real value as iso formated date. default (true)
39070      */ 
39071     useIso : true,
39072     /**
39073      * @cfg {String/Object} autoCreate
39074      * A DomHelper element spec, or true for a default element spec (defaults to
39075      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
39076      */ 
39077     // private
39078     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
39079     
39080     // private
39081     hiddenField: false,
39082     
39083     hideMonthPicker : false,
39084     
39085     onRender : function(ct, position)
39086     {
39087         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39088         if (this.useIso) {
39089             this.el.dom.removeAttribute('name'); 
39090             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39091                     'before', true);
39092             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39093             // prevent input submission
39094             this.hiddenName = this.name;
39095         }
39096             
39097             
39098     },
39099     
39100     // private
39101     validateValue : function(value)
39102     {
39103         value = this.formatDate(value);
39104         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39105             return false;
39106         }
39107         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39108              return true;
39109         }
39110         var svalue = value;
39111         value = this.parseDate(value);
39112         if(!value){
39113             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39114             return false;
39115         }
39116         var time = value.getTime();
39117         if(this.minValue && time < this.minValue.getTime()){
39118             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39119             return false;
39120         }
39121         if(this.maxValue && time > this.maxValue.getTime()){
39122             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39123             return false;
39124         }
39125         /*if(this.disabledDays){
39126             var day = value.getDay();
39127             for(var i = 0; i < this.disabledDays.length; i++) {
39128                 if(day === this.disabledDays[i]){
39129                     this.markInvalid(this.disabledDaysText);
39130                     return false;
39131                 }
39132             }
39133         }
39134         */
39135         var fvalue = this.formatDate(value);
39136         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39137             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39138             return false;
39139         }
39140         */
39141         return true;
39142     },
39143
39144     // private
39145     // Provides logic to override the default TriggerField.validateBlur which just returns true
39146     validateBlur : function(){
39147         return !this.menu || !this.menu.isVisible();
39148     },
39149
39150     /**
39151      * Returns the current date value of the date field.
39152      * @return {Date} The date value
39153      */
39154     getValue : function(){
39155         
39156         
39157         
39158         return  this.hiddenField ?
39159                 this.hiddenField.value :
39160                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39161     },
39162
39163     /**
39164      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39165      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39166      * (the default format used is "m/d/y").
39167      * <br />Usage:
39168      * <pre><code>
39169 //All of these calls set the same date value (May 4, 2006)
39170
39171 //Pass a date object:
39172 var dt = new Date('5/4/06');
39173 monthField.setValue(dt);
39174
39175 //Pass a date string (default format):
39176 monthField.setValue('5/4/06');
39177
39178 //Pass a date string (custom format):
39179 monthField.format = 'Y-m-d';
39180 monthField.setValue('2006-5-4');
39181 </code></pre>
39182      * @param {String/Date} date The date or valid date string
39183      */
39184     setValue : function(date){
39185         Roo.log('month setValue' + date);
39186         // can only be first of month..
39187         
39188         var val = this.parseDate(date);
39189         
39190         if (this.hiddenField) {
39191             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39192         }
39193         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39194         this.value = this.parseDate(date);
39195     },
39196
39197     // private
39198     parseDate : function(value){
39199         if(!value || value instanceof Date){
39200             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39201             return value;
39202         }
39203         var v = Date.parseDate(value, this.format);
39204         if (!v && this.useIso) {
39205             v = Date.parseDate(value, 'Y-m-d');
39206         }
39207         if (v) {
39208             // 
39209             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39210         }
39211         
39212         
39213         if(!v && this.altFormats){
39214             if(!this.altFormatsArray){
39215                 this.altFormatsArray = this.altFormats.split("|");
39216             }
39217             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39218                 v = Date.parseDate(value, this.altFormatsArray[i]);
39219             }
39220         }
39221         return v;
39222     },
39223
39224     // private
39225     formatDate : function(date, fmt){
39226         return (!date || !(date instanceof Date)) ?
39227                date : date.dateFormat(fmt || this.format);
39228     },
39229
39230     // private
39231     menuListeners : {
39232         select: function(m, d){
39233             this.setValue(d);
39234             this.fireEvent('select', this, d);
39235         },
39236         show : function(){ // retain focus styling
39237             this.onFocus();
39238         },
39239         hide : function(){
39240             this.focus.defer(10, this);
39241             var ml = this.menuListeners;
39242             this.menu.un("select", ml.select,  this);
39243             this.menu.un("show", ml.show,  this);
39244             this.menu.un("hide", ml.hide,  this);
39245         }
39246     },
39247     // private
39248     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39249     onTriggerClick : function(){
39250         if(this.disabled){
39251             return;
39252         }
39253         if(this.menu == null){
39254             this.menu = new Roo.menu.DateMenu();
39255            
39256         }
39257         
39258         Roo.apply(this.menu.picker,  {
39259             
39260             showClear: this.allowBlank,
39261             minDate : this.minValue,
39262             maxDate : this.maxValue,
39263             disabledDatesRE : this.ddMatch,
39264             disabledDatesText : this.disabledDatesText,
39265             
39266             format : this.useIso ? 'Y-m-d' : this.format,
39267             minText : String.format(this.minText, this.formatDate(this.minValue)),
39268             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39269             
39270         });
39271          this.menu.on(Roo.apply({}, this.menuListeners, {
39272             scope:this
39273         }));
39274        
39275         
39276         var m = this.menu;
39277         var p = m.picker;
39278         
39279         // hide month picker get's called when we called by 'before hide';
39280         
39281         var ignorehide = true;
39282         p.hideMonthPicker  = function(disableAnim){
39283             if (ignorehide) {
39284                 return;
39285             }
39286              if(this.monthPicker){
39287                 Roo.log("hideMonthPicker called");
39288                 if(disableAnim === true){
39289                     this.monthPicker.hide();
39290                 }else{
39291                     this.monthPicker.slideOut('t', {duration:.2});
39292                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39293                     p.fireEvent("select", this, this.value);
39294                     m.hide();
39295                 }
39296             }
39297         }
39298         
39299         Roo.log('picker set value');
39300         Roo.log(this.getValue());
39301         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39302         m.show(this.el, 'tl-bl?');
39303         ignorehide  = false;
39304         // this will trigger hideMonthPicker..
39305         
39306         
39307         // hidden the day picker
39308         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39309         
39310         
39311         
39312       
39313         
39314         p.showMonthPicker.defer(100, p);
39315     
39316         
39317        
39318     },
39319
39320     beforeBlur : function(){
39321         var v = this.parseDate(this.getRawValue());
39322         if(v){
39323             this.setValue(v);
39324         }
39325     }
39326
39327     /** @cfg {Boolean} grow @hide */
39328     /** @cfg {Number} growMin @hide */
39329     /** @cfg {Number} growMax @hide */
39330     /**
39331      * @hide
39332      * @method autoSize
39333      */
39334 });/*
39335  * Based on:
39336  * Ext JS Library 1.1.1
39337  * Copyright(c) 2006-2007, Ext JS, LLC.
39338  *
39339  * Originally Released Under LGPL - original licence link has changed is not relivant.
39340  *
39341  * Fork - LGPL
39342  * <script type="text/javascript">
39343  */
39344  
39345
39346 /**
39347  * @class Roo.form.ComboBox
39348  * @extends Roo.form.TriggerField
39349  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39350  * @constructor
39351  * Create a new ComboBox.
39352  * @param {Object} config Configuration options
39353  */
39354 Roo.form.ComboBox = function(config){
39355     Roo.form.ComboBox.superclass.constructor.call(this, config);
39356     this.addEvents({
39357         /**
39358          * @event expand
39359          * Fires when the dropdown list is expanded
39360              * @param {Roo.form.ComboBox} combo This combo box
39361              */
39362         'expand' : true,
39363         /**
39364          * @event collapse
39365          * Fires when the dropdown list is collapsed
39366              * @param {Roo.form.ComboBox} combo This combo box
39367              */
39368         'collapse' : true,
39369         /**
39370          * @event beforeselect
39371          * Fires before a list item is selected. Return false to cancel the selection.
39372              * @param {Roo.form.ComboBox} combo This combo box
39373              * @param {Roo.data.Record} record The data record returned from the underlying store
39374              * @param {Number} index The index of the selected item in the dropdown list
39375              */
39376         'beforeselect' : true,
39377         /**
39378          * @event select
39379          * Fires when a list item is selected
39380              * @param {Roo.form.ComboBox} combo This combo box
39381              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39382              * @param {Number} index The index of the selected item in the dropdown list
39383              */
39384         'select' : true,
39385         /**
39386          * @event beforequery
39387          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39388          * The event object passed has these properties:
39389              * @param {Roo.form.ComboBox} combo This combo box
39390              * @param {String} query The query
39391              * @param {Boolean} forceAll true to force "all" query
39392              * @param {Boolean} cancel true to cancel the query
39393              * @param {Object} e The query event object
39394              */
39395         'beforequery': true,
39396          /**
39397          * @event add
39398          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39399              * @param {Roo.form.ComboBox} combo This combo box
39400              */
39401         'add' : true,
39402         /**
39403          * @event edit
39404          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39405              * @param {Roo.form.ComboBox} combo This combo box
39406              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39407              */
39408         'edit' : true
39409         
39410         
39411     });
39412     if(this.transform){
39413         this.allowDomMove = false;
39414         var s = Roo.getDom(this.transform);
39415         if(!this.hiddenName){
39416             this.hiddenName = s.name;
39417         }
39418         if(!this.store){
39419             this.mode = 'local';
39420             var d = [], opts = s.options;
39421             for(var i = 0, len = opts.length;i < len; i++){
39422                 var o = opts[i];
39423                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39424                 if(o.selected) {
39425                     this.value = value;
39426                 }
39427                 d.push([value, o.text]);
39428             }
39429             this.store = new Roo.data.SimpleStore({
39430                 'id': 0,
39431                 fields: ['value', 'text'],
39432                 data : d
39433             });
39434             this.valueField = 'value';
39435             this.displayField = 'text';
39436         }
39437         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39438         if(!this.lazyRender){
39439             this.target = true;
39440             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39441             s.parentNode.removeChild(s); // remove it
39442             this.render(this.el.parentNode);
39443         }else{
39444             s.parentNode.removeChild(s); // remove it
39445         }
39446
39447     }
39448     if (this.store) {
39449         this.store = Roo.factory(this.store, Roo.data);
39450     }
39451     
39452     this.selectedIndex = -1;
39453     if(this.mode == 'local'){
39454         if(config.queryDelay === undefined){
39455             this.queryDelay = 10;
39456         }
39457         if(config.minChars === undefined){
39458             this.minChars = 0;
39459         }
39460     }
39461 };
39462
39463 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39464     /**
39465      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39466      */
39467     /**
39468      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39469      * rendering into an Roo.Editor, defaults to false)
39470      */
39471     /**
39472      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39473      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39474      */
39475     /**
39476      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39477      */
39478     /**
39479      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39480      * the dropdown list (defaults to undefined, with no header element)
39481      */
39482
39483      /**
39484      * @cfg {String/Roo.Template} tpl The template to use to render the output
39485      */
39486      
39487     // private
39488     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39489     /**
39490      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39491      */
39492     listWidth: undefined,
39493     /**
39494      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39495      * mode = 'remote' or 'text' if mode = 'local')
39496      */
39497     displayField: undefined,
39498     /**
39499      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39500      * mode = 'remote' or 'value' if mode = 'local'). 
39501      * Note: use of a valueField requires the user make a selection
39502      * in order for a value to be mapped.
39503      */
39504     valueField: undefined,
39505     
39506     
39507     /**
39508      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39509      * field's data value (defaults to the underlying DOM element's name)
39510      */
39511     hiddenName: undefined,
39512     /**
39513      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39514      */
39515     listClass: '',
39516     /**
39517      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39518      */
39519     selectedClass: 'x-combo-selected',
39520     /**
39521      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39522      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39523      * which displays a downward arrow icon).
39524      */
39525     triggerClass : 'x-form-arrow-trigger',
39526     /**
39527      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39528      */
39529     shadow:'sides',
39530     /**
39531      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39532      * anchor positions (defaults to 'tl-bl')
39533      */
39534     listAlign: 'tl-bl?',
39535     /**
39536      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39537      */
39538     maxHeight: 300,
39539     /**
39540      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39541      * query specified by the allQuery config option (defaults to 'query')
39542      */
39543     triggerAction: 'query',
39544     /**
39545      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39546      * (defaults to 4, does not apply if editable = false)
39547      */
39548     minChars : 4,
39549     /**
39550      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39551      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39552      */
39553     typeAhead: false,
39554     /**
39555      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39556      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39557      */
39558     queryDelay: 500,
39559     /**
39560      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39561      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39562      */
39563     pageSize: 0,
39564     /**
39565      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39566      * when editable = true (defaults to false)
39567      */
39568     selectOnFocus:false,
39569     /**
39570      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39571      */
39572     queryParam: 'query',
39573     /**
39574      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39575      * when mode = 'remote' (defaults to 'Loading...')
39576      */
39577     loadingText: 'Loading...',
39578     /**
39579      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39580      */
39581     resizable: false,
39582     /**
39583      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39584      */
39585     handleHeight : 8,
39586     /**
39587      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39588      * traditional select (defaults to true)
39589      */
39590     editable: true,
39591     /**
39592      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39593      */
39594     allQuery: '',
39595     /**
39596      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39597      */
39598     mode: 'remote',
39599     /**
39600      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39601      * listWidth has a higher value)
39602      */
39603     minListWidth : 70,
39604     /**
39605      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39606      * allow the user to set arbitrary text into the field (defaults to false)
39607      */
39608     forceSelection:false,
39609     /**
39610      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39611      * if typeAhead = true (defaults to 250)
39612      */
39613     typeAheadDelay : 250,
39614     /**
39615      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39616      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39617      */
39618     valueNotFoundText : undefined,
39619     /**
39620      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39621      */
39622     blockFocus : false,
39623     
39624     /**
39625      * @cfg {Boolean} disableClear Disable showing of clear button.
39626      */
39627     disableClear : false,
39628     /**
39629      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39630      */
39631     alwaysQuery : false,
39632     
39633     //private
39634     addicon : false,
39635     editicon: false,
39636     
39637     // element that contains real text value.. (when hidden is used..)
39638      
39639     // private
39640     onRender : function(ct, position){
39641         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39642         if(this.hiddenName){
39643             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39644                     'before', true);
39645             this.hiddenField.value =
39646                 this.hiddenValue !== undefined ? this.hiddenValue :
39647                 this.value !== undefined ? this.value : '';
39648
39649             // prevent input submission
39650             this.el.dom.removeAttribute('name');
39651              
39652              
39653         }
39654         if(Roo.isGecko){
39655             this.el.dom.setAttribute('autocomplete', 'off');
39656         }
39657
39658         var cls = 'x-combo-list';
39659
39660         this.list = new Roo.Layer({
39661             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39662         });
39663
39664         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39665         this.list.setWidth(lw);
39666         this.list.swallowEvent('mousewheel');
39667         this.assetHeight = 0;
39668
39669         if(this.title){
39670             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39671             this.assetHeight += this.header.getHeight();
39672         }
39673
39674         this.innerList = this.list.createChild({cls:cls+'-inner'});
39675         this.innerList.on('mouseover', this.onViewOver, this);
39676         this.innerList.on('mousemove', this.onViewMove, this);
39677         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39678         
39679         if(this.allowBlank && !this.pageSize && !this.disableClear){
39680             this.footer = this.list.createChild({cls:cls+'-ft'});
39681             this.pageTb = new Roo.Toolbar(this.footer);
39682            
39683         }
39684         if(this.pageSize){
39685             this.footer = this.list.createChild({cls:cls+'-ft'});
39686             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39687                     {pageSize: this.pageSize});
39688             
39689         }
39690         
39691         if (this.pageTb && this.allowBlank && !this.disableClear) {
39692             var _this = this;
39693             this.pageTb.add(new Roo.Toolbar.Fill(), {
39694                 cls: 'x-btn-icon x-btn-clear',
39695                 text: '&#160;',
39696                 handler: function()
39697                 {
39698                     _this.collapse();
39699                     _this.clearValue();
39700                     _this.onSelect(false, -1);
39701                 }
39702             });
39703         }
39704         if (this.footer) {
39705             this.assetHeight += this.footer.getHeight();
39706         }
39707         
39708
39709         if(!this.tpl){
39710             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39711         }
39712
39713         this.view = new Roo.View(this.innerList, this.tpl, {
39714             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39715         });
39716
39717         this.view.on('click', this.onViewClick, this);
39718
39719         this.store.on('beforeload', this.onBeforeLoad, this);
39720         this.store.on('load', this.onLoad, this);
39721         this.store.on('loadexception', this.onLoadException, this);
39722
39723         if(this.resizable){
39724             this.resizer = new Roo.Resizable(this.list,  {
39725                pinned:true, handles:'se'
39726             });
39727             this.resizer.on('resize', function(r, w, h){
39728                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39729                 this.listWidth = w;
39730                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39731                 this.restrictHeight();
39732             }, this);
39733             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39734         }
39735         if(!this.editable){
39736             this.editable = true;
39737             this.setEditable(false);
39738         }  
39739         
39740         
39741         if (typeof(this.events.add.listeners) != 'undefined') {
39742             
39743             this.addicon = this.wrap.createChild(
39744                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39745        
39746             this.addicon.on('click', function(e) {
39747                 this.fireEvent('add', this);
39748             }, this);
39749         }
39750         if (typeof(this.events.edit.listeners) != 'undefined') {
39751             
39752             this.editicon = this.wrap.createChild(
39753                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39754             if (this.addicon) {
39755                 this.editicon.setStyle('margin-left', '40px');
39756             }
39757             this.editicon.on('click', function(e) {
39758                 
39759                 // we fire even  if inothing is selected..
39760                 this.fireEvent('edit', this, this.lastData );
39761                 
39762             }, this);
39763         }
39764         
39765         
39766         
39767     },
39768
39769     // private
39770     initEvents : function(){
39771         Roo.form.ComboBox.superclass.initEvents.call(this);
39772
39773         this.keyNav = new Roo.KeyNav(this.el, {
39774             "up" : function(e){
39775                 this.inKeyMode = true;
39776                 this.selectPrev();
39777             },
39778
39779             "down" : function(e){
39780                 if(!this.isExpanded()){
39781                     this.onTriggerClick();
39782                 }else{
39783                     this.inKeyMode = true;
39784                     this.selectNext();
39785                 }
39786             },
39787
39788             "enter" : function(e){
39789                 this.onViewClick();
39790                 //return true;
39791             },
39792
39793             "esc" : function(e){
39794                 this.collapse();
39795             },
39796
39797             "tab" : function(e){
39798                 this.onViewClick(false);
39799                 this.fireEvent("specialkey", this, e);
39800                 return true;
39801             },
39802
39803             scope : this,
39804
39805             doRelay : function(foo, bar, hname){
39806                 if(hname == 'down' || this.scope.isExpanded()){
39807                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39808                 }
39809                 return true;
39810             },
39811
39812             forceKeyDown: true
39813         });
39814         this.queryDelay = Math.max(this.queryDelay || 10,
39815                 this.mode == 'local' ? 10 : 250);
39816         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39817         if(this.typeAhead){
39818             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39819         }
39820         if(this.editable !== false){
39821             this.el.on("keyup", this.onKeyUp, this);
39822         }
39823         if(this.forceSelection){
39824             this.on('blur', this.doForce, this);
39825         }
39826     },
39827
39828     onDestroy : function(){
39829         if(this.view){
39830             this.view.setStore(null);
39831             this.view.el.removeAllListeners();
39832             this.view.el.remove();
39833             this.view.purgeListeners();
39834         }
39835         if(this.list){
39836             this.list.destroy();
39837         }
39838         if(this.store){
39839             this.store.un('beforeload', this.onBeforeLoad, this);
39840             this.store.un('load', this.onLoad, this);
39841             this.store.un('loadexception', this.onLoadException, this);
39842         }
39843         Roo.form.ComboBox.superclass.onDestroy.call(this);
39844     },
39845
39846     // private
39847     fireKey : function(e){
39848         if(e.isNavKeyPress() && !this.list.isVisible()){
39849             this.fireEvent("specialkey", this, e);
39850         }
39851     },
39852
39853     // private
39854     onResize: function(w, h){
39855         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39856         
39857         if(typeof w != 'number'){
39858             // we do not handle it!?!?
39859             return;
39860         }
39861         var tw = this.trigger.getWidth();
39862         tw += this.addicon ? this.addicon.getWidth() : 0;
39863         tw += this.editicon ? this.editicon.getWidth() : 0;
39864         var x = w - tw;
39865         this.el.setWidth( this.adjustWidth('input', x));
39866             
39867         this.trigger.setStyle('left', x+'px');
39868         
39869         if(this.list && this.listWidth === undefined){
39870             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39871             this.list.setWidth(lw);
39872             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39873         }
39874         
39875     
39876         
39877     },
39878
39879     /**
39880      * Allow or prevent the user from directly editing the field text.  If false is passed,
39881      * the user will only be able to select from the items defined in the dropdown list.  This method
39882      * is the runtime equivalent of setting the 'editable' config option at config time.
39883      * @param {Boolean} value True to allow the user to directly edit the field text
39884      */
39885     setEditable : function(value){
39886         if(value == this.editable){
39887             return;
39888         }
39889         this.editable = value;
39890         if(!value){
39891             this.el.dom.setAttribute('readOnly', true);
39892             this.el.on('mousedown', this.onTriggerClick,  this);
39893             this.el.addClass('x-combo-noedit');
39894         }else{
39895             this.el.dom.setAttribute('readOnly', false);
39896             this.el.un('mousedown', this.onTriggerClick,  this);
39897             this.el.removeClass('x-combo-noedit');
39898         }
39899     },
39900
39901     // private
39902     onBeforeLoad : function(){
39903         if(!this.hasFocus){
39904             return;
39905         }
39906         this.innerList.update(this.loadingText ?
39907                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39908         this.restrictHeight();
39909         this.selectedIndex = -1;
39910     },
39911
39912     // private
39913     onLoad : function(){
39914         if(!this.hasFocus){
39915             return;
39916         }
39917         if(this.store.getCount() > 0){
39918             this.expand();
39919             this.restrictHeight();
39920             if(this.lastQuery == this.allQuery){
39921                 if(this.editable){
39922                     this.el.dom.select();
39923                 }
39924                 if(!this.selectByValue(this.value, true)){
39925                     this.select(0, true);
39926                 }
39927             }else{
39928                 this.selectNext();
39929                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39930                     this.taTask.delay(this.typeAheadDelay);
39931                 }
39932             }
39933         }else{
39934             this.onEmptyResults();
39935         }
39936         //this.el.focus();
39937     },
39938     // private
39939     onLoadException : function()
39940     {
39941         this.collapse();
39942         Roo.log(this.store.reader.jsonData);
39943         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39944             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39945         }
39946         
39947         
39948     },
39949     // private
39950     onTypeAhead : function(){
39951         if(this.store.getCount() > 0){
39952             var r = this.store.getAt(0);
39953             var newValue = r.data[this.displayField];
39954             var len = newValue.length;
39955             var selStart = this.getRawValue().length;
39956             if(selStart != len){
39957                 this.setRawValue(newValue);
39958                 this.selectText(selStart, newValue.length);
39959             }
39960         }
39961     },
39962
39963     // private
39964     onSelect : function(record, index){
39965         if(this.fireEvent('beforeselect', this, record, index) !== false){
39966             this.setFromData(index > -1 ? record.data : false);
39967             this.collapse();
39968             this.fireEvent('select', this, record, index);
39969         }
39970     },
39971
39972     /**
39973      * Returns the currently selected field value or empty string if no value is set.
39974      * @return {String} value The selected value
39975      */
39976     getValue : function(){
39977         if(this.valueField){
39978             return typeof this.value != 'undefined' ? this.value : '';
39979         }
39980         return Roo.form.ComboBox.superclass.getValue.call(this);
39981     },
39982
39983     /**
39984      * Clears any text/value currently set in the field
39985      */
39986     clearValue : function(){
39987         if(this.hiddenField){
39988             this.hiddenField.value = '';
39989         }
39990         this.value = '';
39991         this.setRawValue('');
39992         this.lastSelectionText = '';
39993         
39994     },
39995
39996     /**
39997      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39998      * will be displayed in the field.  If the value does not match the data value of an existing item,
39999      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
40000      * Otherwise the field will be blank (although the value will still be set).
40001      * @param {String} value The value to match
40002      */
40003     setValue : function(v){
40004         var text = v;
40005         if(this.valueField){
40006             var r = this.findRecord(this.valueField, v);
40007             if(r){
40008                 text = r.data[this.displayField];
40009             }else if(this.valueNotFoundText !== undefined){
40010                 text = this.valueNotFoundText;
40011             }
40012         }
40013         this.lastSelectionText = text;
40014         if(this.hiddenField){
40015             this.hiddenField.value = v;
40016         }
40017         Roo.form.ComboBox.superclass.setValue.call(this, text);
40018         this.value = v;
40019     },
40020     /**
40021      * @property {Object} the last set data for the element
40022      */
40023     
40024     lastData : false,
40025     /**
40026      * Sets the value of the field based on a object which is related to the record format for the store.
40027      * @param {Object} value the value to set as. or false on reset?
40028      */
40029     setFromData : function(o){
40030         var dv = ''; // display value
40031         var vv = ''; // value value..
40032         this.lastData = o;
40033         if (this.displayField) {
40034             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
40035         } else {
40036             // this is an error condition!!!
40037             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
40038         }
40039         
40040         if(this.valueField){
40041             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
40042         }
40043         if(this.hiddenField){
40044             this.hiddenField.value = vv;
40045             
40046             this.lastSelectionText = dv;
40047             Roo.form.ComboBox.superclass.setValue.call(this, dv);
40048             this.value = vv;
40049             return;
40050         }
40051         // no hidden field.. - we store the value in 'value', but still display
40052         // display field!!!!
40053         this.lastSelectionText = dv;
40054         Roo.form.ComboBox.superclass.setValue.call(this, dv);
40055         this.value = vv;
40056         
40057         
40058     },
40059     // private
40060     reset : function(){
40061         // overridden so that last data is reset..
40062         this.setValue(this.resetValue);
40063         this.clearInvalid();
40064         this.lastData = false;
40065         if (this.view) {
40066             this.view.clearSelections();
40067         }
40068     },
40069     // private
40070     findRecord : function(prop, value){
40071         var record;
40072         if(this.store.getCount() > 0){
40073             this.store.each(function(r){
40074                 if(r.data[prop] == value){
40075                     record = r;
40076                     return false;
40077                 }
40078                 return true;
40079             });
40080         }
40081         return record;
40082     },
40083     
40084     getName: function()
40085     {
40086         // returns hidden if it's set..
40087         if (!this.rendered) {return ''};
40088         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40089         
40090     },
40091     // private
40092     onViewMove : function(e, t){
40093         this.inKeyMode = false;
40094     },
40095
40096     // private
40097     onViewOver : function(e, t){
40098         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40099             return;
40100         }
40101         var item = this.view.findItemFromChild(t);
40102         if(item){
40103             var index = this.view.indexOf(item);
40104             this.select(index, false);
40105         }
40106     },
40107
40108     // private
40109     onViewClick : function(doFocus)
40110     {
40111         var index = this.view.getSelectedIndexes()[0];
40112         var r = this.store.getAt(index);
40113         if(r){
40114             this.onSelect(r, index);
40115         }
40116         if(doFocus !== false && !this.blockFocus){
40117             this.el.focus();
40118         }
40119     },
40120
40121     // private
40122     restrictHeight : function(){
40123         this.innerList.dom.style.height = '';
40124         var inner = this.innerList.dom;
40125         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40126         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40127         this.list.beginUpdate();
40128         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40129         this.list.alignTo(this.el, this.listAlign);
40130         this.list.endUpdate();
40131     },
40132
40133     // private
40134     onEmptyResults : function(){
40135         this.collapse();
40136     },
40137
40138     /**
40139      * Returns true if the dropdown list is expanded, else false.
40140      */
40141     isExpanded : function(){
40142         return this.list.isVisible();
40143     },
40144
40145     /**
40146      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40147      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40148      * @param {String} value The data value of the item to select
40149      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40150      * selected item if it is not currently in view (defaults to true)
40151      * @return {Boolean} True if the value matched an item in the list, else false
40152      */
40153     selectByValue : function(v, scrollIntoView){
40154         if(v !== undefined && v !== null){
40155             var r = this.findRecord(this.valueField || this.displayField, v);
40156             if(r){
40157                 this.select(this.store.indexOf(r), scrollIntoView);
40158                 return true;
40159             }
40160         }
40161         return false;
40162     },
40163
40164     /**
40165      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40166      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40167      * @param {Number} index The zero-based index of the list item to select
40168      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40169      * selected item if it is not currently in view (defaults to true)
40170      */
40171     select : function(index, scrollIntoView){
40172         this.selectedIndex = index;
40173         this.view.select(index);
40174         if(scrollIntoView !== false){
40175             var el = this.view.getNode(index);
40176             if(el){
40177                 this.innerList.scrollChildIntoView(el, false);
40178             }
40179         }
40180     },
40181
40182     // private
40183     selectNext : function(){
40184         var ct = this.store.getCount();
40185         if(ct > 0){
40186             if(this.selectedIndex == -1){
40187                 this.select(0);
40188             }else if(this.selectedIndex < ct-1){
40189                 this.select(this.selectedIndex+1);
40190             }
40191         }
40192     },
40193
40194     // private
40195     selectPrev : function(){
40196         var ct = this.store.getCount();
40197         if(ct > 0){
40198             if(this.selectedIndex == -1){
40199                 this.select(0);
40200             }else if(this.selectedIndex != 0){
40201                 this.select(this.selectedIndex-1);
40202             }
40203         }
40204     },
40205
40206     // private
40207     onKeyUp : function(e){
40208         if(this.editable !== false && !e.isSpecialKey()){
40209             this.lastKey = e.getKey();
40210             this.dqTask.delay(this.queryDelay);
40211         }
40212     },
40213
40214     // private
40215     validateBlur : function(){
40216         return !this.list || !this.list.isVisible();   
40217     },
40218
40219     // private
40220     initQuery : function(){
40221         this.doQuery(this.getRawValue());
40222     },
40223
40224     // private
40225     doForce : function(){
40226         if(this.el.dom.value.length > 0){
40227             this.el.dom.value =
40228                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40229              
40230         }
40231     },
40232
40233     /**
40234      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40235      * query allowing the query action to be canceled if needed.
40236      * @param {String} query The SQL query to execute
40237      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40238      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40239      * saved in the current store (defaults to false)
40240      */
40241     doQuery : function(q, forceAll){
40242         if(q === undefined || q === null){
40243             q = '';
40244         }
40245         var qe = {
40246             query: q,
40247             forceAll: forceAll,
40248             combo: this,
40249             cancel:false
40250         };
40251         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40252             return false;
40253         }
40254         q = qe.query;
40255         forceAll = qe.forceAll;
40256         if(forceAll === true || (q.length >= this.minChars)){
40257             if(this.lastQuery != q || this.alwaysQuery){
40258                 this.lastQuery = q;
40259                 if(this.mode == 'local'){
40260                     this.selectedIndex = -1;
40261                     if(forceAll){
40262                         this.store.clearFilter();
40263                     }else{
40264                         this.store.filter(this.displayField, q);
40265                     }
40266                     this.onLoad();
40267                 }else{
40268                     this.store.baseParams[this.queryParam] = q;
40269                     this.store.load({
40270                         params: this.getParams(q)
40271                     });
40272                     this.expand();
40273                 }
40274             }else{
40275                 this.selectedIndex = -1;
40276                 this.onLoad();   
40277             }
40278         }
40279     },
40280
40281     // private
40282     getParams : function(q){
40283         var p = {};
40284         //p[this.queryParam] = q;
40285         if(this.pageSize){
40286             p.start = 0;
40287             p.limit = this.pageSize;
40288         }
40289         return p;
40290     },
40291
40292     /**
40293      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40294      */
40295     collapse : function(){
40296         if(!this.isExpanded()){
40297             return;
40298         }
40299         this.list.hide();
40300         Roo.get(document).un('mousedown', this.collapseIf, this);
40301         Roo.get(document).un('mousewheel', this.collapseIf, this);
40302         if (!this.editable) {
40303             Roo.get(document).un('keydown', this.listKeyPress, this);
40304         }
40305         this.fireEvent('collapse', this);
40306     },
40307
40308     // private
40309     collapseIf : function(e){
40310         if(!e.within(this.wrap) && !e.within(this.list)){
40311             this.collapse();
40312         }
40313     },
40314
40315     /**
40316      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40317      */
40318     expand : function(){
40319         if(this.isExpanded() || !this.hasFocus){
40320             return;
40321         }
40322         this.list.alignTo(this.el, this.listAlign);
40323         this.list.show();
40324         Roo.get(document).on('mousedown', this.collapseIf, this);
40325         Roo.get(document).on('mousewheel', this.collapseIf, this);
40326         if (!this.editable) {
40327             Roo.get(document).on('keydown', this.listKeyPress, this);
40328         }
40329         
40330         this.fireEvent('expand', this);
40331     },
40332
40333     // private
40334     // Implements the default empty TriggerField.onTriggerClick function
40335     onTriggerClick : function(){
40336         if(this.disabled){
40337             return;
40338         }
40339         if(this.isExpanded()){
40340             this.collapse();
40341             if (!this.blockFocus) {
40342                 this.el.focus();
40343             }
40344             
40345         }else {
40346             this.hasFocus = true;
40347             if(this.triggerAction == 'all') {
40348                 this.doQuery(this.allQuery, true);
40349             } else {
40350                 this.doQuery(this.getRawValue());
40351             }
40352             if (!this.blockFocus) {
40353                 this.el.focus();
40354             }
40355         }
40356     },
40357     listKeyPress : function(e)
40358     {
40359         //Roo.log('listkeypress');
40360         // scroll to first matching element based on key pres..
40361         if (e.isSpecialKey()) {
40362             return false;
40363         }
40364         var k = String.fromCharCode(e.getKey()).toUpperCase();
40365         //Roo.log(k);
40366         var match  = false;
40367         var csel = this.view.getSelectedNodes();
40368         var cselitem = false;
40369         if (csel.length) {
40370             var ix = this.view.indexOf(csel[0]);
40371             cselitem  = this.store.getAt(ix);
40372             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40373                 cselitem = false;
40374             }
40375             
40376         }
40377         
40378         this.store.each(function(v) { 
40379             if (cselitem) {
40380                 // start at existing selection.
40381                 if (cselitem.id == v.id) {
40382                     cselitem = false;
40383                 }
40384                 return;
40385             }
40386                 
40387             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40388                 match = this.store.indexOf(v);
40389                 return false;
40390             }
40391         }, this);
40392         
40393         if (match === false) {
40394             return true; // no more action?
40395         }
40396         // scroll to?
40397         this.view.select(match);
40398         var sn = Roo.get(this.view.getSelectedNodes()[0]);
40399         sn.scrollIntoView(sn.dom.parentNode, false);
40400     }
40401
40402     /** 
40403     * @cfg {Boolean} grow 
40404     * @hide 
40405     */
40406     /** 
40407     * @cfg {Number} growMin 
40408     * @hide 
40409     */
40410     /** 
40411     * @cfg {Number} growMax 
40412     * @hide 
40413     */
40414     /**
40415      * @hide
40416      * @method autoSize
40417      */
40418 });/*
40419  * Copyright(c) 2010-2012, Roo J Solutions Limited
40420  *
40421  * Licence LGPL
40422  *
40423  */
40424
40425 /**
40426  * @class Roo.form.ComboBoxArray
40427  * @extends Roo.form.TextField
40428  * A facebook style adder... for lists of email / people / countries  etc...
40429  * pick multiple items from a combo box, and shows each one.
40430  *
40431  *  Fred [x]  Brian [x]  [Pick another |v]
40432  *
40433  *
40434  *  For this to work: it needs various extra information
40435  *    - normal combo problay has
40436  *      name, hiddenName
40437  *    + displayField, valueField
40438  *
40439  *    For our purpose...
40440  *
40441  *
40442  *   If we change from 'extends' to wrapping...
40443  *   
40444  *  
40445  *
40446  
40447  
40448  * @constructor
40449  * Create a new ComboBoxArray.
40450  * @param {Object} config Configuration options
40451  */
40452  
40453
40454 Roo.form.ComboBoxArray = function(config)
40455 {
40456     this.addEvents({
40457         /**
40458          * @event beforeremove
40459          * Fires before remove the value from the list
40460              * @param {Roo.form.ComboBoxArray} _self This combo box array
40461              * @param {Roo.form.ComboBoxArray.Item} item removed item
40462              */
40463         'beforeremove' : true,
40464         /**
40465          * @event remove
40466          * Fires when remove the value from the list
40467              * @param {Roo.form.ComboBoxArray} _self This combo box array
40468              * @param {Roo.form.ComboBoxArray.Item} item removed item
40469              */
40470         'remove' : true
40471         
40472         
40473     });
40474     
40475     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40476     
40477     this.items = new Roo.util.MixedCollection(false);
40478     
40479     // construct the child combo...
40480     
40481     
40482     
40483     
40484    
40485     
40486 }
40487
40488  
40489 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40490
40491     /**
40492      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40493      */
40494     
40495     lastData : false,
40496     
40497     // behavies liek a hiddne field
40498     inputType:      'hidden',
40499     /**
40500      * @cfg {Number} width The width of the box that displays the selected element
40501      */ 
40502     width:          300,
40503
40504     
40505     
40506     /**
40507      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40508      */
40509     name : false,
40510     /**
40511      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40512      */
40513     hiddenName : false,
40514     
40515     
40516     // private the array of items that are displayed..
40517     items  : false,
40518     // private - the hidden field el.
40519     hiddenEl : false,
40520     // private - the filed el..
40521     el : false,
40522     
40523     //validateValue : function() { return true; }, // all values are ok!
40524     //onAddClick: function() { },
40525     
40526     onRender : function(ct, position) 
40527     {
40528         
40529         // create the standard hidden element
40530         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40531         
40532         
40533         // give fake names to child combo;
40534         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40535         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40536         
40537         this.combo = Roo.factory(this.combo, Roo.form);
40538         this.combo.onRender(ct, position);
40539         if (typeof(this.combo.width) != 'undefined') {
40540             this.combo.onResize(this.combo.width,0);
40541         }
40542         
40543         this.combo.initEvents();
40544         
40545         // assigned so form know we need to do this..
40546         this.store          = this.combo.store;
40547         this.valueField     = this.combo.valueField;
40548         this.displayField   = this.combo.displayField ;
40549         
40550         
40551         this.combo.wrap.addClass('x-cbarray-grp');
40552         
40553         var cbwrap = this.combo.wrap.createChild(
40554             {tag: 'div', cls: 'x-cbarray-cb'},
40555             this.combo.el.dom
40556         );
40557         
40558              
40559         this.hiddenEl = this.combo.wrap.createChild({
40560             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40561         });
40562         this.el = this.combo.wrap.createChild({
40563             tag: 'input',  type:'hidden' , name: this.name, value : ''
40564         });
40565          //   this.el.dom.removeAttribute("name");
40566         
40567         
40568         this.outerWrap = this.combo.wrap;
40569         this.wrap = cbwrap;
40570         
40571         this.outerWrap.setWidth(this.width);
40572         this.outerWrap.dom.removeChild(this.el.dom);
40573         
40574         this.wrap.dom.appendChild(this.el.dom);
40575         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40576         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40577         
40578         this.combo.trigger.setStyle('position','relative');
40579         this.combo.trigger.setStyle('left', '0px');
40580         this.combo.trigger.setStyle('top', '2px');
40581         
40582         this.combo.el.setStyle('vertical-align', 'text-bottom');
40583         
40584         //this.trigger.setStyle('vertical-align', 'top');
40585         
40586         // this should use the code from combo really... on('add' ....)
40587         if (this.adder) {
40588             
40589         
40590             this.adder = this.outerWrap.createChild(
40591                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40592             var _t = this;
40593             this.adder.on('click', function(e) {
40594                 _t.fireEvent('adderclick', this, e);
40595             }, _t);
40596         }
40597         //var _t = this;
40598         //this.adder.on('click', this.onAddClick, _t);
40599         
40600         
40601         this.combo.on('select', function(cb, rec, ix) {
40602             this.addItem(rec.data);
40603             
40604             cb.setValue('');
40605             cb.el.dom.value = '';
40606             //cb.lastData = rec.data;
40607             // add to list
40608             
40609         }, this);
40610         
40611         
40612     },
40613     
40614     
40615     getName: function()
40616     {
40617         // returns hidden if it's set..
40618         if (!this.rendered) {return ''};
40619         return  this.hiddenName ? this.hiddenName : this.name;
40620         
40621     },
40622     
40623     
40624     onResize: function(w, h){
40625         
40626         return;
40627         // not sure if this is needed..
40628         //this.combo.onResize(w,h);
40629         
40630         if(typeof w != 'number'){
40631             // we do not handle it!?!?
40632             return;
40633         }
40634         var tw = this.combo.trigger.getWidth();
40635         tw += this.addicon ? this.addicon.getWidth() : 0;
40636         tw += this.editicon ? this.editicon.getWidth() : 0;
40637         var x = w - tw;
40638         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40639             
40640         this.combo.trigger.setStyle('left', '0px');
40641         
40642         if(this.list && this.listWidth === undefined){
40643             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40644             this.list.setWidth(lw);
40645             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40646         }
40647         
40648     
40649         
40650     },
40651     
40652     addItem: function(rec)
40653     {
40654         var valueField = this.combo.valueField;
40655         var displayField = this.combo.displayField;
40656         if (this.items.indexOfKey(rec[valueField]) > -1) {
40657             //console.log("GOT " + rec.data.id);
40658             return;
40659         }
40660         
40661         var x = new Roo.form.ComboBoxArray.Item({
40662             //id : rec[this.idField],
40663             data : rec,
40664             displayField : displayField ,
40665             tipField : displayField ,
40666             cb : this
40667         });
40668         // use the 
40669         this.items.add(rec[valueField],x);
40670         // add it before the element..
40671         this.updateHiddenEl();
40672         x.render(this.outerWrap, this.wrap.dom);
40673         // add the image handler..
40674     },
40675     
40676     updateHiddenEl : function()
40677     {
40678         this.validate();
40679         if (!this.hiddenEl) {
40680             return;
40681         }
40682         var ar = [];
40683         var idField = this.combo.valueField;
40684         
40685         this.items.each(function(f) {
40686             ar.push(f.data[idField]);
40687            
40688         });
40689         this.hiddenEl.dom.value = ar.join(',');
40690         this.validate();
40691     },
40692     
40693     reset : function()
40694     {
40695         this.items.clear();
40696         
40697         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
40698            el.remove();
40699         });
40700         
40701         this.el.dom.value = '';
40702         if (this.hiddenEl) {
40703             this.hiddenEl.dom.value = '';
40704         }
40705         
40706     },
40707     getValue: function()
40708     {
40709         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40710     },
40711     setValue: function(v) // not a valid action - must use addItems..
40712     {
40713          
40714         this.reset();
40715         
40716         
40717         
40718         if (this.store.isLocal && (typeof(v) == 'string')) {
40719             // then we can use the store to find the values..
40720             // comma seperated at present.. this needs to allow JSON based encoding..
40721             this.hiddenEl.value  = v;
40722             var v_ar = [];
40723             Roo.each(v.split(','), function(k) {
40724                 Roo.log("CHECK " + this.valueField + ',' + k);
40725                 var li = this.store.query(this.valueField, k);
40726                 if (!li.length) {
40727                     return;
40728                 }
40729                 var add = {};
40730                 add[this.valueField] = k;
40731                 add[this.displayField] = li.item(0).data[this.displayField];
40732                 
40733                 this.addItem(add);
40734             }, this) 
40735              
40736         }
40737         if (typeof(v) == 'object' ) {
40738             // then let's assume it's an array of objects..
40739             Roo.each(v, function(l) {
40740                 this.addItem(l);
40741             }, this);
40742              
40743         }
40744         
40745         
40746     },
40747     setFromData: function(v)
40748     {
40749         // this recieves an object, if setValues is called.
40750         this.reset();
40751         this.el.dom.value = v[this.displayField];
40752         this.hiddenEl.dom.value = v[this.valueField];
40753         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40754             return;
40755         }
40756         var kv = v[this.valueField];
40757         var dv = v[this.displayField];
40758         kv = typeof(kv) != 'string' ? '' : kv;
40759         dv = typeof(dv) != 'string' ? '' : dv;
40760         
40761         
40762         var keys = kv.split(',');
40763         var display = dv.split(',');
40764         for (var i = 0 ; i < keys.length; i++) {
40765             
40766             add = {};
40767             add[this.valueField] = keys[i];
40768             add[this.displayField] = display[i];
40769             this.addItem(add);
40770         }
40771       
40772         
40773     },
40774     
40775     /**
40776      * Validates the combox array value
40777      * @return {Boolean} True if the value is valid, else false
40778      */
40779     validate : function(){
40780         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40781             this.clearInvalid();
40782             return true;
40783         }
40784         return false;
40785     },
40786     
40787     validateValue : function(value){
40788         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40789         
40790     },
40791     
40792     /*@
40793      * overide
40794      * 
40795      */
40796     isDirty : function() {
40797         if(this.disabled) {
40798             return false;
40799         }
40800         
40801         try {
40802             var d = Roo.decode(String(this.originalValue));
40803         } catch (e) {
40804             return String(this.getValue()) !== String(this.originalValue);
40805         }
40806         
40807         var originalValue = [];
40808         
40809         for (var i = 0; i < d.length; i++){
40810             originalValue.push(d[i][this.valueField]);
40811         }
40812         
40813         return String(this.getValue()) !== String(originalValue.join(','));
40814         
40815     }
40816     
40817 });
40818
40819
40820
40821 /**
40822  * @class Roo.form.ComboBoxArray.Item
40823  * @extends Roo.BoxComponent
40824  * A selected item in the list
40825  *  Fred [x]  Brian [x]  [Pick another |v]
40826  * 
40827  * @constructor
40828  * Create a new item.
40829  * @param {Object} config Configuration options
40830  */
40831  
40832 Roo.form.ComboBoxArray.Item = function(config) {
40833     config.id = Roo.id();
40834     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40835 }
40836
40837 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40838     data : {},
40839     cb: false,
40840     displayField : false,
40841     tipField : false,
40842     
40843     
40844     defaultAutoCreate : {
40845         tag: 'div',
40846         cls: 'x-cbarray-item',
40847         cn : [ 
40848             { tag: 'div' },
40849             {
40850                 tag: 'img',
40851                 width:16,
40852                 height : 16,
40853                 src : Roo.BLANK_IMAGE_URL ,
40854                 align: 'center'
40855             }
40856         ]
40857         
40858     },
40859     
40860  
40861     onRender : function(ct, position)
40862     {
40863         Roo.form.Field.superclass.onRender.call(this, ct, position);
40864         
40865         if(!this.el){
40866             var cfg = this.getAutoCreate();
40867             this.el = ct.createChild(cfg, position);
40868         }
40869         
40870         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40871         
40872         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40873             this.cb.renderer(this.data) :
40874             String.format('{0}',this.data[this.displayField]);
40875         
40876             
40877         this.el.child('div').dom.setAttribute('qtip',
40878                         String.format('{0}',this.data[this.tipField])
40879         );
40880         
40881         this.el.child('img').on('click', this.remove, this);
40882         
40883     },
40884    
40885     remove : function()
40886     {
40887         if(this.cb.disabled){
40888             return;
40889         }
40890         
40891         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40892             this.cb.items.remove(this);
40893             this.el.child('img').un('click', this.remove, this);
40894             this.el.remove();
40895             this.cb.updateHiddenEl();
40896
40897             this.cb.fireEvent('remove', this.cb, this);
40898         }
40899         
40900     }
40901 });/*
40902  * Based on:
40903  * Ext JS Library 1.1.1
40904  * Copyright(c) 2006-2007, Ext JS, LLC.
40905  *
40906  * Originally Released Under LGPL - original licence link has changed is not relivant.
40907  *
40908  * Fork - LGPL
40909  * <script type="text/javascript">
40910  */
40911 /**
40912  * @class Roo.form.Checkbox
40913  * @extends Roo.form.Field
40914  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40915  * @constructor
40916  * Creates a new Checkbox
40917  * @param {Object} config Configuration options
40918  */
40919 Roo.form.Checkbox = function(config){
40920     Roo.form.Checkbox.superclass.constructor.call(this, config);
40921     this.addEvents({
40922         /**
40923          * @event check
40924          * Fires when the checkbox is checked or unchecked.
40925              * @param {Roo.form.Checkbox} this This checkbox
40926              * @param {Boolean} checked The new checked value
40927              */
40928         check : true
40929     });
40930 };
40931
40932 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40933     /**
40934      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40935      */
40936     focusClass : undefined,
40937     /**
40938      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40939      */
40940     fieldClass: "x-form-field",
40941     /**
40942      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40943      */
40944     checked: false,
40945     /**
40946      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40947      * {tag: "input", type: "checkbox", autocomplete: "off"})
40948      */
40949     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40950     /**
40951      * @cfg {String} boxLabel The text that appears beside the checkbox
40952      */
40953     boxLabel : "",
40954     /**
40955      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40956      */  
40957     inputValue : '1',
40958     /**
40959      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40960      */
40961      valueOff: '0', // value when not checked..
40962
40963     actionMode : 'viewEl', 
40964     //
40965     // private
40966     itemCls : 'x-menu-check-item x-form-item',
40967     groupClass : 'x-menu-group-item',
40968     inputType : 'hidden',
40969     
40970     
40971     inSetChecked: false, // check that we are not calling self...
40972     
40973     inputElement: false, // real input element?
40974     basedOn: false, // ????
40975     
40976     isFormField: true, // not sure where this is needed!!!!
40977
40978     onResize : function(){
40979         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40980         if(!this.boxLabel){
40981             this.el.alignTo(this.wrap, 'c-c');
40982         }
40983     },
40984
40985     initEvents : function(){
40986         Roo.form.Checkbox.superclass.initEvents.call(this);
40987         this.el.on("click", this.onClick,  this);
40988         this.el.on("change", this.onClick,  this);
40989     },
40990
40991
40992     getResizeEl : function(){
40993         return this.wrap;
40994     },
40995
40996     getPositionEl : function(){
40997         return this.wrap;
40998     },
40999
41000     // private
41001     onRender : function(ct, position){
41002         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41003         /*
41004         if(this.inputValue !== undefined){
41005             this.el.dom.value = this.inputValue;
41006         }
41007         */
41008         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41009         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41010         var viewEl = this.wrap.createChild({ 
41011             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41012         this.viewEl = viewEl;   
41013         this.wrap.on('click', this.onClick,  this); 
41014         
41015         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41016         this.el.on('propertychange', this.setFromHidden,  this);  //ie
41017         
41018         
41019         
41020         if(this.boxLabel){
41021             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41022         //    viewEl.on('click', this.onClick,  this); 
41023         }
41024         //if(this.checked){
41025             this.setChecked(this.checked);
41026         //}else{
41027             //this.checked = this.el.dom;
41028         //}
41029
41030     },
41031
41032     // private
41033     initValue : Roo.emptyFn,
41034
41035     /**
41036      * Returns the checked state of the checkbox.
41037      * @return {Boolean} True if checked, else false
41038      */
41039     getValue : function(){
41040         if(this.el){
41041             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
41042         }
41043         return this.valueOff;
41044         
41045     },
41046
41047         // private
41048     onClick : function(){ 
41049         if (this.disabled) {
41050             return;
41051         }
41052         this.setChecked(!this.checked);
41053
41054         //if(this.el.dom.checked != this.checked){
41055         //    this.setValue(this.el.dom.checked);
41056        // }
41057     },
41058
41059     /**
41060      * Sets the checked state of the checkbox.
41061      * On is always based on a string comparison between inputValue and the param.
41062      * @param {Boolean/String} value - the value to set 
41063      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
41064      */
41065     setValue : function(v,suppressEvent){
41066         
41067         
41068         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
41069         //if(this.el && this.el.dom){
41070         //    this.el.dom.checked = this.checked;
41071         //    this.el.dom.defaultChecked = this.checked;
41072         //}
41073         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
41074         //this.fireEvent("check", this, this.checked);
41075     },
41076     // private..
41077     setChecked : function(state,suppressEvent)
41078     {
41079         if (this.inSetChecked) {
41080             this.checked = state;
41081             return;
41082         }
41083         
41084     
41085         if(this.wrap){
41086             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41087         }
41088         this.checked = state;
41089         if(suppressEvent !== true){
41090             this.fireEvent('check', this, state);
41091         }
41092         this.inSetChecked = true;
41093         this.el.dom.value = state ? this.inputValue : this.valueOff;
41094         this.inSetChecked = false;
41095         
41096     },
41097     // handle setting of hidden value by some other method!!?!?
41098     setFromHidden: function()
41099     {
41100         if(!this.el){
41101             return;
41102         }
41103         //console.log("SET FROM HIDDEN");
41104         //alert('setFrom hidden');
41105         this.setValue(this.el.dom.value);
41106     },
41107     
41108     onDestroy : function()
41109     {
41110         if(this.viewEl){
41111             Roo.get(this.viewEl).remove();
41112         }
41113          
41114         Roo.form.Checkbox.superclass.onDestroy.call(this);
41115     },
41116     
41117     setBoxLabel : function(str)
41118     {
41119         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
41120     }
41121
41122 });/*
41123  * Based on:
41124  * Ext JS Library 1.1.1
41125  * Copyright(c) 2006-2007, Ext JS, LLC.
41126  *
41127  * Originally Released Under LGPL - original licence link has changed is not relivant.
41128  *
41129  * Fork - LGPL
41130  * <script type="text/javascript">
41131  */
41132  
41133 /**
41134  * @class Roo.form.Radio
41135  * @extends Roo.form.Checkbox
41136  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41137  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41138  * @constructor
41139  * Creates a new Radio
41140  * @param {Object} config Configuration options
41141  */
41142 Roo.form.Radio = function(){
41143     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41144 };
41145 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41146     inputType: 'radio',
41147
41148     /**
41149      * If this radio is part of a group, it will return the selected value
41150      * @return {String}
41151      */
41152     getGroupValue : function(){
41153         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41154     },
41155     
41156     
41157     onRender : function(ct, position){
41158         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41159         
41160         if(this.inputValue !== undefined){
41161             this.el.dom.value = this.inputValue;
41162         }
41163          
41164         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41165         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41166         //var viewEl = this.wrap.createChild({ 
41167         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41168         //this.viewEl = viewEl;   
41169         //this.wrap.on('click', this.onClick,  this); 
41170         
41171         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41172         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41173         
41174         
41175         
41176         if(this.boxLabel){
41177             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41178         //    viewEl.on('click', this.onClick,  this); 
41179         }
41180          if(this.checked){
41181             this.el.dom.checked =   'checked' ;
41182         }
41183          
41184     } 
41185     
41186     
41187 });//<script type="text/javascript">
41188
41189 /*
41190  * Based  Ext JS Library 1.1.1
41191  * Copyright(c) 2006-2007, Ext JS, LLC.
41192  * LGPL
41193  *
41194  */
41195  
41196 /**
41197  * @class Roo.HtmlEditorCore
41198  * @extends Roo.Component
41199  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41200  *
41201  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41202  */
41203
41204 Roo.HtmlEditorCore = function(config){
41205     
41206     
41207     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41208     
41209     
41210     this.addEvents({
41211         /**
41212          * @event initialize
41213          * Fires when the editor is fully initialized (including the iframe)
41214          * @param {Roo.HtmlEditorCore} this
41215          */
41216         initialize: true,
41217         /**
41218          * @event activate
41219          * Fires when the editor is first receives the focus. Any insertion must wait
41220          * until after this event.
41221          * @param {Roo.HtmlEditorCore} this
41222          */
41223         activate: true,
41224          /**
41225          * @event beforesync
41226          * Fires before the textarea is updated with content from the editor iframe. Return false
41227          * to cancel the sync.
41228          * @param {Roo.HtmlEditorCore} this
41229          * @param {String} html
41230          */
41231         beforesync: true,
41232          /**
41233          * @event beforepush
41234          * Fires before the iframe editor is updated with content from the textarea. Return false
41235          * to cancel the push.
41236          * @param {Roo.HtmlEditorCore} this
41237          * @param {String} html
41238          */
41239         beforepush: true,
41240          /**
41241          * @event sync
41242          * Fires when the textarea is updated with content from the editor iframe.
41243          * @param {Roo.HtmlEditorCore} this
41244          * @param {String} html
41245          */
41246         sync: true,
41247          /**
41248          * @event push
41249          * Fires when the iframe editor is updated with content from the textarea.
41250          * @param {Roo.HtmlEditorCore} this
41251          * @param {String} html
41252          */
41253         push: true,
41254         
41255         /**
41256          * @event editorevent
41257          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41258          * @param {Roo.HtmlEditorCore} this
41259          */
41260         editorevent: true
41261         
41262     });
41263     
41264     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41265     
41266     // defaults : white / black...
41267     this.applyBlacklists();
41268     
41269     
41270     
41271 };
41272
41273
41274 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41275
41276
41277      /**
41278      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41279      */
41280     
41281     owner : false,
41282     
41283      /**
41284      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41285      *                        Roo.resizable.
41286      */
41287     resizable : false,
41288      /**
41289      * @cfg {Number} height (in pixels)
41290      */   
41291     height: 300,
41292    /**
41293      * @cfg {Number} width (in pixels)
41294      */   
41295     width: 500,
41296     
41297     /**
41298      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41299      * 
41300      */
41301     stylesheets: false,
41302     
41303     // id of frame..
41304     frameId: false,
41305     
41306     // private properties
41307     validationEvent : false,
41308     deferHeight: true,
41309     initialized : false,
41310     activated : false,
41311     sourceEditMode : false,
41312     onFocus : Roo.emptyFn,
41313     iframePad:3,
41314     hideMode:'offsets',
41315     
41316     clearUp: true,
41317     
41318     // blacklist + whitelisted elements..
41319     black: false,
41320     white: false,
41321      
41322     
41323
41324     /**
41325      * Protected method that will not generally be called directly. It
41326      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41327      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41328      */
41329     getDocMarkup : function(){
41330         // body styles..
41331         var st = '';
41332         
41333         // inherit styels from page...?? 
41334         if (this.stylesheets === false) {
41335             
41336             Roo.get(document.head).select('style').each(function(node) {
41337                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41338             });
41339             
41340             Roo.get(document.head).select('link').each(function(node) { 
41341                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41342             });
41343             
41344         } else if (!this.stylesheets.length) {
41345                 // simple..
41346                 st = '<style type="text/css">' +
41347                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41348                    '</style>';
41349         } else { 
41350             
41351         }
41352         
41353         st +=  '<style type="text/css">' +
41354             'IMG { cursor: pointer } ' +
41355         '</style>';
41356
41357         
41358         return '<html><head>' + st  +
41359             //<style type="text/css">' +
41360             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41361             //'</style>' +
41362             ' </head><body class="roo-htmleditor-body"></body></html>';
41363     },
41364
41365     // private
41366     onRender : function(ct, position)
41367     {
41368         var _t = this;
41369         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41370         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41371         
41372         
41373         this.el.dom.style.border = '0 none';
41374         this.el.dom.setAttribute('tabIndex', -1);
41375         this.el.addClass('x-hidden hide');
41376         
41377         
41378         
41379         if(Roo.isIE){ // fix IE 1px bogus margin
41380             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41381         }
41382        
41383         
41384         this.frameId = Roo.id();
41385         
41386          
41387         
41388         var iframe = this.owner.wrap.createChild({
41389             tag: 'iframe',
41390             cls: 'form-control', // bootstrap..
41391             id: this.frameId,
41392             name: this.frameId,
41393             frameBorder : 'no',
41394             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41395         }, this.el
41396         );
41397         
41398         
41399         this.iframe = iframe.dom;
41400
41401          this.assignDocWin();
41402         
41403         this.doc.designMode = 'on';
41404        
41405         this.doc.open();
41406         this.doc.write(this.getDocMarkup());
41407         this.doc.close();
41408
41409         
41410         var task = { // must defer to wait for browser to be ready
41411             run : function(){
41412                 //console.log("run task?" + this.doc.readyState);
41413                 this.assignDocWin();
41414                 if(this.doc.body || this.doc.readyState == 'complete'){
41415                     try {
41416                         this.doc.designMode="on";
41417                     } catch (e) {
41418                         return;
41419                     }
41420                     Roo.TaskMgr.stop(task);
41421                     this.initEditor.defer(10, this);
41422                 }
41423             },
41424             interval : 10,
41425             duration: 10000,
41426             scope: this
41427         };
41428         Roo.TaskMgr.start(task);
41429
41430     },
41431
41432     // private
41433     onResize : function(w, h)
41434     {
41435          Roo.log('resize: ' +w + ',' + h );
41436         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41437         if(!this.iframe){
41438             return;
41439         }
41440         if(typeof w == 'number'){
41441             
41442             this.iframe.style.width = w + 'px';
41443         }
41444         if(typeof h == 'number'){
41445             
41446             this.iframe.style.height = h + 'px';
41447             if(this.doc){
41448                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41449             }
41450         }
41451         
41452     },
41453
41454     /**
41455      * Toggles the editor between standard and source edit mode.
41456      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41457      */
41458     toggleSourceEdit : function(sourceEditMode){
41459         
41460         this.sourceEditMode = sourceEditMode === true;
41461         
41462         if(this.sourceEditMode){
41463  
41464             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41465             
41466         }else{
41467             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41468             //this.iframe.className = '';
41469             this.deferFocus();
41470         }
41471         //this.setSize(this.owner.wrap.getSize());
41472         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41473     },
41474
41475     
41476   
41477
41478     /**
41479      * Protected method that will not generally be called directly. If you need/want
41480      * custom HTML cleanup, this is the method you should override.
41481      * @param {String} html The HTML to be cleaned
41482      * return {String} The cleaned HTML
41483      */
41484     cleanHtml : function(html){
41485         html = String(html);
41486         if(html.length > 5){
41487             if(Roo.isSafari){ // strip safari nonsense
41488                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41489             }
41490         }
41491         if(html == '&nbsp;'){
41492             html = '';
41493         }
41494         return html;
41495     },
41496
41497     /**
41498      * HTML Editor -> Textarea
41499      * Protected method that will not generally be called directly. Syncs the contents
41500      * of the editor iframe with the textarea.
41501      */
41502     syncValue : function(){
41503         if(this.initialized){
41504             var bd = (this.doc.body || this.doc.documentElement);
41505             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41506             var html = bd.innerHTML;
41507             if(Roo.isSafari){
41508                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41509                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41510                 if(m && m[1]){
41511                     html = '<div style="'+m[0]+'">' + html + '</div>';
41512                 }
41513             }
41514             html = this.cleanHtml(html);
41515             // fix up the special chars.. normaly like back quotes in word...
41516             // however we do not want to do this with chinese..
41517             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41518                 var cc = b.charCodeAt();
41519                 if (
41520                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41521                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41522                     (cc >= 0xf900 && cc < 0xfb00 )
41523                 ) {
41524                         return b;
41525                 }
41526                 return "&#"+cc+";" 
41527             });
41528             if(this.owner.fireEvent('beforesync', this, html) !== false){
41529                 this.el.dom.value = html;
41530                 this.owner.fireEvent('sync', this, html);
41531             }
41532         }
41533     },
41534
41535     /**
41536      * Protected method that will not generally be called directly. Pushes the value of the textarea
41537      * into the iframe editor.
41538      */
41539     pushValue : function(){
41540         if(this.initialized){
41541             var v = this.el.dom.value.trim();
41542             
41543 //            if(v.length < 1){
41544 //                v = '&#160;';
41545 //            }
41546             
41547             if(this.owner.fireEvent('beforepush', this, v) !== false){
41548                 var d = (this.doc.body || this.doc.documentElement);
41549                 d.innerHTML = v;
41550                 this.cleanUpPaste();
41551                 this.el.dom.value = d.innerHTML;
41552                 this.owner.fireEvent('push', this, v);
41553             }
41554         }
41555     },
41556
41557     // private
41558     deferFocus : function(){
41559         this.focus.defer(10, this);
41560     },
41561
41562     // doc'ed in Field
41563     focus : function(){
41564         if(this.win && !this.sourceEditMode){
41565             this.win.focus();
41566         }else{
41567             this.el.focus();
41568         }
41569     },
41570     
41571     assignDocWin: function()
41572     {
41573         var iframe = this.iframe;
41574         
41575          if(Roo.isIE){
41576             this.doc = iframe.contentWindow.document;
41577             this.win = iframe.contentWindow;
41578         } else {
41579 //            if (!Roo.get(this.frameId)) {
41580 //                return;
41581 //            }
41582 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41583 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41584             
41585             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41586                 return;
41587             }
41588             
41589             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41590             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41591         }
41592     },
41593     
41594     // private
41595     initEditor : function(){
41596         //console.log("INIT EDITOR");
41597         this.assignDocWin();
41598         
41599         
41600         
41601         this.doc.designMode="on";
41602         this.doc.open();
41603         this.doc.write(this.getDocMarkup());
41604         this.doc.close();
41605         
41606         var dbody = (this.doc.body || this.doc.documentElement);
41607         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41608         // this copies styles from the containing element into thsi one..
41609         // not sure why we need all of this..
41610         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41611         
41612         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41613         //ss['background-attachment'] = 'fixed'; // w3c
41614         dbody.bgProperties = 'fixed'; // ie
41615         //Roo.DomHelper.applyStyles(dbody, ss);
41616         Roo.EventManager.on(this.doc, {
41617             //'mousedown': this.onEditorEvent,
41618             'mouseup': this.onEditorEvent,
41619             'dblclick': this.onEditorEvent,
41620             'click': this.onEditorEvent,
41621             'keyup': this.onEditorEvent,
41622             buffer:100,
41623             scope: this
41624         });
41625         if(Roo.isGecko){
41626             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41627         }
41628         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41629             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41630         }
41631         this.initialized = true;
41632
41633         this.owner.fireEvent('initialize', this);
41634         this.pushValue();
41635     },
41636
41637     // private
41638     onDestroy : function(){
41639         
41640         
41641         
41642         if(this.rendered){
41643             
41644             //for (var i =0; i < this.toolbars.length;i++) {
41645             //    // fixme - ask toolbars for heights?
41646             //    this.toolbars[i].onDestroy();
41647            // }
41648             
41649             //this.wrap.dom.innerHTML = '';
41650             //this.wrap.remove();
41651         }
41652     },
41653
41654     // private
41655     onFirstFocus : function(){
41656         
41657         this.assignDocWin();
41658         
41659         
41660         this.activated = true;
41661          
41662     
41663         if(Roo.isGecko){ // prevent silly gecko errors
41664             this.win.focus();
41665             var s = this.win.getSelection();
41666             if(!s.focusNode || s.focusNode.nodeType != 3){
41667                 var r = s.getRangeAt(0);
41668                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41669                 r.collapse(true);
41670                 this.deferFocus();
41671             }
41672             try{
41673                 this.execCmd('useCSS', true);
41674                 this.execCmd('styleWithCSS', false);
41675             }catch(e){}
41676         }
41677         this.owner.fireEvent('activate', this);
41678     },
41679
41680     // private
41681     adjustFont: function(btn){
41682         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41683         //if(Roo.isSafari){ // safari
41684         //    adjust *= 2;
41685        // }
41686         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41687         if(Roo.isSafari){ // safari
41688             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41689             v =  (v < 10) ? 10 : v;
41690             v =  (v > 48) ? 48 : v;
41691             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41692             
41693         }
41694         
41695         
41696         v = Math.max(1, v+adjust);
41697         
41698         this.execCmd('FontSize', v  );
41699     },
41700
41701     onEditorEvent : function(e)
41702     {
41703         this.owner.fireEvent('editorevent', this, e);
41704       //  this.updateToolbar();
41705         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41706     },
41707
41708     insertTag : function(tg)
41709     {
41710         // could be a bit smarter... -> wrap the current selected tRoo..
41711         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41712             
41713             range = this.createRange(this.getSelection());
41714             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41715             wrappingNode.appendChild(range.extractContents());
41716             range.insertNode(wrappingNode);
41717
41718             return;
41719             
41720             
41721             
41722         }
41723         this.execCmd("formatblock",   tg);
41724         
41725     },
41726     
41727     insertText : function(txt)
41728     {
41729         
41730         
41731         var range = this.createRange();
41732         range.deleteContents();
41733                //alert(Sender.getAttribute('label'));
41734                
41735         range.insertNode(this.doc.createTextNode(txt));
41736     } ,
41737     
41738      
41739
41740     /**
41741      * Executes a Midas editor command on the editor document and performs necessary focus and
41742      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41743      * @param {String} cmd The Midas command
41744      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41745      */
41746     relayCmd : function(cmd, value){
41747         this.win.focus();
41748         this.execCmd(cmd, value);
41749         this.owner.fireEvent('editorevent', this);
41750         //this.updateToolbar();
41751         this.owner.deferFocus();
41752     },
41753
41754     /**
41755      * Executes a Midas editor command directly on the editor document.
41756      * For visual commands, you should use {@link #relayCmd} instead.
41757      * <b>This should only be called after the editor is initialized.</b>
41758      * @param {String} cmd The Midas command
41759      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41760      */
41761     execCmd : function(cmd, value){
41762         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41763         this.syncValue();
41764     },
41765  
41766  
41767    
41768     /**
41769      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41770      * to insert tRoo.
41771      * @param {String} text | dom node.. 
41772      */
41773     insertAtCursor : function(text)
41774     {
41775         
41776         
41777         
41778         if(!this.activated){
41779             return;
41780         }
41781         /*
41782         if(Roo.isIE){
41783             this.win.focus();
41784             var r = this.doc.selection.createRange();
41785             if(r){
41786                 r.collapse(true);
41787                 r.pasteHTML(text);
41788                 this.syncValue();
41789                 this.deferFocus();
41790             
41791             }
41792             return;
41793         }
41794         */
41795         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41796             this.win.focus();
41797             
41798             
41799             // from jquery ui (MIT licenced)
41800             var range, node;
41801             var win = this.win;
41802             
41803             if (win.getSelection && win.getSelection().getRangeAt) {
41804                 range = win.getSelection().getRangeAt(0);
41805                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41806                 range.insertNode(node);
41807             } else if (win.document.selection && win.document.selection.createRange) {
41808                 // no firefox support
41809                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41810                 win.document.selection.createRange().pasteHTML(txt);
41811             } else {
41812                 // no firefox support
41813                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41814                 this.execCmd('InsertHTML', txt);
41815             } 
41816             
41817             this.syncValue();
41818             
41819             this.deferFocus();
41820         }
41821     },
41822  // private
41823     mozKeyPress : function(e){
41824         if(e.ctrlKey){
41825             var c = e.getCharCode(), cmd;
41826           
41827             if(c > 0){
41828                 c = String.fromCharCode(c).toLowerCase();
41829                 switch(c){
41830                     case 'b':
41831                         cmd = 'bold';
41832                         break;
41833                     case 'i':
41834                         cmd = 'italic';
41835                         break;
41836                     
41837                     case 'u':
41838                         cmd = 'underline';
41839                         break;
41840                     
41841                     case 'v':
41842                         this.cleanUpPaste.defer(100, this);
41843                         return;
41844                         
41845                 }
41846                 if(cmd){
41847                     this.win.focus();
41848                     this.execCmd(cmd);
41849                     this.deferFocus();
41850                     e.preventDefault();
41851                 }
41852                 
41853             }
41854         }
41855     },
41856
41857     // private
41858     fixKeys : function(){ // load time branching for fastest keydown performance
41859         if(Roo.isIE){
41860             return function(e){
41861                 var k = e.getKey(), r;
41862                 if(k == e.TAB){
41863                     e.stopEvent();
41864                     r = this.doc.selection.createRange();
41865                     if(r){
41866                         r.collapse(true);
41867                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41868                         this.deferFocus();
41869                     }
41870                     return;
41871                 }
41872                 
41873                 if(k == e.ENTER){
41874                     r = this.doc.selection.createRange();
41875                     if(r){
41876                         var target = r.parentElement();
41877                         if(!target || target.tagName.toLowerCase() != 'li'){
41878                             e.stopEvent();
41879                             r.pasteHTML('<br />');
41880                             r.collapse(false);
41881                             r.select();
41882                         }
41883                     }
41884                 }
41885                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41886                     this.cleanUpPaste.defer(100, this);
41887                     return;
41888                 }
41889                 
41890                 
41891             };
41892         }else if(Roo.isOpera){
41893             return function(e){
41894                 var k = e.getKey();
41895                 if(k == e.TAB){
41896                     e.stopEvent();
41897                     this.win.focus();
41898                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41899                     this.deferFocus();
41900                 }
41901                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41902                     this.cleanUpPaste.defer(100, this);
41903                     return;
41904                 }
41905                 
41906             };
41907         }else if(Roo.isSafari){
41908             return function(e){
41909                 var k = e.getKey();
41910                 
41911                 if(k == e.TAB){
41912                     e.stopEvent();
41913                     this.execCmd('InsertText','\t');
41914                     this.deferFocus();
41915                     return;
41916                 }
41917                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41918                     this.cleanUpPaste.defer(100, this);
41919                     return;
41920                 }
41921                 
41922              };
41923         }
41924     }(),
41925     
41926     getAllAncestors: function()
41927     {
41928         var p = this.getSelectedNode();
41929         var a = [];
41930         if (!p) {
41931             a.push(p); // push blank onto stack..
41932             p = this.getParentElement();
41933         }
41934         
41935         
41936         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41937             a.push(p);
41938             p = p.parentNode;
41939         }
41940         a.push(this.doc.body);
41941         return a;
41942     },
41943     lastSel : false,
41944     lastSelNode : false,
41945     
41946     
41947     getSelection : function() 
41948     {
41949         this.assignDocWin();
41950         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41951     },
41952     
41953     getSelectedNode: function() 
41954     {
41955         // this may only work on Gecko!!!
41956         
41957         // should we cache this!!!!
41958         
41959         
41960         
41961          
41962         var range = this.createRange(this.getSelection()).cloneRange();
41963         
41964         if (Roo.isIE) {
41965             var parent = range.parentElement();
41966             while (true) {
41967                 var testRange = range.duplicate();
41968                 testRange.moveToElementText(parent);
41969                 if (testRange.inRange(range)) {
41970                     break;
41971                 }
41972                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41973                     break;
41974                 }
41975                 parent = parent.parentElement;
41976             }
41977             return parent;
41978         }
41979         
41980         // is ancestor a text element.
41981         var ac =  range.commonAncestorContainer;
41982         if (ac.nodeType == 3) {
41983             ac = ac.parentNode;
41984         }
41985         
41986         var ar = ac.childNodes;
41987          
41988         var nodes = [];
41989         var other_nodes = [];
41990         var has_other_nodes = false;
41991         for (var i=0;i<ar.length;i++) {
41992             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41993                 continue;
41994             }
41995             // fullly contained node.
41996             
41997             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41998                 nodes.push(ar[i]);
41999                 continue;
42000             }
42001             
42002             // probably selected..
42003             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
42004                 other_nodes.push(ar[i]);
42005                 continue;
42006             }
42007             // outer..
42008             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
42009                 continue;
42010             }
42011             
42012             
42013             has_other_nodes = true;
42014         }
42015         if (!nodes.length && other_nodes.length) {
42016             nodes= other_nodes;
42017         }
42018         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
42019             return false;
42020         }
42021         
42022         return nodes[0];
42023     },
42024     createRange: function(sel)
42025     {
42026         // this has strange effects when using with 
42027         // top toolbar - not sure if it's a great idea.
42028         //this.editor.contentWindow.focus();
42029         if (typeof sel != "undefined") {
42030             try {
42031                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
42032             } catch(e) {
42033                 return this.doc.createRange();
42034             }
42035         } else {
42036             return this.doc.createRange();
42037         }
42038     },
42039     getParentElement: function()
42040     {
42041         
42042         this.assignDocWin();
42043         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
42044         
42045         var range = this.createRange(sel);
42046          
42047         try {
42048             var p = range.commonAncestorContainer;
42049             while (p.nodeType == 3) { // text node
42050                 p = p.parentNode;
42051             }
42052             return p;
42053         } catch (e) {
42054             return null;
42055         }
42056     
42057     },
42058     /***
42059      *
42060      * Range intersection.. the hard stuff...
42061      *  '-1' = before
42062      *  '0' = hits..
42063      *  '1' = after.
42064      *         [ -- selected range --- ]
42065      *   [fail]                        [fail]
42066      *
42067      *    basically..
42068      *      if end is before start or  hits it. fail.
42069      *      if start is after end or hits it fail.
42070      *
42071      *   if either hits (but other is outside. - then it's not 
42072      *   
42073      *    
42074      **/
42075     
42076     
42077     // @see http://www.thismuchiknow.co.uk/?p=64.
42078     rangeIntersectsNode : function(range, node)
42079     {
42080         var nodeRange = node.ownerDocument.createRange();
42081         try {
42082             nodeRange.selectNode(node);
42083         } catch (e) {
42084             nodeRange.selectNodeContents(node);
42085         }
42086     
42087         var rangeStartRange = range.cloneRange();
42088         rangeStartRange.collapse(true);
42089     
42090         var rangeEndRange = range.cloneRange();
42091         rangeEndRange.collapse(false);
42092     
42093         var nodeStartRange = nodeRange.cloneRange();
42094         nodeStartRange.collapse(true);
42095     
42096         var nodeEndRange = nodeRange.cloneRange();
42097         nodeEndRange.collapse(false);
42098     
42099         return rangeStartRange.compareBoundaryPoints(
42100                  Range.START_TO_START, nodeEndRange) == -1 &&
42101                rangeEndRange.compareBoundaryPoints(
42102                  Range.START_TO_START, nodeStartRange) == 1;
42103         
42104          
42105     },
42106     rangeCompareNode : function(range, node)
42107     {
42108         var nodeRange = node.ownerDocument.createRange();
42109         try {
42110             nodeRange.selectNode(node);
42111         } catch (e) {
42112             nodeRange.selectNodeContents(node);
42113         }
42114         
42115         
42116         range.collapse(true);
42117     
42118         nodeRange.collapse(true);
42119      
42120         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42121         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42122          
42123         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42124         
42125         var nodeIsBefore   =  ss == 1;
42126         var nodeIsAfter    = ee == -1;
42127         
42128         if (nodeIsBefore && nodeIsAfter) {
42129             return 0; // outer
42130         }
42131         if (!nodeIsBefore && nodeIsAfter) {
42132             return 1; //right trailed.
42133         }
42134         
42135         if (nodeIsBefore && !nodeIsAfter) {
42136             return 2;  // left trailed.
42137         }
42138         // fully contined.
42139         return 3;
42140     },
42141
42142     // private? - in a new class?
42143     cleanUpPaste :  function()
42144     {
42145         // cleans up the whole document..
42146         Roo.log('cleanuppaste');
42147         
42148         this.cleanUpChildren(this.doc.body);
42149         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42150         if (clean != this.doc.body.innerHTML) {
42151             this.doc.body.innerHTML = clean;
42152         }
42153         
42154     },
42155     
42156     cleanWordChars : function(input) {// change the chars to hex code
42157         var he = Roo.HtmlEditorCore;
42158         
42159         var output = input;
42160         Roo.each(he.swapCodes, function(sw) { 
42161             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42162             
42163             output = output.replace(swapper, sw[1]);
42164         });
42165         
42166         return output;
42167     },
42168     
42169     
42170     cleanUpChildren : function (n)
42171     {
42172         if (!n.childNodes.length) {
42173             return;
42174         }
42175         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42176            this.cleanUpChild(n.childNodes[i]);
42177         }
42178     },
42179     
42180     
42181         
42182     
42183     cleanUpChild : function (node)
42184     {
42185         var ed = this;
42186         //console.log(node);
42187         if (node.nodeName == "#text") {
42188             // clean up silly Windows -- stuff?
42189             return; 
42190         }
42191         if (node.nodeName == "#comment") {
42192             node.parentNode.removeChild(node);
42193             // clean up silly Windows -- stuff?
42194             return; 
42195         }
42196         var lcname = node.tagName.toLowerCase();
42197         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42198         // whitelist of tags..
42199         
42200         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42201             // remove node.
42202             node.parentNode.removeChild(node);
42203             return;
42204             
42205         }
42206         
42207         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42208         
42209         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42210         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42211         
42212         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42213         //    remove_keep_children = true;
42214         //}
42215         
42216         if (remove_keep_children) {
42217             this.cleanUpChildren(node);
42218             // inserts everything just before this node...
42219             while (node.childNodes.length) {
42220                 var cn = node.childNodes[0];
42221                 node.removeChild(cn);
42222                 node.parentNode.insertBefore(cn, node);
42223             }
42224             node.parentNode.removeChild(node);
42225             return;
42226         }
42227         
42228         if (!node.attributes || !node.attributes.length) {
42229             this.cleanUpChildren(node);
42230             return;
42231         }
42232         
42233         function cleanAttr(n,v)
42234         {
42235             
42236             if (v.match(/^\./) || v.match(/^\//)) {
42237                 return;
42238             }
42239             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42240                 return;
42241             }
42242             if (v.match(/^#/)) {
42243                 return;
42244             }
42245 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42246             node.removeAttribute(n);
42247             
42248         }
42249         
42250         var cwhite = this.cwhite;
42251         var cblack = this.cblack;
42252             
42253         function cleanStyle(n,v)
42254         {
42255             if (v.match(/expression/)) { //XSS?? should we even bother..
42256                 node.removeAttribute(n);
42257                 return;
42258             }
42259             
42260             var parts = v.split(/;/);
42261             var clean = [];
42262             
42263             Roo.each(parts, function(p) {
42264                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42265                 if (!p.length) {
42266                     return true;
42267                 }
42268                 var l = p.split(':').shift().replace(/\s+/g,'');
42269                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42270                 
42271                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42272 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42273                     //node.removeAttribute(n);
42274                     return true;
42275                 }
42276                 //Roo.log()
42277                 // only allow 'c whitelisted system attributes'
42278                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42279 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42280                     //node.removeAttribute(n);
42281                     return true;
42282                 }
42283                 
42284                 
42285                  
42286                 
42287                 clean.push(p);
42288                 return true;
42289             });
42290             if (clean.length) { 
42291                 node.setAttribute(n, clean.join(';'));
42292             } else {
42293                 node.removeAttribute(n);
42294             }
42295             
42296         }
42297         
42298         
42299         for (var i = node.attributes.length-1; i > -1 ; i--) {
42300             var a = node.attributes[i];
42301             //console.log(a);
42302             
42303             if (a.name.toLowerCase().substr(0,2)=='on')  {
42304                 node.removeAttribute(a.name);
42305                 continue;
42306             }
42307             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42308                 node.removeAttribute(a.name);
42309                 continue;
42310             }
42311             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42312                 cleanAttr(a.name,a.value); // fixme..
42313                 continue;
42314             }
42315             if (a.name == 'style') {
42316                 cleanStyle(a.name,a.value);
42317                 continue;
42318             }
42319             /// clean up MS crap..
42320             // tecnically this should be a list of valid class'es..
42321             
42322             
42323             if (a.name == 'class') {
42324                 if (a.value.match(/^Mso/)) {
42325                     node.className = '';
42326                 }
42327                 
42328                 if (a.value.match(/body/)) {
42329                     node.className = '';
42330                 }
42331                 continue;
42332             }
42333             
42334             // style cleanup!?
42335             // class cleanup?
42336             
42337         }
42338         
42339         
42340         this.cleanUpChildren(node);
42341         
42342         
42343     },
42344     
42345     /**
42346      * Clean up MS wordisms...
42347      */
42348     cleanWord : function(node)
42349     {
42350         
42351         
42352         if (!node) {
42353             this.cleanWord(this.doc.body);
42354             return;
42355         }
42356         if (node.nodeName == "#text") {
42357             // clean up silly Windows -- stuff?
42358             return; 
42359         }
42360         if (node.nodeName == "#comment") {
42361             node.parentNode.removeChild(node);
42362             // clean up silly Windows -- stuff?
42363             return; 
42364         }
42365         
42366         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42367             node.parentNode.removeChild(node);
42368             return;
42369         }
42370         
42371         // remove - but keep children..
42372         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42373             while (node.childNodes.length) {
42374                 var cn = node.childNodes[0];
42375                 node.removeChild(cn);
42376                 node.parentNode.insertBefore(cn, node);
42377             }
42378             node.parentNode.removeChild(node);
42379             this.iterateChildren(node, this.cleanWord);
42380             return;
42381         }
42382         // clean styles
42383         if (node.className.length) {
42384             
42385             var cn = node.className.split(/\W+/);
42386             var cna = [];
42387             Roo.each(cn, function(cls) {
42388                 if (cls.match(/Mso[a-zA-Z]+/)) {
42389                     return;
42390                 }
42391                 cna.push(cls);
42392             });
42393             node.className = cna.length ? cna.join(' ') : '';
42394             if (!cna.length) {
42395                 node.removeAttribute("class");
42396             }
42397         }
42398         
42399         if (node.hasAttribute("lang")) {
42400             node.removeAttribute("lang");
42401         }
42402         
42403         if (node.hasAttribute("style")) {
42404             
42405             var styles = node.getAttribute("style").split(";");
42406             var nstyle = [];
42407             Roo.each(styles, function(s) {
42408                 if (!s.match(/:/)) {
42409                     return;
42410                 }
42411                 var kv = s.split(":");
42412                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42413                     return;
42414                 }
42415                 // what ever is left... we allow.
42416                 nstyle.push(s);
42417             });
42418             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42419             if (!nstyle.length) {
42420                 node.removeAttribute('style');
42421             }
42422         }
42423         this.iterateChildren(node, this.cleanWord);
42424         
42425         
42426         
42427     },
42428     /**
42429      * iterateChildren of a Node, calling fn each time, using this as the scole..
42430      * @param {DomNode} node node to iterate children of.
42431      * @param {Function} fn method of this class to call on each item.
42432      */
42433     iterateChildren : function(node, fn)
42434     {
42435         if (!node.childNodes.length) {
42436                 return;
42437         }
42438         for (var i = node.childNodes.length-1; i > -1 ; i--) {
42439            fn.call(this, node.childNodes[i])
42440         }
42441     },
42442     
42443     
42444     /**
42445      * cleanTableWidths.
42446      *
42447      * Quite often pasting from word etc.. results in tables with column and widths.
42448      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
42449      *
42450      */
42451     cleanTableWidths : function(node)
42452     {
42453          
42454          
42455         if (!node) {
42456             this.cleanTableWidths(this.doc.body);
42457             return;
42458         }
42459         
42460         // ignore list...
42461         if (node.nodeName == "#text" || node.nodeName == "#comment") {
42462             return; 
42463         }
42464         Roo.log(node.tagName);
42465         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
42466             this.iterateChildren(node, this.cleanTableWidths);
42467             return;
42468         }
42469         if (node.hasAttribute('width')) {
42470             node.removeAttribute('width');
42471         }
42472         
42473          
42474         if (node.hasAttribute("style")) {
42475             // pretty basic...
42476             
42477             var styles = node.getAttribute("style").split(";");
42478             var nstyle = [];
42479             Roo.each(styles, function(s) {
42480                 if (!s.match(/:/)) {
42481                     return;
42482                 }
42483                 var kv = s.split(":");
42484                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
42485                     return;
42486                 }
42487                 // what ever is left... we allow.
42488                 nstyle.push(s);
42489             });
42490             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42491             if (!nstyle.length) {
42492                 node.removeAttribute('style');
42493             }
42494         }
42495         
42496         this.iterateChildren(node, this.cleanTableWidths);
42497         
42498         
42499     },
42500     
42501     
42502     
42503     
42504     domToHTML : function(currentElement, depth, nopadtext) {
42505         
42506         depth = depth || 0;
42507         nopadtext = nopadtext || false;
42508     
42509         if (!currentElement) {
42510             return this.domToHTML(this.doc.body);
42511         }
42512         
42513         //Roo.log(currentElement);
42514         var j;
42515         var allText = false;
42516         var nodeName = currentElement.nodeName;
42517         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42518         
42519         if  (nodeName == '#text') {
42520             
42521             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42522         }
42523         
42524         
42525         var ret = '';
42526         if (nodeName != 'BODY') {
42527              
42528             var i = 0;
42529             // Prints the node tagName, such as <A>, <IMG>, etc
42530             if (tagName) {
42531                 var attr = [];
42532                 for(i = 0; i < currentElement.attributes.length;i++) {
42533                     // quoting?
42534                     var aname = currentElement.attributes.item(i).name;
42535                     if (!currentElement.attributes.item(i).value.length) {
42536                         continue;
42537                     }
42538                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42539                 }
42540                 
42541                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42542             } 
42543             else {
42544                 
42545                 // eack
42546             }
42547         } else {
42548             tagName = false;
42549         }
42550         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42551             return ret;
42552         }
42553         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42554             nopadtext = true;
42555         }
42556         
42557         
42558         // Traverse the tree
42559         i = 0;
42560         var currentElementChild = currentElement.childNodes.item(i);
42561         var allText = true;
42562         var innerHTML  = '';
42563         lastnode = '';
42564         while (currentElementChild) {
42565             // Formatting code (indent the tree so it looks nice on the screen)
42566             var nopad = nopadtext;
42567             if (lastnode == 'SPAN') {
42568                 nopad  = true;
42569             }
42570             // text
42571             if  (currentElementChild.nodeName == '#text') {
42572                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42573                 toadd = nopadtext ? toadd : toadd.trim();
42574                 if (!nopad && toadd.length > 80) {
42575                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42576                 }
42577                 innerHTML  += toadd;
42578                 
42579                 i++;
42580                 currentElementChild = currentElement.childNodes.item(i);
42581                 lastNode = '';
42582                 continue;
42583             }
42584             allText = false;
42585             
42586             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42587                 
42588             // Recursively traverse the tree structure of the child node
42589             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42590             lastnode = currentElementChild.nodeName;
42591             i++;
42592             currentElementChild=currentElement.childNodes.item(i);
42593         }
42594         
42595         ret += innerHTML;
42596         
42597         if (!allText) {
42598                 // The remaining code is mostly for formatting the tree
42599             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42600         }
42601         
42602         
42603         if (tagName) {
42604             ret+= "</"+tagName+">";
42605         }
42606         return ret;
42607         
42608     },
42609         
42610     applyBlacklists : function()
42611     {
42612         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42613         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42614         
42615         this.white = [];
42616         this.black = [];
42617         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42618             if (b.indexOf(tag) > -1) {
42619                 return;
42620             }
42621             this.white.push(tag);
42622             
42623         }, this);
42624         
42625         Roo.each(w, function(tag) {
42626             if (b.indexOf(tag) > -1) {
42627                 return;
42628             }
42629             if (this.white.indexOf(tag) > -1) {
42630                 return;
42631             }
42632             this.white.push(tag);
42633             
42634         }, this);
42635         
42636         
42637         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42638             if (w.indexOf(tag) > -1) {
42639                 return;
42640             }
42641             this.black.push(tag);
42642             
42643         }, this);
42644         
42645         Roo.each(b, function(tag) {
42646             if (w.indexOf(tag) > -1) {
42647                 return;
42648             }
42649             if (this.black.indexOf(tag) > -1) {
42650                 return;
42651             }
42652             this.black.push(tag);
42653             
42654         }, this);
42655         
42656         
42657         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42658         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42659         
42660         this.cwhite = [];
42661         this.cblack = [];
42662         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42663             if (b.indexOf(tag) > -1) {
42664                 return;
42665             }
42666             this.cwhite.push(tag);
42667             
42668         }, this);
42669         
42670         Roo.each(w, function(tag) {
42671             if (b.indexOf(tag) > -1) {
42672                 return;
42673             }
42674             if (this.cwhite.indexOf(tag) > -1) {
42675                 return;
42676             }
42677             this.cwhite.push(tag);
42678             
42679         }, this);
42680         
42681         
42682         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42683             if (w.indexOf(tag) > -1) {
42684                 return;
42685             }
42686             this.cblack.push(tag);
42687             
42688         }, this);
42689         
42690         Roo.each(b, function(tag) {
42691             if (w.indexOf(tag) > -1) {
42692                 return;
42693             }
42694             if (this.cblack.indexOf(tag) > -1) {
42695                 return;
42696             }
42697             this.cblack.push(tag);
42698             
42699         }, this);
42700     },
42701     
42702     setStylesheets : function(stylesheets)
42703     {
42704         if(typeof(stylesheets) == 'string'){
42705             Roo.get(this.iframe.contentDocument.head).createChild({
42706                 tag : 'link',
42707                 rel : 'stylesheet',
42708                 type : 'text/css',
42709                 href : stylesheets
42710             });
42711             
42712             return;
42713         }
42714         var _this = this;
42715      
42716         Roo.each(stylesheets, function(s) {
42717             if(!s.length){
42718                 return;
42719             }
42720             
42721             Roo.get(_this.iframe.contentDocument.head).createChild({
42722                 tag : 'link',
42723                 rel : 'stylesheet',
42724                 type : 'text/css',
42725                 href : s
42726             });
42727         });
42728
42729         
42730     },
42731     
42732     removeStylesheets : function()
42733     {
42734         var _this = this;
42735         
42736         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42737             s.remove();
42738         });
42739     }
42740     
42741     // hide stuff that is not compatible
42742     /**
42743      * @event blur
42744      * @hide
42745      */
42746     /**
42747      * @event change
42748      * @hide
42749      */
42750     /**
42751      * @event focus
42752      * @hide
42753      */
42754     /**
42755      * @event specialkey
42756      * @hide
42757      */
42758     /**
42759      * @cfg {String} fieldClass @hide
42760      */
42761     /**
42762      * @cfg {String} focusClass @hide
42763      */
42764     /**
42765      * @cfg {String} autoCreate @hide
42766      */
42767     /**
42768      * @cfg {String} inputType @hide
42769      */
42770     /**
42771      * @cfg {String} invalidClass @hide
42772      */
42773     /**
42774      * @cfg {String} invalidText @hide
42775      */
42776     /**
42777      * @cfg {String} msgFx @hide
42778      */
42779     /**
42780      * @cfg {String} validateOnBlur @hide
42781      */
42782 });
42783
42784 Roo.HtmlEditorCore.white = [
42785         'area', 'br', 'img', 'input', 'hr', 'wbr',
42786         
42787        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42788        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42789        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42790        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42791        'table',   'ul',         'xmp', 
42792        
42793        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42794       'thead',   'tr', 
42795      
42796       'dir', 'menu', 'ol', 'ul', 'dl',
42797        
42798       'embed',  'object'
42799 ];
42800
42801
42802 Roo.HtmlEditorCore.black = [
42803     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42804         'applet', // 
42805         'base',   'basefont', 'bgsound', 'blink',  'body', 
42806         'frame',  'frameset', 'head',    'html',   'ilayer', 
42807         'iframe', 'layer',  'link',     'meta',    'object',   
42808         'script', 'style' ,'title',  'xml' // clean later..
42809 ];
42810 Roo.HtmlEditorCore.clean = [
42811     'script', 'style', 'title', 'xml'
42812 ];
42813 Roo.HtmlEditorCore.remove = [
42814     'font'
42815 ];
42816 // attributes..
42817
42818 Roo.HtmlEditorCore.ablack = [
42819     'on'
42820 ];
42821     
42822 Roo.HtmlEditorCore.aclean = [ 
42823     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42824 ];
42825
42826 // protocols..
42827 Roo.HtmlEditorCore.pwhite= [
42828         'http',  'https',  'mailto'
42829 ];
42830
42831 // white listed style attributes.
42832 Roo.HtmlEditorCore.cwhite= [
42833       //  'text-align', /// default is to allow most things..
42834       
42835          
42836 //        'font-size'//??
42837 ];
42838
42839 // black listed style attributes.
42840 Roo.HtmlEditorCore.cblack= [
42841       //  'font-size' -- this can be set by the project 
42842 ];
42843
42844
42845 Roo.HtmlEditorCore.swapCodes   =[ 
42846     [    8211, "--" ], 
42847     [    8212, "--" ], 
42848     [    8216,  "'" ],  
42849     [    8217, "'" ],  
42850     [    8220, '"' ],  
42851     [    8221, '"' ],  
42852     [    8226, "*" ],  
42853     [    8230, "..." ]
42854 ]; 
42855
42856     //<script type="text/javascript">
42857
42858 /*
42859  * Ext JS Library 1.1.1
42860  * Copyright(c) 2006-2007, Ext JS, LLC.
42861  * Licence LGPL
42862  * 
42863  */
42864  
42865  
42866 Roo.form.HtmlEditor = function(config){
42867     
42868     
42869     
42870     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42871     
42872     if (!this.toolbars) {
42873         this.toolbars = [];
42874     }
42875     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42876     
42877     
42878 };
42879
42880 /**
42881  * @class Roo.form.HtmlEditor
42882  * @extends Roo.form.Field
42883  * Provides a lightweight HTML Editor component.
42884  *
42885  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42886  * 
42887  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42888  * supported by this editor.</b><br/><br/>
42889  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42890  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42891  */
42892 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42893     /**
42894      * @cfg {Boolean} clearUp
42895      */
42896     clearUp : true,
42897       /**
42898      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42899      */
42900     toolbars : false,
42901    
42902      /**
42903      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42904      *                        Roo.resizable.
42905      */
42906     resizable : false,
42907      /**
42908      * @cfg {Number} height (in pixels)
42909      */   
42910     height: 300,
42911    /**
42912      * @cfg {Number} width (in pixels)
42913      */   
42914     width: 500,
42915     
42916     /**
42917      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42918      * 
42919      */
42920     stylesheets: false,
42921     
42922     
42923      /**
42924      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42925      * 
42926      */
42927     cblack: false,
42928     /**
42929      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42930      * 
42931      */
42932     cwhite: false,
42933     
42934      /**
42935      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42936      * 
42937      */
42938     black: false,
42939     /**
42940      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42941      * 
42942      */
42943     white: false,
42944     
42945     // id of frame..
42946     frameId: false,
42947     
42948     // private properties
42949     validationEvent : false,
42950     deferHeight: true,
42951     initialized : false,
42952     activated : false,
42953     
42954     onFocus : Roo.emptyFn,
42955     iframePad:3,
42956     hideMode:'offsets',
42957     
42958     actionMode : 'container', // defaults to hiding it...
42959     
42960     defaultAutoCreate : { // modified by initCompnoent..
42961         tag: "textarea",
42962         style:"width:500px;height:300px;",
42963         autocomplete: "new-password"
42964     },
42965
42966     // private
42967     initComponent : function(){
42968         this.addEvents({
42969             /**
42970              * @event initialize
42971              * Fires when the editor is fully initialized (including the iframe)
42972              * @param {HtmlEditor} this
42973              */
42974             initialize: true,
42975             /**
42976              * @event activate
42977              * Fires when the editor is first receives the focus. Any insertion must wait
42978              * until after this event.
42979              * @param {HtmlEditor} this
42980              */
42981             activate: true,
42982              /**
42983              * @event beforesync
42984              * Fires before the textarea is updated with content from the editor iframe. Return false
42985              * to cancel the sync.
42986              * @param {HtmlEditor} this
42987              * @param {String} html
42988              */
42989             beforesync: true,
42990              /**
42991              * @event beforepush
42992              * Fires before the iframe editor is updated with content from the textarea. Return false
42993              * to cancel the push.
42994              * @param {HtmlEditor} this
42995              * @param {String} html
42996              */
42997             beforepush: true,
42998              /**
42999              * @event sync
43000              * Fires when the textarea is updated with content from the editor iframe.
43001              * @param {HtmlEditor} this
43002              * @param {String} html
43003              */
43004             sync: true,
43005              /**
43006              * @event push
43007              * Fires when the iframe editor is updated with content from the textarea.
43008              * @param {HtmlEditor} this
43009              * @param {String} html
43010              */
43011             push: true,
43012              /**
43013              * @event editmodechange
43014              * Fires when the editor switches edit modes
43015              * @param {HtmlEditor} this
43016              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
43017              */
43018             editmodechange: true,
43019             /**
43020              * @event editorevent
43021              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43022              * @param {HtmlEditor} this
43023              */
43024             editorevent: true,
43025             /**
43026              * @event firstfocus
43027              * Fires when on first focus - needed by toolbars..
43028              * @param {HtmlEditor} this
43029              */
43030             firstfocus: true,
43031             /**
43032              * @event autosave
43033              * Auto save the htmlEditor value as a file into Events
43034              * @param {HtmlEditor} this
43035              */
43036             autosave: true,
43037             /**
43038              * @event savedpreview
43039              * preview the saved version of htmlEditor
43040              * @param {HtmlEditor} this
43041              */
43042             savedpreview: true,
43043             
43044             /**
43045             * @event stylesheetsclick
43046             * Fires when press the Sytlesheets button
43047             * @param {Roo.HtmlEditorCore} this
43048             */
43049             stylesheetsclick: true
43050         });
43051         this.defaultAutoCreate =  {
43052             tag: "textarea",
43053             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
43054             autocomplete: "new-password"
43055         };
43056     },
43057
43058     /**
43059      * Protected method that will not generally be called directly. It
43060      * is called when the editor creates its toolbar. Override this method if you need to
43061      * add custom toolbar buttons.
43062      * @param {HtmlEditor} editor
43063      */
43064     createToolbar : function(editor){
43065         Roo.log("create toolbars");
43066         if (!editor.toolbars || !editor.toolbars.length) {
43067             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
43068         }
43069         
43070         for (var i =0 ; i < editor.toolbars.length;i++) {
43071             editor.toolbars[i] = Roo.factory(
43072                     typeof(editor.toolbars[i]) == 'string' ?
43073                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
43074                 Roo.form.HtmlEditor);
43075             editor.toolbars[i].init(editor);
43076         }
43077          
43078         
43079     },
43080
43081      
43082     // private
43083     onRender : function(ct, position)
43084     {
43085         var _t = this;
43086         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
43087         
43088         this.wrap = this.el.wrap({
43089             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
43090         });
43091         
43092         this.editorcore.onRender(ct, position);
43093          
43094         if (this.resizable) {
43095             this.resizeEl = new Roo.Resizable(this.wrap, {
43096                 pinned : true,
43097                 wrap: true,
43098                 dynamic : true,
43099                 minHeight : this.height,
43100                 height: this.height,
43101                 handles : this.resizable,
43102                 width: this.width,
43103                 listeners : {
43104                     resize : function(r, w, h) {
43105                         _t.onResize(w,h); // -something
43106                     }
43107                 }
43108             });
43109             
43110         }
43111         this.createToolbar(this);
43112        
43113         
43114         if(!this.width){
43115             this.setSize(this.wrap.getSize());
43116         }
43117         if (this.resizeEl) {
43118             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43119             // should trigger onReize..
43120         }
43121         
43122         this.keyNav = new Roo.KeyNav(this.el, {
43123             
43124             "tab" : function(e){
43125                 e.preventDefault();
43126                 
43127                 var value = this.getValue();
43128                 
43129                 var start = this.el.dom.selectionStart;
43130                 var end = this.el.dom.selectionEnd;
43131                 
43132                 if(!e.shiftKey){
43133                     
43134                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43135                     this.el.dom.setSelectionRange(end + 1, end + 1);
43136                     return;
43137                 }
43138                 
43139                 var f = value.substring(0, start).split("\t");
43140                 
43141                 if(f.pop().length != 0){
43142                     return;
43143                 }
43144                 
43145                 this.setValue(f.join("\t") + value.substring(end));
43146                 this.el.dom.setSelectionRange(start - 1, start - 1);
43147                 
43148             },
43149             
43150             "home" : function(e){
43151                 e.preventDefault();
43152                 
43153                 var curr = this.el.dom.selectionStart;
43154                 var lines = this.getValue().split("\n");
43155                 
43156                 if(!lines.length){
43157                     return;
43158                 }
43159                 
43160                 if(e.ctrlKey){
43161                     this.el.dom.setSelectionRange(0, 0);
43162                     return;
43163                 }
43164                 
43165                 var pos = 0;
43166                 
43167                 for (var i = 0; i < lines.length;i++) {
43168                     pos += lines[i].length;
43169                     
43170                     if(i != 0){
43171                         pos += 1;
43172                     }
43173                     
43174                     if(pos < curr){
43175                         continue;
43176                     }
43177                     
43178                     pos -= lines[i].length;
43179                     
43180                     break;
43181                 }
43182                 
43183                 if(!e.shiftKey){
43184                     this.el.dom.setSelectionRange(pos, pos);
43185                     return;
43186                 }
43187                 
43188                 this.el.dom.selectionStart = pos;
43189                 this.el.dom.selectionEnd = curr;
43190             },
43191             
43192             "end" : function(e){
43193                 e.preventDefault();
43194                 
43195                 var curr = this.el.dom.selectionStart;
43196                 var lines = this.getValue().split("\n");
43197                 
43198                 if(!lines.length){
43199                     return;
43200                 }
43201                 
43202                 if(e.ctrlKey){
43203                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43204                     return;
43205                 }
43206                 
43207                 var pos = 0;
43208                 
43209                 for (var i = 0; i < lines.length;i++) {
43210                     
43211                     pos += lines[i].length;
43212                     
43213                     if(i != 0){
43214                         pos += 1;
43215                     }
43216                     
43217                     if(pos < curr){
43218                         continue;
43219                     }
43220                     
43221                     break;
43222                 }
43223                 
43224                 if(!e.shiftKey){
43225                     this.el.dom.setSelectionRange(pos, pos);
43226                     return;
43227                 }
43228                 
43229                 this.el.dom.selectionStart = curr;
43230                 this.el.dom.selectionEnd = pos;
43231             },
43232
43233             scope : this,
43234
43235             doRelay : function(foo, bar, hname){
43236                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43237             },
43238
43239             forceKeyDown: true
43240         });
43241         
43242 //        if(this.autosave && this.w){
43243 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43244 //        }
43245     },
43246
43247     // private
43248     onResize : function(w, h)
43249     {
43250         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43251         var ew = false;
43252         var eh = false;
43253         
43254         if(this.el ){
43255             if(typeof w == 'number'){
43256                 var aw = w - this.wrap.getFrameWidth('lr');
43257                 this.el.setWidth(this.adjustWidth('textarea', aw));
43258                 ew = aw;
43259             }
43260             if(typeof h == 'number'){
43261                 var tbh = 0;
43262                 for (var i =0; i < this.toolbars.length;i++) {
43263                     // fixme - ask toolbars for heights?
43264                     tbh += this.toolbars[i].tb.el.getHeight();
43265                     if (this.toolbars[i].footer) {
43266                         tbh += this.toolbars[i].footer.el.getHeight();
43267                     }
43268                 }
43269                 
43270                 
43271                 
43272                 
43273                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43274                 ah -= 5; // knock a few pixes off for look..
43275 //                Roo.log(ah);
43276                 this.el.setHeight(this.adjustWidth('textarea', ah));
43277                 var eh = ah;
43278             }
43279         }
43280         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43281         this.editorcore.onResize(ew,eh);
43282         
43283     },
43284
43285     /**
43286      * Toggles the editor between standard and source edit mode.
43287      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43288      */
43289     toggleSourceEdit : function(sourceEditMode)
43290     {
43291         this.editorcore.toggleSourceEdit(sourceEditMode);
43292         
43293         if(this.editorcore.sourceEditMode){
43294             Roo.log('editor - showing textarea');
43295             
43296 //            Roo.log('in');
43297 //            Roo.log(this.syncValue());
43298             this.editorcore.syncValue();
43299             this.el.removeClass('x-hidden');
43300             this.el.dom.removeAttribute('tabIndex');
43301             this.el.focus();
43302             
43303             for (var i = 0; i < this.toolbars.length; i++) {
43304                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43305                     this.toolbars[i].tb.hide();
43306                     this.toolbars[i].footer.hide();
43307                 }
43308             }
43309             
43310         }else{
43311             Roo.log('editor - hiding textarea');
43312 //            Roo.log('out')
43313 //            Roo.log(this.pushValue()); 
43314             this.editorcore.pushValue();
43315             
43316             this.el.addClass('x-hidden');
43317             this.el.dom.setAttribute('tabIndex', -1);
43318             
43319             for (var i = 0; i < this.toolbars.length; i++) {
43320                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43321                     this.toolbars[i].tb.show();
43322                     this.toolbars[i].footer.show();
43323                 }
43324             }
43325             
43326             //this.deferFocus();
43327         }
43328         
43329         this.setSize(this.wrap.getSize());
43330         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43331         
43332         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43333     },
43334  
43335     // private (for BoxComponent)
43336     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43337
43338     // private (for BoxComponent)
43339     getResizeEl : function(){
43340         return this.wrap;
43341     },
43342
43343     // private (for BoxComponent)
43344     getPositionEl : function(){
43345         return this.wrap;
43346     },
43347
43348     // private
43349     initEvents : function(){
43350         this.originalValue = this.getValue();
43351     },
43352
43353     /**
43354      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43355      * @method
43356      */
43357     markInvalid : Roo.emptyFn,
43358     /**
43359      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43360      * @method
43361      */
43362     clearInvalid : Roo.emptyFn,
43363
43364     setValue : function(v){
43365         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43366         this.editorcore.pushValue();
43367     },
43368
43369      
43370     // private
43371     deferFocus : function(){
43372         this.focus.defer(10, this);
43373     },
43374
43375     // doc'ed in Field
43376     focus : function(){
43377         this.editorcore.focus();
43378         
43379     },
43380       
43381
43382     // private
43383     onDestroy : function(){
43384         
43385         
43386         
43387         if(this.rendered){
43388             
43389             for (var i =0; i < this.toolbars.length;i++) {
43390                 // fixme - ask toolbars for heights?
43391                 this.toolbars[i].onDestroy();
43392             }
43393             
43394             this.wrap.dom.innerHTML = '';
43395             this.wrap.remove();
43396         }
43397     },
43398
43399     // private
43400     onFirstFocus : function(){
43401         //Roo.log("onFirstFocus");
43402         this.editorcore.onFirstFocus();
43403          for (var i =0; i < this.toolbars.length;i++) {
43404             this.toolbars[i].onFirstFocus();
43405         }
43406         
43407     },
43408     
43409     // private
43410     syncValue : function()
43411     {
43412         this.editorcore.syncValue();
43413     },
43414     
43415     pushValue : function()
43416     {
43417         this.editorcore.pushValue();
43418     },
43419     
43420     setStylesheets : function(stylesheets)
43421     {
43422         this.editorcore.setStylesheets(stylesheets);
43423     },
43424     
43425     removeStylesheets : function()
43426     {
43427         this.editorcore.removeStylesheets();
43428     }
43429      
43430     
43431     // hide stuff that is not compatible
43432     /**
43433      * @event blur
43434      * @hide
43435      */
43436     /**
43437      * @event change
43438      * @hide
43439      */
43440     /**
43441      * @event focus
43442      * @hide
43443      */
43444     /**
43445      * @event specialkey
43446      * @hide
43447      */
43448     /**
43449      * @cfg {String} fieldClass @hide
43450      */
43451     /**
43452      * @cfg {String} focusClass @hide
43453      */
43454     /**
43455      * @cfg {String} autoCreate @hide
43456      */
43457     /**
43458      * @cfg {String} inputType @hide
43459      */
43460     /**
43461      * @cfg {String} invalidClass @hide
43462      */
43463     /**
43464      * @cfg {String} invalidText @hide
43465      */
43466     /**
43467      * @cfg {String} msgFx @hide
43468      */
43469     /**
43470      * @cfg {String} validateOnBlur @hide
43471      */
43472 });
43473  
43474     // <script type="text/javascript">
43475 /*
43476  * Based on
43477  * Ext JS Library 1.1.1
43478  * Copyright(c) 2006-2007, Ext JS, LLC.
43479  *  
43480  
43481  */
43482
43483 /**
43484  * @class Roo.form.HtmlEditorToolbar1
43485  * Basic Toolbar
43486  * 
43487  * Usage:
43488  *
43489  new Roo.form.HtmlEditor({
43490     ....
43491     toolbars : [
43492         new Roo.form.HtmlEditorToolbar1({
43493             disable : { fonts: 1 , format: 1, ..., ... , ...],
43494             btns : [ .... ]
43495         })
43496     }
43497      
43498  * 
43499  * @cfg {Object} disable List of elements to disable..
43500  * @cfg {Array} btns List of additional buttons.
43501  * 
43502  * 
43503  * NEEDS Extra CSS? 
43504  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43505  */
43506  
43507 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43508 {
43509     
43510     Roo.apply(this, config);
43511     
43512     // default disabled, based on 'good practice'..
43513     this.disable = this.disable || {};
43514     Roo.applyIf(this.disable, {
43515         fontSize : true,
43516         colors : true,
43517         specialElements : true
43518     });
43519     
43520     
43521     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43522     // dont call parent... till later.
43523 }
43524
43525 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43526     
43527     tb: false,
43528     
43529     rendered: false,
43530     
43531     editor : false,
43532     editorcore : false,
43533     /**
43534      * @cfg {Object} disable  List of toolbar elements to disable
43535          
43536      */
43537     disable : false,
43538     
43539     
43540      /**
43541      * @cfg {String} createLinkText The default text for the create link prompt
43542      */
43543     createLinkText : 'Please enter the URL for the link:',
43544     /**
43545      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43546      */
43547     defaultLinkValue : 'http:/'+'/',
43548    
43549     
43550       /**
43551      * @cfg {Array} fontFamilies An array of available font families
43552      */
43553     fontFamilies : [
43554         'Arial',
43555         'Courier New',
43556         'Tahoma',
43557         'Times New Roman',
43558         'Verdana'
43559     ],
43560     
43561     specialChars : [
43562            "&#169;",
43563           "&#174;",     
43564           "&#8482;",    
43565           "&#163;" ,    
43566          // "&#8212;",    
43567           "&#8230;",    
43568           "&#247;" ,    
43569         //  "&#225;" ,     ?? a acute?
43570            "&#8364;"    , //Euro
43571        //   "&#8220;"    ,
43572         //  "&#8221;"    ,
43573         //  "&#8226;"    ,
43574           "&#176;"  //   , // degrees
43575
43576          // "&#233;"     , // e ecute
43577          // "&#250;"     , // u ecute?
43578     ],
43579     
43580     specialElements : [
43581         {
43582             text: "Insert Table",
43583             xtype: 'MenuItem',
43584             xns : Roo.Menu,
43585             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43586                 
43587         },
43588         {    
43589             text: "Insert Image",
43590             xtype: 'MenuItem',
43591             xns : Roo.Menu,
43592             ihtml : '<img src="about:blank"/>'
43593             
43594         }
43595         
43596          
43597     ],
43598     
43599     
43600     inputElements : [ 
43601             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43602             "input:submit", "input:button", "select", "textarea", "label" ],
43603     formats : [
43604         ["p"] ,  
43605         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43606         ["pre"],[ "code"], 
43607         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43608         ['div'],['span']
43609     ],
43610     
43611     cleanStyles : [
43612         "font-size"
43613     ],
43614      /**
43615      * @cfg {String} defaultFont default font to use.
43616      */
43617     defaultFont: 'tahoma',
43618    
43619     fontSelect : false,
43620     
43621     
43622     formatCombo : false,
43623     
43624     init : function(editor)
43625     {
43626         this.editor = editor;
43627         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43628         var editorcore = this.editorcore;
43629         
43630         var _t = this;
43631         
43632         var fid = editorcore.frameId;
43633         var etb = this;
43634         function btn(id, toggle, handler){
43635             var xid = fid + '-'+ id ;
43636             return {
43637                 id : xid,
43638                 cmd : id,
43639                 cls : 'x-btn-icon x-edit-'+id,
43640                 enableToggle:toggle !== false,
43641                 scope: _t, // was editor...
43642                 handler:handler||_t.relayBtnCmd,
43643                 clickEvent:'mousedown',
43644                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43645                 tabIndex:-1
43646             };
43647         }
43648         
43649         
43650         
43651         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43652         this.tb = tb;
43653          // stop form submits
43654         tb.el.on('click', function(e){
43655             e.preventDefault(); // what does this do?
43656         });
43657
43658         if(!this.disable.font) { // && !Roo.isSafari){
43659             /* why no safari for fonts 
43660             editor.fontSelect = tb.el.createChild({
43661                 tag:'select',
43662                 tabIndex: -1,
43663                 cls:'x-font-select',
43664                 html: this.createFontOptions()
43665             });
43666             
43667             editor.fontSelect.on('change', function(){
43668                 var font = editor.fontSelect.dom.value;
43669                 editor.relayCmd('fontname', font);
43670                 editor.deferFocus();
43671             }, editor);
43672             
43673             tb.add(
43674                 editor.fontSelect.dom,
43675                 '-'
43676             );
43677             */
43678             
43679         };
43680         if(!this.disable.formats){
43681             this.formatCombo = new Roo.form.ComboBox({
43682                 store: new Roo.data.SimpleStore({
43683                     id : 'tag',
43684                     fields: ['tag'],
43685                     data : this.formats // from states.js
43686                 }),
43687                 blockFocus : true,
43688                 name : '',
43689                 //autoCreate : {tag: "div",  size: "20"},
43690                 displayField:'tag',
43691                 typeAhead: false,
43692                 mode: 'local',
43693                 editable : false,
43694                 triggerAction: 'all',
43695                 emptyText:'Add tag',
43696                 selectOnFocus:true,
43697                 width:135,
43698                 listeners : {
43699                     'select': function(c, r, i) {
43700                         editorcore.insertTag(r.get('tag'));
43701                         editor.focus();
43702                     }
43703                 }
43704
43705             });
43706             tb.addField(this.formatCombo);
43707             
43708         }
43709         
43710         if(!this.disable.format){
43711             tb.add(
43712                 btn('bold'),
43713                 btn('italic'),
43714                 btn('underline'),
43715                 btn('strikethrough')
43716             );
43717         };
43718         if(!this.disable.fontSize){
43719             tb.add(
43720                 '-',
43721                 
43722                 
43723                 btn('increasefontsize', false, editorcore.adjustFont),
43724                 btn('decreasefontsize', false, editorcore.adjustFont)
43725             );
43726         };
43727         
43728         
43729         if(!this.disable.colors){
43730             tb.add(
43731                 '-', {
43732                     id:editorcore.frameId +'-forecolor',
43733                     cls:'x-btn-icon x-edit-forecolor',
43734                     clickEvent:'mousedown',
43735                     tooltip: this.buttonTips['forecolor'] || undefined,
43736                     tabIndex:-1,
43737                     menu : new Roo.menu.ColorMenu({
43738                         allowReselect: true,
43739                         focus: Roo.emptyFn,
43740                         value:'000000',
43741                         plain:true,
43742                         selectHandler: function(cp, color){
43743                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43744                             editor.deferFocus();
43745                         },
43746                         scope: editorcore,
43747                         clickEvent:'mousedown'
43748                     })
43749                 }, {
43750                     id:editorcore.frameId +'backcolor',
43751                     cls:'x-btn-icon x-edit-backcolor',
43752                     clickEvent:'mousedown',
43753                     tooltip: this.buttonTips['backcolor'] || undefined,
43754                     tabIndex:-1,
43755                     menu : new Roo.menu.ColorMenu({
43756                         focus: Roo.emptyFn,
43757                         value:'FFFFFF',
43758                         plain:true,
43759                         allowReselect: true,
43760                         selectHandler: function(cp, color){
43761                             if(Roo.isGecko){
43762                                 editorcore.execCmd('useCSS', false);
43763                                 editorcore.execCmd('hilitecolor', color);
43764                                 editorcore.execCmd('useCSS', true);
43765                                 editor.deferFocus();
43766                             }else{
43767                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43768                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43769                                 editor.deferFocus();
43770                             }
43771                         },
43772                         scope:editorcore,
43773                         clickEvent:'mousedown'
43774                     })
43775                 }
43776             );
43777         };
43778         // now add all the items...
43779         
43780
43781         if(!this.disable.alignments){
43782             tb.add(
43783                 '-',
43784                 btn('justifyleft'),
43785                 btn('justifycenter'),
43786                 btn('justifyright')
43787             );
43788         };
43789
43790         //if(!Roo.isSafari){
43791             if(!this.disable.links){
43792                 tb.add(
43793                     '-',
43794                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43795                 );
43796             };
43797
43798             if(!this.disable.lists){
43799                 tb.add(
43800                     '-',
43801                     btn('insertorderedlist'),
43802                     btn('insertunorderedlist')
43803                 );
43804             }
43805             if(!this.disable.sourceEdit){
43806                 tb.add(
43807                     '-',
43808                     btn('sourceedit', true, function(btn){
43809                         this.toggleSourceEdit(btn.pressed);
43810                     })
43811                 );
43812             }
43813         //}
43814         
43815         var smenu = { };
43816         // special menu.. - needs to be tidied up..
43817         if (!this.disable.special) {
43818             smenu = {
43819                 text: "&#169;",
43820                 cls: 'x-edit-none',
43821                 
43822                 menu : {
43823                     items : []
43824                 }
43825             };
43826             for (var i =0; i < this.specialChars.length; i++) {
43827                 smenu.menu.items.push({
43828                     
43829                     html: this.specialChars[i],
43830                     handler: function(a,b) {
43831                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43832                         //editor.insertAtCursor(a.html);
43833                         
43834                     },
43835                     tabIndex:-1
43836                 });
43837             }
43838             
43839             
43840             tb.add(smenu);
43841             
43842             
43843         }
43844         
43845         var cmenu = { };
43846         if (!this.disable.cleanStyles) {
43847             cmenu = {
43848                 cls: 'x-btn-icon x-btn-clear',
43849                 
43850                 menu : {
43851                     items : []
43852                 }
43853             };
43854             for (var i =0; i < this.cleanStyles.length; i++) {
43855                 cmenu.menu.items.push({
43856                     actiontype : this.cleanStyles[i],
43857                     html: 'Remove ' + this.cleanStyles[i],
43858                     handler: function(a,b) {
43859 //                        Roo.log(a);
43860 //                        Roo.log(b);
43861                         var c = Roo.get(editorcore.doc.body);
43862                         c.select('[style]').each(function(s) {
43863                             s.dom.style.removeProperty(a.actiontype);
43864                         });
43865                         editorcore.syncValue();
43866                     },
43867                     tabIndex:-1
43868                 });
43869             }
43870              cmenu.menu.items.push({
43871                 actiontype : 'tablewidths',
43872                 html: 'Remove Table Widths',
43873                 handler: function(a,b) {
43874                     editorcore.cleanTableWidths();
43875                     editorcore.syncValue();
43876                 },
43877                 tabIndex:-1
43878             });
43879             cmenu.menu.items.push({
43880                 actiontype : 'word',
43881                 html: 'Remove MS Word Formating',
43882                 handler: function(a,b) {
43883                     editorcore.cleanWord();
43884                     editorcore.syncValue();
43885                 },
43886                 tabIndex:-1
43887             });
43888             
43889             cmenu.menu.items.push({
43890                 actiontype : 'all',
43891                 html: 'Remove All Styles',
43892                 handler: function(a,b) {
43893                     
43894                     var c = Roo.get(editorcore.doc.body);
43895                     c.select('[style]').each(function(s) {
43896                         s.dom.removeAttribute('style');
43897                     });
43898                     editorcore.syncValue();
43899                 },
43900                 tabIndex:-1
43901             });
43902             
43903             cmenu.menu.items.push({
43904                 actiontype : 'all',
43905                 html: 'Remove All CSS Classes',
43906                 handler: function(a,b) {
43907                     
43908                     var c = Roo.get(editorcore.doc.body);
43909                     c.select('[class]').each(function(s) {
43910                         s.dom.className = '';
43911                     });
43912                     editorcore.syncValue();
43913                 },
43914                 tabIndex:-1
43915             });
43916             
43917              cmenu.menu.items.push({
43918                 actiontype : 'tidy',
43919                 html: 'Tidy HTML Source',
43920                 handler: function(a,b) {
43921                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43922                     editorcore.syncValue();
43923                 },
43924                 tabIndex:-1
43925             });
43926             
43927             
43928             tb.add(cmenu);
43929         }
43930          
43931         if (!this.disable.specialElements) {
43932             var semenu = {
43933                 text: "Other;",
43934                 cls: 'x-edit-none',
43935                 menu : {
43936                     items : []
43937                 }
43938             };
43939             for (var i =0; i < this.specialElements.length; i++) {
43940                 semenu.menu.items.push(
43941                     Roo.apply({ 
43942                         handler: function(a,b) {
43943                             editor.insertAtCursor(this.ihtml);
43944                         }
43945                     }, this.specialElements[i])
43946                 );
43947                     
43948             }
43949             
43950             tb.add(semenu);
43951             
43952             
43953         }
43954          
43955         
43956         if (this.btns) {
43957             for(var i =0; i< this.btns.length;i++) {
43958                 var b = Roo.factory(this.btns[i],Roo.form);
43959                 b.cls =  'x-edit-none';
43960                 
43961                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43962                     b.cls += ' x-init-enable';
43963                 }
43964                 
43965                 b.scope = editorcore;
43966                 tb.add(b);
43967             }
43968         
43969         }
43970         
43971         
43972         
43973         // disable everything...
43974         
43975         this.tb.items.each(function(item){
43976             
43977            if(
43978                 item.id != editorcore.frameId+ '-sourceedit' && 
43979                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43980             ){
43981                 
43982                 item.disable();
43983             }
43984         });
43985         this.rendered = true;
43986         
43987         // the all the btns;
43988         editor.on('editorevent', this.updateToolbar, this);
43989         // other toolbars need to implement this..
43990         //editor.on('editmodechange', this.updateToolbar, this);
43991     },
43992     
43993     
43994     relayBtnCmd : function(btn) {
43995         this.editorcore.relayCmd(btn.cmd);
43996     },
43997     // private used internally
43998     createLink : function(){
43999         Roo.log("create link?");
44000         var url = prompt(this.createLinkText, this.defaultLinkValue);
44001         if(url && url != 'http:/'+'/'){
44002             this.editorcore.relayCmd('createlink', url);
44003         }
44004     },
44005
44006     
44007     /**
44008      * Protected method that will not generally be called directly. It triggers
44009      * a toolbar update by reading the markup state of the current selection in the editor.
44010      */
44011     updateToolbar: function(){
44012
44013         if(!this.editorcore.activated){
44014             this.editor.onFirstFocus();
44015             return;
44016         }
44017
44018         var btns = this.tb.items.map, 
44019             doc = this.editorcore.doc,
44020             frameId = this.editorcore.frameId;
44021
44022         if(!this.disable.font && !Roo.isSafari){
44023             /*
44024             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
44025             if(name != this.fontSelect.dom.value){
44026                 this.fontSelect.dom.value = name;
44027             }
44028             */
44029         }
44030         if(!this.disable.format){
44031             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
44032             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
44033             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
44034             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
44035         }
44036         if(!this.disable.alignments){
44037             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
44038             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
44039             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
44040         }
44041         if(!Roo.isSafari && !this.disable.lists){
44042             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
44043             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
44044         }
44045         
44046         var ans = this.editorcore.getAllAncestors();
44047         if (this.formatCombo) {
44048             
44049             
44050             var store = this.formatCombo.store;
44051             this.formatCombo.setValue("");
44052             for (var i =0; i < ans.length;i++) {
44053                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
44054                     // select it..
44055                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
44056                     break;
44057                 }
44058             }
44059         }
44060         
44061         
44062         
44063         // hides menus... - so this cant be on a menu...
44064         Roo.menu.MenuMgr.hideAll();
44065
44066         //this.editorsyncValue();
44067     },
44068    
44069     
44070     createFontOptions : function(){
44071         var buf = [], fs = this.fontFamilies, ff, lc;
44072         
44073         
44074         
44075         for(var i = 0, len = fs.length; i< len; i++){
44076             ff = fs[i];
44077             lc = ff.toLowerCase();
44078             buf.push(
44079                 '<option value="',lc,'" style="font-family:',ff,';"',
44080                     (this.defaultFont == lc ? ' selected="true">' : '>'),
44081                     ff,
44082                 '</option>'
44083             );
44084         }
44085         return buf.join('');
44086     },
44087     
44088     toggleSourceEdit : function(sourceEditMode){
44089         
44090         Roo.log("toolbar toogle");
44091         if(sourceEditMode === undefined){
44092             sourceEditMode = !this.sourceEditMode;
44093         }
44094         this.sourceEditMode = sourceEditMode === true;
44095         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
44096         // just toggle the button?
44097         if(btn.pressed !== this.sourceEditMode){
44098             btn.toggle(this.sourceEditMode);
44099             return;
44100         }
44101         
44102         if(sourceEditMode){
44103             Roo.log("disabling buttons");
44104             this.tb.items.each(function(item){
44105                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
44106                     item.disable();
44107                 }
44108             });
44109           
44110         }else{
44111             Roo.log("enabling buttons");
44112             if(this.editorcore.initialized){
44113                 this.tb.items.each(function(item){
44114                     item.enable();
44115                 });
44116             }
44117             
44118         }
44119         Roo.log("calling toggole on editor");
44120         // tell the editor that it's been pressed..
44121         this.editor.toggleSourceEdit(sourceEditMode);
44122        
44123     },
44124      /**
44125      * Object collection of toolbar tooltips for the buttons in the editor. The key
44126      * is the command id associated with that button and the value is a valid QuickTips object.
44127      * For example:
44128 <pre><code>
44129 {
44130     bold : {
44131         title: 'Bold (Ctrl+B)',
44132         text: 'Make the selected text bold.',
44133         cls: 'x-html-editor-tip'
44134     },
44135     italic : {
44136         title: 'Italic (Ctrl+I)',
44137         text: 'Make the selected text italic.',
44138         cls: 'x-html-editor-tip'
44139     },
44140     ...
44141 </code></pre>
44142     * @type Object
44143      */
44144     buttonTips : {
44145         bold : {
44146             title: 'Bold (Ctrl+B)',
44147             text: 'Make the selected text bold.',
44148             cls: 'x-html-editor-tip'
44149         },
44150         italic : {
44151             title: 'Italic (Ctrl+I)',
44152             text: 'Make the selected text italic.',
44153             cls: 'x-html-editor-tip'
44154         },
44155         underline : {
44156             title: 'Underline (Ctrl+U)',
44157             text: 'Underline the selected text.',
44158             cls: 'x-html-editor-tip'
44159         },
44160         strikethrough : {
44161             title: 'Strikethrough',
44162             text: 'Strikethrough the selected text.',
44163             cls: 'x-html-editor-tip'
44164         },
44165         increasefontsize : {
44166             title: 'Grow Text',
44167             text: 'Increase the font size.',
44168             cls: 'x-html-editor-tip'
44169         },
44170         decreasefontsize : {
44171             title: 'Shrink Text',
44172             text: 'Decrease the font size.',
44173             cls: 'x-html-editor-tip'
44174         },
44175         backcolor : {
44176             title: 'Text Highlight Color',
44177             text: 'Change the background color of the selected text.',
44178             cls: 'x-html-editor-tip'
44179         },
44180         forecolor : {
44181             title: 'Font Color',
44182             text: 'Change the color of the selected text.',
44183             cls: 'x-html-editor-tip'
44184         },
44185         justifyleft : {
44186             title: 'Align Text Left',
44187             text: 'Align text to the left.',
44188             cls: 'x-html-editor-tip'
44189         },
44190         justifycenter : {
44191             title: 'Center Text',
44192             text: 'Center text in the editor.',
44193             cls: 'x-html-editor-tip'
44194         },
44195         justifyright : {
44196             title: 'Align Text Right',
44197             text: 'Align text to the right.',
44198             cls: 'x-html-editor-tip'
44199         },
44200         insertunorderedlist : {
44201             title: 'Bullet List',
44202             text: 'Start a bulleted list.',
44203             cls: 'x-html-editor-tip'
44204         },
44205         insertorderedlist : {
44206             title: 'Numbered List',
44207             text: 'Start a numbered list.',
44208             cls: 'x-html-editor-tip'
44209         },
44210         createlink : {
44211             title: 'Hyperlink',
44212             text: 'Make the selected text a hyperlink.',
44213             cls: 'x-html-editor-tip'
44214         },
44215         sourceedit : {
44216             title: 'Source Edit',
44217             text: 'Switch to source editing mode.',
44218             cls: 'x-html-editor-tip'
44219         }
44220     },
44221     // private
44222     onDestroy : function(){
44223         if(this.rendered){
44224             
44225             this.tb.items.each(function(item){
44226                 if(item.menu){
44227                     item.menu.removeAll();
44228                     if(item.menu.el){
44229                         item.menu.el.destroy();
44230                     }
44231                 }
44232                 item.destroy();
44233             });
44234              
44235         }
44236     },
44237     onFirstFocus: function() {
44238         this.tb.items.each(function(item){
44239            item.enable();
44240         });
44241     }
44242 });
44243
44244
44245
44246
44247 // <script type="text/javascript">
44248 /*
44249  * Based on
44250  * Ext JS Library 1.1.1
44251  * Copyright(c) 2006-2007, Ext JS, LLC.
44252  *  
44253  
44254  */
44255
44256  
44257 /**
44258  * @class Roo.form.HtmlEditor.ToolbarContext
44259  * Context Toolbar
44260  * 
44261  * Usage:
44262  *
44263  new Roo.form.HtmlEditor({
44264     ....
44265     toolbars : [
44266         { xtype: 'ToolbarStandard', styles : {} }
44267         { xtype: 'ToolbarContext', disable : {} }
44268     ]
44269 })
44270
44271      
44272  * 
44273  * @config : {Object} disable List of elements to disable.. (not done yet.)
44274  * @config : {Object} styles  Map of styles available.
44275  * 
44276  */
44277
44278 Roo.form.HtmlEditor.ToolbarContext = function(config)
44279 {
44280     
44281     Roo.apply(this, config);
44282     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44283     // dont call parent... till later.
44284     this.styles = this.styles || {};
44285 }
44286
44287  
44288
44289 Roo.form.HtmlEditor.ToolbarContext.types = {
44290     'IMG' : {
44291         width : {
44292             title: "Width",
44293             width: 40
44294         },
44295         height:  {
44296             title: "Height",
44297             width: 40
44298         },
44299         align: {
44300             title: "Align",
44301             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44302             width : 80
44303             
44304         },
44305         border: {
44306             title: "Border",
44307             width: 40
44308         },
44309         alt: {
44310             title: "Alt",
44311             width: 120
44312         },
44313         src : {
44314             title: "Src",
44315             width: 220
44316         }
44317         
44318     },
44319     'A' : {
44320         name : {
44321             title: "Name",
44322             width: 50
44323         },
44324         target:  {
44325             title: "Target",
44326             width: 120
44327         },
44328         href:  {
44329             title: "Href",
44330             width: 220
44331         } // border?
44332         
44333     },
44334     'TABLE' : {
44335         rows : {
44336             title: "Rows",
44337             width: 20
44338         },
44339         cols : {
44340             title: "Cols",
44341             width: 20
44342         },
44343         width : {
44344             title: "Width",
44345             width: 40
44346         },
44347         height : {
44348             title: "Height",
44349             width: 40
44350         },
44351         border : {
44352             title: "Border",
44353             width: 20
44354         }
44355     },
44356     'TD' : {
44357         width : {
44358             title: "Width",
44359             width: 40
44360         },
44361         height : {
44362             title: "Height",
44363             width: 40
44364         },   
44365         align: {
44366             title: "Align",
44367             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44368             width: 80
44369         },
44370         valign: {
44371             title: "Valign",
44372             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44373             width: 80
44374         },
44375         colspan: {
44376             title: "Colspan",
44377             width: 20
44378             
44379         },
44380          'font-family'  : {
44381             title : "Font",
44382             style : 'fontFamily',
44383             displayField: 'display',
44384             optname : 'font-family',
44385             width: 140
44386         }
44387     },
44388     'INPUT' : {
44389         name : {
44390             title: "name",
44391             width: 120
44392         },
44393         value : {
44394             title: "Value",
44395             width: 120
44396         },
44397         width : {
44398             title: "Width",
44399             width: 40
44400         }
44401     },
44402     'LABEL' : {
44403         'for' : {
44404             title: "For",
44405             width: 120
44406         }
44407     },
44408     'TEXTAREA' : {
44409           name : {
44410             title: "name",
44411             width: 120
44412         },
44413         rows : {
44414             title: "Rows",
44415             width: 20
44416         },
44417         cols : {
44418             title: "Cols",
44419             width: 20
44420         }
44421     },
44422     'SELECT' : {
44423         name : {
44424             title: "name",
44425             width: 120
44426         },
44427         selectoptions : {
44428             title: "Options",
44429             width: 200
44430         }
44431     },
44432     
44433     // should we really allow this??
44434     // should this just be 
44435     'BODY' : {
44436         title : {
44437             title: "Title",
44438             width: 200,
44439             disabled : true
44440         }
44441     },
44442     'SPAN' : {
44443         'font-family'  : {
44444             title : "Font",
44445             style : 'fontFamily',
44446             displayField: 'display',
44447             optname : 'font-family',
44448             width: 140
44449         }
44450     },
44451     'DIV' : {
44452         'font-family'  : {
44453             title : "Font",
44454             style : 'fontFamily',
44455             displayField: 'display',
44456             optname : 'font-family',
44457             width: 140
44458         }
44459     },
44460      'P' : {
44461         'font-family'  : {
44462             title : "Font",
44463             style : 'fontFamily',
44464             displayField: 'display',
44465             optname : 'font-family',
44466             width: 140
44467         }
44468     },
44469     
44470     '*' : {
44471         // empty..
44472     }
44473
44474 };
44475
44476 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44477 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44478
44479 Roo.form.HtmlEditor.ToolbarContext.options = {
44480         'font-family'  : [ 
44481                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44482                 [ 'Courier New', 'Courier New'],
44483                 [ 'Tahoma', 'Tahoma'],
44484                 [ 'Times New Roman,serif', 'Times'],
44485                 [ 'Verdana','Verdana' ]
44486         ]
44487 };
44488
44489 // fixme - these need to be configurable..
44490  
44491
44492 //Roo.form.HtmlEditor.ToolbarContext.types
44493
44494
44495 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44496     
44497     tb: false,
44498     
44499     rendered: false,
44500     
44501     editor : false,
44502     editorcore : false,
44503     /**
44504      * @cfg {Object} disable  List of toolbar elements to disable
44505          
44506      */
44507     disable : false,
44508     /**
44509      * @cfg {Object} styles List of styles 
44510      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44511      *
44512      * These must be defined in the page, so they get rendered correctly..
44513      * .headline { }
44514      * TD.underline { }
44515      * 
44516      */
44517     styles : false,
44518     
44519     options: false,
44520     
44521     toolbars : false,
44522     
44523     init : function(editor)
44524     {
44525         this.editor = editor;
44526         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44527         var editorcore = this.editorcore;
44528         
44529         var fid = editorcore.frameId;
44530         var etb = this;
44531         function btn(id, toggle, handler){
44532             var xid = fid + '-'+ id ;
44533             return {
44534                 id : xid,
44535                 cmd : id,
44536                 cls : 'x-btn-icon x-edit-'+id,
44537                 enableToggle:toggle !== false,
44538                 scope: editorcore, // was editor...
44539                 handler:handler||editorcore.relayBtnCmd,
44540                 clickEvent:'mousedown',
44541                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44542                 tabIndex:-1
44543             };
44544         }
44545         // create a new element.
44546         var wdiv = editor.wrap.createChild({
44547                 tag: 'div'
44548             }, editor.wrap.dom.firstChild.nextSibling, true);
44549         
44550         // can we do this more than once??
44551         
44552          // stop form submits
44553       
44554  
44555         // disable everything...
44556         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44557         this.toolbars = {};
44558            
44559         for (var i in  ty) {
44560           
44561             this.toolbars[i] = this.buildToolbar(ty[i],i);
44562         }
44563         this.tb = this.toolbars.BODY;
44564         this.tb.el.show();
44565         this.buildFooter();
44566         this.footer.show();
44567         editor.on('hide', function( ) { this.footer.hide() }, this);
44568         editor.on('show', function( ) { this.footer.show() }, this);
44569         
44570          
44571         this.rendered = true;
44572         
44573         // the all the btns;
44574         editor.on('editorevent', this.updateToolbar, this);
44575         // other toolbars need to implement this..
44576         //editor.on('editmodechange', this.updateToolbar, this);
44577     },
44578     
44579     
44580     
44581     /**
44582      * Protected method that will not generally be called directly. It triggers
44583      * a toolbar update by reading the markup state of the current selection in the editor.
44584      *
44585      * Note you can force an update by calling on('editorevent', scope, false)
44586      */
44587     updateToolbar: function(editor,ev,sel){
44588
44589         //Roo.log(ev);
44590         // capture mouse up - this is handy for selecting images..
44591         // perhaps should go somewhere else...
44592         if(!this.editorcore.activated){
44593              this.editor.onFirstFocus();
44594             return;
44595         }
44596         
44597         
44598         
44599         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44600         // selectNode - might want to handle IE?
44601         if (ev &&
44602             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44603             ev.target && ev.target.tagName == 'IMG') {
44604             // they have click on an image...
44605             // let's see if we can change the selection...
44606             sel = ev.target;
44607          
44608               var nodeRange = sel.ownerDocument.createRange();
44609             try {
44610                 nodeRange.selectNode(sel);
44611             } catch (e) {
44612                 nodeRange.selectNodeContents(sel);
44613             }
44614             //nodeRange.collapse(true);
44615             var s = this.editorcore.win.getSelection();
44616             s.removeAllRanges();
44617             s.addRange(nodeRange);
44618         }  
44619         
44620       
44621         var updateFooter = sel ? false : true;
44622         
44623         
44624         var ans = this.editorcore.getAllAncestors();
44625         
44626         // pick
44627         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44628         
44629         if (!sel) { 
44630             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44631             sel = sel ? sel : this.editorcore.doc.body;
44632             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44633             
44634         }
44635         // pick a menu that exists..
44636         var tn = sel.tagName.toUpperCase();
44637         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44638         
44639         tn = sel.tagName.toUpperCase();
44640         
44641         var lastSel = this.tb.selectedNode;
44642         
44643         this.tb.selectedNode = sel;
44644         
44645         // if current menu does not match..
44646         
44647         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44648                 
44649             this.tb.el.hide();
44650             ///console.log("show: " + tn);
44651             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44652             this.tb.el.show();
44653             // update name
44654             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44655             
44656             
44657             // update attributes
44658             if (this.tb.fields) {
44659                 this.tb.fields.each(function(e) {
44660                     if (e.stylename) {
44661                         e.setValue(sel.style[e.stylename]);
44662                         return;
44663                     } 
44664                    e.setValue(sel.getAttribute(e.attrname));
44665                 });
44666             }
44667             
44668             var hasStyles = false;
44669             for(var i in this.styles) {
44670                 hasStyles = true;
44671                 break;
44672             }
44673             
44674             // update styles
44675             if (hasStyles) { 
44676                 var st = this.tb.fields.item(0);
44677                 
44678                 st.store.removeAll();
44679                
44680                 
44681                 var cn = sel.className.split(/\s+/);
44682                 
44683                 var avs = [];
44684                 if (this.styles['*']) {
44685                     
44686                     Roo.each(this.styles['*'], function(v) {
44687                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44688                     });
44689                 }
44690                 if (this.styles[tn]) { 
44691                     Roo.each(this.styles[tn], function(v) {
44692                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44693                     });
44694                 }
44695                 
44696                 st.store.loadData(avs);
44697                 st.collapse();
44698                 st.setValue(cn);
44699             }
44700             // flag our selected Node.
44701             this.tb.selectedNode = sel;
44702            
44703            
44704             Roo.menu.MenuMgr.hideAll();
44705
44706         }
44707         
44708         if (!updateFooter) {
44709             //this.footDisp.dom.innerHTML = ''; 
44710             return;
44711         }
44712         // update the footer
44713         //
44714         var html = '';
44715         
44716         this.footerEls = ans.reverse();
44717         Roo.each(this.footerEls, function(a,i) {
44718             if (!a) { return; }
44719             html += html.length ? ' &gt; '  :  '';
44720             
44721             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44722             
44723         });
44724        
44725         // 
44726         var sz = this.footDisp.up('td').getSize();
44727         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44728         this.footDisp.dom.style.marginLeft = '5px';
44729         
44730         this.footDisp.dom.style.overflow = 'hidden';
44731         
44732         this.footDisp.dom.innerHTML = html;
44733             
44734         //this.editorsyncValue();
44735     },
44736      
44737     
44738    
44739        
44740     // private
44741     onDestroy : function(){
44742         if(this.rendered){
44743             
44744             this.tb.items.each(function(item){
44745                 if(item.menu){
44746                     item.menu.removeAll();
44747                     if(item.menu.el){
44748                         item.menu.el.destroy();
44749                     }
44750                 }
44751                 item.destroy();
44752             });
44753              
44754         }
44755     },
44756     onFirstFocus: function() {
44757         // need to do this for all the toolbars..
44758         this.tb.items.each(function(item){
44759            item.enable();
44760         });
44761     },
44762     buildToolbar: function(tlist, nm)
44763     {
44764         var editor = this.editor;
44765         var editorcore = this.editorcore;
44766          // create a new element.
44767         var wdiv = editor.wrap.createChild({
44768                 tag: 'div'
44769             }, editor.wrap.dom.firstChild.nextSibling, true);
44770         
44771        
44772         var tb = new Roo.Toolbar(wdiv);
44773         // add the name..
44774         
44775         tb.add(nm+ ":&nbsp;");
44776         
44777         var styles = [];
44778         for(var i in this.styles) {
44779             styles.push(i);
44780         }
44781         
44782         // styles...
44783         if (styles && styles.length) {
44784             
44785             // this needs a multi-select checkbox...
44786             tb.addField( new Roo.form.ComboBox({
44787                 store: new Roo.data.SimpleStore({
44788                     id : 'val',
44789                     fields: ['val', 'selected'],
44790                     data : [] 
44791                 }),
44792                 name : '-roo-edit-className',
44793                 attrname : 'className',
44794                 displayField: 'val',
44795                 typeAhead: false,
44796                 mode: 'local',
44797                 editable : false,
44798                 triggerAction: 'all',
44799                 emptyText:'Select Style',
44800                 selectOnFocus:true,
44801                 width: 130,
44802                 listeners : {
44803                     'select': function(c, r, i) {
44804                         // initial support only for on class per el..
44805                         tb.selectedNode.className =  r ? r.get('val') : '';
44806                         editorcore.syncValue();
44807                     }
44808                 }
44809     
44810             }));
44811         }
44812         
44813         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44814         var tbops = tbc.options;
44815         
44816         for (var i in tlist) {
44817             
44818             var item = tlist[i];
44819             tb.add(item.title + ":&nbsp;");
44820             
44821             
44822             //optname == used so you can configure the options available..
44823             var opts = item.opts ? item.opts : false;
44824             if (item.optname) {
44825                 opts = tbops[item.optname];
44826            
44827             }
44828             
44829             if (opts) {
44830                 // opts == pulldown..
44831                 tb.addField( new Roo.form.ComboBox({
44832                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44833                         id : 'val',
44834                         fields: ['val', 'display'],
44835                         data : opts  
44836                     }),
44837                     name : '-roo-edit-' + i,
44838                     attrname : i,
44839                     stylename : item.style ? item.style : false,
44840                     displayField: item.displayField ? item.displayField : 'val',
44841                     valueField :  'val',
44842                     typeAhead: false,
44843                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44844                     editable : false,
44845                     triggerAction: 'all',
44846                     emptyText:'Select',
44847                     selectOnFocus:true,
44848                     width: item.width ? item.width  : 130,
44849                     listeners : {
44850                         'select': function(c, r, i) {
44851                             if (c.stylename) {
44852                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44853                                 return;
44854                             }
44855                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44856                         }
44857                     }
44858
44859                 }));
44860                 continue;
44861                     
44862                  
44863                 
44864                 tb.addField( new Roo.form.TextField({
44865                     name: i,
44866                     width: 100,
44867                     //allowBlank:false,
44868                     value: ''
44869                 }));
44870                 continue;
44871             }
44872             tb.addField( new Roo.form.TextField({
44873                 name: '-roo-edit-' + i,
44874                 attrname : i,
44875                 
44876                 width: item.width,
44877                 //allowBlank:true,
44878                 value: '',
44879                 listeners: {
44880                     'change' : function(f, nv, ov) {
44881                         tb.selectedNode.setAttribute(f.attrname, nv);
44882                     }
44883                 }
44884             }));
44885              
44886         }
44887         
44888         var _this = this;
44889         
44890         if(nm == 'BODY'){
44891             tb.addSeparator();
44892         
44893             tb.addButton( {
44894                 text: 'Stylesheets',
44895
44896                 listeners : {
44897                     click : function ()
44898                     {
44899                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44900                     }
44901                 }
44902             });
44903         }
44904         
44905         tb.addFill();
44906         tb.addButton( {
44907             text: 'Remove Tag',
44908     
44909             listeners : {
44910                 click : function ()
44911                 {
44912                     // remove
44913                     // undo does not work.
44914                      
44915                     var sn = tb.selectedNode;
44916                     
44917                     var pn = sn.parentNode;
44918                     
44919                     var stn =  sn.childNodes[0];
44920                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44921                     while (sn.childNodes.length) {
44922                         var node = sn.childNodes[0];
44923                         sn.removeChild(node);
44924                         //Roo.log(node);
44925                         pn.insertBefore(node, sn);
44926                         
44927                     }
44928                     pn.removeChild(sn);
44929                     var range = editorcore.createRange();
44930         
44931                     range.setStart(stn,0);
44932                     range.setEnd(en,0); //????
44933                     //range.selectNode(sel);
44934                     
44935                     
44936                     var selection = editorcore.getSelection();
44937                     selection.removeAllRanges();
44938                     selection.addRange(range);
44939                     
44940                     
44941                     
44942                     //_this.updateToolbar(null, null, pn);
44943                     _this.updateToolbar(null, null, null);
44944                     _this.footDisp.dom.innerHTML = ''; 
44945                 }
44946             }
44947             
44948                     
44949                 
44950             
44951         });
44952         
44953         
44954         tb.el.on('click', function(e){
44955             e.preventDefault(); // what does this do?
44956         });
44957         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44958         tb.el.hide();
44959         tb.name = nm;
44960         // dont need to disable them... as they will get hidden
44961         return tb;
44962          
44963         
44964     },
44965     buildFooter : function()
44966     {
44967         
44968         var fel = this.editor.wrap.createChild();
44969         this.footer = new Roo.Toolbar(fel);
44970         // toolbar has scrolly on left / right?
44971         var footDisp= new Roo.Toolbar.Fill();
44972         var _t = this;
44973         this.footer.add(
44974             {
44975                 text : '&lt;',
44976                 xtype: 'Button',
44977                 handler : function() {
44978                     _t.footDisp.scrollTo('left',0,true)
44979                 }
44980             }
44981         );
44982         this.footer.add( footDisp );
44983         this.footer.add( 
44984             {
44985                 text : '&gt;',
44986                 xtype: 'Button',
44987                 handler : function() {
44988                     // no animation..
44989                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44990                 }
44991             }
44992         );
44993         var fel = Roo.get(footDisp.el);
44994         fel.addClass('x-editor-context');
44995         this.footDispWrap = fel; 
44996         this.footDispWrap.overflow  = 'hidden';
44997         
44998         this.footDisp = fel.createChild();
44999         this.footDispWrap.on('click', this.onContextClick, this)
45000         
45001         
45002     },
45003     onContextClick : function (ev,dom)
45004     {
45005         ev.preventDefault();
45006         var  cn = dom.className;
45007         //Roo.log(cn);
45008         if (!cn.match(/x-ed-loc-/)) {
45009             return;
45010         }
45011         var n = cn.split('-').pop();
45012         var ans = this.footerEls;
45013         var sel = ans[n];
45014         
45015          // pick
45016         var range = this.editorcore.createRange();
45017         
45018         range.selectNodeContents(sel);
45019         //range.selectNode(sel);
45020         
45021         
45022         var selection = this.editorcore.getSelection();
45023         selection.removeAllRanges();
45024         selection.addRange(range);
45025         
45026         
45027         
45028         this.updateToolbar(null, null, sel);
45029         
45030         
45031     }
45032     
45033     
45034     
45035     
45036     
45037 });
45038
45039
45040
45041
45042
45043 /*
45044  * Based on:
45045  * Ext JS Library 1.1.1
45046  * Copyright(c) 2006-2007, Ext JS, LLC.
45047  *
45048  * Originally Released Under LGPL - original licence link has changed is not relivant.
45049  *
45050  * Fork - LGPL
45051  * <script type="text/javascript">
45052  */
45053  
45054 /**
45055  * @class Roo.form.BasicForm
45056  * @extends Roo.util.Observable
45057  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
45058  * @constructor
45059  * @param {String/HTMLElement/Roo.Element} el The form element or its id
45060  * @param {Object} config Configuration options
45061  */
45062 Roo.form.BasicForm = function(el, config){
45063     this.allItems = [];
45064     this.childForms = [];
45065     Roo.apply(this, config);
45066     /*
45067      * The Roo.form.Field items in this form.
45068      * @type MixedCollection
45069      */
45070      
45071      
45072     this.items = new Roo.util.MixedCollection(false, function(o){
45073         return o.id || (o.id = Roo.id());
45074     });
45075     this.addEvents({
45076         /**
45077          * @event beforeaction
45078          * Fires before any action is performed. Return false to cancel the action.
45079          * @param {Form} this
45080          * @param {Action} action The action to be performed
45081          */
45082         beforeaction: true,
45083         /**
45084          * @event actionfailed
45085          * Fires when an action fails.
45086          * @param {Form} this
45087          * @param {Action} action The action that failed
45088          */
45089         actionfailed : true,
45090         /**
45091          * @event actioncomplete
45092          * Fires when an action is completed.
45093          * @param {Form} this
45094          * @param {Action} action The action that completed
45095          */
45096         actioncomplete : true
45097     });
45098     if(el){
45099         this.initEl(el);
45100     }
45101     Roo.form.BasicForm.superclass.constructor.call(this);
45102 };
45103
45104 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
45105     /**
45106      * @cfg {String} method
45107      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
45108      */
45109     /**
45110      * @cfg {DataReader} reader
45111      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
45112      * This is optional as there is built-in support for processing JSON.
45113      */
45114     /**
45115      * @cfg {DataReader} errorReader
45116      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
45117      * This is completely optional as there is built-in support for processing JSON.
45118      */
45119     /**
45120      * @cfg {String} url
45121      * The URL to use for form actions if one isn't supplied in the action options.
45122      */
45123     /**
45124      * @cfg {Boolean} fileUpload
45125      * Set to true if this form is a file upload.
45126      */
45127      
45128     /**
45129      * @cfg {Object} baseParams
45130      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
45131      */
45132      /**
45133      
45134     /**
45135      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
45136      */
45137     timeout: 30,
45138
45139     // private
45140     activeAction : null,
45141
45142     /**
45143      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
45144      * or setValues() data instead of when the form was first created.
45145      */
45146     trackResetOnLoad : false,
45147     
45148     
45149     /**
45150      * childForms - used for multi-tab forms
45151      * @type {Array}
45152      */
45153     childForms : false,
45154     
45155     /**
45156      * allItems - full list of fields.
45157      * @type {Array}
45158      */
45159     allItems : false,
45160     
45161     /**
45162      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45163      * element by passing it or its id or mask the form itself by passing in true.
45164      * @type Mixed
45165      */
45166     waitMsgTarget : false,
45167
45168     // private
45169     initEl : function(el){
45170         this.el = Roo.get(el);
45171         this.id = this.el.id || Roo.id();
45172         this.el.on('submit', this.onSubmit, this);
45173         this.el.addClass('x-form');
45174     },
45175
45176     // private
45177     onSubmit : function(e){
45178         e.stopEvent();
45179     },
45180
45181     /**
45182      * Returns true if client-side validation on the form is successful.
45183      * @return Boolean
45184      */
45185     isValid : function(){
45186         var valid = true;
45187         this.items.each(function(f){
45188            if(!f.validate()){
45189                valid = false;
45190            }
45191         });
45192         return valid;
45193     },
45194
45195     /**
45196      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
45197      * @return Boolean
45198      */
45199     isDirty : function(){
45200         var dirty = false;
45201         this.items.each(function(f){
45202            if(f.isDirty()){
45203                dirty = true;
45204                return false;
45205            }
45206         });
45207         return dirty;
45208     },
45209     
45210     /**
45211      * Returns true if any fields in this form have changed since their original load. (New version)
45212      * @return Boolean
45213      */
45214     
45215     hasChanged : function()
45216     {
45217         var dirty = false;
45218         this.items.each(function(f){
45219            if(f.hasChanged()){
45220                dirty = true;
45221                return false;
45222            }
45223         });
45224         return dirty;
45225         
45226     },
45227     /**
45228      * Resets all hasChanged to 'false' -
45229      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
45230      * So hasChanged storage is only to be used for this purpose
45231      * @return Boolean
45232      */
45233     resetHasChanged : function()
45234     {
45235         this.items.each(function(f){
45236            f.resetHasChanged();
45237         });
45238         
45239     },
45240     
45241     
45242     /**
45243      * Performs a predefined action (submit or load) or custom actions you define on this form.
45244      * @param {String} actionName The name of the action type
45245      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45246      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45247      * accept other config options):
45248      * <pre>
45249 Property          Type             Description
45250 ----------------  ---------------  ----------------------------------------------------------------------------------
45251 url               String           The url for the action (defaults to the form's url)
45252 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45253 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45254 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45255                                    validate the form on the client (defaults to false)
45256      * </pre>
45257      * @return {BasicForm} this
45258      */
45259     doAction : function(action, options){
45260         if(typeof action == 'string'){
45261             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45262         }
45263         if(this.fireEvent('beforeaction', this, action) !== false){
45264             this.beforeAction(action);
45265             action.run.defer(100, action);
45266         }
45267         return this;
45268     },
45269
45270     /**
45271      * Shortcut to do a submit action.
45272      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45273      * @return {BasicForm} this
45274      */
45275     submit : function(options){
45276         this.doAction('submit', options);
45277         return this;
45278     },
45279
45280     /**
45281      * Shortcut to do a load action.
45282      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45283      * @return {BasicForm} this
45284      */
45285     load : function(options){
45286         this.doAction('load', options);
45287         return this;
45288     },
45289
45290     /**
45291      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45292      * @param {Record} record The record to edit
45293      * @return {BasicForm} this
45294      */
45295     updateRecord : function(record){
45296         record.beginEdit();
45297         var fs = record.fields;
45298         fs.each(function(f){
45299             var field = this.findField(f.name);
45300             if(field){
45301                 record.set(f.name, field.getValue());
45302             }
45303         }, this);
45304         record.endEdit();
45305         return this;
45306     },
45307
45308     /**
45309      * Loads an Roo.data.Record into this form.
45310      * @param {Record} record The record to load
45311      * @return {BasicForm} this
45312      */
45313     loadRecord : function(record){
45314         this.setValues(record.data);
45315         return this;
45316     },
45317
45318     // private
45319     beforeAction : function(action){
45320         var o = action.options;
45321         
45322        
45323         if(this.waitMsgTarget === true){
45324             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45325         }else if(this.waitMsgTarget){
45326             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45327             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45328         }else {
45329             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45330         }
45331          
45332     },
45333
45334     // private
45335     afterAction : function(action, success){
45336         this.activeAction = null;
45337         var o = action.options;
45338         
45339         if(this.waitMsgTarget === true){
45340             this.el.unmask();
45341         }else if(this.waitMsgTarget){
45342             this.waitMsgTarget.unmask();
45343         }else{
45344             Roo.MessageBox.updateProgress(1);
45345             Roo.MessageBox.hide();
45346         }
45347          
45348         if(success){
45349             if(o.reset){
45350                 this.reset();
45351             }
45352             Roo.callback(o.success, o.scope, [this, action]);
45353             this.fireEvent('actioncomplete', this, action);
45354             
45355         }else{
45356             
45357             // failure condition..
45358             // we have a scenario where updates need confirming.
45359             // eg. if a locking scenario exists..
45360             // we look for { errors : { needs_confirm : true }} in the response.
45361             if (
45362                 (typeof(action.result) != 'undefined')  &&
45363                 (typeof(action.result.errors) != 'undefined')  &&
45364                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45365            ){
45366                 var _t = this;
45367                 Roo.MessageBox.confirm(
45368                     "Change requires confirmation",
45369                     action.result.errorMsg,
45370                     function(r) {
45371                         if (r != 'yes') {
45372                             return;
45373                         }
45374                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45375                     }
45376                     
45377                 );
45378                 
45379                 
45380                 
45381                 return;
45382             }
45383             
45384             Roo.callback(o.failure, o.scope, [this, action]);
45385             // show an error message if no failed handler is set..
45386             if (!this.hasListener('actionfailed')) {
45387                 Roo.MessageBox.alert("Error",
45388                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45389                         action.result.errorMsg :
45390                         "Saving Failed, please check your entries or try again"
45391                 );
45392             }
45393             
45394             this.fireEvent('actionfailed', this, action);
45395         }
45396         
45397     },
45398
45399     /**
45400      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45401      * @param {String} id The value to search for
45402      * @return Field
45403      */
45404     findField : function(id){
45405         var field = this.items.get(id);
45406         if(!field){
45407             this.items.each(function(f){
45408                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45409                     field = f;
45410                     return false;
45411                 }
45412             });
45413         }
45414         return field || null;
45415     },
45416
45417     /**
45418      * Add a secondary form to this one, 
45419      * Used to provide tabbed forms. One form is primary, with hidden values 
45420      * which mirror the elements from the other forms.
45421      * 
45422      * @param {Roo.form.Form} form to add.
45423      * 
45424      */
45425     addForm : function(form)
45426     {
45427        
45428         if (this.childForms.indexOf(form) > -1) {
45429             // already added..
45430             return;
45431         }
45432         this.childForms.push(form);
45433         var n = '';
45434         Roo.each(form.allItems, function (fe) {
45435             
45436             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45437             if (this.findField(n)) { // already added..
45438                 return;
45439             }
45440             var add = new Roo.form.Hidden({
45441                 name : n
45442             });
45443             add.render(this.el);
45444             
45445             this.add( add );
45446         }, this);
45447         
45448     },
45449     /**
45450      * Mark fields in this form invalid in bulk.
45451      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45452      * @return {BasicForm} this
45453      */
45454     markInvalid : function(errors){
45455         if(errors instanceof Array){
45456             for(var i = 0, len = errors.length; i < len; i++){
45457                 var fieldError = errors[i];
45458                 var f = this.findField(fieldError.id);
45459                 if(f){
45460                     f.markInvalid(fieldError.msg);
45461                 }
45462             }
45463         }else{
45464             var field, id;
45465             for(id in errors){
45466                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45467                     field.markInvalid(errors[id]);
45468                 }
45469             }
45470         }
45471         Roo.each(this.childForms || [], function (f) {
45472             f.markInvalid(errors);
45473         });
45474         
45475         return this;
45476     },
45477
45478     /**
45479      * Set values for fields in this form in bulk.
45480      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45481      * @return {BasicForm} this
45482      */
45483     setValues : function(values){
45484         if(values instanceof Array){ // array of objects
45485             for(var i = 0, len = values.length; i < len; i++){
45486                 var v = values[i];
45487                 var f = this.findField(v.id);
45488                 if(f){
45489                     f.setValue(v.value);
45490                     if(this.trackResetOnLoad){
45491                         f.originalValue = f.getValue();
45492                     }
45493                 }
45494             }
45495         }else{ // object hash
45496             var field, id;
45497             for(id in values){
45498                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45499                     
45500                     if (field.setFromData && 
45501                         field.valueField && 
45502                         field.displayField &&
45503                         // combos' with local stores can 
45504                         // be queried via setValue()
45505                         // to set their value..
45506                         (field.store && !field.store.isLocal)
45507                         ) {
45508                         // it's a combo
45509                         var sd = { };
45510                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45511                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45512                         field.setFromData(sd);
45513                         
45514                     } else {
45515                         field.setValue(values[id]);
45516                     }
45517                     
45518                     
45519                     if(this.trackResetOnLoad){
45520                         field.originalValue = field.getValue();
45521                     }
45522                 }
45523             }
45524         }
45525         this.resetHasChanged();
45526         
45527         
45528         Roo.each(this.childForms || [], function (f) {
45529             f.setValues(values);
45530             f.resetHasChanged();
45531         });
45532                 
45533         return this;
45534     },
45535
45536     /**
45537      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45538      * they are returned as an array.
45539      * @param {Boolean} asString
45540      * @return {Object}
45541      */
45542     getValues : function(asString){
45543         if (this.childForms) {
45544             // copy values from the child forms
45545             Roo.each(this.childForms, function (f) {
45546                 this.setValues(f.getValues());
45547             }, this);
45548         }
45549         
45550         
45551         
45552         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45553         if(asString === true){
45554             return fs;
45555         }
45556         return Roo.urlDecode(fs);
45557     },
45558     
45559     /**
45560      * Returns the fields in this form as an object with key/value pairs. 
45561      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45562      * @return {Object}
45563      */
45564     getFieldValues : function(with_hidden)
45565     {
45566         if (this.childForms) {
45567             // copy values from the child forms
45568             // should this call getFieldValues - probably not as we do not currently copy
45569             // hidden fields when we generate..
45570             Roo.each(this.childForms, function (f) {
45571                 this.setValues(f.getValues());
45572             }, this);
45573         }
45574         
45575         var ret = {};
45576         this.items.each(function(f){
45577             if (!f.getName()) {
45578                 return;
45579             }
45580             var v = f.getValue();
45581             if (f.inputType =='radio') {
45582                 if (typeof(ret[f.getName()]) == 'undefined') {
45583                     ret[f.getName()] = ''; // empty..
45584                 }
45585                 
45586                 if (!f.el.dom.checked) {
45587                     return;
45588                     
45589                 }
45590                 v = f.el.dom.value;
45591                 
45592             }
45593             
45594             // not sure if this supported any more..
45595             if ((typeof(v) == 'object') && f.getRawValue) {
45596                 v = f.getRawValue() ; // dates..
45597             }
45598             // combo boxes where name != hiddenName...
45599             if (f.name != f.getName()) {
45600                 ret[f.name] = f.getRawValue();
45601             }
45602             ret[f.getName()] = v;
45603         });
45604         
45605         return ret;
45606     },
45607
45608     /**
45609      * Clears all invalid messages in this form.
45610      * @return {BasicForm} this
45611      */
45612     clearInvalid : function(){
45613         this.items.each(function(f){
45614            f.clearInvalid();
45615         });
45616         
45617         Roo.each(this.childForms || [], function (f) {
45618             f.clearInvalid();
45619         });
45620         
45621         
45622         return this;
45623     },
45624
45625     /**
45626      * Resets this form.
45627      * @return {BasicForm} this
45628      */
45629     reset : function(){
45630         this.items.each(function(f){
45631             f.reset();
45632         });
45633         
45634         Roo.each(this.childForms || [], function (f) {
45635             f.reset();
45636         });
45637         this.resetHasChanged();
45638         
45639         return this;
45640     },
45641
45642     /**
45643      * Add Roo.form components to this form.
45644      * @param {Field} field1
45645      * @param {Field} field2 (optional)
45646      * @param {Field} etc (optional)
45647      * @return {BasicForm} this
45648      */
45649     add : function(){
45650         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45651         return this;
45652     },
45653
45654
45655     /**
45656      * Removes a field from the items collection (does NOT remove its markup).
45657      * @param {Field} field
45658      * @return {BasicForm} this
45659      */
45660     remove : function(field){
45661         this.items.remove(field);
45662         return this;
45663     },
45664
45665     /**
45666      * Looks at the fields in this form, checks them for an id attribute,
45667      * and calls applyTo on the existing dom element with that id.
45668      * @return {BasicForm} this
45669      */
45670     render : function(){
45671         this.items.each(function(f){
45672             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45673                 f.applyTo(f.id);
45674             }
45675         });
45676         return this;
45677     },
45678
45679     /**
45680      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45681      * @param {Object} values
45682      * @return {BasicForm} this
45683      */
45684     applyToFields : function(o){
45685         this.items.each(function(f){
45686            Roo.apply(f, o);
45687         });
45688         return this;
45689     },
45690
45691     /**
45692      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45693      * @param {Object} values
45694      * @return {BasicForm} this
45695      */
45696     applyIfToFields : function(o){
45697         this.items.each(function(f){
45698            Roo.applyIf(f, o);
45699         });
45700         return this;
45701     }
45702 });
45703
45704 // back compat
45705 Roo.BasicForm = Roo.form.BasicForm;/*
45706  * Based on:
45707  * Ext JS Library 1.1.1
45708  * Copyright(c) 2006-2007, Ext JS, LLC.
45709  *
45710  * Originally Released Under LGPL - original licence link has changed is not relivant.
45711  *
45712  * Fork - LGPL
45713  * <script type="text/javascript">
45714  */
45715
45716 /**
45717  * @class Roo.form.Form
45718  * @extends Roo.form.BasicForm
45719  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45720  * @constructor
45721  * @param {Object} config Configuration options
45722  */
45723 Roo.form.Form = function(config){
45724     var xitems =  [];
45725     if (config.items) {
45726         xitems = config.items;
45727         delete config.items;
45728     }
45729    
45730     
45731     Roo.form.Form.superclass.constructor.call(this, null, config);
45732     this.url = this.url || this.action;
45733     if(!this.root){
45734         this.root = new Roo.form.Layout(Roo.applyIf({
45735             id: Roo.id()
45736         }, config));
45737     }
45738     this.active = this.root;
45739     /**
45740      * Array of all the buttons that have been added to this form via {@link addButton}
45741      * @type Array
45742      */
45743     this.buttons = [];
45744     this.allItems = [];
45745     this.addEvents({
45746         /**
45747          * @event clientvalidation
45748          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45749          * @param {Form} this
45750          * @param {Boolean} valid true if the form has passed client-side validation
45751          */
45752         clientvalidation: true,
45753         /**
45754          * @event rendered
45755          * Fires when the form is rendered
45756          * @param {Roo.form.Form} form
45757          */
45758         rendered : true
45759     });
45760     
45761     if (this.progressUrl) {
45762             // push a hidden field onto the list of fields..
45763             this.addxtype( {
45764                     xns: Roo.form, 
45765                     xtype : 'Hidden', 
45766                     name : 'UPLOAD_IDENTIFIER' 
45767             });
45768         }
45769         
45770     
45771     Roo.each(xitems, this.addxtype, this);
45772     
45773     
45774     
45775 };
45776
45777 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45778     /**
45779      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45780      */
45781     /**
45782      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45783      */
45784     /**
45785      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45786      */
45787     buttonAlign:'center',
45788
45789     /**
45790      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45791      */
45792     minButtonWidth:75,
45793
45794     /**
45795      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45796      * This property cascades to child containers if not set.
45797      */
45798     labelAlign:'left',
45799
45800     /**
45801      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45802      * fires a looping event with that state. This is required to bind buttons to the valid
45803      * state using the config value formBind:true on the button.
45804      */
45805     monitorValid : false,
45806
45807     /**
45808      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45809      */
45810     monitorPoll : 200,
45811     
45812     /**
45813      * @cfg {String} progressUrl - Url to return progress data 
45814      */
45815     
45816     progressUrl : false,
45817   
45818     /**
45819      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45820      * fields are added and the column is closed. If no fields are passed the column remains open
45821      * until end() is called.
45822      * @param {Object} config The config to pass to the column
45823      * @param {Field} field1 (optional)
45824      * @param {Field} field2 (optional)
45825      * @param {Field} etc (optional)
45826      * @return Column The column container object
45827      */
45828     column : function(c){
45829         var col = new Roo.form.Column(c);
45830         this.start(col);
45831         if(arguments.length > 1){ // duplicate code required because of Opera
45832             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45833             this.end();
45834         }
45835         return col;
45836     },
45837
45838     /**
45839      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45840      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45841      * until end() is called.
45842      * @param {Object} config The config to pass to the fieldset
45843      * @param {Field} field1 (optional)
45844      * @param {Field} field2 (optional)
45845      * @param {Field} etc (optional)
45846      * @return FieldSet The fieldset container object
45847      */
45848     fieldset : function(c){
45849         var fs = new Roo.form.FieldSet(c);
45850         this.start(fs);
45851         if(arguments.length > 1){ // duplicate code required because of Opera
45852             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45853             this.end();
45854         }
45855         return fs;
45856     },
45857
45858     /**
45859      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45860      * fields are added and the container is closed. If no fields are passed the container remains open
45861      * until end() is called.
45862      * @param {Object} config The config to pass to the Layout
45863      * @param {Field} field1 (optional)
45864      * @param {Field} field2 (optional)
45865      * @param {Field} etc (optional)
45866      * @return Layout The container object
45867      */
45868     container : function(c){
45869         var l = new Roo.form.Layout(c);
45870         this.start(l);
45871         if(arguments.length > 1){ // duplicate code required because of Opera
45872             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45873             this.end();
45874         }
45875         return l;
45876     },
45877
45878     /**
45879      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45880      * @param {Object} container A Roo.form.Layout or subclass of Layout
45881      * @return {Form} this
45882      */
45883     start : function(c){
45884         // cascade label info
45885         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45886         this.active.stack.push(c);
45887         c.ownerCt = this.active;
45888         this.active = c;
45889         return this;
45890     },
45891
45892     /**
45893      * Closes the current open container
45894      * @return {Form} this
45895      */
45896     end : function(){
45897         if(this.active == this.root){
45898             return this;
45899         }
45900         this.active = this.active.ownerCt;
45901         return this;
45902     },
45903
45904     /**
45905      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45906      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45907      * as the label of the field.
45908      * @param {Field} field1
45909      * @param {Field} field2 (optional)
45910      * @param {Field} etc. (optional)
45911      * @return {Form} this
45912      */
45913     add : function(){
45914         this.active.stack.push.apply(this.active.stack, arguments);
45915         this.allItems.push.apply(this.allItems,arguments);
45916         var r = [];
45917         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45918             if(a[i].isFormField){
45919                 r.push(a[i]);
45920             }
45921         }
45922         if(r.length > 0){
45923             Roo.form.Form.superclass.add.apply(this, r);
45924         }
45925         return this;
45926     },
45927     
45928
45929     
45930     
45931     
45932      /**
45933      * Find any element that has been added to a form, using it's ID or name
45934      * This can include framesets, columns etc. along with regular fields..
45935      * @param {String} id - id or name to find.
45936      
45937      * @return {Element} e - or false if nothing found.
45938      */
45939     findbyId : function(id)
45940     {
45941         var ret = false;
45942         if (!id) {
45943             return ret;
45944         }
45945         Roo.each(this.allItems, function(f){
45946             if (f.id == id || f.name == id ){
45947                 ret = f;
45948                 return false;
45949             }
45950         });
45951         return ret;
45952     },
45953
45954     
45955     
45956     /**
45957      * Render this form into the passed container. This should only be called once!
45958      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45959      * @return {Form} this
45960      */
45961     render : function(ct)
45962     {
45963         
45964         
45965         
45966         ct = Roo.get(ct);
45967         var o = this.autoCreate || {
45968             tag: 'form',
45969             method : this.method || 'POST',
45970             id : this.id || Roo.id()
45971         };
45972         this.initEl(ct.createChild(o));
45973
45974         this.root.render(this.el);
45975         
45976        
45977              
45978         this.items.each(function(f){
45979             f.render('x-form-el-'+f.id);
45980         });
45981
45982         if(this.buttons.length > 0){
45983             // tables are required to maintain order and for correct IE layout
45984             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45985                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45986                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45987             }}, null, true);
45988             var tr = tb.getElementsByTagName('tr')[0];
45989             for(var i = 0, len = this.buttons.length; i < len; i++) {
45990                 var b = this.buttons[i];
45991                 var td = document.createElement('td');
45992                 td.className = 'x-form-btn-td';
45993                 b.render(tr.appendChild(td));
45994             }
45995         }
45996         if(this.monitorValid){ // initialize after render
45997             this.startMonitoring();
45998         }
45999         this.fireEvent('rendered', this);
46000         return this;
46001     },
46002
46003     /**
46004      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
46005      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
46006      * object or a valid Roo.DomHelper element config
46007      * @param {Function} handler The function called when the button is clicked
46008      * @param {Object} scope (optional) The scope of the handler function
46009      * @return {Roo.Button}
46010      */
46011     addButton : function(config, handler, scope){
46012         var bc = {
46013             handler: handler,
46014             scope: scope,
46015             minWidth: this.minButtonWidth,
46016             hideParent:true
46017         };
46018         if(typeof config == "string"){
46019             bc.text = config;
46020         }else{
46021             Roo.apply(bc, config);
46022         }
46023         var btn = new Roo.Button(null, bc);
46024         this.buttons.push(btn);
46025         return btn;
46026     },
46027
46028      /**
46029      * Adds a series of form elements (using the xtype property as the factory method.
46030      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
46031      * @param {Object} config 
46032      */
46033     
46034     addxtype : function()
46035     {
46036         var ar = Array.prototype.slice.call(arguments, 0);
46037         var ret = false;
46038         for(var i = 0; i < ar.length; i++) {
46039             if (!ar[i]) {
46040                 continue; // skip -- if this happends something invalid got sent, we 
46041                 // should ignore it, as basically that interface element will not show up
46042                 // and that should be pretty obvious!!
46043             }
46044             
46045             if (Roo.form[ar[i].xtype]) {
46046                 ar[i].form = this;
46047                 var fe = Roo.factory(ar[i], Roo.form);
46048                 if (!ret) {
46049                     ret = fe;
46050                 }
46051                 fe.form = this;
46052                 if (fe.store) {
46053                     fe.store.form = this;
46054                 }
46055                 if (fe.isLayout) {  
46056                          
46057                     this.start(fe);
46058                     this.allItems.push(fe);
46059                     if (fe.items && fe.addxtype) {
46060                         fe.addxtype.apply(fe, fe.items);
46061                         delete fe.items;
46062                     }
46063                      this.end();
46064                     continue;
46065                 }
46066                 
46067                 
46068                  
46069                 this.add(fe);
46070               //  console.log('adding ' + ar[i].xtype);
46071             }
46072             if (ar[i].xtype == 'Button') {  
46073                 //console.log('adding button');
46074                 //console.log(ar[i]);
46075                 this.addButton(ar[i]);
46076                 this.allItems.push(fe);
46077                 continue;
46078             }
46079             
46080             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
46081                 alert('end is not supported on xtype any more, use items');
46082             //    this.end();
46083             //    //console.log('adding end');
46084             }
46085             
46086         }
46087         return ret;
46088     },
46089     
46090     /**
46091      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
46092      * option "monitorValid"
46093      */
46094     startMonitoring : function(){
46095         if(!this.bound){
46096             this.bound = true;
46097             Roo.TaskMgr.start({
46098                 run : this.bindHandler,
46099                 interval : this.monitorPoll || 200,
46100                 scope: this
46101             });
46102         }
46103     },
46104
46105     /**
46106      * Stops monitoring of the valid state of this form
46107      */
46108     stopMonitoring : function(){
46109         this.bound = false;
46110     },
46111
46112     // private
46113     bindHandler : function(){
46114         if(!this.bound){
46115             return false; // stops binding
46116         }
46117         var valid = true;
46118         this.items.each(function(f){
46119             if(!f.isValid(true)){
46120                 valid = false;
46121                 return false;
46122             }
46123         });
46124         for(var i = 0, len = this.buttons.length; i < len; i++){
46125             var btn = this.buttons[i];
46126             if(btn.formBind === true && btn.disabled === valid){
46127                 btn.setDisabled(!valid);
46128             }
46129         }
46130         this.fireEvent('clientvalidation', this, valid);
46131     }
46132     
46133     
46134     
46135     
46136     
46137     
46138     
46139     
46140 });
46141
46142
46143 // back compat
46144 Roo.Form = Roo.form.Form;
46145 /*
46146  * Based on:
46147  * Ext JS Library 1.1.1
46148  * Copyright(c) 2006-2007, Ext JS, LLC.
46149  *
46150  * Originally Released Under LGPL - original licence link has changed is not relivant.
46151  *
46152  * Fork - LGPL
46153  * <script type="text/javascript">
46154  */
46155
46156 // as we use this in bootstrap.
46157 Roo.namespace('Roo.form');
46158  /**
46159  * @class Roo.form.Action
46160  * Internal Class used to handle form actions
46161  * @constructor
46162  * @param {Roo.form.BasicForm} el The form element or its id
46163  * @param {Object} config Configuration options
46164  */
46165
46166  
46167  
46168 // define the action interface
46169 Roo.form.Action = function(form, options){
46170     this.form = form;
46171     this.options = options || {};
46172 };
46173 /**
46174  * Client Validation Failed
46175  * @const 
46176  */
46177 Roo.form.Action.CLIENT_INVALID = 'client';
46178 /**
46179  * Server Validation Failed
46180  * @const 
46181  */
46182 Roo.form.Action.SERVER_INVALID = 'server';
46183  /**
46184  * Connect to Server Failed
46185  * @const 
46186  */
46187 Roo.form.Action.CONNECT_FAILURE = 'connect';
46188 /**
46189  * Reading Data from Server Failed
46190  * @const 
46191  */
46192 Roo.form.Action.LOAD_FAILURE = 'load';
46193
46194 Roo.form.Action.prototype = {
46195     type : 'default',
46196     failureType : undefined,
46197     response : undefined,
46198     result : undefined,
46199
46200     // interface method
46201     run : function(options){
46202
46203     },
46204
46205     // interface method
46206     success : function(response){
46207
46208     },
46209
46210     // interface method
46211     handleResponse : function(response){
46212
46213     },
46214
46215     // default connection failure
46216     failure : function(response){
46217         
46218         this.response = response;
46219         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46220         this.form.afterAction(this, false);
46221     },
46222
46223     processResponse : function(response){
46224         this.response = response;
46225         if(!response.responseText){
46226             return true;
46227         }
46228         this.result = this.handleResponse(response);
46229         return this.result;
46230     },
46231
46232     // utility functions used internally
46233     getUrl : function(appendParams){
46234         var url = this.options.url || this.form.url || this.form.el.dom.action;
46235         if(appendParams){
46236             var p = this.getParams();
46237             if(p){
46238                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46239             }
46240         }
46241         return url;
46242     },
46243
46244     getMethod : function(){
46245         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46246     },
46247
46248     getParams : function(){
46249         var bp = this.form.baseParams;
46250         var p = this.options.params;
46251         if(p){
46252             if(typeof p == "object"){
46253                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46254             }else if(typeof p == 'string' && bp){
46255                 p += '&' + Roo.urlEncode(bp);
46256             }
46257         }else if(bp){
46258             p = Roo.urlEncode(bp);
46259         }
46260         return p;
46261     },
46262
46263     createCallback : function(){
46264         return {
46265             success: this.success,
46266             failure: this.failure,
46267             scope: this,
46268             timeout: (this.form.timeout*1000),
46269             upload: this.form.fileUpload ? this.success : undefined
46270         };
46271     }
46272 };
46273
46274 Roo.form.Action.Submit = function(form, options){
46275     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46276 };
46277
46278 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46279     type : 'submit',
46280
46281     haveProgress : false,
46282     uploadComplete : false,
46283     
46284     // uploadProgress indicator.
46285     uploadProgress : function()
46286     {
46287         if (!this.form.progressUrl) {
46288             return;
46289         }
46290         
46291         if (!this.haveProgress) {
46292             Roo.MessageBox.progress("Uploading", "Uploading");
46293         }
46294         if (this.uploadComplete) {
46295            Roo.MessageBox.hide();
46296            return;
46297         }
46298         
46299         this.haveProgress = true;
46300    
46301         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46302         
46303         var c = new Roo.data.Connection();
46304         c.request({
46305             url : this.form.progressUrl,
46306             params: {
46307                 id : uid
46308             },
46309             method: 'GET',
46310             success : function(req){
46311                //console.log(data);
46312                 var rdata = false;
46313                 var edata;
46314                 try  {
46315                    rdata = Roo.decode(req.responseText)
46316                 } catch (e) {
46317                     Roo.log("Invalid data from server..");
46318                     Roo.log(edata);
46319                     return;
46320                 }
46321                 if (!rdata || !rdata.success) {
46322                     Roo.log(rdata);
46323                     Roo.MessageBox.alert(Roo.encode(rdata));
46324                     return;
46325                 }
46326                 var data = rdata.data;
46327                 
46328                 if (this.uploadComplete) {
46329                    Roo.MessageBox.hide();
46330                    return;
46331                 }
46332                    
46333                 if (data){
46334                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46335                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46336                     );
46337                 }
46338                 this.uploadProgress.defer(2000,this);
46339             },
46340        
46341             failure: function(data) {
46342                 Roo.log('progress url failed ');
46343                 Roo.log(data);
46344             },
46345             scope : this
46346         });
46347            
46348     },
46349     
46350     
46351     run : function()
46352     {
46353         // run get Values on the form, so it syncs any secondary forms.
46354         this.form.getValues();
46355         
46356         var o = this.options;
46357         var method = this.getMethod();
46358         var isPost = method == 'POST';
46359         if(o.clientValidation === false || this.form.isValid()){
46360             
46361             if (this.form.progressUrl) {
46362                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46363                     (new Date() * 1) + '' + Math.random());
46364                     
46365             } 
46366             
46367             
46368             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46369                 form:this.form.el.dom,
46370                 url:this.getUrl(!isPost),
46371                 method: method,
46372                 params:isPost ? this.getParams() : null,
46373                 isUpload: this.form.fileUpload
46374             }));
46375             
46376             this.uploadProgress();
46377
46378         }else if (o.clientValidation !== false){ // client validation failed
46379             this.failureType = Roo.form.Action.CLIENT_INVALID;
46380             this.form.afterAction(this, false);
46381         }
46382     },
46383
46384     success : function(response)
46385     {
46386         this.uploadComplete= true;
46387         if (this.haveProgress) {
46388             Roo.MessageBox.hide();
46389         }
46390         
46391         
46392         var result = this.processResponse(response);
46393         if(result === true || result.success){
46394             this.form.afterAction(this, true);
46395             return;
46396         }
46397         if(result.errors){
46398             this.form.markInvalid(result.errors);
46399             this.failureType = Roo.form.Action.SERVER_INVALID;
46400         }
46401         this.form.afterAction(this, false);
46402     },
46403     failure : function(response)
46404     {
46405         this.uploadComplete= true;
46406         if (this.haveProgress) {
46407             Roo.MessageBox.hide();
46408         }
46409         
46410         this.response = response;
46411         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46412         this.form.afterAction(this, false);
46413     },
46414     
46415     handleResponse : function(response){
46416         if(this.form.errorReader){
46417             var rs = this.form.errorReader.read(response);
46418             var errors = [];
46419             if(rs.records){
46420                 for(var i = 0, len = rs.records.length; i < len; i++) {
46421                     var r = rs.records[i];
46422                     errors[i] = r.data;
46423                 }
46424             }
46425             if(errors.length < 1){
46426                 errors = null;
46427             }
46428             return {
46429                 success : rs.success,
46430                 errors : errors
46431             };
46432         }
46433         var ret = false;
46434         try {
46435             ret = Roo.decode(response.responseText);
46436         } catch (e) {
46437             ret = {
46438                 success: false,
46439                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46440                 errors : []
46441             };
46442         }
46443         return ret;
46444         
46445     }
46446 });
46447
46448
46449 Roo.form.Action.Load = function(form, options){
46450     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46451     this.reader = this.form.reader;
46452 };
46453
46454 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46455     type : 'load',
46456
46457     run : function(){
46458         
46459         Roo.Ajax.request(Roo.apply(
46460                 this.createCallback(), {
46461                     method:this.getMethod(),
46462                     url:this.getUrl(false),
46463                     params:this.getParams()
46464         }));
46465     },
46466
46467     success : function(response){
46468         
46469         var result = this.processResponse(response);
46470         if(result === true || !result.success || !result.data){
46471             this.failureType = Roo.form.Action.LOAD_FAILURE;
46472             this.form.afterAction(this, false);
46473             return;
46474         }
46475         this.form.clearInvalid();
46476         this.form.setValues(result.data);
46477         this.form.afterAction(this, true);
46478     },
46479
46480     handleResponse : function(response){
46481         if(this.form.reader){
46482             var rs = this.form.reader.read(response);
46483             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46484             return {
46485                 success : rs.success,
46486                 data : data
46487             };
46488         }
46489         return Roo.decode(response.responseText);
46490     }
46491 });
46492
46493 Roo.form.Action.ACTION_TYPES = {
46494     'load' : Roo.form.Action.Load,
46495     'submit' : Roo.form.Action.Submit
46496 };/*
46497  * Based on:
46498  * Ext JS Library 1.1.1
46499  * Copyright(c) 2006-2007, Ext JS, LLC.
46500  *
46501  * Originally Released Under LGPL - original licence link has changed is not relivant.
46502  *
46503  * Fork - LGPL
46504  * <script type="text/javascript">
46505  */
46506  
46507 /**
46508  * @class Roo.form.Layout
46509  * @extends Roo.Component
46510  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46511  * @constructor
46512  * @param {Object} config Configuration options
46513  */
46514 Roo.form.Layout = function(config){
46515     var xitems = [];
46516     if (config.items) {
46517         xitems = config.items;
46518         delete config.items;
46519     }
46520     Roo.form.Layout.superclass.constructor.call(this, config);
46521     this.stack = [];
46522     Roo.each(xitems, this.addxtype, this);
46523      
46524 };
46525
46526 Roo.extend(Roo.form.Layout, Roo.Component, {
46527     /**
46528      * @cfg {String/Object} autoCreate
46529      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46530      */
46531     /**
46532      * @cfg {String/Object/Function} style
46533      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46534      * a function which returns such a specification.
46535      */
46536     /**
46537      * @cfg {String} labelAlign
46538      * Valid values are "left," "top" and "right" (defaults to "left")
46539      */
46540     /**
46541      * @cfg {Number} labelWidth
46542      * Fixed width in pixels of all field labels (defaults to undefined)
46543      */
46544     /**
46545      * @cfg {Boolean} clear
46546      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46547      */
46548     clear : true,
46549     /**
46550      * @cfg {String} labelSeparator
46551      * The separator to use after field labels (defaults to ':')
46552      */
46553     labelSeparator : ':',
46554     /**
46555      * @cfg {Boolean} hideLabels
46556      * True to suppress the display of field labels in this layout (defaults to false)
46557      */
46558     hideLabels : false,
46559
46560     // private
46561     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46562     
46563     isLayout : true,
46564     
46565     // private
46566     onRender : function(ct, position){
46567         if(this.el){ // from markup
46568             this.el = Roo.get(this.el);
46569         }else {  // generate
46570             var cfg = this.getAutoCreate();
46571             this.el = ct.createChild(cfg, position);
46572         }
46573         if(this.style){
46574             this.el.applyStyles(this.style);
46575         }
46576         if(this.labelAlign){
46577             this.el.addClass('x-form-label-'+this.labelAlign);
46578         }
46579         if(this.hideLabels){
46580             this.labelStyle = "display:none";
46581             this.elementStyle = "padding-left:0;";
46582         }else{
46583             if(typeof this.labelWidth == 'number'){
46584                 this.labelStyle = "width:"+this.labelWidth+"px;";
46585                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46586             }
46587             if(this.labelAlign == 'top'){
46588                 this.labelStyle = "width:auto;";
46589                 this.elementStyle = "padding-left:0;";
46590             }
46591         }
46592         var stack = this.stack;
46593         var slen = stack.length;
46594         if(slen > 0){
46595             if(!this.fieldTpl){
46596                 var t = new Roo.Template(
46597                     '<div class="x-form-item {5}">',
46598                         '<label for="{0}" style="{2}">{1}{4}</label>',
46599                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46600                         '</div>',
46601                     '</div><div class="x-form-clear-left"></div>'
46602                 );
46603                 t.disableFormats = true;
46604                 t.compile();
46605                 Roo.form.Layout.prototype.fieldTpl = t;
46606             }
46607             for(var i = 0; i < slen; i++) {
46608                 if(stack[i].isFormField){
46609                     this.renderField(stack[i]);
46610                 }else{
46611                     this.renderComponent(stack[i]);
46612                 }
46613             }
46614         }
46615         if(this.clear){
46616             this.el.createChild({cls:'x-form-clear'});
46617         }
46618     },
46619
46620     // private
46621     renderField : function(f){
46622         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46623                f.id, //0
46624                f.fieldLabel, //1
46625                f.labelStyle||this.labelStyle||'', //2
46626                this.elementStyle||'', //3
46627                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46628                f.itemCls||this.itemCls||''  //5
46629        ], true).getPrevSibling());
46630     },
46631
46632     // private
46633     renderComponent : function(c){
46634         c.render(c.isLayout ? this.el : this.el.createChild());    
46635     },
46636     /**
46637      * Adds a object form elements (using the xtype property as the factory method.)
46638      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46639      * @param {Object} config 
46640      */
46641     addxtype : function(o)
46642     {
46643         // create the lement.
46644         o.form = this.form;
46645         var fe = Roo.factory(o, Roo.form);
46646         this.form.allItems.push(fe);
46647         this.stack.push(fe);
46648         
46649         if (fe.isFormField) {
46650             this.form.items.add(fe);
46651         }
46652          
46653         return fe;
46654     }
46655 });
46656
46657 /**
46658  * @class Roo.form.Column
46659  * @extends Roo.form.Layout
46660  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46661  * @constructor
46662  * @param {Object} config Configuration options
46663  */
46664 Roo.form.Column = function(config){
46665     Roo.form.Column.superclass.constructor.call(this, config);
46666 };
46667
46668 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46669     /**
46670      * @cfg {Number/String} width
46671      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46672      */
46673     /**
46674      * @cfg {String/Object} autoCreate
46675      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46676      */
46677
46678     // private
46679     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46680
46681     // private
46682     onRender : function(ct, position){
46683         Roo.form.Column.superclass.onRender.call(this, ct, position);
46684         if(this.width){
46685             this.el.setWidth(this.width);
46686         }
46687     }
46688 });
46689
46690
46691 /**
46692  * @class Roo.form.Row
46693  * @extends Roo.form.Layout
46694  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46695  * @constructor
46696  * @param {Object} config Configuration options
46697  */
46698
46699  
46700 Roo.form.Row = function(config){
46701     Roo.form.Row.superclass.constructor.call(this, config);
46702 };
46703  
46704 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46705       /**
46706      * @cfg {Number/String} width
46707      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46708      */
46709     /**
46710      * @cfg {Number/String} height
46711      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46712      */
46713     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46714     
46715     padWidth : 20,
46716     // private
46717     onRender : function(ct, position){
46718         //console.log('row render');
46719         if(!this.rowTpl){
46720             var t = new Roo.Template(
46721                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46722                     '<label for="{0}" style="{2}">{1}{4}</label>',
46723                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46724                     '</div>',
46725                 '</div>'
46726             );
46727             t.disableFormats = true;
46728             t.compile();
46729             Roo.form.Layout.prototype.rowTpl = t;
46730         }
46731         this.fieldTpl = this.rowTpl;
46732         
46733         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46734         var labelWidth = 100;
46735         
46736         if ((this.labelAlign != 'top')) {
46737             if (typeof this.labelWidth == 'number') {
46738                 labelWidth = this.labelWidth
46739             }
46740             this.padWidth =  20 + labelWidth;
46741             
46742         }
46743         
46744         Roo.form.Column.superclass.onRender.call(this, ct, position);
46745         if(this.width){
46746             this.el.setWidth(this.width);
46747         }
46748         if(this.height){
46749             this.el.setHeight(this.height);
46750         }
46751     },
46752     
46753     // private
46754     renderField : function(f){
46755         f.fieldEl = this.fieldTpl.append(this.el, [
46756                f.id, f.fieldLabel,
46757                f.labelStyle||this.labelStyle||'',
46758                this.elementStyle||'',
46759                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46760                f.itemCls||this.itemCls||'',
46761                f.width ? f.width + this.padWidth : 160 + this.padWidth
46762        ],true);
46763     }
46764 });
46765  
46766
46767 /**
46768  * @class Roo.form.FieldSet
46769  * @extends Roo.form.Layout
46770  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46771  * @constructor
46772  * @param {Object} config Configuration options
46773  */
46774 Roo.form.FieldSet = function(config){
46775     Roo.form.FieldSet.superclass.constructor.call(this, config);
46776 };
46777
46778 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46779     /**
46780      * @cfg {String} legend
46781      * The text to display as the legend for the FieldSet (defaults to '')
46782      */
46783     /**
46784      * @cfg {String/Object} autoCreate
46785      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46786      */
46787
46788     // private
46789     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46790
46791     // private
46792     onRender : function(ct, position){
46793         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46794         if(this.legend){
46795             this.setLegend(this.legend);
46796         }
46797     },
46798
46799     // private
46800     setLegend : function(text){
46801         if(this.rendered){
46802             this.el.child('legend').update(text);
46803         }
46804     }
46805 });/*
46806  * Based on:
46807  * Ext JS Library 1.1.1
46808  * Copyright(c) 2006-2007, Ext JS, LLC.
46809  *
46810  * Originally Released Under LGPL - original licence link has changed is not relivant.
46811  *
46812  * Fork - LGPL
46813  * <script type="text/javascript">
46814  */
46815 /**
46816  * @class Roo.form.VTypes
46817  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46818  * @singleton
46819  */
46820 Roo.form.VTypes = function(){
46821     // closure these in so they are only created once.
46822     var alpha = /^[a-zA-Z_]+$/;
46823     var alphanum = /^[a-zA-Z0-9_]+$/;
46824     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
46825     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46826
46827     // All these messages and functions are configurable
46828     return {
46829         /**
46830          * The function used to validate email addresses
46831          * @param {String} value The email address
46832          */
46833         'email' : function(v){
46834             return email.test(v);
46835         },
46836         /**
46837          * The error text to display when the email validation function returns false
46838          * @type String
46839          */
46840         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46841         /**
46842          * The keystroke filter mask to be applied on email input
46843          * @type RegExp
46844          */
46845         'emailMask' : /[a-z0-9_\.\-@]/i,
46846
46847         /**
46848          * The function used to validate URLs
46849          * @param {String} value The URL
46850          */
46851         'url' : function(v){
46852             return url.test(v);
46853         },
46854         /**
46855          * The error text to display when the url validation function returns false
46856          * @type String
46857          */
46858         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46859         
46860         /**
46861          * The function used to validate alpha values
46862          * @param {String} value The value
46863          */
46864         'alpha' : function(v){
46865             return alpha.test(v);
46866         },
46867         /**
46868          * The error text to display when the alpha validation function returns false
46869          * @type String
46870          */
46871         'alphaText' : 'This field should only contain letters and _',
46872         /**
46873          * The keystroke filter mask to be applied on alpha input
46874          * @type RegExp
46875          */
46876         'alphaMask' : /[a-z_]/i,
46877
46878         /**
46879          * The function used to validate alphanumeric values
46880          * @param {String} value The value
46881          */
46882         'alphanum' : function(v){
46883             return alphanum.test(v);
46884         },
46885         /**
46886          * The error text to display when the alphanumeric validation function returns false
46887          * @type String
46888          */
46889         'alphanumText' : 'This field should only contain letters, numbers and _',
46890         /**
46891          * The keystroke filter mask to be applied on alphanumeric input
46892          * @type RegExp
46893          */
46894         'alphanumMask' : /[a-z0-9_]/i
46895     };
46896 }();//<script type="text/javascript">
46897
46898 /**
46899  * @class Roo.form.FCKeditor
46900  * @extends Roo.form.TextArea
46901  * Wrapper around the FCKEditor http://www.fckeditor.net
46902  * @constructor
46903  * Creates a new FCKeditor
46904  * @param {Object} config Configuration options
46905  */
46906 Roo.form.FCKeditor = function(config){
46907     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46908     this.addEvents({
46909          /**
46910          * @event editorinit
46911          * Fired when the editor is initialized - you can add extra handlers here..
46912          * @param {FCKeditor} this
46913          * @param {Object} the FCK object.
46914          */
46915         editorinit : true
46916     });
46917     
46918     
46919 };
46920 Roo.form.FCKeditor.editors = { };
46921 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46922 {
46923     //defaultAutoCreate : {
46924     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46925     //},
46926     // private
46927     /**
46928      * @cfg {Object} fck options - see fck manual for details.
46929      */
46930     fckconfig : false,
46931     
46932     /**
46933      * @cfg {Object} fck toolbar set (Basic or Default)
46934      */
46935     toolbarSet : 'Basic',
46936     /**
46937      * @cfg {Object} fck BasePath
46938      */ 
46939     basePath : '/fckeditor/',
46940     
46941     
46942     frame : false,
46943     
46944     value : '',
46945     
46946    
46947     onRender : function(ct, position)
46948     {
46949         if(!this.el){
46950             this.defaultAutoCreate = {
46951                 tag: "textarea",
46952                 style:"width:300px;height:60px;",
46953                 autocomplete: "new-password"
46954             };
46955         }
46956         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46957         /*
46958         if(this.grow){
46959             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46960             if(this.preventScrollbars){
46961                 this.el.setStyle("overflow", "hidden");
46962             }
46963             this.el.setHeight(this.growMin);
46964         }
46965         */
46966         //console.log('onrender' + this.getId() );
46967         Roo.form.FCKeditor.editors[this.getId()] = this;
46968          
46969
46970         this.replaceTextarea() ;
46971         
46972     },
46973     
46974     getEditor : function() {
46975         return this.fckEditor;
46976     },
46977     /**
46978      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46979      * @param {Mixed} value The value to set
46980      */
46981     
46982     
46983     setValue : function(value)
46984     {
46985         //console.log('setValue: ' + value);
46986         
46987         if(typeof(value) == 'undefined') { // not sure why this is happending...
46988             return;
46989         }
46990         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46991         
46992         //if(!this.el || !this.getEditor()) {
46993         //    this.value = value;
46994             //this.setValue.defer(100,this,[value]);    
46995         //    return;
46996         //} 
46997         
46998         if(!this.getEditor()) {
46999             return;
47000         }
47001         
47002         this.getEditor().SetData(value);
47003         
47004         //
47005
47006     },
47007
47008     /**
47009      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
47010      * @return {Mixed} value The field value
47011      */
47012     getValue : function()
47013     {
47014         
47015         if (this.frame && this.frame.dom.style.display == 'none') {
47016             return Roo.form.FCKeditor.superclass.getValue.call(this);
47017         }
47018         
47019         if(!this.el || !this.getEditor()) {
47020            
47021            // this.getValue.defer(100,this); 
47022             return this.value;
47023         }
47024        
47025         
47026         var value=this.getEditor().GetData();
47027         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
47028         return Roo.form.FCKeditor.superclass.getValue.call(this);
47029         
47030
47031     },
47032
47033     /**
47034      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
47035      * @return {Mixed} value The field value
47036      */
47037     getRawValue : function()
47038     {
47039         if (this.frame && this.frame.dom.style.display == 'none') {
47040             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
47041         }
47042         
47043         if(!this.el || !this.getEditor()) {
47044             //this.getRawValue.defer(100,this); 
47045             return this.value;
47046             return;
47047         }
47048         
47049         
47050         
47051         var value=this.getEditor().GetData();
47052         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
47053         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
47054          
47055     },
47056     
47057     setSize : function(w,h) {
47058         
47059         
47060         
47061         //if (this.frame && this.frame.dom.style.display == 'none') {
47062         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
47063         //    return;
47064         //}
47065         //if(!this.el || !this.getEditor()) {
47066         //    this.setSize.defer(100,this, [w,h]); 
47067         //    return;
47068         //}
47069         
47070         
47071         
47072         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
47073         
47074         this.frame.dom.setAttribute('width', w);
47075         this.frame.dom.setAttribute('height', h);
47076         this.frame.setSize(w,h);
47077         
47078     },
47079     
47080     toggleSourceEdit : function(value) {
47081         
47082       
47083          
47084         this.el.dom.style.display = value ? '' : 'none';
47085         this.frame.dom.style.display = value ?  'none' : '';
47086         
47087     },
47088     
47089     
47090     focus: function(tag)
47091     {
47092         if (this.frame.dom.style.display == 'none') {
47093             return Roo.form.FCKeditor.superclass.focus.call(this);
47094         }
47095         if(!this.el || !this.getEditor()) {
47096             this.focus.defer(100,this, [tag]); 
47097             return;
47098         }
47099         
47100         
47101         
47102         
47103         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
47104         this.getEditor().Focus();
47105         if (tgs.length) {
47106             if (!this.getEditor().Selection.GetSelection()) {
47107                 this.focus.defer(100,this, [tag]); 
47108                 return;
47109             }
47110             
47111             
47112             var r = this.getEditor().EditorDocument.createRange();
47113             r.setStart(tgs[0],0);
47114             r.setEnd(tgs[0],0);
47115             this.getEditor().Selection.GetSelection().removeAllRanges();
47116             this.getEditor().Selection.GetSelection().addRange(r);
47117             this.getEditor().Focus();
47118         }
47119         
47120     },
47121     
47122     
47123     
47124     replaceTextarea : function()
47125     {
47126         if ( document.getElementById( this.getId() + '___Frame' ) ) {
47127             return ;
47128         }
47129         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
47130         //{
47131             // We must check the elements firstly using the Id and then the name.
47132         var oTextarea = document.getElementById( this.getId() );
47133         
47134         var colElementsByName = document.getElementsByName( this.getId() ) ;
47135          
47136         oTextarea.style.display = 'none' ;
47137
47138         if ( oTextarea.tabIndex ) {            
47139             this.TabIndex = oTextarea.tabIndex ;
47140         }
47141         
47142         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
47143         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
47144         this.frame = Roo.get(this.getId() + '___Frame')
47145     },
47146     
47147     _getConfigHtml : function()
47148     {
47149         var sConfig = '' ;
47150
47151         for ( var o in this.fckconfig ) {
47152             sConfig += sConfig.length > 0  ? '&amp;' : '';
47153             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
47154         }
47155
47156         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
47157     },
47158     
47159     
47160     _getIFrameHtml : function()
47161     {
47162         var sFile = 'fckeditor.html' ;
47163         /* no idea what this is about..
47164         try
47165         {
47166             if ( (/fcksource=true/i).test( window.top.location.search ) )
47167                 sFile = 'fckeditor.original.html' ;
47168         }
47169         catch (e) { 
47170         */
47171
47172         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
47173         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
47174         
47175         
47176         var html = '<iframe id="' + this.getId() +
47177             '___Frame" src="' + sLink +
47178             '" width="' + this.width +
47179             '" height="' + this.height + '"' +
47180             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
47181             ' frameborder="0" scrolling="no"></iframe>' ;
47182
47183         return html ;
47184     },
47185     
47186     _insertHtmlBefore : function( html, element )
47187     {
47188         if ( element.insertAdjacentHTML )       {
47189             // IE
47190             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47191         } else { // Gecko
47192             var oRange = document.createRange() ;
47193             oRange.setStartBefore( element ) ;
47194             var oFragment = oRange.createContextualFragment( html );
47195             element.parentNode.insertBefore( oFragment, element ) ;
47196         }
47197     }
47198     
47199     
47200   
47201     
47202     
47203     
47204     
47205
47206 });
47207
47208 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47209
47210 function FCKeditor_OnComplete(editorInstance){
47211     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47212     f.fckEditor = editorInstance;
47213     //console.log("loaded");
47214     f.fireEvent('editorinit', f, editorInstance);
47215
47216   
47217
47218  
47219
47220
47221
47222
47223
47224
47225
47226
47227
47228
47229
47230
47231
47232
47233
47234 //<script type="text/javascript">
47235 /**
47236  * @class Roo.form.GridField
47237  * @extends Roo.form.Field
47238  * Embed a grid (or editable grid into a form)
47239  * STATUS ALPHA
47240  * 
47241  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47242  * it needs 
47243  * xgrid.store = Roo.data.Store
47244  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47245  * xgrid.store.reader = Roo.data.JsonReader 
47246  * 
47247  * 
47248  * @constructor
47249  * Creates a new GridField
47250  * @param {Object} config Configuration options
47251  */
47252 Roo.form.GridField = function(config){
47253     Roo.form.GridField.superclass.constructor.call(this, config);
47254      
47255 };
47256
47257 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47258     /**
47259      * @cfg {Number} width  - used to restrict width of grid..
47260      */
47261     width : 100,
47262     /**
47263      * @cfg {Number} height - used to restrict height of grid..
47264      */
47265     height : 50,
47266      /**
47267      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47268          * 
47269          *}
47270      */
47271     xgrid : false, 
47272     /**
47273      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47274      * {tag: "input", type: "checkbox", autocomplete: "off"})
47275      */
47276    // defaultAutoCreate : { tag: 'div' },
47277     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47278     /**
47279      * @cfg {String} addTitle Text to include for adding a title.
47280      */
47281     addTitle : false,
47282     //
47283     onResize : function(){
47284         Roo.form.Field.superclass.onResize.apply(this, arguments);
47285     },
47286
47287     initEvents : function(){
47288         // Roo.form.Checkbox.superclass.initEvents.call(this);
47289         // has no events...
47290        
47291     },
47292
47293
47294     getResizeEl : function(){
47295         return this.wrap;
47296     },
47297
47298     getPositionEl : function(){
47299         return this.wrap;
47300     },
47301
47302     // private
47303     onRender : function(ct, position){
47304         
47305         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47306         var style = this.style;
47307         delete this.style;
47308         
47309         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47310         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47311         this.viewEl = this.wrap.createChild({ tag: 'div' });
47312         if (style) {
47313             this.viewEl.applyStyles(style);
47314         }
47315         if (this.width) {
47316             this.viewEl.setWidth(this.width);
47317         }
47318         if (this.height) {
47319             this.viewEl.setHeight(this.height);
47320         }
47321         //if(this.inputValue !== undefined){
47322         //this.setValue(this.value);
47323         
47324         
47325         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47326         
47327         
47328         this.grid.render();
47329         this.grid.getDataSource().on('remove', this.refreshValue, this);
47330         this.grid.getDataSource().on('update', this.refreshValue, this);
47331         this.grid.on('afteredit', this.refreshValue, this);
47332  
47333     },
47334      
47335     
47336     /**
47337      * Sets the value of the item. 
47338      * @param {String} either an object  or a string..
47339      */
47340     setValue : function(v){
47341         //this.value = v;
47342         v = v || []; // empty set..
47343         // this does not seem smart - it really only affects memoryproxy grids..
47344         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47345             var ds = this.grid.getDataSource();
47346             // assumes a json reader..
47347             var data = {}
47348             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47349             ds.loadData( data);
47350         }
47351         // clear selection so it does not get stale.
47352         if (this.grid.sm) { 
47353             this.grid.sm.clearSelections();
47354         }
47355         
47356         Roo.form.GridField.superclass.setValue.call(this, v);
47357         this.refreshValue();
47358         // should load data in the grid really....
47359     },
47360     
47361     // private
47362     refreshValue: function() {
47363          var val = [];
47364         this.grid.getDataSource().each(function(r) {
47365             val.push(r.data);
47366         });
47367         this.el.dom.value = Roo.encode(val);
47368     }
47369     
47370      
47371     
47372     
47373 });/*
47374  * Based on:
47375  * Ext JS Library 1.1.1
47376  * Copyright(c) 2006-2007, Ext JS, LLC.
47377  *
47378  * Originally Released Under LGPL - original licence link has changed is not relivant.
47379  *
47380  * Fork - LGPL
47381  * <script type="text/javascript">
47382  */
47383 /**
47384  * @class Roo.form.DisplayField
47385  * @extends Roo.form.Field
47386  * A generic Field to display non-editable data.
47387  * @cfg {Boolean} closable (true|false) default false
47388  * @constructor
47389  * Creates a new Display Field item.
47390  * @param {Object} config Configuration options
47391  */
47392 Roo.form.DisplayField = function(config){
47393     Roo.form.DisplayField.superclass.constructor.call(this, config);
47394     
47395     this.addEvents({
47396         /**
47397          * @event close
47398          * Fires after the click the close btn
47399              * @param {Roo.form.DisplayField} this
47400              */
47401         close : true
47402     });
47403 };
47404
47405 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47406     inputType:      'hidden',
47407     allowBlank:     true,
47408     readOnly:         true,
47409     
47410  
47411     /**
47412      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47413      */
47414     focusClass : undefined,
47415     /**
47416      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47417      */
47418     fieldClass: 'x-form-field',
47419     
47420      /**
47421      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47422      */
47423     valueRenderer: undefined,
47424     
47425     width: 100,
47426     /**
47427      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47428      * {tag: "input", type: "checkbox", autocomplete: "off"})
47429      */
47430      
47431  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47432  
47433     closable : false,
47434     
47435     onResize : function(){
47436         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47437         
47438     },
47439
47440     initEvents : function(){
47441         // Roo.form.Checkbox.superclass.initEvents.call(this);
47442         // has no events...
47443         
47444         if(this.closable){
47445             this.closeEl.on('click', this.onClose, this);
47446         }
47447        
47448     },
47449
47450
47451     getResizeEl : function(){
47452         return this.wrap;
47453     },
47454
47455     getPositionEl : function(){
47456         return this.wrap;
47457     },
47458
47459     // private
47460     onRender : function(ct, position){
47461         
47462         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47463         //if(this.inputValue !== undefined){
47464         this.wrap = this.el.wrap();
47465         
47466         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47467         
47468         if(this.closable){
47469             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
47470         }
47471         
47472         if (this.bodyStyle) {
47473             this.viewEl.applyStyles(this.bodyStyle);
47474         }
47475         //this.viewEl.setStyle('padding', '2px');
47476         
47477         this.setValue(this.value);
47478         
47479     },
47480 /*
47481     // private
47482     initValue : Roo.emptyFn,
47483
47484   */
47485
47486         // private
47487     onClick : function(){
47488         
47489     },
47490
47491     /**
47492      * Sets the checked state of the checkbox.
47493      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47494      */
47495     setValue : function(v){
47496         this.value = v;
47497         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47498         // this might be called before we have a dom element..
47499         if (!this.viewEl) {
47500             return;
47501         }
47502         this.viewEl.dom.innerHTML = html;
47503         Roo.form.DisplayField.superclass.setValue.call(this, v);
47504
47505     },
47506     
47507     onClose : function(e)
47508     {
47509         e.preventDefault();
47510         
47511         this.fireEvent('close', this);
47512     }
47513 });/*
47514  * 
47515  * Licence- LGPL
47516  * 
47517  */
47518
47519 /**
47520  * @class Roo.form.DayPicker
47521  * @extends Roo.form.Field
47522  * A Day picker show [M] [T] [W] ....
47523  * @constructor
47524  * Creates a new Day Picker
47525  * @param {Object} config Configuration options
47526  */
47527 Roo.form.DayPicker= function(config){
47528     Roo.form.DayPicker.superclass.constructor.call(this, config);
47529      
47530 };
47531
47532 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47533     /**
47534      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47535      */
47536     focusClass : undefined,
47537     /**
47538      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47539      */
47540     fieldClass: "x-form-field",
47541    
47542     /**
47543      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47544      * {tag: "input", type: "checkbox", autocomplete: "off"})
47545      */
47546     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47547     
47548    
47549     actionMode : 'viewEl', 
47550     //
47551     // private
47552  
47553     inputType : 'hidden',
47554     
47555      
47556     inputElement: false, // real input element?
47557     basedOn: false, // ????
47558     
47559     isFormField: true, // not sure where this is needed!!!!
47560
47561     onResize : function(){
47562         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47563         if(!this.boxLabel){
47564             this.el.alignTo(this.wrap, 'c-c');
47565         }
47566     },
47567
47568     initEvents : function(){
47569         Roo.form.Checkbox.superclass.initEvents.call(this);
47570         this.el.on("click", this.onClick,  this);
47571         this.el.on("change", this.onClick,  this);
47572     },
47573
47574
47575     getResizeEl : function(){
47576         return this.wrap;
47577     },
47578
47579     getPositionEl : function(){
47580         return this.wrap;
47581     },
47582
47583     
47584     // private
47585     onRender : function(ct, position){
47586         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47587        
47588         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47589         
47590         var r1 = '<table><tr>';
47591         var r2 = '<tr class="x-form-daypick-icons">';
47592         for (var i=0; i < 7; i++) {
47593             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47594             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47595         }
47596         
47597         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47598         viewEl.select('img').on('click', this.onClick, this);
47599         this.viewEl = viewEl;   
47600         
47601         
47602         // this will not work on Chrome!!!
47603         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47604         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47605         
47606         
47607           
47608
47609     },
47610
47611     // private
47612     initValue : Roo.emptyFn,
47613
47614     /**
47615      * Returns the checked state of the checkbox.
47616      * @return {Boolean} True if checked, else false
47617      */
47618     getValue : function(){
47619         return this.el.dom.value;
47620         
47621     },
47622
47623         // private
47624     onClick : function(e){ 
47625         //this.setChecked(!this.checked);
47626         Roo.get(e.target).toggleClass('x-menu-item-checked');
47627         this.refreshValue();
47628         //if(this.el.dom.checked != this.checked){
47629         //    this.setValue(this.el.dom.checked);
47630        // }
47631     },
47632     
47633     // private
47634     refreshValue : function()
47635     {
47636         var val = '';
47637         this.viewEl.select('img',true).each(function(e,i,n)  {
47638             val += e.is(".x-menu-item-checked") ? String(n) : '';
47639         });
47640         this.setValue(val, true);
47641     },
47642
47643     /**
47644      * Sets the checked state of the checkbox.
47645      * On is always based on a string comparison between inputValue and the param.
47646      * @param {Boolean/String} value - the value to set 
47647      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47648      */
47649     setValue : function(v,suppressEvent){
47650         if (!this.el.dom) {
47651             return;
47652         }
47653         var old = this.el.dom.value ;
47654         this.el.dom.value = v;
47655         if (suppressEvent) {
47656             return ;
47657         }
47658          
47659         // update display..
47660         this.viewEl.select('img',true).each(function(e,i,n)  {
47661             
47662             var on = e.is(".x-menu-item-checked");
47663             var newv = v.indexOf(String(n)) > -1;
47664             if (on != newv) {
47665                 e.toggleClass('x-menu-item-checked');
47666             }
47667             
47668         });
47669         
47670         
47671         this.fireEvent('change', this, v, old);
47672         
47673         
47674     },
47675    
47676     // handle setting of hidden value by some other method!!?!?
47677     setFromHidden: function()
47678     {
47679         if(!this.el){
47680             return;
47681         }
47682         //console.log("SET FROM HIDDEN");
47683         //alert('setFrom hidden');
47684         this.setValue(this.el.dom.value);
47685     },
47686     
47687     onDestroy : function()
47688     {
47689         if(this.viewEl){
47690             Roo.get(this.viewEl).remove();
47691         }
47692          
47693         Roo.form.DayPicker.superclass.onDestroy.call(this);
47694     }
47695
47696 });/*
47697  * RooJS Library 1.1.1
47698  * Copyright(c) 2008-2011  Alan Knowles
47699  *
47700  * License - LGPL
47701  */
47702  
47703
47704 /**
47705  * @class Roo.form.ComboCheck
47706  * @extends Roo.form.ComboBox
47707  * A combobox for multiple select items.
47708  *
47709  * FIXME - could do with a reset button..
47710  * 
47711  * @constructor
47712  * Create a new ComboCheck
47713  * @param {Object} config Configuration options
47714  */
47715 Roo.form.ComboCheck = function(config){
47716     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47717     // should verify some data...
47718     // like
47719     // hiddenName = required..
47720     // displayField = required
47721     // valudField == required
47722     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47723     var _t = this;
47724     Roo.each(req, function(e) {
47725         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47726             throw "Roo.form.ComboCheck : missing value for: " + e;
47727         }
47728     });
47729     
47730     
47731 };
47732
47733 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47734      
47735      
47736     editable : false,
47737      
47738     selectedClass: 'x-menu-item-checked', 
47739     
47740     // private
47741     onRender : function(ct, position){
47742         var _t = this;
47743         
47744         
47745         
47746         if(!this.tpl){
47747             var cls = 'x-combo-list';
47748
47749             
47750             this.tpl =  new Roo.Template({
47751                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47752                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47753                    '<span>{' + this.displayField + '}</span>' +
47754                     '</div>' 
47755                 
47756             });
47757         }
47758  
47759         
47760         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47761         this.view.singleSelect = false;
47762         this.view.multiSelect = true;
47763         this.view.toggleSelect = true;
47764         this.pageTb.add(new Roo.Toolbar.Fill(), {
47765             
47766             text: 'Done',
47767             handler: function()
47768             {
47769                 _t.collapse();
47770             }
47771         });
47772     },
47773     
47774     onViewOver : function(e, t){
47775         // do nothing...
47776         return;
47777         
47778     },
47779     
47780     onViewClick : function(doFocus,index){
47781         return;
47782         
47783     },
47784     select: function () {
47785         //Roo.log("SELECT CALLED");
47786     },
47787      
47788     selectByValue : function(xv, scrollIntoView){
47789         var ar = this.getValueArray();
47790         var sels = [];
47791         
47792         Roo.each(ar, function(v) {
47793             if(v === undefined || v === null){
47794                 return;
47795             }
47796             var r = this.findRecord(this.valueField, v);
47797             if(r){
47798                 sels.push(this.store.indexOf(r))
47799                 
47800             }
47801         },this);
47802         this.view.select(sels);
47803         return false;
47804     },
47805     
47806     
47807     
47808     onSelect : function(record, index){
47809        // Roo.log("onselect Called");
47810        // this is only called by the clear button now..
47811         this.view.clearSelections();
47812         this.setValue('[]');
47813         if (this.value != this.valueBefore) {
47814             this.fireEvent('change', this, this.value, this.valueBefore);
47815             this.valueBefore = this.value;
47816         }
47817     },
47818     getValueArray : function()
47819     {
47820         var ar = [] ;
47821         
47822         try {
47823             //Roo.log(this.value);
47824             if (typeof(this.value) == 'undefined') {
47825                 return [];
47826             }
47827             var ar = Roo.decode(this.value);
47828             return  ar instanceof Array ? ar : []; //?? valid?
47829             
47830         } catch(e) {
47831             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47832             return [];
47833         }
47834          
47835     },
47836     expand : function ()
47837     {
47838         
47839         Roo.form.ComboCheck.superclass.expand.call(this);
47840         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47841         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47842         
47843
47844     },
47845     
47846     collapse : function(){
47847         Roo.form.ComboCheck.superclass.collapse.call(this);
47848         var sl = this.view.getSelectedIndexes();
47849         var st = this.store;
47850         var nv = [];
47851         var tv = [];
47852         var r;
47853         Roo.each(sl, function(i) {
47854             r = st.getAt(i);
47855             nv.push(r.get(this.valueField));
47856         },this);
47857         this.setValue(Roo.encode(nv));
47858         if (this.value != this.valueBefore) {
47859
47860             this.fireEvent('change', this, this.value, this.valueBefore);
47861             this.valueBefore = this.value;
47862         }
47863         
47864     },
47865     
47866     setValue : function(v){
47867         // Roo.log(v);
47868         this.value = v;
47869         
47870         var vals = this.getValueArray();
47871         var tv = [];
47872         Roo.each(vals, function(k) {
47873             var r = this.findRecord(this.valueField, k);
47874             if(r){
47875                 tv.push(r.data[this.displayField]);
47876             }else if(this.valueNotFoundText !== undefined){
47877                 tv.push( this.valueNotFoundText );
47878             }
47879         },this);
47880        // Roo.log(tv);
47881         
47882         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47883         this.hiddenField.value = v;
47884         this.value = v;
47885     }
47886     
47887 });/*
47888  * Based on:
47889  * Ext JS Library 1.1.1
47890  * Copyright(c) 2006-2007, Ext JS, LLC.
47891  *
47892  * Originally Released Under LGPL - original licence link has changed is not relivant.
47893  *
47894  * Fork - LGPL
47895  * <script type="text/javascript">
47896  */
47897  
47898 /**
47899  * @class Roo.form.Signature
47900  * @extends Roo.form.Field
47901  * Signature field.  
47902  * @constructor
47903  * 
47904  * @param {Object} config Configuration options
47905  */
47906
47907 Roo.form.Signature = function(config){
47908     Roo.form.Signature.superclass.constructor.call(this, config);
47909     
47910     this.addEvents({// not in used??
47911          /**
47912          * @event confirm
47913          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47914              * @param {Roo.form.Signature} combo This combo box
47915              */
47916         'confirm' : true,
47917         /**
47918          * @event reset
47919          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47920              * @param {Roo.form.ComboBox} combo This combo box
47921              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47922              */
47923         'reset' : true
47924     });
47925 };
47926
47927 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47928     /**
47929      * @cfg {Object} labels Label to use when rendering a form.
47930      * defaults to 
47931      * labels : { 
47932      *      clear : "Clear",
47933      *      confirm : "Confirm"
47934      *  }
47935      */
47936     labels : { 
47937         clear : "Clear",
47938         confirm : "Confirm"
47939     },
47940     /**
47941      * @cfg {Number} width The signature panel width (defaults to 300)
47942      */
47943     width: 300,
47944     /**
47945      * @cfg {Number} height The signature panel height (defaults to 100)
47946      */
47947     height : 100,
47948     /**
47949      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47950      */
47951     allowBlank : false,
47952     
47953     //private
47954     // {Object} signPanel The signature SVG panel element (defaults to {})
47955     signPanel : {},
47956     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47957     isMouseDown : false,
47958     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47959     isConfirmed : false,
47960     // {String} signatureTmp SVG mapping string (defaults to empty string)
47961     signatureTmp : '',
47962     
47963     
47964     defaultAutoCreate : { // modified by initCompnoent..
47965         tag: "input",
47966         type:"hidden"
47967     },
47968
47969     // private
47970     onRender : function(ct, position){
47971         
47972         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47973         
47974         this.wrap = this.el.wrap({
47975             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47976         });
47977         
47978         this.createToolbar(this);
47979         this.signPanel = this.wrap.createChild({
47980                 tag: 'div',
47981                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47982             }, this.el
47983         );
47984             
47985         this.svgID = Roo.id();
47986         this.svgEl = this.signPanel.createChild({
47987               xmlns : 'http://www.w3.org/2000/svg',
47988               tag : 'svg',
47989               id : this.svgID + "-svg",
47990               width: this.width,
47991               height: this.height,
47992               viewBox: '0 0 '+this.width+' '+this.height,
47993               cn : [
47994                 {
47995                     tag: "rect",
47996                     id: this.svgID + "-svg-r",
47997                     width: this.width,
47998                     height: this.height,
47999                     fill: "#ffa"
48000                 },
48001                 {
48002                     tag: "line",
48003                     id: this.svgID + "-svg-l",
48004                     x1: "0", // start
48005                     y1: (this.height*0.8), // start set the line in 80% of height
48006                     x2: this.width, // end
48007                     y2: (this.height*0.8), // end set the line in 80% of height
48008                     'stroke': "#666",
48009                     'stroke-width': "1",
48010                     'stroke-dasharray': "3",
48011                     'shape-rendering': "crispEdges",
48012                     'pointer-events': "none"
48013                 },
48014                 {
48015                     tag: "path",
48016                     id: this.svgID + "-svg-p",
48017                     'stroke': "navy",
48018                     'stroke-width': "3",
48019                     'fill': "none",
48020                     'pointer-events': 'none'
48021                 }
48022               ]
48023         });
48024         this.createSVG();
48025         this.svgBox = this.svgEl.dom.getScreenCTM();
48026     },
48027     createSVG : function(){ 
48028         var svg = this.signPanel;
48029         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
48030         var t = this;
48031
48032         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
48033         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
48034         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
48035         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
48036         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
48037         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
48038         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
48039         
48040     },
48041     isTouchEvent : function(e){
48042         return e.type.match(/^touch/);
48043     },
48044     getCoords : function (e) {
48045         var pt    = this.svgEl.dom.createSVGPoint();
48046         pt.x = e.clientX; 
48047         pt.y = e.clientY;
48048         if (this.isTouchEvent(e)) {
48049             pt.x =  e.targetTouches[0].clientX;
48050             pt.y = e.targetTouches[0].clientY;
48051         }
48052         var a = this.svgEl.dom.getScreenCTM();
48053         var b = a.inverse();
48054         var mx = pt.matrixTransform(b);
48055         return mx.x + ',' + mx.y;
48056     },
48057     //mouse event headler 
48058     down : function (e) {
48059         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
48060         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
48061         
48062         this.isMouseDown = true;
48063         
48064         e.preventDefault();
48065     },
48066     move : function (e) {
48067         if (this.isMouseDown) {
48068             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
48069             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
48070         }
48071         
48072         e.preventDefault();
48073     },
48074     up : function (e) {
48075         this.isMouseDown = false;
48076         var sp = this.signatureTmp.split(' ');
48077         
48078         if(sp.length > 1){
48079             if(!sp[sp.length-2].match(/^L/)){
48080                 sp.pop();
48081                 sp.pop();
48082                 sp.push("");
48083                 this.signatureTmp = sp.join(" ");
48084             }
48085         }
48086         if(this.getValue() != this.signatureTmp){
48087             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48088             this.isConfirmed = false;
48089         }
48090         e.preventDefault();
48091     },
48092     
48093     /**
48094      * Protected method that will not generally be called directly. It
48095      * is called when the editor creates its toolbar. Override this method if you need to
48096      * add custom toolbar buttons.
48097      * @param {HtmlEditor} editor
48098      */
48099     createToolbar : function(editor){
48100          function btn(id, toggle, handler){
48101             var xid = fid + '-'+ id ;
48102             return {
48103                 id : xid,
48104                 cmd : id,
48105                 cls : 'x-btn-icon x-edit-'+id,
48106                 enableToggle:toggle !== false,
48107                 scope: editor, // was editor...
48108                 handler:handler||editor.relayBtnCmd,
48109                 clickEvent:'mousedown',
48110                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48111                 tabIndex:-1
48112             };
48113         }
48114         
48115         
48116         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48117         this.tb = tb;
48118         this.tb.add(
48119            {
48120                 cls : ' x-signature-btn x-signature-'+id,
48121                 scope: editor, // was editor...
48122                 handler: this.reset,
48123                 clickEvent:'mousedown',
48124                 text: this.labels.clear
48125             },
48126             {
48127                  xtype : 'Fill',
48128                  xns: Roo.Toolbar
48129             }, 
48130             {
48131                 cls : '  x-signature-btn x-signature-'+id,
48132                 scope: editor, // was editor...
48133                 handler: this.confirmHandler,
48134                 clickEvent:'mousedown',
48135                 text: this.labels.confirm
48136             }
48137         );
48138     
48139     },
48140     //public
48141     /**
48142      * when user is clicked confirm then show this image.....
48143      * 
48144      * @return {String} Image Data URI
48145      */
48146     getImageDataURI : function(){
48147         var svg = this.svgEl.dom.parentNode.innerHTML;
48148         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
48149         return src; 
48150     },
48151     /**
48152      * 
48153      * @return {Boolean} this.isConfirmed
48154      */
48155     getConfirmed : function(){
48156         return this.isConfirmed;
48157     },
48158     /**
48159      * 
48160      * @return {Number} this.width
48161      */
48162     getWidth : function(){
48163         return this.width;
48164     },
48165     /**
48166      * 
48167      * @return {Number} this.height
48168      */
48169     getHeight : function(){
48170         return this.height;
48171     },
48172     // private
48173     getSignature : function(){
48174         return this.signatureTmp;
48175     },
48176     // private
48177     reset : function(){
48178         this.signatureTmp = '';
48179         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48180         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
48181         this.isConfirmed = false;
48182         Roo.form.Signature.superclass.reset.call(this);
48183     },
48184     setSignature : function(s){
48185         this.signatureTmp = s;
48186         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48187         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
48188         this.setValue(s);
48189         this.isConfirmed = false;
48190         Roo.form.Signature.superclass.reset.call(this);
48191     }, 
48192     test : function(){
48193 //        Roo.log(this.signPanel.dom.contentWindow.up())
48194     },
48195     //private
48196     setConfirmed : function(){
48197         
48198         
48199         
48200 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
48201     },
48202     // private
48203     confirmHandler : function(){
48204         if(!this.getSignature()){
48205             return;
48206         }
48207         
48208         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
48209         this.setValue(this.getSignature());
48210         this.isConfirmed = true;
48211         
48212         this.fireEvent('confirm', this);
48213     },
48214     // private
48215     // Subclasses should provide the validation implementation by overriding this
48216     validateValue : function(value){
48217         if(this.allowBlank){
48218             return true;
48219         }
48220         
48221         if(this.isConfirmed){
48222             return true;
48223         }
48224         return false;
48225     }
48226 });/*
48227  * Based on:
48228  * Ext JS Library 1.1.1
48229  * Copyright(c) 2006-2007, Ext JS, LLC.
48230  *
48231  * Originally Released Under LGPL - original licence link has changed is not relivant.
48232  *
48233  * Fork - LGPL
48234  * <script type="text/javascript">
48235  */
48236  
48237
48238 /**
48239  * @class Roo.form.ComboBox
48240  * @extends Roo.form.TriggerField
48241  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48242  * @constructor
48243  * Create a new ComboBox.
48244  * @param {Object} config Configuration options
48245  */
48246 Roo.form.Select = function(config){
48247     Roo.form.Select.superclass.constructor.call(this, config);
48248      
48249 };
48250
48251 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48252     /**
48253      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48254      */
48255     /**
48256      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48257      * rendering into an Roo.Editor, defaults to false)
48258      */
48259     /**
48260      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48261      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48262      */
48263     /**
48264      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48265      */
48266     /**
48267      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48268      * the dropdown list (defaults to undefined, with no header element)
48269      */
48270
48271      /**
48272      * @cfg {String/Roo.Template} tpl The template to use to render the output
48273      */
48274      
48275     // private
48276     defaultAutoCreate : {tag: "select"  },
48277     /**
48278      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48279      */
48280     listWidth: undefined,
48281     /**
48282      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48283      * mode = 'remote' or 'text' if mode = 'local')
48284      */
48285     displayField: undefined,
48286     /**
48287      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48288      * mode = 'remote' or 'value' if mode = 'local'). 
48289      * Note: use of a valueField requires the user make a selection
48290      * in order for a value to be mapped.
48291      */
48292     valueField: undefined,
48293     
48294     
48295     /**
48296      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48297      * field's data value (defaults to the underlying DOM element's name)
48298      */
48299     hiddenName: undefined,
48300     /**
48301      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48302      */
48303     listClass: '',
48304     /**
48305      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48306      */
48307     selectedClass: 'x-combo-selected',
48308     /**
48309      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48310      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48311      * which displays a downward arrow icon).
48312      */
48313     triggerClass : 'x-form-arrow-trigger',
48314     /**
48315      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48316      */
48317     shadow:'sides',
48318     /**
48319      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48320      * anchor positions (defaults to 'tl-bl')
48321      */
48322     listAlign: 'tl-bl?',
48323     /**
48324      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48325      */
48326     maxHeight: 300,
48327     /**
48328      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48329      * query specified by the allQuery config option (defaults to 'query')
48330      */
48331     triggerAction: 'query',
48332     /**
48333      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48334      * (defaults to 4, does not apply if editable = false)
48335      */
48336     minChars : 4,
48337     /**
48338      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48339      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48340      */
48341     typeAhead: false,
48342     /**
48343      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48344      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48345      */
48346     queryDelay: 500,
48347     /**
48348      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48349      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48350      */
48351     pageSize: 0,
48352     /**
48353      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48354      * when editable = true (defaults to false)
48355      */
48356     selectOnFocus:false,
48357     /**
48358      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48359      */
48360     queryParam: 'query',
48361     /**
48362      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48363      * when mode = 'remote' (defaults to 'Loading...')
48364      */
48365     loadingText: 'Loading...',
48366     /**
48367      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48368      */
48369     resizable: false,
48370     /**
48371      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48372      */
48373     handleHeight : 8,
48374     /**
48375      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48376      * traditional select (defaults to true)
48377      */
48378     editable: true,
48379     /**
48380      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48381      */
48382     allQuery: '',
48383     /**
48384      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48385      */
48386     mode: 'remote',
48387     /**
48388      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48389      * listWidth has a higher value)
48390      */
48391     minListWidth : 70,
48392     /**
48393      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48394      * allow the user to set arbitrary text into the field (defaults to false)
48395      */
48396     forceSelection:false,
48397     /**
48398      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48399      * if typeAhead = true (defaults to 250)
48400      */
48401     typeAheadDelay : 250,
48402     /**
48403      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48404      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48405      */
48406     valueNotFoundText : undefined,
48407     
48408     /**
48409      * @cfg {String} defaultValue The value displayed after loading the store.
48410      */
48411     defaultValue: '',
48412     
48413     /**
48414      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48415      */
48416     blockFocus : false,
48417     
48418     /**
48419      * @cfg {Boolean} disableClear Disable showing of clear button.
48420      */
48421     disableClear : false,
48422     /**
48423      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48424      */
48425     alwaysQuery : false,
48426     
48427     //private
48428     addicon : false,
48429     editicon: false,
48430     
48431     // element that contains real text value.. (when hidden is used..)
48432      
48433     // private
48434     onRender : function(ct, position){
48435         Roo.form.Field.prototype.onRender.call(this, ct, position);
48436         
48437         if(this.store){
48438             this.store.on('beforeload', this.onBeforeLoad, this);
48439             this.store.on('load', this.onLoad, this);
48440             this.store.on('loadexception', this.onLoadException, this);
48441             this.store.load({});
48442         }
48443         
48444         
48445         
48446     },
48447
48448     // private
48449     initEvents : function(){
48450         //Roo.form.ComboBox.superclass.initEvents.call(this);
48451  
48452     },
48453
48454     onDestroy : function(){
48455        
48456         if(this.store){
48457             this.store.un('beforeload', this.onBeforeLoad, this);
48458             this.store.un('load', this.onLoad, this);
48459             this.store.un('loadexception', this.onLoadException, this);
48460         }
48461         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48462     },
48463
48464     // private
48465     fireKey : function(e){
48466         if(e.isNavKeyPress() && !this.list.isVisible()){
48467             this.fireEvent("specialkey", this, e);
48468         }
48469     },
48470
48471     // private
48472     onResize: function(w, h){
48473         
48474         return; 
48475     
48476         
48477     },
48478
48479     /**
48480      * Allow or prevent the user from directly editing the field text.  If false is passed,
48481      * the user will only be able to select from the items defined in the dropdown list.  This method
48482      * is the runtime equivalent of setting the 'editable' config option at config time.
48483      * @param {Boolean} value True to allow the user to directly edit the field text
48484      */
48485     setEditable : function(value){
48486          
48487     },
48488
48489     // private
48490     onBeforeLoad : function(){
48491         
48492         Roo.log("Select before load");
48493         return;
48494     
48495         this.innerList.update(this.loadingText ?
48496                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48497         //this.restrictHeight();
48498         this.selectedIndex = -1;
48499     },
48500
48501     // private
48502     onLoad : function(){
48503
48504     
48505         var dom = this.el.dom;
48506         dom.innerHTML = '';
48507          var od = dom.ownerDocument;
48508          
48509         if (this.emptyText) {
48510             var op = od.createElement('option');
48511             op.setAttribute('value', '');
48512             op.innerHTML = String.format('{0}', this.emptyText);
48513             dom.appendChild(op);
48514         }
48515         if(this.store.getCount() > 0){
48516            
48517             var vf = this.valueField;
48518             var df = this.displayField;
48519             this.store.data.each(function(r) {
48520                 // which colmsn to use... testing - cdoe / title..
48521                 var op = od.createElement('option');
48522                 op.setAttribute('value', r.data[vf]);
48523                 op.innerHTML = String.format('{0}', r.data[df]);
48524                 dom.appendChild(op);
48525             });
48526             if (typeof(this.defaultValue != 'undefined')) {
48527                 this.setValue(this.defaultValue);
48528             }
48529             
48530              
48531         }else{
48532             //this.onEmptyResults();
48533         }
48534         //this.el.focus();
48535     },
48536     // private
48537     onLoadException : function()
48538     {
48539         dom.innerHTML = '';
48540             
48541         Roo.log("Select on load exception");
48542         return;
48543     
48544         this.collapse();
48545         Roo.log(this.store.reader.jsonData);
48546         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48547             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48548         }
48549         
48550         
48551     },
48552     // private
48553     onTypeAhead : function(){
48554          
48555     },
48556
48557     // private
48558     onSelect : function(record, index){
48559         Roo.log('on select?');
48560         return;
48561         if(this.fireEvent('beforeselect', this, record, index) !== false){
48562             this.setFromData(index > -1 ? record.data : false);
48563             this.collapse();
48564             this.fireEvent('select', this, record, index);
48565         }
48566     },
48567
48568     /**
48569      * Returns the currently selected field value or empty string if no value is set.
48570      * @return {String} value The selected value
48571      */
48572     getValue : function(){
48573         var dom = this.el.dom;
48574         this.value = dom.options[dom.selectedIndex].value;
48575         return this.value;
48576         
48577     },
48578
48579     /**
48580      * Clears any text/value currently set in the field
48581      */
48582     clearValue : function(){
48583         this.value = '';
48584         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48585         
48586     },
48587
48588     /**
48589      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48590      * will be displayed in the field.  If the value does not match the data value of an existing item,
48591      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48592      * Otherwise the field will be blank (although the value will still be set).
48593      * @param {String} value The value to match
48594      */
48595     setValue : function(v){
48596         var d = this.el.dom;
48597         for (var i =0; i < d.options.length;i++) {
48598             if (v == d.options[i].value) {
48599                 d.selectedIndex = i;
48600                 this.value = v;
48601                 return;
48602             }
48603         }
48604         this.clearValue();
48605     },
48606     /**
48607      * @property {Object} the last set data for the element
48608      */
48609     
48610     lastData : false,
48611     /**
48612      * Sets the value of the field based on a object which is related to the record format for the store.
48613      * @param {Object} value the value to set as. or false on reset?
48614      */
48615     setFromData : function(o){
48616         Roo.log('setfrom data?');
48617          
48618         
48619         
48620     },
48621     // private
48622     reset : function(){
48623         this.clearValue();
48624     },
48625     // private
48626     findRecord : function(prop, value){
48627         
48628         return false;
48629     
48630         var record;
48631         if(this.store.getCount() > 0){
48632             this.store.each(function(r){
48633                 if(r.data[prop] == value){
48634                     record = r;
48635                     return false;
48636                 }
48637                 return true;
48638             });
48639         }
48640         return record;
48641     },
48642     
48643     getName: function()
48644     {
48645         // returns hidden if it's set..
48646         if (!this.rendered) {return ''};
48647         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48648         
48649     },
48650      
48651
48652     
48653
48654     // private
48655     onEmptyResults : function(){
48656         Roo.log('empty results');
48657         //this.collapse();
48658     },
48659
48660     /**
48661      * Returns true if the dropdown list is expanded, else false.
48662      */
48663     isExpanded : function(){
48664         return false;
48665     },
48666
48667     /**
48668      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48669      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48670      * @param {String} value The data value of the item to select
48671      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48672      * selected item if it is not currently in view (defaults to true)
48673      * @return {Boolean} True if the value matched an item in the list, else false
48674      */
48675     selectByValue : function(v, scrollIntoView){
48676         Roo.log('select By Value');
48677         return false;
48678     
48679         if(v !== undefined && v !== null){
48680             var r = this.findRecord(this.valueField || this.displayField, v);
48681             if(r){
48682                 this.select(this.store.indexOf(r), scrollIntoView);
48683                 return true;
48684             }
48685         }
48686         return false;
48687     },
48688
48689     /**
48690      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48691      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48692      * @param {Number} index The zero-based index of the list item to select
48693      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48694      * selected item if it is not currently in view (defaults to true)
48695      */
48696     select : function(index, scrollIntoView){
48697         Roo.log('select ');
48698         return  ;
48699         
48700         this.selectedIndex = index;
48701         this.view.select(index);
48702         if(scrollIntoView !== false){
48703             var el = this.view.getNode(index);
48704             if(el){
48705                 this.innerList.scrollChildIntoView(el, false);
48706             }
48707         }
48708     },
48709
48710       
48711
48712     // private
48713     validateBlur : function(){
48714         
48715         return;
48716         
48717     },
48718
48719     // private
48720     initQuery : function(){
48721         this.doQuery(this.getRawValue());
48722     },
48723
48724     // private
48725     doForce : function(){
48726         if(this.el.dom.value.length > 0){
48727             this.el.dom.value =
48728                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48729              
48730         }
48731     },
48732
48733     /**
48734      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48735      * query allowing the query action to be canceled if needed.
48736      * @param {String} query The SQL query to execute
48737      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48738      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48739      * saved in the current store (defaults to false)
48740      */
48741     doQuery : function(q, forceAll){
48742         
48743         Roo.log('doQuery?');
48744         if(q === undefined || q === null){
48745             q = '';
48746         }
48747         var qe = {
48748             query: q,
48749             forceAll: forceAll,
48750             combo: this,
48751             cancel:false
48752         };
48753         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48754             return false;
48755         }
48756         q = qe.query;
48757         forceAll = qe.forceAll;
48758         if(forceAll === true || (q.length >= this.minChars)){
48759             if(this.lastQuery != q || this.alwaysQuery){
48760                 this.lastQuery = q;
48761                 if(this.mode == 'local'){
48762                     this.selectedIndex = -1;
48763                     if(forceAll){
48764                         this.store.clearFilter();
48765                     }else{
48766                         this.store.filter(this.displayField, q);
48767                     }
48768                     this.onLoad();
48769                 }else{
48770                     this.store.baseParams[this.queryParam] = q;
48771                     this.store.load({
48772                         params: this.getParams(q)
48773                     });
48774                     this.expand();
48775                 }
48776             }else{
48777                 this.selectedIndex = -1;
48778                 this.onLoad();   
48779             }
48780         }
48781     },
48782
48783     // private
48784     getParams : function(q){
48785         var p = {};
48786         //p[this.queryParam] = q;
48787         if(this.pageSize){
48788             p.start = 0;
48789             p.limit = this.pageSize;
48790         }
48791         return p;
48792     },
48793
48794     /**
48795      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48796      */
48797     collapse : function(){
48798         
48799     },
48800
48801     // private
48802     collapseIf : function(e){
48803         
48804     },
48805
48806     /**
48807      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48808      */
48809     expand : function(){
48810         
48811     } ,
48812
48813     // private
48814      
48815
48816     /** 
48817     * @cfg {Boolean} grow 
48818     * @hide 
48819     */
48820     /** 
48821     * @cfg {Number} growMin 
48822     * @hide 
48823     */
48824     /** 
48825     * @cfg {Number} growMax 
48826     * @hide 
48827     */
48828     /**
48829      * @hide
48830      * @method autoSize
48831      */
48832     
48833     setWidth : function()
48834     {
48835         
48836     },
48837     getResizeEl : function(){
48838         return this.el;
48839     }
48840 });//<script type="text/javasscript">
48841  
48842
48843 /**
48844  * @class Roo.DDView
48845  * A DnD enabled version of Roo.View.
48846  * @param {Element/String} container The Element in which to create the View.
48847  * @param {String} tpl The template string used to create the markup for each element of the View
48848  * @param {Object} config The configuration properties. These include all the config options of
48849  * {@link Roo.View} plus some specific to this class.<br>
48850  * <p>
48851  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48852  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48853  * <p>
48854  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48855 .x-view-drag-insert-above {
48856         border-top:1px dotted #3366cc;
48857 }
48858 .x-view-drag-insert-below {
48859         border-bottom:1px dotted #3366cc;
48860 }
48861 </code></pre>
48862  * 
48863  */
48864  
48865 Roo.DDView = function(container, tpl, config) {
48866     Roo.DDView.superclass.constructor.apply(this, arguments);
48867     this.getEl().setStyle("outline", "0px none");
48868     this.getEl().unselectable();
48869     if (this.dragGroup) {
48870                 this.setDraggable(this.dragGroup.split(","));
48871     }
48872     if (this.dropGroup) {
48873                 this.setDroppable(this.dropGroup.split(","));
48874     }
48875     if (this.deletable) {
48876         this.setDeletable();
48877     }
48878     this.isDirtyFlag = false;
48879         this.addEvents({
48880                 "drop" : true
48881         });
48882 };
48883
48884 Roo.extend(Roo.DDView, Roo.View, {
48885 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48886 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48887 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48888 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48889
48890         isFormField: true,
48891
48892         reset: Roo.emptyFn,
48893         
48894         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48895
48896         validate: function() {
48897                 return true;
48898         },
48899         
48900         destroy: function() {
48901                 this.purgeListeners();
48902                 this.getEl.removeAllListeners();
48903                 this.getEl().remove();
48904                 if (this.dragZone) {
48905                         if (this.dragZone.destroy) {
48906                                 this.dragZone.destroy();
48907                         }
48908                 }
48909                 if (this.dropZone) {
48910                         if (this.dropZone.destroy) {
48911                                 this.dropZone.destroy();
48912                         }
48913                 }
48914         },
48915
48916 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48917         getName: function() {
48918                 return this.name;
48919         },
48920
48921 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48922         setValue: function(v) {
48923                 if (!this.store) {
48924                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48925                 }
48926                 var data = {};
48927                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48928                 this.store.proxy = new Roo.data.MemoryProxy(data);
48929                 this.store.load();
48930         },
48931
48932 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48933         getValue: function() {
48934                 var result = '(';
48935                 this.store.each(function(rec) {
48936                         result += rec.id + ',';
48937                 });
48938                 return result.substr(0, result.length - 1) + ')';
48939         },
48940         
48941         getIds: function() {
48942                 var i = 0, result = new Array(this.store.getCount());
48943                 this.store.each(function(rec) {
48944                         result[i++] = rec.id;
48945                 });
48946                 return result;
48947         },
48948         
48949         isDirty: function() {
48950                 return this.isDirtyFlag;
48951         },
48952
48953 /**
48954  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48955  *      whole Element becomes the target, and this causes the drop gesture to append.
48956  */
48957     getTargetFromEvent : function(e) {
48958                 var target = e.getTarget();
48959                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48960                 target = target.parentNode;
48961                 }
48962                 if (!target) {
48963                         target = this.el.dom.lastChild || this.el.dom;
48964                 }
48965                 return target;
48966     },
48967
48968 /**
48969  *      Create the drag data which consists of an object which has the property "ddel" as
48970  *      the drag proxy element. 
48971  */
48972     getDragData : function(e) {
48973         var target = this.findItemFromChild(e.getTarget());
48974                 if(target) {
48975                         this.handleSelection(e);
48976                         var selNodes = this.getSelectedNodes();
48977             var dragData = {
48978                 source: this,
48979                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48980                 nodes: selNodes,
48981                 records: []
48982                         };
48983                         var selectedIndices = this.getSelectedIndexes();
48984                         for (var i = 0; i < selectedIndices.length; i++) {
48985                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48986                         }
48987                         if (selNodes.length == 1) {
48988                                 dragData.ddel = target.cloneNode(true); // the div element
48989                         } else {
48990                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48991                                 div.className = 'multi-proxy';
48992                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48993                                         div.appendChild(selNodes[i].cloneNode(true));
48994                                 }
48995                                 dragData.ddel = div;
48996                         }
48997             //console.log(dragData)
48998             //console.log(dragData.ddel.innerHTML)
48999                         return dragData;
49000                 }
49001         //console.log('nodragData')
49002                 return false;
49003     },
49004     
49005 /**     Specify to which ddGroup items in this DDView may be dragged. */
49006     setDraggable: function(ddGroup) {
49007         if (ddGroup instanceof Array) {
49008                 Roo.each(ddGroup, this.setDraggable, this);
49009                 return;
49010         }
49011         if (this.dragZone) {
49012                 this.dragZone.addToGroup(ddGroup);
49013         } else {
49014                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
49015                                 containerScroll: true,
49016                                 ddGroup: ddGroup 
49017
49018                         });
49019 //                      Draggability implies selection. DragZone's mousedown selects the element.
49020                         if (!this.multiSelect) { this.singleSelect = true; }
49021
49022 //                      Wire the DragZone's handlers up to methods in *this*
49023                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
49024                 }
49025     },
49026
49027 /**     Specify from which ddGroup this DDView accepts drops. */
49028     setDroppable: function(ddGroup) {
49029         if (ddGroup instanceof Array) {
49030                 Roo.each(ddGroup, this.setDroppable, this);
49031                 return;
49032         }
49033         if (this.dropZone) {
49034                 this.dropZone.addToGroup(ddGroup);
49035         } else {
49036                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
49037                                 containerScroll: true,
49038                                 ddGroup: ddGroup
49039                         });
49040
49041 //                      Wire the DropZone's handlers up to methods in *this*
49042                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
49043                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
49044                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
49045                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
49046                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
49047                 }
49048     },
49049
49050 /**     Decide whether to drop above or below a View node. */
49051     getDropPoint : function(e, n, dd){
49052         if (n == this.el.dom) { return "above"; }
49053                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
49054                 var c = t + (b - t) / 2;
49055                 var y = Roo.lib.Event.getPageY(e);
49056                 if(y <= c) {
49057                         return "above";
49058                 }else{
49059                         return "below";
49060                 }
49061     },
49062
49063     onNodeEnter : function(n, dd, e, data){
49064                 return false;
49065     },
49066     
49067     onNodeOver : function(n, dd, e, data){
49068                 var pt = this.getDropPoint(e, n, dd);
49069                 // set the insert point style on the target node
49070                 var dragElClass = this.dropNotAllowed;
49071                 if (pt) {
49072                         var targetElClass;
49073                         if (pt == "above"){
49074                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
49075                                 targetElClass = "x-view-drag-insert-above";
49076                         } else {
49077                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
49078                                 targetElClass = "x-view-drag-insert-below";
49079                         }
49080                         if (this.lastInsertClass != targetElClass){
49081                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
49082                                 this.lastInsertClass = targetElClass;
49083                         }
49084                 }
49085                 return dragElClass;
49086         },
49087
49088     onNodeOut : function(n, dd, e, data){
49089                 this.removeDropIndicators(n);
49090     },
49091
49092     onNodeDrop : function(n, dd, e, data){
49093         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
49094                 return false;
49095         }
49096         var pt = this.getDropPoint(e, n, dd);
49097                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
49098                 if (pt == "below") { insertAt++; }
49099                 for (var i = 0; i < data.records.length; i++) {
49100                         var r = data.records[i];
49101                         var dup = this.store.getById(r.id);
49102                         if (dup && (dd != this.dragZone)) {
49103                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
49104                         } else {
49105                                 if (data.copy) {
49106                                         this.store.insert(insertAt++, r.copy());
49107                                 } else {
49108                                         data.source.isDirtyFlag = true;
49109                                         r.store.remove(r);
49110                                         this.store.insert(insertAt++, r);
49111                                 }
49112                                 this.isDirtyFlag = true;
49113                         }
49114                 }
49115                 this.dragZone.cachedTarget = null;
49116                 return true;
49117     },
49118
49119     removeDropIndicators : function(n){
49120                 if(n){
49121                         Roo.fly(n).removeClass([
49122                                 "x-view-drag-insert-above",
49123                                 "x-view-drag-insert-below"]);
49124                         this.lastInsertClass = "_noclass";
49125                 }
49126     },
49127
49128 /**
49129  *      Utility method. Add a delete option to the DDView's context menu.
49130  *      @param {String} imageUrl The URL of the "delete" icon image.
49131  */
49132         setDeletable: function(imageUrl) {
49133                 if (!this.singleSelect && !this.multiSelect) {
49134                         this.singleSelect = true;
49135                 }
49136                 var c = this.getContextMenu();
49137                 this.contextMenu.on("itemclick", function(item) {
49138                         switch (item.id) {
49139                                 case "delete":
49140                                         this.remove(this.getSelectedIndexes());
49141                                         break;
49142                         }
49143                 }, this);
49144                 this.contextMenu.add({
49145                         icon: imageUrl,
49146                         id: "delete",
49147                         text: 'Delete'
49148                 });
49149         },
49150         
49151 /**     Return the context menu for this DDView. */
49152         getContextMenu: function() {
49153                 if (!this.contextMenu) {
49154 //                      Create the View's context menu
49155                         this.contextMenu = new Roo.menu.Menu({
49156                                 id: this.id + "-contextmenu"
49157                         });
49158                         this.el.on("contextmenu", this.showContextMenu, this);
49159                 }
49160                 return this.contextMenu;
49161         },
49162         
49163         disableContextMenu: function() {
49164                 if (this.contextMenu) {
49165                         this.el.un("contextmenu", this.showContextMenu, this);
49166                 }
49167         },
49168
49169         showContextMenu: function(e, item) {
49170         item = this.findItemFromChild(e.getTarget());
49171                 if (item) {
49172                         e.stopEvent();
49173                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
49174                         this.contextMenu.showAt(e.getXY());
49175             }
49176     },
49177
49178 /**
49179  *      Remove {@link Roo.data.Record}s at the specified indices.
49180  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
49181  */
49182     remove: function(selectedIndices) {
49183                 selectedIndices = [].concat(selectedIndices);
49184                 for (var i = 0; i < selectedIndices.length; i++) {
49185                         var rec = this.store.getAt(selectedIndices[i]);
49186                         this.store.remove(rec);
49187                 }
49188     },
49189
49190 /**
49191  *      Double click fires the event, but also, if this is draggable, and there is only one other
49192  *      related DropZone, it transfers the selected node.
49193  */
49194     onDblClick : function(e){
49195         var item = this.findItemFromChild(e.getTarget());
49196         if(item){
49197             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
49198                 return false;
49199             }
49200             if (this.dragGroup) {
49201                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
49202                     while (targets.indexOf(this.dropZone) > -1) {
49203                             targets.remove(this.dropZone);
49204                                 }
49205                     if (targets.length == 1) {
49206                                         this.dragZone.cachedTarget = null;
49207                         var el = Roo.get(targets[0].getEl());
49208                         var box = el.getBox(true);
49209                         targets[0].onNodeDrop(el.dom, {
49210                                 target: el.dom,
49211                                 xy: [box.x, box.y + box.height - 1]
49212                         }, null, this.getDragData(e));
49213                     }
49214                 }
49215         }
49216     },
49217     
49218     handleSelection: function(e) {
49219                 this.dragZone.cachedTarget = null;
49220         var item = this.findItemFromChild(e.getTarget());
49221         if (!item) {
49222                 this.clearSelections(true);
49223                 return;
49224         }
49225                 if (item && (this.multiSelect || this.singleSelect)){
49226                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49227                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49228                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49229                                 this.unselect(item);
49230                         } else {
49231                                 this.select(item, this.multiSelect && e.ctrlKey);
49232                                 this.lastSelection = item;
49233                         }
49234                 }
49235     },
49236
49237     onItemClick : function(item, index, e){
49238                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49239                         return false;
49240                 }
49241                 return true;
49242     },
49243
49244     unselect : function(nodeInfo, suppressEvent){
49245                 var node = this.getNode(nodeInfo);
49246                 if(node && this.isSelected(node)){
49247                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49248                                 Roo.fly(node).removeClass(this.selectedClass);
49249                                 this.selections.remove(node);
49250                                 if(!suppressEvent){
49251                                         this.fireEvent("selectionchange", this, this.selections);
49252                                 }
49253                         }
49254                 }
49255     }
49256 });
49257 /*
49258  * Based on:
49259  * Ext JS Library 1.1.1
49260  * Copyright(c) 2006-2007, Ext JS, LLC.
49261  *
49262  * Originally Released Under LGPL - original licence link has changed is not relivant.
49263  *
49264  * Fork - LGPL
49265  * <script type="text/javascript">
49266  */
49267  
49268 /**
49269  * @class Roo.LayoutManager
49270  * @extends Roo.util.Observable
49271  * Base class for layout managers.
49272  */
49273 Roo.LayoutManager = function(container, config){
49274     Roo.LayoutManager.superclass.constructor.call(this);
49275     this.el = Roo.get(container);
49276     // ie scrollbar fix
49277     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49278         document.body.scroll = "no";
49279     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49280         this.el.position('relative');
49281     }
49282     this.id = this.el.id;
49283     this.el.addClass("x-layout-container");
49284     /** false to disable window resize monitoring @type Boolean */
49285     this.monitorWindowResize = true;
49286     this.regions = {};
49287     this.addEvents({
49288         /**
49289          * @event layout
49290          * Fires when a layout is performed. 
49291          * @param {Roo.LayoutManager} this
49292          */
49293         "layout" : true,
49294         /**
49295          * @event regionresized
49296          * Fires when the user resizes a region. 
49297          * @param {Roo.LayoutRegion} region The resized region
49298          * @param {Number} newSize The new size (width for east/west, height for north/south)
49299          */
49300         "regionresized" : true,
49301         /**
49302          * @event regioncollapsed
49303          * Fires when a region is collapsed. 
49304          * @param {Roo.LayoutRegion} region The collapsed region
49305          */
49306         "regioncollapsed" : true,
49307         /**
49308          * @event regionexpanded
49309          * Fires when a region is expanded.  
49310          * @param {Roo.LayoutRegion} region The expanded region
49311          */
49312         "regionexpanded" : true
49313     });
49314     this.updating = false;
49315     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49316 };
49317
49318 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49319     /**
49320      * Returns true if this layout is currently being updated
49321      * @return {Boolean}
49322      */
49323     isUpdating : function(){
49324         return this.updating; 
49325     },
49326     
49327     /**
49328      * Suspend the LayoutManager from doing auto-layouts while
49329      * making multiple add or remove calls
49330      */
49331     beginUpdate : function(){
49332         this.updating = true;    
49333     },
49334     
49335     /**
49336      * Restore auto-layouts and optionally disable the manager from performing a layout
49337      * @param {Boolean} noLayout true to disable a layout update 
49338      */
49339     endUpdate : function(noLayout){
49340         this.updating = false;
49341         if(!noLayout){
49342             this.layout();
49343         }    
49344     },
49345     
49346     layout: function(){
49347         
49348     },
49349     
49350     onRegionResized : function(region, newSize){
49351         this.fireEvent("regionresized", region, newSize);
49352         this.layout();
49353     },
49354     
49355     onRegionCollapsed : function(region){
49356         this.fireEvent("regioncollapsed", region);
49357     },
49358     
49359     onRegionExpanded : function(region){
49360         this.fireEvent("regionexpanded", region);
49361     },
49362         
49363     /**
49364      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49365      * performs box-model adjustments.
49366      * @return {Object} The size as an object {width: (the width), height: (the height)}
49367      */
49368     getViewSize : function(){
49369         var size;
49370         if(this.el.dom != document.body){
49371             size = this.el.getSize();
49372         }else{
49373             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49374         }
49375         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49376         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49377         return size;
49378     },
49379     
49380     /**
49381      * Returns the Element this layout is bound to.
49382      * @return {Roo.Element}
49383      */
49384     getEl : function(){
49385         return this.el;
49386     },
49387     
49388     /**
49389      * Returns the specified region.
49390      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49391      * @return {Roo.LayoutRegion}
49392      */
49393     getRegion : function(target){
49394         return this.regions[target.toLowerCase()];
49395     },
49396     
49397     onWindowResize : function(){
49398         if(this.monitorWindowResize){
49399             this.layout();
49400         }
49401     }
49402 });/*
49403  * Based on:
49404  * Ext JS Library 1.1.1
49405  * Copyright(c) 2006-2007, Ext JS, LLC.
49406  *
49407  * Originally Released Under LGPL - original licence link has changed is not relivant.
49408  *
49409  * Fork - LGPL
49410  * <script type="text/javascript">
49411  */
49412 /**
49413  * @class Roo.BorderLayout
49414  * @extends Roo.LayoutManager
49415  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49416  * please see: <br><br>
49417  * <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>
49418  * <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>
49419  * Example:
49420  <pre><code>
49421  var layout = new Roo.BorderLayout(document.body, {
49422     north: {
49423         initialSize: 25,
49424         titlebar: false
49425     },
49426     west: {
49427         split:true,
49428         initialSize: 200,
49429         minSize: 175,
49430         maxSize: 400,
49431         titlebar: true,
49432         collapsible: true
49433     },
49434     east: {
49435         split:true,
49436         initialSize: 202,
49437         minSize: 175,
49438         maxSize: 400,
49439         titlebar: true,
49440         collapsible: true
49441     },
49442     south: {
49443         split:true,
49444         initialSize: 100,
49445         minSize: 100,
49446         maxSize: 200,
49447         titlebar: true,
49448         collapsible: true
49449     },
49450     center: {
49451         titlebar: true,
49452         autoScroll:true,
49453         resizeTabs: true,
49454         minTabWidth: 50,
49455         preferredTabWidth: 150
49456     }
49457 });
49458
49459 // shorthand
49460 var CP = Roo.ContentPanel;
49461
49462 layout.beginUpdate();
49463 layout.add("north", new CP("north", "North"));
49464 layout.add("south", new CP("south", {title: "South", closable: true}));
49465 layout.add("west", new CP("west", {title: "West"}));
49466 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49467 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49468 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49469 layout.getRegion("center").showPanel("center1");
49470 layout.endUpdate();
49471 </code></pre>
49472
49473 <b>The container the layout is rendered into can be either the body element or any other element.
49474 If it is not the body element, the container needs to either be an absolute positioned element,
49475 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49476 the container size if it is not the body element.</b>
49477
49478 * @constructor
49479 * Create a new BorderLayout
49480 * @param {String/HTMLElement/Element} container The container this layout is bound to
49481 * @param {Object} config Configuration options
49482  */
49483 Roo.BorderLayout = function(container, config){
49484     config = config || {};
49485     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49486     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49487     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49488         var target = this.factory.validRegions[i];
49489         if(config[target]){
49490             this.addRegion(target, config[target]);
49491         }
49492     }
49493 };
49494
49495 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49496     /**
49497      * Creates and adds a new region if it doesn't already exist.
49498      * @param {String} target The target region key (north, south, east, west or center).
49499      * @param {Object} config The regions config object
49500      * @return {BorderLayoutRegion} The new region
49501      */
49502     addRegion : function(target, config){
49503         if(!this.regions[target]){
49504             var r = this.factory.create(target, this, config);
49505             this.bindRegion(target, r);
49506         }
49507         return this.regions[target];
49508     },
49509
49510     // private (kinda)
49511     bindRegion : function(name, r){
49512         this.regions[name] = r;
49513         r.on("visibilitychange", this.layout, this);
49514         r.on("paneladded", this.layout, this);
49515         r.on("panelremoved", this.layout, this);
49516         r.on("invalidated", this.layout, this);
49517         r.on("resized", this.onRegionResized, this);
49518         r.on("collapsed", this.onRegionCollapsed, this);
49519         r.on("expanded", this.onRegionExpanded, this);
49520     },
49521
49522     /**
49523      * Performs a layout update.
49524      */
49525     layout : function(){
49526         if(this.updating) {
49527             return;
49528         }
49529         var size = this.getViewSize();
49530         var w = size.width;
49531         var h = size.height;
49532         var centerW = w;
49533         var centerH = h;
49534         var centerY = 0;
49535         var centerX = 0;
49536         //var x = 0, y = 0;
49537
49538         var rs = this.regions;
49539         var north = rs["north"];
49540         var south = rs["south"]; 
49541         var west = rs["west"];
49542         var east = rs["east"];
49543         var center = rs["center"];
49544         //if(this.hideOnLayout){ // not supported anymore
49545             //c.el.setStyle("display", "none");
49546         //}
49547         if(north && north.isVisible()){
49548             var b = north.getBox();
49549             var m = north.getMargins();
49550             b.width = w - (m.left+m.right);
49551             b.x = m.left;
49552             b.y = m.top;
49553             centerY = b.height + b.y + m.bottom;
49554             centerH -= centerY;
49555             north.updateBox(this.safeBox(b));
49556         }
49557         if(south && south.isVisible()){
49558             var b = south.getBox();
49559             var m = south.getMargins();
49560             b.width = w - (m.left+m.right);
49561             b.x = m.left;
49562             var totalHeight = (b.height + m.top + m.bottom);
49563             b.y = h - totalHeight + m.top;
49564             centerH -= totalHeight;
49565             south.updateBox(this.safeBox(b));
49566         }
49567         if(west && west.isVisible()){
49568             var b = west.getBox();
49569             var m = west.getMargins();
49570             b.height = centerH - (m.top+m.bottom);
49571             b.x = m.left;
49572             b.y = centerY + m.top;
49573             var totalWidth = (b.width + m.left + m.right);
49574             centerX += totalWidth;
49575             centerW -= totalWidth;
49576             west.updateBox(this.safeBox(b));
49577         }
49578         if(east && east.isVisible()){
49579             var b = east.getBox();
49580             var m = east.getMargins();
49581             b.height = centerH - (m.top+m.bottom);
49582             var totalWidth = (b.width + m.left + m.right);
49583             b.x = w - totalWidth + m.left;
49584             b.y = centerY + m.top;
49585             centerW -= totalWidth;
49586             east.updateBox(this.safeBox(b));
49587         }
49588         if(center){
49589             var m = center.getMargins();
49590             var centerBox = {
49591                 x: centerX + m.left,
49592                 y: centerY + m.top,
49593                 width: centerW - (m.left+m.right),
49594                 height: centerH - (m.top+m.bottom)
49595             };
49596             //if(this.hideOnLayout){
49597                 //center.el.setStyle("display", "block");
49598             //}
49599             center.updateBox(this.safeBox(centerBox));
49600         }
49601         this.el.repaint();
49602         this.fireEvent("layout", this);
49603     },
49604
49605     // private
49606     safeBox : function(box){
49607         box.width = Math.max(0, box.width);
49608         box.height = Math.max(0, box.height);
49609         return box;
49610     },
49611
49612     /**
49613      * Adds a ContentPanel (or subclass) to this layout.
49614      * @param {String} target The target region key (north, south, east, west or center).
49615      * @param {Roo.ContentPanel} panel The panel to add
49616      * @return {Roo.ContentPanel} The added panel
49617      */
49618     add : function(target, panel){
49619          
49620         target = target.toLowerCase();
49621         return this.regions[target].add(panel);
49622     },
49623
49624     /**
49625      * Remove a ContentPanel (or subclass) to this layout.
49626      * @param {String} target The target region key (north, south, east, west or center).
49627      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49628      * @return {Roo.ContentPanel} The removed panel
49629      */
49630     remove : function(target, panel){
49631         target = target.toLowerCase();
49632         return this.regions[target].remove(panel);
49633     },
49634
49635     /**
49636      * Searches all regions for a panel with the specified id
49637      * @param {String} panelId
49638      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49639      */
49640     findPanel : function(panelId){
49641         var rs = this.regions;
49642         for(var target in rs){
49643             if(typeof rs[target] != "function"){
49644                 var p = rs[target].getPanel(panelId);
49645                 if(p){
49646                     return p;
49647                 }
49648             }
49649         }
49650         return null;
49651     },
49652
49653     /**
49654      * Searches all regions for a panel with the specified id and activates (shows) it.
49655      * @param {String/ContentPanel} panelId The panels id or the panel itself
49656      * @return {Roo.ContentPanel} The shown panel or null
49657      */
49658     showPanel : function(panelId) {
49659       var rs = this.regions;
49660       for(var target in rs){
49661          var r = rs[target];
49662          if(typeof r != "function"){
49663             if(r.hasPanel(panelId)){
49664                return r.showPanel(panelId);
49665             }
49666          }
49667       }
49668       return null;
49669    },
49670
49671    /**
49672      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49673      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49674      */
49675     restoreState : function(provider){
49676         if(!provider){
49677             provider = Roo.state.Manager;
49678         }
49679         var sm = new Roo.LayoutStateManager();
49680         sm.init(this, provider);
49681     },
49682
49683     /**
49684      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49685      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49686      * a valid ContentPanel config object.  Example:
49687      * <pre><code>
49688 // Create the main layout
49689 var layout = new Roo.BorderLayout('main-ct', {
49690     west: {
49691         split:true,
49692         minSize: 175,
49693         titlebar: true
49694     },
49695     center: {
49696         title:'Components'
49697     }
49698 }, 'main-ct');
49699
49700 // Create and add multiple ContentPanels at once via configs
49701 layout.batchAdd({
49702    west: {
49703        id: 'source-files',
49704        autoCreate:true,
49705        title:'Ext Source Files',
49706        autoScroll:true,
49707        fitToFrame:true
49708    },
49709    center : {
49710        el: cview,
49711        autoScroll:true,
49712        fitToFrame:true,
49713        toolbar: tb,
49714        resizeEl:'cbody'
49715    }
49716 });
49717 </code></pre>
49718      * @param {Object} regions An object containing ContentPanel configs by region name
49719      */
49720     batchAdd : function(regions){
49721         this.beginUpdate();
49722         for(var rname in regions){
49723             var lr = this.regions[rname];
49724             if(lr){
49725                 this.addTypedPanels(lr, regions[rname]);
49726             }
49727         }
49728         this.endUpdate();
49729     },
49730
49731     // private
49732     addTypedPanels : function(lr, ps){
49733         if(typeof ps == 'string'){
49734             lr.add(new Roo.ContentPanel(ps));
49735         }
49736         else if(ps instanceof Array){
49737             for(var i =0, len = ps.length; i < len; i++){
49738                 this.addTypedPanels(lr, ps[i]);
49739             }
49740         }
49741         else if(!ps.events){ // raw config?
49742             var el = ps.el;
49743             delete ps.el; // prevent conflict
49744             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49745         }
49746         else {  // panel object assumed!
49747             lr.add(ps);
49748         }
49749     },
49750     /**
49751      * Adds a xtype elements to the layout.
49752      * <pre><code>
49753
49754 layout.addxtype({
49755        xtype : 'ContentPanel',
49756        region: 'west',
49757        items: [ .... ]
49758    }
49759 );
49760
49761 layout.addxtype({
49762         xtype : 'NestedLayoutPanel',
49763         region: 'west',
49764         layout: {
49765            center: { },
49766            west: { }   
49767         },
49768         items : [ ... list of content panels or nested layout panels.. ]
49769    }
49770 );
49771 </code></pre>
49772      * @param {Object} cfg Xtype definition of item to add.
49773      */
49774     addxtype : function(cfg)
49775     {
49776         // basically accepts a pannel...
49777         // can accept a layout region..!?!?
49778         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49779         
49780         if (!cfg.xtype.match(/Panel$/)) {
49781             return false;
49782         }
49783         var ret = false;
49784         
49785         if (typeof(cfg.region) == 'undefined') {
49786             Roo.log("Failed to add Panel, region was not set");
49787             Roo.log(cfg);
49788             return false;
49789         }
49790         var region = cfg.region;
49791         delete cfg.region;
49792         
49793           
49794         var xitems = [];
49795         if (cfg.items) {
49796             xitems = cfg.items;
49797             delete cfg.items;
49798         }
49799         var nb = false;
49800         
49801         switch(cfg.xtype) 
49802         {
49803             case 'ContentPanel':  // ContentPanel (el, cfg)
49804             case 'ScrollPanel':  // ContentPanel (el, cfg)
49805             case 'ViewPanel': 
49806                 if(cfg.autoCreate) {
49807                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49808                 } else {
49809                     var el = this.el.createChild();
49810                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49811                 }
49812                 
49813                 this.add(region, ret);
49814                 break;
49815             
49816             
49817             case 'TreePanel': // our new panel!
49818                 cfg.el = this.el.createChild();
49819                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49820                 this.add(region, ret);
49821                 break;
49822             
49823             case 'NestedLayoutPanel': 
49824                 // create a new Layout (which is  a Border Layout...
49825                 var el = this.el.createChild();
49826                 var clayout = cfg.layout;
49827                 delete cfg.layout;
49828                 clayout.items   = clayout.items  || [];
49829                 // replace this exitems with the clayout ones..
49830                 xitems = clayout.items;
49831                  
49832                 
49833                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49834                     cfg.background = false;
49835                 }
49836                 var layout = new Roo.BorderLayout(el, clayout);
49837                 
49838                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49839                 //console.log('adding nested layout panel '  + cfg.toSource());
49840                 this.add(region, ret);
49841                 nb = {}; /// find first...
49842                 break;
49843                 
49844             case 'GridPanel': 
49845             
49846                 // needs grid and region
49847                 
49848                 //var el = this.getRegion(region).el.createChild();
49849                 var el = this.el.createChild();
49850                 // create the grid first...
49851                 
49852                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49853                 delete cfg.grid;
49854                 if (region == 'center' && this.active ) {
49855                     cfg.background = false;
49856                 }
49857                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49858                 
49859                 this.add(region, ret);
49860                 if (cfg.background) {
49861                     ret.on('activate', function(gp) {
49862                         if (!gp.grid.rendered) {
49863                             gp.grid.render();
49864                         }
49865                     });
49866                 } else {
49867                     grid.render();
49868                 }
49869                 break;
49870            
49871            
49872            
49873                 
49874                 
49875                 
49876             default:
49877                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49878                     
49879                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49880                     this.add(region, ret);
49881                 } else {
49882                 
49883                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49884                     return null;
49885                 }
49886                 
49887              // GridPanel (grid, cfg)
49888             
49889         }
49890         this.beginUpdate();
49891         // add children..
49892         var region = '';
49893         var abn = {};
49894         Roo.each(xitems, function(i)  {
49895             region = nb && i.region ? i.region : false;
49896             
49897             var add = ret.addxtype(i);
49898            
49899             if (region) {
49900                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49901                 if (!i.background) {
49902                     abn[region] = nb[region] ;
49903                 }
49904             }
49905             
49906         });
49907         this.endUpdate();
49908
49909         // make the last non-background panel active..
49910         //if (nb) { Roo.log(abn); }
49911         if (nb) {
49912             
49913             for(var r in abn) {
49914                 region = this.getRegion(r);
49915                 if (region) {
49916                     // tried using nb[r], but it does not work..
49917                      
49918                     region.showPanel(abn[r]);
49919                    
49920                 }
49921             }
49922         }
49923         return ret;
49924         
49925     }
49926 });
49927
49928 /**
49929  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49930  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49931  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49932  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49933  * <pre><code>
49934 // shorthand
49935 var CP = Roo.ContentPanel;
49936
49937 var layout = Roo.BorderLayout.create({
49938     north: {
49939         initialSize: 25,
49940         titlebar: false,
49941         panels: [new CP("north", "North")]
49942     },
49943     west: {
49944         split:true,
49945         initialSize: 200,
49946         minSize: 175,
49947         maxSize: 400,
49948         titlebar: true,
49949         collapsible: true,
49950         panels: [new CP("west", {title: "West"})]
49951     },
49952     east: {
49953         split:true,
49954         initialSize: 202,
49955         minSize: 175,
49956         maxSize: 400,
49957         titlebar: true,
49958         collapsible: true,
49959         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49960     },
49961     south: {
49962         split:true,
49963         initialSize: 100,
49964         minSize: 100,
49965         maxSize: 200,
49966         titlebar: true,
49967         collapsible: true,
49968         panels: [new CP("south", {title: "South", closable: true})]
49969     },
49970     center: {
49971         titlebar: true,
49972         autoScroll:true,
49973         resizeTabs: true,
49974         minTabWidth: 50,
49975         preferredTabWidth: 150,
49976         panels: [
49977             new CP("center1", {title: "Close Me", closable: true}),
49978             new CP("center2", {title: "Center Panel", closable: false})
49979         ]
49980     }
49981 }, document.body);
49982
49983 layout.getRegion("center").showPanel("center1");
49984 </code></pre>
49985  * @param config
49986  * @param targetEl
49987  */
49988 Roo.BorderLayout.create = function(config, targetEl){
49989     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49990     layout.beginUpdate();
49991     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49992     for(var j = 0, jlen = regions.length; j < jlen; j++){
49993         var lr = regions[j];
49994         if(layout.regions[lr] && config[lr].panels){
49995             var r = layout.regions[lr];
49996             var ps = config[lr].panels;
49997             layout.addTypedPanels(r, ps);
49998         }
49999     }
50000     layout.endUpdate();
50001     return layout;
50002 };
50003
50004 // private
50005 Roo.BorderLayout.RegionFactory = {
50006     // private
50007     validRegions : ["north","south","east","west","center"],
50008
50009     // private
50010     create : function(target, mgr, config){
50011         target = target.toLowerCase();
50012         if(config.lightweight || config.basic){
50013             return new Roo.BasicLayoutRegion(mgr, config, target);
50014         }
50015         switch(target){
50016             case "north":
50017                 return new Roo.NorthLayoutRegion(mgr, config);
50018             case "south":
50019                 return new Roo.SouthLayoutRegion(mgr, config);
50020             case "east":
50021                 return new Roo.EastLayoutRegion(mgr, config);
50022             case "west":
50023                 return new Roo.WestLayoutRegion(mgr, config);
50024             case "center":
50025                 return new Roo.CenterLayoutRegion(mgr, config);
50026         }
50027         throw 'Layout region "'+target+'" not supported.';
50028     }
50029 };/*
50030  * Based on:
50031  * Ext JS Library 1.1.1
50032  * Copyright(c) 2006-2007, Ext JS, LLC.
50033  *
50034  * Originally Released Under LGPL - original licence link has changed is not relivant.
50035  *
50036  * Fork - LGPL
50037  * <script type="text/javascript">
50038  */
50039  
50040 /**
50041  * @class Roo.BasicLayoutRegion
50042  * @extends Roo.util.Observable
50043  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
50044  * and does not have a titlebar, tabs or any other features. All it does is size and position 
50045  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
50046  */
50047 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
50048     this.mgr = mgr;
50049     this.position  = pos;
50050     this.events = {
50051         /**
50052          * @scope Roo.BasicLayoutRegion
50053          */
50054         
50055         /**
50056          * @event beforeremove
50057          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
50058          * @param {Roo.LayoutRegion} this
50059          * @param {Roo.ContentPanel} panel The panel
50060          * @param {Object} e The cancel event object
50061          */
50062         "beforeremove" : true,
50063         /**
50064          * @event invalidated
50065          * Fires when the layout for this region is changed.
50066          * @param {Roo.LayoutRegion} this
50067          */
50068         "invalidated" : true,
50069         /**
50070          * @event visibilitychange
50071          * Fires when this region is shown or hidden 
50072          * @param {Roo.LayoutRegion} this
50073          * @param {Boolean} visibility true or false
50074          */
50075         "visibilitychange" : true,
50076         /**
50077          * @event paneladded
50078          * Fires when a panel is added. 
50079          * @param {Roo.LayoutRegion} this
50080          * @param {Roo.ContentPanel} panel The panel
50081          */
50082         "paneladded" : true,
50083         /**
50084          * @event panelremoved
50085          * Fires when a panel is removed. 
50086          * @param {Roo.LayoutRegion} this
50087          * @param {Roo.ContentPanel} panel The panel
50088          */
50089         "panelremoved" : true,
50090         /**
50091          * @event collapsed
50092          * Fires when this region is collapsed.
50093          * @param {Roo.LayoutRegion} this
50094          */
50095         "collapsed" : true,
50096         /**
50097          * @event expanded
50098          * Fires when this region is expanded.
50099          * @param {Roo.LayoutRegion} this
50100          */
50101         "expanded" : true,
50102         /**
50103          * @event slideshow
50104          * Fires when this region is slid into view.
50105          * @param {Roo.LayoutRegion} this
50106          */
50107         "slideshow" : true,
50108         /**
50109          * @event slidehide
50110          * Fires when this region slides out of view. 
50111          * @param {Roo.LayoutRegion} this
50112          */
50113         "slidehide" : true,
50114         /**
50115          * @event panelactivated
50116          * Fires when a panel is activated. 
50117          * @param {Roo.LayoutRegion} this
50118          * @param {Roo.ContentPanel} panel The activated panel
50119          */
50120         "panelactivated" : true,
50121         /**
50122          * @event resized
50123          * Fires when the user resizes this region. 
50124          * @param {Roo.LayoutRegion} this
50125          * @param {Number} newSize The new size (width for east/west, height for north/south)
50126          */
50127         "resized" : true
50128     };
50129     /** A collection of panels in this region. @type Roo.util.MixedCollection */
50130     this.panels = new Roo.util.MixedCollection();
50131     this.panels.getKey = this.getPanelId.createDelegate(this);
50132     this.box = null;
50133     this.activePanel = null;
50134     // ensure listeners are added...
50135     
50136     if (config.listeners || config.events) {
50137         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
50138             listeners : config.listeners || {},
50139             events : config.events || {}
50140         });
50141     }
50142     
50143     if(skipConfig !== true){
50144         this.applyConfig(config);
50145     }
50146 };
50147
50148 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
50149     getPanelId : function(p){
50150         return p.getId();
50151     },
50152     
50153     applyConfig : function(config){
50154         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50155         this.config = config;
50156         
50157     },
50158     
50159     /**
50160      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
50161      * the width, for horizontal (north, south) the height.
50162      * @param {Number} newSize The new width or height
50163      */
50164     resizeTo : function(newSize){
50165         var el = this.el ? this.el :
50166                  (this.activePanel ? this.activePanel.getEl() : null);
50167         if(el){
50168             switch(this.position){
50169                 case "east":
50170                 case "west":
50171                     el.setWidth(newSize);
50172                     this.fireEvent("resized", this, newSize);
50173                 break;
50174                 case "north":
50175                 case "south":
50176                     el.setHeight(newSize);
50177                     this.fireEvent("resized", this, newSize);
50178                 break;                
50179             }
50180         }
50181     },
50182     
50183     getBox : function(){
50184         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
50185     },
50186     
50187     getMargins : function(){
50188         return this.margins;
50189     },
50190     
50191     updateBox : function(box){
50192         this.box = box;
50193         var el = this.activePanel.getEl();
50194         el.dom.style.left = box.x + "px";
50195         el.dom.style.top = box.y + "px";
50196         this.activePanel.setSize(box.width, box.height);
50197     },
50198     
50199     /**
50200      * Returns the container element for this region.
50201      * @return {Roo.Element}
50202      */
50203     getEl : function(){
50204         return this.activePanel;
50205     },
50206     
50207     /**
50208      * Returns true if this region is currently visible.
50209      * @return {Boolean}
50210      */
50211     isVisible : function(){
50212         return this.activePanel ? true : false;
50213     },
50214     
50215     setActivePanel : function(panel){
50216         panel = this.getPanel(panel);
50217         if(this.activePanel && this.activePanel != panel){
50218             this.activePanel.setActiveState(false);
50219             this.activePanel.getEl().setLeftTop(-10000,-10000);
50220         }
50221         this.activePanel = panel;
50222         panel.setActiveState(true);
50223         if(this.box){
50224             panel.setSize(this.box.width, this.box.height);
50225         }
50226         this.fireEvent("panelactivated", this, panel);
50227         this.fireEvent("invalidated");
50228     },
50229     
50230     /**
50231      * Show the specified panel.
50232      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50233      * @return {Roo.ContentPanel} The shown panel or null
50234      */
50235     showPanel : function(panel){
50236         if(panel = this.getPanel(panel)){
50237             this.setActivePanel(panel);
50238         }
50239         return panel;
50240     },
50241     
50242     /**
50243      * Get the active panel for this region.
50244      * @return {Roo.ContentPanel} The active panel or null
50245      */
50246     getActivePanel : function(){
50247         return this.activePanel;
50248     },
50249     
50250     /**
50251      * Add the passed ContentPanel(s)
50252      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50253      * @return {Roo.ContentPanel} The panel added (if only one was added)
50254      */
50255     add : function(panel){
50256         if(arguments.length > 1){
50257             for(var i = 0, len = arguments.length; i < len; i++) {
50258                 this.add(arguments[i]);
50259             }
50260             return null;
50261         }
50262         if(this.hasPanel(panel)){
50263             this.showPanel(panel);
50264             return panel;
50265         }
50266         var el = panel.getEl();
50267         if(el.dom.parentNode != this.mgr.el.dom){
50268             this.mgr.el.dom.appendChild(el.dom);
50269         }
50270         if(panel.setRegion){
50271             panel.setRegion(this);
50272         }
50273         this.panels.add(panel);
50274         el.setStyle("position", "absolute");
50275         if(!panel.background){
50276             this.setActivePanel(panel);
50277             if(this.config.initialSize && this.panels.getCount()==1){
50278                 this.resizeTo(this.config.initialSize);
50279             }
50280         }
50281         this.fireEvent("paneladded", this, panel);
50282         return panel;
50283     },
50284     
50285     /**
50286      * Returns true if the panel is in this region.
50287      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50288      * @return {Boolean}
50289      */
50290     hasPanel : function(panel){
50291         if(typeof panel == "object"){ // must be panel obj
50292             panel = panel.getId();
50293         }
50294         return this.getPanel(panel) ? true : false;
50295     },
50296     
50297     /**
50298      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50299      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50300      * @param {Boolean} preservePanel Overrides the config preservePanel option
50301      * @return {Roo.ContentPanel} The panel that was removed
50302      */
50303     remove : function(panel, preservePanel){
50304         panel = this.getPanel(panel);
50305         if(!panel){
50306             return null;
50307         }
50308         var e = {};
50309         this.fireEvent("beforeremove", this, panel, e);
50310         if(e.cancel === true){
50311             return null;
50312         }
50313         var panelId = panel.getId();
50314         this.panels.removeKey(panelId);
50315         return panel;
50316     },
50317     
50318     /**
50319      * Returns the panel specified or null if it's not in this region.
50320      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50321      * @return {Roo.ContentPanel}
50322      */
50323     getPanel : function(id){
50324         if(typeof id == "object"){ // must be panel obj
50325             return id;
50326         }
50327         return this.panels.get(id);
50328     },
50329     
50330     /**
50331      * Returns this regions position (north/south/east/west/center).
50332      * @return {String} 
50333      */
50334     getPosition: function(){
50335         return this.position;    
50336     }
50337 });/*
50338  * Based on:
50339  * Ext JS Library 1.1.1
50340  * Copyright(c) 2006-2007, Ext JS, LLC.
50341  *
50342  * Originally Released Under LGPL - original licence link has changed is not relivant.
50343  *
50344  * Fork - LGPL
50345  * <script type="text/javascript">
50346  */
50347  
50348 /**
50349  * @class Roo.LayoutRegion
50350  * @extends Roo.BasicLayoutRegion
50351  * This class represents a region in a layout manager.
50352  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50353  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50354  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50355  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50356  * @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})
50357  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50358  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50359  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50360  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50361  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50362  * @cfg {String}    title           The title for the region (overrides panel titles)
50363  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50364  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50365  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50366  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50367  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50368  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50369  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50370  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50371  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50372  * @cfg {Boolean}   showPin         True to show a pin button
50373  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50374  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50375  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50376  * @cfg {Number}    width           For East/West panels
50377  * @cfg {Number}    height          For North/South panels
50378  * @cfg {Boolean}   split           To show the splitter
50379  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50380  */
50381 Roo.LayoutRegion = function(mgr, config, pos){
50382     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50383     var dh = Roo.DomHelper;
50384     /** This region's container element 
50385     * @type Roo.Element */
50386     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50387     /** This region's title element 
50388     * @type Roo.Element */
50389
50390     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50391         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50392         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50393     ]}, true);
50394     this.titleEl.enableDisplayMode();
50395     /** This region's title text element 
50396     * @type HTMLElement */
50397     this.titleTextEl = this.titleEl.dom.firstChild;
50398     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50399     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50400     this.closeBtn.enableDisplayMode();
50401     this.closeBtn.on("click", this.closeClicked, this);
50402     this.closeBtn.hide();
50403
50404     this.createBody(config);
50405     this.visible = true;
50406     this.collapsed = false;
50407
50408     if(config.hideWhenEmpty){
50409         this.hide();
50410         this.on("paneladded", this.validateVisibility, this);
50411         this.on("panelremoved", this.validateVisibility, this);
50412     }
50413     this.applyConfig(config);
50414 };
50415
50416 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50417
50418     createBody : function(){
50419         /** This region's body element 
50420         * @type Roo.Element */
50421         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50422     },
50423
50424     applyConfig : function(c){
50425         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50426             var dh = Roo.DomHelper;
50427             if(c.titlebar !== false){
50428                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50429                 this.collapseBtn.on("click", this.collapse, this);
50430                 this.collapseBtn.enableDisplayMode();
50431
50432                 if(c.showPin === true || this.showPin){
50433                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50434                     this.stickBtn.enableDisplayMode();
50435                     this.stickBtn.on("click", this.expand, this);
50436                     this.stickBtn.hide();
50437                 }
50438             }
50439             /** This region's collapsed element
50440             * @type Roo.Element */
50441             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50442                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50443             ]}, true);
50444             if(c.floatable !== false){
50445                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50446                this.collapsedEl.on("click", this.collapseClick, this);
50447             }
50448
50449             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50450                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50451                    id: "message", unselectable: "on", style:{"float":"left"}});
50452                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50453              }
50454             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50455             this.expandBtn.on("click", this.expand, this);
50456         }
50457         if(this.collapseBtn){
50458             this.collapseBtn.setVisible(c.collapsible == true);
50459         }
50460         this.cmargins = c.cmargins || this.cmargins ||
50461                          (this.position == "west" || this.position == "east" ?
50462                              {top: 0, left: 2, right:2, bottom: 0} :
50463                              {top: 2, left: 0, right:0, bottom: 2});
50464         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50465         this.bottomTabs = c.tabPosition != "top";
50466         this.autoScroll = c.autoScroll || false;
50467         if(this.autoScroll){
50468             this.bodyEl.setStyle("overflow", "auto");
50469         }else{
50470             this.bodyEl.setStyle("overflow", "hidden");
50471         }
50472         //if(c.titlebar !== false){
50473             if((!c.titlebar && !c.title) || c.titlebar === false){
50474                 this.titleEl.hide();
50475             }else{
50476                 this.titleEl.show();
50477                 if(c.title){
50478                     this.titleTextEl.innerHTML = c.title;
50479                 }
50480             }
50481         //}
50482         this.duration = c.duration || .30;
50483         this.slideDuration = c.slideDuration || .45;
50484         this.config = c;
50485         if(c.collapsed){
50486             this.collapse(true);
50487         }
50488         if(c.hidden){
50489             this.hide();
50490         }
50491     },
50492     /**
50493      * Returns true if this region is currently visible.
50494      * @return {Boolean}
50495      */
50496     isVisible : function(){
50497         return this.visible;
50498     },
50499
50500     /**
50501      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50502      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50503      */
50504     setCollapsedTitle : function(title){
50505         title = title || "&#160;";
50506         if(this.collapsedTitleTextEl){
50507             this.collapsedTitleTextEl.innerHTML = title;
50508         }
50509     },
50510
50511     getBox : function(){
50512         var b;
50513         if(!this.collapsed){
50514             b = this.el.getBox(false, true);
50515         }else{
50516             b = this.collapsedEl.getBox(false, true);
50517         }
50518         return b;
50519     },
50520
50521     getMargins : function(){
50522         return this.collapsed ? this.cmargins : this.margins;
50523     },
50524
50525     highlight : function(){
50526         this.el.addClass("x-layout-panel-dragover");
50527     },
50528
50529     unhighlight : function(){
50530         this.el.removeClass("x-layout-panel-dragover");
50531     },
50532
50533     updateBox : function(box){
50534         this.box = box;
50535         if(!this.collapsed){
50536             this.el.dom.style.left = box.x + "px";
50537             this.el.dom.style.top = box.y + "px";
50538             this.updateBody(box.width, box.height);
50539         }else{
50540             this.collapsedEl.dom.style.left = box.x + "px";
50541             this.collapsedEl.dom.style.top = box.y + "px";
50542             this.collapsedEl.setSize(box.width, box.height);
50543         }
50544         if(this.tabs){
50545             this.tabs.autoSizeTabs();
50546         }
50547     },
50548
50549     updateBody : function(w, h){
50550         if(w !== null){
50551             this.el.setWidth(w);
50552             w -= this.el.getBorderWidth("rl");
50553             if(this.config.adjustments){
50554                 w += this.config.adjustments[0];
50555             }
50556         }
50557         if(h !== null){
50558             this.el.setHeight(h);
50559             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50560             h -= this.el.getBorderWidth("tb");
50561             if(this.config.adjustments){
50562                 h += this.config.adjustments[1];
50563             }
50564             this.bodyEl.setHeight(h);
50565             if(this.tabs){
50566                 h = this.tabs.syncHeight(h);
50567             }
50568         }
50569         if(this.panelSize){
50570             w = w !== null ? w : this.panelSize.width;
50571             h = h !== null ? h : this.panelSize.height;
50572         }
50573         if(this.activePanel){
50574             var el = this.activePanel.getEl();
50575             w = w !== null ? w : el.getWidth();
50576             h = h !== null ? h : el.getHeight();
50577             this.panelSize = {width: w, height: h};
50578             this.activePanel.setSize(w, h);
50579         }
50580         if(Roo.isIE && this.tabs){
50581             this.tabs.el.repaint();
50582         }
50583     },
50584
50585     /**
50586      * Returns the container element for this region.
50587      * @return {Roo.Element}
50588      */
50589     getEl : function(){
50590         return this.el;
50591     },
50592
50593     /**
50594      * Hides this region.
50595      */
50596     hide : function(){
50597         if(!this.collapsed){
50598             this.el.dom.style.left = "-2000px";
50599             this.el.hide();
50600         }else{
50601             this.collapsedEl.dom.style.left = "-2000px";
50602             this.collapsedEl.hide();
50603         }
50604         this.visible = false;
50605         this.fireEvent("visibilitychange", this, false);
50606     },
50607
50608     /**
50609      * Shows this region if it was previously hidden.
50610      */
50611     show : function(){
50612         if(!this.collapsed){
50613             this.el.show();
50614         }else{
50615             this.collapsedEl.show();
50616         }
50617         this.visible = true;
50618         this.fireEvent("visibilitychange", this, true);
50619     },
50620
50621     closeClicked : function(){
50622         if(this.activePanel){
50623             this.remove(this.activePanel);
50624         }
50625     },
50626
50627     collapseClick : function(e){
50628         if(this.isSlid){
50629            e.stopPropagation();
50630            this.slideIn();
50631         }else{
50632            e.stopPropagation();
50633            this.slideOut();
50634         }
50635     },
50636
50637     /**
50638      * Collapses this region.
50639      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50640      */
50641     collapse : function(skipAnim){
50642         if(this.collapsed) {
50643             return;
50644         }
50645         this.collapsed = true;
50646         if(this.split){
50647             this.split.el.hide();
50648         }
50649         if(this.config.animate && skipAnim !== true){
50650             this.fireEvent("invalidated", this);
50651             this.animateCollapse();
50652         }else{
50653             this.el.setLocation(-20000,-20000);
50654             this.el.hide();
50655             this.collapsedEl.show();
50656             this.fireEvent("collapsed", this);
50657             this.fireEvent("invalidated", this);
50658         }
50659     },
50660
50661     animateCollapse : function(){
50662         // overridden
50663     },
50664
50665     /**
50666      * Expands this region if it was previously collapsed.
50667      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50668      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50669      */
50670     expand : function(e, skipAnim){
50671         if(e) {
50672             e.stopPropagation();
50673         }
50674         if(!this.collapsed || this.el.hasActiveFx()) {
50675             return;
50676         }
50677         if(this.isSlid){
50678             this.afterSlideIn();
50679             skipAnim = true;
50680         }
50681         this.collapsed = false;
50682         if(this.config.animate && skipAnim !== true){
50683             this.animateExpand();
50684         }else{
50685             this.el.show();
50686             if(this.split){
50687                 this.split.el.show();
50688             }
50689             this.collapsedEl.setLocation(-2000,-2000);
50690             this.collapsedEl.hide();
50691             this.fireEvent("invalidated", this);
50692             this.fireEvent("expanded", this);
50693         }
50694     },
50695
50696     animateExpand : function(){
50697         // overridden
50698     },
50699
50700     initTabs : function()
50701     {
50702         this.bodyEl.setStyle("overflow", "hidden");
50703         var ts = new Roo.TabPanel(
50704                 this.bodyEl.dom,
50705                 {
50706                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50707                     disableTooltips: this.config.disableTabTips,
50708                     toolbar : this.config.toolbar
50709                 }
50710         );
50711         if(this.config.hideTabs){
50712             ts.stripWrap.setDisplayed(false);
50713         }
50714         this.tabs = ts;
50715         ts.resizeTabs = this.config.resizeTabs === true;
50716         ts.minTabWidth = this.config.minTabWidth || 40;
50717         ts.maxTabWidth = this.config.maxTabWidth || 250;
50718         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50719         ts.monitorResize = false;
50720         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50721         ts.bodyEl.addClass('x-layout-tabs-body');
50722         this.panels.each(this.initPanelAsTab, this);
50723     },
50724
50725     initPanelAsTab : function(panel){
50726         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50727                     this.config.closeOnTab && panel.isClosable());
50728         if(panel.tabTip !== undefined){
50729             ti.setTooltip(panel.tabTip);
50730         }
50731         ti.on("activate", function(){
50732               this.setActivePanel(panel);
50733         }, this);
50734         if(this.config.closeOnTab){
50735             ti.on("beforeclose", function(t, e){
50736                 e.cancel = true;
50737                 this.remove(panel);
50738             }, this);
50739         }
50740         return ti;
50741     },
50742
50743     updatePanelTitle : function(panel, title){
50744         if(this.activePanel == panel){
50745             this.updateTitle(title);
50746         }
50747         if(this.tabs){
50748             var ti = this.tabs.getTab(panel.getEl().id);
50749             ti.setText(title);
50750             if(panel.tabTip !== undefined){
50751                 ti.setTooltip(panel.tabTip);
50752             }
50753         }
50754     },
50755
50756     updateTitle : function(title){
50757         if(this.titleTextEl && !this.config.title){
50758             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50759         }
50760     },
50761
50762     setActivePanel : function(panel){
50763         panel = this.getPanel(panel);
50764         if(this.activePanel && this.activePanel != panel){
50765             this.activePanel.setActiveState(false);
50766         }
50767         this.activePanel = panel;
50768         panel.setActiveState(true);
50769         if(this.panelSize){
50770             panel.setSize(this.panelSize.width, this.panelSize.height);
50771         }
50772         if(this.closeBtn){
50773             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50774         }
50775         this.updateTitle(panel.getTitle());
50776         if(this.tabs){
50777             this.fireEvent("invalidated", this);
50778         }
50779         this.fireEvent("panelactivated", this, panel);
50780     },
50781
50782     /**
50783      * Shows the specified panel.
50784      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50785      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50786      */
50787     showPanel : function(panel)
50788     {
50789         panel = this.getPanel(panel);
50790         if(panel){
50791             if(this.tabs){
50792                 var tab = this.tabs.getTab(panel.getEl().id);
50793                 if(tab.isHidden()){
50794                     this.tabs.unhideTab(tab.id);
50795                 }
50796                 tab.activate();
50797             }else{
50798                 this.setActivePanel(panel);
50799             }
50800         }
50801         return panel;
50802     },
50803
50804     /**
50805      * Get the active panel for this region.
50806      * @return {Roo.ContentPanel} The active panel or null
50807      */
50808     getActivePanel : function(){
50809         return this.activePanel;
50810     },
50811
50812     validateVisibility : function(){
50813         if(this.panels.getCount() < 1){
50814             this.updateTitle("&#160;");
50815             this.closeBtn.hide();
50816             this.hide();
50817         }else{
50818             if(!this.isVisible()){
50819                 this.show();
50820             }
50821         }
50822     },
50823
50824     /**
50825      * Adds the passed ContentPanel(s) to this region.
50826      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50827      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50828      */
50829     add : function(panel){
50830         if(arguments.length > 1){
50831             for(var i = 0, len = arguments.length; i < len; i++) {
50832                 this.add(arguments[i]);
50833             }
50834             return null;
50835         }
50836         if(this.hasPanel(panel)){
50837             this.showPanel(panel);
50838             return panel;
50839         }
50840         panel.setRegion(this);
50841         this.panels.add(panel);
50842         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50843             this.bodyEl.dom.appendChild(panel.getEl().dom);
50844             if(panel.background !== true){
50845                 this.setActivePanel(panel);
50846             }
50847             this.fireEvent("paneladded", this, panel);
50848             return panel;
50849         }
50850         if(!this.tabs){
50851             this.initTabs();
50852         }else{
50853             this.initPanelAsTab(panel);
50854         }
50855         if(panel.background !== true){
50856             this.tabs.activate(panel.getEl().id);
50857         }
50858         this.fireEvent("paneladded", this, panel);
50859         return panel;
50860     },
50861
50862     /**
50863      * Hides the tab for the specified panel.
50864      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50865      */
50866     hidePanel : function(panel){
50867         if(this.tabs && (panel = this.getPanel(panel))){
50868             this.tabs.hideTab(panel.getEl().id);
50869         }
50870     },
50871
50872     /**
50873      * Unhides the tab for a previously hidden panel.
50874      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50875      */
50876     unhidePanel : function(panel){
50877         if(this.tabs && (panel = this.getPanel(panel))){
50878             this.tabs.unhideTab(panel.getEl().id);
50879         }
50880     },
50881
50882     clearPanels : function(){
50883         while(this.panels.getCount() > 0){
50884              this.remove(this.panels.first());
50885         }
50886     },
50887
50888     /**
50889      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50890      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50891      * @param {Boolean} preservePanel Overrides the config preservePanel option
50892      * @return {Roo.ContentPanel} The panel that was removed
50893      */
50894     remove : function(panel, preservePanel){
50895         panel = this.getPanel(panel);
50896         if(!panel){
50897             return null;
50898         }
50899         var e = {};
50900         this.fireEvent("beforeremove", this, panel, e);
50901         if(e.cancel === true){
50902             return null;
50903         }
50904         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50905         var panelId = panel.getId();
50906         this.panels.removeKey(panelId);
50907         if(preservePanel){
50908             document.body.appendChild(panel.getEl().dom);
50909         }
50910         if(this.tabs){
50911             this.tabs.removeTab(panel.getEl().id);
50912         }else if (!preservePanel){
50913             this.bodyEl.dom.removeChild(panel.getEl().dom);
50914         }
50915         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50916             var p = this.panels.first();
50917             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50918             tempEl.appendChild(p.getEl().dom);
50919             this.bodyEl.update("");
50920             this.bodyEl.dom.appendChild(p.getEl().dom);
50921             tempEl = null;
50922             this.updateTitle(p.getTitle());
50923             this.tabs = null;
50924             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50925             this.setActivePanel(p);
50926         }
50927         panel.setRegion(null);
50928         if(this.activePanel == panel){
50929             this.activePanel = null;
50930         }
50931         if(this.config.autoDestroy !== false && preservePanel !== true){
50932             try{panel.destroy();}catch(e){}
50933         }
50934         this.fireEvent("panelremoved", this, panel);
50935         return panel;
50936     },
50937
50938     /**
50939      * Returns the TabPanel component used by this region
50940      * @return {Roo.TabPanel}
50941      */
50942     getTabs : function(){
50943         return this.tabs;
50944     },
50945
50946     createTool : function(parentEl, className){
50947         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50948             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50949         btn.addClassOnOver("x-layout-tools-button-over");
50950         return btn;
50951     }
50952 });/*
50953  * Based on:
50954  * Ext JS Library 1.1.1
50955  * Copyright(c) 2006-2007, Ext JS, LLC.
50956  *
50957  * Originally Released Under LGPL - original licence link has changed is not relivant.
50958  *
50959  * Fork - LGPL
50960  * <script type="text/javascript">
50961  */
50962  
50963
50964
50965 /**
50966  * @class Roo.SplitLayoutRegion
50967  * @extends Roo.LayoutRegion
50968  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50969  */
50970 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50971     this.cursor = cursor;
50972     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50973 };
50974
50975 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50976     splitTip : "Drag to resize.",
50977     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50978     useSplitTips : false,
50979
50980     applyConfig : function(config){
50981         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50982         if(config.split){
50983             if(!this.split){
50984                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50985                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50986                 /** The SplitBar for this region 
50987                 * @type Roo.SplitBar */
50988                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50989                 this.split.on("moved", this.onSplitMove, this);
50990                 this.split.useShim = config.useShim === true;
50991                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50992                 if(this.useSplitTips){
50993                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50994                 }
50995                 if(config.collapsible){
50996                     this.split.el.on("dblclick", this.collapse,  this);
50997                 }
50998             }
50999             if(typeof config.minSize != "undefined"){
51000                 this.split.minSize = config.minSize;
51001             }
51002             if(typeof config.maxSize != "undefined"){
51003                 this.split.maxSize = config.maxSize;
51004             }
51005             if(config.hideWhenEmpty || config.hidden || config.collapsed){
51006                 this.hideSplitter();
51007             }
51008         }
51009     },
51010
51011     getHMaxSize : function(){
51012          var cmax = this.config.maxSize || 10000;
51013          var center = this.mgr.getRegion("center");
51014          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
51015     },
51016
51017     getVMaxSize : function(){
51018          var cmax = this.config.maxSize || 10000;
51019          var center = this.mgr.getRegion("center");
51020          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
51021     },
51022
51023     onSplitMove : function(split, newSize){
51024         this.fireEvent("resized", this, newSize);
51025     },
51026     
51027     /** 
51028      * Returns the {@link Roo.SplitBar} for this region.
51029      * @return {Roo.SplitBar}
51030      */
51031     getSplitBar : function(){
51032         return this.split;
51033     },
51034     
51035     hide : function(){
51036         this.hideSplitter();
51037         Roo.SplitLayoutRegion.superclass.hide.call(this);
51038     },
51039
51040     hideSplitter : function(){
51041         if(this.split){
51042             this.split.el.setLocation(-2000,-2000);
51043             this.split.el.hide();
51044         }
51045     },
51046
51047     show : function(){
51048         if(this.split){
51049             this.split.el.show();
51050         }
51051         Roo.SplitLayoutRegion.superclass.show.call(this);
51052     },
51053     
51054     beforeSlide: function(){
51055         if(Roo.isGecko){// firefox overflow auto bug workaround
51056             this.bodyEl.clip();
51057             if(this.tabs) {
51058                 this.tabs.bodyEl.clip();
51059             }
51060             if(this.activePanel){
51061                 this.activePanel.getEl().clip();
51062                 
51063                 if(this.activePanel.beforeSlide){
51064                     this.activePanel.beforeSlide();
51065                 }
51066             }
51067         }
51068     },
51069     
51070     afterSlide : function(){
51071         if(Roo.isGecko){// firefox overflow auto bug workaround
51072             this.bodyEl.unclip();
51073             if(this.tabs) {
51074                 this.tabs.bodyEl.unclip();
51075             }
51076             if(this.activePanel){
51077                 this.activePanel.getEl().unclip();
51078                 if(this.activePanel.afterSlide){
51079                     this.activePanel.afterSlide();
51080                 }
51081             }
51082         }
51083     },
51084
51085     initAutoHide : function(){
51086         if(this.autoHide !== false){
51087             if(!this.autoHideHd){
51088                 var st = new Roo.util.DelayedTask(this.slideIn, this);
51089                 this.autoHideHd = {
51090                     "mouseout": function(e){
51091                         if(!e.within(this.el, true)){
51092                             st.delay(500);
51093                         }
51094                     },
51095                     "mouseover" : function(e){
51096                         st.cancel();
51097                     },
51098                     scope : this
51099                 };
51100             }
51101             this.el.on(this.autoHideHd);
51102         }
51103     },
51104
51105     clearAutoHide : function(){
51106         if(this.autoHide !== false){
51107             this.el.un("mouseout", this.autoHideHd.mouseout);
51108             this.el.un("mouseover", this.autoHideHd.mouseover);
51109         }
51110     },
51111
51112     clearMonitor : function(){
51113         Roo.get(document).un("click", this.slideInIf, this);
51114     },
51115
51116     // these names are backwards but not changed for compat
51117     slideOut : function(){
51118         if(this.isSlid || this.el.hasActiveFx()){
51119             return;
51120         }
51121         this.isSlid = true;
51122         if(this.collapseBtn){
51123             this.collapseBtn.hide();
51124         }
51125         this.closeBtnState = this.closeBtn.getStyle('display');
51126         this.closeBtn.hide();
51127         if(this.stickBtn){
51128             this.stickBtn.show();
51129         }
51130         this.el.show();
51131         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
51132         this.beforeSlide();
51133         this.el.setStyle("z-index", 10001);
51134         this.el.slideIn(this.getSlideAnchor(), {
51135             callback: function(){
51136                 this.afterSlide();
51137                 this.initAutoHide();
51138                 Roo.get(document).on("click", this.slideInIf, this);
51139                 this.fireEvent("slideshow", this);
51140             },
51141             scope: this,
51142             block: true
51143         });
51144     },
51145
51146     afterSlideIn : function(){
51147         this.clearAutoHide();
51148         this.isSlid = false;
51149         this.clearMonitor();
51150         this.el.setStyle("z-index", "");
51151         if(this.collapseBtn){
51152             this.collapseBtn.show();
51153         }
51154         this.closeBtn.setStyle('display', this.closeBtnState);
51155         if(this.stickBtn){
51156             this.stickBtn.hide();
51157         }
51158         this.fireEvent("slidehide", this);
51159     },
51160
51161     slideIn : function(cb){
51162         if(!this.isSlid || this.el.hasActiveFx()){
51163             Roo.callback(cb);
51164             return;
51165         }
51166         this.isSlid = false;
51167         this.beforeSlide();
51168         this.el.slideOut(this.getSlideAnchor(), {
51169             callback: function(){
51170                 this.el.setLeftTop(-10000, -10000);
51171                 this.afterSlide();
51172                 this.afterSlideIn();
51173                 Roo.callback(cb);
51174             },
51175             scope: this,
51176             block: true
51177         });
51178     },
51179     
51180     slideInIf : function(e){
51181         if(!e.within(this.el)){
51182             this.slideIn();
51183         }
51184     },
51185
51186     animateCollapse : function(){
51187         this.beforeSlide();
51188         this.el.setStyle("z-index", 20000);
51189         var anchor = this.getSlideAnchor();
51190         this.el.slideOut(anchor, {
51191             callback : function(){
51192                 this.el.setStyle("z-index", "");
51193                 this.collapsedEl.slideIn(anchor, {duration:.3});
51194                 this.afterSlide();
51195                 this.el.setLocation(-10000,-10000);
51196                 this.el.hide();
51197                 this.fireEvent("collapsed", this);
51198             },
51199             scope: this,
51200             block: true
51201         });
51202     },
51203
51204     animateExpand : function(){
51205         this.beforeSlide();
51206         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
51207         this.el.setStyle("z-index", 20000);
51208         this.collapsedEl.hide({
51209             duration:.1
51210         });
51211         this.el.slideIn(this.getSlideAnchor(), {
51212             callback : function(){
51213                 this.el.setStyle("z-index", "");
51214                 this.afterSlide();
51215                 if(this.split){
51216                     this.split.el.show();
51217                 }
51218                 this.fireEvent("invalidated", this);
51219                 this.fireEvent("expanded", this);
51220             },
51221             scope: this,
51222             block: true
51223         });
51224     },
51225
51226     anchors : {
51227         "west" : "left",
51228         "east" : "right",
51229         "north" : "top",
51230         "south" : "bottom"
51231     },
51232
51233     sanchors : {
51234         "west" : "l",
51235         "east" : "r",
51236         "north" : "t",
51237         "south" : "b"
51238     },
51239
51240     canchors : {
51241         "west" : "tl-tr",
51242         "east" : "tr-tl",
51243         "north" : "tl-bl",
51244         "south" : "bl-tl"
51245     },
51246
51247     getAnchor : function(){
51248         return this.anchors[this.position];
51249     },
51250
51251     getCollapseAnchor : function(){
51252         return this.canchors[this.position];
51253     },
51254
51255     getSlideAnchor : function(){
51256         return this.sanchors[this.position];
51257     },
51258
51259     getAlignAdj : function(){
51260         var cm = this.cmargins;
51261         switch(this.position){
51262             case "west":
51263                 return [0, 0];
51264             break;
51265             case "east":
51266                 return [0, 0];
51267             break;
51268             case "north":
51269                 return [0, 0];
51270             break;
51271             case "south":
51272                 return [0, 0];
51273             break;
51274         }
51275     },
51276
51277     getExpandAdj : function(){
51278         var c = this.collapsedEl, cm = this.cmargins;
51279         switch(this.position){
51280             case "west":
51281                 return [-(cm.right+c.getWidth()+cm.left), 0];
51282             break;
51283             case "east":
51284                 return [cm.right+c.getWidth()+cm.left, 0];
51285             break;
51286             case "north":
51287                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51288             break;
51289             case "south":
51290                 return [0, cm.top+cm.bottom+c.getHeight()];
51291             break;
51292         }
51293     }
51294 });/*
51295  * Based on:
51296  * Ext JS Library 1.1.1
51297  * Copyright(c) 2006-2007, Ext JS, LLC.
51298  *
51299  * Originally Released Under LGPL - original licence link has changed is not relivant.
51300  *
51301  * Fork - LGPL
51302  * <script type="text/javascript">
51303  */
51304 /*
51305  * These classes are private internal classes
51306  */
51307 Roo.CenterLayoutRegion = function(mgr, config){
51308     Roo.LayoutRegion.call(this, mgr, config, "center");
51309     this.visible = true;
51310     this.minWidth = config.minWidth || 20;
51311     this.minHeight = config.minHeight || 20;
51312 };
51313
51314 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51315     hide : function(){
51316         // center panel can't be hidden
51317     },
51318     
51319     show : function(){
51320         // center panel can't be hidden
51321     },
51322     
51323     getMinWidth: function(){
51324         return this.minWidth;
51325     },
51326     
51327     getMinHeight: function(){
51328         return this.minHeight;
51329     }
51330 });
51331
51332
51333 Roo.NorthLayoutRegion = function(mgr, config){
51334     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51335     if(this.split){
51336         this.split.placement = Roo.SplitBar.TOP;
51337         this.split.orientation = Roo.SplitBar.VERTICAL;
51338         this.split.el.addClass("x-layout-split-v");
51339     }
51340     var size = config.initialSize || config.height;
51341     if(typeof size != "undefined"){
51342         this.el.setHeight(size);
51343     }
51344 };
51345 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51346     orientation: Roo.SplitBar.VERTICAL,
51347     getBox : function(){
51348         if(this.collapsed){
51349             return this.collapsedEl.getBox();
51350         }
51351         var box = this.el.getBox();
51352         if(this.split){
51353             box.height += this.split.el.getHeight();
51354         }
51355         return box;
51356     },
51357     
51358     updateBox : function(box){
51359         if(this.split && !this.collapsed){
51360             box.height -= this.split.el.getHeight();
51361             this.split.el.setLeft(box.x);
51362             this.split.el.setTop(box.y+box.height);
51363             this.split.el.setWidth(box.width);
51364         }
51365         if(this.collapsed){
51366             this.updateBody(box.width, null);
51367         }
51368         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51369     }
51370 });
51371
51372 Roo.SouthLayoutRegion = function(mgr, config){
51373     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51374     if(this.split){
51375         this.split.placement = Roo.SplitBar.BOTTOM;
51376         this.split.orientation = Roo.SplitBar.VERTICAL;
51377         this.split.el.addClass("x-layout-split-v");
51378     }
51379     var size = config.initialSize || config.height;
51380     if(typeof size != "undefined"){
51381         this.el.setHeight(size);
51382     }
51383 };
51384 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51385     orientation: Roo.SplitBar.VERTICAL,
51386     getBox : function(){
51387         if(this.collapsed){
51388             return this.collapsedEl.getBox();
51389         }
51390         var box = this.el.getBox();
51391         if(this.split){
51392             var sh = this.split.el.getHeight();
51393             box.height += sh;
51394             box.y -= sh;
51395         }
51396         return box;
51397     },
51398     
51399     updateBox : function(box){
51400         if(this.split && !this.collapsed){
51401             var sh = this.split.el.getHeight();
51402             box.height -= sh;
51403             box.y += sh;
51404             this.split.el.setLeft(box.x);
51405             this.split.el.setTop(box.y-sh);
51406             this.split.el.setWidth(box.width);
51407         }
51408         if(this.collapsed){
51409             this.updateBody(box.width, null);
51410         }
51411         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51412     }
51413 });
51414
51415 Roo.EastLayoutRegion = function(mgr, config){
51416     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51417     if(this.split){
51418         this.split.placement = Roo.SplitBar.RIGHT;
51419         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51420         this.split.el.addClass("x-layout-split-h");
51421     }
51422     var size = config.initialSize || config.width;
51423     if(typeof size != "undefined"){
51424         this.el.setWidth(size);
51425     }
51426 };
51427 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51428     orientation: Roo.SplitBar.HORIZONTAL,
51429     getBox : function(){
51430         if(this.collapsed){
51431             return this.collapsedEl.getBox();
51432         }
51433         var box = this.el.getBox();
51434         if(this.split){
51435             var sw = this.split.el.getWidth();
51436             box.width += sw;
51437             box.x -= sw;
51438         }
51439         return box;
51440     },
51441
51442     updateBox : function(box){
51443         if(this.split && !this.collapsed){
51444             var sw = this.split.el.getWidth();
51445             box.width -= sw;
51446             this.split.el.setLeft(box.x);
51447             this.split.el.setTop(box.y);
51448             this.split.el.setHeight(box.height);
51449             box.x += sw;
51450         }
51451         if(this.collapsed){
51452             this.updateBody(null, box.height);
51453         }
51454         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51455     }
51456 });
51457
51458 Roo.WestLayoutRegion = function(mgr, config){
51459     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51460     if(this.split){
51461         this.split.placement = Roo.SplitBar.LEFT;
51462         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51463         this.split.el.addClass("x-layout-split-h");
51464     }
51465     var size = config.initialSize || config.width;
51466     if(typeof size != "undefined"){
51467         this.el.setWidth(size);
51468     }
51469 };
51470 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51471     orientation: Roo.SplitBar.HORIZONTAL,
51472     getBox : function(){
51473         if(this.collapsed){
51474             return this.collapsedEl.getBox();
51475         }
51476         var box = this.el.getBox();
51477         if(this.split){
51478             box.width += this.split.el.getWidth();
51479         }
51480         return box;
51481     },
51482     
51483     updateBox : function(box){
51484         if(this.split && !this.collapsed){
51485             var sw = this.split.el.getWidth();
51486             box.width -= sw;
51487             this.split.el.setLeft(box.x+box.width);
51488             this.split.el.setTop(box.y);
51489             this.split.el.setHeight(box.height);
51490         }
51491         if(this.collapsed){
51492             this.updateBody(null, box.height);
51493         }
51494         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51495     }
51496 });
51497 /*
51498  * Based on:
51499  * Ext JS Library 1.1.1
51500  * Copyright(c) 2006-2007, Ext JS, LLC.
51501  *
51502  * Originally Released Under LGPL - original licence link has changed is not relivant.
51503  *
51504  * Fork - LGPL
51505  * <script type="text/javascript">
51506  */
51507  
51508  
51509 /*
51510  * Private internal class for reading and applying state
51511  */
51512 Roo.LayoutStateManager = function(layout){
51513      // default empty state
51514      this.state = {
51515         north: {},
51516         south: {},
51517         east: {},
51518         west: {}       
51519     };
51520 };
51521
51522 Roo.LayoutStateManager.prototype = {
51523     init : function(layout, provider){
51524         this.provider = provider;
51525         var state = provider.get(layout.id+"-layout-state");
51526         if(state){
51527             var wasUpdating = layout.isUpdating();
51528             if(!wasUpdating){
51529                 layout.beginUpdate();
51530             }
51531             for(var key in state){
51532                 if(typeof state[key] != "function"){
51533                     var rstate = state[key];
51534                     var r = layout.getRegion(key);
51535                     if(r && rstate){
51536                         if(rstate.size){
51537                             r.resizeTo(rstate.size);
51538                         }
51539                         if(rstate.collapsed == true){
51540                             r.collapse(true);
51541                         }else{
51542                             r.expand(null, true);
51543                         }
51544                     }
51545                 }
51546             }
51547             if(!wasUpdating){
51548                 layout.endUpdate();
51549             }
51550             this.state = state; 
51551         }
51552         this.layout = layout;
51553         layout.on("regionresized", this.onRegionResized, this);
51554         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51555         layout.on("regionexpanded", this.onRegionExpanded, this);
51556     },
51557     
51558     storeState : function(){
51559         this.provider.set(this.layout.id+"-layout-state", this.state);
51560     },
51561     
51562     onRegionResized : function(region, newSize){
51563         this.state[region.getPosition()].size = newSize;
51564         this.storeState();
51565     },
51566     
51567     onRegionCollapsed : function(region){
51568         this.state[region.getPosition()].collapsed = true;
51569         this.storeState();
51570     },
51571     
51572     onRegionExpanded : function(region){
51573         this.state[region.getPosition()].collapsed = false;
51574         this.storeState();
51575     }
51576 };/*
51577  * Based on:
51578  * Ext JS Library 1.1.1
51579  * Copyright(c) 2006-2007, Ext JS, LLC.
51580  *
51581  * Originally Released Under LGPL - original licence link has changed is not relivant.
51582  *
51583  * Fork - LGPL
51584  * <script type="text/javascript">
51585  */
51586 /**
51587  * @class Roo.ContentPanel
51588  * @extends Roo.util.Observable
51589  * A basic ContentPanel element.
51590  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51591  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51592  * @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
51593  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51594  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51595  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51596  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51597  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51598  * @cfg {String} title          The title for this panel
51599  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51600  * @cfg {String} url            Calls {@link #setUrl} with this value
51601  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51602  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51603  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51604  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51605
51606  * @constructor
51607  * Create a new ContentPanel.
51608  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51609  * @param {String/Object} config A string to set only the title or a config object
51610  * @param {String} content (optional) Set the HTML content for this panel
51611  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51612  */
51613 Roo.ContentPanel = function(el, config, content){
51614     
51615      
51616     /*
51617     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51618         config = el;
51619         el = Roo.id();
51620     }
51621     if (config && config.parentLayout) { 
51622         el = config.parentLayout.el.createChild(); 
51623     }
51624     */
51625     if(el.autoCreate){ // xtype is available if this is called from factory
51626         config = el;
51627         el = Roo.id();
51628     }
51629     this.el = Roo.get(el);
51630     if(!this.el && config && config.autoCreate){
51631         if(typeof config.autoCreate == "object"){
51632             if(!config.autoCreate.id){
51633                 config.autoCreate.id = config.id||el;
51634             }
51635             this.el = Roo.DomHelper.append(document.body,
51636                         config.autoCreate, true);
51637         }else{
51638             this.el = Roo.DomHelper.append(document.body,
51639                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51640         }
51641     }
51642     this.closable = false;
51643     this.loaded = false;
51644     this.active = false;
51645     if(typeof config == "string"){
51646         this.title = config;
51647     }else{
51648         Roo.apply(this, config);
51649     }
51650     
51651     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51652         this.wrapEl = this.el.wrap();
51653         this.toolbar.container = this.el.insertSibling(false, 'before');
51654         this.toolbar = new Roo.Toolbar(this.toolbar);
51655     }
51656     
51657     // xtype created footer. - not sure if will work as we normally have to render first..
51658     if (this.footer && !this.footer.el && this.footer.xtype) {
51659         if (!this.wrapEl) {
51660             this.wrapEl = this.el.wrap();
51661         }
51662     
51663         this.footer.container = this.wrapEl.createChild();
51664          
51665         this.footer = Roo.factory(this.footer, Roo);
51666         
51667     }
51668     
51669     if(this.resizeEl){
51670         this.resizeEl = Roo.get(this.resizeEl, true);
51671     }else{
51672         this.resizeEl = this.el;
51673     }
51674     // handle view.xtype
51675     
51676  
51677     
51678     
51679     this.addEvents({
51680         /**
51681          * @event activate
51682          * Fires when this panel is activated. 
51683          * @param {Roo.ContentPanel} this
51684          */
51685         "activate" : true,
51686         /**
51687          * @event deactivate
51688          * Fires when this panel is activated. 
51689          * @param {Roo.ContentPanel} this
51690          */
51691         "deactivate" : true,
51692
51693         /**
51694          * @event resize
51695          * Fires when this panel is resized if fitToFrame is true.
51696          * @param {Roo.ContentPanel} this
51697          * @param {Number} width The width after any component adjustments
51698          * @param {Number} height The height after any component adjustments
51699          */
51700         "resize" : true,
51701         
51702          /**
51703          * @event render
51704          * Fires when this tab is created
51705          * @param {Roo.ContentPanel} this
51706          */
51707         "render" : true
51708         
51709         
51710         
51711     });
51712     
51713
51714     
51715     
51716     if(this.autoScroll){
51717         this.resizeEl.setStyle("overflow", "auto");
51718     } else {
51719         // fix randome scrolling
51720         this.el.on('scroll', function() {
51721             Roo.log('fix random scolling');
51722             this.scrollTo('top',0); 
51723         });
51724     }
51725     content = content || this.content;
51726     if(content){
51727         this.setContent(content);
51728     }
51729     if(config && config.url){
51730         this.setUrl(this.url, this.params, this.loadOnce);
51731     }
51732     
51733     
51734     
51735     Roo.ContentPanel.superclass.constructor.call(this);
51736     
51737     if (this.view && typeof(this.view.xtype) != 'undefined') {
51738         this.view.el = this.el.appendChild(document.createElement("div"));
51739         this.view = Roo.factory(this.view); 
51740         this.view.render  &&  this.view.render(false, '');  
51741     }
51742     
51743     
51744     this.fireEvent('render', this);
51745 };
51746
51747 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51748     tabTip:'',
51749     setRegion : function(region){
51750         this.region = region;
51751         if(region){
51752            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51753         }else{
51754            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51755         } 
51756     },
51757     
51758     /**
51759      * Returns the toolbar for this Panel if one was configured. 
51760      * @return {Roo.Toolbar} 
51761      */
51762     getToolbar : function(){
51763         return this.toolbar;
51764     },
51765     
51766     setActiveState : function(active){
51767         this.active = active;
51768         if(!active){
51769             this.fireEvent("deactivate", this);
51770         }else{
51771             this.fireEvent("activate", this);
51772         }
51773     },
51774     /**
51775      * Updates this panel's element
51776      * @param {String} content The new content
51777      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51778     */
51779     setContent : function(content, loadScripts){
51780         this.el.update(content, loadScripts);
51781     },
51782
51783     ignoreResize : function(w, h){
51784         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51785             return true;
51786         }else{
51787             this.lastSize = {width: w, height: h};
51788             return false;
51789         }
51790     },
51791     /**
51792      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51793      * @return {Roo.UpdateManager} The UpdateManager
51794      */
51795     getUpdateManager : function(){
51796         return this.el.getUpdateManager();
51797     },
51798      /**
51799      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51800      * @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:
51801 <pre><code>
51802 panel.load({
51803     url: "your-url.php",
51804     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51805     callback: yourFunction,
51806     scope: yourObject, //(optional scope)
51807     discardUrl: false,
51808     nocache: false,
51809     text: "Loading...",
51810     timeout: 30,
51811     scripts: false
51812 });
51813 </code></pre>
51814      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51815      * 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.
51816      * @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}
51817      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51818      * @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.
51819      * @return {Roo.ContentPanel} this
51820      */
51821     load : function(){
51822         var um = this.el.getUpdateManager();
51823         um.update.apply(um, arguments);
51824         return this;
51825     },
51826
51827
51828     /**
51829      * 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.
51830      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51831      * @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)
51832      * @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)
51833      * @return {Roo.UpdateManager} The UpdateManager
51834      */
51835     setUrl : function(url, params, loadOnce){
51836         if(this.refreshDelegate){
51837             this.removeListener("activate", this.refreshDelegate);
51838         }
51839         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51840         this.on("activate", this.refreshDelegate);
51841         return this.el.getUpdateManager();
51842     },
51843     
51844     _handleRefresh : function(url, params, loadOnce){
51845         if(!loadOnce || !this.loaded){
51846             var updater = this.el.getUpdateManager();
51847             updater.update(url, params, this._setLoaded.createDelegate(this));
51848         }
51849     },
51850     
51851     _setLoaded : function(){
51852         this.loaded = true;
51853     }, 
51854     
51855     /**
51856      * Returns this panel's id
51857      * @return {String} 
51858      */
51859     getId : function(){
51860         return this.el.id;
51861     },
51862     
51863     /** 
51864      * Returns this panel's element - used by regiosn to add.
51865      * @return {Roo.Element} 
51866      */
51867     getEl : function(){
51868         return this.wrapEl || this.el;
51869     },
51870     
51871     adjustForComponents : function(width, height)
51872     {
51873         //Roo.log('adjustForComponents ');
51874         if(this.resizeEl != this.el){
51875             width -= this.el.getFrameWidth('lr');
51876             height -= this.el.getFrameWidth('tb');
51877         }
51878         if(this.toolbar){
51879             var te = this.toolbar.getEl();
51880             height -= te.getHeight();
51881             te.setWidth(width);
51882         }
51883         if(this.footer){
51884             var te = this.footer.getEl();
51885             Roo.log("footer:" + te.getHeight());
51886             
51887             height -= te.getHeight();
51888             te.setWidth(width);
51889         }
51890         
51891         
51892         if(this.adjustments){
51893             width += this.adjustments[0];
51894             height += this.adjustments[1];
51895         }
51896         return {"width": width, "height": height};
51897     },
51898     
51899     setSize : function(width, height){
51900         if(this.fitToFrame && !this.ignoreResize(width, height)){
51901             if(this.fitContainer && this.resizeEl != this.el){
51902                 this.el.setSize(width, height);
51903             }
51904             var size = this.adjustForComponents(width, height);
51905             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51906             this.fireEvent('resize', this, size.width, size.height);
51907         }
51908     },
51909     
51910     /**
51911      * Returns this panel's title
51912      * @return {String} 
51913      */
51914     getTitle : function(){
51915         return this.title;
51916     },
51917     
51918     /**
51919      * Set this panel's title
51920      * @param {String} title
51921      */
51922     setTitle : function(title){
51923         this.title = title;
51924         if(this.region){
51925             this.region.updatePanelTitle(this, title);
51926         }
51927     },
51928     
51929     /**
51930      * Returns true is this panel was configured to be closable
51931      * @return {Boolean} 
51932      */
51933     isClosable : function(){
51934         return this.closable;
51935     },
51936     
51937     beforeSlide : function(){
51938         this.el.clip();
51939         this.resizeEl.clip();
51940     },
51941     
51942     afterSlide : function(){
51943         this.el.unclip();
51944         this.resizeEl.unclip();
51945     },
51946     
51947     /**
51948      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51949      *   Will fail silently if the {@link #setUrl} method has not been called.
51950      *   This does not activate the panel, just updates its content.
51951      */
51952     refresh : function(){
51953         if(this.refreshDelegate){
51954            this.loaded = false;
51955            this.refreshDelegate();
51956         }
51957     },
51958     
51959     /**
51960      * Destroys this panel
51961      */
51962     destroy : function(){
51963         this.el.removeAllListeners();
51964         var tempEl = document.createElement("span");
51965         tempEl.appendChild(this.el.dom);
51966         tempEl.innerHTML = "";
51967         this.el.remove();
51968         this.el = null;
51969     },
51970     
51971     /**
51972      * form - if the content panel contains a form - this is a reference to it.
51973      * @type {Roo.form.Form}
51974      */
51975     form : false,
51976     /**
51977      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51978      *    This contains a reference to it.
51979      * @type {Roo.View}
51980      */
51981     view : false,
51982     
51983       /**
51984      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51985      * <pre><code>
51986
51987 layout.addxtype({
51988        xtype : 'Form',
51989        items: [ .... ]
51990    }
51991 );
51992
51993 </code></pre>
51994      * @param {Object} cfg Xtype definition of item to add.
51995      */
51996     
51997     addxtype : function(cfg) {
51998         // add form..
51999         if (cfg.xtype.match(/^Form$/)) {
52000             
52001             var el;
52002             //if (this.footer) {
52003             //    el = this.footer.container.insertSibling(false, 'before');
52004             //} else {
52005                 el = this.el.createChild();
52006             //}
52007
52008             this.form = new  Roo.form.Form(cfg);
52009             
52010             
52011             if ( this.form.allItems.length) {
52012                 this.form.render(el.dom);
52013             }
52014             return this.form;
52015         }
52016         // should only have one of theses..
52017         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
52018             // views.. should not be just added - used named prop 'view''
52019             
52020             cfg.el = this.el.appendChild(document.createElement("div"));
52021             // factory?
52022             
52023             var ret = new Roo.factory(cfg);
52024              
52025              ret.render && ret.render(false, ''); // render blank..
52026             this.view = ret;
52027             return ret;
52028         }
52029         return false;
52030     }
52031 });
52032
52033 /**
52034  * @class Roo.GridPanel
52035  * @extends Roo.ContentPanel
52036  * @constructor
52037  * Create a new GridPanel.
52038  * @param {Roo.grid.Grid} grid The grid for this panel
52039  * @param {String/Object} config A string to set only the panel's title, or a config object
52040  */
52041 Roo.GridPanel = function(grid, config){
52042     
52043   
52044     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
52045         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
52046         
52047     this.wrapper.dom.appendChild(grid.getGridEl().dom);
52048     
52049     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
52050     
52051     if(this.toolbar){
52052         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
52053     }
52054     // xtype created footer. - not sure if will work as we normally have to render first..
52055     if (this.footer && !this.footer.el && this.footer.xtype) {
52056         
52057         this.footer.container = this.grid.getView().getFooterPanel(true);
52058         this.footer.dataSource = this.grid.dataSource;
52059         this.footer = Roo.factory(this.footer, Roo);
52060         
52061     }
52062     
52063     grid.monitorWindowResize = false; // turn off autosizing
52064     grid.autoHeight = false;
52065     grid.autoWidth = false;
52066     this.grid = grid;
52067     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
52068 };
52069
52070 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
52071     getId : function(){
52072         return this.grid.id;
52073     },
52074     
52075     /**
52076      * Returns the grid for this panel
52077      * @return {Roo.grid.Grid} 
52078      */
52079     getGrid : function(){
52080         return this.grid;    
52081     },
52082     
52083     setSize : function(width, height){
52084         if(!this.ignoreResize(width, height)){
52085             var grid = this.grid;
52086             var size = this.adjustForComponents(width, height);
52087             grid.getGridEl().setSize(size.width, size.height);
52088             grid.autoSize();
52089         }
52090     },
52091     
52092     beforeSlide : function(){
52093         this.grid.getView().scroller.clip();
52094     },
52095     
52096     afterSlide : function(){
52097         this.grid.getView().scroller.unclip();
52098     },
52099     
52100     destroy : function(){
52101         this.grid.destroy();
52102         delete this.grid;
52103         Roo.GridPanel.superclass.destroy.call(this); 
52104     }
52105 });
52106
52107
52108 /**
52109  * @class Roo.NestedLayoutPanel
52110  * @extends Roo.ContentPanel
52111  * @constructor
52112  * Create a new NestedLayoutPanel.
52113  * 
52114  * 
52115  * @param {Roo.BorderLayout} layout The layout for this panel
52116  * @param {String/Object} config A string to set only the title or a config object
52117  */
52118 Roo.NestedLayoutPanel = function(layout, config)
52119 {
52120     // construct with only one argument..
52121     /* FIXME - implement nicer consturctors
52122     if (layout.layout) {
52123         config = layout;
52124         layout = config.layout;
52125         delete config.layout;
52126     }
52127     if (layout.xtype && !layout.getEl) {
52128         // then layout needs constructing..
52129         layout = Roo.factory(layout, Roo);
52130     }
52131     */
52132     
52133     
52134     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
52135     
52136     layout.monitorWindowResize = false; // turn off autosizing
52137     this.layout = layout;
52138     this.layout.getEl().addClass("x-layout-nested-layout");
52139     
52140     
52141     
52142     
52143 };
52144
52145 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
52146
52147     setSize : function(width, height){
52148         if(!this.ignoreResize(width, height)){
52149             var size = this.adjustForComponents(width, height);
52150             var el = this.layout.getEl();
52151             el.setSize(size.width, size.height);
52152             var touch = el.dom.offsetWidth;
52153             this.layout.layout();
52154             // ie requires a double layout on the first pass
52155             if(Roo.isIE && !this.initialized){
52156                 this.initialized = true;
52157                 this.layout.layout();
52158             }
52159         }
52160     },
52161     
52162     // activate all subpanels if not currently active..
52163     
52164     setActiveState : function(active){
52165         this.active = active;
52166         if(!active){
52167             this.fireEvent("deactivate", this);
52168             return;
52169         }
52170         
52171         this.fireEvent("activate", this);
52172         // not sure if this should happen before or after..
52173         if (!this.layout) {
52174             return; // should not happen..
52175         }
52176         var reg = false;
52177         for (var r in this.layout.regions) {
52178             reg = this.layout.getRegion(r);
52179             if (reg.getActivePanel()) {
52180                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
52181                 reg.setActivePanel(reg.getActivePanel());
52182                 continue;
52183             }
52184             if (!reg.panels.length) {
52185                 continue;
52186             }
52187             reg.showPanel(reg.getPanel(0));
52188         }
52189         
52190         
52191         
52192         
52193     },
52194     
52195     /**
52196      * Returns the nested BorderLayout for this panel
52197      * @return {Roo.BorderLayout} 
52198      */
52199     getLayout : function(){
52200         return this.layout;
52201     },
52202     
52203      /**
52204      * Adds a xtype elements to the layout of the nested panel
52205      * <pre><code>
52206
52207 panel.addxtype({
52208        xtype : 'ContentPanel',
52209        region: 'west',
52210        items: [ .... ]
52211    }
52212 );
52213
52214 panel.addxtype({
52215         xtype : 'NestedLayoutPanel',
52216         region: 'west',
52217         layout: {
52218            center: { },
52219            west: { }   
52220         },
52221         items : [ ... list of content panels or nested layout panels.. ]
52222    }
52223 );
52224 </code></pre>
52225      * @param {Object} cfg Xtype definition of item to add.
52226      */
52227     addxtype : function(cfg) {
52228         return this.layout.addxtype(cfg);
52229     
52230     }
52231 });
52232
52233 Roo.ScrollPanel = function(el, config, content){
52234     config = config || {};
52235     config.fitToFrame = true;
52236     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52237     
52238     this.el.dom.style.overflow = "hidden";
52239     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52240     this.el.removeClass("x-layout-inactive-content");
52241     this.el.on("mousewheel", this.onWheel, this);
52242
52243     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52244     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52245     up.unselectable(); down.unselectable();
52246     up.on("click", this.scrollUp, this);
52247     down.on("click", this.scrollDown, this);
52248     up.addClassOnOver("x-scroller-btn-over");
52249     down.addClassOnOver("x-scroller-btn-over");
52250     up.addClassOnClick("x-scroller-btn-click");
52251     down.addClassOnClick("x-scroller-btn-click");
52252     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52253
52254     this.resizeEl = this.el;
52255     this.el = wrap; this.up = up; this.down = down;
52256 };
52257
52258 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52259     increment : 100,
52260     wheelIncrement : 5,
52261     scrollUp : function(){
52262         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52263     },
52264
52265     scrollDown : function(){
52266         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52267     },
52268
52269     afterScroll : function(){
52270         var el = this.resizeEl;
52271         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52272         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52273         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52274     },
52275
52276     setSize : function(){
52277         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52278         this.afterScroll();
52279     },
52280
52281     onWheel : function(e){
52282         var d = e.getWheelDelta();
52283         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52284         this.afterScroll();
52285         e.stopEvent();
52286     },
52287
52288     setContent : function(content, loadScripts){
52289         this.resizeEl.update(content, loadScripts);
52290     }
52291
52292 });
52293
52294
52295
52296
52297
52298
52299
52300
52301
52302 /**
52303  * @class Roo.TreePanel
52304  * @extends Roo.ContentPanel
52305  * @constructor
52306  * Create a new TreePanel. - defaults to fit/scoll contents.
52307  * @param {String/Object} config A string to set only the panel's title, or a config object
52308  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52309  */
52310 Roo.TreePanel = function(config){
52311     var el = config.el;
52312     var tree = config.tree;
52313     delete config.tree; 
52314     delete config.el; // hopefull!
52315     
52316     // wrapper for IE7 strict & safari scroll issue
52317     
52318     var treeEl = el.createChild();
52319     config.resizeEl = treeEl;
52320     
52321     
52322     
52323     Roo.TreePanel.superclass.constructor.call(this, el, config);
52324  
52325  
52326     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52327     //console.log(tree);
52328     this.on('activate', function()
52329     {
52330         if (this.tree.rendered) {
52331             return;
52332         }
52333         //console.log('render tree');
52334         this.tree.render();
52335     });
52336     // this should not be needed.. - it's actually the 'el' that resizes?
52337     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52338     
52339     //this.on('resize',  function (cp, w, h) {
52340     //        this.tree.innerCt.setWidth(w);
52341     //        this.tree.innerCt.setHeight(h);
52342     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52343     //});
52344
52345         
52346     
52347 };
52348
52349 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52350     fitToFrame : true,
52351     autoScroll : true
52352 });
52353
52354
52355
52356
52357
52358
52359
52360
52361
52362
52363
52364 /*
52365  * Based on:
52366  * Ext JS Library 1.1.1
52367  * Copyright(c) 2006-2007, Ext JS, LLC.
52368  *
52369  * Originally Released Under LGPL - original licence link has changed is not relivant.
52370  *
52371  * Fork - LGPL
52372  * <script type="text/javascript">
52373  */
52374  
52375
52376 /**
52377  * @class Roo.ReaderLayout
52378  * @extends Roo.BorderLayout
52379  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52380  * center region containing two nested regions (a top one for a list view and one for item preview below),
52381  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52382  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52383  * expedites the setup of the overall layout and regions for this common application style.
52384  * Example:
52385  <pre><code>
52386 var reader = new Roo.ReaderLayout();
52387 var CP = Roo.ContentPanel;  // shortcut for adding
52388
52389 reader.beginUpdate();
52390 reader.add("north", new CP("north", "North"));
52391 reader.add("west", new CP("west", {title: "West"}));
52392 reader.add("east", new CP("east", {title: "East"}));
52393
52394 reader.regions.listView.add(new CP("listView", "List"));
52395 reader.regions.preview.add(new CP("preview", "Preview"));
52396 reader.endUpdate();
52397 </code></pre>
52398 * @constructor
52399 * Create a new ReaderLayout
52400 * @param {Object} config Configuration options
52401 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52402 * document.body if omitted)
52403 */
52404 Roo.ReaderLayout = function(config, renderTo){
52405     var c = config || {size:{}};
52406     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52407         north: c.north !== false ? Roo.apply({
52408             split:false,
52409             initialSize: 32,
52410             titlebar: false
52411         }, c.north) : false,
52412         west: c.west !== false ? Roo.apply({
52413             split:true,
52414             initialSize: 200,
52415             minSize: 175,
52416             maxSize: 400,
52417             titlebar: true,
52418             collapsible: true,
52419             animate: true,
52420             margins:{left:5,right:0,bottom:5,top:5},
52421             cmargins:{left:5,right:5,bottom:5,top:5}
52422         }, c.west) : false,
52423         east: c.east !== false ? Roo.apply({
52424             split:true,
52425             initialSize: 200,
52426             minSize: 175,
52427             maxSize: 400,
52428             titlebar: true,
52429             collapsible: true,
52430             animate: true,
52431             margins:{left:0,right:5,bottom:5,top:5},
52432             cmargins:{left:5,right:5,bottom:5,top:5}
52433         }, c.east) : false,
52434         center: Roo.apply({
52435             tabPosition: 'top',
52436             autoScroll:false,
52437             closeOnTab: true,
52438             titlebar:false,
52439             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52440         }, c.center)
52441     });
52442
52443     this.el.addClass('x-reader');
52444
52445     this.beginUpdate();
52446
52447     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52448         south: c.preview !== false ? Roo.apply({
52449             split:true,
52450             initialSize: 200,
52451             minSize: 100,
52452             autoScroll:true,
52453             collapsible:true,
52454             titlebar: true,
52455             cmargins:{top:5,left:0, right:0, bottom:0}
52456         }, c.preview) : false,
52457         center: Roo.apply({
52458             autoScroll:false,
52459             titlebar:false,
52460             minHeight:200
52461         }, c.listView)
52462     });
52463     this.add('center', new Roo.NestedLayoutPanel(inner,
52464             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52465
52466     this.endUpdate();
52467
52468     this.regions.preview = inner.getRegion('south');
52469     this.regions.listView = inner.getRegion('center');
52470 };
52471
52472 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52473  * Based on:
52474  * Ext JS Library 1.1.1
52475  * Copyright(c) 2006-2007, Ext JS, LLC.
52476  *
52477  * Originally Released Under LGPL - original licence link has changed is not relivant.
52478  *
52479  * Fork - LGPL
52480  * <script type="text/javascript">
52481  */
52482  
52483 /**
52484  * @class Roo.grid.Grid
52485  * @extends Roo.util.Observable
52486  * This class represents the primary interface of a component based grid control.
52487  * <br><br>Usage:<pre><code>
52488  var grid = new Roo.grid.Grid("my-container-id", {
52489      ds: myDataStore,
52490      cm: myColModel,
52491      selModel: mySelectionModel,
52492      autoSizeColumns: true,
52493      monitorWindowResize: false,
52494      trackMouseOver: true
52495  });
52496  // set any options
52497  grid.render();
52498  * </code></pre>
52499  * <b>Common Problems:</b><br/>
52500  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52501  * element will correct this<br/>
52502  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52503  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52504  * are unpredictable.<br/>
52505  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52506  * grid to calculate dimensions/offsets.<br/>
52507   * @constructor
52508  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52509  * The container MUST have some type of size defined for the grid to fill. The container will be
52510  * automatically set to position relative if it isn't already.
52511  * @param {Object} config A config object that sets properties on this grid.
52512  */
52513 Roo.grid.Grid = function(container, config){
52514         // initialize the container
52515         this.container = Roo.get(container);
52516         this.container.update("");
52517         this.container.setStyle("overflow", "hidden");
52518     this.container.addClass('x-grid-container');
52519
52520     this.id = this.container.id;
52521
52522     Roo.apply(this, config);
52523     // check and correct shorthanded configs
52524     if(this.ds){
52525         this.dataSource = this.ds;
52526         delete this.ds;
52527     }
52528     if(this.cm){
52529         this.colModel = this.cm;
52530         delete this.cm;
52531     }
52532     if(this.sm){
52533         this.selModel = this.sm;
52534         delete this.sm;
52535     }
52536
52537     if (this.selModel) {
52538         this.selModel = Roo.factory(this.selModel, Roo.grid);
52539         this.sm = this.selModel;
52540         this.sm.xmodule = this.xmodule || false;
52541     }
52542     if (typeof(this.colModel.config) == 'undefined') {
52543         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52544         this.cm = this.colModel;
52545         this.cm.xmodule = this.xmodule || false;
52546     }
52547     if (this.dataSource) {
52548         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52549         this.ds = this.dataSource;
52550         this.ds.xmodule = this.xmodule || false;
52551          
52552     }
52553     
52554     
52555     
52556     if(this.width){
52557         this.container.setWidth(this.width);
52558     }
52559
52560     if(this.height){
52561         this.container.setHeight(this.height);
52562     }
52563     /** @private */
52564         this.addEvents({
52565         // raw events
52566         /**
52567          * @event click
52568          * The raw click event for the entire grid.
52569          * @param {Roo.EventObject} e
52570          */
52571         "click" : true,
52572         /**
52573          * @event dblclick
52574          * The raw dblclick event for the entire grid.
52575          * @param {Roo.EventObject} e
52576          */
52577         "dblclick" : true,
52578         /**
52579          * @event contextmenu
52580          * The raw contextmenu event for the entire grid.
52581          * @param {Roo.EventObject} e
52582          */
52583         "contextmenu" : true,
52584         /**
52585          * @event mousedown
52586          * The raw mousedown event for the entire grid.
52587          * @param {Roo.EventObject} e
52588          */
52589         "mousedown" : true,
52590         /**
52591          * @event mouseup
52592          * The raw mouseup event for the entire grid.
52593          * @param {Roo.EventObject} e
52594          */
52595         "mouseup" : true,
52596         /**
52597          * @event mouseover
52598          * The raw mouseover event for the entire grid.
52599          * @param {Roo.EventObject} e
52600          */
52601         "mouseover" : true,
52602         /**
52603          * @event mouseout
52604          * The raw mouseout event for the entire grid.
52605          * @param {Roo.EventObject} e
52606          */
52607         "mouseout" : true,
52608         /**
52609          * @event keypress
52610          * The raw keypress event for the entire grid.
52611          * @param {Roo.EventObject} e
52612          */
52613         "keypress" : true,
52614         /**
52615          * @event keydown
52616          * The raw keydown event for the entire grid.
52617          * @param {Roo.EventObject} e
52618          */
52619         "keydown" : true,
52620
52621         // custom events
52622
52623         /**
52624          * @event cellclick
52625          * Fires when a cell is clicked
52626          * @param {Grid} this
52627          * @param {Number} rowIndex
52628          * @param {Number} columnIndex
52629          * @param {Roo.EventObject} e
52630          */
52631         "cellclick" : true,
52632         /**
52633          * @event celldblclick
52634          * Fires when a cell is double clicked
52635          * @param {Grid} this
52636          * @param {Number} rowIndex
52637          * @param {Number} columnIndex
52638          * @param {Roo.EventObject} e
52639          */
52640         "celldblclick" : true,
52641         /**
52642          * @event rowclick
52643          * Fires when a row is clicked
52644          * @param {Grid} this
52645          * @param {Number} rowIndex
52646          * @param {Roo.EventObject} e
52647          */
52648         "rowclick" : true,
52649         /**
52650          * @event rowdblclick
52651          * Fires when a row is double clicked
52652          * @param {Grid} this
52653          * @param {Number} rowIndex
52654          * @param {Roo.EventObject} e
52655          */
52656         "rowdblclick" : true,
52657         /**
52658          * @event headerclick
52659          * Fires when a header is clicked
52660          * @param {Grid} this
52661          * @param {Number} columnIndex
52662          * @param {Roo.EventObject} e
52663          */
52664         "headerclick" : true,
52665         /**
52666          * @event headerdblclick
52667          * Fires when a header cell is double clicked
52668          * @param {Grid} this
52669          * @param {Number} columnIndex
52670          * @param {Roo.EventObject} e
52671          */
52672         "headerdblclick" : true,
52673         /**
52674          * @event rowcontextmenu
52675          * Fires when a row is right clicked
52676          * @param {Grid} this
52677          * @param {Number} rowIndex
52678          * @param {Roo.EventObject} e
52679          */
52680         "rowcontextmenu" : true,
52681         /**
52682          * @event cellcontextmenu
52683          * Fires when a cell is right clicked
52684          * @param {Grid} this
52685          * @param {Number} rowIndex
52686          * @param {Number} cellIndex
52687          * @param {Roo.EventObject} e
52688          */
52689          "cellcontextmenu" : true,
52690         /**
52691          * @event headercontextmenu
52692          * Fires when a header is right clicked
52693          * @param {Grid} this
52694          * @param {Number} columnIndex
52695          * @param {Roo.EventObject} e
52696          */
52697         "headercontextmenu" : true,
52698         /**
52699          * @event bodyscroll
52700          * Fires when the body element is scrolled
52701          * @param {Number} scrollLeft
52702          * @param {Number} scrollTop
52703          */
52704         "bodyscroll" : true,
52705         /**
52706          * @event columnresize
52707          * Fires when the user resizes a column
52708          * @param {Number} columnIndex
52709          * @param {Number} newSize
52710          */
52711         "columnresize" : true,
52712         /**
52713          * @event columnmove
52714          * Fires when the user moves a column
52715          * @param {Number} oldIndex
52716          * @param {Number} newIndex
52717          */
52718         "columnmove" : true,
52719         /**
52720          * @event startdrag
52721          * Fires when row(s) start being dragged
52722          * @param {Grid} this
52723          * @param {Roo.GridDD} dd The drag drop object
52724          * @param {event} e The raw browser event
52725          */
52726         "startdrag" : true,
52727         /**
52728          * @event enddrag
52729          * Fires when a drag operation is complete
52730          * @param {Grid} this
52731          * @param {Roo.GridDD} dd The drag drop object
52732          * @param {event} e The raw browser event
52733          */
52734         "enddrag" : true,
52735         /**
52736          * @event dragdrop
52737          * Fires when dragged row(s) are dropped on a valid DD target
52738          * @param {Grid} this
52739          * @param {Roo.GridDD} dd The drag drop object
52740          * @param {String} targetId The target drag drop object
52741          * @param {event} e The raw browser event
52742          */
52743         "dragdrop" : true,
52744         /**
52745          * @event dragover
52746          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52747          * @param {Grid} this
52748          * @param {Roo.GridDD} dd The drag drop object
52749          * @param {String} targetId The target drag drop object
52750          * @param {event} e The raw browser event
52751          */
52752         "dragover" : true,
52753         /**
52754          * @event dragenter
52755          *  Fires when the dragged row(s) first cross another DD target while being dragged
52756          * @param {Grid} this
52757          * @param {Roo.GridDD} dd The drag drop object
52758          * @param {String} targetId The target drag drop object
52759          * @param {event} e The raw browser event
52760          */
52761         "dragenter" : true,
52762         /**
52763          * @event dragout
52764          * Fires when the dragged row(s) leave another DD target while being dragged
52765          * @param {Grid} this
52766          * @param {Roo.GridDD} dd The drag drop object
52767          * @param {String} targetId The target drag drop object
52768          * @param {event} e The raw browser event
52769          */
52770         "dragout" : true,
52771         /**
52772          * @event rowclass
52773          * Fires when a row is rendered, so you can change add a style to it.
52774          * @param {GridView} gridview   The grid view
52775          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52776          */
52777         'rowclass' : true,
52778
52779         /**
52780          * @event render
52781          * Fires when the grid is rendered
52782          * @param {Grid} grid
52783          */
52784         'render' : true
52785     });
52786
52787     Roo.grid.Grid.superclass.constructor.call(this);
52788 };
52789 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52790     
52791     /**
52792      * @cfg {String} ddGroup - drag drop group.
52793      */
52794
52795     /**
52796      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52797      */
52798     minColumnWidth : 25,
52799
52800     /**
52801      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52802      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52803      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52804      */
52805     autoSizeColumns : false,
52806
52807     /**
52808      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52809      */
52810     autoSizeHeaders : true,
52811
52812     /**
52813      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52814      */
52815     monitorWindowResize : true,
52816
52817     /**
52818      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52819      * rows measured to get a columns size. Default is 0 (all rows).
52820      */
52821     maxRowsToMeasure : 0,
52822
52823     /**
52824      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52825      */
52826     trackMouseOver : true,
52827
52828     /**
52829     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52830     */
52831     
52832     /**
52833     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52834     */
52835     enableDragDrop : false,
52836     
52837     /**
52838     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52839     */
52840     enableColumnMove : true,
52841     
52842     /**
52843     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52844     */
52845     enableColumnHide : true,
52846     
52847     /**
52848     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52849     */
52850     enableRowHeightSync : false,
52851     
52852     /**
52853     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52854     */
52855     stripeRows : true,
52856     
52857     /**
52858     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52859     */
52860     autoHeight : false,
52861
52862     /**
52863      * @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.
52864      */
52865     autoExpandColumn : false,
52866
52867     /**
52868     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52869     * Default is 50.
52870     */
52871     autoExpandMin : 50,
52872
52873     /**
52874     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52875     */
52876     autoExpandMax : 1000,
52877
52878     /**
52879     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52880     */
52881     view : null,
52882
52883     /**
52884     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52885     */
52886     loadMask : false,
52887     /**
52888     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52889     */
52890     dropTarget: false,
52891     
52892    
52893     
52894     // private
52895     rendered : false,
52896
52897     /**
52898     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52899     * of a fixed width. Default is false.
52900     */
52901     /**
52902     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52903     */
52904     /**
52905      * Called once after all setup has been completed and the grid is ready to be rendered.
52906      * @return {Roo.grid.Grid} this
52907      */
52908     render : function()
52909     {
52910         var c = this.container;
52911         // try to detect autoHeight/width mode
52912         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52913             this.autoHeight = true;
52914         }
52915         var view = this.getView();
52916         view.init(this);
52917
52918         c.on("click", this.onClick, this);
52919         c.on("dblclick", this.onDblClick, this);
52920         c.on("contextmenu", this.onContextMenu, this);
52921         c.on("keydown", this.onKeyDown, this);
52922         if (Roo.isTouch) {
52923             c.on("touchstart", this.onTouchStart, this);
52924         }
52925
52926         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52927
52928         this.getSelectionModel().init(this);
52929
52930         view.render();
52931
52932         if(this.loadMask){
52933             this.loadMask = new Roo.LoadMask(this.container,
52934                     Roo.apply({store:this.dataSource}, this.loadMask));
52935         }
52936         
52937         
52938         if (this.toolbar && this.toolbar.xtype) {
52939             this.toolbar.container = this.getView().getHeaderPanel(true);
52940             this.toolbar = new Roo.Toolbar(this.toolbar);
52941         }
52942         if (this.footer && this.footer.xtype) {
52943             this.footer.dataSource = this.getDataSource();
52944             this.footer.container = this.getView().getFooterPanel(true);
52945             this.footer = Roo.factory(this.footer, Roo);
52946         }
52947         if (this.dropTarget && this.dropTarget.xtype) {
52948             delete this.dropTarget.xtype;
52949             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52950         }
52951         
52952         
52953         this.rendered = true;
52954         this.fireEvent('render', this);
52955         return this;
52956     },
52957
52958         /**
52959          * Reconfigures the grid to use a different Store and Column Model.
52960          * The View will be bound to the new objects and refreshed.
52961          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52962          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52963          */
52964     reconfigure : function(dataSource, colModel){
52965         if(this.loadMask){
52966             this.loadMask.destroy();
52967             this.loadMask = new Roo.LoadMask(this.container,
52968                     Roo.apply({store:dataSource}, this.loadMask));
52969         }
52970         this.view.bind(dataSource, colModel);
52971         this.dataSource = dataSource;
52972         this.colModel = colModel;
52973         this.view.refresh(true);
52974     },
52975
52976     // private
52977     onKeyDown : function(e){
52978         this.fireEvent("keydown", e);
52979     },
52980
52981     /**
52982      * Destroy this grid.
52983      * @param {Boolean} removeEl True to remove the element
52984      */
52985     destroy : function(removeEl, keepListeners){
52986         if(this.loadMask){
52987             this.loadMask.destroy();
52988         }
52989         var c = this.container;
52990         c.removeAllListeners();
52991         this.view.destroy();
52992         this.colModel.purgeListeners();
52993         if(!keepListeners){
52994             this.purgeListeners();
52995         }
52996         c.update("");
52997         if(removeEl === true){
52998             c.remove();
52999         }
53000     },
53001
53002     // private
53003     processEvent : function(name, e){
53004         // does this fire select???
53005         //Roo.log('grid:processEvent '  + name);
53006         
53007         if (name != 'touchstart' ) {
53008             this.fireEvent(name, e);    
53009         }
53010         
53011         var t = e.getTarget();
53012         var v = this.view;
53013         var header = v.findHeaderIndex(t);
53014         if(header !== false){
53015             var ename = name == 'touchstart' ? 'click' : name;
53016              
53017             this.fireEvent("header" + ename, this, header, e);
53018         }else{
53019             var row = v.findRowIndex(t);
53020             var cell = v.findCellIndex(t);
53021             if (name == 'touchstart') {
53022                 // first touch is always a click.
53023                 // hopefull this happens after selection is updated.?
53024                 name = false;
53025                 
53026                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
53027                     var cs = this.selModel.getSelectedCell();
53028                     if (row == cs[0] && cell == cs[1]){
53029                         name = 'dblclick';
53030                     }
53031                 }
53032                 if (typeof(this.selModel.getSelections) != 'undefined') {
53033                     var cs = this.selModel.getSelections();
53034                     var ds = this.dataSource;
53035                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
53036                         name = 'dblclick';
53037                     }
53038                 }
53039                 if (!name) {
53040                     return;
53041                 }
53042             }
53043             
53044             
53045             if(row !== false){
53046                 this.fireEvent("row" + name, this, row, e);
53047                 if(cell !== false){
53048                     this.fireEvent("cell" + name, this, row, cell, e);
53049                 }
53050             }
53051         }
53052     },
53053
53054     // private
53055     onClick : function(e){
53056         this.processEvent("click", e);
53057     },
53058    // private
53059     onTouchStart : function(e){
53060         this.processEvent("touchstart", e);
53061     },
53062
53063     // private
53064     onContextMenu : function(e, t){
53065         this.processEvent("contextmenu", e);
53066     },
53067
53068     // private
53069     onDblClick : function(e){
53070         this.processEvent("dblclick", e);
53071     },
53072
53073     // private
53074     walkCells : function(row, col, step, fn, scope){
53075         var cm = this.colModel, clen = cm.getColumnCount();
53076         var ds = this.dataSource, rlen = ds.getCount(), first = true;
53077         if(step < 0){
53078             if(col < 0){
53079                 row--;
53080                 first = false;
53081             }
53082             while(row >= 0){
53083                 if(!first){
53084                     col = clen-1;
53085                 }
53086                 first = false;
53087                 while(col >= 0){
53088                     if(fn.call(scope || this, row, col, cm) === true){
53089                         return [row, col];
53090                     }
53091                     col--;
53092                 }
53093                 row--;
53094             }
53095         } else {
53096             if(col >= clen){
53097                 row++;
53098                 first = false;
53099             }
53100             while(row < rlen){
53101                 if(!first){
53102                     col = 0;
53103                 }
53104                 first = false;
53105                 while(col < clen){
53106                     if(fn.call(scope || this, row, col, cm) === true){
53107                         return [row, col];
53108                     }
53109                     col++;
53110                 }
53111                 row++;
53112             }
53113         }
53114         return null;
53115     },
53116
53117     // private
53118     getSelections : function(){
53119         return this.selModel.getSelections();
53120     },
53121
53122     /**
53123      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
53124      * but if manual update is required this method will initiate it.
53125      */
53126     autoSize : function(){
53127         if(this.rendered){
53128             this.view.layout();
53129             if(this.view.adjustForScroll){
53130                 this.view.adjustForScroll();
53131             }
53132         }
53133     },
53134
53135     /**
53136      * Returns the grid's underlying element.
53137      * @return {Element} The element
53138      */
53139     getGridEl : function(){
53140         return this.container;
53141     },
53142
53143     // private for compatibility, overridden by editor grid
53144     stopEditing : function(){},
53145
53146     /**
53147      * Returns the grid's SelectionModel.
53148      * @return {SelectionModel}
53149      */
53150     getSelectionModel : function(){
53151         if(!this.selModel){
53152             this.selModel = new Roo.grid.RowSelectionModel();
53153         }
53154         return this.selModel;
53155     },
53156
53157     /**
53158      * Returns the grid's DataSource.
53159      * @return {DataSource}
53160      */
53161     getDataSource : function(){
53162         return this.dataSource;
53163     },
53164
53165     /**
53166      * Returns the grid's ColumnModel.
53167      * @return {ColumnModel}
53168      */
53169     getColumnModel : function(){
53170         return this.colModel;
53171     },
53172
53173     /**
53174      * Returns the grid's GridView object.
53175      * @return {GridView}
53176      */
53177     getView : function(){
53178         if(!this.view){
53179             this.view = new Roo.grid.GridView(this.viewConfig);
53180         }
53181         return this.view;
53182     },
53183     /**
53184      * Called to get grid's drag proxy text, by default returns this.ddText.
53185      * @return {String}
53186      */
53187     getDragDropText : function(){
53188         var count = this.selModel.getCount();
53189         return String.format(this.ddText, count, count == 1 ? '' : 's');
53190     }
53191 });
53192 /**
53193  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
53194  * %0 is replaced with the number of selected rows.
53195  * @type String
53196  */
53197 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
53198  * Based on:
53199  * Ext JS Library 1.1.1
53200  * Copyright(c) 2006-2007, Ext JS, LLC.
53201  *
53202  * Originally Released Under LGPL - original licence link has changed is not relivant.
53203  *
53204  * Fork - LGPL
53205  * <script type="text/javascript">
53206  */
53207  
53208 Roo.grid.AbstractGridView = function(){
53209         this.grid = null;
53210         
53211         this.events = {
53212             "beforerowremoved" : true,
53213             "beforerowsinserted" : true,
53214             "beforerefresh" : true,
53215             "rowremoved" : true,
53216             "rowsinserted" : true,
53217             "rowupdated" : true,
53218             "refresh" : true
53219         };
53220     Roo.grid.AbstractGridView.superclass.constructor.call(this);
53221 };
53222
53223 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
53224     rowClass : "x-grid-row",
53225     cellClass : "x-grid-cell",
53226     tdClass : "x-grid-td",
53227     hdClass : "x-grid-hd",
53228     splitClass : "x-grid-hd-split",
53229     
53230     init: function(grid){
53231         this.grid = grid;
53232                 var cid = this.grid.getGridEl().id;
53233         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53234         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53235         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53236         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53237         },
53238         
53239     getColumnRenderers : function(){
53240         var renderers = [];
53241         var cm = this.grid.colModel;
53242         var colCount = cm.getColumnCount();
53243         for(var i = 0; i < colCount; i++){
53244             renderers[i] = cm.getRenderer(i);
53245         }
53246         return renderers;
53247     },
53248     
53249     getColumnIds : function(){
53250         var ids = [];
53251         var cm = this.grid.colModel;
53252         var colCount = cm.getColumnCount();
53253         for(var i = 0; i < colCount; i++){
53254             ids[i] = cm.getColumnId(i);
53255         }
53256         return ids;
53257     },
53258     
53259     getDataIndexes : function(){
53260         if(!this.indexMap){
53261             this.indexMap = this.buildIndexMap();
53262         }
53263         return this.indexMap.colToData;
53264     },
53265     
53266     getColumnIndexByDataIndex : function(dataIndex){
53267         if(!this.indexMap){
53268             this.indexMap = this.buildIndexMap();
53269         }
53270         return this.indexMap.dataToCol[dataIndex];
53271     },
53272     
53273     /**
53274      * Set a css style for a column dynamically. 
53275      * @param {Number} colIndex The index of the column
53276      * @param {String} name The css property name
53277      * @param {String} value The css value
53278      */
53279     setCSSStyle : function(colIndex, name, value){
53280         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53281         Roo.util.CSS.updateRule(selector, name, value);
53282     },
53283     
53284     generateRules : function(cm){
53285         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53286         Roo.util.CSS.removeStyleSheet(rulesId);
53287         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53288             var cid = cm.getColumnId(i);
53289             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53290                          this.tdSelector, cid, " {\n}\n",
53291                          this.hdSelector, cid, " {\n}\n",
53292                          this.splitSelector, cid, " {\n}\n");
53293         }
53294         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53295     }
53296 });/*
53297  * Based on:
53298  * Ext JS Library 1.1.1
53299  * Copyright(c) 2006-2007, Ext JS, LLC.
53300  *
53301  * Originally Released Under LGPL - original licence link has changed is not relivant.
53302  *
53303  * Fork - LGPL
53304  * <script type="text/javascript">
53305  */
53306
53307 // private
53308 // This is a support class used internally by the Grid components
53309 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53310     this.grid = grid;
53311     this.view = grid.getView();
53312     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53313     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53314     if(hd2){
53315         this.setHandleElId(Roo.id(hd));
53316         this.setOuterHandleElId(Roo.id(hd2));
53317     }
53318     this.scroll = false;
53319 };
53320 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53321     maxDragWidth: 120,
53322     getDragData : function(e){
53323         var t = Roo.lib.Event.getTarget(e);
53324         var h = this.view.findHeaderCell(t);
53325         if(h){
53326             return {ddel: h.firstChild, header:h};
53327         }
53328         return false;
53329     },
53330
53331     onInitDrag : function(e){
53332         this.view.headersDisabled = true;
53333         var clone = this.dragData.ddel.cloneNode(true);
53334         clone.id = Roo.id();
53335         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53336         this.proxy.update(clone);
53337         return true;
53338     },
53339
53340     afterValidDrop : function(){
53341         var v = this.view;
53342         setTimeout(function(){
53343             v.headersDisabled = false;
53344         }, 50);
53345     },
53346
53347     afterInvalidDrop : function(){
53348         var v = this.view;
53349         setTimeout(function(){
53350             v.headersDisabled = false;
53351         }, 50);
53352     }
53353 });
53354 /*
53355  * Based on:
53356  * Ext JS Library 1.1.1
53357  * Copyright(c) 2006-2007, Ext JS, LLC.
53358  *
53359  * Originally Released Under LGPL - original licence link has changed is not relivant.
53360  *
53361  * Fork - LGPL
53362  * <script type="text/javascript">
53363  */
53364 // private
53365 // This is a support class used internally by the Grid components
53366 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53367     this.grid = grid;
53368     this.view = grid.getView();
53369     // split the proxies so they don't interfere with mouse events
53370     this.proxyTop = Roo.DomHelper.append(document.body, {
53371         cls:"col-move-top", html:"&#160;"
53372     }, true);
53373     this.proxyBottom = Roo.DomHelper.append(document.body, {
53374         cls:"col-move-bottom", html:"&#160;"
53375     }, true);
53376     this.proxyTop.hide = this.proxyBottom.hide = function(){
53377         this.setLeftTop(-100,-100);
53378         this.setStyle("visibility", "hidden");
53379     };
53380     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53381     // temporarily disabled
53382     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53383     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53384 };
53385 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53386     proxyOffsets : [-4, -9],
53387     fly: Roo.Element.fly,
53388
53389     getTargetFromEvent : function(e){
53390         var t = Roo.lib.Event.getTarget(e);
53391         var cindex = this.view.findCellIndex(t);
53392         if(cindex !== false){
53393             return this.view.getHeaderCell(cindex);
53394         }
53395         return null;
53396     },
53397
53398     nextVisible : function(h){
53399         var v = this.view, cm = this.grid.colModel;
53400         h = h.nextSibling;
53401         while(h){
53402             if(!cm.isHidden(v.getCellIndex(h))){
53403                 return h;
53404             }
53405             h = h.nextSibling;
53406         }
53407         return null;
53408     },
53409
53410     prevVisible : function(h){
53411         var v = this.view, cm = this.grid.colModel;
53412         h = h.prevSibling;
53413         while(h){
53414             if(!cm.isHidden(v.getCellIndex(h))){
53415                 return h;
53416             }
53417             h = h.prevSibling;
53418         }
53419         return null;
53420     },
53421
53422     positionIndicator : function(h, n, e){
53423         var x = Roo.lib.Event.getPageX(e);
53424         var r = Roo.lib.Dom.getRegion(n.firstChild);
53425         var px, pt, py = r.top + this.proxyOffsets[1];
53426         if((r.right - x) <= (r.right-r.left)/2){
53427             px = r.right+this.view.borderWidth;
53428             pt = "after";
53429         }else{
53430             px = r.left;
53431             pt = "before";
53432         }
53433         var oldIndex = this.view.getCellIndex(h);
53434         var newIndex = this.view.getCellIndex(n);
53435
53436         if(this.grid.colModel.isFixed(newIndex)){
53437             return false;
53438         }
53439
53440         var locked = this.grid.colModel.isLocked(newIndex);
53441
53442         if(pt == "after"){
53443             newIndex++;
53444         }
53445         if(oldIndex < newIndex){
53446             newIndex--;
53447         }
53448         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53449             return false;
53450         }
53451         px +=  this.proxyOffsets[0];
53452         this.proxyTop.setLeftTop(px, py);
53453         this.proxyTop.show();
53454         if(!this.bottomOffset){
53455             this.bottomOffset = this.view.mainHd.getHeight();
53456         }
53457         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53458         this.proxyBottom.show();
53459         return pt;
53460     },
53461
53462     onNodeEnter : function(n, dd, e, data){
53463         if(data.header != n){
53464             this.positionIndicator(data.header, n, e);
53465         }
53466     },
53467
53468     onNodeOver : function(n, dd, e, data){
53469         var result = false;
53470         if(data.header != n){
53471             result = this.positionIndicator(data.header, n, e);
53472         }
53473         if(!result){
53474             this.proxyTop.hide();
53475             this.proxyBottom.hide();
53476         }
53477         return result ? this.dropAllowed : this.dropNotAllowed;
53478     },
53479
53480     onNodeOut : function(n, dd, e, data){
53481         this.proxyTop.hide();
53482         this.proxyBottom.hide();
53483     },
53484
53485     onNodeDrop : function(n, dd, e, data){
53486         var h = data.header;
53487         if(h != n){
53488             var cm = this.grid.colModel;
53489             var x = Roo.lib.Event.getPageX(e);
53490             var r = Roo.lib.Dom.getRegion(n.firstChild);
53491             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53492             var oldIndex = this.view.getCellIndex(h);
53493             var newIndex = this.view.getCellIndex(n);
53494             var locked = cm.isLocked(newIndex);
53495             if(pt == "after"){
53496                 newIndex++;
53497             }
53498             if(oldIndex < newIndex){
53499                 newIndex--;
53500             }
53501             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53502                 return false;
53503             }
53504             cm.setLocked(oldIndex, locked, true);
53505             cm.moveColumn(oldIndex, newIndex);
53506             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53507             return true;
53508         }
53509         return false;
53510     }
53511 });
53512 /*
53513  * Based on:
53514  * Ext JS Library 1.1.1
53515  * Copyright(c) 2006-2007, Ext JS, LLC.
53516  *
53517  * Originally Released Under LGPL - original licence link has changed is not relivant.
53518  *
53519  * Fork - LGPL
53520  * <script type="text/javascript">
53521  */
53522   
53523 /**
53524  * @class Roo.grid.GridView
53525  * @extends Roo.util.Observable
53526  *
53527  * @constructor
53528  * @param {Object} config
53529  */
53530 Roo.grid.GridView = function(config){
53531     Roo.grid.GridView.superclass.constructor.call(this);
53532     this.el = null;
53533
53534     Roo.apply(this, config);
53535 };
53536
53537 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53538
53539     unselectable :  'unselectable="on"',
53540     unselectableCls :  'x-unselectable',
53541     
53542     
53543     rowClass : "x-grid-row",
53544
53545     cellClass : "x-grid-col",
53546
53547     tdClass : "x-grid-td",
53548
53549     hdClass : "x-grid-hd",
53550
53551     splitClass : "x-grid-split",
53552
53553     sortClasses : ["sort-asc", "sort-desc"],
53554
53555     enableMoveAnim : false,
53556
53557     hlColor: "C3DAF9",
53558
53559     dh : Roo.DomHelper,
53560
53561     fly : Roo.Element.fly,
53562
53563     css : Roo.util.CSS,
53564
53565     borderWidth: 1,
53566
53567     splitOffset: 3,
53568
53569     scrollIncrement : 22,
53570
53571     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53572
53573     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53574
53575     bind : function(ds, cm){
53576         if(this.ds){
53577             this.ds.un("load", this.onLoad, this);
53578             this.ds.un("datachanged", this.onDataChange, this);
53579             this.ds.un("add", this.onAdd, this);
53580             this.ds.un("remove", this.onRemove, this);
53581             this.ds.un("update", this.onUpdate, this);
53582             this.ds.un("clear", this.onClear, this);
53583         }
53584         if(ds){
53585             ds.on("load", this.onLoad, this);
53586             ds.on("datachanged", this.onDataChange, this);
53587             ds.on("add", this.onAdd, this);
53588             ds.on("remove", this.onRemove, this);
53589             ds.on("update", this.onUpdate, this);
53590             ds.on("clear", this.onClear, this);
53591         }
53592         this.ds = ds;
53593
53594         if(this.cm){
53595             this.cm.un("widthchange", this.onColWidthChange, this);
53596             this.cm.un("headerchange", this.onHeaderChange, this);
53597             this.cm.un("hiddenchange", this.onHiddenChange, this);
53598             this.cm.un("columnmoved", this.onColumnMove, this);
53599             this.cm.un("columnlockchange", this.onColumnLock, this);
53600         }
53601         if(cm){
53602             this.generateRules(cm);
53603             cm.on("widthchange", this.onColWidthChange, this);
53604             cm.on("headerchange", this.onHeaderChange, this);
53605             cm.on("hiddenchange", this.onHiddenChange, this);
53606             cm.on("columnmoved", this.onColumnMove, this);
53607             cm.on("columnlockchange", this.onColumnLock, this);
53608         }
53609         this.cm = cm;
53610     },
53611
53612     init: function(grid){
53613         Roo.grid.GridView.superclass.init.call(this, grid);
53614
53615         this.bind(grid.dataSource, grid.colModel);
53616
53617         grid.on("headerclick", this.handleHeaderClick, this);
53618
53619         if(grid.trackMouseOver){
53620             grid.on("mouseover", this.onRowOver, this);
53621             grid.on("mouseout", this.onRowOut, this);
53622         }
53623         grid.cancelTextSelection = function(){};
53624         this.gridId = grid.id;
53625
53626         var tpls = this.templates || {};
53627
53628         if(!tpls.master){
53629             tpls.master = new Roo.Template(
53630                '<div class="x-grid" hidefocus="true">',
53631                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53632                   '<div class="x-grid-topbar"></div>',
53633                   '<div class="x-grid-scroller"><div></div></div>',
53634                   '<div class="x-grid-locked">',
53635                       '<div class="x-grid-header">{lockedHeader}</div>',
53636                       '<div class="x-grid-body">{lockedBody}</div>',
53637                   "</div>",
53638                   '<div class="x-grid-viewport">',
53639                       '<div class="x-grid-header">{header}</div>',
53640                       '<div class="x-grid-body">{body}</div>',
53641                   "</div>",
53642                   '<div class="x-grid-bottombar"></div>',
53643                  
53644                   '<div class="x-grid-resize-proxy">&#160;</div>',
53645                "</div>"
53646             );
53647             tpls.master.disableformats = true;
53648         }
53649
53650         if(!tpls.header){
53651             tpls.header = new Roo.Template(
53652                '<table border="0" cellspacing="0" cellpadding="0">',
53653                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53654                "</table>{splits}"
53655             );
53656             tpls.header.disableformats = true;
53657         }
53658         tpls.header.compile();
53659
53660         if(!tpls.hcell){
53661             tpls.hcell = new Roo.Template(
53662                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53663                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53664                 "</div></td>"
53665              );
53666              tpls.hcell.disableFormats = true;
53667         }
53668         tpls.hcell.compile();
53669
53670         if(!tpls.hsplit){
53671             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53672                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53673             tpls.hsplit.disableFormats = true;
53674         }
53675         tpls.hsplit.compile();
53676
53677         if(!tpls.body){
53678             tpls.body = new Roo.Template(
53679                '<table border="0" cellspacing="0" cellpadding="0">',
53680                "<tbody>{rows}</tbody>",
53681                "</table>"
53682             );
53683             tpls.body.disableFormats = true;
53684         }
53685         tpls.body.compile();
53686
53687         if(!tpls.row){
53688             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53689             tpls.row.disableFormats = true;
53690         }
53691         tpls.row.compile();
53692
53693         if(!tpls.cell){
53694             tpls.cell = new Roo.Template(
53695                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53696                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53697                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53698                 "</td>"
53699             );
53700             tpls.cell.disableFormats = true;
53701         }
53702         tpls.cell.compile();
53703
53704         this.templates = tpls;
53705     },
53706
53707     // remap these for backwards compat
53708     onColWidthChange : function(){
53709         this.updateColumns.apply(this, arguments);
53710     },
53711     onHeaderChange : function(){
53712         this.updateHeaders.apply(this, arguments);
53713     }, 
53714     onHiddenChange : function(){
53715         this.handleHiddenChange.apply(this, arguments);
53716     },
53717     onColumnMove : function(){
53718         this.handleColumnMove.apply(this, arguments);
53719     },
53720     onColumnLock : function(){
53721         this.handleLockChange.apply(this, arguments);
53722     },
53723
53724     onDataChange : function(){
53725         this.refresh();
53726         this.updateHeaderSortState();
53727     },
53728
53729     onClear : function(){
53730         this.refresh();
53731     },
53732
53733     onUpdate : function(ds, record){
53734         this.refreshRow(record);
53735     },
53736
53737     refreshRow : function(record){
53738         var ds = this.ds, index;
53739         if(typeof record == 'number'){
53740             index = record;
53741             record = ds.getAt(index);
53742         }else{
53743             index = ds.indexOf(record);
53744         }
53745         this.insertRows(ds, index, index, true);
53746         this.onRemove(ds, record, index+1, true);
53747         this.syncRowHeights(index, index);
53748         this.layout();
53749         this.fireEvent("rowupdated", this, index, record);
53750     },
53751
53752     onAdd : function(ds, records, index){
53753         this.insertRows(ds, index, index + (records.length-1));
53754     },
53755
53756     onRemove : function(ds, record, index, isUpdate){
53757         if(isUpdate !== true){
53758             this.fireEvent("beforerowremoved", this, index, record);
53759         }
53760         var bt = this.getBodyTable(), lt = this.getLockedTable();
53761         if(bt.rows[index]){
53762             bt.firstChild.removeChild(bt.rows[index]);
53763         }
53764         if(lt.rows[index]){
53765             lt.firstChild.removeChild(lt.rows[index]);
53766         }
53767         if(isUpdate !== true){
53768             this.stripeRows(index);
53769             this.syncRowHeights(index, index);
53770             this.layout();
53771             this.fireEvent("rowremoved", this, index, record);
53772         }
53773     },
53774
53775     onLoad : function(){
53776         this.scrollToTop();
53777     },
53778
53779     /**
53780      * Scrolls the grid to the top
53781      */
53782     scrollToTop : function(){
53783         if(this.scroller){
53784             this.scroller.dom.scrollTop = 0;
53785             this.syncScroll();
53786         }
53787     },
53788
53789     /**
53790      * Gets a panel in the header of the grid that can be used for toolbars etc.
53791      * After modifying the contents of this panel a call to grid.autoSize() may be
53792      * required to register any changes in size.
53793      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53794      * @return Roo.Element
53795      */
53796     getHeaderPanel : function(doShow){
53797         if(doShow){
53798             this.headerPanel.show();
53799         }
53800         return this.headerPanel;
53801     },
53802
53803     /**
53804      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53805      * After modifying the contents of this panel a call to grid.autoSize() may be
53806      * required to register any changes in size.
53807      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53808      * @return Roo.Element
53809      */
53810     getFooterPanel : function(doShow){
53811         if(doShow){
53812             this.footerPanel.show();
53813         }
53814         return this.footerPanel;
53815     },
53816
53817     initElements : function(){
53818         var E = Roo.Element;
53819         var el = this.grid.getGridEl().dom.firstChild;
53820         var cs = el.childNodes;
53821
53822         this.el = new E(el);
53823         
53824          this.focusEl = new E(el.firstChild);
53825         this.focusEl.swallowEvent("click", true);
53826         
53827         this.headerPanel = new E(cs[1]);
53828         this.headerPanel.enableDisplayMode("block");
53829
53830         this.scroller = new E(cs[2]);
53831         this.scrollSizer = new E(this.scroller.dom.firstChild);
53832
53833         this.lockedWrap = new E(cs[3]);
53834         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53835         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53836
53837         this.mainWrap = new E(cs[4]);
53838         this.mainHd = new E(this.mainWrap.dom.firstChild);
53839         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53840
53841         this.footerPanel = new E(cs[5]);
53842         this.footerPanel.enableDisplayMode("block");
53843
53844         this.resizeProxy = new E(cs[6]);
53845
53846         this.headerSelector = String.format(
53847            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53848            this.lockedHd.id, this.mainHd.id
53849         );
53850
53851         this.splitterSelector = String.format(
53852            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53853            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53854         );
53855     },
53856     idToCssName : function(s)
53857     {
53858         return s.replace(/[^a-z0-9]+/ig, '-');
53859     },
53860
53861     getHeaderCell : function(index){
53862         return Roo.DomQuery.select(this.headerSelector)[index];
53863     },
53864
53865     getHeaderCellMeasure : function(index){
53866         return this.getHeaderCell(index).firstChild;
53867     },
53868
53869     getHeaderCellText : function(index){
53870         return this.getHeaderCell(index).firstChild.firstChild;
53871     },
53872
53873     getLockedTable : function(){
53874         return this.lockedBody.dom.firstChild;
53875     },
53876
53877     getBodyTable : function(){
53878         return this.mainBody.dom.firstChild;
53879     },
53880
53881     getLockedRow : function(index){
53882         return this.getLockedTable().rows[index];
53883     },
53884
53885     getRow : function(index){
53886         return this.getBodyTable().rows[index];
53887     },
53888
53889     getRowComposite : function(index){
53890         if(!this.rowEl){
53891             this.rowEl = new Roo.CompositeElementLite();
53892         }
53893         var els = [], lrow, mrow;
53894         if(lrow = this.getLockedRow(index)){
53895             els.push(lrow);
53896         }
53897         if(mrow = this.getRow(index)){
53898             els.push(mrow);
53899         }
53900         this.rowEl.elements = els;
53901         return this.rowEl;
53902     },
53903     /**
53904      * Gets the 'td' of the cell
53905      * 
53906      * @param {Integer} rowIndex row to select
53907      * @param {Integer} colIndex column to select
53908      * 
53909      * @return {Object} 
53910      */
53911     getCell : function(rowIndex, colIndex){
53912         var locked = this.cm.getLockedCount();
53913         var source;
53914         if(colIndex < locked){
53915             source = this.lockedBody.dom.firstChild;
53916         }else{
53917             source = this.mainBody.dom.firstChild;
53918             colIndex -= locked;
53919         }
53920         return source.rows[rowIndex].childNodes[colIndex];
53921     },
53922
53923     getCellText : function(rowIndex, colIndex){
53924         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53925     },
53926
53927     getCellBox : function(cell){
53928         var b = this.fly(cell).getBox();
53929         if(Roo.isOpera){ // opera fails to report the Y
53930             b.y = cell.offsetTop + this.mainBody.getY();
53931         }
53932         return b;
53933     },
53934
53935     getCellIndex : function(cell){
53936         var id = String(cell.className).match(this.cellRE);
53937         if(id){
53938             return parseInt(id[1], 10);
53939         }
53940         return 0;
53941     },
53942
53943     findHeaderIndex : function(n){
53944         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53945         return r ? this.getCellIndex(r) : false;
53946     },
53947
53948     findHeaderCell : function(n){
53949         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53950         return r ? r : false;
53951     },
53952
53953     findRowIndex : function(n){
53954         if(!n){
53955             return false;
53956         }
53957         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53958         return r ? r.rowIndex : false;
53959     },
53960
53961     findCellIndex : function(node){
53962         var stop = this.el.dom;
53963         while(node && node != stop){
53964             if(this.findRE.test(node.className)){
53965                 return this.getCellIndex(node);
53966             }
53967             node = node.parentNode;
53968         }
53969         return false;
53970     },
53971
53972     getColumnId : function(index){
53973         return this.cm.getColumnId(index);
53974     },
53975
53976     getSplitters : function()
53977     {
53978         if(this.splitterSelector){
53979            return Roo.DomQuery.select(this.splitterSelector);
53980         }else{
53981             return null;
53982       }
53983     },
53984
53985     getSplitter : function(index){
53986         return this.getSplitters()[index];
53987     },
53988
53989     onRowOver : function(e, t){
53990         var row;
53991         if((row = this.findRowIndex(t)) !== false){
53992             this.getRowComposite(row).addClass("x-grid-row-over");
53993         }
53994     },
53995
53996     onRowOut : function(e, t){
53997         var row;
53998         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53999             this.getRowComposite(row).removeClass("x-grid-row-over");
54000         }
54001     },
54002
54003     renderHeaders : function(){
54004         var cm = this.cm;
54005         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
54006         var cb = [], lb = [], sb = [], lsb = [], p = {};
54007         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54008             p.cellId = "x-grid-hd-0-" + i;
54009             p.splitId = "x-grid-csplit-0-" + i;
54010             p.id = cm.getColumnId(i);
54011             p.title = cm.getColumnTooltip(i) || cm.getColumnHeader(i).match(/\</)  ? '' :  cm.getColumnHeader(i) || "";
54012             p.value = cm.getColumnHeader(i) || "";
54013             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
54014             if(!cm.isLocked(i)){
54015                 cb[cb.length] = ct.apply(p);
54016                 sb[sb.length] = st.apply(p);
54017             }else{
54018                 lb[lb.length] = ct.apply(p);
54019                 lsb[lsb.length] = st.apply(p);
54020             }
54021         }
54022         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
54023                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
54024     },
54025
54026     updateHeaders : function(){
54027         var html = this.renderHeaders();
54028         this.lockedHd.update(html[0]);
54029         this.mainHd.update(html[1]);
54030     },
54031
54032     /**
54033      * Focuses the specified row.
54034      * @param {Number} row The row index
54035      */
54036     focusRow : function(row)
54037     {
54038         //Roo.log('GridView.focusRow');
54039         var x = this.scroller.dom.scrollLeft;
54040         this.focusCell(row, 0, false);
54041         this.scroller.dom.scrollLeft = x;
54042     },
54043
54044     /**
54045      * Focuses the specified cell.
54046      * @param {Number} row The row index
54047      * @param {Number} col The column index
54048      * @param {Boolean} hscroll false to disable horizontal scrolling
54049      */
54050     focusCell : function(row, col, hscroll)
54051     {
54052         //Roo.log('GridView.focusCell');
54053         var el = this.ensureVisible(row, col, hscroll);
54054         this.focusEl.alignTo(el, "tl-tl");
54055         if(Roo.isGecko){
54056             this.focusEl.focus();
54057         }else{
54058             this.focusEl.focus.defer(1, this.focusEl);
54059         }
54060     },
54061
54062     /**
54063      * Scrolls the specified cell into view
54064      * @param {Number} row The row index
54065      * @param {Number} col The column index
54066      * @param {Boolean} hscroll false to disable horizontal scrolling
54067      */
54068     ensureVisible : function(row, col, hscroll)
54069     {
54070         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
54071         //return null; //disable for testing.
54072         if(typeof row != "number"){
54073             row = row.rowIndex;
54074         }
54075         if(row < 0 && row >= this.ds.getCount()){
54076             return  null;
54077         }
54078         col = (col !== undefined ? col : 0);
54079         var cm = this.grid.colModel;
54080         while(cm.isHidden(col)){
54081             col++;
54082         }
54083
54084         var el = this.getCell(row, col);
54085         if(!el){
54086             return null;
54087         }
54088         var c = this.scroller.dom;
54089
54090         var ctop = parseInt(el.offsetTop, 10);
54091         var cleft = parseInt(el.offsetLeft, 10);
54092         var cbot = ctop + el.offsetHeight;
54093         var cright = cleft + el.offsetWidth;
54094         
54095         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
54096         var stop = parseInt(c.scrollTop, 10);
54097         var sleft = parseInt(c.scrollLeft, 10);
54098         var sbot = stop + ch;
54099         var sright = sleft + c.clientWidth;
54100         /*
54101         Roo.log('GridView.ensureVisible:' +
54102                 ' ctop:' + ctop +
54103                 ' c.clientHeight:' + c.clientHeight +
54104                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
54105                 ' stop:' + stop +
54106                 ' cbot:' + cbot +
54107                 ' sbot:' + sbot +
54108                 ' ch:' + ch  
54109                 );
54110         */
54111         if(ctop < stop){
54112              c.scrollTop = ctop;
54113             //Roo.log("set scrolltop to ctop DISABLE?");
54114         }else if(cbot > sbot){
54115             //Roo.log("set scrolltop to cbot-ch");
54116             c.scrollTop = cbot-ch;
54117         }
54118         
54119         if(hscroll !== false){
54120             if(cleft < sleft){
54121                 c.scrollLeft = cleft;
54122             }else if(cright > sright){
54123                 c.scrollLeft = cright-c.clientWidth;
54124             }
54125         }
54126          
54127         return el;
54128     },
54129
54130     updateColumns : function(){
54131         this.grid.stopEditing();
54132         var cm = this.grid.colModel, colIds = this.getColumnIds();
54133         //var totalWidth = cm.getTotalWidth();
54134         var pos = 0;
54135         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54136             //if(cm.isHidden(i)) continue;
54137             var w = cm.getColumnWidth(i);
54138             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
54139             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
54140         }
54141         this.updateSplitters();
54142     },
54143
54144     generateRules : function(cm){
54145         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
54146         Roo.util.CSS.removeStyleSheet(rulesId);
54147         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54148             var cid = cm.getColumnId(i);
54149             var align = '';
54150             if(cm.config[i].align){
54151                 align = 'text-align:'+cm.config[i].align+';';
54152             }
54153             var hidden = '';
54154             if(cm.isHidden(i)){
54155                 hidden = 'display:none;';
54156             }
54157             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
54158             ruleBuf.push(
54159                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
54160                     this.hdSelector, cid, " {\n", align, width, "}\n",
54161                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
54162                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
54163         }
54164         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54165     },
54166
54167     updateSplitters : function(){
54168         var cm = this.cm, s = this.getSplitters();
54169         if(s){ // splitters not created yet
54170             var pos = 0, locked = true;
54171             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54172                 if(cm.isHidden(i)) {
54173                     continue;
54174                 }
54175                 var w = cm.getColumnWidth(i); // make sure it's a number
54176                 if(!cm.isLocked(i) && locked){
54177                     pos = 0;
54178                     locked = false;
54179                 }
54180                 pos += w;
54181                 s[i].style.left = (pos-this.splitOffset) + "px";
54182             }
54183         }
54184     },
54185
54186     handleHiddenChange : function(colModel, colIndex, hidden){
54187         if(hidden){
54188             this.hideColumn(colIndex);
54189         }else{
54190             this.unhideColumn(colIndex);
54191         }
54192     },
54193
54194     hideColumn : function(colIndex){
54195         var cid = this.getColumnId(colIndex);
54196         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
54197         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
54198         if(Roo.isSafari){
54199             this.updateHeaders();
54200         }
54201         this.updateSplitters();
54202         this.layout();
54203     },
54204
54205     unhideColumn : function(colIndex){
54206         var cid = this.getColumnId(colIndex);
54207         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
54208         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
54209
54210         if(Roo.isSafari){
54211             this.updateHeaders();
54212         }
54213         this.updateSplitters();
54214         this.layout();
54215     },
54216
54217     insertRows : function(dm, firstRow, lastRow, isUpdate){
54218         if(firstRow == 0 && lastRow == dm.getCount()-1){
54219             this.refresh();
54220         }else{
54221             if(!isUpdate){
54222                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
54223             }
54224             var s = this.getScrollState();
54225             var markup = this.renderRows(firstRow, lastRow);
54226             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
54227             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54228             this.restoreScroll(s);
54229             if(!isUpdate){
54230                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54231                 this.syncRowHeights(firstRow, lastRow);
54232                 this.stripeRows(firstRow);
54233                 this.layout();
54234             }
54235         }
54236     },
54237
54238     bufferRows : function(markup, target, index){
54239         var before = null, trows = target.rows, tbody = target.tBodies[0];
54240         if(index < trows.length){
54241             before = trows[index];
54242         }
54243         var b = document.createElement("div");
54244         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54245         var rows = b.firstChild.rows;
54246         for(var i = 0, len = rows.length; i < len; i++){
54247             if(before){
54248                 tbody.insertBefore(rows[0], before);
54249             }else{
54250                 tbody.appendChild(rows[0]);
54251             }
54252         }
54253         b.innerHTML = "";
54254         b = null;
54255     },
54256
54257     deleteRows : function(dm, firstRow, lastRow){
54258         if(dm.getRowCount()<1){
54259             this.fireEvent("beforerefresh", this);
54260             this.mainBody.update("");
54261             this.lockedBody.update("");
54262             this.fireEvent("refresh", this);
54263         }else{
54264             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54265             var bt = this.getBodyTable();
54266             var tbody = bt.firstChild;
54267             var rows = bt.rows;
54268             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54269                 tbody.removeChild(rows[firstRow]);
54270             }
54271             this.stripeRows(firstRow);
54272             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54273         }
54274     },
54275
54276     updateRows : function(dataSource, firstRow, lastRow){
54277         var s = this.getScrollState();
54278         this.refresh();
54279         this.restoreScroll(s);
54280     },
54281
54282     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54283         if(!noRefresh){
54284            this.refresh();
54285         }
54286         this.updateHeaderSortState();
54287     },
54288
54289     getScrollState : function(){
54290         
54291         var sb = this.scroller.dom;
54292         return {left: sb.scrollLeft, top: sb.scrollTop};
54293     },
54294
54295     stripeRows : function(startRow){
54296         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54297             return;
54298         }
54299         startRow = startRow || 0;
54300         var rows = this.getBodyTable().rows;
54301         var lrows = this.getLockedTable().rows;
54302         var cls = ' x-grid-row-alt ';
54303         for(var i = startRow, len = rows.length; i < len; i++){
54304             var row = rows[i], lrow = lrows[i];
54305             var isAlt = ((i+1) % 2 == 0);
54306             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54307             if(isAlt == hasAlt){
54308                 continue;
54309             }
54310             if(isAlt){
54311                 row.className += " x-grid-row-alt";
54312             }else{
54313                 row.className = row.className.replace("x-grid-row-alt", "");
54314             }
54315             if(lrow){
54316                 lrow.className = row.className;
54317             }
54318         }
54319     },
54320
54321     restoreScroll : function(state){
54322         //Roo.log('GridView.restoreScroll');
54323         var sb = this.scroller.dom;
54324         sb.scrollLeft = state.left;
54325         sb.scrollTop = state.top;
54326         this.syncScroll();
54327     },
54328
54329     syncScroll : function(){
54330         //Roo.log('GridView.syncScroll');
54331         var sb = this.scroller.dom;
54332         var sh = this.mainHd.dom;
54333         var bs = this.mainBody.dom;
54334         var lv = this.lockedBody.dom;
54335         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54336         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54337     },
54338
54339     handleScroll : function(e){
54340         this.syncScroll();
54341         var sb = this.scroller.dom;
54342         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54343         e.stopEvent();
54344     },
54345
54346     handleWheel : function(e){
54347         var d = e.getWheelDelta();
54348         this.scroller.dom.scrollTop -= d*22;
54349         // set this here to prevent jumpy scrolling on large tables
54350         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54351         e.stopEvent();
54352     },
54353
54354     renderRows : function(startRow, endRow){
54355         // pull in all the crap needed to render rows
54356         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54357         var colCount = cm.getColumnCount();
54358
54359         if(ds.getCount() < 1){
54360             return ["", ""];
54361         }
54362
54363         // build a map for all the columns
54364         var cs = [];
54365         for(var i = 0; i < colCount; i++){
54366             var name = cm.getDataIndex(i);
54367             cs[i] = {
54368                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54369                 renderer : cm.getRenderer(i),
54370                 id : cm.getColumnId(i),
54371                 locked : cm.isLocked(i),
54372                 has_editor : cm.isCellEditable(i)
54373             };
54374         }
54375
54376         startRow = startRow || 0;
54377         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54378
54379         // records to render
54380         var rs = ds.getRange(startRow, endRow);
54381
54382         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54383     },
54384
54385     // As much as I hate to duplicate code, this was branched because FireFox really hates
54386     // [].join("") on strings. The performance difference was substantial enough to
54387     // branch this function
54388     doRender : Roo.isGecko ?
54389             function(cs, rs, ds, startRow, colCount, stripe){
54390                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54391                 // buffers
54392                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54393                 
54394                 var hasListener = this.grid.hasListener('rowclass');
54395                 var rowcfg = {};
54396                 for(var j = 0, len = rs.length; j < len; j++){
54397                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54398                     for(var i = 0; i < colCount; i++){
54399                         c = cs[i];
54400                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54401                         p.id = c.id;
54402                         p.css = p.attr = "";
54403                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54404                         if(p.value == undefined || p.value === "") {
54405                             p.value = "&#160;";
54406                         }
54407                         if(c.has_editor){
54408                             p.css += ' x-grid-editable-cell';
54409                         }
54410                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
54411                             p.css +=  ' x-grid-dirty-cell';
54412                         }
54413                         var markup = ct.apply(p);
54414                         if(!c.locked){
54415                             cb+= markup;
54416                         }else{
54417                             lcb+= markup;
54418                         }
54419                     }
54420                     var alt = [];
54421                     if(stripe && ((rowIndex+1) % 2 == 0)){
54422                         alt.push("x-grid-row-alt")
54423                     }
54424                     if(r.dirty){
54425                         alt.push(  " x-grid-dirty-row");
54426                     }
54427                     rp.cells = lcb;
54428                     if(this.getRowClass){
54429                         alt.push(this.getRowClass(r, rowIndex));
54430                     }
54431                     if (hasListener) {
54432                         rowcfg = {
54433                              
54434                             record: r,
54435                             rowIndex : rowIndex,
54436                             rowClass : ''
54437                         };
54438                         this.grid.fireEvent('rowclass', this, rowcfg);
54439                         alt.push(rowcfg.rowClass);
54440                     }
54441                     rp.alt = alt.join(" ");
54442                     lbuf+= rt.apply(rp);
54443                     rp.cells = cb;
54444                     buf+=  rt.apply(rp);
54445                 }
54446                 return [lbuf, buf];
54447             } :
54448             function(cs, rs, ds, startRow, colCount, stripe){
54449                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54450                 // buffers
54451                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54452                 var hasListener = this.grid.hasListener('rowclass');
54453  
54454                 var rowcfg = {};
54455                 for(var j = 0, len = rs.length; j < len; j++){
54456                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54457                     for(var i = 0; i < colCount; i++){
54458                         c = cs[i];
54459                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54460                         p.id = c.id;
54461                         p.css = p.attr = "";
54462                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54463                         if(p.value == undefined || p.value === "") {
54464                             p.value = "&#160;";
54465                         }
54466                         //Roo.log(c);
54467                          if(c.has_editor){
54468                             p.css += ' x-grid-editable-cell';
54469                         }
54470                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54471                             p.css += ' x-grid-dirty-cell' 
54472                         }
54473                         
54474                         var markup = ct.apply(p);
54475                         if(!c.locked){
54476                             cb[cb.length] = markup;
54477                         }else{
54478                             lcb[lcb.length] = markup;
54479                         }
54480                     }
54481                     var alt = [];
54482                     if(stripe && ((rowIndex+1) % 2 == 0)){
54483                         alt.push( "x-grid-row-alt");
54484                     }
54485                     if(r.dirty){
54486                         alt.push(" x-grid-dirty-row");
54487                     }
54488                     rp.cells = lcb;
54489                     if(this.getRowClass){
54490                         alt.push( this.getRowClass(r, rowIndex));
54491                     }
54492                     if (hasListener) {
54493                         rowcfg = {
54494                              
54495                             record: r,
54496                             rowIndex : rowIndex,
54497                             rowClass : ''
54498                         };
54499                         this.grid.fireEvent('rowclass', this, rowcfg);
54500                         alt.push(rowcfg.rowClass);
54501                     }
54502                     
54503                     rp.alt = alt.join(" ");
54504                     rp.cells = lcb.join("");
54505                     lbuf[lbuf.length] = rt.apply(rp);
54506                     rp.cells = cb.join("");
54507                     buf[buf.length] =  rt.apply(rp);
54508                 }
54509                 return [lbuf.join(""), buf.join("")];
54510             },
54511
54512     renderBody : function(){
54513         var markup = this.renderRows();
54514         var bt = this.templates.body;
54515         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54516     },
54517
54518     /**
54519      * Refreshes the grid
54520      * @param {Boolean} headersToo
54521      */
54522     refresh : function(headersToo){
54523         this.fireEvent("beforerefresh", this);
54524         this.grid.stopEditing();
54525         var result = this.renderBody();
54526         this.lockedBody.update(result[0]);
54527         this.mainBody.update(result[1]);
54528         if(headersToo === true){
54529             this.updateHeaders();
54530             this.updateColumns();
54531             this.updateSplitters();
54532             this.updateHeaderSortState();
54533         }
54534         this.syncRowHeights();
54535         this.layout();
54536         this.fireEvent("refresh", this);
54537     },
54538
54539     handleColumnMove : function(cm, oldIndex, newIndex){
54540         this.indexMap = null;
54541         var s = this.getScrollState();
54542         this.refresh(true);
54543         this.restoreScroll(s);
54544         this.afterMove(newIndex);
54545     },
54546
54547     afterMove : function(colIndex){
54548         if(this.enableMoveAnim && Roo.enableFx){
54549             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54550         }
54551         // if multisort - fix sortOrder, and reload..
54552         if (this.grid.dataSource.multiSort) {
54553             // the we can call sort again..
54554             var dm = this.grid.dataSource;
54555             var cm = this.grid.colModel;
54556             var so = [];
54557             for(var i = 0; i < cm.config.length; i++ ) {
54558                 
54559                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54560                     continue; // dont' bother, it's not in sort list or being set.
54561                 }
54562                 
54563                 so.push(cm.config[i].dataIndex);
54564             };
54565             dm.sortOrder = so;
54566             dm.load(dm.lastOptions);
54567             
54568             
54569         }
54570         
54571     },
54572
54573     updateCell : function(dm, rowIndex, dataIndex){
54574         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54575         if(typeof colIndex == "undefined"){ // not present in grid
54576             return;
54577         }
54578         var cm = this.grid.colModel;
54579         var cell = this.getCell(rowIndex, colIndex);
54580         var cellText = this.getCellText(rowIndex, colIndex);
54581
54582         var p = {
54583             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54584             id : cm.getColumnId(colIndex),
54585             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54586         };
54587         var renderer = cm.getRenderer(colIndex);
54588         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54589         if(typeof val == "undefined" || val === "") {
54590             val = "&#160;";
54591         }
54592         cellText.innerHTML = val;
54593         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54594         this.syncRowHeights(rowIndex, rowIndex);
54595     },
54596
54597     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54598         var maxWidth = 0;
54599         if(this.grid.autoSizeHeaders){
54600             var h = this.getHeaderCellMeasure(colIndex);
54601             maxWidth = Math.max(maxWidth, h.scrollWidth);
54602         }
54603         var tb, index;
54604         if(this.cm.isLocked(colIndex)){
54605             tb = this.getLockedTable();
54606             index = colIndex;
54607         }else{
54608             tb = this.getBodyTable();
54609             index = colIndex - this.cm.getLockedCount();
54610         }
54611         if(tb && tb.rows){
54612             var rows = tb.rows;
54613             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54614             for(var i = 0; i < stopIndex; i++){
54615                 var cell = rows[i].childNodes[index].firstChild;
54616                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54617             }
54618         }
54619         return maxWidth + /*margin for error in IE*/ 5;
54620     },
54621     /**
54622      * Autofit a column to its content.
54623      * @param {Number} colIndex
54624      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54625      */
54626      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54627          if(this.cm.isHidden(colIndex)){
54628              return; // can't calc a hidden column
54629          }
54630         if(forceMinSize){
54631             var cid = this.cm.getColumnId(colIndex);
54632             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54633            if(this.grid.autoSizeHeaders){
54634                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54635            }
54636         }
54637         var newWidth = this.calcColumnWidth(colIndex);
54638         this.cm.setColumnWidth(colIndex,
54639             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54640         if(!suppressEvent){
54641             this.grid.fireEvent("columnresize", colIndex, newWidth);
54642         }
54643     },
54644
54645     /**
54646      * Autofits all columns to their content and then expands to fit any extra space in the grid
54647      */
54648      autoSizeColumns : function(){
54649         var cm = this.grid.colModel;
54650         var colCount = cm.getColumnCount();
54651         for(var i = 0; i < colCount; i++){
54652             this.autoSizeColumn(i, true, true);
54653         }
54654         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54655             this.fitColumns();
54656         }else{
54657             this.updateColumns();
54658             this.layout();
54659         }
54660     },
54661
54662     /**
54663      * Autofits all columns to the grid's width proportionate with their current size
54664      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54665      */
54666     fitColumns : function(reserveScrollSpace){
54667         var cm = this.grid.colModel;
54668         var colCount = cm.getColumnCount();
54669         var cols = [];
54670         var width = 0;
54671         var i, w;
54672         for (i = 0; i < colCount; i++){
54673             if(!cm.isHidden(i) && !cm.isFixed(i)){
54674                 w = cm.getColumnWidth(i);
54675                 cols.push(i);
54676                 cols.push(w);
54677                 width += w;
54678             }
54679         }
54680         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54681         if(reserveScrollSpace){
54682             avail -= 17;
54683         }
54684         var frac = (avail - cm.getTotalWidth())/width;
54685         while (cols.length){
54686             w = cols.pop();
54687             i = cols.pop();
54688             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54689         }
54690         this.updateColumns();
54691         this.layout();
54692     },
54693
54694     onRowSelect : function(rowIndex){
54695         var row = this.getRowComposite(rowIndex);
54696         row.addClass("x-grid-row-selected");
54697     },
54698
54699     onRowDeselect : function(rowIndex){
54700         var row = this.getRowComposite(rowIndex);
54701         row.removeClass("x-grid-row-selected");
54702     },
54703
54704     onCellSelect : function(row, col){
54705         var cell = this.getCell(row, col);
54706         if(cell){
54707             Roo.fly(cell).addClass("x-grid-cell-selected");
54708         }
54709     },
54710
54711     onCellDeselect : function(row, col){
54712         var cell = this.getCell(row, col);
54713         if(cell){
54714             Roo.fly(cell).removeClass("x-grid-cell-selected");
54715         }
54716     },
54717
54718     updateHeaderSortState : function(){
54719         
54720         // sort state can be single { field: xxx, direction : yyy}
54721         // or   { xxx=>ASC , yyy : DESC ..... }
54722         
54723         var mstate = {};
54724         if (!this.ds.multiSort) { 
54725             var state = this.ds.getSortState();
54726             if(!state){
54727                 return;
54728             }
54729             mstate[state.field] = state.direction;
54730             // FIXME... - this is not used here.. but might be elsewhere..
54731             this.sortState = state;
54732             
54733         } else {
54734             mstate = this.ds.sortToggle;
54735         }
54736         //remove existing sort classes..
54737         
54738         var sc = this.sortClasses;
54739         var hds = this.el.select(this.headerSelector).removeClass(sc);
54740         
54741         for(var f in mstate) {
54742         
54743             var sortColumn = this.cm.findColumnIndex(f);
54744             
54745             if(sortColumn != -1){
54746                 var sortDir = mstate[f];        
54747                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54748             }
54749         }
54750         
54751          
54752         
54753     },
54754
54755
54756     handleHeaderClick : function(g, index,e){
54757         
54758         Roo.log("header click");
54759         
54760         if (Roo.isTouch) {
54761             // touch events on header are handled by context
54762             this.handleHdCtx(g,index,e);
54763             return;
54764         }
54765         
54766         
54767         if(this.headersDisabled){
54768             return;
54769         }
54770         var dm = g.dataSource, cm = g.colModel;
54771         if(!cm.isSortable(index)){
54772             return;
54773         }
54774         g.stopEditing();
54775         
54776         if (dm.multiSort) {
54777             // update the sortOrder
54778             var so = [];
54779             for(var i = 0; i < cm.config.length; i++ ) {
54780                 
54781                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54782                     continue; // dont' bother, it's not in sort list or being set.
54783                 }
54784                 
54785                 so.push(cm.config[i].dataIndex);
54786             };
54787             dm.sortOrder = so;
54788         }
54789         
54790         
54791         dm.sort(cm.getDataIndex(index));
54792     },
54793
54794
54795     destroy : function(){
54796         if(this.colMenu){
54797             this.colMenu.removeAll();
54798             Roo.menu.MenuMgr.unregister(this.colMenu);
54799             this.colMenu.getEl().remove();
54800             delete this.colMenu;
54801         }
54802         if(this.hmenu){
54803             this.hmenu.removeAll();
54804             Roo.menu.MenuMgr.unregister(this.hmenu);
54805             this.hmenu.getEl().remove();
54806             delete this.hmenu;
54807         }
54808         if(this.grid.enableColumnMove){
54809             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54810             if(dds){
54811                 for(var dd in dds){
54812                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54813                         var elid = dds[dd].dragElId;
54814                         dds[dd].unreg();
54815                         Roo.get(elid).remove();
54816                     } else if(dds[dd].config.isTarget){
54817                         dds[dd].proxyTop.remove();
54818                         dds[dd].proxyBottom.remove();
54819                         dds[dd].unreg();
54820                     }
54821                     if(Roo.dd.DDM.locationCache[dd]){
54822                         delete Roo.dd.DDM.locationCache[dd];
54823                     }
54824                 }
54825                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54826             }
54827         }
54828         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54829         this.bind(null, null);
54830         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54831     },
54832
54833     handleLockChange : function(){
54834         this.refresh(true);
54835     },
54836
54837     onDenyColumnLock : function(){
54838
54839     },
54840
54841     onDenyColumnHide : function(){
54842
54843     },
54844
54845     handleHdMenuClick : function(item){
54846         var index = this.hdCtxIndex;
54847         var cm = this.cm, ds = this.ds;
54848         switch(item.id){
54849             case "asc":
54850                 ds.sort(cm.getDataIndex(index), "ASC");
54851                 break;
54852             case "desc":
54853                 ds.sort(cm.getDataIndex(index), "DESC");
54854                 break;
54855             case "lock":
54856                 var lc = cm.getLockedCount();
54857                 if(cm.getColumnCount(true) <= lc+1){
54858                     this.onDenyColumnLock();
54859                     return;
54860                 }
54861                 if(lc != index){
54862                     cm.setLocked(index, true, true);
54863                     cm.moveColumn(index, lc);
54864                     this.grid.fireEvent("columnmove", index, lc);
54865                 }else{
54866                     cm.setLocked(index, true);
54867                 }
54868             break;
54869             case "unlock":
54870                 var lc = cm.getLockedCount();
54871                 if((lc-1) != index){
54872                     cm.setLocked(index, false, true);
54873                     cm.moveColumn(index, lc-1);
54874                     this.grid.fireEvent("columnmove", index, lc-1);
54875                 }else{
54876                     cm.setLocked(index, false);
54877                 }
54878             break;
54879             case 'wider': // used to expand cols on touch..
54880             case 'narrow':
54881                 var cw = cm.getColumnWidth(index);
54882                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54883                 cw = Math.max(0, cw);
54884                 cw = Math.min(cw,4000);
54885                 cm.setColumnWidth(index, cw);
54886                 break;
54887                 
54888             default:
54889                 index = cm.getIndexById(item.id.substr(4));
54890                 if(index != -1){
54891                     if(item.checked && cm.getColumnCount(true) <= 1){
54892                         this.onDenyColumnHide();
54893                         return false;
54894                     }
54895                     cm.setHidden(index, item.checked);
54896                 }
54897         }
54898         return true;
54899     },
54900
54901     beforeColMenuShow : function(){
54902         var cm = this.cm,  colCount = cm.getColumnCount();
54903         this.colMenu.removeAll();
54904         for(var i = 0; i < colCount; i++){
54905             this.colMenu.add(new Roo.menu.CheckItem({
54906                 id: "col-"+cm.getColumnId(i),
54907                 text: cm.getColumnHeader(i),
54908                 checked: !cm.isHidden(i),
54909                 hideOnClick:false
54910             }));
54911         }
54912     },
54913
54914     handleHdCtx : function(g, index, e){
54915         e.stopEvent();
54916         var hd = this.getHeaderCell(index);
54917         this.hdCtxIndex = index;
54918         var ms = this.hmenu.items, cm = this.cm;
54919         ms.get("asc").setDisabled(!cm.isSortable(index));
54920         ms.get("desc").setDisabled(!cm.isSortable(index));
54921         if(this.grid.enableColLock !== false){
54922             ms.get("lock").setDisabled(cm.isLocked(index));
54923             ms.get("unlock").setDisabled(!cm.isLocked(index));
54924         }
54925         this.hmenu.show(hd, "tl-bl");
54926     },
54927
54928     handleHdOver : function(e){
54929         var hd = this.findHeaderCell(e.getTarget());
54930         if(hd && !this.headersDisabled){
54931             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54932                this.fly(hd).addClass("x-grid-hd-over");
54933             }
54934         }
54935     },
54936
54937     handleHdOut : function(e){
54938         var hd = this.findHeaderCell(e.getTarget());
54939         if(hd){
54940             this.fly(hd).removeClass("x-grid-hd-over");
54941         }
54942     },
54943
54944     handleSplitDblClick : function(e, t){
54945         var i = this.getCellIndex(t);
54946         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54947             this.autoSizeColumn(i, true);
54948             this.layout();
54949         }
54950     },
54951
54952     render : function(){
54953
54954         var cm = this.cm;
54955         var colCount = cm.getColumnCount();
54956
54957         if(this.grid.monitorWindowResize === true){
54958             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54959         }
54960         var header = this.renderHeaders();
54961         var body = this.templates.body.apply({rows:""});
54962         var html = this.templates.master.apply({
54963             lockedBody: body,
54964             body: body,
54965             lockedHeader: header[0],
54966             header: header[1]
54967         });
54968
54969         //this.updateColumns();
54970
54971         this.grid.getGridEl().dom.innerHTML = html;
54972
54973         this.initElements();
54974         
54975         // a kludge to fix the random scolling effect in webkit
54976         this.el.on("scroll", function() {
54977             this.el.dom.scrollTop=0; // hopefully not recursive..
54978         },this);
54979
54980         this.scroller.on("scroll", this.handleScroll, this);
54981         this.lockedBody.on("mousewheel", this.handleWheel, this);
54982         this.mainBody.on("mousewheel", this.handleWheel, this);
54983
54984         this.mainHd.on("mouseover", this.handleHdOver, this);
54985         this.mainHd.on("mouseout", this.handleHdOut, this);
54986         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54987                 {delegate: "."+this.splitClass});
54988
54989         this.lockedHd.on("mouseover", this.handleHdOver, this);
54990         this.lockedHd.on("mouseout", this.handleHdOut, this);
54991         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54992                 {delegate: "."+this.splitClass});
54993
54994         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54995             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54996         }
54997
54998         this.updateSplitters();
54999
55000         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
55001             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
55002             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
55003         }
55004
55005         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
55006             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
55007             this.hmenu.add(
55008                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
55009                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
55010             );
55011             if(this.grid.enableColLock !== false){
55012                 this.hmenu.add('-',
55013                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
55014                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
55015                 );
55016             }
55017             if (Roo.isTouch) {
55018                  this.hmenu.add('-',
55019                     {id:"wider", text: this.columnsWiderText},
55020                     {id:"narrow", text: this.columnsNarrowText }
55021                 );
55022                 
55023                  
55024             }
55025             
55026             if(this.grid.enableColumnHide !== false){
55027
55028                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
55029                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
55030                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
55031
55032                 this.hmenu.add('-',
55033                     {id:"columns", text: this.columnsText, menu: this.colMenu}
55034                 );
55035             }
55036             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
55037
55038             this.grid.on("headercontextmenu", this.handleHdCtx, this);
55039         }
55040
55041         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
55042             this.dd = new Roo.grid.GridDragZone(this.grid, {
55043                 ddGroup : this.grid.ddGroup || 'GridDD'
55044             });
55045             
55046         }
55047
55048         /*
55049         for(var i = 0; i < colCount; i++){
55050             if(cm.isHidden(i)){
55051                 this.hideColumn(i);
55052             }
55053             if(cm.config[i].align){
55054                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
55055                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
55056             }
55057         }*/
55058         
55059         this.updateHeaderSortState();
55060
55061         this.beforeInitialResize();
55062         this.layout(true);
55063
55064         // two part rendering gives faster view to the user
55065         this.renderPhase2.defer(1, this);
55066     },
55067
55068     renderPhase2 : function(){
55069         // render the rows now
55070         this.refresh();
55071         if(this.grid.autoSizeColumns){
55072             this.autoSizeColumns();
55073         }
55074     },
55075
55076     beforeInitialResize : function(){
55077
55078     },
55079
55080     onColumnSplitterMoved : function(i, w){
55081         this.userResized = true;
55082         var cm = this.grid.colModel;
55083         cm.setColumnWidth(i, w, true);
55084         var cid = cm.getColumnId(i);
55085         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
55086         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
55087         this.updateSplitters();
55088         this.layout();
55089         this.grid.fireEvent("columnresize", i, w);
55090     },
55091
55092     syncRowHeights : function(startIndex, endIndex){
55093         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
55094             startIndex = startIndex || 0;
55095             var mrows = this.getBodyTable().rows;
55096             var lrows = this.getLockedTable().rows;
55097             var len = mrows.length-1;
55098             endIndex = Math.min(endIndex || len, len);
55099             for(var i = startIndex; i <= endIndex; i++){
55100                 var m = mrows[i], l = lrows[i];
55101                 var h = Math.max(m.offsetHeight, l.offsetHeight);
55102                 m.style.height = l.style.height = h + "px";
55103             }
55104         }
55105     },
55106
55107     layout : function(initialRender, is2ndPass){
55108         var g = this.grid;
55109         var auto = g.autoHeight;
55110         var scrollOffset = 16;
55111         var c = g.getGridEl(), cm = this.cm,
55112                 expandCol = g.autoExpandColumn,
55113                 gv = this;
55114         //c.beginMeasure();
55115
55116         if(!c.dom.offsetWidth){ // display:none?
55117             if(initialRender){
55118                 this.lockedWrap.show();
55119                 this.mainWrap.show();
55120             }
55121             return;
55122         }
55123
55124         var hasLock = this.cm.isLocked(0);
55125
55126         var tbh = this.headerPanel.getHeight();
55127         var bbh = this.footerPanel.getHeight();
55128
55129         if(auto){
55130             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
55131             var newHeight = ch + c.getBorderWidth("tb");
55132             if(g.maxHeight){
55133                 newHeight = Math.min(g.maxHeight, newHeight);
55134             }
55135             c.setHeight(newHeight);
55136         }
55137
55138         if(g.autoWidth){
55139             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
55140         }
55141
55142         var s = this.scroller;
55143
55144         var csize = c.getSize(true);
55145
55146         this.el.setSize(csize.width, csize.height);
55147
55148         this.headerPanel.setWidth(csize.width);
55149         this.footerPanel.setWidth(csize.width);
55150
55151         var hdHeight = this.mainHd.getHeight();
55152         var vw = csize.width;
55153         var vh = csize.height - (tbh + bbh);
55154
55155         s.setSize(vw, vh);
55156
55157         var bt = this.getBodyTable();
55158         
55159         if(cm.getLockedCount() == cm.config.length){
55160             bt = this.getLockedTable();
55161         }
55162         
55163         var ltWidth = hasLock ?
55164                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
55165
55166         var scrollHeight = bt.offsetHeight;
55167         var scrollWidth = ltWidth + bt.offsetWidth;
55168         var vscroll = false, hscroll = false;
55169
55170         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
55171
55172         var lw = this.lockedWrap, mw = this.mainWrap;
55173         var lb = this.lockedBody, mb = this.mainBody;
55174
55175         setTimeout(function(){
55176             var t = s.dom.offsetTop;
55177             var w = s.dom.clientWidth,
55178                 h = s.dom.clientHeight;
55179
55180             lw.setTop(t);
55181             lw.setSize(ltWidth, h);
55182
55183             mw.setLeftTop(ltWidth, t);
55184             mw.setSize(w-ltWidth, h);
55185
55186             lb.setHeight(h-hdHeight);
55187             mb.setHeight(h-hdHeight);
55188
55189             if(is2ndPass !== true && !gv.userResized && expandCol){
55190                 // high speed resize without full column calculation
55191                 
55192                 var ci = cm.getIndexById(expandCol);
55193                 if (ci < 0) {
55194                     ci = cm.findColumnIndex(expandCol);
55195                 }
55196                 ci = Math.max(0, ci); // make sure it's got at least the first col.
55197                 var expandId = cm.getColumnId(ci);
55198                 var  tw = cm.getTotalWidth(false);
55199                 var currentWidth = cm.getColumnWidth(ci);
55200                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
55201                 if(currentWidth != cw){
55202                     cm.setColumnWidth(ci, cw, true);
55203                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55204                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55205                     gv.updateSplitters();
55206                     gv.layout(false, true);
55207                 }
55208             }
55209
55210             if(initialRender){
55211                 lw.show();
55212                 mw.show();
55213             }
55214             //c.endMeasure();
55215         }, 10);
55216     },
55217
55218     onWindowResize : function(){
55219         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
55220             return;
55221         }
55222         this.layout();
55223     },
55224
55225     appendFooter : function(parentEl){
55226         return null;
55227     },
55228
55229     sortAscText : "Sort Ascending",
55230     sortDescText : "Sort Descending",
55231     lockText : "Lock Column",
55232     unlockText : "Unlock Column",
55233     columnsText : "Columns",
55234  
55235     columnsWiderText : "Wider",
55236     columnsNarrowText : "Thinner"
55237 });
55238
55239
55240 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
55241     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
55242     this.proxy.el.addClass('x-grid3-col-dd');
55243 };
55244
55245 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
55246     handleMouseDown : function(e){
55247
55248     },
55249
55250     callHandleMouseDown : function(e){
55251         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55252     }
55253 });
55254 /*
55255  * Based on:
55256  * Ext JS Library 1.1.1
55257  * Copyright(c) 2006-2007, Ext JS, LLC.
55258  *
55259  * Originally Released Under LGPL - original licence link has changed is not relivant.
55260  *
55261  * Fork - LGPL
55262  * <script type="text/javascript">
55263  */
55264  
55265 // private
55266 // This is a support class used internally by the Grid components
55267 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55268     this.grid = grid;
55269     this.view = grid.getView();
55270     this.proxy = this.view.resizeProxy;
55271     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55272         "gridSplitters" + this.grid.getGridEl().id, {
55273         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55274     });
55275     this.setHandleElId(Roo.id(hd));
55276     this.setOuterHandleElId(Roo.id(hd2));
55277     this.scroll = false;
55278 };
55279 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55280     fly: Roo.Element.fly,
55281
55282     b4StartDrag : function(x, y){
55283         this.view.headersDisabled = true;
55284         this.proxy.setHeight(this.view.mainWrap.getHeight());
55285         var w = this.cm.getColumnWidth(this.cellIndex);
55286         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55287         this.resetConstraints();
55288         this.setXConstraint(minw, 1000);
55289         this.setYConstraint(0, 0);
55290         this.minX = x - minw;
55291         this.maxX = x + 1000;
55292         this.startPos = x;
55293         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55294     },
55295
55296
55297     handleMouseDown : function(e){
55298         ev = Roo.EventObject.setEvent(e);
55299         var t = this.fly(ev.getTarget());
55300         if(t.hasClass("x-grid-split")){
55301             this.cellIndex = this.view.getCellIndex(t.dom);
55302             this.split = t.dom;
55303             this.cm = this.grid.colModel;
55304             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55305                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55306             }
55307         }
55308     },
55309
55310     endDrag : function(e){
55311         this.view.headersDisabled = false;
55312         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55313         var diff = endX - this.startPos;
55314         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55315     },
55316
55317     autoOffset : function(){
55318         this.setDelta(0,0);
55319     }
55320 });/*
55321  * Based on:
55322  * Ext JS Library 1.1.1
55323  * Copyright(c) 2006-2007, Ext JS, LLC.
55324  *
55325  * Originally Released Under LGPL - original licence link has changed is not relivant.
55326  *
55327  * Fork - LGPL
55328  * <script type="text/javascript">
55329  */
55330  
55331 // private
55332 // This is a support class used internally by the Grid components
55333 Roo.grid.GridDragZone = function(grid, config){
55334     this.view = grid.getView();
55335     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55336     if(this.view.lockedBody){
55337         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55338         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55339     }
55340     this.scroll = false;
55341     this.grid = grid;
55342     this.ddel = document.createElement('div');
55343     this.ddel.className = 'x-grid-dd-wrap';
55344 };
55345
55346 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55347     ddGroup : "GridDD",
55348
55349     getDragData : function(e){
55350         var t = Roo.lib.Event.getTarget(e);
55351         var rowIndex = this.view.findRowIndex(t);
55352         var sm = this.grid.selModel;
55353             
55354         //Roo.log(rowIndex);
55355         
55356         if (sm.getSelectedCell) {
55357             // cell selection..
55358             if (!sm.getSelectedCell()) {
55359                 return false;
55360             }
55361             if (rowIndex != sm.getSelectedCell()[0]) {
55362                 return false;
55363             }
55364         
55365         }
55366         
55367         if(rowIndex !== false){
55368             
55369             // if editorgrid.. 
55370             
55371             
55372             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55373                
55374             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55375               //  
55376             //}
55377             if (e.hasModifier()){
55378                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55379             }
55380             
55381             Roo.log("getDragData");
55382             
55383             return {
55384                 grid: this.grid,
55385                 ddel: this.ddel,
55386                 rowIndex: rowIndex,
55387                 selections:sm.getSelections ? sm.getSelections() : (
55388                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55389                 )
55390             };
55391         }
55392         return false;
55393     },
55394
55395     onInitDrag : function(e){
55396         var data = this.dragData;
55397         this.ddel.innerHTML = this.grid.getDragDropText();
55398         this.proxy.update(this.ddel);
55399         // fire start drag?
55400     },
55401
55402     afterRepair : function(){
55403         this.dragging = false;
55404     },
55405
55406     getRepairXY : function(e, data){
55407         return false;
55408     },
55409
55410     onEndDrag : function(data, e){
55411         // fire end drag?
55412     },
55413
55414     onValidDrop : function(dd, e, id){
55415         // fire drag drop?
55416         this.hideProxy();
55417     },
55418
55419     beforeInvalidDrop : function(e, id){
55420
55421     }
55422 });/*
55423  * Based on:
55424  * Ext JS Library 1.1.1
55425  * Copyright(c) 2006-2007, Ext JS, LLC.
55426  *
55427  * Originally Released Under LGPL - original licence link has changed is not relivant.
55428  *
55429  * Fork - LGPL
55430  * <script type="text/javascript">
55431  */
55432  
55433
55434 /**
55435  * @class Roo.grid.ColumnModel
55436  * @extends Roo.util.Observable
55437  * This is the default implementation of a ColumnModel used by the Grid. It defines
55438  * the columns in the grid.
55439  * <br>Usage:<br>
55440  <pre><code>
55441  var colModel = new Roo.grid.ColumnModel([
55442         {header: "Ticker", width: 60, sortable: true, locked: true},
55443         {header: "Company Name", width: 150, sortable: true},
55444         {header: "Market Cap.", width: 100, sortable: true},
55445         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55446         {header: "Employees", width: 100, sortable: true, resizable: false}
55447  ]);
55448  </code></pre>
55449  * <p>
55450  
55451  * The config options listed for this class are options which may appear in each
55452  * individual column definition.
55453  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55454  * @constructor
55455  * @param {Object} config An Array of column config objects. See this class's
55456  * config objects for details.
55457 */
55458 Roo.grid.ColumnModel = function(config){
55459         /**
55460      * The config passed into the constructor
55461      */
55462     this.config = config;
55463     this.lookup = {};
55464
55465     // if no id, create one
55466     // if the column does not have a dataIndex mapping,
55467     // map it to the order it is in the config
55468     for(var i = 0, len = config.length; i < len; i++){
55469         var c = config[i];
55470         if(typeof c.dataIndex == "undefined"){
55471             c.dataIndex = i;
55472         }
55473         if(typeof c.renderer == "string"){
55474             c.renderer = Roo.util.Format[c.renderer];
55475         }
55476         if(typeof c.id == "undefined"){
55477             c.id = Roo.id();
55478         }
55479         if(c.editor && c.editor.xtype){
55480             c.editor  = Roo.factory(c.editor, Roo.grid);
55481         }
55482         if(c.editor && c.editor.isFormField){
55483             c.editor = new Roo.grid.GridEditor(c.editor);
55484         }
55485         this.lookup[c.id] = c;
55486     }
55487
55488     /**
55489      * The width of columns which have no width specified (defaults to 100)
55490      * @type Number
55491      */
55492     this.defaultWidth = 100;
55493
55494     /**
55495      * Default sortable of columns which have no sortable specified (defaults to false)
55496      * @type Boolean
55497      */
55498     this.defaultSortable = false;
55499
55500     this.addEvents({
55501         /**
55502              * @event widthchange
55503              * Fires when the width of a column changes.
55504              * @param {ColumnModel} this
55505              * @param {Number} columnIndex The column index
55506              * @param {Number} newWidth The new width
55507              */
55508             "widthchange": true,
55509         /**
55510              * @event headerchange
55511              * Fires when the text of a header changes.
55512              * @param {ColumnModel} this
55513              * @param {Number} columnIndex The column index
55514              * @param {Number} newText The new header text
55515              */
55516             "headerchange": true,
55517         /**
55518              * @event hiddenchange
55519              * Fires when a column is hidden or "unhidden".
55520              * @param {ColumnModel} this
55521              * @param {Number} columnIndex The column index
55522              * @param {Boolean} hidden true if hidden, false otherwise
55523              */
55524             "hiddenchange": true,
55525             /**
55526          * @event columnmoved
55527          * Fires when a column is moved.
55528          * @param {ColumnModel} this
55529          * @param {Number} oldIndex
55530          * @param {Number} newIndex
55531          */
55532         "columnmoved" : true,
55533         /**
55534          * @event columlockchange
55535          * Fires when a column's locked state is changed
55536          * @param {ColumnModel} this
55537          * @param {Number} colIndex
55538          * @param {Boolean} locked true if locked
55539          */
55540         "columnlockchange" : true
55541     });
55542     Roo.grid.ColumnModel.superclass.constructor.call(this);
55543 };
55544 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55545     /**
55546      * @cfg {String} header The header text to display in the Grid view.
55547      */
55548     /**
55549      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55550      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55551      * specified, the column's index is used as an index into the Record's data Array.
55552      */
55553     /**
55554      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55555      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55556      */
55557     /**
55558      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55559      * Defaults to the value of the {@link #defaultSortable} property.
55560      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55561      */
55562     /**
55563      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55564      */
55565     /**
55566      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55567      */
55568     /**
55569      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55570      */
55571     /**
55572      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55573      */
55574     /**
55575      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55576      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55577      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55578      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55579      */
55580        /**
55581      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55582      */
55583     /**
55584      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55585      */
55586     /**
55587      * @cfg {String} cursor (Optional)
55588      */
55589     /**
55590      * @cfg {String} tooltip (Optional)
55591      */
55592     /**
55593      * @cfg {Number} xs (Optional)
55594      */
55595     /**
55596      * @cfg {Number} sm (Optional)
55597      */
55598     /**
55599      * @cfg {Number} md (Optional)
55600      */
55601     /**
55602      * @cfg {Number} lg (Optional)
55603      */
55604     /**
55605      * Returns the id of the column at the specified index.
55606      * @param {Number} index The column index
55607      * @return {String} the id
55608      */
55609     getColumnId : function(index){
55610         return this.config[index].id;
55611     },
55612
55613     /**
55614      * Returns the column for a specified id.
55615      * @param {String} id The column id
55616      * @return {Object} the column
55617      */
55618     getColumnById : function(id){
55619         return this.lookup[id];
55620     },
55621
55622     
55623     /**
55624      * Returns the column for a specified dataIndex.
55625      * @param {String} dataIndex The column dataIndex
55626      * @return {Object|Boolean} the column or false if not found
55627      */
55628     getColumnByDataIndex: function(dataIndex){
55629         var index = this.findColumnIndex(dataIndex);
55630         return index > -1 ? this.config[index] : false;
55631     },
55632     
55633     /**
55634      * Returns the index for a specified column id.
55635      * @param {String} id The column id
55636      * @return {Number} the index, or -1 if not found
55637      */
55638     getIndexById : function(id){
55639         for(var i = 0, len = this.config.length; i < len; i++){
55640             if(this.config[i].id == id){
55641                 return i;
55642             }
55643         }
55644         return -1;
55645     },
55646     
55647     /**
55648      * Returns the index for a specified column dataIndex.
55649      * @param {String} dataIndex The column dataIndex
55650      * @return {Number} the index, or -1 if not found
55651      */
55652     
55653     findColumnIndex : function(dataIndex){
55654         for(var i = 0, len = this.config.length; i < len; i++){
55655             if(this.config[i].dataIndex == dataIndex){
55656                 return i;
55657             }
55658         }
55659         return -1;
55660     },
55661     
55662     
55663     moveColumn : function(oldIndex, newIndex){
55664         var c = this.config[oldIndex];
55665         this.config.splice(oldIndex, 1);
55666         this.config.splice(newIndex, 0, c);
55667         this.dataMap = null;
55668         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55669     },
55670
55671     isLocked : function(colIndex){
55672         return this.config[colIndex].locked === true;
55673     },
55674
55675     setLocked : function(colIndex, value, suppressEvent){
55676         if(this.isLocked(colIndex) == value){
55677             return;
55678         }
55679         this.config[colIndex].locked = value;
55680         if(!suppressEvent){
55681             this.fireEvent("columnlockchange", this, colIndex, value);
55682         }
55683     },
55684
55685     getTotalLockedWidth : function(){
55686         var totalWidth = 0;
55687         for(var i = 0; i < this.config.length; i++){
55688             if(this.isLocked(i) && !this.isHidden(i)){
55689                 this.totalWidth += this.getColumnWidth(i);
55690             }
55691         }
55692         return totalWidth;
55693     },
55694
55695     getLockedCount : function(){
55696         for(var i = 0, len = this.config.length; i < len; i++){
55697             if(!this.isLocked(i)){
55698                 return i;
55699             }
55700         }
55701         
55702         return this.config.length;
55703     },
55704
55705     /**
55706      * Returns the number of columns.
55707      * @return {Number}
55708      */
55709     getColumnCount : function(visibleOnly){
55710         if(visibleOnly === true){
55711             var c = 0;
55712             for(var i = 0, len = this.config.length; i < len; i++){
55713                 if(!this.isHidden(i)){
55714                     c++;
55715                 }
55716             }
55717             return c;
55718         }
55719         return this.config.length;
55720     },
55721
55722     /**
55723      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55724      * @param {Function} fn
55725      * @param {Object} scope (optional)
55726      * @return {Array} result
55727      */
55728     getColumnsBy : function(fn, scope){
55729         var r = [];
55730         for(var i = 0, len = this.config.length; i < len; i++){
55731             var c = this.config[i];
55732             if(fn.call(scope||this, c, i) === true){
55733                 r[r.length] = c;
55734             }
55735         }
55736         return r;
55737     },
55738
55739     /**
55740      * Returns true if the specified column is sortable.
55741      * @param {Number} col The column index
55742      * @return {Boolean}
55743      */
55744     isSortable : function(col){
55745         if(typeof this.config[col].sortable == "undefined"){
55746             return this.defaultSortable;
55747         }
55748         return this.config[col].sortable;
55749     },
55750
55751     /**
55752      * Returns the rendering (formatting) function defined for the column.
55753      * @param {Number} col The column index.
55754      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55755      */
55756     getRenderer : function(col){
55757         if(!this.config[col].renderer){
55758             return Roo.grid.ColumnModel.defaultRenderer;
55759         }
55760         return this.config[col].renderer;
55761     },
55762
55763     /**
55764      * Sets the rendering (formatting) function for a column.
55765      * @param {Number} col The column index
55766      * @param {Function} fn The function to use to process the cell's raw data
55767      * to return HTML markup for the grid view. The render function is called with
55768      * the following parameters:<ul>
55769      * <li>Data value.</li>
55770      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55771      * <li>css A CSS style string to apply to the table cell.</li>
55772      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55773      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55774      * <li>Row index</li>
55775      * <li>Column index</li>
55776      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55777      */
55778     setRenderer : function(col, fn){
55779         this.config[col].renderer = fn;
55780     },
55781
55782     /**
55783      * Returns the width for the specified column.
55784      * @param {Number} col The column index
55785      * @return {Number}
55786      */
55787     getColumnWidth : function(col){
55788         return this.config[col].width * 1 || this.defaultWidth;
55789     },
55790
55791     /**
55792      * Sets the width for a column.
55793      * @param {Number} col The column index
55794      * @param {Number} width The new width
55795      */
55796     setColumnWidth : function(col, width, suppressEvent){
55797         this.config[col].width = width;
55798         this.totalWidth = null;
55799         if(!suppressEvent){
55800              this.fireEvent("widthchange", this, col, width);
55801         }
55802     },
55803
55804     /**
55805      * Returns the total width of all columns.
55806      * @param {Boolean} includeHidden True to include hidden column widths
55807      * @return {Number}
55808      */
55809     getTotalWidth : function(includeHidden){
55810         if(!this.totalWidth){
55811             this.totalWidth = 0;
55812             for(var i = 0, len = this.config.length; i < len; i++){
55813                 if(includeHidden || !this.isHidden(i)){
55814                     this.totalWidth += this.getColumnWidth(i);
55815                 }
55816             }
55817         }
55818         return this.totalWidth;
55819     },
55820
55821     /**
55822      * Returns the header for the specified column.
55823      * @param {Number} col The column index
55824      * @return {String}
55825      */
55826     getColumnHeader : function(col){
55827         return this.config[col].header;
55828     },
55829
55830     /**
55831      * Sets the header for a column.
55832      * @param {Number} col The column index
55833      * @param {String} header The new header
55834      */
55835     setColumnHeader : function(col, header){
55836         this.config[col].header = header;
55837         this.fireEvent("headerchange", this, col, header);
55838     },
55839
55840     /**
55841      * Returns the tooltip for the specified column.
55842      * @param {Number} col The column index
55843      * @return {String}
55844      */
55845     getColumnTooltip : function(col){
55846             return this.config[col].tooltip;
55847     },
55848     /**
55849      * Sets the tooltip for a column.
55850      * @param {Number} col The column index
55851      * @param {String} tooltip The new tooltip
55852      */
55853     setColumnTooltip : function(col, tooltip){
55854             this.config[col].tooltip = tooltip;
55855     },
55856
55857     /**
55858      * Returns the dataIndex for the specified column.
55859      * @param {Number} col The column index
55860      * @return {Number}
55861      */
55862     getDataIndex : function(col){
55863         return this.config[col].dataIndex;
55864     },
55865
55866     /**
55867      * Sets the dataIndex for a column.
55868      * @param {Number} col The column index
55869      * @param {Number} dataIndex The new dataIndex
55870      */
55871     setDataIndex : function(col, dataIndex){
55872         this.config[col].dataIndex = dataIndex;
55873     },
55874
55875     
55876     
55877     /**
55878      * Returns true if the cell is editable.
55879      * @param {Number} colIndex The column index
55880      * @param {Number} rowIndex The row index - this is nto actually used..?
55881      * @return {Boolean}
55882      */
55883     isCellEditable : function(colIndex, rowIndex){
55884         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55885     },
55886
55887     /**
55888      * Returns the editor defined for the cell/column.
55889      * return false or null to disable editing.
55890      * @param {Number} colIndex The column index
55891      * @param {Number} rowIndex The row index
55892      * @return {Object}
55893      */
55894     getCellEditor : function(colIndex, rowIndex){
55895         return this.config[colIndex].editor;
55896     },
55897
55898     /**
55899      * Sets if a column is editable.
55900      * @param {Number} col The column index
55901      * @param {Boolean} editable True if the column is editable
55902      */
55903     setEditable : function(col, editable){
55904         this.config[col].editable = editable;
55905     },
55906
55907
55908     /**
55909      * Returns true if the column is hidden.
55910      * @param {Number} colIndex The column index
55911      * @return {Boolean}
55912      */
55913     isHidden : function(colIndex){
55914         return this.config[colIndex].hidden;
55915     },
55916
55917
55918     /**
55919      * Returns true if the column width cannot be changed
55920      */
55921     isFixed : function(colIndex){
55922         return this.config[colIndex].fixed;
55923     },
55924
55925     /**
55926      * Returns true if the column can be resized
55927      * @return {Boolean}
55928      */
55929     isResizable : function(colIndex){
55930         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55931     },
55932     /**
55933      * Sets if a column is hidden.
55934      * @param {Number} colIndex The column index
55935      * @param {Boolean} hidden True if the column is hidden
55936      */
55937     setHidden : function(colIndex, hidden){
55938         this.config[colIndex].hidden = hidden;
55939         this.totalWidth = null;
55940         this.fireEvent("hiddenchange", this, colIndex, hidden);
55941     },
55942
55943     /**
55944      * Sets the editor for a column.
55945      * @param {Number} col The column index
55946      * @param {Object} editor The editor object
55947      */
55948     setEditor : function(col, editor){
55949         this.config[col].editor = editor;
55950     }
55951 });
55952
55953 Roo.grid.ColumnModel.defaultRenderer = function(value){
55954         if(typeof value == "string" && value.length < 1){
55955             return "&#160;";
55956         }
55957         return value;
55958 };
55959
55960 // Alias for backwards compatibility
55961 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55962 /*
55963  * Based on:
55964  * Ext JS Library 1.1.1
55965  * Copyright(c) 2006-2007, Ext JS, LLC.
55966  *
55967  * Originally Released Under LGPL - original licence link has changed is not relivant.
55968  *
55969  * Fork - LGPL
55970  * <script type="text/javascript">
55971  */
55972
55973 /**
55974  * @class Roo.grid.AbstractSelectionModel
55975  * @extends Roo.util.Observable
55976  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55977  * implemented by descendant classes.  This class should not be directly instantiated.
55978  * @constructor
55979  */
55980 Roo.grid.AbstractSelectionModel = function(){
55981     this.locked = false;
55982     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55983 };
55984
55985 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55986     /** @ignore Called by the grid automatically. Do not call directly. */
55987     init : function(grid){
55988         this.grid = grid;
55989         this.initEvents();
55990     },
55991
55992     /**
55993      * Locks the selections.
55994      */
55995     lock : function(){
55996         this.locked = true;
55997     },
55998
55999     /**
56000      * Unlocks the selections.
56001      */
56002     unlock : function(){
56003         this.locked = false;
56004     },
56005
56006     /**
56007      * Returns true if the selections are locked.
56008      * @return {Boolean}
56009      */
56010     isLocked : function(){
56011         return this.locked;
56012     }
56013 });/*
56014  * Based on:
56015  * Ext JS Library 1.1.1
56016  * Copyright(c) 2006-2007, Ext JS, LLC.
56017  *
56018  * Originally Released Under LGPL - original licence link has changed is not relivant.
56019  *
56020  * Fork - LGPL
56021  * <script type="text/javascript">
56022  */
56023 /**
56024  * @extends Roo.grid.AbstractSelectionModel
56025  * @class Roo.grid.RowSelectionModel
56026  * The default SelectionModel used by {@link Roo.grid.Grid}.
56027  * It supports multiple selections and keyboard selection/navigation. 
56028  * @constructor
56029  * @param {Object} config
56030  */
56031 Roo.grid.RowSelectionModel = function(config){
56032     Roo.apply(this, config);
56033     this.selections = new Roo.util.MixedCollection(false, function(o){
56034         return o.id;
56035     });
56036
56037     this.last = false;
56038     this.lastActive = false;
56039
56040     this.addEvents({
56041         /**
56042              * @event selectionchange
56043              * Fires when the selection changes
56044              * @param {SelectionModel} this
56045              */
56046             "selectionchange" : true,
56047         /**
56048              * @event afterselectionchange
56049              * Fires after the selection changes (eg. by key press or clicking)
56050              * @param {SelectionModel} this
56051              */
56052             "afterselectionchange" : true,
56053         /**
56054              * @event beforerowselect
56055              * Fires when a row is selected being selected, return false to cancel.
56056              * @param {SelectionModel} this
56057              * @param {Number} rowIndex The selected index
56058              * @param {Boolean} keepExisting False if other selections will be cleared
56059              */
56060             "beforerowselect" : true,
56061         /**
56062              * @event rowselect
56063              * Fires when a row is selected.
56064              * @param {SelectionModel} this
56065              * @param {Number} rowIndex The selected index
56066              * @param {Roo.data.Record} r The record
56067              */
56068             "rowselect" : true,
56069         /**
56070              * @event rowdeselect
56071              * Fires when a row is deselected.
56072              * @param {SelectionModel} this
56073              * @param {Number} rowIndex The selected index
56074              */
56075         "rowdeselect" : true
56076     });
56077     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
56078     this.locked = false;
56079 };
56080
56081 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
56082     /**
56083      * @cfg {Boolean} singleSelect
56084      * True to allow selection of only one row at a time (defaults to false)
56085      */
56086     singleSelect : false,
56087
56088     // private
56089     initEvents : function(){
56090
56091         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
56092             this.grid.on("mousedown", this.handleMouseDown, this);
56093         }else{ // allow click to work like normal
56094             this.grid.on("rowclick", this.handleDragableRowClick, this);
56095         }
56096
56097         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
56098             "up" : function(e){
56099                 if(!e.shiftKey){
56100                     this.selectPrevious(e.shiftKey);
56101                 }else if(this.last !== false && this.lastActive !== false){
56102                     var last = this.last;
56103                     this.selectRange(this.last,  this.lastActive-1);
56104                     this.grid.getView().focusRow(this.lastActive);
56105                     if(last !== false){
56106                         this.last = last;
56107                     }
56108                 }else{
56109                     this.selectFirstRow();
56110                 }
56111                 this.fireEvent("afterselectionchange", this);
56112             },
56113             "down" : function(e){
56114                 if(!e.shiftKey){
56115                     this.selectNext(e.shiftKey);
56116                 }else if(this.last !== false && this.lastActive !== false){
56117                     var last = this.last;
56118                     this.selectRange(this.last,  this.lastActive+1);
56119                     this.grid.getView().focusRow(this.lastActive);
56120                     if(last !== false){
56121                         this.last = last;
56122                     }
56123                 }else{
56124                     this.selectFirstRow();
56125                 }
56126                 this.fireEvent("afterselectionchange", this);
56127             },
56128             scope: this
56129         });
56130
56131         var view = this.grid.view;
56132         view.on("refresh", this.onRefresh, this);
56133         view.on("rowupdated", this.onRowUpdated, this);
56134         view.on("rowremoved", this.onRemove, this);
56135     },
56136
56137     // private
56138     onRefresh : function(){
56139         var ds = this.grid.dataSource, i, v = this.grid.view;
56140         var s = this.selections;
56141         s.each(function(r){
56142             if((i = ds.indexOfId(r.id)) != -1){
56143                 v.onRowSelect(i);
56144                 s.add(ds.getAt(i)); // updating the selection relate data
56145             }else{
56146                 s.remove(r);
56147             }
56148         });
56149     },
56150
56151     // private
56152     onRemove : function(v, index, r){
56153         this.selections.remove(r);
56154     },
56155
56156     // private
56157     onRowUpdated : function(v, index, r){
56158         if(this.isSelected(r)){
56159             v.onRowSelect(index);
56160         }
56161     },
56162
56163     /**
56164      * Select records.
56165      * @param {Array} records The records to select
56166      * @param {Boolean} keepExisting (optional) True to keep existing selections
56167      */
56168     selectRecords : function(records, keepExisting){
56169         if(!keepExisting){
56170             this.clearSelections();
56171         }
56172         var ds = this.grid.dataSource;
56173         for(var i = 0, len = records.length; i < len; i++){
56174             this.selectRow(ds.indexOf(records[i]), true);
56175         }
56176     },
56177
56178     /**
56179      * Gets the number of selected rows.
56180      * @return {Number}
56181      */
56182     getCount : function(){
56183         return this.selections.length;
56184     },
56185
56186     /**
56187      * Selects the first row in the grid.
56188      */
56189     selectFirstRow : function(){
56190         this.selectRow(0);
56191     },
56192
56193     /**
56194      * Select the last row.
56195      * @param {Boolean} keepExisting (optional) True to keep existing selections
56196      */
56197     selectLastRow : function(keepExisting){
56198         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
56199     },
56200
56201     /**
56202      * Selects the row immediately following the last selected row.
56203      * @param {Boolean} keepExisting (optional) True to keep existing selections
56204      */
56205     selectNext : function(keepExisting){
56206         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
56207             this.selectRow(this.last+1, keepExisting);
56208             this.grid.getView().focusRow(this.last);
56209         }
56210     },
56211
56212     /**
56213      * Selects the row that precedes the last selected row.
56214      * @param {Boolean} keepExisting (optional) True to keep existing selections
56215      */
56216     selectPrevious : function(keepExisting){
56217         if(this.last){
56218             this.selectRow(this.last-1, keepExisting);
56219             this.grid.getView().focusRow(this.last);
56220         }
56221     },
56222
56223     /**
56224      * Returns the selected records
56225      * @return {Array} Array of selected records
56226      */
56227     getSelections : function(){
56228         return [].concat(this.selections.items);
56229     },
56230
56231     /**
56232      * Returns the first selected record.
56233      * @return {Record}
56234      */
56235     getSelected : function(){
56236         return this.selections.itemAt(0);
56237     },
56238
56239
56240     /**
56241      * Clears all selections.
56242      */
56243     clearSelections : function(fast){
56244         if(this.locked) {
56245             return;
56246         }
56247         if(fast !== true){
56248             var ds = this.grid.dataSource;
56249             var s = this.selections;
56250             s.each(function(r){
56251                 this.deselectRow(ds.indexOfId(r.id));
56252             }, this);
56253             s.clear();
56254         }else{
56255             this.selections.clear();
56256         }
56257         this.last = false;
56258     },
56259
56260
56261     /**
56262      * Selects all rows.
56263      */
56264     selectAll : function(){
56265         if(this.locked) {
56266             return;
56267         }
56268         this.selections.clear();
56269         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56270             this.selectRow(i, true);
56271         }
56272     },
56273
56274     /**
56275      * Returns True if there is a selection.
56276      * @return {Boolean}
56277      */
56278     hasSelection : function(){
56279         return this.selections.length > 0;
56280     },
56281
56282     /**
56283      * Returns True if the specified row is selected.
56284      * @param {Number/Record} record The record or index of the record to check
56285      * @return {Boolean}
56286      */
56287     isSelected : function(index){
56288         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56289         return (r && this.selections.key(r.id) ? true : false);
56290     },
56291
56292     /**
56293      * Returns True if the specified record id is selected.
56294      * @param {String} id The id of record to check
56295      * @return {Boolean}
56296      */
56297     isIdSelected : function(id){
56298         return (this.selections.key(id) ? true : false);
56299     },
56300
56301     // private
56302     handleMouseDown : function(e, t){
56303         var view = this.grid.getView(), rowIndex;
56304         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56305             return;
56306         };
56307         if(e.shiftKey && this.last !== false){
56308             var last = this.last;
56309             this.selectRange(last, rowIndex, e.ctrlKey);
56310             this.last = last; // reset the last
56311             view.focusRow(rowIndex);
56312         }else{
56313             var isSelected = this.isSelected(rowIndex);
56314             if(e.button !== 0 && isSelected){
56315                 view.focusRow(rowIndex);
56316             }else if(e.ctrlKey && isSelected){
56317                 this.deselectRow(rowIndex);
56318             }else if(!isSelected){
56319                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56320                 view.focusRow(rowIndex);
56321             }
56322         }
56323         this.fireEvent("afterselectionchange", this);
56324     },
56325     // private
56326     handleDragableRowClick :  function(grid, rowIndex, e) 
56327     {
56328         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56329             this.selectRow(rowIndex, false);
56330             grid.view.focusRow(rowIndex);
56331              this.fireEvent("afterselectionchange", this);
56332         }
56333     },
56334     
56335     /**
56336      * Selects multiple rows.
56337      * @param {Array} rows Array of the indexes of the row to select
56338      * @param {Boolean} keepExisting (optional) True to keep existing selections
56339      */
56340     selectRows : function(rows, keepExisting){
56341         if(!keepExisting){
56342             this.clearSelections();
56343         }
56344         for(var i = 0, len = rows.length; i < len; i++){
56345             this.selectRow(rows[i], true);
56346         }
56347     },
56348
56349     /**
56350      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56351      * @param {Number} startRow The index of the first row in the range
56352      * @param {Number} endRow The index of the last row in the range
56353      * @param {Boolean} keepExisting (optional) True to retain existing selections
56354      */
56355     selectRange : function(startRow, endRow, keepExisting){
56356         if(this.locked) {
56357             return;
56358         }
56359         if(!keepExisting){
56360             this.clearSelections();
56361         }
56362         if(startRow <= endRow){
56363             for(var i = startRow; i <= endRow; i++){
56364                 this.selectRow(i, true);
56365             }
56366         }else{
56367             for(var i = startRow; i >= endRow; i--){
56368                 this.selectRow(i, true);
56369             }
56370         }
56371     },
56372
56373     /**
56374      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56375      * @param {Number} startRow The index of the first row in the range
56376      * @param {Number} endRow The index of the last row in the range
56377      */
56378     deselectRange : function(startRow, endRow, preventViewNotify){
56379         if(this.locked) {
56380             return;
56381         }
56382         for(var i = startRow; i <= endRow; i++){
56383             this.deselectRow(i, preventViewNotify);
56384         }
56385     },
56386
56387     /**
56388      * Selects a row.
56389      * @param {Number} row The index of the row to select
56390      * @param {Boolean} keepExisting (optional) True to keep existing selections
56391      */
56392     selectRow : function(index, keepExisting, preventViewNotify){
56393         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
56394             return;
56395         }
56396         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56397             if(!keepExisting || this.singleSelect){
56398                 this.clearSelections();
56399             }
56400             var r = this.grid.dataSource.getAt(index);
56401             this.selections.add(r);
56402             this.last = this.lastActive = index;
56403             if(!preventViewNotify){
56404                 this.grid.getView().onRowSelect(index);
56405             }
56406             this.fireEvent("rowselect", this, index, r);
56407             this.fireEvent("selectionchange", this);
56408         }
56409     },
56410
56411     /**
56412      * Deselects a row.
56413      * @param {Number} row The index of the row to deselect
56414      */
56415     deselectRow : function(index, preventViewNotify){
56416         if(this.locked) {
56417             return;
56418         }
56419         if(this.last == index){
56420             this.last = false;
56421         }
56422         if(this.lastActive == index){
56423             this.lastActive = false;
56424         }
56425         var r = this.grid.dataSource.getAt(index);
56426         this.selections.remove(r);
56427         if(!preventViewNotify){
56428             this.grid.getView().onRowDeselect(index);
56429         }
56430         this.fireEvent("rowdeselect", this, index);
56431         this.fireEvent("selectionchange", this);
56432     },
56433
56434     // private
56435     restoreLast : function(){
56436         if(this._last){
56437             this.last = this._last;
56438         }
56439     },
56440
56441     // private
56442     acceptsNav : function(row, col, cm){
56443         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56444     },
56445
56446     // private
56447     onEditorKey : function(field, e){
56448         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56449         if(k == e.TAB){
56450             e.stopEvent();
56451             ed.completeEdit();
56452             if(e.shiftKey){
56453                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56454             }else{
56455                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56456             }
56457         }else if(k == e.ENTER && !e.ctrlKey){
56458             e.stopEvent();
56459             ed.completeEdit();
56460             if(e.shiftKey){
56461                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56462             }else{
56463                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56464             }
56465         }else if(k == e.ESC){
56466             ed.cancelEdit();
56467         }
56468         if(newCell){
56469             g.startEditing(newCell[0], newCell[1]);
56470         }
56471     }
56472 });/*
56473  * Based on:
56474  * Ext JS Library 1.1.1
56475  * Copyright(c) 2006-2007, Ext JS, LLC.
56476  *
56477  * Originally Released Under LGPL - original licence link has changed is not relivant.
56478  *
56479  * Fork - LGPL
56480  * <script type="text/javascript">
56481  */
56482 /**
56483  * @class Roo.grid.CellSelectionModel
56484  * @extends Roo.grid.AbstractSelectionModel
56485  * This class provides the basic implementation for cell selection in a grid.
56486  * @constructor
56487  * @param {Object} config The object containing the configuration of this model.
56488  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56489  */
56490 Roo.grid.CellSelectionModel = function(config){
56491     Roo.apply(this, config);
56492
56493     this.selection = null;
56494
56495     this.addEvents({
56496         /**
56497              * @event beforerowselect
56498              * Fires before a cell is selected.
56499              * @param {SelectionModel} this
56500              * @param {Number} rowIndex The selected row index
56501              * @param {Number} colIndex The selected cell index
56502              */
56503             "beforecellselect" : true,
56504         /**
56505              * @event cellselect
56506              * Fires when a cell is selected.
56507              * @param {SelectionModel} this
56508              * @param {Number} rowIndex The selected row index
56509              * @param {Number} colIndex The selected cell index
56510              */
56511             "cellselect" : true,
56512         /**
56513              * @event selectionchange
56514              * Fires when the active selection changes.
56515              * @param {SelectionModel} this
56516              * @param {Object} selection null for no selection or an object (o) with two properties
56517                 <ul>
56518                 <li>o.record: the record object for the row the selection is in</li>
56519                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56520                 </ul>
56521              */
56522             "selectionchange" : true,
56523         /**
56524              * @event tabend
56525              * Fires when the tab (or enter) was pressed on the last editable cell
56526              * You can use this to trigger add new row.
56527              * @param {SelectionModel} this
56528              */
56529             "tabend" : true,
56530          /**
56531              * @event beforeeditnext
56532              * Fires before the next editable sell is made active
56533              * You can use this to skip to another cell or fire the tabend
56534              *    if you set cell to false
56535              * @param {Object} eventdata object : { cell : [ row, col ] } 
56536              */
56537             "beforeeditnext" : true
56538     });
56539     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56540 };
56541
56542 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56543     
56544     enter_is_tab: false,
56545
56546     /** @ignore */
56547     initEvents : function(){
56548         this.grid.on("mousedown", this.handleMouseDown, this);
56549         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56550         var view = this.grid.view;
56551         view.on("refresh", this.onViewChange, this);
56552         view.on("rowupdated", this.onRowUpdated, this);
56553         view.on("beforerowremoved", this.clearSelections, this);
56554         view.on("beforerowsinserted", this.clearSelections, this);
56555         if(this.grid.isEditor){
56556             this.grid.on("beforeedit", this.beforeEdit,  this);
56557         }
56558     },
56559
56560         //private
56561     beforeEdit : function(e){
56562         this.select(e.row, e.column, false, true, e.record);
56563     },
56564
56565         //private
56566     onRowUpdated : function(v, index, r){
56567         if(this.selection && this.selection.record == r){
56568             v.onCellSelect(index, this.selection.cell[1]);
56569         }
56570     },
56571
56572         //private
56573     onViewChange : function(){
56574         this.clearSelections(true);
56575     },
56576
56577         /**
56578          * Returns the currently selected cell,.
56579          * @return {Array} The selected cell (row, column) or null if none selected.
56580          */
56581     getSelectedCell : function(){
56582         return this.selection ? this.selection.cell : null;
56583     },
56584
56585     /**
56586      * Clears all selections.
56587      * @param {Boolean} true to prevent the gridview from being notified about the change.
56588      */
56589     clearSelections : function(preventNotify){
56590         var s = this.selection;
56591         if(s){
56592             if(preventNotify !== true){
56593                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56594             }
56595             this.selection = null;
56596             this.fireEvent("selectionchange", this, null);
56597         }
56598     },
56599
56600     /**
56601      * Returns true if there is a selection.
56602      * @return {Boolean}
56603      */
56604     hasSelection : function(){
56605         return this.selection ? true : false;
56606     },
56607
56608     /** @ignore */
56609     handleMouseDown : function(e, t){
56610         var v = this.grid.getView();
56611         if(this.isLocked()){
56612             return;
56613         };
56614         var row = v.findRowIndex(t);
56615         var cell = v.findCellIndex(t);
56616         if(row !== false && cell !== false){
56617             this.select(row, cell);
56618         }
56619     },
56620
56621     /**
56622      * Selects a cell.
56623      * @param {Number} rowIndex
56624      * @param {Number} collIndex
56625      */
56626     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56627         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56628             this.clearSelections();
56629             r = r || this.grid.dataSource.getAt(rowIndex);
56630             this.selection = {
56631                 record : r,
56632                 cell : [rowIndex, colIndex]
56633             };
56634             if(!preventViewNotify){
56635                 var v = this.grid.getView();
56636                 v.onCellSelect(rowIndex, colIndex);
56637                 if(preventFocus !== true){
56638                     v.focusCell(rowIndex, colIndex);
56639                 }
56640             }
56641             this.fireEvent("cellselect", this, rowIndex, colIndex);
56642             this.fireEvent("selectionchange", this, this.selection);
56643         }
56644     },
56645
56646         //private
56647     isSelectable : function(rowIndex, colIndex, cm){
56648         return !cm.isHidden(colIndex);
56649     },
56650
56651     /** @ignore */
56652     handleKeyDown : function(e){
56653         //Roo.log('Cell Sel Model handleKeyDown');
56654         if(!e.isNavKeyPress()){
56655             return;
56656         }
56657         var g = this.grid, s = this.selection;
56658         if(!s){
56659             e.stopEvent();
56660             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56661             if(cell){
56662                 this.select(cell[0], cell[1]);
56663             }
56664             return;
56665         }
56666         var sm = this;
56667         var walk = function(row, col, step){
56668             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56669         };
56670         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56671         var newCell;
56672
56673       
56674
56675         switch(k){
56676             case e.TAB:
56677                 // handled by onEditorKey
56678                 if (g.isEditor && g.editing) {
56679                     return;
56680                 }
56681                 if(e.shiftKey) {
56682                     newCell = walk(r, c-1, -1);
56683                 } else {
56684                     newCell = walk(r, c+1, 1);
56685                 }
56686                 break;
56687             
56688             case e.DOWN:
56689                newCell = walk(r+1, c, 1);
56690                 break;
56691             
56692             case e.UP:
56693                 newCell = walk(r-1, c, -1);
56694                 break;
56695             
56696             case e.RIGHT:
56697                 newCell = walk(r, c+1, 1);
56698                 break;
56699             
56700             case e.LEFT:
56701                 newCell = walk(r, c-1, -1);
56702                 break;
56703             
56704             case e.ENTER:
56705                 
56706                 if(g.isEditor && !g.editing){
56707                    g.startEditing(r, c);
56708                    e.stopEvent();
56709                    return;
56710                 }
56711                 
56712                 
56713              break;
56714         };
56715         if(newCell){
56716             this.select(newCell[0], newCell[1]);
56717             e.stopEvent();
56718             
56719         }
56720     },
56721
56722     acceptsNav : function(row, col, cm){
56723         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56724     },
56725     /**
56726      * Selects a cell.
56727      * @param {Number} field (not used) - as it's normally used as a listener
56728      * @param {Number} e - event - fake it by using
56729      *
56730      * var e = Roo.EventObjectImpl.prototype;
56731      * e.keyCode = e.TAB
56732      *
56733      * 
56734      */
56735     onEditorKey : function(field, e){
56736         
56737         var k = e.getKey(),
56738             newCell,
56739             g = this.grid,
56740             ed = g.activeEditor,
56741             forward = false;
56742         ///Roo.log('onEditorKey' + k);
56743         
56744         
56745         if (this.enter_is_tab && k == e.ENTER) {
56746             k = e.TAB;
56747         }
56748         
56749         if(k == e.TAB){
56750             if(e.shiftKey){
56751                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56752             }else{
56753                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56754                 forward = true;
56755             }
56756             
56757             e.stopEvent();
56758             
56759         } else if(k == e.ENTER &&  !e.ctrlKey){
56760             ed.completeEdit();
56761             e.stopEvent();
56762             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56763         
56764                 } else if(k == e.ESC){
56765             ed.cancelEdit();
56766         }
56767                 
56768         if (newCell) {
56769             var ecall = { cell : newCell, forward : forward };
56770             this.fireEvent('beforeeditnext', ecall );
56771             newCell = ecall.cell;
56772                         forward = ecall.forward;
56773         }
56774                 
56775         if(newCell){
56776             //Roo.log('next cell after edit');
56777             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56778         } else if (forward) {
56779             // tabbed past last
56780             this.fireEvent.defer(100, this, ['tabend',this]);
56781         }
56782     }
56783 });/*
56784  * Based on:
56785  * Ext JS Library 1.1.1
56786  * Copyright(c) 2006-2007, Ext JS, LLC.
56787  *
56788  * Originally Released Under LGPL - original licence link has changed is not relivant.
56789  *
56790  * Fork - LGPL
56791  * <script type="text/javascript">
56792  */
56793  
56794 /**
56795  * @class Roo.grid.EditorGrid
56796  * @extends Roo.grid.Grid
56797  * Class for creating and editable grid.
56798  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56799  * The container MUST have some type of size defined for the grid to fill. The container will be 
56800  * automatically set to position relative if it isn't already.
56801  * @param {Object} dataSource The data model to bind to
56802  * @param {Object} colModel The column model with info about this grid's columns
56803  */
56804 Roo.grid.EditorGrid = function(container, config){
56805     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56806     this.getGridEl().addClass("xedit-grid");
56807
56808     if(!this.selModel){
56809         this.selModel = new Roo.grid.CellSelectionModel();
56810     }
56811
56812     this.activeEditor = null;
56813
56814         this.addEvents({
56815             /**
56816              * @event beforeedit
56817              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56818              * <ul style="padding:5px;padding-left:16px;">
56819              * <li>grid - This grid</li>
56820              * <li>record - The record being edited</li>
56821              * <li>field - The field name being edited</li>
56822              * <li>value - The value for the field being edited.</li>
56823              * <li>row - The grid row index</li>
56824              * <li>column - The grid column index</li>
56825              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56826              * </ul>
56827              * @param {Object} e An edit event (see above for description)
56828              */
56829             "beforeedit" : true,
56830             /**
56831              * @event afteredit
56832              * Fires after a cell is edited. <br />
56833              * <ul style="padding:5px;padding-left:16px;">
56834              * <li>grid - This grid</li>
56835              * <li>record - The record being edited</li>
56836              * <li>field - The field name being edited</li>
56837              * <li>value - The value being set</li>
56838              * <li>originalValue - The original value for the field, before the edit.</li>
56839              * <li>row - The grid row index</li>
56840              * <li>column - The grid column index</li>
56841              * </ul>
56842              * @param {Object} e An edit event (see above for description)
56843              */
56844             "afteredit" : true,
56845             /**
56846              * @event validateedit
56847              * Fires after a cell is edited, but before the value is set in the record. 
56848          * You can use this to modify the value being set in the field, Return false
56849              * to cancel the change. The edit event object has the following properties <br />
56850              * <ul style="padding:5px;padding-left:16px;">
56851          * <li>editor - This editor</li>
56852              * <li>grid - This grid</li>
56853              * <li>record - The record being edited</li>
56854              * <li>field - The field name being edited</li>
56855              * <li>value - The value being set</li>
56856              * <li>originalValue - The original value for the field, before the edit.</li>
56857              * <li>row - The grid row index</li>
56858              * <li>column - The grid column index</li>
56859              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56860              * </ul>
56861              * @param {Object} e An edit event (see above for description)
56862              */
56863             "validateedit" : true
56864         });
56865     this.on("bodyscroll", this.stopEditing,  this);
56866     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56867 };
56868
56869 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56870     /**
56871      * @cfg {Number} clicksToEdit
56872      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56873      */
56874     clicksToEdit: 2,
56875
56876     // private
56877     isEditor : true,
56878     // private
56879     trackMouseOver: false, // causes very odd FF errors
56880
56881     onCellDblClick : function(g, row, col){
56882         this.startEditing(row, col);
56883     },
56884
56885     onEditComplete : function(ed, value, startValue){
56886         this.editing = false;
56887         this.activeEditor = null;
56888         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56889         var r = ed.record;
56890         var field = this.colModel.getDataIndex(ed.col);
56891         var e = {
56892             grid: this,
56893             record: r,
56894             field: field,
56895             originalValue: startValue,
56896             value: value,
56897             row: ed.row,
56898             column: ed.col,
56899             cancel:false,
56900             editor: ed
56901         };
56902         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
56903         cell.show();
56904           
56905         if(String(value) !== String(startValue)){
56906             
56907             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56908                 r.set(field, e.value);
56909                 // if we are dealing with a combo box..
56910                 // then we also set the 'name' colum to be the displayField
56911                 if (ed.field.displayField && ed.field.name) {
56912                     r.set(ed.field.name, ed.field.el.dom.value);
56913                 }
56914                 
56915                 delete e.cancel; //?? why!!!
56916                 this.fireEvent("afteredit", e);
56917             }
56918         } else {
56919             this.fireEvent("afteredit", e); // always fire it!
56920         }
56921         this.view.focusCell(ed.row, ed.col);
56922     },
56923
56924     /**
56925      * Starts editing the specified for the specified row/column
56926      * @param {Number} rowIndex
56927      * @param {Number} colIndex
56928      */
56929     startEditing : function(row, col){
56930         this.stopEditing();
56931         if(this.colModel.isCellEditable(col, row)){
56932             this.view.ensureVisible(row, col, true);
56933           
56934             var r = this.dataSource.getAt(row);
56935             var field = this.colModel.getDataIndex(col);
56936             var cell = Roo.get(this.view.getCell(row,col));
56937             var e = {
56938                 grid: this,
56939                 record: r,
56940                 field: field,
56941                 value: r.data[field],
56942                 row: row,
56943                 column: col,
56944                 cancel:false 
56945             };
56946             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56947                 this.editing = true;
56948                 var ed = this.colModel.getCellEditor(col, row);
56949                 
56950                 if (!ed) {
56951                     return;
56952                 }
56953                 if(!ed.rendered){
56954                     ed.render(ed.parentEl || document.body);
56955                 }
56956                 ed.field.reset();
56957                
56958                 cell.hide();
56959                 
56960                 (function(){ // complex but required for focus issues in safari, ie and opera
56961                     ed.row = row;
56962                     ed.col = col;
56963                     ed.record = r;
56964                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56965                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56966                     this.activeEditor = ed;
56967                     var v = r.data[field];
56968                     ed.startEdit(this.view.getCell(row, col), v);
56969                     // combo's with 'displayField and name set
56970                     if (ed.field.displayField && ed.field.name) {
56971                         ed.field.el.dom.value = r.data[ed.field.name];
56972                     }
56973                     
56974                     
56975                 }).defer(50, this);
56976             }
56977         }
56978     },
56979         
56980     /**
56981      * Stops any active editing
56982      */
56983     stopEditing : function(){
56984         if(this.activeEditor){
56985             this.activeEditor.completeEdit();
56986         }
56987         this.activeEditor = null;
56988     },
56989         
56990          /**
56991      * Called to get grid's drag proxy text, by default returns this.ddText.
56992      * @return {String}
56993      */
56994     getDragDropText : function(){
56995         var count = this.selModel.getSelectedCell() ? 1 : 0;
56996         return String.format(this.ddText, count, count == 1 ? '' : 's');
56997     }
56998         
56999 });/*
57000  * Based on:
57001  * Ext JS Library 1.1.1
57002  * Copyright(c) 2006-2007, Ext JS, LLC.
57003  *
57004  * Originally Released Under LGPL - original licence link has changed is not relivant.
57005  *
57006  * Fork - LGPL
57007  * <script type="text/javascript">
57008  */
57009
57010 // private - not really -- you end up using it !
57011 // This is a support class used internally by the Grid components
57012
57013 /**
57014  * @class Roo.grid.GridEditor
57015  * @extends Roo.Editor
57016  * Class for creating and editable grid elements.
57017  * @param {Object} config any settings (must include field)
57018  */
57019 Roo.grid.GridEditor = function(field, config){
57020     if (!config && field.field) {
57021         config = field;
57022         field = Roo.factory(config.field, Roo.form);
57023     }
57024     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
57025     field.monitorTab = false;
57026 };
57027
57028 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
57029     
57030     /**
57031      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
57032      */
57033     
57034     alignment: "tl-tl",
57035     autoSize: "width",
57036     hideEl : false,
57037     cls: "x-small-editor x-grid-editor",
57038     shim:false,
57039     shadow:"frame"
57040 });/*
57041  * Based on:
57042  * Ext JS Library 1.1.1
57043  * Copyright(c) 2006-2007, Ext JS, LLC.
57044  *
57045  * Originally Released Under LGPL - original licence link has changed is not relivant.
57046  *
57047  * Fork - LGPL
57048  * <script type="text/javascript">
57049  */
57050   
57051
57052   
57053 Roo.grid.PropertyRecord = Roo.data.Record.create([
57054     {name:'name',type:'string'},  'value'
57055 ]);
57056
57057
57058 Roo.grid.PropertyStore = function(grid, source){
57059     this.grid = grid;
57060     this.store = new Roo.data.Store({
57061         recordType : Roo.grid.PropertyRecord
57062     });
57063     this.store.on('update', this.onUpdate,  this);
57064     if(source){
57065         this.setSource(source);
57066     }
57067     Roo.grid.PropertyStore.superclass.constructor.call(this);
57068 };
57069
57070
57071
57072 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
57073     setSource : function(o){
57074         this.source = o;
57075         this.store.removeAll();
57076         var data = [];
57077         for(var k in o){
57078             if(this.isEditableValue(o[k])){
57079                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
57080             }
57081         }
57082         this.store.loadRecords({records: data}, {}, true);
57083     },
57084
57085     onUpdate : function(ds, record, type){
57086         if(type == Roo.data.Record.EDIT){
57087             var v = record.data['value'];
57088             var oldValue = record.modified['value'];
57089             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
57090                 this.source[record.id] = v;
57091                 record.commit();
57092                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
57093             }else{
57094                 record.reject();
57095             }
57096         }
57097     },
57098
57099     getProperty : function(row){
57100        return this.store.getAt(row);
57101     },
57102
57103     isEditableValue: function(val){
57104         if(val && val instanceof Date){
57105             return true;
57106         }else if(typeof val == 'object' || typeof val == 'function'){
57107             return false;
57108         }
57109         return true;
57110     },
57111
57112     setValue : function(prop, value){
57113         this.source[prop] = value;
57114         this.store.getById(prop).set('value', value);
57115     },
57116
57117     getSource : function(){
57118         return this.source;
57119     }
57120 });
57121
57122 Roo.grid.PropertyColumnModel = function(grid, store){
57123     this.grid = grid;
57124     var g = Roo.grid;
57125     g.PropertyColumnModel.superclass.constructor.call(this, [
57126         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
57127         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
57128     ]);
57129     this.store = store;
57130     this.bselect = Roo.DomHelper.append(document.body, {
57131         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
57132             {tag: 'option', value: 'true', html: 'true'},
57133             {tag: 'option', value: 'false', html: 'false'}
57134         ]
57135     });
57136     Roo.id(this.bselect);
57137     var f = Roo.form;
57138     this.editors = {
57139         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
57140         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
57141         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
57142         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
57143         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
57144     };
57145     this.renderCellDelegate = this.renderCell.createDelegate(this);
57146     this.renderPropDelegate = this.renderProp.createDelegate(this);
57147 };
57148
57149 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
57150     
57151     
57152     nameText : 'Name',
57153     valueText : 'Value',
57154     
57155     dateFormat : 'm/j/Y',
57156     
57157     
57158     renderDate : function(dateVal){
57159         return dateVal.dateFormat(this.dateFormat);
57160     },
57161
57162     renderBool : function(bVal){
57163         return bVal ? 'true' : 'false';
57164     },
57165
57166     isCellEditable : function(colIndex, rowIndex){
57167         return colIndex == 1;
57168     },
57169
57170     getRenderer : function(col){
57171         return col == 1 ?
57172             this.renderCellDelegate : this.renderPropDelegate;
57173     },
57174
57175     renderProp : function(v){
57176         return this.getPropertyName(v);
57177     },
57178
57179     renderCell : function(val){
57180         var rv = val;
57181         if(val instanceof Date){
57182             rv = this.renderDate(val);
57183         }else if(typeof val == 'boolean'){
57184             rv = this.renderBool(val);
57185         }
57186         return Roo.util.Format.htmlEncode(rv);
57187     },
57188
57189     getPropertyName : function(name){
57190         var pn = this.grid.propertyNames;
57191         return pn && pn[name] ? pn[name] : name;
57192     },
57193
57194     getCellEditor : function(colIndex, rowIndex){
57195         var p = this.store.getProperty(rowIndex);
57196         var n = p.data['name'], val = p.data['value'];
57197         
57198         if(typeof(this.grid.customEditors[n]) == 'string'){
57199             return this.editors[this.grid.customEditors[n]];
57200         }
57201         if(typeof(this.grid.customEditors[n]) != 'undefined'){
57202             return this.grid.customEditors[n];
57203         }
57204         if(val instanceof Date){
57205             return this.editors['date'];
57206         }else if(typeof val == 'number'){
57207             return this.editors['number'];
57208         }else if(typeof val == 'boolean'){
57209             return this.editors['boolean'];
57210         }else{
57211             return this.editors['string'];
57212         }
57213     }
57214 });
57215
57216 /**
57217  * @class Roo.grid.PropertyGrid
57218  * @extends Roo.grid.EditorGrid
57219  * This class represents the  interface of a component based property grid control.
57220  * <br><br>Usage:<pre><code>
57221  var grid = new Roo.grid.PropertyGrid("my-container-id", {
57222       
57223  });
57224  // set any options
57225  grid.render();
57226  * </code></pre>
57227   
57228  * @constructor
57229  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57230  * The container MUST have some type of size defined for the grid to fill. The container will be
57231  * automatically set to position relative if it isn't already.
57232  * @param {Object} config A config object that sets properties on this grid.
57233  */
57234 Roo.grid.PropertyGrid = function(container, config){
57235     config = config || {};
57236     var store = new Roo.grid.PropertyStore(this);
57237     this.store = store;
57238     var cm = new Roo.grid.PropertyColumnModel(this, store);
57239     store.store.sort('name', 'ASC');
57240     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
57241         ds: store.store,
57242         cm: cm,
57243         enableColLock:false,
57244         enableColumnMove:false,
57245         stripeRows:false,
57246         trackMouseOver: false,
57247         clicksToEdit:1
57248     }, config));
57249     this.getGridEl().addClass('x-props-grid');
57250     this.lastEditRow = null;
57251     this.on('columnresize', this.onColumnResize, this);
57252     this.addEvents({
57253          /**
57254              * @event beforepropertychange
57255              * Fires before a property changes (return false to stop?)
57256              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57257              * @param {String} id Record Id
57258              * @param {String} newval New Value
57259          * @param {String} oldval Old Value
57260              */
57261         "beforepropertychange": true,
57262         /**
57263              * @event propertychange
57264              * Fires after a property changes
57265              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57266              * @param {String} id Record Id
57267              * @param {String} newval New Value
57268          * @param {String} oldval Old Value
57269              */
57270         "propertychange": true
57271     });
57272     this.customEditors = this.customEditors || {};
57273 };
57274 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57275     
57276      /**
57277      * @cfg {Object} customEditors map of colnames=> custom editors.
57278      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57279      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57280      * false disables editing of the field.
57281          */
57282     
57283       /**
57284      * @cfg {Object} propertyNames map of property Names to their displayed value
57285          */
57286     
57287     render : function(){
57288         Roo.grid.PropertyGrid.superclass.render.call(this);
57289         this.autoSize.defer(100, this);
57290     },
57291
57292     autoSize : function(){
57293         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57294         if(this.view){
57295             this.view.fitColumns();
57296         }
57297     },
57298
57299     onColumnResize : function(){
57300         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57301         this.autoSize();
57302     },
57303     /**
57304      * Sets the data for the Grid
57305      * accepts a Key => Value object of all the elements avaiable.
57306      * @param {Object} data  to appear in grid.
57307      */
57308     setSource : function(source){
57309         this.store.setSource(source);
57310         //this.autoSize();
57311     },
57312     /**
57313      * Gets all the data from the grid.
57314      * @return {Object} data  data stored in grid
57315      */
57316     getSource : function(){
57317         return this.store.getSource();
57318     }
57319 });/*
57320   
57321  * Licence LGPL
57322  
57323  */
57324  
57325 /**
57326  * @class Roo.grid.Calendar
57327  * @extends Roo.util.Grid
57328  * This class extends the Grid to provide a calendar widget
57329  * <br><br>Usage:<pre><code>
57330  var grid = new Roo.grid.Calendar("my-container-id", {
57331      ds: myDataStore,
57332      cm: myColModel,
57333      selModel: mySelectionModel,
57334      autoSizeColumns: true,
57335      monitorWindowResize: false,
57336      trackMouseOver: true
57337      eventstore : real data store..
57338  });
57339  // set any options
57340  grid.render();
57341   
57342   * @constructor
57343  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57344  * The container MUST have some type of size defined for the grid to fill. The container will be
57345  * automatically set to position relative if it isn't already.
57346  * @param {Object} config A config object that sets properties on this grid.
57347  */
57348 Roo.grid.Calendar = function(container, config){
57349         // initialize the container
57350         this.container = Roo.get(container);
57351         this.container.update("");
57352         this.container.setStyle("overflow", "hidden");
57353     this.container.addClass('x-grid-container');
57354
57355     this.id = this.container.id;
57356
57357     Roo.apply(this, config);
57358     // check and correct shorthanded configs
57359     
57360     var rows = [];
57361     var d =1;
57362     for (var r = 0;r < 6;r++) {
57363         
57364         rows[r]=[];
57365         for (var c =0;c < 7;c++) {
57366             rows[r][c]= '';
57367         }
57368     }
57369     if (this.eventStore) {
57370         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57371         this.eventStore.on('load',this.onLoad, this);
57372         this.eventStore.on('beforeload',this.clearEvents, this);
57373          
57374     }
57375     
57376     this.dataSource = new Roo.data.Store({
57377             proxy: new Roo.data.MemoryProxy(rows),
57378             reader: new Roo.data.ArrayReader({}, [
57379                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57380     });
57381
57382     this.dataSource.load();
57383     this.ds = this.dataSource;
57384     this.ds.xmodule = this.xmodule || false;
57385     
57386     
57387     var cellRender = function(v,x,r)
57388     {
57389         return String.format(
57390             '<div class="fc-day  fc-widget-content"><div>' +
57391                 '<div class="fc-event-container"></div>' +
57392                 '<div class="fc-day-number">{0}</div>'+
57393                 
57394                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57395             '</div></div>', v);
57396     
57397     }
57398     
57399     
57400     this.colModel = new Roo.grid.ColumnModel( [
57401         {
57402             xtype: 'ColumnModel',
57403             xns: Roo.grid,
57404             dataIndex : 'weekday0',
57405             header : 'Sunday',
57406             renderer : cellRender
57407         },
57408         {
57409             xtype: 'ColumnModel',
57410             xns: Roo.grid,
57411             dataIndex : 'weekday1',
57412             header : 'Monday',
57413             renderer : cellRender
57414         },
57415         {
57416             xtype: 'ColumnModel',
57417             xns: Roo.grid,
57418             dataIndex : 'weekday2',
57419             header : 'Tuesday',
57420             renderer : cellRender
57421         },
57422         {
57423             xtype: 'ColumnModel',
57424             xns: Roo.grid,
57425             dataIndex : 'weekday3',
57426             header : 'Wednesday',
57427             renderer : cellRender
57428         },
57429         {
57430             xtype: 'ColumnModel',
57431             xns: Roo.grid,
57432             dataIndex : 'weekday4',
57433             header : 'Thursday',
57434             renderer : cellRender
57435         },
57436         {
57437             xtype: 'ColumnModel',
57438             xns: Roo.grid,
57439             dataIndex : 'weekday5',
57440             header : 'Friday',
57441             renderer : cellRender
57442         },
57443         {
57444             xtype: 'ColumnModel',
57445             xns: Roo.grid,
57446             dataIndex : 'weekday6',
57447             header : 'Saturday',
57448             renderer : cellRender
57449         }
57450     ]);
57451     this.cm = this.colModel;
57452     this.cm.xmodule = this.xmodule || false;
57453  
57454         
57455           
57456     //this.selModel = new Roo.grid.CellSelectionModel();
57457     //this.sm = this.selModel;
57458     //this.selModel.init(this);
57459     
57460     
57461     if(this.width){
57462         this.container.setWidth(this.width);
57463     }
57464
57465     if(this.height){
57466         this.container.setHeight(this.height);
57467     }
57468     /** @private */
57469         this.addEvents({
57470         // raw events
57471         /**
57472          * @event click
57473          * The raw click event for the entire grid.
57474          * @param {Roo.EventObject} e
57475          */
57476         "click" : true,
57477         /**
57478          * @event dblclick
57479          * The raw dblclick event for the entire grid.
57480          * @param {Roo.EventObject} e
57481          */
57482         "dblclick" : true,
57483         /**
57484          * @event contextmenu
57485          * The raw contextmenu event for the entire grid.
57486          * @param {Roo.EventObject} e
57487          */
57488         "contextmenu" : true,
57489         /**
57490          * @event mousedown
57491          * The raw mousedown event for the entire grid.
57492          * @param {Roo.EventObject} e
57493          */
57494         "mousedown" : true,
57495         /**
57496          * @event mouseup
57497          * The raw mouseup event for the entire grid.
57498          * @param {Roo.EventObject} e
57499          */
57500         "mouseup" : true,
57501         /**
57502          * @event mouseover
57503          * The raw mouseover event for the entire grid.
57504          * @param {Roo.EventObject} e
57505          */
57506         "mouseover" : true,
57507         /**
57508          * @event mouseout
57509          * The raw mouseout event for the entire grid.
57510          * @param {Roo.EventObject} e
57511          */
57512         "mouseout" : true,
57513         /**
57514          * @event keypress
57515          * The raw keypress event for the entire grid.
57516          * @param {Roo.EventObject} e
57517          */
57518         "keypress" : true,
57519         /**
57520          * @event keydown
57521          * The raw keydown event for the entire grid.
57522          * @param {Roo.EventObject} e
57523          */
57524         "keydown" : true,
57525
57526         // custom events
57527
57528         /**
57529          * @event cellclick
57530          * Fires when a cell is clicked
57531          * @param {Grid} this
57532          * @param {Number} rowIndex
57533          * @param {Number} columnIndex
57534          * @param {Roo.EventObject} e
57535          */
57536         "cellclick" : true,
57537         /**
57538          * @event celldblclick
57539          * Fires when a cell is double clicked
57540          * @param {Grid} this
57541          * @param {Number} rowIndex
57542          * @param {Number} columnIndex
57543          * @param {Roo.EventObject} e
57544          */
57545         "celldblclick" : true,
57546         /**
57547          * @event rowclick
57548          * Fires when a row is clicked
57549          * @param {Grid} this
57550          * @param {Number} rowIndex
57551          * @param {Roo.EventObject} e
57552          */
57553         "rowclick" : true,
57554         /**
57555          * @event rowdblclick
57556          * Fires when a row is double clicked
57557          * @param {Grid} this
57558          * @param {Number} rowIndex
57559          * @param {Roo.EventObject} e
57560          */
57561         "rowdblclick" : true,
57562         /**
57563          * @event headerclick
57564          * Fires when a header is clicked
57565          * @param {Grid} this
57566          * @param {Number} columnIndex
57567          * @param {Roo.EventObject} e
57568          */
57569         "headerclick" : true,
57570         /**
57571          * @event headerdblclick
57572          * Fires when a header cell is double clicked
57573          * @param {Grid} this
57574          * @param {Number} columnIndex
57575          * @param {Roo.EventObject} e
57576          */
57577         "headerdblclick" : true,
57578         /**
57579          * @event rowcontextmenu
57580          * Fires when a row is right clicked
57581          * @param {Grid} this
57582          * @param {Number} rowIndex
57583          * @param {Roo.EventObject} e
57584          */
57585         "rowcontextmenu" : true,
57586         /**
57587          * @event cellcontextmenu
57588          * Fires when a cell is right clicked
57589          * @param {Grid} this
57590          * @param {Number} rowIndex
57591          * @param {Number} cellIndex
57592          * @param {Roo.EventObject} e
57593          */
57594          "cellcontextmenu" : true,
57595         /**
57596          * @event headercontextmenu
57597          * Fires when a header is right clicked
57598          * @param {Grid} this
57599          * @param {Number} columnIndex
57600          * @param {Roo.EventObject} e
57601          */
57602         "headercontextmenu" : true,
57603         /**
57604          * @event bodyscroll
57605          * Fires when the body element is scrolled
57606          * @param {Number} scrollLeft
57607          * @param {Number} scrollTop
57608          */
57609         "bodyscroll" : true,
57610         /**
57611          * @event columnresize
57612          * Fires when the user resizes a column
57613          * @param {Number} columnIndex
57614          * @param {Number} newSize
57615          */
57616         "columnresize" : true,
57617         /**
57618          * @event columnmove
57619          * Fires when the user moves a column
57620          * @param {Number} oldIndex
57621          * @param {Number} newIndex
57622          */
57623         "columnmove" : true,
57624         /**
57625          * @event startdrag
57626          * Fires when row(s) start being dragged
57627          * @param {Grid} this
57628          * @param {Roo.GridDD} dd The drag drop object
57629          * @param {event} e The raw browser event
57630          */
57631         "startdrag" : true,
57632         /**
57633          * @event enddrag
57634          * Fires when a drag operation is complete
57635          * @param {Grid} this
57636          * @param {Roo.GridDD} dd The drag drop object
57637          * @param {event} e The raw browser event
57638          */
57639         "enddrag" : true,
57640         /**
57641          * @event dragdrop
57642          * Fires when dragged row(s) are dropped on a valid DD target
57643          * @param {Grid} this
57644          * @param {Roo.GridDD} dd The drag drop object
57645          * @param {String} targetId The target drag drop object
57646          * @param {event} e The raw browser event
57647          */
57648         "dragdrop" : true,
57649         /**
57650          * @event dragover
57651          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57652          * @param {Grid} this
57653          * @param {Roo.GridDD} dd The drag drop object
57654          * @param {String} targetId The target drag drop object
57655          * @param {event} e The raw browser event
57656          */
57657         "dragover" : true,
57658         /**
57659          * @event dragenter
57660          *  Fires when the dragged row(s) first cross another DD target while being dragged
57661          * @param {Grid} this
57662          * @param {Roo.GridDD} dd The drag drop object
57663          * @param {String} targetId The target drag drop object
57664          * @param {event} e The raw browser event
57665          */
57666         "dragenter" : true,
57667         /**
57668          * @event dragout
57669          * Fires when the dragged row(s) leave another DD target while being dragged
57670          * @param {Grid} this
57671          * @param {Roo.GridDD} dd The drag drop object
57672          * @param {String} targetId The target drag drop object
57673          * @param {event} e The raw browser event
57674          */
57675         "dragout" : true,
57676         /**
57677          * @event rowclass
57678          * Fires when a row is rendered, so you can change add a style to it.
57679          * @param {GridView} gridview   The grid view
57680          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57681          */
57682         'rowclass' : true,
57683
57684         /**
57685          * @event render
57686          * Fires when the grid is rendered
57687          * @param {Grid} grid
57688          */
57689         'render' : true,
57690             /**
57691              * @event select
57692              * Fires when a date is selected
57693              * @param {DatePicker} this
57694              * @param {Date} date The selected date
57695              */
57696         'select': true,
57697         /**
57698              * @event monthchange
57699              * Fires when the displayed month changes 
57700              * @param {DatePicker} this
57701              * @param {Date} date The selected month
57702              */
57703         'monthchange': true,
57704         /**
57705              * @event evententer
57706              * Fires when mouse over an event
57707              * @param {Calendar} this
57708              * @param {event} Event
57709              */
57710         'evententer': true,
57711         /**
57712              * @event eventleave
57713              * Fires when the mouse leaves an
57714              * @param {Calendar} this
57715              * @param {event}
57716              */
57717         'eventleave': true,
57718         /**
57719              * @event eventclick
57720              * Fires when the mouse click an
57721              * @param {Calendar} this
57722              * @param {event}
57723              */
57724         'eventclick': true,
57725         /**
57726              * @event eventrender
57727              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57728              * @param {Calendar} this
57729              * @param {data} data to be modified
57730              */
57731         'eventrender': true
57732         
57733     });
57734
57735     Roo.grid.Grid.superclass.constructor.call(this);
57736     this.on('render', function() {
57737         this.view.el.addClass('x-grid-cal'); 
57738         
57739         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57740
57741     },this);
57742     
57743     if (!Roo.grid.Calendar.style) {
57744         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57745             
57746             
57747             '.x-grid-cal .x-grid-col' :  {
57748                 height: 'auto !important',
57749                 'vertical-align': 'top'
57750             },
57751             '.x-grid-cal  .fc-event-hori' : {
57752                 height: '14px'
57753             }
57754              
57755             
57756         }, Roo.id());
57757     }
57758
57759     
57760     
57761 };
57762 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57763     /**
57764      * @cfg {Store} eventStore The store that loads events.
57765      */
57766     eventStore : 25,
57767
57768      
57769     activeDate : false,
57770     startDay : 0,
57771     autoWidth : true,
57772     monitorWindowResize : false,
57773
57774     
57775     resizeColumns : function() {
57776         var col = (this.view.el.getWidth() / 7) - 3;
57777         // loop through cols, and setWidth
57778         for(var i =0 ; i < 7 ; i++){
57779             this.cm.setColumnWidth(i, col);
57780         }
57781     },
57782      setDate :function(date) {
57783         
57784         Roo.log('setDate?');
57785         
57786         this.resizeColumns();
57787         var vd = this.activeDate;
57788         this.activeDate = date;
57789 //        if(vd && this.el){
57790 //            var t = date.getTime();
57791 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57792 //                Roo.log('using add remove');
57793 //                
57794 //                this.fireEvent('monthchange', this, date);
57795 //                
57796 //                this.cells.removeClass("fc-state-highlight");
57797 //                this.cells.each(function(c){
57798 //                   if(c.dateValue == t){
57799 //                       c.addClass("fc-state-highlight");
57800 //                       setTimeout(function(){
57801 //                            try{c.dom.firstChild.focus();}catch(e){}
57802 //                       }, 50);
57803 //                       return false;
57804 //                   }
57805 //                   return true;
57806 //                });
57807 //                return;
57808 //            }
57809 //        }
57810         
57811         var days = date.getDaysInMonth();
57812         
57813         var firstOfMonth = date.getFirstDateOfMonth();
57814         var startingPos = firstOfMonth.getDay()-this.startDay;
57815         
57816         if(startingPos < this.startDay){
57817             startingPos += 7;
57818         }
57819         
57820         var pm = date.add(Date.MONTH, -1);
57821         var prevStart = pm.getDaysInMonth()-startingPos;
57822 //        
57823         
57824         
57825         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57826         
57827         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57828         //this.cells.addClassOnOver('fc-state-hover');
57829         
57830         var cells = this.cells.elements;
57831         var textEls = this.textNodes;
57832         
57833         //Roo.each(cells, function(cell){
57834         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57835         //});
57836         
57837         days += startingPos;
57838
57839         // convert everything to numbers so it's fast
57840         var day = 86400000;
57841         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57842         //Roo.log(d);
57843         //Roo.log(pm);
57844         //Roo.log(prevStart);
57845         
57846         var today = new Date().clearTime().getTime();
57847         var sel = date.clearTime().getTime();
57848         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57849         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57850         var ddMatch = this.disabledDatesRE;
57851         var ddText = this.disabledDatesText;
57852         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57853         var ddaysText = this.disabledDaysText;
57854         var format = this.format;
57855         
57856         var setCellClass = function(cal, cell){
57857             
57858             //Roo.log('set Cell Class');
57859             cell.title = "";
57860             var t = d.getTime();
57861             
57862             //Roo.log(d);
57863             
57864             
57865             cell.dateValue = t;
57866             if(t == today){
57867                 cell.className += " fc-today";
57868                 cell.className += " fc-state-highlight";
57869                 cell.title = cal.todayText;
57870             }
57871             if(t == sel){
57872                 // disable highlight in other month..
57873                 cell.className += " fc-state-highlight";
57874                 
57875             }
57876             // disabling
57877             if(t < min) {
57878                 //cell.className = " fc-state-disabled";
57879                 cell.title = cal.minText;
57880                 return;
57881             }
57882             if(t > max) {
57883                 //cell.className = " fc-state-disabled";
57884                 cell.title = cal.maxText;
57885                 return;
57886             }
57887             if(ddays){
57888                 if(ddays.indexOf(d.getDay()) != -1){
57889                     // cell.title = ddaysText;
57890                    // cell.className = " fc-state-disabled";
57891                 }
57892             }
57893             if(ddMatch && format){
57894                 var fvalue = d.dateFormat(format);
57895                 if(ddMatch.test(fvalue)){
57896                     cell.title = ddText.replace("%0", fvalue);
57897                    cell.className = " fc-state-disabled";
57898                 }
57899             }
57900             
57901             if (!cell.initialClassName) {
57902                 cell.initialClassName = cell.dom.className;
57903             }
57904             
57905             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57906         };
57907
57908         var i = 0;
57909         
57910         for(; i < startingPos; i++) {
57911             cells[i].dayName =  (++prevStart);
57912             Roo.log(textEls[i]);
57913             d.setDate(d.getDate()+1);
57914             
57915             //cells[i].className = "fc-past fc-other-month";
57916             setCellClass(this, cells[i]);
57917         }
57918         
57919         var intDay = 0;
57920         
57921         for(; i < days; i++){
57922             intDay = i - startingPos + 1;
57923             cells[i].dayName =  (intDay);
57924             d.setDate(d.getDate()+1);
57925             
57926             cells[i].className = ''; // "x-date-active";
57927             setCellClass(this, cells[i]);
57928         }
57929         var extraDays = 0;
57930         
57931         for(; i < 42; i++) {
57932             //textEls[i].innerHTML = (++extraDays);
57933             
57934             d.setDate(d.getDate()+1);
57935             cells[i].dayName = (++extraDays);
57936             cells[i].className = "fc-future fc-other-month";
57937             setCellClass(this, cells[i]);
57938         }
57939         
57940         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57941         
57942         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57943         
57944         // this will cause all the cells to mis
57945         var rows= [];
57946         var i =0;
57947         for (var r = 0;r < 6;r++) {
57948             for (var c =0;c < 7;c++) {
57949                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57950             }    
57951         }
57952         
57953         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57954         for(i=0;i<cells.length;i++) {
57955             
57956             this.cells.elements[i].dayName = cells[i].dayName ;
57957             this.cells.elements[i].className = cells[i].className;
57958             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57959             this.cells.elements[i].title = cells[i].title ;
57960             this.cells.elements[i].dateValue = cells[i].dateValue ;
57961         }
57962         
57963         
57964         
57965         
57966         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57967         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57968         
57969         ////if(totalRows != 6){
57970             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57971            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57972        // }
57973         
57974         this.fireEvent('monthchange', this, date);
57975         
57976         
57977     },
57978  /**
57979      * Returns the grid's SelectionModel.
57980      * @return {SelectionModel}
57981      */
57982     getSelectionModel : function(){
57983         if(!this.selModel){
57984             this.selModel = new Roo.grid.CellSelectionModel();
57985         }
57986         return this.selModel;
57987     },
57988
57989     load: function() {
57990         this.eventStore.load()
57991         
57992         
57993         
57994     },
57995     
57996     findCell : function(dt) {
57997         dt = dt.clearTime().getTime();
57998         var ret = false;
57999         this.cells.each(function(c){
58000             //Roo.log("check " +c.dateValue + '?=' + dt);
58001             if(c.dateValue == dt){
58002                 ret = c;
58003                 return false;
58004             }
58005             return true;
58006         });
58007         
58008         return ret;
58009     },
58010     
58011     findCells : function(rec) {
58012         var s = rec.data.start_dt.clone().clearTime().getTime();
58013        // Roo.log(s);
58014         var e= rec.data.end_dt.clone().clearTime().getTime();
58015        // Roo.log(e);
58016         var ret = [];
58017         this.cells.each(function(c){
58018              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
58019             
58020             if(c.dateValue > e){
58021                 return ;
58022             }
58023             if(c.dateValue < s){
58024                 return ;
58025             }
58026             ret.push(c);
58027         });
58028         
58029         return ret;    
58030     },
58031     
58032     findBestRow: function(cells)
58033     {
58034         var ret = 0;
58035         
58036         for (var i =0 ; i < cells.length;i++) {
58037             ret  = Math.max(cells[i].rows || 0,ret);
58038         }
58039         return ret;
58040         
58041     },
58042     
58043     
58044     addItem : function(rec)
58045     {
58046         // look for vertical location slot in
58047         var cells = this.findCells(rec);
58048         
58049         rec.row = this.findBestRow(cells);
58050         
58051         // work out the location.
58052         
58053         var crow = false;
58054         var rows = [];
58055         for(var i =0; i < cells.length; i++) {
58056             if (!crow) {
58057                 crow = {
58058                     start : cells[i],
58059                     end :  cells[i]
58060                 };
58061                 continue;
58062             }
58063             if (crow.start.getY() == cells[i].getY()) {
58064                 // on same row.
58065                 crow.end = cells[i];
58066                 continue;
58067             }
58068             // different row.
58069             rows.push(crow);
58070             crow = {
58071                 start: cells[i],
58072                 end : cells[i]
58073             };
58074             
58075         }
58076         
58077         rows.push(crow);
58078         rec.els = [];
58079         rec.rows = rows;
58080         rec.cells = cells;
58081         for (var i = 0; i < cells.length;i++) {
58082             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
58083             
58084         }
58085         
58086         
58087     },
58088     
58089     clearEvents: function() {
58090         
58091         if (!this.eventStore.getCount()) {
58092             return;
58093         }
58094         // reset number of rows in cells.
58095         Roo.each(this.cells.elements, function(c){
58096             c.rows = 0;
58097         });
58098         
58099         this.eventStore.each(function(e) {
58100             this.clearEvent(e);
58101         },this);
58102         
58103     },
58104     
58105     clearEvent : function(ev)
58106     {
58107         if (ev.els) {
58108             Roo.each(ev.els, function(el) {
58109                 el.un('mouseenter' ,this.onEventEnter, this);
58110                 el.un('mouseleave' ,this.onEventLeave, this);
58111                 el.remove();
58112             },this);
58113             ev.els = [];
58114         }
58115     },
58116     
58117     
58118     renderEvent : function(ev,ctr) {
58119         if (!ctr) {
58120              ctr = this.view.el.select('.fc-event-container',true).first();
58121         }
58122         
58123          
58124         this.clearEvent(ev);
58125             //code
58126        
58127         
58128         
58129         ev.els = [];
58130         var cells = ev.cells;
58131         var rows = ev.rows;
58132         this.fireEvent('eventrender', this, ev);
58133         
58134         for(var i =0; i < rows.length; i++) {
58135             
58136             cls = '';
58137             if (i == 0) {
58138                 cls += ' fc-event-start';
58139             }
58140             if ((i+1) == rows.length) {
58141                 cls += ' fc-event-end';
58142             }
58143             
58144             //Roo.log(ev.data);
58145             // how many rows should it span..
58146             var cg = this.eventTmpl.append(ctr,Roo.apply({
58147                 fccls : cls
58148                 
58149             }, ev.data) , true);
58150             
58151             
58152             cg.on('mouseenter' ,this.onEventEnter, this, ev);
58153             cg.on('mouseleave' ,this.onEventLeave, this, ev);
58154             cg.on('click', this.onEventClick, this, ev);
58155             
58156             ev.els.push(cg);
58157             
58158             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
58159             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
58160             //Roo.log(cg);
58161              
58162             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
58163             cg.setWidth(ebox.right - sbox.x -2);
58164         }
58165     },
58166     
58167     renderEvents: function()
58168     {   
58169         // first make sure there is enough space..
58170         
58171         if (!this.eventTmpl) {
58172             this.eventTmpl = new Roo.Template(
58173                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
58174                     '<div class="fc-event-inner">' +
58175                         '<span class="fc-event-time">{time}</span>' +
58176                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
58177                     '</div>' +
58178                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
58179                 '</div>'
58180             );
58181                 
58182         }
58183                
58184         
58185         
58186         this.cells.each(function(c) {
58187             //Roo.log(c.select('.fc-day-content div',true).first());
58188             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
58189         });
58190         
58191         var ctr = this.view.el.select('.fc-event-container',true).first();
58192         
58193         var cls;
58194         this.eventStore.each(function(ev){
58195             
58196             this.renderEvent(ev);
58197              
58198              
58199         }, this);
58200         this.view.layout();
58201         
58202     },
58203     
58204     onEventEnter: function (e, el,event,d) {
58205         this.fireEvent('evententer', this, el, event);
58206     },
58207     
58208     onEventLeave: function (e, el,event,d) {
58209         this.fireEvent('eventleave', this, el, event);
58210     },
58211     
58212     onEventClick: function (e, el,event,d) {
58213         this.fireEvent('eventclick', this, el, event);
58214     },
58215     
58216     onMonthChange: function () {
58217         this.store.load();
58218     },
58219     
58220     onLoad: function () {
58221         
58222         //Roo.log('calendar onload');
58223 //         
58224         if(this.eventStore.getCount() > 0){
58225             
58226            
58227             
58228             this.eventStore.each(function(d){
58229                 
58230                 
58231                 // FIXME..
58232                 var add =   d.data;
58233                 if (typeof(add.end_dt) == 'undefined')  {
58234                     Roo.log("Missing End time in calendar data: ");
58235                     Roo.log(d);
58236                     return;
58237                 }
58238                 if (typeof(add.start_dt) == 'undefined')  {
58239                     Roo.log("Missing Start time in calendar data: ");
58240                     Roo.log(d);
58241                     return;
58242                 }
58243                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
58244                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
58245                 add.id = add.id || d.id;
58246                 add.title = add.title || '??';
58247                 
58248                 this.addItem(d);
58249                 
58250              
58251             },this);
58252         }
58253         
58254         this.renderEvents();
58255     }
58256     
58257
58258 });
58259 /*
58260  grid : {
58261                 xtype: 'Grid',
58262                 xns: Roo.grid,
58263                 listeners : {
58264                     render : function ()
58265                     {
58266                         _this.grid = this;
58267                         
58268                         if (!this.view.el.hasClass('course-timesheet')) {
58269                             this.view.el.addClass('course-timesheet');
58270                         }
58271                         if (this.tsStyle) {
58272                             this.ds.load({});
58273                             return; 
58274                         }
58275                         Roo.log('width');
58276                         Roo.log(_this.grid.view.el.getWidth());
58277                         
58278                         
58279                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58280                             '.course-timesheet .x-grid-row' : {
58281                                 height: '80px'
58282                             },
58283                             '.x-grid-row td' : {
58284                                 'vertical-align' : 0
58285                             },
58286                             '.course-edit-link' : {
58287                                 'color' : 'blue',
58288                                 'text-overflow' : 'ellipsis',
58289                                 'overflow' : 'hidden',
58290                                 'white-space' : 'nowrap',
58291                                 'cursor' : 'pointer'
58292                             },
58293                             '.sub-link' : {
58294                                 'color' : 'green'
58295                             },
58296                             '.de-act-sup-link' : {
58297                                 'color' : 'purple',
58298                                 'text-decoration' : 'line-through'
58299                             },
58300                             '.de-act-link' : {
58301                                 'color' : 'red',
58302                                 'text-decoration' : 'line-through'
58303                             },
58304                             '.course-timesheet .course-highlight' : {
58305                                 'border-top-style': 'dashed !important',
58306                                 'border-bottom-bottom': 'dashed !important'
58307                             },
58308                             '.course-timesheet .course-item' : {
58309                                 'font-family'   : 'tahoma, arial, helvetica',
58310                                 'font-size'     : '11px',
58311                                 'overflow'      : 'hidden',
58312                                 'padding-left'  : '10px',
58313                                 'padding-right' : '10px',
58314                                 'padding-top' : '10px' 
58315                             }
58316                             
58317                         }, Roo.id());
58318                                 this.ds.load({});
58319                     }
58320                 },
58321                 autoWidth : true,
58322                 monitorWindowResize : false,
58323                 cellrenderer : function(v,x,r)
58324                 {
58325                     return v;
58326                 },
58327                 sm : {
58328                     xtype: 'CellSelectionModel',
58329                     xns: Roo.grid
58330                 },
58331                 dataSource : {
58332                     xtype: 'Store',
58333                     xns: Roo.data,
58334                     listeners : {
58335                         beforeload : function (_self, options)
58336                         {
58337                             options.params = options.params || {};
58338                             options.params._month = _this.monthField.getValue();
58339                             options.params.limit = 9999;
58340                             options.params['sort'] = 'when_dt';    
58341                             options.params['dir'] = 'ASC';    
58342                             this.proxy.loadResponse = this.loadResponse;
58343                             Roo.log("load?");
58344                             //this.addColumns();
58345                         },
58346                         load : function (_self, records, options)
58347                         {
58348                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58349                                 // if you click on the translation.. you can edit it...
58350                                 var el = Roo.get(this);
58351                                 var id = el.dom.getAttribute('data-id');
58352                                 var d = el.dom.getAttribute('data-date');
58353                                 var t = el.dom.getAttribute('data-time');
58354                                 //var id = this.child('span').dom.textContent;
58355                                 
58356                                 //Roo.log(this);
58357                                 Pman.Dialog.CourseCalendar.show({
58358                                     id : id,
58359                                     when_d : d,
58360                                     when_t : t,
58361                                     productitem_active : id ? 1 : 0
58362                                 }, function() {
58363                                     _this.grid.ds.load({});
58364                                 });
58365                            
58366                            });
58367                            
58368                            _this.panel.fireEvent('resize', [ '', '' ]);
58369                         }
58370                     },
58371                     loadResponse : function(o, success, response){
58372                             // this is overridden on before load..
58373                             
58374                             Roo.log("our code?");       
58375                             //Roo.log(success);
58376                             //Roo.log(response)
58377                             delete this.activeRequest;
58378                             if(!success){
58379                                 this.fireEvent("loadexception", this, o, response);
58380                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58381                                 return;
58382                             }
58383                             var result;
58384                             try {
58385                                 result = o.reader.read(response);
58386                             }catch(e){
58387                                 Roo.log("load exception?");
58388                                 this.fireEvent("loadexception", this, o, response, e);
58389                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58390                                 return;
58391                             }
58392                             Roo.log("ready...");        
58393                             // loop through result.records;
58394                             // and set this.tdate[date] = [] << array of records..
58395                             _this.tdata  = {};
58396                             Roo.each(result.records, function(r){
58397                                 //Roo.log(r.data);
58398                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58399                                     _this.tdata[r.data.when_dt.format('j')] = [];
58400                                 }
58401                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58402                             });
58403                             
58404                             //Roo.log(_this.tdata);
58405                             
58406                             result.records = [];
58407                             result.totalRecords = 6;
58408                     
58409                             // let's generate some duumy records for the rows.
58410                             //var st = _this.dateField.getValue();
58411                             
58412                             // work out monday..
58413                             //st = st.add(Date.DAY, -1 * st.format('w'));
58414                             
58415                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58416                             
58417                             var firstOfMonth = date.getFirstDayOfMonth();
58418                             var days = date.getDaysInMonth();
58419                             var d = 1;
58420                             var firstAdded = false;
58421                             for (var i = 0; i < result.totalRecords ; i++) {
58422                                 //var d= st.add(Date.DAY, i);
58423                                 var row = {};
58424                                 var added = 0;
58425                                 for(var w = 0 ; w < 7 ; w++){
58426                                     if(!firstAdded && firstOfMonth != w){
58427                                         continue;
58428                                     }
58429                                     if(d > days){
58430                                         continue;
58431                                     }
58432                                     firstAdded = true;
58433                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58434                                     row['weekday'+w] = String.format(
58435                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58436                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58437                                                     d,
58438                                                     date.format('Y-m-')+dd
58439                                                 );
58440                                     added++;
58441                                     if(typeof(_this.tdata[d]) != 'undefined'){
58442                                         Roo.each(_this.tdata[d], function(r){
58443                                             var is_sub = '';
58444                                             var deactive = '';
58445                                             var id = r.id;
58446                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58447                                             if(r.parent_id*1>0){
58448                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58449                                                 id = r.parent_id;
58450                                             }
58451                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58452                                                 deactive = 'de-act-link';
58453                                             }
58454                                             
58455                                             row['weekday'+w] += String.format(
58456                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58457                                                     id, //0
58458                                                     r.product_id_name, //1
58459                                                     r.when_dt.format('h:ia'), //2
58460                                                     is_sub, //3
58461                                                     deactive, //4
58462                                                     desc // 5
58463                                             );
58464                                         });
58465                                     }
58466                                     d++;
58467                                 }
58468                                 
58469                                 // only do this if something added..
58470                                 if(added > 0){ 
58471                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58472                                 }
58473                                 
58474                                 
58475                                 // push it twice. (second one with an hour..
58476                                 
58477                             }
58478                             //Roo.log(result);
58479                             this.fireEvent("load", this, o, o.request.arg);
58480                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58481                         },
58482                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58483                     proxy : {
58484                         xtype: 'HttpProxy',
58485                         xns: Roo.data,
58486                         method : 'GET',
58487                         url : baseURL + '/Roo/Shop_course.php'
58488                     },
58489                     reader : {
58490                         xtype: 'JsonReader',
58491                         xns: Roo.data,
58492                         id : 'id',
58493                         fields : [
58494                             {
58495                                 'name': 'id',
58496                                 'type': 'int'
58497                             },
58498                             {
58499                                 'name': 'when_dt',
58500                                 'type': 'string'
58501                             },
58502                             {
58503                                 'name': 'end_dt',
58504                                 'type': 'string'
58505                             },
58506                             {
58507                                 'name': 'parent_id',
58508                                 'type': 'int'
58509                             },
58510                             {
58511                                 'name': 'product_id',
58512                                 'type': 'int'
58513                             },
58514                             {
58515                                 'name': 'productitem_id',
58516                                 'type': 'int'
58517                             },
58518                             {
58519                                 'name': 'guid',
58520                                 'type': 'int'
58521                             }
58522                         ]
58523                     }
58524                 },
58525                 toolbar : {
58526                     xtype: 'Toolbar',
58527                     xns: Roo,
58528                     items : [
58529                         {
58530                             xtype: 'Button',
58531                             xns: Roo.Toolbar,
58532                             listeners : {
58533                                 click : function (_self, e)
58534                                 {
58535                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58536                                     sd.setMonth(sd.getMonth()-1);
58537                                     _this.monthField.setValue(sd.format('Y-m-d'));
58538                                     _this.grid.ds.load({});
58539                                 }
58540                             },
58541                             text : "Back"
58542                         },
58543                         {
58544                             xtype: 'Separator',
58545                             xns: Roo.Toolbar
58546                         },
58547                         {
58548                             xtype: 'MonthField',
58549                             xns: Roo.form,
58550                             listeners : {
58551                                 render : function (_self)
58552                                 {
58553                                     _this.monthField = _self;
58554                                    // _this.monthField.set  today
58555                                 },
58556                                 select : function (combo, date)
58557                                 {
58558                                     _this.grid.ds.load({});
58559                                 }
58560                             },
58561                             value : (function() { return new Date(); })()
58562                         },
58563                         {
58564                             xtype: 'Separator',
58565                             xns: Roo.Toolbar
58566                         },
58567                         {
58568                             xtype: 'TextItem',
58569                             xns: Roo.Toolbar,
58570                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58571                         },
58572                         {
58573                             xtype: 'Fill',
58574                             xns: Roo.Toolbar
58575                         },
58576                         {
58577                             xtype: 'Button',
58578                             xns: Roo.Toolbar,
58579                             listeners : {
58580                                 click : function (_self, e)
58581                                 {
58582                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58583                                     sd.setMonth(sd.getMonth()+1);
58584                                     _this.monthField.setValue(sd.format('Y-m-d'));
58585                                     _this.grid.ds.load({});
58586                                 }
58587                             },
58588                             text : "Next"
58589                         }
58590                     ]
58591                 },
58592                  
58593             }
58594         };
58595         
58596         *//*
58597  * Based on:
58598  * Ext JS Library 1.1.1
58599  * Copyright(c) 2006-2007, Ext JS, LLC.
58600  *
58601  * Originally Released Under LGPL - original licence link has changed is not relivant.
58602  *
58603  * Fork - LGPL
58604  * <script type="text/javascript">
58605  */
58606  
58607 /**
58608  * @class Roo.LoadMask
58609  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58610  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58611  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58612  * element's UpdateManager load indicator and will be destroyed after the initial load.
58613  * @constructor
58614  * Create a new LoadMask
58615  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58616  * @param {Object} config The config object
58617  */
58618 Roo.LoadMask = function(el, config){
58619     this.el = Roo.get(el);
58620     Roo.apply(this, config);
58621     if(this.store){
58622         this.store.on('beforeload', this.onBeforeLoad, this);
58623         this.store.on('load', this.onLoad, this);
58624         this.store.on('loadexception', this.onLoadException, this);
58625         this.removeMask = false;
58626     }else{
58627         var um = this.el.getUpdateManager();
58628         um.showLoadIndicator = false; // disable the default indicator
58629         um.on('beforeupdate', this.onBeforeLoad, this);
58630         um.on('update', this.onLoad, this);
58631         um.on('failure', this.onLoad, this);
58632         this.removeMask = true;
58633     }
58634 };
58635
58636 Roo.LoadMask.prototype = {
58637     /**
58638      * @cfg {Boolean} removeMask
58639      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58640      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58641      */
58642     /**
58643      * @cfg {String} msg
58644      * The text to display in a centered loading message box (defaults to 'Loading...')
58645      */
58646     msg : 'Loading...',
58647     /**
58648      * @cfg {String} msgCls
58649      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58650      */
58651     msgCls : 'x-mask-loading',
58652
58653     /**
58654      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58655      * @type Boolean
58656      */
58657     disabled: false,
58658
58659     /**
58660      * Disables the mask to prevent it from being displayed
58661      */
58662     disable : function(){
58663        this.disabled = true;
58664     },
58665
58666     /**
58667      * Enables the mask so that it can be displayed
58668      */
58669     enable : function(){
58670         this.disabled = false;
58671     },
58672     
58673     onLoadException : function()
58674     {
58675         Roo.log(arguments);
58676         
58677         if (typeof(arguments[3]) != 'undefined') {
58678             Roo.MessageBox.alert("Error loading",arguments[3]);
58679         } 
58680         /*
58681         try {
58682             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58683                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58684             }   
58685         } catch(e) {
58686             
58687         }
58688         */
58689     
58690         
58691         
58692         this.el.unmask(this.removeMask);
58693     },
58694     // private
58695     onLoad : function()
58696     {
58697         this.el.unmask(this.removeMask);
58698     },
58699
58700     // private
58701     onBeforeLoad : function(){
58702         if(!this.disabled){
58703             this.el.mask(this.msg, this.msgCls);
58704         }
58705     },
58706
58707     // private
58708     destroy : function(){
58709         if(this.store){
58710             this.store.un('beforeload', this.onBeforeLoad, this);
58711             this.store.un('load', this.onLoad, this);
58712             this.store.un('loadexception', this.onLoadException, this);
58713         }else{
58714             var um = this.el.getUpdateManager();
58715             um.un('beforeupdate', this.onBeforeLoad, this);
58716             um.un('update', this.onLoad, this);
58717             um.un('failure', this.onLoad, this);
58718         }
58719     }
58720 };/*
58721  * Based on:
58722  * Ext JS Library 1.1.1
58723  * Copyright(c) 2006-2007, Ext JS, LLC.
58724  *
58725  * Originally Released Under LGPL - original licence link has changed is not relivant.
58726  *
58727  * Fork - LGPL
58728  * <script type="text/javascript">
58729  */
58730
58731
58732 /**
58733  * @class Roo.XTemplate
58734  * @extends Roo.Template
58735  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58736 <pre><code>
58737 var t = new Roo.XTemplate(
58738         '&lt;select name="{name}"&gt;',
58739                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58740         '&lt;/select&gt;'
58741 );
58742  
58743 // then append, applying the master template values
58744  </code></pre>
58745  *
58746  * Supported features:
58747  *
58748  *  Tags:
58749
58750 <pre><code>
58751       {a_variable} - output encoded.
58752       {a_variable.format:("Y-m-d")} - call a method on the variable
58753       {a_variable:raw} - unencoded output
58754       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58755       {a_variable:this.method_on_template(...)} - call a method on the template object.
58756  
58757 </code></pre>
58758  *  The tpl tag:
58759 <pre><code>
58760         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58761         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58762         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58763         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58764   
58765         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58766         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58767 </code></pre>
58768  *      
58769  */
58770 Roo.XTemplate = function()
58771 {
58772     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58773     if (this.html) {
58774         this.compile();
58775     }
58776 };
58777
58778
58779 Roo.extend(Roo.XTemplate, Roo.Template, {
58780
58781     /**
58782      * The various sub templates
58783      */
58784     tpls : false,
58785     /**
58786      *
58787      * basic tag replacing syntax
58788      * WORD:WORD()
58789      *
58790      * // you can fake an object call by doing this
58791      *  x.t:(test,tesT) 
58792      * 
58793      */
58794     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58795
58796     /**
58797      * compile the template
58798      *
58799      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58800      *
58801      */
58802     compile: function()
58803     {
58804         var s = this.html;
58805      
58806         s = ['<tpl>', s, '</tpl>'].join('');
58807     
58808         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58809             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58810             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58811             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58812             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58813             m,
58814             id     = 0,
58815             tpls   = [];
58816     
58817         while(true == !!(m = s.match(re))){
58818             var forMatch   = m[0].match(nameRe),
58819                 ifMatch   = m[0].match(ifRe),
58820                 execMatch   = m[0].match(execRe),
58821                 namedMatch   = m[0].match(namedRe),
58822                 
58823                 exp  = null, 
58824                 fn   = null,
58825                 exec = null,
58826                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58827                 
58828             if (ifMatch) {
58829                 // if - puts fn into test..
58830                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58831                 if(exp){
58832                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58833                 }
58834             }
58835             
58836             if (execMatch) {
58837                 // exec - calls a function... returns empty if true is  returned.
58838                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58839                 if(exp){
58840                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58841                 }
58842             }
58843             
58844             
58845             if (name) {
58846                 // for = 
58847                 switch(name){
58848                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58849                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58850                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58851                 }
58852             }
58853             var uid = namedMatch ? namedMatch[1] : id;
58854             
58855             
58856             tpls.push({
58857                 id:     namedMatch ? namedMatch[1] : id,
58858                 target: name,
58859                 exec:   exec,
58860                 test:   fn,
58861                 body:   m[1] || ''
58862             });
58863             if (namedMatch) {
58864                 s = s.replace(m[0], '');
58865             } else { 
58866                 s = s.replace(m[0], '{xtpl'+ id + '}');
58867             }
58868             ++id;
58869         }
58870         this.tpls = [];
58871         for(var i = tpls.length-1; i >= 0; --i){
58872             this.compileTpl(tpls[i]);
58873             this.tpls[tpls[i].id] = tpls[i];
58874         }
58875         this.master = tpls[tpls.length-1];
58876         return this;
58877     },
58878     /**
58879      * same as applyTemplate, except it's done to one of the subTemplates
58880      * when using named templates, you can do:
58881      *
58882      * var str = pl.applySubTemplate('your-name', values);
58883      *
58884      * 
58885      * @param {Number} id of the template
58886      * @param {Object} values to apply to template
58887      * @param {Object} parent (normaly the instance of this object)
58888      */
58889     applySubTemplate : function(id, values, parent)
58890     {
58891         
58892         
58893         var t = this.tpls[id];
58894         
58895         
58896         try { 
58897             if(t.test && !t.test.call(this, values, parent)){
58898                 return '';
58899             }
58900         } catch(e) {
58901             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58902             Roo.log(e.toString());
58903             Roo.log(t.test);
58904             return ''
58905         }
58906         try { 
58907             
58908             if(t.exec && t.exec.call(this, values, parent)){
58909                 return '';
58910             }
58911         } catch(e) {
58912             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58913             Roo.log(e.toString());
58914             Roo.log(t.exec);
58915             return ''
58916         }
58917         try {
58918             var vs = t.target ? t.target.call(this, values, parent) : values;
58919             parent = t.target ? values : parent;
58920             if(t.target && vs instanceof Array){
58921                 var buf = [];
58922                 for(var i = 0, len = vs.length; i < len; i++){
58923                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58924                 }
58925                 return buf.join('');
58926             }
58927             return t.compiled.call(this, vs, parent);
58928         } catch (e) {
58929             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58930             Roo.log(e.toString());
58931             Roo.log(t.compiled);
58932             return '';
58933         }
58934     },
58935
58936     compileTpl : function(tpl)
58937     {
58938         var fm = Roo.util.Format;
58939         var useF = this.disableFormats !== true;
58940         var sep = Roo.isGecko ? "+" : ",";
58941         var undef = function(str) {
58942             Roo.log("Property not found :"  + str);
58943             return '';
58944         };
58945         
58946         var fn = function(m, name, format, args)
58947         {
58948             //Roo.log(arguments);
58949             args = args ? args.replace(/\\'/g,"'") : args;
58950             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58951             if (typeof(format) == 'undefined') {
58952                 format= 'htmlEncode';
58953             }
58954             if (format == 'raw' ) {
58955                 format = false;
58956             }
58957             
58958             if(name.substr(0, 4) == 'xtpl'){
58959                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58960             }
58961             
58962             // build an array of options to determine if value is undefined..
58963             
58964             // basically get 'xxxx.yyyy' then do
58965             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58966             //    (function () { Roo.log("Property not found"); return ''; })() :
58967             //    ......
58968             
58969             var udef_ar = [];
58970             var lookfor = '';
58971             Roo.each(name.split('.'), function(st) {
58972                 lookfor += (lookfor.length ? '.': '') + st;
58973                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58974             });
58975             
58976             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58977             
58978             
58979             if(format && useF){
58980                 
58981                 args = args ? ',' + args : "";
58982                  
58983                 if(format.substr(0, 5) != "this."){
58984                     format = "fm." + format + '(';
58985                 }else{
58986                     format = 'this.call("'+ format.substr(5) + '", ';
58987                     args = ", values";
58988                 }
58989                 
58990                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58991             }
58992              
58993             if (args.length) {
58994                 // called with xxyx.yuu:(test,test)
58995                 // change to ()
58996                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58997             }
58998             // raw.. - :raw modifier..
58999             return "'"+ sep + udef_st  + name + ")"+sep+"'";
59000             
59001         };
59002         var body;
59003         // branched to use + in gecko and [].join() in others
59004         if(Roo.isGecko){
59005             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
59006                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
59007                     "';};};";
59008         }else{
59009             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
59010             body.push(tpl.body.replace(/(\r\n|\n)/g,
59011                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
59012             body.push("'].join('');};};");
59013             body = body.join('');
59014         }
59015         
59016         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
59017        
59018         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
59019         eval(body);
59020         
59021         return this;
59022     },
59023
59024     applyTemplate : function(values){
59025         return this.master.compiled.call(this, values, {});
59026         //var s = this.subs;
59027     },
59028
59029     apply : function(){
59030         return this.applyTemplate.apply(this, arguments);
59031     }
59032
59033  });
59034
59035 Roo.XTemplate.from = function(el){
59036     el = Roo.getDom(el);
59037     return new Roo.XTemplate(el.value || el.innerHTML);
59038 };