roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isGecko = !isSafari && ua.indexOf("gecko") > -1,
61         isBorderBox = isIE && !isStrict,
62         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
63         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
64         isLinux = (ua.indexOf("linux") != -1),
65         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
66         isIOS = /iphone|ipad/.test(ua),
67         isTouch =  (function() {
68             try {  
69                 document.createEvent("TouchEvent");  
70                 return true;  
71             } catch (e) {  
72                 return false;  
73             } 
74             
75         })();
76     // remove css image flicker
77         if(isIE && !isIE7){
78         try{
79             document.execCommand("BackgroundImageCache", false, true);
80         }catch(e){}
81     }
82     
83     Roo.apply(Roo, {
84         /**
85          * True if the browser is in strict mode
86          * @type Boolean
87          */
88         isStrict : isStrict,
89         /**
90          * True if the page is running over SSL
91          * @type Boolean
92          */
93         isSecure : isSecure,
94         /**
95          * True when the document is fully initialized and ready for action
96          * @type Boolean
97          */
98         isReady : false,
99         /**
100          * Turn on debugging output (currently only the factory uses this)
101          * @type Boolean
102          */
103         
104         debug: false,
105
106         /**
107          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
108          * @type Boolean
109          */
110         enableGarbageCollector : true,
111
112         /**
113          * True to automatically purge event listeners after uncaching an element (defaults to false).
114          * Note: this only happens if enableGarbageCollector is true.
115          * @type Boolean
116          */
117         enableListenerCollection:false,
118
119         /**
120          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
121          * the IE insecure content warning (defaults to javascript:false).
122          * @type String
123          */
124         SSL_SECURE_URL : "javascript:false",
125
126         /**
127          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
128          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
129          * @type String
130          */
131         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
132
133         emptyFn : function(){},
134         
135         /**
136          * Copies all the properties of config to obj if they don't already exist.
137          * @param {Object} obj The receiver of the properties
138          * @param {Object} config The source of the properties
139          * @return {Object} returns obj
140          */
141         applyIf : function(o, c){
142             if(o && c){
143                 for(var p in c){
144                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
145                 }
146             }
147             return o;
148         },
149
150         /**
151          * Applies event listeners to elements by selectors when the document is ready.
152          * The event name is specified with an @ suffix.
153 <pre><code>
154 Roo.addBehaviors({
155    // add a listener for click on all anchors in element with id foo
156    '#foo a@click' : function(e, t){
157        // do something
158    },
159
160    // add the same listener to multiple selectors (separated by comma BEFORE the @)
161    '#foo a, #bar span.some-class@mouseover' : function(){
162        // do something
163    }
164 });
165 </code></pre>
166          * @param {Object} obj The list of behaviors to apply
167          */
168         addBehaviors : function(o){
169             if(!Roo.isReady){
170                 Roo.onReady(function(){
171                     Roo.addBehaviors(o);
172                 });
173                 return;
174             }
175             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
176             for(var b in o){
177                 var parts = b.split('@');
178                 if(parts[1]){ // for Object prototype breakers
179                     var s = parts[0];
180                     if(!cache[s]){
181                         cache[s] = Roo.select(s);
182                     }
183                     cache[s].on(parts[1], o[b]);
184                 }
185             }
186             cache = null;
187         },
188
189         /**
190          * Generates unique ids. If the element already has an id, it is unchanged
191          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
192          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
193          * @return {String} The generated Id.
194          */
195         id : function(el, prefix){
196             prefix = prefix || "roo-gen";
197             el = Roo.getDom(el);
198             var id = prefix + (++idSeed);
199             return el ? (el.id ? el.id : (el.id = id)) : id;
200         },
201          
202        
203         /**
204          * Extends one class with another class and optionally overrides members with the passed literal. This class
205          * also adds the function "override()" to the class that can be used to override
206          * members on an instance.
207          * @param {Object} subclass The class inheriting the functionality
208          * @param {Object} superclass The class being extended
209          * @param {Object} overrides (optional) A literal with members
210          * @method extend
211          */
212         extend : function(){
213             // inline overrides
214             var io = function(o){
215                 for(var m in o){
216                     this[m] = o[m];
217                 }
218             };
219             return function(sb, sp, overrides){
220                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
221                     overrides = sp;
222                     sp = sb;
223                     sb = function(){sp.apply(this, arguments);};
224                 }
225                 var F = function(){}, sbp, spp = sp.prototype;
226                 F.prototype = spp;
227                 sbp = sb.prototype = new F();
228                 sbp.constructor=sb;
229                 sb.superclass=spp;
230                 
231                 if(spp.constructor == Object.prototype.constructor){
232                     spp.constructor=sp;
233                    
234                 }
235                 
236                 sb.override = function(o){
237                     Roo.override(sb, o);
238                 };
239                 sbp.override = io;
240                 Roo.override(sb, overrides);
241                 return sb;
242             };
243         }(),
244
245         /**
246          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
247          * Usage:<pre><code>
248 Roo.override(MyClass, {
249     newMethod1: function(){
250         // etc.
251     },
252     newMethod2: function(foo){
253         // etc.
254     }
255 });
256  </code></pre>
257          * @param {Object} origclass The class to override
258          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
259          * containing one or more methods.
260          * @method override
261          */
262         override : function(origclass, overrides){
263             if(overrides){
264                 var p = origclass.prototype;
265                 for(var method in overrides){
266                     p[method] = overrides[method];
267                 }
268             }
269         },
270         /**
271          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
272          * <pre><code>
273 Roo.namespace('Company', 'Company.data');
274 Company.Widget = function() { ... }
275 Company.data.CustomStore = function(config) { ... }
276 </code></pre>
277          * @param {String} namespace1
278          * @param {String} namespace2
279          * @param {String} etc
280          * @method namespace
281          */
282         namespace : function(){
283             var a=arguments, o=null, i, j, d, rt;
284             for (i=0; i<a.length; ++i) {
285                 d=a[i].split(".");
286                 rt = d[0];
287                 /** eval:var:o */
288                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
289                 for (j=1; j<d.length; ++j) {
290                     o[d[j]]=o[d[j]] || {};
291                     o=o[d[j]];
292                 }
293             }
294         },
295         /**
296          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
297          * <pre><code>
298 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
299 Roo.factory(conf, Roo.data);
300 </code></pre>
301          * @param {String} classname
302          * @param {String} namespace (optional)
303          * @method factory
304          */
305          
306         factory : function(c, ns)
307         {
308             // no xtype, no ns or c.xns - or forced off by c.xns
309             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
310                 return c;
311             }
312             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
313             if (c.constructor == ns[c.xtype]) {// already created...
314                 return c;
315             }
316             if (ns[c.xtype]) {
317                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
318                 var ret = new ns[c.xtype](c);
319                 ret.xns = false;
320                 return ret;
321             }
322             c.xns = false; // prevent recursion..
323             return c;
324         },
325          /**
326          * Logs to console if it can.
327          *
328          * @param {String|Object} string
329          * @method log
330          */
331         log : function(s)
332         {
333             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
334                 return; // alerT?
335             }
336             console.log(s);
337             
338         },
339         /**
340          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
341          * @param {Object} o
342          * @return {String}
343          */
344         urlEncode : function(o){
345             if(!o){
346                 return "";
347             }
348             var buf = [];
349             for(var key in o){
350                 var ov = o[key], k = Roo.encodeURIComponent(key);
351                 var type = typeof ov;
352                 if(type == 'undefined'){
353                     buf.push(k, "=&");
354                 }else if(type != "function" && type != "object"){
355                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
356                 }else if(ov instanceof Array){
357                     if (ov.length) {
358                             for(var i = 0, len = ov.length; i < len; i++) {
359                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
360                             }
361                         } else {
362                             buf.push(k, "=&");
363                         }
364                 }
365             }
366             buf.pop();
367             return buf.join("");
368         },
369          /**
370          * Safe version of encodeURIComponent
371          * @param {String} data 
372          * @return {String} 
373          */
374         
375         encodeURIComponent : function (data)
376         {
377             try {
378                 return encodeURIComponent(data);
379             } catch(e) {} // should be an uri encode error.
380             
381             if (data == '' || data == null){
382                return '';
383             }
384             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
385             function nibble_to_hex(nibble){
386                 var chars = '0123456789ABCDEF';
387                 return chars.charAt(nibble);
388             }
389             data = data.toString();
390             var buffer = '';
391             for(var i=0; i<data.length; i++){
392                 var c = data.charCodeAt(i);
393                 var bs = new Array();
394                 if (c > 0x10000){
395                         // 4 bytes
396                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
397                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
398                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
399                     bs[3] = 0x80 | (c & 0x3F);
400                 }else if (c > 0x800){
401                          // 3 bytes
402                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
403                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
404                     bs[2] = 0x80 | (c & 0x3F);
405                 }else if (c > 0x80){
406                        // 2 bytes
407                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
408                     bs[1] = 0x80 | (c & 0x3F);
409                 }else{
410                         // 1 byte
411                     bs[0] = c;
412                 }
413                 for(var j=0; j<bs.length; j++){
414                     var b = bs[j];
415                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
416                             + nibble_to_hex(b &0x0F);
417                     buffer += '%'+hex;
418                }
419             }
420             return buffer;    
421              
422         },
423
424         /**
425          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
426          * @param {String} string
427          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
428          * @return {Object} A literal with members
429          */
430         urlDecode : function(string, overwrite){
431             if(!string || !string.length){
432                 return {};
433             }
434             var obj = {};
435             var pairs = string.split('&');
436             var pair, name, value;
437             for(var i = 0, len = pairs.length; i < len; i++){
438                 pair = pairs[i].split('=');
439                 name = decodeURIComponent(pair[0]);
440                 value = decodeURIComponent(pair[1]);
441                 if(overwrite !== true){
442                     if(typeof obj[name] == "undefined"){
443                         obj[name] = value;
444                     }else if(typeof obj[name] == "string"){
445                         obj[name] = [obj[name]];
446                         obj[name].push(value);
447                     }else{
448                         obj[name].push(value);
449                     }
450                 }else{
451                     obj[name] = value;
452                 }
453             }
454             return obj;
455         },
456
457         /**
458          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
459          * passed array is not really an array, your function is called once with it.
460          * The supplied function is called with (Object item, Number index, Array allItems).
461          * @param {Array/NodeList/Mixed} array
462          * @param {Function} fn
463          * @param {Object} scope
464          */
465         each : function(array, fn, scope){
466             if(typeof array.length == "undefined" || typeof array == "string"){
467                 array = [array];
468             }
469             for(var i = 0, len = array.length; i < len; i++){
470                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
471             }
472         },
473
474         // deprecated
475         combine : function(){
476             var as = arguments, l = as.length, r = [];
477             for(var i = 0; i < l; i++){
478                 var a = as[i];
479                 if(a instanceof Array){
480                     r = r.concat(a);
481                 }else if(a.length !== undefined && !a.substr){
482                     r = r.concat(Array.prototype.slice.call(a, 0));
483                 }else{
484                     r.push(a);
485                 }
486             }
487             return r;
488         },
489
490         /**
491          * Escapes the passed string for use in a regular expression
492          * @param {String} str
493          * @return {String}
494          */
495         escapeRe : function(s) {
496             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
497         },
498
499         // internal
500         callback : function(cb, scope, args, delay){
501             if(typeof cb == "function"){
502                 if(delay){
503                     cb.defer(delay, scope, args || []);
504                 }else{
505                     cb.apply(scope, args || []);
506                 }
507             }
508         },
509
510         /**
511          * Return the dom node for the passed string (id), dom node, or Roo.Element
512          * @param {String/HTMLElement/Roo.Element} el
513          * @return HTMLElement
514          */
515         getDom : function(el){
516             if(!el){
517                 return null;
518             }
519             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
520         },
521
522         /**
523         * Shorthand for {@link Roo.ComponentMgr#get}
524         * @param {String} id
525         * @return Roo.Component
526         */
527         getCmp : function(id){
528             return Roo.ComponentMgr.get(id);
529         },
530          
531         num : function(v, defaultValue){
532             if(typeof v != 'number'){
533                 return defaultValue;
534             }
535             return v;
536         },
537
538         destroy : function(){
539             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
540                 var as = a[i];
541                 if(as){
542                     if(as.dom){
543                         as.removeAllListeners();
544                         as.remove();
545                         continue;
546                     }
547                     if(typeof as.purgeListeners == 'function'){
548                         as.purgeListeners();
549                     }
550                     if(typeof as.destroy == 'function'){
551                         as.destroy();
552                     }
553                 }
554             }
555         },
556
557         // inpired by a similar function in mootools library
558         /**
559          * Returns the type of object that is passed in. If the object passed in is null or undefined it
560          * return false otherwise it returns one of the following values:<ul>
561          * <li><b>string</b>: If the object passed is a string</li>
562          * <li><b>number</b>: If the object passed is a number</li>
563          * <li><b>boolean</b>: If the object passed is a boolean value</li>
564          * <li><b>function</b>: If the object passed is a function reference</li>
565          * <li><b>object</b>: If the object passed is an object</li>
566          * <li><b>array</b>: If the object passed is an array</li>
567          * <li><b>regexp</b>: If the object passed is a regular expression</li>
568          * <li><b>element</b>: If the object passed is a DOM Element</li>
569          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
570          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
571          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
572          * @param {Mixed} object
573          * @return {String}
574          */
575         type : function(o){
576             if(o === undefined || o === null){
577                 return false;
578             }
579             if(o.htmlElement){
580                 return 'element';
581             }
582             var t = typeof o;
583             if(t == 'object' && o.nodeName) {
584                 switch(o.nodeType) {
585                     case 1: return 'element';
586                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
587                 }
588             }
589             if(t == 'object' || t == 'function') {
590                 switch(o.constructor) {
591                     case Array: return 'array';
592                     case RegExp: return 'regexp';
593                 }
594                 if(typeof o.length == 'number' && typeof o.item == 'function') {
595                     return 'nodelist';
596                 }
597             }
598             return t;
599         },
600
601         /**
602          * Returns true if the passed value is null, undefined or an empty string (optional).
603          * @param {Mixed} value The value to test
604          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
605          * @return {Boolean}
606          */
607         isEmpty : function(v, allowBlank){
608             return v === null || v === undefined || (!allowBlank ? v === '' : false);
609         },
610         
611         /** @type Boolean */
612         isOpera : isOpera,
613         /** @type Boolean */
614         isSafari : isSafari,
615         /** @type Boolean */
616         isFirefox : isFirefox,
617         /** @type Boolean */
618         isIE : isIE,
619         /** @type Boolean */
620         isIE7 : isIE7,
621         /** @type Boolean */
622         isIE11 : isIE11,
623         /** @type Boolean */
624         isGecko : isGecko,
625         /** @type Boolean */
626         isBorderBox : isBorderBox,
627         /** @type Boolean */
628         isWindows : isWindows,
629         /** @type Boolean */
630         isLinux : isLinux,
631         /** @type Boolean */
632         isMac : isMac,
633         /** @type Boolean */
634         isIOS : isIOS,
635         /** @type Boolean */
636         isTouch : isTouch,
637
638         /**
639          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
640          * you may want to set this to true.
641          * @type Boolean
642          */
643         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
644         
645         
646                 
647         /**
648          * Selects a single element as a Roo Element
649          * This is about as close as you can get to jQuery's $('do crazy stuff')
650          * @param {String} selector The selector/xpath query
651          * @param {Node} root (optional) The start of the query (defaults to document).
652          * @return {Roo.Element}
653          */
654         selectNode : function(selector, root) 
655         {
656             var node = Roo.DomQuery.selectNode(selector,root);
657             return node ? Roo.get(node) : new Roo.Element(false);
658         }
659         
660     });
661
662
663 })();
664
665 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
666                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
667                 "Roo.app", "Roo.ux",
668                 "Roo.bootstrap",
669                 "Roo.bootstrap.dash");
670 /*
671  * Based on:
672  * Ext JS Library 1.1.1
673  * Copyright(c) 2006-2007, Ext JS, LLC.
674  *
675  * Originally Released Under LGPL - original licence link has changed is not relivant.
676  *
677  * Fork - LGPL
678  * <script type="text/javascript">
679  */
680
681 (function() {    
682     // wrappedn so fnCleanup is not in global scope...
683     if(Roo.isIE) {
684         function fnCleanUp() {
685             var p = Function.prototype;
686             delete p.createSequence;
687             delete p.defer;
688             delete p.createDelegate;
689             delete p.createCallback;
690             delete p.createInterceptor;
691
692             window.detachEvent("onunload", fnCleanUp);
693         }
694         window.attachEvent("onunload", fnCleanUp);
695     }
696 })();
697
698
699 /**
700  * @class Function
701  * These functions are available on every Function object (any JavaScript function).
702  */
703 Roo.apply(Function.prototype, {
704      /**
705      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
706      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
707      * Will create a function that is bound to those 2 args.
708      * @return {Function} The new function
709     */
710     createCallback : function(/*args...*/){
711         // make args available, in function below
712         var args = arguments;
713         var method = this;
714         return function() {
715             return method.apply(window, args);
716         };
717     },
718
719     /**
720      * Creates a delegate (callback) that sets the scope to obj.
721      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
722      * Will create a function that is automatically scoped to this.
723      * @param {Object} obj (optional) The object for which the scope is set
724      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
725      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
726      *                                             if a number the args are inserted at the specified position
727      * @return {Function} The new function
728      */
729     createDelegate : function(obj, args, appendArgs){
730         var method = this;
731         return function() {
732             var callArgs = args || arguments;
733             if(appendArgs === true){
734                 callArgs = Array.prototype.slice.call(arguments, 0);
735                 callArgs = callArgs.concat(args);
736             }else if(typeof appendArgs == "number"){
737                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
738                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
739                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
740             }
741             return method.apply(obj || window, callArgs);
742         };
743     },
744
745     /**
746      * Calls this function after the number of millseconds specified.
747      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
748      * @param {Object} obj (optional) The object for which the scope is set
749      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
750      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
751      *                                             if a number the args are inserted at the specified position
752      * @return {Number} The timeout id that can be used with clearTimeout
753      */
754     defer : function(millis, obj, args, appendArgs){
755         var fn = this.createDelegate(obj, args, appendArgs);
756         if(millis){
757             return setTimeout(fn, millis);
758         }
759         fn();
760         return 0;
761     },
762     /**
763      * Create a combined function call sequence of the original function + the passed function.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function
766      * @param {Function} fcn The function to sequence
767      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
768      * @return {Function} The new function
769      */
770     createSequence : function(fcn, scope){
771         if(typeof fcn != "function"){
772             return this;
773         }
774         var method = this;
775         return function() {
776             var retval = method.apply(this || window, arguments);
777             fcn.apply(scope || this || window, arguments);
778             return retval;
779         };
780     },
781
782     /**
783      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
784      * The resulting function returns the results of the original function.
785      * The passed fcn is called with the parameters of the original function.
786      * @addon
787      * @param {Function} fcn The function to call before the original
788      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
789      * @return {Function} The new function
790      */
791     createInterceptor : function(fcn, scope){
792         if(typeof fcn != "function"){
793             return this;
794         }
795         var method = this;
796         return function() {
797             fcn.target = this;
798             fcn.method = method;
799             if(fcn.apply(scope || this || window, arguments) === false){
800                 return;
801             }
802             return method.apply(this || window, arguments);
803         };
804     }
805 });
806 /*
807  * Based on:
808  * Ext JS Library 1.1.1
809  * Copyright(c) 2006-2007, Ext JS, LLC.
810  *
811  * Originally Released Under LGPL - original licence link has changed is not relivant.
812  *
813  * Fork - LGPL
814  * <script type="text/javascript">
815  */
816
817 Roo.applyIf(String, {
818     
819     /** @scope String */
820     
821     /**
822      * Escapes the passed string for ' and \
823      * @param {String} string The string to escape
824      * @return {String} The escaped string
825      * @static
826      */
827     escape : function(string) {
828         return string.replace(/('|\\)/g, "\\$1");
829     },
830
831     /**
832      * Pads the left side of a string with a specified character.  This is especially useful
833      * for normalizing number and date strings.  Example usage:
834      * <pre><code>
835 var s = String.leftPad('123', 5, '0');
836 // s now contains the string: '00123'
837 </code></pre>
838      * @param {String} string The original string
839      * @param {Number} size The total length of the output string
840      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
841      * @return {String} The padded string
842      * @static
843      */
844     leftPad : function (val, size, ch) {
845         var result = new String(val);
846         if(ch === null || ch === undefined || ch === '') {
847             ch = " ";
848         }
849         while (result.length < size) {
850             result = ch + result;
851         }
852         return result;
853     },
854
855     /**
856      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
857      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
858      * <pre><code>
859 var cls = 'my-class', text = 'Some text';
860 var s = String.format('<div class="{0}">{1}</div>', cls, text);
861 // s now contains the string: '<div class="my-class">Some text</div>'
862 </code></pre>
863      * @param {String} string The tokenized string to be formatted
864      * @param {String} value1 The value to replace token {0}
865      * @param {String} value2 Etc...
866      * @return {String} The formatted string
867      * @static
868      */
869     format : function(format){
870         var args = Array.prototype.slice.call(arguments, 1);
871         return format.replace(/\{(\d+)\}/g, function(m, i){
872             return Roo.util.Format.htmlEncode(args[i]);
873         });
874     }
875 });
876
877 /**
878  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
879  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
880  * they are already different, the first value passed in is returned.  Note that this method returns the new value
881  * but does not change the current string.
882  * <pre><code>
883 // alternate sort directions
884 sort = sort.toggle('ASC', 'DESC');
885
886 // instead of conditional logic:
887 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
888 </code></pre>
889  * @param {String} value The value to compare to the current string
890  * @param {String} other The new value to use if the string already equals the first value passed in
891  * @return {String} The new value
892  */
893  
894 String.prototype.toggle = function(value, other){
895     return this == value ? other : value;
896 };/*
897  * Based on:
898  * Ext JS Library 1.1.1
899  * Copyright(c) 2006-2007, Ext JS, LLC.
900  *
901  * Originally Released Under LGPL - original licence link has changed is not relivant.
902  *
903  * Fork - LGPL
904  * <script type="text/javascript">
905  */
906
907  /**
908  * @class Number
909  */
910 Roo.applyIf(Number.prototype, {
911     /**
912      * Checks whether or not the current number is within a desired range.  If the number is already within the
913      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
914      * exceeded.  Note that this method returns the constrained value but does not change the current number.
915      * @param {Number} min The minimum number in the range
916      * @param {Number} max The maximum number in the range
917      * @return {Number} The constrained value if outside the range, otherwise the current value
918      */
919     constrain : function(min, max){
920         return Math.min(Math.max(this, min), max);
921     }
922 });/*
923  * Based on:
924  * Ext JS Library 1.1.1
925  * Copyright(c) 2006-2007, Ext JS, LLC.
926  *
927  * Originally Released Under LGPL - original licence link has changed is not relivant.
928  *
929  * Fork - LGPL
930  * <script type="text/javascript">
931  */
932  /**
933  * @class Array
934  */
935 Roo.applyIf(Array.prototype, {
936     /**
937      * 
938      * Checks whether or not the specified object exists in the array.
939      * @param {Object} o The object to check for
940      * @return {Number} The index of o in the array (or -1 if it is not found)
941      */
942     indexOf : function(o){
943        for (var i = 0, len = this.length; i < len; i++){
944               if(this[i] == o) { return i; }
945        }
946            return -1;
947     },
948
949     /**
950      * Removes the specified object from the array.  If the object is not found nothing happens.
951      * @param {Object} o The object to remove
952      */
953     remove : function(o){
954        var index = this.indexOf(o);
955        if(index != -1){
956            this.splice(index, 1);
957        }
958     },
959     /**
960      * Map (JS 1.6 compatibility)
961      * @param {Function} function  to call
962      */
963     map : function(fun )
964     {
965         var len = this.length >>> 0;
966         if (typeof fun != "function") {
967             throw new TypeError();
968         }
969         var res = new Array(len);
970         var thisp = arguments[1];
971         for (var i = 0; i < len; i++)
972         {
973             if (i in this) {
974                 res[i] = fun.call(thisp, this[i], i, this);
975             }
976         }
977
978         return res;
979     }
980     
981 });
982
983
984  
985 /*
986  * Based on:
987  * Ext JS Library 1.1.1
988  * Copyright(c) 2006-2007, Ext JS, LLC.
989  *
990  * Originally Released Under LGPL - original licence link has changed is not relivant.
991  *
992  * Fork - LGPL
993  * <script type="text/javascript">
994  */
995
996 /**
997  * @class Date
998  *
999  * The date parsing and format syntax is a subset of
1000  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1001  * supported will provide results equivalent to their PHP versions.
1002  *
1003  * Following is the list of all currently supported formats:
1004  *<pre>
1005 Sample date:
1006 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1007
1008 Format  Output      Description
1009 ------  ----------  --------------------------------------------------------------
1010   d      10         Day of the month, 2 digits with leading zeros
1011   D      Wed        A textual representation of a day, three letters
1012   j      10         Day of the month without leading zeros
1013   l      Wednesday  A full textual representation of the day of the week
1014   S      th         English ordinal day of month suffix, 2 chars (use with j)
1015   w      3          Numeric representation of the day of the week
1016   z      9          The julian date, or day of the year (0-365)
1017   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1018   F      January    A full textual representation of the month
1019   m      01         Numeric representation of a month, with leading zeros
1020   M      Jan        Month name abbreviation, three letters
1021   n      1          Numeric representation of a month, without leading zeros
1022   t      31         Number of days in the given month
1023   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1024   Y      2007       A full numeric representation of a year, 4 digits
1025   y      07         A two digit representation of a year
1026   a      pm         Lowercase Ante meridiem and Post meridiem
1027   A      PM         Uppercase Ante meridiem and Post meridiem
1028   g      3          12-hour format of an hour without leading zeros
1029   G      15         24-hour format of an hour without leading zeros
1030   h      03         12-hour format of an hour with leading zeros
1031   H      15         24-hour format of an hour with leading zeros
1032   i      05         Minutes with leading zeros
1033   s      01         Seconds, with leading zeros
1034   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1035   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1036   T      CST        Timezone setting of the machine running the code
1037   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1038 </pre>
1039  *
1040  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1041  * <pre><code>
1042 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1043 document.write(dt.format('Y-m-d'));                         //2007-01-10
1044 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1045 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1046  </code></pre>
1047  *
1048  * Here are some standard date/time patterns that you might find helpful.  They
1049  * are not part of the source of Date.js, but to use them you can simply copy this
1050  * block of code into any script that is included after Date.js and they will also become
1051  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1052  * <pre><code>
1053 Date.patterns = {
1054     ISO8601Long:"Y-m-d H:i:s",
1055     ISO8601Short:"Y-m-d",
1056     ShortDate: "n/j/Y",
1057     LongDate: "l, F d, Y",
1058     FullDateTime: "l, F d, Y g:i:s A",
1059     MonthDay: "F d",
1060     ShortTime: "g:i A",
1061     LongTime: "g:i:s A",
1062     SortableDateTime: "Y-m-d\\TH:i:s",
1063     UniversalSortableDateTime: "Y-m-d H:i:sO",
1064     YearMonth: "F, Y"
1065 };
1066 </code></pre>
1067  *
1068  * Example usage:
1069  * <pre><code>
1070 var dt = new Date();
1071 document.write(dt.format(Date.patterns.ShortDate));
1072  </code></pre>
1073  */
1074
1075 /*
1076  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1077  * They generate precompiled functions from date formats instead of parsing and
1078  * processing the pattern every time you format a date.  These functions are available
1079  * on every Date object (any javascript function).
1080  *
1081  * The original article and download are here:
1082  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1083  *
1084  */
1085  
1086  
1087  // was in core
1088 /**
1089  Returns the number of milliseconds between this date and date
1090  @param {Date} date (optional) Defaults to now
1091  @return {Number} The diff in milliseconds
1092  @member Date getElapsed
1093  */
1094 Date.prototype.getElapsed = function(date) {
1095         return Math.abs((date || new Date()).getTime()-this.getTime());
1096 };
1097 // was in date file..
1098
1099
1100 // private
1101 Date.parseFunctions = {count:0};
1102 // private
1103 Date.parseRegexes = [];
1104 // private
1105 Date.formatFunctions = {count:0};
1106
1107 // private
1108 Date.prototype.dateFormat = function(format) {
1109     if (Date.formatFunctions[format] == null) {
1110         Date.createNewFormat(format);
1111     }
1112     var func = Date.formatFunctions[format];
1113     return this[func]();
1114 };
1115
1116
1117 /**
1118  * Formats a date given the supplied format string
1119  * @param {String} format The format string
1120  * @return {String} The formatted date
1121  * @method
1122  */
1123 Date.prototype.format = Date.prototype.dateFormat;
1124
1125 // private
1126 Date.createNewFormat = function(format) {
1127     var funcName = "format" + Date.formatFunctions.count++;
1128     Date.formatFunctions[format] = funcName;
1129     var code = "Date.prototype." + funcName + " = function(){return ";
1130     var special = false;
1131     var ch = '';
1132     for (var i = 0; i < format.length; ++i) {
1133         ch = format.charAt(i);
1134         if (!special && ch == "\\") {
1135             special = true;
1136         }
1137         else if (special) {
1138             special = false;
1139             code += "'" + String.escape(ch) + "' + ";
1140         }
1141         else {
1142             code += Date.getFormatCode(ch);
1143         }
1144     }
1145     /** eval:var:zzzzzzzzzzzzz */
1146     eval(code.substring(0, code.length - 3) + ";}");
1147 };
1148
1149 // private
1150 Date.getFormatCode = function(character) {
1151     switch (character) {
1152     case "d":
1153         return "String.leftPad(this.getDate(), 2, '0') + ";
1154     case "D":
1155         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1156     case "j":
1157         return "this.getDate() + ";
1158     case "l":
1159         return "Date.dayNames[this.getDay()] + ";
1160     case "S":
1161         return "this.getSuffix() + ";
1162     case "w":
1163         return "this.getDay() + ";
1164     case "z":
1165         return "this.getDayOfYear() + ";
1166     case "W":
1167         return "this.getWeekOfYear() + ";
1168     case "F":
1169         return "Date.monthNames[this.getMonth()] + ";
1170     case "m":
1171         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1172     case "M":
1173         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1174     case "n":
1175         return "(this.getMonth() + 1) + ";
1176     case "t":
1177         return "this.getDaysInMonth() + ";
1178     case "L":
1179         return "(this.isLeapYear() ? 1 : 0) + ";
1180     case "Y":
1181         return "this.getFullYear() + ";
1182     case "y":
1183         return "('' + this.getFullYear()).substring(2, 4) + ";
1184     case "a":
1185         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1186     case "A":
1187         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1188     case "g":
1189         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1190     case "G":
1191         return "this.getHours() + ";
1192     case "h":
1193         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1194     case "H":
1195         return "String.leftPad(this.getHours(), 2, '0') + ";
1196     case "i":
1197         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1198     case "s":
1199         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1200     case "O":
1201         return "this.getGMTOffset() + ";
1202     case "P":
1203         return "this.getGMTColonOffset() + ";
1204     case "T":
1205         return "this.getTimezone() + ";
1206     case "Z":
1207         return "(this.getTimezoneOffset() * -60) + ";
1208     default:
1209         return "'" + String.escape(character) + "' + ";
1210     }
1211 };
1212
1213 /**
1214  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1215  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1216  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1217  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1218  * string or the parse operation will fail.
1219  * Example Usage:
1220 <pre><code>
1221 //dt = Fri May 25 2007 (current date)
1222 var dt = new Date();
1223
1224 //dt = Thu May 25 2006 (today's month/day in 2006)
1225 dt = Date.parseDate("2006", "Y");
1226
1227 //dt = Sun Jan 15 2006 (all date parts specified)
1228 dt = Date.parseDate("2006-1-15", "Y-m-d");
1229
1230 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1231 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1232 </code></pre>
1233  * @param {String} input The unparsed date as a string
1234  * @param {String} format The format the date is in
1235  * @return {Date} The parsed date
1236  * @static
1237  */
1238 Date.parseDate = function(input, format) {
1239     if (Date.parseFunctions[format] == null) {
1240         Date.createParser(format);
1241     }
1242     var func = Date.parseFunctions[format];
1243     return Date[func](input);
1244 };
1245 /**
1246  * @private
1247  */
1248
1249 Date.createParser = function(format) {
1250     var funcName = "parse" + Date.parseFunctions.count++;
1251     var regexNum = Date.parseRegexes.length;
1252     var currentGroup = 1;
1253     Date.parseFunctions[format] = funcName;
1254
1255     var code = "Date." + funcName + " = function(input){\n"
1256         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1257         + "var d = new Date();\n"
1258         + "y = d.getFullYear();\n"
1259         + "m = d.getMonth();\n"
1260         + "d = d.getDate();\n"
1261         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1262         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1263         + "if (results && results.length > 0) {";
1264     var regex = "";
1265
1266     var special = false;
1267     var ch = '';
1268     for (var i = 0; i < format.length; ++i) {
1269         ch = format.charAt(i);
1270         if (!special && ch == "\\") {
1271             special = true;
1272         }
1273         else if (special) {
1274             special = false;
1275             regex += String.escape(ch);
1276         }
1277         else {
1278             var obj = Date.formatCodeToRegex(ch, currentGroup);
1279             currentGroup += obj.g;
1280             regex += obj.s;
1281             if (obj.g && obj.c) {
1282                 code += obj.c;
1283             }
1284         }
1285     }
1286
1287     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1288         + "{v = new Date(y, m, d, h, i, s);}\n"
1289         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1290         + "{v = new Date(y, m, d, h, i);}\n"
1291         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1292         + "{v = new Date(y, m, d, h);}\n"
1293         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1294         + "{v = new Date(y, m, d);}\n"
1295         + "else if (y >= 0 && m >= 0)\n"
1296         + "{v = new Date(y, m);}\n"
1297         + "else if (y >= 0)\n"
1298         + "{v = new Date(y);}\n"
1299         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1300         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1301         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1302         + ";}";
1303
1304     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1305     /** eval:var:zzzzzzzzzzzzz */
1306     eval(code);
1307 };
1308
1309 // private
1310 Date.formatCodeToRegex = function(character, currentGroup) {
1311     switch (character) {
1312     case "D":
1313         return {g:0,
1314         c:null,
1315         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1316     case "j":
1317         return {g:1,
1318             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1319             s:"(\\d{1,2})"}; // day of month without leading zeroes
1320     case "d":
1321         return {g:1,
1322             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1323             s:"(\\d{2})"}; // day of month with leading zeroes
1324     case "l":
1325         return {g:0,
1326             c:null,
1327             s:"(?:" + Date.dayNames.join("|") + ")"};
1328     case "S":
1329         return {g:0,
1330             c:null,
1331             s:"(?:st|nd|rd|th)"};
1332     case "w":
1333         return {g:0,
1334             c:null,
1335             s:"\\d"};
1336     case "z":
1337         return {g:0,
1338             c:null,
1339             s:"(?:\\d{1,3})"};
1340     case "W":
1341         return {g:0,
1342             c:null,
1343             s:"(?:\\d{2})"};
1344     case "F":
1345         return {g:1,
1346             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1347             s:"(" + Date.monthNames.join("|") + ")"};
1348     case "M":
1349         return {g:1,
1350             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1351             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1352     case "n":
1353         return {g:1,
1354             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1355             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1356     case "m":
1357         return {g:1,
1358             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1359             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1360     case "t":
1361         return {g:0,
1362             c:null,
1363             s:"\\d{1,2}"};
1364     case "L":
1365         return {g:0,
1366             c:null,
1367             s:"(?:1|0)"};
1368     case "Y":
1369         return {g:1,
1370             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{4})"};
1372     case "y":
1373         return {g:1,
1374             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1375                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1376             s:"(\\d{1,2})"};
1377     case "a":
1378         return {g:1,
1379             c:"if (results[" + currentGroup + "] == 'am') {\n"
1380                 + "if (h == 12) { h = 0; }\n"
1381                 + "} else { if (h < 12) { h += 12; }}",
1382             s:"(am|pm)"};
1383     case "A":
1384         return {g:1,
1385             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1386                 + "if (h == 12) { h = 0; }\n"
1387                 + "} else { if (h < 12) { h += 12; }}",
1388             s:"(AM|PM)"};
1389     case "g":
1390     case "G":
1391         return {g:1,
1392             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1393             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1394     case "h":
1395     case "H":
1396         return {g:1,
1397             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1398             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1399     case "i":
1400         return {g:1,
1401             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1402             s:"(\\d{2})"};
1403     case "s":
1404         return {g:1,
1405             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1406             s:"(\\d{2})"};
1407     case "O":
1408         return {g:1,
1409             c:[
1410                 "o = results[", currentGroup, "];\n",
1411                 "var sn = o.substring(0,1);\n", // get + / - sign
1412                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1413                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1414                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1415                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1416             ].join(""),
1417             s:"([+\-]\\d{2,4})"};
1418     
1419     
1420     case "P":
1421         return {g:1,
1422                 c:[
1423                    "o = results[", currentGroup, "];\n",
1424                    "var sn = o.substring(0,1);\n",
1425                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1426                    "var mn = o.substring(4,6) % 60;\n",
1427                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1428                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1429             ].join(""),
1430             s:"([+\-]\\d{4})"};
1431     case "T":
1432         return {g:0,
1433             c:null,
1434             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1435     case "Z":
1436         return {g:1,
1437             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1438                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1439             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1440     default:
1441         return {g:0,
1442             c:null,
1443             s:String.escape(character)};
1444     }
1445 };
1446
1447 /**
1448  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1449  * @return {String} The abbreviated timezone name (e.g. 'CST')
1450  */
1451 Date.prototype.getTimezone = function() {
1452     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1453 };
1454
1455 /**
1456  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1457  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1458  */
1459 Date.prototype.getGMTOffset = function() {
1460     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1461         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1462         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1463 };
1464
1465 /**
1466  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1467  * @return {String} 2-characters representing hours and 2-characters representing minutes
1468  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1469  */
1470 Date.prototype.getGMTColonOffset = function() {
1471         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1472                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1473                 + ":"
1474                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1475 }
1476
1477 /**
1478  * Get the numeric day number of the year, adjusted for leap year.
1479  * @return {Number} 0 through 364 (365 in leap years)
1480  */
1481 Date.prototype.getDayOfYear = function() {
1482     var num = 0;
1483     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1484     for (var i = 0; i < this.getMonth(); ++i) {
1485         num += Date.daysInMonth[i];
1486     }
1487     return num + this.getDate() - 1;
1488 };
1489
1490 /**
1491  * Get the string representation of the numeric week number of the year
1492  * (equivalent to the format specifier 'W').
1493  * @return {String} '00' through '52'
1494  */
1495 Date.prototype.getWeekOfYear = function() {
1496     // Skip to Thursday of this week
1497     var now = this.getDayOfYear() + (4 - this.getDay());
1498     // Find the first Thursday of the year
1499     var jan1 = new Date(this.getFullYear(), 0, 1);
1500     var then = (7 - jan1.getDay() + 4);
1501     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1502 };
1503
1504 /**
1505  * Whether or not the current date is in a leap year.
1506  * @return {Boolean} True if the current date is in a leap year, else false
1507  */
1508 Date.prototype.isLeapYear = function() {
1509     var year = this.getFullYear();
1510     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1511 };
1512
1513 /**
1514  * Get the first day of the current month, adjusted for leap year.  The returned value
1515  * is the numeric day index within the week (0-6) which can be used in conjunction with
1516  * the {@link #monthNames} array to retrieve the textual day name.
1517  * Example:
1518  *<pre><code>
1519 var dt = new Date('1/10/2007');
1520 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1521 </code></pre>
1522  * @return {Number} The day number (0-6)
1523  */
1524 Date.prototype.getFirstDayOfMonth = function() {
1525     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1526     return (day < 0) ? (day + 7) : day;
1527 };
1528
1529 /**
1530  * Get the last day of the current month, adjusted for leap year.  The returned value
1531  * is the numeric day index within the week (0-6) which can be used in conjunction with
1532  * the {@link #monthNames} array to retrieve the textual day name.
1533  * Example:
1534  *<pre><code>
1535 var dt = new Date('1/10/2007');
1536 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1537 </code></pre>
1538  * @return {Number} The day number (0-6)
1539  */
1540 Date.prototype.getLastDayOfMonth = function() {
1541     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1542     return (day < 0) ? (day + 7) : day;
1543 };
1544
1545
1546 /**
1547  * Get the first date of this date's month
1548  * @return {Date}
1549  */
1550 Date.prototype.getFirstDateOfMonth = function() {
1551     return new Date(this.getFullYear(), this.getMonth(), 1);
1552 };
1553
1554 /**
1555  * Get the last date of this date's month
1556  * @return {Date}
1557  */
1558 Date.prototype.getLastDateOfMonth = function() {
1559     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1560 };
1561 /**
1562  * Get the number of days in the current month, adjusted for leap year.
1563  * @return {Number} The number of days in the month
1564  */
1565 Date.prototype.getDaysInMonth = function() {
1566     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1567     return Date.daysInMonth[this.getMonth()];
1568 };
1569
1570 /**
1571  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1572  * @return {String} 'st, 'nd', 'rd' or 'th'
1573  */
1574 Date.prototype.getSuffix = function() {
1575     switch (this.getDate()) {
1576         case 1:
1577         case 21:
1578         case 31:
1579             return "st";
1580         case 2:
1581         case 22:
1582             return "nd";
1583         case 3:
1584         case 23:
1585             return "rd";
1586         default:
1587             return "th";
1588     }
1589 };
1590
1591 // private
1592 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1593
1594 /**
1595  * An array of textual month names.
1596  * Override these values for international dates, for example...
1597  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1598  * @type Array
1599  * @static
1600  */
1601 Date.monthNames =
1602    ["January",
1603     "February",
1604     "March",
1605     "April",
1606     "May",
1607     "June",
1608     "July",
1609     "August",
1610     "September",
1611     "October",
1612     "November",
1613     "December"];
1614
1615 /**
1616  * An array of textual day names.
1617  * Override these values for international dates, for example...
1618  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1619  * @type Array
1620  * @static
1621  */
1622 Date.dayNames =
1623    ["Sunday",
1624     "Monday",
1625     "Tuesday",
1626     "Wednesday",
1627     "Thursday",
1628     "Friday",
1629     "Saturday"];
1630
1631 // private
1632 Date.y2kYear = 50;
1633 // private
1634 Date.monthNumbers = {
1635     Jan:0,
1636     Feb:1,
1637     Mar:2,
1638     Apr:3,
1639     May:4,
1640     Jun:5,
1641     Jul:6,
1642     Aug:7,
1643     Sep:8,
1644     Oct:9,
1645     Nov:10,
1646     Dec:11};
1647
1648 /**
1649  * Creates and returns a new Date instance with the exact same date value as the called instance.
1650  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1651  * variable will also be changed.  When the intention is to create a new variable that will not
1652  * modify the original instance, you should create a clone.
1653  *
1654  * Example of correctly cloning a date:
1655  * <pre><code>
1656 //wrong way:
1657 var orig = new Date('10/1/2006');
1658 var copy = orig;
1659 copy.setDate(5);
1660 document.write(orig);  //returns 'Thu Oct 05 2006'!
1661
1662 //correct way:
1663 var orig = new Date('10/1/2006');
1664 var copy = orig.clone();
1665 copy.setDate(5);
1666 document.write(orig);  //returns 'Thu Oct 01 2006'
1667 </code></pre>
1668  * @return {Date} The new Date instance
1669  */
1670 Date.prototype.clone = function() {
1671         return new Date(this.getTime());
1672 };
1673
1674 /**
1675  * Clears any time information from this date
1676  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1677  @return {Date} this or the clone
1678  */
1679 Date.prototype.clearTime = function(clone){
1680     if(clone){
1681         return this.clone().clearTime();
1682     }
1683     this.setHours(0);
1684     this.setMinutes(0);
1685     this.setSeconds(0);
1686     this.setMilliseconds(0);
1687     return this;
1688 };
1689
1690 // private
1691 // safari setMonth is broken
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
37195         // private ??
37196         initComponent : function(){
37197         Roo.form.Field.superclass.initComponent.call(this);
37198         this.addEvents({
37199             /**
37200              * @event focus
37201              * Fires when this field receives input focus.
37202              * @param {Roo.form.Field} this
37203              */
37204             focus : true,
37205             /**
37206              * @event blur
37207              * Fires when this field loses input focus.
37208              * @param {Roo.form.Field} this
37209              */
37210             blur : true,
37211             /**
37212              * @event specialkey
37213              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37214              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37215              * @param {Roo.form.Field} this
37216              * @param {Roo.EventObject} e The event object
37217              */
37218             specialkey : true,
37219             /**
37220              * @event change
37221              * Fires just before the field blurs if the field value has changed.
37222              * @param {Roo.form.Field} this
37223              * @param {Mixed} newValue The new value
37224              * @param {Mixed} oldValue The original value
37225              */
37226             change : true,
37227             /**
37228              * @event invalid
37229              * Fires after the field has been marked as invalid.
37230              * @param {Roo.form.Field} this
37231              * @param {String} msg The validation message
37232              */
37233             invalid : true,
37234             /**
37235              * @event valid
37236              * Fires after the field has been validated with no errors.
37237              * @param {Roo.form.Field} this
37238              */
37239             valid : true,
37240              /**
37241              * @event keyup
37242              * Fires after the key up
37243              * @param {Roo.form.Field} this
37244              * @param {Roo.EventObject}  e The event Object
37245              */
37246             keyup : true
37247         });
37248     },
37249
37250     /**
37251      * Returns the name attribute of the field if available
37252      * @return {String} name The field name
37253      */
37254     getName: function(){
37255          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37256     },
37257
37258     // private
37259     onRender : function(ct, position){
37260         Roo.form.Field.superclass.onRender.call(this, ct, position);
37261         if(!this.el){
37262             var cfg = this.getAutoCreate();
37263             if(!cfg.name){
37264                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37265             }
37266             if (!cfg.name.length) {
37267                 delete cfg.name;
37268             }
37269             if(this.inputType){
37270                 cfg.type = this.inputType;
37271             }
37272             this.el = ct.createChild(cfg, position);
37273         }
37274         var type = this.el.dom.type;
37275         if(type){
37276             if(type == 'password'){
37277                 type = 'text';
37278             }
37279             this.el.addClass('x-form-'+type);
37280         }
37281         if(this.readOnly){
37282             this.el.dom.readOnly = true;
37283         }
37284         if(this.tabIndex !== undefined){
37285             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37286         }
37287
37288         this.el.addClass([this.fieldClass, this.cls]);
37289         this.initValue();
37290     },
37291
37292     /**
37293      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37294      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37295      * @return {Roo.form.Field} this
37296      */
37297     applyTo : function(target){
37298         this.allowDomMove = false;
37299         this.el = Roo.get(target);
37300         this.render(this.el.dom.parentNode);
37301         return this;
37302     },
37303
37304     // private
37305     initValue : function(){
37306         if(this.value !== undefined){
37307             this.setValue(this.value);
37308         }else if(this.el.dom.value.length > 0){
37309             this.setValue(this.el.dom.value);
37310         }
37311     },
37312
37313     /**
37314      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37315      */
37316     isDirty : function() {
37317         if(this.disabled) {
37318             return false;
37319         }
37320         return String(this.getValue()) !== String(this.originalValue);
37321     },
37322
37323     // private
37324     afterRender : function(){
37325         Roo.form.Field.superclass.afterRender.call(this);
37326         this.initEvents();
37327     },
37328
37329     // private
37330     fireKey : function(e){
37331         //Roo.log('field ' + e.getKey());
37332         if(e.isNavKeyPress()){
37333             this.fireEvent("specialkey", this, e);
37334         }
37335     },
37336
37337     /**
37338      * Resets the current field value to the originally loaded value and clears any validation messages
37339      */
37340     reset : function(){
37341         this.setValue(this.resetValue);
37342         this.clearInvalid();
37343     },
37344
37345     // private
37346     initEvents : function(){
37347         // safari killled keypress - so keydown is now used..
37348         this.el.on("keydown" , this.fireKey,  this);
37349         this.el.on("focus", this.onFocus,  this);
37350         this.el.on("blur", this.onBlur,  this);
37351         this.el.relayEvent('keyup', this);
37352
37353         // reference to original value for reset
37354         this.originalValue = this.getValue();
37355         this.resetValue =  this.getValue();
37356     },
37357
37358     // private
37359     onFocus : function(){
37360         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37361             this.el.addClass(this.focusClass);
37362         }
37363         if(!this.hasFocus){
37364             this.hasFocus = true;
37365             this.startValue = this.getValue();
37366             this.fireEvent("focus", this);
37367         }
37368     },
37369
37370     beforeBlur : Roo.emptyFn,
37371
37372     // private
37373     onBlur : function(){
37374         this.beforeBlur();
37375         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37376             this.el.removeClass(this.focusClass);
37377         }
37378         this.hasFocus = false;
37379         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37380             this.validate();
37381         }
37382         var v = this.getValue();
37383         if(String(v) !== String(this.startValue)){
37384             this.fireEvent('change', this, v, this.startValue);
37385         }
37386         this.fireEvent("blur", this);
37387     },
37388
37389     /**
37390      * Returns whether or not the field value is currently valid
37391      * @param {Boolean} preventMark True to disable marking the field invalid
37392      * @return {Boolean} True if the value is valid, else false
37393      */
37394     isValid : function(preventMark){
37395         if(this.disabled){
37396             return true;
37397         }
37398         var restore = this.preventMark;
37399         this.preventMark = preventMark === true;
37400         var v = this.validateValue(this.processValue(this.getRawValue()));
37401         this.preventMark = restore;
37402         return v;
37403     },
37404
37405     /**
37406      * Validates the field value
37407      * @return {Boolean} True if the value is valid, else false
37408      */
37409     validate : function(){
37410         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37411             this.clearInvalid();
37412             return true;
37413         }
37414         return false;
37415     },
37416
37417     processValue : function(value){
37418         return value;
37419     },
37420
37421     // private
37422     // Subclasses should provide the validation implementation by overriding this
37423     validateValue : function(value){
37424         return true;
37425     },
37426
37427     /**
37428      * Mark this field as invalid
37429      * @param {String} msg The validation message
37430      */
37431     markInvalid : function(msg){
37432         if(!this.rendered || this.preventMark){ // not rendered
37433             return;
37434         }
37435         
37436         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37437         
37438         obj.el.addClass(this.invalidClass);
37439         msg = msg || this.invalidText;
37440         switch(this.msgTarget){
37441             case 'qtip':
37442                 obj.el.dom.qtip = msg;
37443                 obj.el.dom.qclass = 'x-form-invalid-tip';
37444                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37445                     Roo.QuickTips.enable();
37446                 }
37447                 break;
37448             case 'title':
37449                 this.el.dom.title = msg;
37450                 break;
37451             case 'under':
37452                 if(!this.errorEl){
37453                     var elp = this.el.findParent('.x-form-element', 5, true);
37454                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37455                     this.errorEl.setWidth(elp.getWidth(true)-20);
37456                 }
37457                 this.errorEl.update(msg);
37458                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37459                 break;
37460             case 'side':
37461                 if(!this.errorIcon){
37462                     var elp = this.el.findParent('.x-form-element', 5, true);
37463                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37464                 }
37465                 this.alignErrorIcon();
37466                 this.errorIcon.dom.qtip = msg;
37467                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37468                 this.errorIcon.show();
37469                 this.on('resize', this.alignErrorIcon, this);
37470                 break;
37471             default:
37472                 var t = Roo.getDom(this.msgTarget);
37473                 t.innerHTML = msg;
37474                 t.style.display = this.msgDisplay;
37475                 break;
37476         }
37477         this.fireEvent('invalid', this, msg);
37478     },
37479
37480     // private
37481     alignErrorIcon : function(){
37482         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37483     },
37484
37485     /**
37486      * Clear any invalid styles/messages for this field
37487      */
37488     clearInvalid : function(){
37489         if(!this.rendered || this.preventMark){ // not rendered
37490             return;
37491         }
37492         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37493         
37494         obj.el.removeClass(this.invalidClass);
37495         switch(this.msgTarget){
37496             case 'qtip':
37497                 obj.el.dom.qtip = '';
37498                 break;
37499             case 'title':
37500                 this.el.dom.title = '';
37501                 break;
37502             case 'under':
37503                 if(this.errorEl){
37504                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37505                 }
37506                 break;
37507             case 'side':
37508                 if(this.errorIcon){
37509                     this.errorIcon.dom.qtip = '';
37510                     this.errorIcon.hide();
37511                     this.un('resize', this.alignErrorIcon, this);
37512                 }
37513                 break;
37514             default:
37515                 var t = Roo.getDom(this.msgTarget);
37516                 t.innerHTML = '';
37517                 t.style.display = 'none';
37518                 break;
37519         }
37520         this.fireEvent('valid', this);
37521     },
37522
37523     /**
37524      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37525      * @return {Mixed} value The field value
37526      */
37527     getRawValue : function(){
37528         var v = this.el.getValue();
37529         
37530         return v;
37531     },
37532
37533     /**
37534      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37535      * @return {Mixed} value The field value
37536      */
37537     getValue : function(){
37538         var v = this.el.getValue();
37539          
37540         return v;
37541     },
37542
37543     /**
37544      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37545      * @param {Mixed} value The value to set
37546      */
37547     setRawValue : function(v){
37548         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37549     },
37550
37551     /**
37552      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37553      * @param {Mixed} value The value to set
37554      */
37555     setValue : function(v){
37556         this.value = v;
37557         if(this.rendered){
37558             this.el.dom.value = (v === null || v === undefined ? '' : v);
37559              this.validate();
37560         }
37561     },
37562
37563     adjustSize : function(w, h){
37564         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37565         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37566         return s;
37567     },
37568
37569     adjustWidth : function(tag, w){
37570         tag = tag.toLowerCase();
37571         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37572             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37573                 if(tag == 'input'){
37574                     return w + 2;
37575                 }
37576                 if(tag == 'textarea'){
37577                     return w-2;
37578                 }
37579             }else if(Roo.isOpera){
37580                 if(tag == 'input'){
37581                     return w + 2;
37582                 }
37583                 if(tag == 'textarea'){
37584                     return w-2;
37585                 }
37586             }
37587         }
37588         return w;
37589     }
37590 });
37591
37592
37593 // anything other than normal should be considered experimental
37594 Roo.form.Field.msgFx = {
37595     normal : {
37596         show: function(msgEl, f){
37597             msgEl.setDisplayed('block');
37598         },
37599
37600         hide : function(msgEl, f){
37601             msgEl.setDisplayed(false).update('');
37602         }
37603     },
37604
37605     slide : {
37606         show: function(msgEl, f){
37607             msgEl.slideIn('t', {stopFx:true});
37608         },
37609
37610         hide : function(msgEl, f){
37611             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37612         }
37613     },
37614
37615     slideRight : {
37616         show: function(msgEl, f){
37617             msgEl.fixDisplay();
37618             msgEl.alignTo(f.el, 'tl-tr');
37619             msgEl.slideIn('l', {stopFx:true});
37620         },
37621
37622         hide : function(msgEl, f){
37623             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37624         }
37625     }
37626 };/*
37627  * Based on:
37628  * Ext JS Library 1.1.1
37629  * Copyright(c) 2006-2007, Ext JS, LLC.
37630  *
37631  * Originally Released Under LGPL - original licence link has changed is not relivant.
37632  *
37633  * Fork - LGPL
37634  * <script type="text/javascript">
37635  */
37636  
37637
37638 /**
37639  * @class Roo.form.TextField
37640  * @extends Roo.form.Field
37641  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37642  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37643  * @constructor
37644  * Creates a new TextField
37645  * @param {Object} config Configuration options
37646  */
37647 Roo.form.TextField = function(config){
37648     Roo.form.TextField.superclass.constructor.call(this, config);
37649     this.addEvents({
37650         /**
37651          * @event autosize
37652          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37653          * according to the default logic, but this event provides a hook for the developer to apply additional
37654          * logic at runtime to resize the field if needed.
37655              * @param {Roo.form.Field} this This text field
37656              * @param {Number} width The new field width
37657              */
37658         autosize : true
37659     });
37660 };
37661
37662 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37663     /**
37664      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37665      */
37666     grow : false,
37667     /**
37668      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37669      */
37670     growMin : 30,
37671     /**
37672      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37673      */
37674     growMax : 800,
37675     /**
37676      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37677      */
37678     vtype : null,
37679     /**
37680      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37681      */
37682     maskRe : null,
37683     /**
37684      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37685      */
37686     disableKeyFilter : false,
37687     /**
37688      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37689      */
37690     allowBlank : true,
37691     /**
37692      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37693      */
37694     minLength : 0,
37695     /**
37696      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37697      */
37698     maxLength : Number.MAX_VALUE,
37699     /**
37700      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37701      */
37702     minLengthText : "The minimum length for this field is {0}",
37703     /**
37704      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37705      */
37706     maxLengthText : "The maximum length for this field is {0}",
37707     /**
37708      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37709      */
37710     selectOnFocus : false,
37711     /**
37712      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37713      */
37714     blankText : "This field is required",
37715     /**
37716      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37717      * If available, this function will be called only after the basic validators all return true, and will be passed the
37718      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37719      */
37720     validator : null,
37721     /**
37722      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37723      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37724      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37725      */
37726     regex : null,
37727     /**
37728      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37729      */
37730     regexText : "",
37731     /**
37732      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37733      */
37734     emptyText : null,
37735    
37736
37737     // private
37738     initEvents : function()
37739     {
37740         if (this.emptyText) {
37741             this.el.attr('placeholder', this.emptyText);
37742         }
37743         
37744         Roo.form.TextField.superclass.initEvents.call(this);
37745         if(this.validationEvent == 'keyup'){
37746             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37747             this.el.on('keyup', this.filterValidation, this);
37748         }
37749         else if(this.validationEvent !== false){
37750             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37751         }
37752         
37753         if(this.selectOnFocus){
37754             this.on("focus", this.preFocus, this);
37755             
37756         }
37757         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37758             this.el.on("keypress", this.filterKeys, this);
37759         }
37760         if(this.grow){
37761             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37762             this.el.on("click", this.autoSize,  this);
37763         }
37764         if(this.el.is('input[type=password]') && Roo.isSafari){
37765             this.el.on('keydown', this.SafariOnKeyDown, this);
37766         }
37767     },
37768
37769     processValue : function(value){
37770         if(this.stripCharsRe){
37771             var newValue = value.replace(this.stripCharsRe, '');
37772             if(newValue !== value){
37773                 this.setRawValue(newValue);
37774                 return newValue;
37775             }
37776         }
37777         return value;
37778     },
37779
37780     filterValidation : function(e){
37781         if(!e.isNavKeyPress()){
37782             this.validationTask.delay(this.validationDelay);
37783         }
37784     },
37785
37786     // private
37787     onKeyUp : function(e){
37788         if(!e.isNavKeyPress()){
37789             this.autoSize();
37790         }
37791     },
37792
37793     /**
37794      * Resets the current field value to the originally-loaded value and clears any validation messages.
37795      *  
37796      */
37797     reset : function(){
37798         Roo.form.TextField.superclass.reset.call(this);
37799        
37800     },
37801
37802     
37803     // private
37804     preFocus : function(){
37805         
37806         if(this.selectOnFocus){
37807             this.el.dom.select();
37808         }
37809     },
37810
37811     
37812     // private
37813     filterKeys : function(e){
37814         var k = e.getKey();
37815         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37816             return;
37817         }
37818         var c = e.getCharCode(), cc = String.fromCharCode(c);
37819         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37820             return;
37821         }
37822         if(!this.maskRe.test(cc)){
37823             e.stopEvent();
37824         }
37825     },
37826
37827     setValue : function(v){
37828         
37829         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37830         
37831         this.autoSize();
37832     },
37833
37834     /**
37835      * Validates a value according to the field's validation rules and marks the field as invalid
37836      * if the validation fails
37837      * @param {Mixed} value The value to validate
37838      * @return {Boolean} True if the value is valid, else false
37839      */
37840     validateValue : function(value){
37841         if(value.length < 1)  { // if it's blank
37842              if(this.allowBlank){
37843                 this.clearInvalid();
37844                 return true;
37845              }else{
37846                 this.markInvalid(this.blankText);
37847                 return false;
37848              }
37849         }
37850         if(value.length < this.minLength){
37851             this.markInvalid(String.format(this.minLengthText, this.minLength));
37852             return false;
37853         }
37854         if(value.length > this.maxLength){
37855             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37856             return false;
37857         }
37858         if(this.vtype){
37859             var vt = Roo.form.VTypes;
37860             if(!vt[this.vtype](value, this)){
37861                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37862                 return false;
37863             }
37864         }
37865         if(typeof this.validator == "function"){
37866             var msg = this.validator(value);
37867             if(msg !== true){
37868                 this.markInvalid(msg);
37869                 return false;
37870             }
37871         }
37872         if(this.regex && !this.regex.test(value)){
37873             this.markInvalid(this.regexText);
37874             return false;
37875         }
37876         return true;
37877     },
37878
37879     /**
37880      * Selects text in this field
37881      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37882      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37883      */
37884     selectText : function(start, end){
37885         var v = this.getRawValue();
37886         if(v.length > 0){
37887             start = start === undefined ? 0 : start;
37888             end = end === undefined ? v.length : end;
37889             var d = this.el.dom;
37890             if(d.setSelectionRange){
37891                 d.setSelectionRange(start, end);
37892             }else if(d.createTextRange){
37893                 var range = d.createTextRange();
37894                 range.moveStart("character", start);
37895                 range.moveEnd("character", v.length-end);
37896                 range.select();
37897             }
37898         }
37899     },
37900
37901     /**
37902      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37903      * This only takes effect if grow = true, and fires the autosize event.
37904      */
37905     autoSize : function(){
37906         if(!this.grow || !this.rendered){
37907             return;
37908         }
37909         if(!this.metrics){
37910             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37911         }
37912         var el = this.el;
37913         var v = el.dom.value;
37914         var d = document.createElement('div');
37915         d.appendChild(document.createTextNode(v));
37916         v = d.innerHTML;
37917         d = null;
37918         v += "&#160;";
37919         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37920         this.el.setWidth(w);
37921         this.fireEvent("autosize", this, w);
37922     },
37923     
37924     // private
37925     SafariOnKeyDown : function(event)
37926     {
37927         // this is a workaround for a password hang bug on chrome/ webkit.
37928         
37929         var isSelectAll = false;
37930         
37931         if(this.el.dom.selectionEnd > 0){
37932             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37933         }
37934         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37935             event.preventDefault();
37936             this.setValue('');
37937             return;
37938         }
37939         
37940         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37941             
37942             event.preventDefault();
37943             // this is very hacky as keydown always get's upper case.
37944             
37945             var cc = String.fromCharCode(event.getCharCode());
37946             
37947             
37948             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37949             
37950         }
37951         
37952         
37953     }
37954 });/*
37955  * Based on:
37956  * Ext JS Library 1.1.1
37957  * Copyright(c) 2006-2007, Ext JS, LLC.
37958  *
37959  * Originally Released Under LGPL - original licence link has changed is not relivant.
37960  *
37961  * Fork - LGPL
37962  * <script type="text/javascript">
37963  */
37964  
37965 /**
37966  * @class Roo.form.Hidden
37967  * @extends Roo.form.TextField
37968  * Simple Hidden element used on forms 
37969  * 
37970  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37971  * 
37972  * @constructor
37973  * Creates a new Hidden form element.
37974  * @param {Object} config Configuration options
37975  */
37976
37977
37978
37979 // easy hidden field...
37980 Roo.form.Hidden = function(config){
37981     Roo.form.Hidden.superclass.constructor.call(this, config);
37982 };
37983   
37984 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37985     fieldLabel:      '',
37986     inputType:      'hidden',
37987     width:          50,
37988     allowBlank:     true,
37989     labelSeparator: '',
37990     hidden:         true,
37991     itemCls :       'x-form-item-display-none'
37992
37993
37994 });
37995
37996
37997 /*
37998  * Based on:
37999  * Ext JS Library 1.1.1
38000  * Copyright(c) 2006-2007, Ext JS, LLC.
38001  *
38002  * Originally Released Under LGPL - original licence link has changed is not relivant.
38003  *
38004  * Fork - LGPL
38005  * <script type="text/javascript">
38006  */
38007  
38008 /**
38009  * @class Roo.form.TriggerField
38010  * @extends Roo.form.TextField
38011  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
38012  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
38013  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
38014  * for which you can provide a custom implementation.  For example:
38015  * <pre><code>
38016 var trigger = new Roo.form.TriggerField();
38017 trigger.onTriggerClick = myTriggerFn;
38018 trigger.applyTo('my-field');
38019 </code></pre>
38020  *
38021  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
38022  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
38023  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
38024  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
38025  * @constructor
38026  * Create a new TriggerField.
38027  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
38028  * to the base TextField)
38029  */
38030 Roo.form.TriggerField = function(config){
38031     this.mimicing = false;
38032     Roo.form.TriggerField.superclass.constructor.call(this, config);
38033 };
38034
38035 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
38036     /**
38037      * @cfg {String} triggerClass A CSS class to apply to the trigger
38038      */
38039     /**
38040      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38041      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
38042      */
38043     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
38044     /**
38045      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38046      */
38047     hideTrigger:false,
38048
38049     /** @cfg {Boolean} grow @hide */
38050     /** @cfg {Number} growMin @hide */
38051     /** @cfg {Number} growMax @hide */
38052
38053     /**
38054      * @hide 
38055      * @method
38056      */
38057     autoSize: Roo.emptyFn,
38058     // private
38059     monitorTab : true,
38060     // private
38061     deferHeight : true,
38062
38063     
38064     actionMode : 'wrap',
38065     // private
38066     onResize : function(w, h){
38067         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38068         if(typeof w == 'number'){
38069             var x = w - this.trigger.getWidth();
38070             this.el.setWidth(this.adjustWidth('input', x));
38071             this.trigger.setStyle('left', x+'px');
38072         }
38073     },
38074
38075     // private
38076     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38077
38078     // private
38079     getResizeEl : function(){
38080         return this.wrap;
38081     },
38082
38083     // private
38084     getPositionEl : function(){
38085         return this.wrap;
38086     },
38087
38088     // private
38089     alignErrorIcon : function(){
38090         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38091     },
38092
38093     // private
38094     onRender : function(ct, position){
38095         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38096         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38097         this.trigger = this.wrap.createChild(this.triggerConfig ||
38098                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38099         if(this.hideTrigger){
38100             this.trigger.setDisplayed(false);
38101         }
38102         this.initTrigger();
38103         if(!this.width){
38104             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38105         }
38106     },
38107
38108     // private
38109     initTrigger : function(){
38110         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38111         this.trigger.addClassOnOver('x-form-trigger-over');
38112         this.trigger.addClassOnClick('x-form-trigger-click');
38113     },
38114
38115     // private
38116     onDestroy : function(){
38117         if(this.trigger){
38118             this.trigger.removeAllListeners();
38119             this.trigger.remove();
38120         }
38121         if(this.wrap){
38122             this.wrap.remove();
38123         }
38124         Roo.form.TriggerField.superclass.onDestroy.call(this);
38125     },
38126
38127     // private
38128     onFocus : function(){
38129         Roo.form.TriggerField.superclass.onFocus.call(this);
38130         if(!this.mimicing){
38131             this.wrap.addClass('x-trigger-wrap-focus');
38132             this.mimicing = true;
38133             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38134             if(this.monitorTab){
38135                 this.el.on("keydown", this.checkTab, this);
38136             }
38137         }
38138     },
38139
38140     // private
38141     checkTab : function(e){
38142         if(e.getKey() == e.TAB){
38143             this.triggerBlur();
38144         }
38145     },
38146
38147     // private
38148     onBlur : function(){
38149         // do nothing
38150     },
38151
38152     // private
38153     mimicBlur : function(e, t){
38154         if(!this.wrap.contains(t) && this.validateBlur()){
38155             this.triggerBlur();
38156         }
38157     },
38158
38159     // private
38160     triggerBlur : function(){
38161         this.mimicing = false;
38162         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38163         if(this.monitorTab){
38164             this.el.un("keydown", this.checkTab, this);
38165         }
38166         this.wrap.removeClass('x-trigger-wrap-focus');
38167         Roo.form.TriggerField.superclass.onBlur.call(this);
38168     },
38169
38170     // private
38171     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38172     validateBlur : function(e, t){
38173         return true;
38174     },
38175
38176     // private
38177     onDisable : function(){
38178         Roo.form.TriggerField.superclass.onDisable.call(this);
38179         if(this.wrap){
38180             this.wrap.addClass('x-item-disabled');
38181         }
38182     },
38183
38184     // private
38185     onEnable : function(){
38186         Roo.form.TriggerField.superclass.onEnable.call(this);
38187         if(this.wrap){
38188             this.wrap.removeClass('x-item-disabled');
38189         }
38190     },
38191
38192     // private
38193     onShow : function(){
38194         var ae = this.getActionEl();
38195         
38196         if(ae){
38197             ae.dom.style.display = '';
38198             ae.dom.style.visibility = 'visible';
38199         }
38200     },
38201
38202     // private
38203     
38204     onHide : function(){
38205         var ae = this.getActionEl();
38206         ae.dom.style.display = 'none';
38207     },
38208
38209     /**
38210      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38211      * by an implementing function.
38212      * @method
38213      * @param {EventObject} e
38214      */
38215     onTriggerClick : Roo.emptyFn
38216 });
38217
38218 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38219 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38220 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38221 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38222     initComponent : function(){
38223         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38224
38225         this.triggerConfig = {
38226             tag:'span', cls:'x-form-twin-triggers', cn:[
38227             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38228             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38229         ]};
38230     },
38231
38232     getTrigger : function(index){
38233         return this.triggers[index];
38234     },
38235
38236     initTrigger : function(){
38237         var ts = this.trigger.select('.x-form-trigger', true);
38238         this.wrap.setStyle('overflow', 'hidden');
38239         var triggerField = this;
38240         ts.each(function(t, all, index){
38241             t.hide = function(){
38242                 var w = triggerField.wrap.getWidth();
38243                 this.dom.style.display = 'none';
38244                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38245             };
38246             t.show = function(){
38247                 var w = triggerField.wrap.getWidth();
38248                 this.dom.style.display = '';
38249                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38250             };
38251             var triggerIndex = 'Trigger'+(index+1);
38252
38253             if(this['hide'+triggerIndex]){
38254                 t.dom.style.display = 'none';
38255             }
38256             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38257             t.addClassOnOver('x-form-trigger-over');
38258             t.addClassOnClick('x-form-trigger-click');
38259         }, this);
38260         this.triggers = ts.elements;
38261     },
38262
38263     onTrigger1Click : Roo.emptyFn,
38264     onTrigger2Click : Roo.emptyFn
38265 });/*
38266  * Based on:
38267  * Ext JS Library 1.1.1
38268  * Copyright(c) 2006-2007, Ext JS, LLC.
38269  *
38270  * Originally Released Under LGPL - original licence link has changed is not relivant.
38271  *
38272  * Fork - LGPL
38273  * <script type="text/javascript">
38274  */
38275  
38276 /**
38277  * @class Roo.form.TextArea
38278  * @extends Roo.form.TextField
38279  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38280  * support for auto-sizing.
38281  * @constructor
38282  * Creates a new TextArea
38283  * @param {Object} config Configuration options
38284  */
38285 Roo.form.TextArea = function(config){
38286     Roo.form.TextArea.superclass.constructor.call(this, config);
38287     // these are provided exchanges for backwards compat
38288     // minHeight/maxHeight were replaced by growMin/growMax to be
38289     // compatible with TextField growing config values
38290     if(this.minHeight !== undefined){
38291         this.growMin = this.minHeight;
38292     }
38293     if(this.maxHeight !== undefined){
38294         this.growMax = this.maxHeight;
38295     }
38296 };
38297
38298 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38299     /**
38300      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38301      */
38302     growMin : 60,
38303     /**
38304      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38305      */
38306     growMax: 1000,
38307     /**
38308      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38309      * in the field (equivalent to setting overflow: hidden, defaults to false)
38310      */
38311     preventScrollbars: false,
38312     /**
38313      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38314      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38315      */
38316
38317     // private
38318     onRender : function(ct, position){
38319         if(!this.el){
38320             this.defaultAutoCreate = {
38321                 tag: "textarea",
38322                 style:"width:300px;height:60px;",
38323                 autocomplete: "new-password"
38324             };
38325         }
38326         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38327         if(this.grow){
38328             this.textSizeEl = Roo.DomHelper.append(document.body, {
38329                 tag: "pre", cls: "x-form-grow-sizer"
38330             });
38331             if(this.preventScrollbars){
38332                 this.el.setStyle("overflow", "hidden");
38333             }
38334             this.el.setHeight(this.growMin);
38335         }
38336     },
38337
38338     onDestroy : function(){
38339         if(this.textSizeEl){
38340             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38341         }
38342         Roo.form.TextArea.superclass.onDestroy.call(this);
38343     },
38344
38345     // private
38346     onKeyUp : function(e){
38347         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38348             this.autoSize();
38349         }
38350     },
38351
38352     /**
38353      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38354      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38355      */
38356     autoSize : function(){
38357         if(!this.grow || !this.textSizeEl){
38358             return;
38359         }
38360         var el = this.el;
38361         var v = el.dom.value;
38362         var ts = this.textSizeEl;
38363
38364         ts.innerHTML = '';
38365         ts.appendChild(document.createTextNode(v));
38366         v = ts.innerHTML;
38367
38368         Roo.fly(ts).setWidth(this.el.getWidth());
38369         if(v.length < 1){
38370             v = "&#160;&#160;";
38371         }else{
38372             if(Roo.isIE){
38373                 v = v.replace(/\n/g, '<p>&#160;</p>');
38374             }
38375             v += "&#160;\n&#160;";
38376         }
38377         ts.innerHTML = v;
38378         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38379         if(h != this.lastHeight){
38380             this.lastHeight = h;
38381             this.el.setHeight(h);
38382             this.fireEvent("autosize", this, h);
38383         }
38384     }
38385 });/*
38386  * Based on:
38387  * Ext JS Library 1.1.1
38388  * Copyright(c) 2006-2007, Ext JS, LLC.
38389  *
38390  * Originally Released Under LGPL - original licence link has changed is not relivant.
38391  *
38392  * Fork - LGPL
38393  * <script type="text/javascript">
38394  */
38395  
38396
38397 /**
38398  * @class Roo.form.NumberField
38399  * @extends Roo.form.TextField
38400  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38401  * @constructor
38402  * Creates a new NumberField
38403  * @param {Object} config Configuration options
38404  */
38405 Roo.form.NumberField = function(config){
38406     Roo.form.NumberField.superclass.constructor.call(this, config);
38407 };
38408
38409 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38410     /**
38411      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38412      */
38413     fieldClass: "x-form-field x-form-num-field",
38414     /**
38415      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38416      */
38417     allowDecimals : true,
38418     /**
38419      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38420      */
38421     decimalSeparator : ".",
38422     /**
38423      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38424      */
38425     decimalPrecision : 2,
38426     /**
38427      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38428      */
38429     allowNegative : true,
38430     /**
38431      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38432      */
38433     minValue : Number.NEGATIVE_INFINITY,
38434     /**
38435      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38436      */
38437     maxValue : Number.MAX_VALUE,
38438     /**
38439      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38440      */
38441     minText : "The minimum value for this field is {0}",
38442     /**
38443      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38444      */
38445     maxText : "The maximum value for this field is {0}",
38446     /**
38447      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38448      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38449      */
38450     nanText : "{0} is not a valid number",
38451
38452     // private
38453     initEvents : function(){
38454         Roo.form.NumberField.superclass.initEvents.call(this);
38455         var allowed = "0123456789";
38456         if(this.allowDecimals){
38457             allowed += this.decimalSeparator;
38458         }
38459         if(this.allowNegative){
38460             allowed += "-";
38461         }
38462         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38463         var keyPress = function(e){
38464             var k = e.getKey();
38465             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38466                 return;
38467             }
38468             var c = e.getCharCode();
38469             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38470                 e.stopEvent();
38471             }
38472         };
38473         this.el.on("keypress", keyPress, this);
38474     },
38475
38476     // private
38477     validateValue : function(value){
38478         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38479             return false;
38480         }
38481         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38482              return true;
38483         }
38484         var num = this.parseValue(value);
38485         if(isNaN(num)){
38486             this.markInvalid(String.format(this.nanText, value));
38487             return false;
38488         }
38489         if(num < this.minValue){
38490             this.markInvalid(String.format(this.minText, this.minValue));
38491             return false;
38492         }
38493         if(num > this.maxValue){
38494             this.markInvalid(String.format(this.maxText, this.maxValue));
38495             return false;
38496         }
38497         return true;
38498     },
38499
38500     getValue : function(){
38501         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38502     },
38503
38504     // private
38505     parseValue : function(value){
38506         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38507         return isNaN(value) ? '' : value;
38508     },
38509
38510     // private
38511     fixPrecision : function(value){
38512         var nan = isNaN(value);
38513         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38514             return nan ? '' : value;
38515         }
38516         return parseFloat(value).toFixed(this.decimalPrecision);
38517     },
38518
38519     setValue : function(v){
38520         v = this.fixPrecision(v);
38521         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38522     },
38523
38524     // private
38525     decimalPrecisionFcn : function(v){
38526         return Math.floor(v);
38527     },
38528
38529     beforeBlur : function(){
38530         var v = this.parseValue(this.getRawValue());
38531         if(v){
38532             this.setValue(v);
38533         }
38534     }
38535 });/*
38536  * Based on:
38537  * Ext JS Library 1.1.1
38538  * Copyright(c) 2006-2007, Ext JS, LLC.
38539  *
38540  * Originally Released Under LGPL - original licence link has changed is not relivant.
38541  *
38542  * Fork - LGPL
38543  * <script type="text/javascript">
38544  */
38545  
38546 /**
38547  * @class Roo.form.DateField
38548  * @extends Roo.form.TriggerField
38549  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38550 * @constructor
38551 * Create a new DateField
38552 * @param {Object} config
38553  */
38554 Roo.form.DateField = function(config){
38555     Roo.form.DateField.superclass.constructor.call(this, config);
38556     
38557       this.addEvents({
38558          
38559         /**
38560          * @event select
38561          * Fires when a date is selected
38562              * @param {Roo.form.DateField} combo This combo box
38563              * @param {Date} date The date selected
38564              */
38565         'select' : true
38566          
38567     });
38568     
38569     
38570     if(typeof this.minValue == "string") {
38571         this.minValue = this.parseDate(this.minValue);
38572     }
38573     if(typeof this.maxValue == "string") {
38574         this.maxValue = this.parseDate(this.maxValue);
38575     }
38576     this.ddMatch = null;
38577     if(this.disabledDates){
38578         var dd = this.disabledDates;
38579         var re = "(?:";
38580         for(var i = 0; i < dd.length; i++){
38581             re += dd[i];
38582             if(i != dd.length-1) {
38583                 re += "|";
38584             }
38585         }
38586         this.ddMatch = new RegExp(re + ")");
38587     }
38588 };
38589
38590 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38591     /**
38592      * @cfg {String} format
38593      * The default date format string which can be overriden for localization support.  The format must be
38594      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38595      */
38596     format : "m/d/y",
38597     /**
38598      * @cfg {String} altFormats
38599      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38600      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38601      */
38602     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38603     /**
38604      * @cfg {Array} disabledDays
38605      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38606      */
38607     disabledDays : null,
38608     /**
38609      * @cfg {String} disabledDaysText
38610      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38611      */
38612     disabledDaysText : "Disabled",
38613     /**
38614      * @cfg {Array} disabledDates
38615      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38616      * expression so they are very powerful. Some examples:
38617      * <ul>
38618      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38619      * <li>["03/08", "09/16"] would disable those days for every year</li>
38620      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38621      * <li>["03/../2006"] would disable every day in March 2006</li>
38622      * <li>["^03"] would disable every day in every March</li>
38623      * </ul>
38624      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38625      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38626      */
38627     disabledDates : null,
38628     /**
38629      * @cfg {String} disabledDatesText
38630      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38631      */
38632     disabledDatesText : "Disabled",
38633     /**
38634      * @cfg {Date/String} minValue
38635      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38636      * valid format (defaults to null).
38637      */
38638     minValue : null,
38639     /**
38640      * @cfg {Date/String} maxValue
38641      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38642      * valid format (defaults to null).
38643      */
38644     maxValue : null,
38645     /**
38646      * @cfg {String} minText
38647      * The error text to display when the date in the cell is before minValue (defaults to
38648      * 'The date in this field must be after {minValue}').
38649      */
38650     minText : "The date in this field must be equal to or after {0}",
38651     /**
38652      * @cfg {String} maxText
38653      * The error text to display when the date in the cell is after maxValue (defaults to
38654      * 'The date in this field must be before {maxValue}').
38655      */
38656     maxText : "The date in this field must be equal to or before {0}",
38657     /**
38658      * @cfg {String} invalidText
38659      * The error text to display when the date in the field is invalid (defaults to
38660      * '{value} is not a valid date - it must be in the format {format}').
38661      */
38662     invalidText : "{0} is not a valid date - it must be in the format {1}",
38663     /**
38664      * @cfg {String} triggerClass
38665      * An additional CSS class used to style the trigger button.  The trigger will always get the
38666      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38667      * which displays a calendar icon).
38668      */
38669     triggerClass : 'x-form-date-trigger',
38670     
38671
38672     /**
38673      * @cfg {Boolean} useIso
38674      * if enabled, then the date field will use a hidden field to store the 
38675      * real value as iso formated date. default (false)
38676      */ 
38677     useIso : false,
38678     /**
38679      * @cfg {String/Object} autoCreate
38680      * A DomHelper element spec, or true for a default element spec (defaults to
38681      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38682      */ 
38683     // private
38684     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38685     
38686     // private
38687     hiddenField: false,
38688     
38689     onRender : function(ct, position)
38690     {
38691         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38692         if (this.useIso) {
38693             //this.el.dom.removeAttribute('name'); 
38694             Roo.log("Changing name?");
38695             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38696             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38697                     'before', true);
38698             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38699             // prevent input submission
38700             this.hiddenName = this.name;
38701         }
38702             
38703             
38704     },
38705     
38706     // private
38707     validateValue : function(value)
38708     {
38709         value = this.formatDate(value);
38710         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38711             Roo.log('super failed');
38712             return false;
38713         }
38714         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38715              return true;
38716         }
38717         var svalue = value;
38718         value = this.parseDate(value);
38719         if(!value){
38720             Roo.log('parse date failed' + svalue);
38721             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38722             return false;
38723         }
38724         var time = value.getTime();
38725         if(this.minValue && time < this.minValue.getTime()){
38726             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38727             return false;
38728         }
38729         if(this.maxValue && time > this.maxValue.getTime()){
38730             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38731             return false;
38732         }
38733         if(this.disabledDays){
38734             var day = value.getDay();
38735             for(var i = 0; i < this.disabledDays.length; i++) {
38736                 if(day === this.disabledDays[i]){
38737                     this.markInvalid(this.disabledDaysText);
38738                     return false;
38739                 }
38740             }
38741         }
38742         var fvalue = this.formatDate(value);
38743         if(this.ddMatch && this.ddMatch.test(fvalue)){
38744             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38745             return false;
38746         }
38747         return true;
38748     },
38749
38750     // private
38751     // Provides logic to override the default TriggerField.validateBlur which just returns true
38752     validateBlur : function(){
38753         return !this.menu || !this.menu.isVisible();
38754     },
38755     
38756     getName: function()
38757     {
38758         // returns hidden if it's set..
38759         if (!this.rendered) {return ''};
38760         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38761         
38762     },
38763
38764     /**
38765      * Returns the current date value of the date field.
38766      * @return {Date} The date value
38767      */
38768     getValue : function(){
38769         
38770         return  this.hiddenField ?
38771                 this.hiddenField.value :
38772                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38773     },
38774
38775     /**
38776      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38777      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38778      * (the default format used is "m/d/y").
38779      * <br />Usage:
38780      * <pre><code>
38781 //All of these calls set the same date value (May 4, 2006)
38782
38783 //Pass a date object:
38784 var dt = new Date('5/4/06');
38785 dateField.setValue(dt);
38786
38787 //Pass a date string (default format):
38788 dateField.setValue('5/4/06');
38789
38790 //Pass a date string (custom format):
38791 dateField.format = 'Y-m-d';
38792 dateField.setValue('2006-5-4');
38793 </code></pre>
38794      * @param {String/Date} date The date or valid date string
38795      */
38796     setValue : function(date){
38797         if (this.hiddenField) {
38798             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38799         }
38800         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38801         // make sure the value field is always stored as a date..
38802         this.value = this.parseDate(date);
38803         
38804         
38805     },
38806
38807     // private
38808     parseDate : function(value){
38809         if(!value || value instanceof Date){
38810             return value;
38811         }
38812         var v = Date.parseDate(value, this.format);
38813          if (!v && this.useIso) {
38814             v = Date.parseDate(value, 'Y-m-d');
38815         }
38816         if(!v && this.altFormats){
38817             if(!this.altFormatsArray){
38818                 this.altFormatsArray = this.altFormats.split("|");
38819             }
38820             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38821                 v = Date.parseDate(value, this.altFormatsArray[i]);
38822             }
38823         }
38824         return v;
38825     },
38826
38827     // private
38828     formatDate : function(date, fmt){
38829         return (!date || !(date instanceof Date)) ?
38830                date : date.dateFormat(fmt || this.format);
38831     },
38832
38833     // private
38834     menuListeners : {
38835         select: function(m, d){
38836             
38837             this.setValue(d);
38838             this.fireEvent('select', this, d);
38839         },
38840         show : function(){ // retain focus styling
38841             this.onFocus();
38842         },
38843         hide : function(){
38844             this.focus.defer(10, this);
38845             var ml = this.menuListeners;
38846             this.menu.un("select", ml.select,  this);
38847             this.menu.un("show", ml.show,  this);
38848             this.menu.un("hide", ml.hide,  this);
38849         }
38850     },
38851
38852     // private
38853     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38854     onTriggerClick : function(){
38855         if(this.disabled){
38856             return;
38857         }
38858         if(this.menu == null){
38859             this.menu = new Roo.menu.DateMenu();
38860         }
38861         Roo.apply(this.menu.picker,  {
38862             showClear: this.allowBlank,
38863             minDate : this.minValue,
38864             maxDate : this.maxValue,
38865             disabledDatesRE : this.ddMatch,
38866             disabledDatesText : this.disabledDatesText,
38867             disabledDays : this.disabledDays,
38868             disabledDaysText : this.disabledDaysText,
38869             format : this.useIso ? 'Y-m-d' : this.format,
38870             minText : String.format(this.minText, this.formatDate(this.minValue)),
38871             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38872         });
38873         this.menu.on(Roo.apply({}, this.menuListeners, {
38874             scope:this
38875         }));
38876         this.menu.picker.setValue(this.getValue() || new Date());
38877         this.menu.show(this.el, "tl-bl?");
38878     },
38879
38880     beforeBlur : function(){
38881         var v = this.parseDate(this.getRawValue());
38882         if(v){
38883             this.setValue(v);
38884         }
38885     },
38886
38887     /*@
38888      * overide
38889      * 
38890      */
38891     isDirty : function() {
38892         if(this.disabled) {
38893             return false;
38894         }
38895         
38896         if(typeof(this.startValue) === 'undefined'){
38897             return false;
38898         }
38899         
38900         return String(this.getValue()) !== String(this.startValue);
38901         
38902     }
38903 });/*
38904  * Based on:
38905  * Ext JS Library 1.1.1
38906  * Copyright(c) 2006-2007, Ext JS, LLC.
38907  *
38908  * Originally Released Under LGPL - original licence link has changed is not relivant.
38909  *
38910  * Fork - LGPL
38911  * <script type="text/javascript">
38912  */
38913  
38914 /**
38915  * @class Roo.form.MonthField
38916  * @extends Roo.form.TriggerField
38917  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38918 * @constructor
38919 * Create a new MonthField
38920 * @param {Object} config
38921  */
38922 Roo.form.MonthField = function(config){
38923     
38924     Roo.form.MonthField.superclass.constructor.call(this, config);
38925     
38926       this.addEvents({
38927          
38928         /**
38929          * @event select
38930          * Fires when a date is selected
38931              * @param {Roo.form.MonthFieeld} combo This combo box
38932              * @param {Date} date The date selected
38933              */
38934         'select' : true
38935          
38936     });
38937     
38938     
38939     if(typeof this.minValue == "string") {
38940         this.minValue = this.parseDate(this.minValue);
38941     }
38942     if(typeof this.maxValue == "string") {
38943         this.maxValue = this.parseDate(this.maxValue);
38944     }
38945     this.ddMatch = null;
38946     if(this.disabledDates){
38947         var dd = this.disabledDates;
38948         var re = "(?:";
38949         for(var i = 0; i < dd.length; i++){
38950             re += dd[i];
38951             if(i != dd.length-1) {
38952                 re += "|";
38953             }
38954         }
38955         this.ddMatch = new RegExp(re + ")");
38956     }
38957 };
38958
38959 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38960     /**
38961      * @cfg {String} format
38962      * The default date format string which can be overriden for localization support.  The format must be
38963      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38964      */
38965     format : "M Y",
38966     /**
38967      * @cfg {String} altFormats
38968      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38969      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38970      */
38971     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38972     /**
38973      * @cfg {Array} disabledDays
38974      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38975      */
38976     disabledDays : [0,1,2,3,4,5,6],
38977     /**
38978      * @cfg {String} disabledDaysText
38979      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38980      */
38981     disabledDaysText : "Disabled",
38982     /**
38983      * @cfg {Array} disabledDates
38984      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38985      * expression so they are very powerful. Some examples:
38986      * <ul>
38987      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38988      * <li>["03/08", "09/16"] would disable those days for every year</li>
38989      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38990      * <li>["03/../2006"] would disable every day in March 2006</li>
38991      * <li>["^03"] would disable every day in every March</li>
38992      * </ul>
38993      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38994      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38995      */
38996     disabledDates : null,
38997     /**
38998      * @cfg {String} disabledDatesText
38999      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
39000      */
39001     disabledDatesText : "Disabled",
39002     /**
39003      * @cfg {Date/String} minValue
39004      * The minimum allowed date. Can be either a Javascript date object or a string date in a
39005      * valid format (defaults to null).
39006      */
39007     minValue : null,
39008     /**
39009      * @cfg {Date/String} maxValue
39010      * The maximum allowed date. Can be either a Javascript date object or a string date in a
39011      * valid format (defaults to null).
39012      */
39013     maxValue : null,
39014     /**
39015      * @cfg {String} minText
39016      * The error text to display when the date in the cell is before minValue (defaults to
39017      * 'The date in this field must be after {minValue}').
39018      */
39019     minText : "The date in this field must be equal to or after {0}",
39020     /**
39021      * @cfg {String} maxTextf
39022      * The error text to display when the date in the cell is after maxValue (defaults to
39023      * 'The date in this field must be before {maxValue}').
39024      */
39025     maxText : "The date in this field must be equal to or before {0}",
39026     /**
39027      * @cfg {String} invalidText
39028      * The error text to display when the date in the field is invalid (defaults to
39029      * '{value} is not a valid date - it must be in the format {format}').
39030      */
39031     invalidText : "{0} is not a valid date - it must be in the format {1}",
39032     /**
39033      * @cfg {String} triggerClass
39034      * An additional CSS class used to style the trigger button.  The trigger will always get the
39035      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
39036      * which displays a calendar icon).
39037      */
39038     triggerClass : 'x-form-date-trigger',
39039     
39040
39041     /**
39042      * @cfg {Boolean} useIso
39043      * if enabled, then the date field will use a hidden field to store the 
39044      * real value as iso formated date. default (true)
39045      */ 
39046     useIso : true,
39047     /**
39048      * @cfg {String/Object} autoCreate
39049      * A DomHelper element spec, or true for a default element spec (defaults to
39050      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
39051      */ 
39052     // private
39053     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
39054     
39055     // private
39056     hiddenField: false,
39057     
39058     hideMonthPicker : false,
39059     
39060     onRender : function(ct, position)
39061     {
39062         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39063         if (this.useIso) {
39064             this.el.dom.removeAttribute('name'); 
39065             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39066                     'before', true);
39067             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39068             // prevent input submission
39069             this.hiddenName = this.name;
39070         }
39071             
39072             
39073     },
39074     
39075     // private
39076     validateValue : function(value)
39077     {
39078         value = this.formatDate(value);
39079         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39080             return false;
39081         }
39082         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39083              return true;
39084         }
39085         var svalue = value;
39086         value = this.parseDate(value);
39087         if(!value){
39088             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39089             return false;
39090         }
39091         var time = value.getTime();
39092         if(this.minValue && time < this.minValue.getTime()){
39093             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39094             return false;
39095         }
39096         if(this.maxValue && time > this.maxValue.getTime()){
39097             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39098             return false;
39099         }
39100         /*if(this.disabledDays){
39101             var day = value.getDay();
39102             for(var i = 0; i < this.disabledDays.length; i++) {
39103                 if(day === this.disabledDays[i]){
39104                     this.markInvalid(this.disabledDaysText);
39105                     return false;
39106                 }
39107             }
39108         }
39109         */
39110         var fvalue = this.formatDate(value);
39111         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39112             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39113             return false;
39114         }
39115         */
39116         return true;
39117     },
39118
39119     // private
39120     // Provides logic to override the default TriggerField.validateBlur which just returns true
39121     validateBlur : function(){
39122         return !this.menu || !this.menu.isVisible();
39123     },
39124
39125     /**
39126      * Returns the current date value of the date field.
39127      * @return {Date} The date value
39128      */
39129     getValue : function(){
39130         
39131         
39132         
39133         return  this.hiddenField ?
39134                 this.hiddenField.value :
39135                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39136     },
39137
39138     /**
39139      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39140      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39141      * (the default format used is "m/d/y").
39142      * <br />Usage:
39143      * <pre><code>
39144 //All of these calls set the same date value (May 4, 2006)
39145
39146 //Pass a date object:
39147 var dt = new Date('5/4/06');
39148 monthField.setValue(dt);
39149
39150 //Pass a date string (default format):
39151 monthField.setValue('5/4/06');
39152
39153 //Pass a date string (custom format):
39154 monthField.format = 'Y-m-d';
39155 monthField.setValue('2006-5-4');
39156 </code></pre>
39157      * @param {String/Date} date The date or valid date string
39158      */
39159     setValue : function(date){
39160         Roo.log('month setValue' + date);
39161         // can only be first of month..
39162         
39163         var val = this.parseDate(date);
39164         
39165         if (this.hiddenField) {
39166             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39167         }
39168         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39169         this.value = this.parseDate(date);
39170     },
39171
39172     // private
39173     parseDate : function(value){
39174         if(!value || value instanceof Date){
39175             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39176             return value;
39177         }
39178         var v = Date.parseDate(value, this.format);
39179         if (!v && this.useIso) {
39180             v = Date.parseDate(value, 'Y-m-d');
39181         }
39182         if (v) {
39183             // 
39184             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39185         }
39186         
39187         
39188         if(!v && this.altFormats){
39189             if(!this.altFormatsArray){
39190                 this.altFormatsArray = this.altFormats.split("|");
39191             }
39192             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39193                 v = Date.parseDate(value, this.altFormatsArray[i]);
39194             }
39195         }
39196         return v;
39197     },
39198
39199     // private
39200     formatDate : function(date, fmt){
39201         return (!date || !(date instanceof Date)) ?
39202                date : date.dateFormat(fmt || this.format);
39203     },
39204
39205     // private
39206     menuListeners : {
39207         select: function(m, d){
39208             this.setValue(d);
39209             this.fireEvent('select', this, d);
39210         },
39211         show : function(){ // retain focus styling
39212             this.onFocus();
39213         },
39214         hide : function(){
39215             this.focus.defer(10, this);
39216             var ml = this.menuListeners;
39217             this.menu.un("select", ml.select,  this);
39218             this.menu.un("show", ml.show,  this);
39219             this.menu.un("hide", ml.hide,  this);
39220         }
39221     },
39222     // private
39223     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39224     onTriggerClick : function(){
39225         if(this.disabled){
39226             return;
39227         }
39228         if(this.menu == null){
39229             this.menu = new Roo.menu.DateMenu();
39230            
39231         }
39232         
39233         Roo.apply(this.menu.picker,  {
39234             
39235             showClear: this.allowBlank,
39236             minDate : this.minValue,
39237             maxDate : this.maxValue,
39238             disabledDatesRE : this.ddMatch,
39239             disabledDatesText : this.disabledDatesText,
39240             
39241             format : this.useIso ? 'Y-m-d' : this.format,
39242             minText : String.format(this.minText, this.formatDate(this.minValue)),
39243             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39244             
39245         });
39246          this.menu.on(Roo.apply({}, this.menuListeners, {
39247             scope:this
39248         }));
39249        
39250         
39251         var m = this.menu;
39252         var p = m.picker;
39253         
39254         // hide month picker get's called when we called by 'before hide';
39255         
39256         var ignorehide = true;
39257         p.hideMonthPicker  = function(disableAnim){
39258             if (ignorehide) {
39259                 return;
39260             }
39261              if(this.monthPicker){
39262                 Roo.log("hideMonthPicker called");
39263                 if(disableAnim === true){
39264                     this.monthPicker.hide();
39265                 }else{
39266                     this.monthPicker.slideOut('t', {duration:.2});
39267                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39268                     p.fireEvent("select", this, this.value);
39269                     m.hide();
39270                 }
39271             }
39272         }
39273         
39274         Roo.log('picker set value');
39275         Roo.log(this.getValue());
39276         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39277         m.show(this.el, 'tl-bl?');
39278         ignorehide  = false;
39279         // this will trigger hideMonthPicker..
39280         
39281         
39282         // hidden the day picker
39283         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39284         
39285         
39286         
39287       
39288         
39289         p.showMonthPicker.defer(100, p);
39290     
39291         
39292        
39293     },
39294
39295     beforeBlur : function(){
39296         var v = this.parseDate(this.getRawValue());
39297         if(v){
39298             this.setValue(v);
39299         }
39300     }
39301
39302     /** @cfg {Boolean} grow @hide */
39303     /** @cfg {Number} growMin @hide */
39304     /** @cfg {Number} growMax @hide */
39305     /**
39306      * @hide
39307      * @method autoSize
39308      */
39309 });/*
39310  * Based on:
39311  * Ext JS Library 1.1.1
39312  * Copyright(c) 2006-2007, Ext JS, LLC.
39313  *
39314  * Originally Released Under LGPL - original licence link has changed is not relivant.
39315  *
39316  * Fork - LGPL
39317  * <script type="text/javascript">
39318  */
39319  
39320
39321 /**
39322  * @class Roo.form.ComboBox
39323  * @extends Roo.form.TriggerField
39324  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39325  * @constructor
39326  * Create a new ComboBox.
39327  * @param {Object} config Configuration options
39328  */
39329 Roo.form.ComboBox = function(config){
39330     Roo.form.ComboBox.superclass.constructor.call(this, config);
39331     this.addEvents({
39332         /**
39333          * @event expand
39334          * Fires when the dropdown list is expanded
39335              * @param {Roo.form.ComboBox} combo This combo box
39336              */
39337         'expand' : true,
39338         /**
39339          * @event collapse
39340          * Fires when the dropdown list is collapsed
39341              * @param {Roo.form.ComboBox} combo This combo box
39342              */
39343         'collapse' : true,
39344         /**
39345          * @event beforeselect
39346          * Fires before a list item is selected. Return false to cancel the selection.
39347              * @param {Roo.form.ComboBox} combo This combo box
39348              * @param {Roo.data.Record} record The data record returned from the underlying store
39349              * @param {Number} index The index of the selected item in the dropdown list
39350              */
39351         'beforeselect' : true,
39352         /**
39353          * @event select
39354          * Fires when a list item is selected
39355              * @param {Roo.form.ComboBox} combo This combo box
39356              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39357              * @param {Number} index The index of the selected item in the dropdown list
39358              */
39359         'select' : true,
39360         /**
39361          * @event beforequery
39362          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39363          * The event object passed has these properties:
39364              * @param {Roo.form.ComboBox} combo This combo box
39365              * @param {String} query The query
39366              * @param {Boolean} forceAll true to force "all" query
39367              * @param {Boolean} cancel true to cancel the query
39368              * @param {Object} e The query event object
39369              */
39370         'beforequery': true,
39371          /**
39372          * @event add
39373          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39374              * @param {Roo.form.ComboBox} combo This combo box
39375              */
39376         'add' : true,
39377         /**
39378          * @event edit
39379          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39380              * @param {Roo.form.ComboBox} combo This combo box
39381              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39382              */
39383         'edit' : true
39384         
39385         
39386     });
39387     if(this.transform){
39388         this.allowDomMove = false;
39389         var s = Roo.getDom(this.transform);
39390         if(!this.hiddenName){
39391             this.hiddenName = s.name;
39392         }
39393         if(!this.store){
39394             this.mode = 'local';
39395             var d = [], opts = s.options;
39396             for(var i = 0, len = opts.length;i < len; i++){
39397                 var o = opts[i];
39398                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39399                 if(o.selected) {
39400                     this.value = value;
39401                 }
39402                 d.push([value, o.text]);
39403             }
39404             this.store = new Roo.data.SimpleStore({
39405                 'id': 0,
39406                 fields: ['value', 'text'],
39407                 data : d
39408             });
39409             this.valueField = 'value';
39410             this.displayField = 'text';
39411         }
39412         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39413         if(!this.lazyRender){
39414             this.target = true;
39415             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39416             s.parentNode.removeChild(s); // remove it
39417             this.render(this.el.parentNode);
39418         }else{
39419             s.parentNode.removeChild(s); // remove it
39420         }
39421
39422     }
39423     if (this.store) {
39424         this.store = Roo.factory(this.store, Roo.data);
39425     }
39426     
39427     this.selectedIndex = -1;
39428     if(this.mode == 'local'){
39429         if(config.queryDelay === undefined){
39430             this.queryDelay = 10;
39431         }
39432         if(config.minChars === undefined){
39433             this.minChars = 0;
39434         }
39435     }
39436 };
39437
39438 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39439     /**
39440      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39441      */
39442     /**
39443      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39444      * rendering into an Roo.Editor, defaults to false)
39445      */
39446     /**
39447      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39448      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39449      */
39450     /**
39451      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39452      */
39453     /**
39454      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39455      * the dropdown list (defaults to undefined, with no header element)
39456      */
39457
39458      /**
39459      * @cfg {String/Roo.Template} tpl The template to use to render the output
39460      */
39461      
39462     // private
39463     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39464     /**
39465      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39466      */
39467     listWidth: undefined,
39468     /**
39469      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39470      * mode = 'remote' or 'text' if mode = 'local')
39471      */
39472     displayField: undefined,
39473     /**
39474      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39475      * mode = 'remote' or 'value' if mode = 'local'). 
39476      * Note: use of a valueField requires the user make a selection
39477      * in order for a value to be mapped.
39478      */
39479     valueField: undefined,
39480     
39481     
39482     /**
39483      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39484      * field's data value (defaults to the underlying DOM element's name)
39485      */
39486     hiddenName: undefined,
39487     /**
39488      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39489      */
39490     listClass: '',
39491     /**
39492      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39493      */
39494     selectedClass: 'x-combo-selected',
39495     /**
39496      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39497      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39498      * which displays a downward arrow icon).
39499      */
39500     triggerClass : 'x-form-arrow-trigger',
39501     /**
39502      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39503      */
39504     shadow:'sides',
39505     /**
39506      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39507      * anchor positions (defaults to 'tl-bl')
39508      */
39509     listAlign: 'tl-bl?',
39510     /**
39511      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39512      */
39513     maxHeight: 300,
39514     /**
39515      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39516      * query specified by the allQuery config option (defaults to 'query')
39517      */
39518     triggerAction: 'query',
39519     /**
39520      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39521      * (defaults to 4, does not apply if editable = false)
39522      */
39523     minChars : 4,
39524     /**
39525      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39526      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39527      */
39528     typeAhead: false,
39529     /**
39530      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39531      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39532      */
39533     queryDelay: 500,
39534     /**
39535      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39536      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39537      */
39538     pageSize: 0,
39539     /**
39540      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39541      * when editable = true (defaults to false)
39542      */
39543     selectOnFocus:false,
39544     /**
39545      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39546      */
39547     queryParam: 'query',
39548     /**
39549      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39550      * when mode = 'remote' (defaults to 'Loading...')
39551      */
39552     loadingText: 'Loading...',
39553     /**
39554      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39555      */
39556     resizable: false,
39557     /**
39558      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39559      */
39560     handleHeight : 8,
39561     /**
39562      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39563      * traditional select (defaults to true)
39564      */
39565     editable: true,
39566     /**
39567      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39568      */
39569     allQuery: '',
39570     /**
39571      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39572      */
39573     mode: 'remote',
39574     /**
39575      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39576      * listWidth has a higher value)
39577      */
39578     minListWidth : 70,
39579     /**
39580      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39581      * allow the user to set arbitrary text into the field (defaults to false)
39582      */
39583     forceSelection:false,
39584     /**
39585      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39586      * if typeAhead = true (defaults to 250)
39587      */
39588     typeAheadDelay : 250,
39589     /**
39590      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39591      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39592      */
39593     valueNotFoundText : undefined,
39594     /**
39595      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39596      */
39597     blockFocus : false,
39598     
39599     /**
39600      * @cfg {Boolean} disableClear Disable showing of clear button.
39601      */
39602     disableClear : false,
39603     /**
39604      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39605      */
39606     alwaysQuery : false,
39607     
39608     //private
39609     addicon : false,
39610     editicon: false,
39611     
39612     // element that contains real text value.. (when hidden is used..)
39613      
39614     // private
39615     onRender : function(ct, position){
39616         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39617         if(this.hiddenName){
39618             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39619                     'before', true);
39620             this.hiddenField.value =
39621                 this.hiddenValue !== undefined ? this.hiddenValue :
39622                 this.value !== undefined ? this.value : '';
39623
39624             // prevent input submission
39625             this.el.dom.removeAttribute('name');
39626              
39627              
39628         }
39629         if(Roo.isGecko){
39630             this.el.dom.setAttribute('autocomplete', 'off');
39631         }
39632
39633         var cls = 'x-combo-list';
39634
39635         this.list = new Roo.Layer({
39636             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39637         });
39638
39639         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39640         this.list.setWidth(lw);
39641         this.list.swallowEvent('mousewheel');
39642         this.assetHeight = 0;
39643
39644         if(this.title){
39645             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39646             this.assetHeight += this.header.getHeight();
39647         }
39648
39649         this.innerList = this.list.createChild({cls:cls+'-inner'});
39650         this.innerList.on('mouseover', this.onViewOver, this);
39651         this.innerList.on('mousemove', this.onViewMove, this);
39652         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39653         
39654         if(this.allowBlank && !this.pageSize && !this.disableClear){
39655             this.footer = this.list.createChild({cls:cls+'-ft'});
39656             this.pageTb = new Roo.Toolbar(this.footer);
39657            
39658         }
39659         if(this.pageSize){
39660             this.footer = this.list.createChild({cls:cls+'-ft'});
39661             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39662                     {pageSize: this.pageSize});
39663             
39664         }
39665         
39666         if (this.pageTb && this.allowBlank && !this.disableClear) {
39667             var _this = this;
39668             this.pageTb.add(new Roo.Toolbar.Fill(), {
39669                 cls: 'x-btn-icon x-btn-clear',
39670                 text: '&#160;',
39671                 handler: function()
39672                 {
39673                     _this.collapse();
39674                     _this.clearValue();
39675                     _this.onSelect(false, -1);
39676                 }
39677             });
39678         }
39679         if (this.footer) {
39680             this.assetHeight += this.footer.getHeight();
39681         }
39682         
39683
39684         if(!this.tpl){
39685             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39686         }
39687
39688         this.view = new Roo.View(this.innerList, this.tpl, {
39689             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39690         });
39691
39692         this.view.on('click', this.onViewClick, this);
39693
39694         this.store.on('beforeload', this.onBeforeLoad, this);
39695         this.store.on('load', this.onLoad, this);
39696         this.store.on('loadexception', this.onLoadException, this);
39697
39698         if(this.resizable){
39699             this.resizer = new Roo.Resizable(this.list,  {
39700                pinned:true, handles:'se'
39701             });
39702             this.resizer.on('resize', function(r, w, h){
39703                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39704                 this.listWidth = w;
39705                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39706                 this.restrictHeight();
39707             }, this);
39708             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39709         }
39710         if(!this.editable){
39711             this.editable = true;
39712             this.setEditable(false);
39713         }  
39714         
39715         
39716         if (typeof(this.events.add.listeners) != 'undefined') {
39717             
39718             this.addicon = this.wrap.createChild(
39719                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39720        
39721             this.addicon.on('click', function(e) {
39722                 this.fireEvent('add', this);
39723             }, this);
39724         }
39725         if (typeof(this.events.edit.listeners) != 'undefined') {
39726             
39727             this.editicon = this.wrap.createChild(
39728                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39729             if (this.addicon) {
39730                 this.editicon.setStyle('margin-left', '40px');
39731             }
39732             this.editicon.on('click', function(e) {
39733                 
39734                 // we fire even  if inothing is selected..
39735                 this.fireEvent('edit', this, this.lastData );
39736                 
39737             }, this);
39738         }
39739         
39740         
39741         
39742     },
39743
39744     // private
39745     initEvents : function(){
39746         Roo.form.ComboBox.superclass.initEvents.call(this);
39747
39748         this.keyNav = new Roo.KeyNav(this.el, {
39749             "up" : function(e){
39750                 this.inKeyMode = true;
39751                 this.selectPrev();
39752             },
39753
39754             "down" : function(e){
39755                 if(!this.isExpanded()){
39756                     this.onTriggerClick();
39757                 }else{
39758                     this.inKeyMode = true;
39759                     this.selectNext();
39760                 }
39761             },
39762
39763             "enter" : function(e){
39764                 this.onViewClick();
39765                 //return true;
39766             },
39767
39768             "esc" : function(e){
39769                 this.collapse();
39770             },
39771
39772             "tab" : function(e){
39773                 this.onViewClick(false);
39774                 this.fireEvent("specialkey", this, e);
39775                 return true;
39776             },
39777
39778             scope : this,
39779
39780             doRelay : function(foo, bar, hname){
39781                 if(hname == 'down' || this.scope.isExpanded()){
39782                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39783                 }
39784                 return true;
39785             },
39786
39787             forceKeyDown: true
39788         });
39789         this.queryDelay = Math.max(this.queryDelay || 10,
39790                 this.mode == 'local' ? 10 : 250);
39791         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39792         if(this.typeAhead){
39793             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39794         }
39795         if(this.editable !== false){
39796             this.el.on("keyup", this.onKeyUp, this);
39797         }
39798         if(this.forceSelection){
39799             this.on('blur', this.doForce, this);
39800         }
39801     },
39802
39803     onDestroy : function(){
39804         if(this.view){
39805             this.view.setStore(null);
39806             this.view.el.removeAllListeners();
39807             this.view.el.remove();
39808             this.view.purgeListeners();
39809         }
39810         if(this.list){
39811             this.list.destroy();
39812         }
39813         if(this.store){
39814             this.store.un('beforeload', this.onBeforeLoad, this);
39815             this.store.un('load', this.onLoad, this);
39816             this.store.un('loadexception', this.onLoadException, this);
39817         }
39818         Roo.form.ComboBox.superclass.onDestroy.call(this);
39819     },
39820
39821     // private
39822     fireKey : function(e){
39823         if(e.isNavKeyPress() && !this.list.isVisible()){
39824             this.fireEvent("specialkey", this, e);
39825         }
39826     },
39827
39828     // private
39829     onResize: function(w, h){
39830         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39831         
39832         if(typeof w != 'number'){
39833             // we do not handle it!?!?
39834             return;
39835         }
39836         var tw = this.trigger.getWidth();
39837         tw += this.addicon ? this.addicon.getWidth() : 0;
39838         tw += this.editicon ? this.editicon.getWidth() : 0;
39839         var x = w - tw;
39840         this.el.setWidth( this.adjustWidth('input', x));
39841             
39842         this.trigger.setStyle('left', x+'px');
39843         
39844         if(this.list && this.listWidth === undefined){
39845             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39846             this.list.setWidth(lw);
39847             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39848         }
39849         
39850     
39851         
39852     },
39853
39854     /**
39855      * Allow or prevent the user from directly editing the field text.  If false is passed,
39856      * the user will only be able to select from the items defined in the dropdown list.  This method
39857      * is the runtime equivalent of setting the 'editable' config option at config time.
39858      * @param {Boolean} value True to allow the user to directly edit the field text
39859      */
39860     setEditable : function(value){
39861         if(value == this.editable){
39862             return;
39863         }
39864         this.editable = value;
39865         if(!value){
39866             this.el.dom.setAttribute('readOnly', true);
39867             this.el.on('mousedown', this.onTriggerClick,  this);
39868             this.el.addClass('x-combo-noedit');
39869         }else{
39870             this.el.dom.setAttribute('readOnly', false);
39871             this.el.un('mousedown', this.onTriggerClick,  this);
39872             this.el.removeClass('x-combo-noedit');
39873         }
39874     },
39875
39876     // private
39877     onBeforeLoad : function(){
39878         if(!this.hasFocus){
39879             return;
39880         }
39881         this.innerList.update(this.loadingText ?
39882                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39883         this.restrictHeight();
39884         this.selectedIndex = -1;
39885     },
39886
39887     // private
39888     onLoad : function(){
39889         if(!this.hasFocus){
39890             return;
39891         }
39892         if(this.store.getCount() > 0){
39893             this.expand();
39894             this.restrictHeight();
39895             if(this.lastQuery == this.allQuery){
39896                 if(this.editable){
39897                     this.el.dom.select();
39898                 }
39899                 if(!this.selectByValue(this.value, true)){
39900                     this.select(0, true);
39901                 }
39902             }else{
39903                 this.selectNext();
39904                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39905                     this.taTask.delay(this.typeAheadDelay);
39906                 }
39907             }
39908         }else{
39909             this.onEmptyResults();
39910         }
39911         //this.el.focus();
39912     },
39913     // private
39914     onLoadException : function()
39915     {
39916         this.collapse();
39917         Roo.log(this.store.reader.jsonData);
39918         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39919             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39920         }
39921         
39922         
39923     },
39924     // private
39925     onTypeAhead : function(){
39926         if(this.store.getCount() > 0){
39927             var r = this.store.getAt(0);
39928             var newValue = r.data[this.displayField];
39929             var len = newValue.length;
39930             var selStart = this.getRawValue().length;
39931             if(selStart != len){
39932                 this.setRawValue(newValue);
39933                 this.selectText(selStart, newValue.length);
39934             }
39935         }
39936     },
39937
39938     // private
39939     onSelect : function(record, index){
39940         if(this.fireEvent('beforeselect', this, record, index) !== false){
39941             this.setFromData(index > -1 ? record.data : false);
39942             this.collapse();
39943             this.fireEvent('select', this, record, index);
39944         }
39945     },
39946
39947     /**
39948      * Returns the currently selected field value or empty string if no value is set.
39949      * @return {String} value The selected value
39950      */
39951     getValue : function(){
39952         if(this.valueField){
39953             return typeof this.value != 'undefined' ? this.value : '';
39954         }
39955         return Roo.form.ComboBox.superclass.getValue.call(this);
39956     },
39957
39958     /**
39959      * Clears any text/value currently set in the field
39960      */
39961     clearValue : function(){
39962         if(this.hiddenField){
39963             this.hiddenField.value = '';
39964         }
39965         this.value = '';
39966         this.setRawValue('');
39967         this.lastSelectionText = '';
39968         
39969     },
39970
39971     /**
39972      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39973      * will be displayed in the field.  If the value does not match the data value of an existing item,
39974      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39975      * Otherwise the field will be blank (although the value will still be set).
39976      * @param {String} value The value to match
39977      */
39978     setValue : function(v){
39979         var text = v;
39980         if(this.valueField){
39981             var r = this.findRecord(this.valueField, v);
39982             if(r){
39983                 text = r.data[this.displayField];
39984             }else if(this.valueNotFoundText !== undefined){
39985                 text = this.valueNotFoundText;
39986             }
39987         }
39988         this.lastSelectionText = text;
39989         if(this.hiddenField){
39990             this.hiddenField.value = v;
39991         }
39992         Roo.form.ComboBox.superclass.setValue.call(this, text);
39993         this.value = v;
39994     },
39995     /**
39996      * @property {Object} the last set data for the element
39997      */
39998     
39999     lastData : false,
40000     /**
40001      * Sets the value of the field based on a object which is related to the record format for the store.
40002      * @param {Object} value the value to set as. or false on reset?
40003      */
40004     setFromData : function(o){
40005         var dv = ''; // display value
40006         var vv = ''; // value value..
40007         this.lastData = o;
40008         if (this.displayField) {
40009             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
40010         } else {
40011             // this is an error condition!!!
40012             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
40013         }
40014         
40015         if(this.valueField){
40016             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
40017         }
40018         if(this.hiddenField){
40019             this.hiddenField.value = vv;
40020             
40021             this.lastSelectionText = dv;
40022             Roo.form.ComboBox.superclass.setValue.call(this, dv);
40023             this.value = vv;
40024             return;
40025         }
40026         // no hidden field.. - we store the value in 'value', but still display
40027         // display field!!!!
40028         this.lastSelectionText = dv;
40029         Roo.form.ComboBox.superclass.setValue.call(this, dv);
40030         this.value = vv;
40031         
40032         
40033     },
40034     // private
40035     reset : function(){
40036         // overridden so that last data is reset..
40037         this.setValue(this.resetValue);
40038         this.clearInvalid();
40039         this.lastData = false;
40040         if (this.view) {
40041             this.view.clearSelections();
40042         }
40043     },
40044     // private
40045     findRecord : function(prop, value){
40046         var record;
40047         if(this.store.getCount() > 0){
40048             this.store.each(function(r){
40049                 if(r.data[prop] == value){
40050                     record = r;
40051                     return false;
40052                 }
40053                 return true;
40054             });
40055         }
40056         return record;
40057     },
40058     
40059     getName: function()
40060     {
40061         // returns hidden if it's set..
40062         if (!this.rendered) {return ''};
40063         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40064         
40065     },
40066     // private
40067     onViewMove : function(e, t){
40068         this.inKeyMode = false;
40069     },
40070
40071     // private
40072     onViewOver : function(e, t){
40073         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40074             return;
40075         }
40076         var item = this.view.findItemFromChild(t);
40077         if(item){
40078             var index = this.view.indexOf(item);
40079             this.select(index, false);
40080         }
40081     },
40082
40083     // private
40084     onViewClick : function(doFocus)
40085     {
40086         var index = this.view.getSelectedIndexes()[0];
40087         var r = this.store.getAt(index);
40088         if(r){
40089             this.onSelect(r, index);
40090         }
40091         if(doFocus !== false && !this.blockFocus){
40092             this.el.focus();
40093         }
40094     },
40095
40096     // private
40097     restrictHeight : function(){
40098         this.innerList.dom.style.height = '';
40099         var inner = this.innerList.dom;
40100         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40101         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40102         this.list.beginUpdate();
40103         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40104         this.list.alignTo(this.el, this.listAlign);
40105         this.list.endUpdate();
40106     },
40107
40108     // private
40109     onEmptyResults : function(){
40110         this.collapse();
40111     },
40112
40113     /**
40114      * Returns true if the dropdown list is expanded, else false.
40115      */
40116     isExpanded : function(){
40117         return this.list.isVisible();
40118     },
40119
40120     /**
40121      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40122      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40123      * @param {String} value The data value of the item to select
40124      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40125      * selected item if it is not currently in view (defaults to true)
40126      * @return {Boolean} True if the value matched an item in the list, else false
40127      */
40128     selectByValue : function(v, scrollIntoView){
40129         if(v !== undefined && v !== null){
40130             var r = this.findRecord(this.valueField || this.displayField, v);
40131             if(r){
40132                 this.select(this.store.indexOf(r), scrollIntoView);
40133                 return true;
40134             }
40135         }
40136         return false;
40137     },
40138
40139     /**
40140      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40141      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40142      * @param {Number} index The zero-based index of the list item to select
40143      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40144      * selected item if it is not currently in view (defaults to true)
40145      */
40146     select : function(index, scrollIntoView){
40147         this.selectedIndex = index;
40148         this.view.select(index);
40149         if(scrollIntoView !== false){
40150             var el = this.view.getNode(index);
40151             if(el){
40152                 this.innerList.scrollChildIntoView(el, false);
40153             }
40154         }
40155     },
40156
40157     // private
40158     selectNext : function(){
40159         var ct = this.store.getCount();
40160         if(ct > 0){
40161             if(this.selectedIndex == -1){
40162                 this.select(0);
40163             }else if(this.selectedIndex < ct-1){
40164                 this.select(this.selectedIndex+1);
40165             }
40166         }
40167     },
40168
40169     // private
40170     selectPrev : function(){
40171         var ct = this.store.getCount();
40172         if(ct > 0){
40173             if(this.selectedIndex == -1){
40174                 this.select(0);
40175             }else if(this.selectedIndex != 0){
40176                 this.select(this.selectedIndex-1);
40177             }
40178         }
40179     },
40180
40181     // private
40182     onKeyUp : function(e){
40183         if(this.editable !== false && !e.isSpecialKey()){
40184             this.lastKey = e.getKey();
40185             this.dqTask.delay(this.queryDelay);
40186         }
40187     },
40188
40189     // private
40190     validateBlur : function(){
40191         return !this.list || !this.list.isVisible();   
40192     },
40193
40194     // private
40195     initQuery : function(){
40196         this.doQuery(this.getRawValue());
40197     },
40198
40199     // private
40200     doForce : function(){
40201         if(this.el.dom.value.length > 0){
40202             this.el.dom.value =
40203                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40204              
40205         }
40206     },
40207
40208     /**
40209      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40210      * query allowing the query action to be canceled if needed.
40211      * @param {String} query The SQL query to execute
40212      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40213      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40214      * saved in the current store (defaults to false)
40215      */
40216     doQuery : function(q, forceAll){
40217         if(q === undefined || q === null){
40218             q = '';
40219         }
40220         var qe = {
40221             query: q,
40222             forceAll: forceAll,
40223             combo: this,
40224             cancel:false
40225         };
40226         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40227             return false;
40228         }
40229         q = qe.query;
40230         forceAll = qe.forceAll;
40231         if(forceAll === true || (q.length >= this.minChars)){
40232             if(this.lastQuery != q || this.alwaysQuery){
40233                 this.lastQuery = q;
40234                 if(this.mode == 'local'){
40235                     this.selectedIndex = -1;
40236                     if(forceAll){
40237                         this.store.clearFilter();
40238                     }else{
40239                         this.store.filter(this.displayField, q);
40240                     }
40241                     this.onLoad();
40242                 }else{
40243                     this.store.baseParams[this.queryParam] = q;
40244                     this.store.load({
40245                         params: this.getParams(q)
40246                     });
40247                     this.expand();
40248                 }
40249             }else{
40250                 this.selectedIndex = -1;
40251                 this.onLoad();   
40252             }
40253         }
40254     },
40255
40256     // private
40257     getParams : function(q){
40258         var p = {};
40259         //p[this.queryParam] = q;
40260         if(this.pageSize){
40261             p.start = 0;
40262             p.limit = this.pageSize;
40263         }
40264         return p;
40265     },
40266
40267     /**
40268      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40269      */
40270     collapse : function(){
40271         if(!this.isExpanded()){
40272             return;
40273         }
40274         this.list.hide();
40275         Roo.get(document).un('mousedown', this.collapseIf, this);
40276         Roo.get(document).un('mousewheel', this.collapseIf, this);
40277         if (!this.editable) {
40278             Roo.get(document).un('keydown', this.listKeyPress, this);
40279         }
40280         this.fireEvent('collapse', this);
40281     },
40282
40283     // private
40284     collapseIf : function(e){
40285         if(!e.within(this.wrap) && !e.within(this.list)){
40286             this.collapse();
40287         }
40288     },
40289
40290     /**
40291      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40292      */
40293     expand : function(){
40294         if(this.isExpanded() || !this.hasFocus){
40295             return;
40296         }
40297         this.list.alignTo(this.el, this.listAlign);
40298         this.list.show();
40299         Roo.get(document).on('mousedown', this.collapseIf, this);
40300         Roo.get(document).on('mousewheel', this.collapseIf, this);
40301         if (!this.editable) {
40302             Roo.get(document).on('keydown', this.listKeyPress, this);
40303         }
40304         
40305         this.fireEvent('expand', this);
40306     },
40307
40308     // private
40309     // Implements the default empty TriggerField.onTriggerClick function
40310     onTriggerClick : function(){
40311         if(this.disabled){
40312             return;
40313         }
40314         if(this.isExpanded()){
40315             this.collapse();
40316             if (!this.blockFocus) {
40317                 this.el.focus();
40318             }
40319             
40320         }else {
40321             this.hasFocus = true;
40322             if(this.triggerAction == 'all') {
40323                 this.doQuery(this.allQuery, true);
40324             } else {
40325                 this.doQuery(this.getRawValue());
40326             }
40327             if (!this.blockFocus) {
40328                 this.el.focus();
40329             }
40330         }
40331     },
40332     listKeyPress : function(e)
40333     {
40334         //Roo.log('listkeypress');
40335         // scroll to first matching element based on key pres..
40336         if (e.isSpecialKey()) {
40337             return false;
40338         }
40339         var k = String.fromCharCode(e.getKey()).toUpperCase();
40340         //Roo.log(k);
40341         var match  = false;
40342         var csel = this.view.getSelectedNodes();
40343         var cselitem = false;
40344         if (csel.length) {
40345             var ix = this.view.indexOf(csel[0]);
40346             cselitem  = this.store.getAt(ix);
40347             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40348                 cselitem = false;
40349             }
40350             
40351         }
40352         
40353         this.store.each(function(v) { 
40354             if (cselitem) {
40355                 // start at existing selection.
40356                 if (cselitem.id == v.id) {
40357                     cselitem = false;
40358                 }
40359                 return;
40360             }
40361                 
40362             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40363                 match = this.store.indexOf(v);
40364                 return false;
40365             }
40366         }, this);
40367         
40368         if (match === false) {
40369             return true; // no more action?
40370         }
40371         // scroll to?
40372         this.view.select(match);
40373         var sn = Roo.get(this.view.getSelectedNodes()[0]);
40374         sn.scrollIntoView(sn.dom.parentNode, false);
40375     }
40376
40377     /** 
40378     * @cfg {Boolean} grow 
40379     * @hide 
40380     */
40381     /** 
40382     * @cfg {Number} growMin 
40383     * @hide 
40384     */
40385     /** 
40386     * @cfg {Number} growMax 
40387     * @hide 
40388     */
40389     /**
40390      * @hide
40391      * @method autoSize
40392      */
40393 });/*
40394  * Copyright(c) 2010-2012, Roo J Solutions Limited
40395  *
40396  * Licence LGPL
40397  *
40398  */
40399
40400 /**
40401  * @class Roo.form.ComboBoxArray
40402  * @extends Roo.form.TextField
40403  * A facebook style adder... for lists of email / people / countries  etc...
40404  * pick multiple items from a combo box, and shows each one.
40405  *
40406  *  Fred [x]  Brian [x]  [Pick another |v]
40407  *
40408  *
40409  *  For this to work: it needs various extra information
40410  *    - normal combo problay has
40411  *      name, hiddenName
40412  *    + displayField, valueField
40413  *
40414  *    For our purpose...
40415  *
40416  *
40417  *   If we change from 'extends' to wrapping...
40418  *   
40419  *  
40420  *
40421  
40422  
40423  * @constructor
40424  * Create a new ComboBoxArray.
40425  * @param {Object} config Configuration options
40426  */
40427  
40428
40429 Roo.form.ComboBoxArray = function(config)
40430 {
40431     this.addEvents({
40432         /**
40433          * @event beforeremove
40434          * Fires before remove the value from the list
40435              * @param {Roo.form.ComboBoxArray} _self This combo box array
40436              * @param {Roo.form.ComboBoxArray.Item} item removed item
40437              */
40438         'beforeremove' : true,
40439         /**
40440          * @event remove
40441          * Fires when remove the value from the list
40442              * @param {Roo.form.ComboBoxArray} _self This combo box array
40443              * @param {Roo.form.ComboBoxArray.Item} item removed item
40444              */
40445         'remove' : true
40446         
40447         
40448     });
40449     
40450     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40451     
40452     this.items = new Roo.util.MixedCollection(false);
40453     
40454     // construct the child combo...
40455     
40456     
40457     
40458     
40459    
40460     
40461 }
40462
40463  
40464 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40465
40466     /**
40467      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40468      */
40469     
40470     lastData : false,
40471     
40472     // behavies liek a hiddne field
40473     inputType:      'hidden',
40474     /**
40475      * @cfg {Number} width The width of the box that displays the selected element
40476      */ 
40477     width:          300,
40478
40479     
40480     
40481     /**
40482      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40483      */
40484     name : false,
40485     /**
40486      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40487      */
40488     hiddenName : false,
40489     
40490     
40491     // private the array of items that are displayed..
40492     items  : false,
40493     // private - the hidden field el.
40494     hiddenEl : false,
40495     // private - the filed el..
40496     el : false,
40497     
40498     //validateValue : function() { return true; }, // all values are ok!
40499     //onAddClick: function() { },
40500     
40501     onRender : function(ct, position) 
40502     {
40503         
40504         // create the standard hidden element
40505         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40506         
40507         
40508         // give fake names to child combo;
40509         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40510         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40511         
40512         this.combo = Roo.factory(this.combo, Roo.form);
40513         this.combo.onRender(ct, position);
40514         if (typeof(this.combo.width) != 'undefined') {
40515             this.combo.onResize(this.combo.width,0);
40516         }
40517         
40518         this.combo.initEvents();
40519         
40520         // assigned so form know we need to do this..
40521         this.store          = this.combo.store;
40522         this.valueField     = this.combo.valueField;
40523         this.displayField   = this.combo.displayField ;
40524         
40525         
40526         this.combo.wrap.addClass('x-cbarray-grp');
40527         
40528         var cbwrap = this.combo.wrap.createChild(
40529             {tag: 'div', cls: 'x-cbarray-cb'},
40530             this.combo.el.dom
40531         );
40532         
40533              
40534         this.hiddenEl = this.combo.wrap.createChild({
40535             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40536         });
40537         this.el = this.combo.wrap.createChild({
40538             tag: 'input',  type:'hidden' , name: this.name, value : ''
40539         });
40540          //   this.el.dom.removeAttribute("name");
40541         
40542         
40543         this.outerWrap = this.combo.wrap;
40544         this.wrap = cbwrap;
40545         
40546         this.outerWrap.setWidth(this.width);
40547         this.outerWrap.dom.removeChild(this.el.dom);
40548         
40549         this.wrap.dom.appendChild(this.el.dom);
40550         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40551         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40552         
40553         this.combo.trigger.setStyle('position','relative');
40554         this.combo.trigger.setStyle('left', '0px');
40555         this.combo.trigger.setStyle('top', '2px');
40556         
40557         this.combo.el.setStyle('vertical-align', 'text-bottom');
40558         
40559         //this.trigger.setStyle('vertical-align', 'top');
40560         
40561         // this should use the code from combo really... on('add' ....)
40562         if (this.adder) {
40563             
40564         
40565             this.adder = this.outerWrap.createChild(
40566                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40567             var _t = this;
40568             this.adder.on('click', function(e) {
40569                 _t.fireEvent('adderclick', this, e);
40570             }, _t);
40571         }
40572         //var _t = this;
40573         //this.adder.on('click', this.onAddClick, _t);
40574         
40575         
40576         this.combo.on('select', function(cb, rec, ix) {
40577             this.addItem(rec.data);
40578             
40579             cb.setValue('');
40580             cb.el.dom.value = '';
40581             //cb.lastData = rec.data;
40582             // add to list
40583             
40584         }, this);
40585         
40586         
40587     },
40588     
40589     
40590     getName: function()
40591     {
40592         // returns hidden if it's set..
40593         if (!this.rendered) {return ''};
40594         return  this.hiddenName ? this.hiddenName : this.name;
40595         
40596     },
40597     
40598     
40599     onResize: function(w, h){
40600         
40601         return;
40602         // not sure if this is needed..
40603         //this.combo.onResize(w,h);
40604         
40605         if(typeof w != 'number'){
40606             // we do not handle it!?!?
40607             return;
40608         }
40609         var tw = this.combo.trigger.getWidth();
40610         tw += this.addicon ? this.addicon.getWidth() : 0;
40611         tw += this.editicon ? this.editicon.getWidth() : 0;
40612         var x = w - tw;
40613         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40614             
40615         this.combo.trigger.setStyle('left', '0px');
40616         
40617         if(this.list && this.listWidth === undefined){
40618             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40619             this.list.setWidth(lw);
40620             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40621         }
40622         
40623     
40624         
40625     },
40626     
40627     addItem: function(rec)
40628     {
40629         var valueField = this.combo.valueField;
40630         var displayField = this.combo.displayField;
40631         if (this.items.indexOfKey(rec[valueField]) > -1) {
40632             //console.log("GOT " + rec.data.id);
40633             return;
40634         }
40635         
40636         var x = new Roo.form.ComboBoxArray.Item({
40637             //id : rec[this.idField],
40638             data : rec,
40639             displayField : displayField ,
40640             tipField : displayField ,
40641             cb : this
40642         });
40643         // use the 
40644         this.items.add(rec[valueField],x);
40645         // add it before the element..
40646         this.updateHiddenEl();
40647         x.render(this.outerWrap, this.wrap.dom);
40648         // add the image handler..
40649     },
40650     
40651     updateHiddenEl : function()
40652     {
40653         this.validate();
40654         if (!this.hiddenEl) {
40655             return;
40656         }
40657         var ar = [];
40658         var idField = this.combo.valueField;
40659         
40660         this.items.each(function(f) {
40661             ar.push(f.data[idField]);
40662            
40663         });
40664         this.hiddenEl.dom.value = ar.join(',');
40665         this.validate();
40666     },
40667     
40668     reset : function()
40669     {
40670         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40671         this.items.each(function(f) {
40672            f.remove(); 
40673         });
40674         this.el.dom.value = '';
40675         if (this.hiddenEl) {
40676             this.hiddenEl.dom.value = '';
40677         }
40678         
40679     },
40680     getValue: function()
40681     {
40682         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40683     },
40684     setValue: function(v) // not a valid action - must use addItems..
40685     {
40686          
40687         this.reset();
40688         
40689         
40690         
40691         if (this.store.isLocal && (typeof(v) == 'string')) {
40692             // then we can use the store to find the values..
40693             // comma seperated at present.. this needs to allow JSON based encoding..
40694             this.hiddenEl.value  = v;
40695             var v_ar = [];
40696             Roo.each(v.split(','), function(k) {
40697                 Roo.log("CHECK " + this.valueField + ',' + k);
40698                 var li = this.store.query(this.valueField, k);
40699                 if (!li.length) {
40700                     return;
40701                 }
40702                 var add = {};
40703                 add[this.valueField] = k;
40704                 add[this.displayField] = li.item(0).data[this.displayField];
40705                 
40706                 this.addItem(add);
40707             }, this) 
40708              
40709         }
40710         if (typeof(v) == 'object' ) {
40711             // then let's assume it's an array of objects..
40712             Roo.each(v, function(l) {
40713                 this.addItem(l);
40714             }, this);
40715              
40716         }
40717         
40718         
40719     },
40720     setFromData: function(v)
40721     {
40722         // this recieves an object, if setValues is called.
40723         this.reset();
40724         this.el.dom.value = v[this.displayField];
40725         this.hiddenEl.dom.value = v[this.valueField];
40726         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40727             return;
40728         }
40729         var kv = v[this.valueField];
40730         var dv = v[this.displayField];
40731         kv = typeof(kv) != 'string' ? '' : kv;
40732         dv = typeof(dv) != 'string' ? '' : dv;
40733         
40734         
40735         var keys = kv.split(',');
40736         var display = dv.split(',');
40737         for (var i = 0 ; i < keys.length; i++) {
40738             
40739             add = {};
40740             add[this.valueField] = keys[i];
40741             add[this.displayField] = display[i];
40742             this.addItem(add);
40743         }
40744       
40745         
40746     },
40747     
40748     /**
40749      * Validates the combox array value
40750      * @return {Boolean} True if the value is valid, else false
40751      */
40752     validate : function(){
40753         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40754             this.clearInvalid();
40755             return true;
40756         }
40757         return false;
40758     },
40759     
40760     validateValue : function(value){
40761         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40762         
40763     },
40764     
40765     /*@
40766      * overide
40767      * 
40768      */
40769     isDirty : function() {
40770         if(this.disabled) {
40771             return false;
40772         }
40773         
40774         try {
40775             var d = Roo.decode(String(this.originalValue));
40776         } catch (e) {
40777             return String(this.getValue()) !== String(this.originalValue);
40778         }
40779         
40780         var originalValue = [];
40781         
40782         for (var i = 0; i < d.length; i++){
40783             originalValue.push(d[i][this.valueField]);
40784         }
40785         
40786         return String(this.getValue()) !== String(originalValue.join(','));
40787         
40788     }
40789     
40790 });
40791
40792
40793
40794 /**
40795  * @class Roo.form.ComboBoxArray.Item
40796  * @extends Roo.BoxComponent
40797  * A selected item in the list
40798  *  Fred [x]  Brian [x]  [Pick another |v]
40799  * 
40800  * @constructor
40801  * Create a new item.
40802  * @param {Object} config Configuration options
40803  */
40804  
40805 Roo.form.ComboBoxArray.Item = function(config) {
40806     config.id = Roo.id();
40807     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40808 }
40809
40810 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40811     data : {},
40812     cb: false,
40813     displayField : false,
40814     tipField : false,
40815     
40816     
40817     defaultAutoCreate : {
40818         tag: 'div',
40819         cls: 'x-cbarray-item',
40820         cn : [ 
40821             { tag: 'div' },
40822             {
40823                 tag: 'img',
40824                 width:16,
40825                 height : 16,
40826                 src : Roo.BLANK_IMAGE_URL ,
40827                 align: 'center'
40828             }
40829         ]
40830         
40831     },
40832     
40833  
40834     onRender : function(ct, position)
40835     {
40836         Roo.form.Field.superclass.onRender.call(this, ct, position);
40837         
40838         if(!this.el){
40839             var cfg = this.getAutoCreate();
40840             this.el = ct.createChild(cfg, position);
40841         }
40842         
40843         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40844         
40845         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40846             this.cb.renderer(this.data) :
40847             String.format('{0}',this.data[this.displayField]);
40848         
40849             
40850         this.el.child('div').dom.setAttribute('qtip',
40851                         String.format('{0}',this.data[this.tipField])
40852         );
40853         
40854         this.el.child('img').on('click', this.remove, this);
40855         
40856     },
40857    
40858     remove : function()
40859     {
40860         if(this.cb.disabled){
40861             return;
40862         }
40863         
40864         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40865             this.cb.items.remove(this);
40866             this.el.child('img').un('click', this.remove, this);
40867             this.el.remove();
40868             this.cb.updateHiddenEl();
40869
40870             this.cb.fireEvent('remove', this.cb, this);
40871         }
40872         
40873     }
40874 });/*
40875  * Based on:
40876  * Ext JS Library 1.1.1
40877  * Copyright(c) 2006-2007, Ext JS, LLC.
40878  *
40879  * Originally Released Under LGPL - original licence link has changed is not relivant.
40880  *
40881  * Fork - LGPL
40882  * <script type="text/javascript">
40883  */
40884 /**
40885  * @class Roo.form.Checkbox
40886  * @extends Roo.form.Field
40887  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40888  * @constructor
40889  * Creates a new Checkbox
40890  * @param {Object} config Configuration options
40891  */
40892 Roo.form.Checkbox = function(config){
40893     Roo.form.Checkbox.superclass.constructor.call(this, config);
40894     this.addEvents({
40895         /**
40896          * @event check
40897          * Fires when the checkbox is checked or unchecked.
40898              * @param {Roo.form.Checkbox} this This checkbox
40899              * @param {Boolean} checked The new checked value
40900              */
40901         check : true
40902     });
40903 };
40904
40905 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40906     /**
40907      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40908      */
40909     focusClass : undefined,
40910     /**
40911      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40912      */
40913     fieldClass: "x-form-field",
40914     /**
40915      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40916      */
40917     checked: false,
40918     /**
40919      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40920      * {tag: "input", type: "checkbox", autocomplete: "off"})
40921      */
40922     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40923     /**
40924      * @cfg {String} boxLabel The text that appears beside the checkbox
40925      */
40926     boxLabel : "",
40927     /**
40928      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40929      */  
40930     inputValue : '1',
40931     /**
40932      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40933      */
40934      valueOff: '0', // value when not checked..
40935
40936     actionMode : 'viewEl', 
40937     //
40938     // private
40939     itemCls : 'x-menu-check-item x-form-item',
40940     groupClass : 'x-menu-group-item',
40941     inputType : 'hidden',
40942     
40943     
40944     inSetChecked: false, // check that we are not calling self...
40945     
40946     inputElement: false, // real input element?
40947     basedOn: false, // ????
40948     
40949     isFormField: true, // not sure where this is needed!!!!
40950
40951     onResize : function(){
40952         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40953         if(!this.boxLabel){
40954             this.el.alignTo(this.wrap, 'c-c');
40955         }
40956     },
40957
40958     initEvents : function(){
40959         Roo.form.Checkbox.superclass.initEvents.call(this);
40960         this.el.on("click", this.onClick,  this);
40961         this.el.on("change", this.onClick,  this);
40962     },
40963
40964
40965     getResizeEl : function(){
40966         return this.wrap;
40967     },
40968
40969     getPositionEl : function(){
40970         return this.wrap;
40971     },
40972
40973     // private
40974     onRender : function(ct, position){
40975         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40976         /*
40977         if(this.inputValue !== undefined){
40978             this.el.dom.value = this.inputValue;
40979         }
40980         */
40981         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40982         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40983         var viewEl = this.wrap.createChild({ 
40984             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40985         this.viewEl = viewEl;   
40986         this.wrap.on('click', this.onClick,  this); 
40987         
40988         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40989         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40990         
40991         
40992         
40993         if(this.boxLabel){
40994             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40995         //    viewEl.on('click', this.onClick,  this); 
40996         }
40997         //if(this.checked){
40998             this.setChecked(this.checked);
40999         //}else{
41000             //this.checked = this.el.dom;
41001         //}
41002
41003     },
41004
41005     // private
41006     initValue : Roo.emptyFn,
41007
41008     /**
41009      * Returns the checked state of the checkbox.
41010      * @return {Boolean} True if checked, else false
41011      */
41012     getValue : function(){
41013         if(this.el){
41014             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
41015         }
41016         return this.valueOff;
41017         
41018     },
41019
41020         // private
41021     onClick : function(){ 
41022         if (this.disabled) {
41023             return;
41024         }
41025         this.setChecked(!this.checked);
41026
41027         //if(this.el.dom.checked != this.checked){
41028         //    this.setValue(this.el.dom.checked);
41029        // }
41030     },
41031
41032     /**
41033      * Sets the checked state of the checkbox.
41034      * On is always based on a string comparison between inputValue and the param.
41035      * @param {Boolean/String} value - the value to set 
41036      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
41037      */
41038     setValue : function(v,suppressEvent){
41039         
41040         
41041         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
41042         //if(this.el && this.el.dom){
41043         //    this.el.dom.checked = this.checked;
41044         //    this.el.dom.defaultChecked = this.checked;
41045         //}
41046         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
41047         //this.fireEvent("check", this, this.checked);
41048     },
41049     // private..
41050     setChecked : function(state,suppressEvent)
41051     {
41052         if (this.inSetChecked) {
41053             this.checked = state;
41054             return;
41055         }
41056         
41057     
41058         if(this.wrap){
41059             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41060         }
41061         this.checked = state;
41062         if(suppressEvent !== true){
41063             this.fireEvent('check', this, state);
41064         }
41065         this.inSetChecked = true;
41066         this.el.dom.value = state ? this.inputValue : this.valueOff;
41067         this.inSetChecked = false;
41068         
41069     },
41070     // handle setting of hidden value by some other method!!?!?
41071     setFromHidden: function()
41072     {
41073         if(!this.el){
41074             return;
41075         }
41076         //console.log("SET FROM HIDDEN");
41077         //alert('setFrom hidden');
41078         this.setValue(this.el.dom.value);
41079     },
41080     
41081     onDestroy : function()
41082     {
41083         if(this.viewEl){
41084             Roo.get(this.viewEl).remove();
41085         }
41086          
41087         Roo.form.Checkbox.superclass.onDestroy.call(this);
41088     }
41089
41090 });/*
41091  * Based on:
41092  * Ext JS Library 1.1.1
41093  * Copyright(c) 2006-2007, Ext JS, LLC.
41094  *
41095  * Originally Released Under LGPL - original licence link has changed is not relivant.
41096  *
41097  * Fork - LGPL
41098  * <script type="text/javascript">
41099  */
41100  
41101 /**
41102  * @class Roo.form.Radio
41103  * @extends Roo.form.Checkbox
41104  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41105  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41106  * @constructor
41107  * Creates a new Radio
41108  * @param {Object} config Configuration options
41109  */
41110 Roo.form.Radio = function(){
41111     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41112 };
41113 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41114     inputType: 'radio',
41115
41116     /**
41117      * If this radio is part of a group, it will return the selected value
41118      * @return {String}
41119      */
41120     getGroupValue : function(){
41121         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41122     },
41123     
41124     
41125     onRender : function(ct, position){
41126         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41127         
41128         if(this.inputValue !== undefined){
41129             this.el.dom.value = this.inputValue;
41130         }
41131          
41132         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41133         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41134         //var viewEl = this.wrap.createChild({ 
41135         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41136         //this.viewEl = viewEl;   
41137         //this.wrap.on('click', this.onClick,  this); 
41138         
41139         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41140         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41141         
41142         
41143         
41144         if(this.boxLabel){
41145             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41146         //    viewEl.on('click', this.onClick,  this); 
41147         }
41148          if(this.checked){
41149             this.el.dom.checked =   'checked' ;
41150         }
41151          
41152     } 
41153     
41154     
41155 });//<script type="text/javascript">
41156
41157 /*
41158  * Based  Ext JS Library 1.1.1
41159  * Copyright(c) 2006-2007, Ext JS, LLC.
41160  * LGPL
41161  *
41162  */
41163  
41164 /**
41165  * @class Roo.HtmlEditorCore
41166  * @extends Roo.Component
41167  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41168  *
41169  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41170  */
41171
41172 Roo.HtmlEditorCore = function(config){
41173     
41174     
41175     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41176     
41177     
41178     this.addEvents({
41179         /**
41180          * @event initialize
41181          * Fires when the editor is fully initialized (including the iframe)
41182          * @param {Roo.HtmlEditorCore} this
41183          */
41184         initialize: true,
41185         /**
41186          * @event activate
41187          * Fires when the editor is first receives the focus. Any insertion must wait
41188          * until after this event.
41189          * @param {Roo.HtmlEditorCore} this
41190          */
41191         activate: true,
41192          /**
41193          * @event beforesync
41194          * Fires before the textarea is updated with content from the editor iframe. Return false
41195          * to cancel the sync.
41196          * @param {Roo.HtmlEditorCore} this
41197          * @param {String} html
41198          */
41199         beforesync: true,
41200          /**
41201          * @event beforepush
41202          * Fires before the iframe editor is updated with content from the textarea. Return false
41203          * to cancel the push.
41204          * @param {Roo.HtmlEditorCore} this
41205          * @param {String} html
41206          */
41207         beforepush: true,
41208          /**
41209          * @event sync
41210          * Fires when the textarea is updated with content from the editor iframe.
41211          * @param {Roo.HtmlEditorCore} this
41212          * @param {String} html
41213          */
41214         sync: true,
41215          /**
41216          * @event push
41217          * Fires when the iframe editor is updated with content from the textarea.
41218          * @param {Roo.HtmlEditorCore} this
41219          * @param {String} html
41220          */
41221         push: true,
41222         
41223         /**
41224          * @event editorevent
41225          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41226          * @param {Roo.HtmlEditorCore} this
41227          */
41228         editorevent: true
41229         
41230     });
41231     
41232     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41233     
41234     // defaults : white / black...
41235     this.applyBlacklists();
41236     
41237     
41238     
41239 };
41240
41241
41242 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41243
41244
41245      /**
41246      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41247      */
41248     
41249     owner : false,
41250     
41251      /**
41252      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41253      *                        Roo.resizable.
41254      */
41255     resizable : false,
41256      /**
41257      * @cfg {Number} height (in pixels)
41258      */   
41259     height: 300,
41260    /**
41261      * @cfg {Number} width (in pixels)
41262      */   
41263     width: 500,
41264     
41265     /**
41266      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41267      * 
41268      */
41269     stylesheets: false,
41270     
41271     // id of frame..
41272     frameId: false,
41273     
41274     // private properties
41275     validationEvent : false,
41276     deferHeight: true,
41277     initialized : false,
41278     activated : false,
41279     sourceEditMode : false,
41280     onFocus : Roo.emptyFn,
41281     iframePad:3,
41282     hideMode:'offsets',
41283     
41284     clearUp: true,
41285     
41286     // blacklist + whitelisted elements..
41287     black: false,
41288     white: false,
41289      
41290     
41291
41292     /**
41293      * Protected method that will not generally be called directly. It
41294      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41295      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41296      */
41297     getDocMarkup : function(){
41298         // body styles..
41299         var st = '';
41300         
41301         // inherit styels from page...?? 
41302         if (this.stylesheets === false) {
41303             
41304             Roo.get(document.head).select('style').each(function(node) {
41305                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41306             });
41307             
41308             Roo.get(document.head).select('link').each(function(node) { 
41309                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41310             });
41311             
41312         } else if (!this.stylesheets.length) {
41313                 // simple..
41314                 st = '<style type="text/css">' +
41315                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41316                    '</style>';
41317         } else { 
41318             
41319         }
41320         
41321         st +=  '<style type="text/css">' +
41322             'IMG { cursor: pointer } ' +
41323         '</style>';
41324
41325         
41326         return '<html><head>' + st  +
41327             //<style type="text/css">' +
41328             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41329             //'</style>' +
41330             ' </head><body class="roo-htmleditor-body"></body></html>';
41331     },
41332
41333     // private
41334     onRender : function(ct, position)
41335     {
41336         var _t = this;
41337         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41338         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41339         
41340         
41341         this.el.dom.style.border = '0 none';
41342         this.el.dom.setAttribute('tabIndex', -1);
41343         this.el.addClass('x-hidden hide');
41344         
41345         
41346         
41347         if(Roo.isIE){ // fix IE 1px bogus margin
41348             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41349         }
41350        
41351         
41352         this.frameId = Roo.id();
41353         
41354          
41355         
41356         var iframe = this.owner.wrap.createChild({
41357             tag: 'iframe',
41358             cls: 'form-control', // bootstrap..
41359             id: this.frameId,
41360             name: this.frameId,
41361             frameBorder : 'no',
41362             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41363         }, this.el
41364         );
41365         
41366         
41367         this.iframe = iframe.dom;
41368
41369          this.assignDocWin();
41370         
41371         this.doc.designMode = 'on';
41372        
41373         this.doc.open();
41374         this.doc.write(this.getDocMarkup());
41375         this.doc.close();
41376
41377         
41378         var task = { // must defer to wait for browser to be ready
41379             run : function(){
41380                 //console.log("run task?" + this.doc.readyState);
41381                 this.assignDocWin();
41382                 if(this.doc.body || this.doc.readyState == 'complete'){
41383                     try {
41384                         this.doc.designMode="on";
41385                     } catch (e) {
41386                         return;
41387                     }
41388                     Roo.TaskMgr.stop(task);
41389                     this.initEditor.defer(10, this);
41390                 }
41391             },
41392             interval : 10,
41393             duration: 10000,
41394             scope: this
41395         };
41396         Roo.TaskMgr.start(task);
41397
41398     },
41399
41400     // private
41401     onResize : function(w, h)
41402     {
41403          Roo.log('resize: ' +w + ',' + h );
41404         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41405         if(!this.iframe){
41406             return;
41407         }
41408         if(typeof w == 'number'){
41409             
41410             this.iframe.style.width = w + 'px';
41411         }
41412         if(typeof h == 'number'){
41413             
41414             this.iframe.style.height = h + 'px';
41415             if(this.doc){
41416                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41417             }
41418         }
41419         
41420     },
41421
41422     /**
41423      * Toggles the editor between standard and source edit mode.
41424      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41425      */
41426     toggleSourceEdit : function(sourceEditMode){
41427         
41428         this.sourceEditMode = sourceEditMode === true;
41429         
41430         if(this.sourceEditMode){
41431  
41432             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41433             
41434         }else{
41435             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41436             //this.iframe.className = '';
41437             this.deferFocus();
41438         }
41439         //this.setSize(this.owner.wrap.getSize());
41440         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41441     },
41442
41443     
41444   
41445
41446     /**
41447      * Protected method that will not generally be called directly. If you need/want
41448      * custom HTML cleanup, this is the method you should override.
41449      * @param {String} html The HTML to be cleaned
41450      * return {String} The cleaned HTML
41451      */
41452     cleanHtml : function(html){
41453         html = String(html);
41454         if(html.length > 5){
41455             if(Roo.isSafari){ // strip safari nonsense
41456                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41457             }
41458         }
41459         if(html == '&nbsp;'){
41460             html = '';
41461         }
41462         return html;
41463     },
41464
41465     /**
41466      * HTML Editor -> Textarea
41467      * Protected method that will not generally be called directly. Syncs the contents
41468      * of the editor iframe with the textarea.
41469      */
41470     syncValue : function(){
41471         if(this.initialized){
41472             var bd = (this.doc.body || this.doc.documentElement);
41473             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41474             var html = bd.innerHTML;
41475             if(Roo.isSafari){
41476                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41477                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41478                 if(m && m[1]){
41479                     html = '<div style="'+m[0]+'">' + html + '</div>';
41480                 }
41481             }
41482             html = this.cleanHtml(html);
41483             // fix up the special chars.. normaly like back quotes in word...
41484             // however we do not want to do this with chinese..
41485             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41486                 var cc = b.charCodeAt();
41487                 if (
41488                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41489                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41490                     (cc >= 0xf900 && cc < 0xfb00 )
41491                 ) {
41492                         return b;
41493                 }
41494                 return "&#"+cc+";" 
41495             });
41496             if(this.owner.fireEvent('beforesync', this, html) !== false){
41497                 this.el.dom.value = html;
41498                 this.owner.fireEvent('sync', this, html);
41499             }
41500         }
41501     },
41502
41503     /**
41504      * Protected method that will not generally be called directly. Pushes the value of the textarea
41505      * into the iframe editor.
41506      */
41507     pushValue : function(){
41508         if(this.initialized){
41509             var v = this.el.dom.value.trim();
41510             
41511 //            if(v.length < 1){
41512 //                v = '&#160;';
41513 //            }
41514             
41515             if(this.owner.fireEvent('beforepush', this, v) !== false){
41516                 var d = (this.doc.body || this.doc.documentElement);
41517                 d.innerHTML = v;
41518                 this.cleanUpPaste();
41519                 this.el.dom.value = d.innerHTML;
41520                 this.owner.fireEvent('push', this, v);
41521             }
41522         }
41523     },
41524
41525     // private
41526     deferFocus : function(){
41527         this.focus.defer(10, this);
41528     },
41529
41530     // doc'ed in Field
41531     focus : function(){
41532         if(this.win && !this.sourceEditMode){
41533             this.win.focus();
41534         }else{
41535             this.el.focus();
41536         }
41537     },
41538     
41539     assignDocWin: function()
41540     {
41541         var iframe = this.iframe;
41542         
41543          if(Roo.isIE){
41544             this.doc = iframe.contentWindow.document;
41545             this.win = iframe.contentWindow;
41546         } else {
41547 //            if (!Roo.get(this.frameId)) {
41548 //                return;
41549 //            }
41550 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41551 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41552             
41553             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41554                 return;
41555             }
41556             
41557             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41558             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41559         }
41560     },
41561     
41562     // private
41563     initEditor : function(){
41564         //console.log("INIT EDITOR");
41565         this.assignDocWin();
41566         
41567         
41568         
41569         this.doc.designMode="on";
41570         this.doc.open();
41571         this.doc.write(this.getDocMarkup());
41572         this.doc.close();
41573         
41574         var dbody = (this.doc.body || this.doc.documentElement);
41575         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41576         // this copies styles from the containing element into thsi one..
41577         // not sure why we need all of this..
41578         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41579         
41580         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41581         //ss['background-attachment'] = 'fixed'; // w3c
41582         dbody.bgProperties = 'fixed'; // ie
41583         //Roo.DomHelper.applyStyles(dbody, ss);
41584         Roo.EventManager.on(this.doc, {
41585             //'mousedown': this.onEditorEvent,
41586             'mouseup': this.onEditorEvent,
41587             'dblclick': this.onEditorEvent,
41588             'click': this.onEditorEvent,
41589             'keyup': this.onEditorEvent,
41590             buffer:100,
41591             scope: this
41592         });
41593         if(Roo.isGecko){
41594             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41595         }
41596         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41597             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41598         }
41599         this.initialized = true;
41600
41601         this.owner.fireEvent('initialize', this);
41602         this.pushValue();
41603     },
41604
41605     // private
41606     onDestroy : function(){
41607         
41608         
41609         
41610         if(this.rendered){
41611             
41612             //for (var i =0; i < this.toolbars.length;i++) {
41613             //    // fixme - ask toolbars for heights?
41614             //    this.toolbars[i].onDestroy();
41615            // }
41616             
41617             //this.wrap.dom.innerHTML = '';
41618             //this.wrap.remove();
41619         }
41620     },
41621
41622     // private
41623     onFirstFocus : function(){
41624         
41625         this.assignDocWin();
41626         
41627         
41628         this.activated = true;
41629          
41630     
41631         if(Roo.isGecko){ // prevent silly gecko errors
41632             this.win.focus();
41633             var s = this.win.getSelection();
41634             if(!s.focusNode || s.focusNode.nodeType != 3){
41635                 var r = s.getRangeAt(0);
41636                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41637                 r.collapse(true);
41638                 this.deferFocus();
41639             }
41640             try{
41641                 this.execCmd('useCSS', true);
41642                 this.execCmd('styleWithCSS', false);
41643             }catch(e){}
41644         }
41645         this.owner.fireEvent('activate', this);
41646     },
41647
41648     // private
41649     adjustFont: function(btn){
41650         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41651         //if(Roo.isSafari){ // safari
41652         //    adjust *= 2;
41653        // }
41654         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41655         if(Roo.isSafari){ // safari
41656             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41657             v =  (v < 10) ? 10 : v;
41658             v =  (v > 48) ? 48 : v;
41659             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41660             
41661         }
41662         
41663         
41664         v = Math.max(1, v+adjust);
41665         
41666         this.execCmd('FontSize', v  );
41667     },
41668
41669     onEditorEvent : function(e)
41670     {
41671         this.owner.fireEvent('editorevent', this, e);
41672       //  this.updateToolbar();
41673         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41674     },
41675
41676     insertTag : function(tg)
41677     {
41678         // could be a bit smarter... -> wrap the current selected tRoo..
41679         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41680             
41681             range = this.createRange(this.getSelection());
41682             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41683             wrappingNode.appendChild(range.extractContents());
41684             range.insertNode(wrappingNode);
41685
41686             return;
41687             
41688             
41689             
41690         }
41691         this.execCmd("formatblock",   tg);
41692         
41693     },
41694     
41695     insertText : function(txt)
41696     {
41697         
41698         
41699         var range = this.createRange();
41700         range.deleteContents();
41701                //alert(Sender.getAttribute('label'));
41702                
41703         range.insertNode(this.doc.createTextNode(txt));
41704     } ,
41705     
41706      
41707
41708     /**
41709      * Executes a Midas editor command on the editor document and performs necessary focus and
41710      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41711      * @param {String} cmd The Midas command
41712      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41713      */
41714     relayCmd : function(cmd, value){
41715         this.win.focus();
41716         this.execCmd(cmd, value);
41717         this.owner.fireEvent('editorevent', this);
41718         //this.updateToolbar();
41719         this.owner.deferFocus();
41720     },
41721
41722     /**
41723      * Executes a Midas editor command directly on the editor document.
41724      * For visual commands, you should use {@link #relayCmd} instead.
41725      * <b>This should only be called after the editor is initialized.</b>
41726      * @param {String} cmd The Midas command
41727      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41728      */
41729     execCmd : function(cmd, value){
41730         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41731         this.syncValue();
41732     },
41733  
41734  
41735    
41736     /**
41737      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41738      * to insert tRoo.
41739      * @param {String} text | dom node.. 
41740      */
41741     insertAtCursor : function(text)
41742     {
41743         
41744         
41745         
41746         if(!this.activated){
41747             return;
41748         }
41749         /*
41750         if(Roo.isIE){
41751             this.win.focus();
41752             var r = this.doc.selection.createRange();
41753             if(r){
41754                 r.collapse(true);
41755                 r.pasteHTML(text);
41756                 this.syncValue();
41757                 this.deferFocus();
41758             
41759             }
41760             return;
41761         }
41762         */
41763         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41764             this.win.focus();
41765             
41766             
41767             // from jquery ui (MIT licenced)
41768             var range, node;
41769             var win = this.win;
41770             
41771             if (win.getSelection && win.getSelection().getRangeAt) {
41772                 range = win.getSelection().getRangeAt(0);
41773                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41774                 range.insertNode(node);
41775             } else if (win.document.selection && win.document.selection.createRange) {
41776                 // no firefox support
41777                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41778                 win.document.selection.createRange().pasteHTML(txt);
41779             } else {
41780                 // no firefox support
41781                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41782                 this.execCmd('InsertHTML', txt);
41783             } 
41784             
41785             this.syncValue();
41786             
41787             this.deferFocus();
41788         }
41789     },
41790  // private
41791     mozKeyPress : function(e){
41792         if(e.ctrlKey){
41793             var c = e.getCharCode(), cmd;
41794           
41795             if(c > 0){
41796                 c = String.fromCharCode(c).toLowerCase();
41797                 switch(c){
41798                     case 'b':
41799                         cmd = 'bold';
41800                         break;
41801                     case 'i':
41802                         cmd = 'italic';
41803                         break;
41804                     
41805                     case 'u':
41806                         cmd = 'underline';
41807                         break;
41808                     
41809                     case 'v':
41810                         this.cleanUpPaste.defer(100, this);
41811                         return;
41812                         
41813                 }
41814                 if(cmd){
41815                     this.win.focus();
41816                     this.execCmd(cmd);
41817                     this.deferFocus();
41818                     e.preventDefault();
41819                 }
41820                 
41821             }
41822         }
41823     },
41824
41825     // private
41826     fixKeys : function(){ // load time branching for fastest keydown performance
41827         if(Roo.isIE){
41828             return function(e){
41829                 var k = e.getKey(), r;
41830                 if(k == e.TAB){
41831                     e.stopEvent();
41832                     r = this.doc.selection.createRange();
41833                     if(r){
41834                         r.collapse(true);
41835                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41836                         this.deferFocus();
41837                     }
41838                     return;
41839                 }
41840                 
41841                 if(k == e.ENTER){
41842                     r = this.doc.selection.createRange();
41843                     if(r){
41844                         var target = r.parentElement();
41845                         if(!target || target.tagName.toLowerCase() != 'li'){
41846                             e.stopEvent();
41847                             r.pasteHTML('<br />');
41848                             r.collapse(false);
41849                             r.select();
41850                         }
41851                     }
41852                 }
41853                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41854                     this.cleanUpPaste.defer(100, this);
41855                     return;
41856                 }
41857                 
41858                 
41859             };
41860         }else if(Roo.isOpera){
41861             return function(e){
41862                 var k = e.getKey();
41863                 if(k == e.TAB){
41864                     e.stopEvent();
41865                     this.win.focus();
41866                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41867                     this.deferFocus();
41868                 }
41869                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41870                     this.cleanUpPaste.defer(100, this);
41871                     return;
41872                 }
41873                 
41874             };
41875         }else if(Roo.isSafari){
41876             return function(e){
41877                 var k = e.getKey();
41878                 
41879                 if(k == e.TAB){
41880                     e.stopEvent();
41881                     this.execCmd('InsertText','\t');
41882                     this.deferFocus();
41883                     return;
41884                 }
41885                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41886                     this.cleanUpPaste.defer(100, this);
41887                     return;
41888                 }
41889                 
41890              };
41891         }
41892     }(),
41893     
41894     getAllAncestors: function()
41895     {
41896         var p = this.getSelectedNode();
41897         var a = [];
41898         if (!p) {
41899             a.push(p); // push blank onto stack..
41900             p = this.getParentElement();
41901         }
41902         
41903         
41904         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41905             a.push(p);
41906             p = p.parentNode;
41907         }
41908         a.push(this.doc.body);
41909         return a;
41910     },
41911     lastSel : false,
41912     lastSelNode : false,
41913     
41914     
41915     getSelection : function() 
41916     {
41917         this.assignDocWin();
41918         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41919     },
41920     
41921     getSelectedNode: function() 
41922     {
41923         // this may only work on Gecko!!!
41924         
41925         // should we cache this!!!!
41926         
41927         
41928         
41929          
41930         var range = this.createRange(this.getSelection()).cloneRange();
41931         
41932         if (Roo.isIE) {
41933             var parent = range.parentElement();
41934             while (true) {
41935                 var testRange = range.duplicate();
41936                 testRange.moveToElementText(parent);
41937                 if (testRange.inRange(range)) {
41938                     break;
41939                 }
41940                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41941                     break;
41942                 }
41943                 parent = parent.parentElement;
41944             }
41945             return parent;
41946         }
41947         
41948         // is ancestor a text element.
41949         var ac =  range.commonAncestorContainer;
41950         if (ac.nodeType == 3) {
41951             ac = ac.parentNode;
41952         }
41953         
41954         var ar = ac.childNodes;
41955          
41956         var nodes = [];
41957         var other_nodes = [];
41958         var has_other_nodes = false;
41959         for (var i=0;i<ar.length;i++) {
41960             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41961                 continue;
41962             }
41963             // fullly contained node.
41964             
41965             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41966                 nodes.push(ar[i]);
41967                 continue;
41968             }
41969             
41970             // probably selected..
41971             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41972                 other_nodes.push(ar[i]);
41973                 continue;
41974             }
41975             // outer..
41976             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41977                 continue;
41978             }
41979             
41980             
41981             has_other_nodes = true;
41982         }
41983         if (!nodes.length && other_nodes.length) {
41984             nodes= other_nodes;
41985         }
41986         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41987             return false;
41988         }
41989         
41990         return nodes[0];
41991     },
41992     createRange: function(sel)
41993     {
41994         // this has strange effects when using with 
41995         // top toolbar - not sure if it's a great idea.
41996         //this.editor.contentWindow.focus();
41997         if (typeof sel != "undefined") {
41998             try {
41999                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
42000             } catch(e) {
42001                 return this.doc.createRange();
42002             }
42003         } else {
42004             return this.doc.createRange();
42005         }
42006     },
42007     getParentElement: function()
42008     {
42009         
42010         this.assignDocWin();
42011         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
42012         
42013         var range = this.createRange(sel);
42014          
42015         try {
42016             var p = range.commonAncestorContainer;
42017             while (p.nodeType == 3) { // text node
42018                 p = p.parentNode;
42019             }
42020             return p;
42021         } catch (e) {
42022             return null;
42023         }
42024     
42025     },
42026     /***
42027      *
42028      * Range intersection.. the hard stuff...
42029      *  '-1' = before
42030      *  '0' = hits..
42031      *  '1' = after.
42032      *         [ -- selected range --- ]
42033      *   [fail]                        [fail]
42034      *
42035      *    basically..
42036      *      if end is before start or  hits it. fail.
42037      *      if start is after end or hits it fail.
42038      *
42039      *   if either hits (but other is outside. - then it's not 
42040      *   
42041      *    
42042      **/
42043     
42044     
42045     // @see http://www.thismuchiknow.co.uk/?p=64.
42046     rangeIntersectsNode : function(range, node)
42047     {
42048         var nodeRange = node.ownerDocument.createRange();
42049         try {
42050             nodeRange.selectNode(node);
42051         } catch (e) {
42052             nodeRange.selectNodeContents(node);
42053         }
42054     
42055         var rangeStartRange = range.cloneRange();
42056         rangeStartRange.collapse(true);
42057     
42058         var rangeEndRange = range.cloneRange();
42059         rangeEndRange.collapse(false);
42060     
42061         var nodeStartRange = nodeRange.cloneRange();
42062         nodeStartRange.collapse(true);
42063     
42064         var nodeEndRange = nodeRange.cloneRange();
42065         nodeEndRange.collapse(false);
42066     
42067         return rangeStartRange.compareBoundaryPoints(
42068                  Range.START_TO_START, nodeEndRange) == -1 &&
42069                rangeEndRange.compareBoundaryPoints(
42070                  Range.START_TO_START, nodeStartRange) == 1;
42071         
42072          
42073     },
42074     rangeCompareNode : function(range, node)
42075     {
42076         var nodeRange = node.ownerDocument.createRange();
42077         try {
42078             nodeRange.selectNode(node);
42079         } catch (e) {
42080             nodeRange.selectNodeContents(node);
42081         }
42082         
42083         
42084         range.collapse(true);
42085     
42086         nodeRange.collapse(true);
42087      
42088         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42089         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42090          
42091         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42092         
42093         var nodeIsBefore   =  ss == 1;
42094         var nodeIsAfter    = ee == -1;
42095         
42096         if (nodeIsBefore && nodeIsAfter) {
42097             return 0; // outer
42098         }
42099         if (!nodeIsBefore && nodeIsAfter) {
42100             return 1; //right trailed.
42101         }
42102         
42103         if (nodeIsBefore && !nodeIsAfter) {
42104             return 2;  // left trailed.
42105         }
42106         // fully contined.
42107         return 3;
42108     },
42109
42110     // private? - in a new class?
42111     cleanUpPaste :  function()
42112     {
42113         // cleans up the whole document..
42114         Roo.log('cleanuppaste');
42115         
42116         this.cleanUpChildren(this.doc.body);
42117         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42118         if (clean != this.doc.body.innerHTML) {
42119             this.doc.body.innerHTML = clean;
42120         }
42121         
42122     },
42123     
42124     cleanWordChars : function(input) {// change the chars to hex code
42125         var he = Roo.HtmlEditorCore;
42126         
42127         var output = input;
42128         Roo.each(he.swapCodes, function(sw) { 
42129             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42130             
42131             output = output.replace(swapper, sw[1]);
42132         });
42133         
42134         return output;
42135     },
42136     
42137     
42138     cleanUpChildren : function (n)
42139     {
42140         if (!n.childNodes.length) {
42141             return;
42142         }
42143         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42144            this.cleanUpChild(n.childNodes[i]);
42145         }
42146     },
42147     
42148     
42149         
42150     
42151     cleanUpChild : function (node)
42152     {
42153         var ed = this;
42154         //console.log(node);
42155         if (node.nodeName == "#text") {
42156             // clean up silly Windows -- stuff?
42157             return; 
42158         }
42159         if (node.nodeName == "#comment") {
42160             node.parentNode.removeChild(node);
42161             // clean up silly Windows -- stuff?
42162             return; 
42163         }
42164         var lcname = node.tagName.toLowerCase();
42165         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42166         // whitelist of tags..
42167         
42168         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42169             // remove node.
42170             node.parentNode.removeChild(node);
42171             return;
42172             
42173         }
42174         
42175         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42176         
42177         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42178         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42179         
42180         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42181         //    remove_keep_children = true;
42182         //}
42183         
42184         if (remove_keep_children) {
42185             this.cleanUpChildren(node);
42186             // inserts everything just before this node...
42187             while (node.childNodes.length) {
42188                 var cn = node.childNodes[0];
42189                 node.removeChild(cn);
42190                 node.parentNode.insertBefore(cn, node);
42191             }
42192             node.parentNode.removeChild(node);
42193             return;
42194         }
42195         
42196         if (!node.attributes || !node.attributes.length) {
42197             this.cleanUpChildren(node);
42198             return;
42199         }
42200         
42201         function cleanAttr(n,v)
42202         {
42203             
42204             if (v.match(/^\./) || v.match(/^\//)) {
42205                 return;
42206             }
42207             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42208                 return;
42209             }
42210             if (v.match(/^#/)) {
42211                 return;
42212             }
42213 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42214             node.removeAttribute(n);
42215             
42216         }
42217         
42218         var cwhite = this.cwhite;
42219         var cblack = this.cblack;
42220             
42221         function cleanStyle(n,v)
42222         {
42223             if (v.match(/expression/)) { //XSS?? should we even bother..
42224                 node.removeAttribute(n);
42225                 return;
42226             }
42227             
42228             var parts = v.split(/;/);
42229             var clean = [];
42230             
42231             Roo.each(parts, function(p) {
42232                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42233                 if (!p.length) {
42234                     return true;
42235                 }
42236                 var l = p.split(':').shift().replace(/\s+/g,'');
42237                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42238                 
42239                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42240 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42241                     //node.removeAttribute(n);
42242                     return true;
42243                 }
42244                 //Roo.log()
42245                 // only allow 'c whitelisted system attributes'
42246                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42247 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42248                     //node.removeAttribute(n);
42249                     return true;
42250                 }
42251                 
42252                 
42253                  
42254                 
42255                 clean.push(p);
42256                 return true;
42257             });
42258             if (clean.length) { 
42259                 node.setAttribute(n, clean.join(';'));
42260             } else {
42261                 node.removeAttribute(n);
42262             }
42263             
42264         }
42265         
42266         
42267         for (var i = node.attributes.length-1; i > -1 ; i--) {
42268             var a = node.attributes[i];
42269             //console.log(a);
42270             
42271             if (a.name.toLowerCase().substr(0,2)=='on')  {
42272                 node.removeAttribute(a.name);
42273                 continue;
42274             }
42275             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42276                 node.removeAttribute(a.name);
42277                 continue;
42278             }
42279             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42280                 cleanAttr(a.name,a.value); // fixme..
42281                 continue;
42282             }
42283             if (a.name == 'style') {
42284                 cleanStyle(a.name,a.value);
42285                 continue;
42286             }
42287             /// clean up MS crap..
42288             // tecnically this should be a list of valid class'es..
42289             
42290             
42291             if (a.name == 'class') {
42292                 if (a.value.match(/^Mso/)) {
42293                     node.className = '';
42294                 }
42295                 
42296                 if (a.value.match(/body/)) {
42297                     node.className = '';
42298                 }
42299                 continue;
42300             }
42301             
42302             // style cleanup!?
42303             // class cleanup?
42304             
42305         }
42306         
42307         
42308         this.cleanUpChildren(node);
42309         
42310         
42311     },
42312     
42313     /**
42314      * Clean up MS wordisms...
42315      */
42316     cleanWord : function(node)
42317     {
42318         
42319         
42320         if (!node) {
42321             this.cleanWord(this.doc.body);
42322             return;
42323         }
42324         if (node.nodeName == "#text") {
42325             // clean up silly Windows -- stuff?
42326             return; 
42327         }
42328         if (node.nodeName == "#comment") {
42329             node.parentNode.removeChild(node);
42330             // clean up silly Windows -- stuff?
42331             return; 
42332         }
42333         
42334         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42335             node.parentNode.removeChild(node);
42336             return;
42337         }
42338         
42339         // remove - but keep children..
42340         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42341             while (node.childNodes.length) {
42342                 var cn = node.childNodes[0];
42343                 node.removeChild(cn);
42344                 node.parentNode.insertBefore(cn, node);
42345             }
42346             node.parentNode.removeChild(node);
42347             this.iterateChildren(node, this.cleanWord);
42348             return;
42349         }
42350         // clean styles
42351         if (node.className.length) {
42352             
42353             var cn = node.className.split(/\W+/);
42354             var cna = [];
42355             Roo.each(cn, function(cls) {
42356                 if (cls.match(/Mso[a-zA-Z]+/)) {
42357                     return;
42358                 }
42359                 cna.push(cls);
42360             });
42361             node.className = cna.length ? cna.join(' ') : '';
42362             if (!cna.length) {
42363                 node.removeAttribute("class");
42364             }
42365         }
42366         
42367         if (node.hasAttribute("lang")) {
42368             node.removeAttribute("lang");
42369         }
42370         
42371         if (node.hasAttribute("style")) {
42372             
42373             var styles = node.getAttribute("style").split(";");
42374             var nstyle = [];
42375             Roo.each(styles, function(s) {
42376                 if (!s.match(/:/)) {
42377                     return;
42378                 }
42379                 var kv = s.split(":");
42380                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42381                     return;
42382                 }
42383                 // what ever is left... we allow.
42384                 nstyle.push(s);
42385             });
42386             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42387             if (!nstyle.length) {
42388                 node.removeAttribute('style');
42389             }
42390         }
42391         this.iterateChildren(node, this.cleanWord);
42392         
42393         
42394         
42395     },
42396     /**
42397      * iterateChildren of a Node, calling fn each time, using this as the scole..
42398      * @param {DomNode} node node to iterate children of.
42399      * @param {Function} fn method of this class to call on each item.
42400      */
42401     iterateChildren : function(node, fn)
42402     {
42403         if (!node.childNodes.length) {
42404                 return;
42405         }
42406         for (var i = node.childNodes.length-1; i > -1 ; i--) {
42407            fn.call(this, node.childNodes[i])
42408         }
42409     },
42410     
42411     
42412     /**
42413      * cleanTableWidths.
42414      *
42415      * Quite often pasting from word etc.. results in tables with column and widths.
42416      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
42417      *
42418      */
42419     cleanTableWidths : function(node)
42420     {
42421          
42422          
42423         if (!node) {
42424             this.cleanTableWidths(this.doc.body);
42425             return;
42426         }
42427         
42428         // ignore list...
42429         if (node.nodeName == "#text" || node.nodeName == "#comment") {
42430             return; 
42431         }
42432         Roo.log(node.tagName);
42433         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
42434             this.iterateChildren(node, this.cleanTableWidths);
42435             return;
42436         }
42437         if (node.hasAttribute('width')) {
42438             node.removeAttribute('width');
42439         }
42440         
42441          
42442         if (node.hasAttribute("style")) {
42443             // pretty basic...
42444             
42445             var styles = node.getAttribute("style").split(";");
42446             var nstyle = [];
42447             Roo.each(styles, function(s) {
42448                 if (!s.match(/:/)) {
42449                     return;
42450                 }
42451                 var kv = s.split(":");
42452                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
42453                     return;
42454                 }
42455                 // what ever is left... we allow.
42456                 nstyle.push(s);
42457             });
42458             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42459             if (!nstyle.length) {
42460                 node.removeAttribute('style');
42461             }
42462         }
42463         
42464         this.iterateChildren(node, this.cleanTableWidths);
42465         
42466         
42467     },
42468     
42469     
42470     
42471     
42472     domToHTML : function(currentElement, depth, nopadtext) {
42473         
42474         depth = depth || 0;
42475         nopadtext = nopadtext || false;
42476     
42477         if (!currentElement) {
42478             return this.domToHTML(this.doc.body);
42479         }
42480         
42481         //Roo.log(currentElement);
42482         var j;
42483         var allText = false;
42484         var nodeName = currentElement.nodeName;
42485         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42486         
42487         if  (nodeName == '#text') {
42488             
42489             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42490         }
42491         
42492         
42493         var ret = '';
42494         if (nodeName != 'BODY') {
42495              
42496             var i = 0;
42497             // Prints the node tagName, such as <A>, <IMG>, etc
42498             if (tagName) {
42499                 var attr = [];
42500                 for(i = 0; i < currentElement.attributes.length;i++) {
42501                     // quoting?
42502                     var aname = currentElement.attributes.item(i).name;
42503                     if (!currentElement.attributes.item(i).value.length) {
42504                         continue;
42505                     }
42506                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42507                 }
42508                 
42509                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42510             } 
42511             else {
42512                 
42513                 // eack
42514             }
42515         } else {
42516             tagName = false;
42517         }
42518         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42519             return ret;
42520         }
42521         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42522             nopadtext = true;
42523         }
42524         
42525         
42526         // Traverse the tree
42527         i = 0;
42528         var currentElementChild = currentElement.childNodes.item(i);
42529         var allText = true;
42530         var innerHTML  = '';
42531         lastnode = '';
42532         while (currentElementChild) {
42533             // Formatting code (indent the tree so it looks nice on the screen)
42534             var nopad = nopadtext;
42535             if (lastnode == 'SPAN') {
42536                 nopad  = true;
42537             }
42538             // text
42539             if  (currentElementChild.nodeName == '#text') {
42540                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42541                 toadd = nopadtext ? toadd : toadd.trim();
42542                 if (!nopad && toadd.length > 80) {
42543                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42544                 }
42545                 innerHTML  += toadd;
42546                 
42547                 i++;
42548                 currentElementChild = currentElement.childNodes.item(i);
42549                 lastNode = '';
42550                 continue;
42551             }
42552             allText = false;
42553             
42554             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42555                 
42556             // Recursively traverse the tree structure of the child node
42557             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42558             lastnode = currentElementChild.nodeName;
42559             i++;
42560             currentElementChild=currentElement.childNodes.item(i);
42561         }
42562         
42563         ret += innerHTML;
42564         
42565         if (!allText) {
42566                 // The remaining code is mostly for formatting the tree
42567             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42568         }
42569         
42570         
42571         if (tagName) {
42572             ret+= "</"+tagName+">";
42573         }
42574         return ret;
42575         
42576     },
42577         
42578     applyBlacklists : function()
42579     {
42580         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42581         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42582         
42583         this.white = [];
42584         this.black = [];
42585         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42586             if (b.indexOf(tag) > -1) {
42587                 return;
42588             }
42589             this.white.push(tag);
42590             
42591         }, this);
42592         
42593         Roo.each(w, function(tag) {
42594             if (b.indexOf(tag) > -1) {
42595                 return;
42596             }
42597             if (this.white.indexOf(tag) > -1) {
42598                 return;
42599             }
42600             this.white.push(tag);
42601             
42602         }, this);
42603         
42604         
42605         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42606             if (w.indexOf(tag) > -1) {
42607                 return;
42608             }
42609             this.black.push(tag);
42610             
42611         }, this);
42612         
42613         Roo.each(b, function(tag) {
42614             if (w.indexOf(tag) > -1) {
42615                 return;
42616             }
42617             if (this.black.indexOf(tag) > -1) {
42618                 return;
42619             }
42620             this.black.push(tag);
42621             
42622         }, this);
42623         
42624         
42625         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42626         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42627         
42628         this.cwhite = [];
42629         this.cblack = [];
42630         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42631             if (b.indexOf(tag) > -1) {
42632                 return;
42633             }
42634             this.cwhite.push(tag);
42635             
42636         }, this);
42637         
42638         Roo.each(w, function(tag) {
42639             if (b.indexOf(tag) > -1) {
42640                 return;
42641             }
42642             if (this.cwhite.indexOf(tag) > -1) {
42643                 return;
42644             }
42645             this.cwhite.push(tag);
42646             
42647         }, this);
42648         
42649         
42650         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42651             if (w.indexOf(tag) > -1) {
42652                 return;
42653             }
42654             this.cblack.push(tag);
42655             
42656         }, this);
42657         
42658         Roo.each(b, function(tag) {
42659             if (w.indexOf(tag) > -1) {
42660                 return;
42661             }
42662             if (this.cblack.indexOf(tag) > -1) {
42663                 return;
42664             }
42665             this.cblack.push(tag);
42666             
42667         }, this);
42668     },
42669     
42670     setStylesheets : function(stylesheets)
42671     {
42672         if(typeof(stylesheets) == 'string'){
42673             Roo.get(this.iframe.contentDocument.head).createChild({
42674                 tag : 'link',
42675                 rel : 'stylesheet',
42676                 type : 'text/css',
42677                 href : stylesheets
42678             });
42679             
42680             return;
42681         }
42682         var _this = this;
42683      
42684         Roo.each(stylesheets, function(s) {
42685             if(!s.length){
42686                 return;
42687             }
42688             
42689             Roo.get(_this.iframe.contentDocument.head).createChild({
42690                 tag : 'link',
42691                 rel : 'stylesheet',
42692                 type : 'text/css',
42693                 href : s
42694             });
42695         });
42696
42697         
42698     },
42699     
42700     removeStylesheets : function()
42701     {
42702         var _this = this;
42703         
42704         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42705             s.remove();
42706         });
42707     }
42708     
42709     // hide stuff that is not compatible
42710     /**
42711      * @event blur
42712      * @hide
42713      */
42714     /**
42715      * @event change
42716      * @hide
42717      */
42718     /**
42719      * @event focus
42720      * @hide
42721      */
42722     /**
42723      * @event specialkey
42724      * @hide
42725      */
42726     /**
42727      * @cfg {String} fieldClass @hide
42728      */
42729     /**
42730      * @cfg {String} focusClass @hide
42731      */
42732     /**
42733      * @cfg {String} autoCreate @hide
42734      */
42735     /**
42736      * @cfg {String} inputType @hide
42737      */
42738     /**
42739      * @cfg {String} invalidClass @hide
42740      */
42741     /**
42742      * @cfg {String} invalidText @hide
42743      */
42744     /**
42745      * @cfg {String} msgFx @hide
42746      */
42747     /**
42748      * @cfg {String} validateOnBlur @hide
42749      */
42750 });
42751
42752 Roo.HtmlEditorCore.white = [
42753         'area', 'br', 'img', 'input', 'hr', 'wbr',
42754         
42755        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42756        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42757        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42758        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42759        'table',   'ul',         'xmp', 
42760        
42761        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42762       'thead',   'tr', 
42763      
42764       'dir', 'menu', 'ol', 'ul', 'dl',
42765        
42766       'embed',  'object'
42767 ];
42768
42769
42770 Roo.HtmlEditorCore.black = [
42771     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42772         'applet', // 
42773         'base',   'basefont', 'bgsound', 'blink',  'body', 
42774         'frame',  'frameset', 'head',    'html',   'ilayer', 
42775         'iframe', 'layer',  'link',     'meta',    'object',   
42776         'script', 'style' ,'title',  'xml' // clean later..
42777 ];
42778 Roo.HtmlEditorCore.clean = [
42779     'script', 'style', 'title', 'xml'
42780 ];
42781 Roo.HtmlEditorCore.remove = [
42782     'font'
42783 ];
42784 // attributes..
42785
42786 Roo.HtmlEditorCore.ablack = [
42787     'on'
42788 ];
42789     
42790 Roo.HtmlEditorCore.aclean = [ 
42791     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42792 ];
42793
42794 // protocols..
42795 Roo.HtmlEditorCore.pwhite= [
42796         'http',  'https',  'mailto'
42797 ];
42798
42799 // white listed style attributes.
42800 Roo.HtmlEditorCore.cwhite= [
42801       //  'text-align', /// default is to allow most things..
42802       
42803          
42804 //        'font-size'//??
42805 ];
42806
42807 // black listed style attributes.
42808 Roo.HtmlEditorCore.cblack= [
42809       //  'font-size' -- this can be set by the project 
42810 ];
42811
42812
42813 Roo.HtmlEditorCore.swapCodes   =[ 
42814     [    8211, "--" ], 
42815     [    8212, "--" ], 
42816     [    8216,  "'" ],  
42817     [    8217, "'" ],  
42818     [    8220, '"' ],  
42819     [    8221, '"' ],  
42820     [    8226, "*" ],  
42821     [    8230, "..." ]
42822 ]; 
42823
42824     //<script type="text/javascript">
42825
42826 /*
42827  * Ext JS Library 1.1.1
42828  * Copyright(c) 2006-2007, Ext JS, LLC.
42829  * Licence LGPL
42830  * 
42831  */
42832  
42833  
42834 Roo.form.HtmlEditor = function(config){
42835     
42836     
42837     
42838     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42839     
42840     if (!this.toolbars) {
42841         this.toolbars = [];
42842     }
42843     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42844     
42845     
42846 };
42847
42848 /**
42849  * @class Roo.form.HtmlEditor
42850  * @extends Roo.form.Field
42851  * Provides a lightweight HTML Editor component.
42852  *
42853  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42854  * 
42855  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42856  * supported by this editor.</b><br/><br/>
42857  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42858  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42859  */
42860 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42861     /**
42862      * @cfg {Boolean} clearUp
42863      */
42864     clearUp : true,
42865       /**
42866      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42867      */
42868     toolbars : false,
42869    
42870      /**
42871      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42872      *                        Roo.resizable.
42873      */
42874     resizable : false,
42875      /**
42876      * @cfg {Number} height (in pixels)
42877      */   
42878     height: 300,
42879    /**
42880      * @cfg {Number} width (in pixels)
42881      */   
42882     width: 500,
42883     
42884     /**
42885      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42886      * 
42887      */
42888     stylesheets: false,
42889     
42890     
42891      /**
42892      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42893      * 
42894      */
42895     cblack: false,
42896     /**
42897      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42898      * 
42899      */
42900     cwhite: false,
42901     
42902      /**
42903      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42904      * 
42905      */
42906     black: false,
42907     /**
42908      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42909      * 
42910      */
42911     white: false,
42912     
42913     // id of frame..
42914     frameId: false,
42915     
42916     // private properties
42917     validationEvent : false,
42918     deferHeight: true,
42919     initialized : false,
42920     activated : false,
42921     
42922     onFocus : Roo.emptyFn,
42923     iframePad:3,
42924     hideMode:'offsets',
42925     
42926     actionMode : 'container', // defaults to hiding it...
42927     
42928     defaultAutoCreate : { // modified by initCompnoent..
42929         tag: "textarea",
42930         style:"width:500px;height:300px;",
42931         autocomplete: "new-password"
42932     },
42933
42934     // private
42935     initComponent : function(){
42936         this.addEvents({
42937             /**
42938              * @event initialize
42939              * Fires when the editor is fully initialized (including the iframe)
42940              * @param {HtmlEditor} this
42941              */
42942             initialize: true,
42943             /**
42944              * @event activate
42945              * Fires when the editor is first receives the focus. Any insertion must wait
42946              * until after this event.
42947              * @param {HtmlEditor} this
42948              */
42949             activate: true,
42950              /**
42951              * @event beforesync
42952              * Fires before the textarea is updated with content from the editor iframe. Return false
42953              * to cancel the sync.
42954              * @param {HtmlEditor} this
42955              * @param {String} html
42956              */
42957             beforesync: true,
42958              /**
42959              * @event beforepush
42960              * Fires before the iframe editor is updated with content from the textarea. Return false
42961              * to cancel the push.
42962              * @param {HtmlEditor} this
42963              * @param {String} html
42964              */
42965             beforepush: true,
42966              /**
42967              * @event sync
42968              * Fires when the textarea is updated with content from the editor iframe.
42969              * @param {HtmlEditor} this
42970              * @param {String} html
42971              */
42972             sync: true,
42973              /**
42974              * @event push
42975              * Fires when the iframe editor is updated with content from the textarea.
42976              * @param {HtmlEditor} this
42977              * @param {String} html
42978              */
42979             push: true,
42980              /**
42981              * @event editmodechange
42982              * Fires when the editor switches edit modes
42983              * @param {HtmlEditor} this
42984              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42985              */
42986             editmodechange: true,
42987             /**
42988              * @event editorevent
42989              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42990              * @param {HtmlEditor} this
42991              */
42992             editorevent: true,
42993             /**
42994              * @event firstfocus
42995              * Fires when on first focus - needed by toolbars..
42996              * @param {HtmlEditor} this
42997              */
42998             firstfocus: true,
42999             /**
43000              * @event autosave
43001              * Auto save the htmlEditor value as a file into Events
43002              * @param {HtmlEditor} this
43003              */
43004             autosave: true,
43005             /**
43006              * @event savedpreview
43007              * preview the saved version of htmlEditor
43008              * @param {HtmlEditor} this
43009              */
43010             savedpreview: true,
43011             
43012             /**
43013             * @event stylesheetsclick
43014             * Fires when press the Sytlesheets button
43015             * @param {Roo.HtmlEditorCore} this
43016             */
43017             stylesheetsclick: true
43018         });
43019         this.defaultAutoCreate =  {
43020             tag: "textarea",
43021             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
43022             autocomplete: "new-password"
43023         };
43024     },
43025
43026     /**
43027      * Protected method that will not generally be called directly. It
43028      * is called when the editor creates its toolbar. Override this method if you need to
43029      * add custom toolbar buttons.
43030      * @param {HtmlEditor} editor
43031      */
43032     createToolbar : function(editor){
43033         Roo.log("create toolbars");
43034         if (!editor.toolbars || !editor.toolbars.length) {
43035             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
43036         }
43037         
43038         for (var i =0 ; i < editor.toolbars.length;i++) {
43039             editor.toolbars[i] = Roo.factory(
43040                     typeof(editor.toolbars[i]) == 'string' ?
43041                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
43042                 Roo.form.HtmlEditor);
43043             editor.toolbars[i].init(editor);
43044         }
43045          
43046         
43047     },
43048
43049      
43050     // private
43051     onRender : function(ct, position)
43052     {
43053         var _t = this;
43054         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
43055         
43056         this.wrap = this.el.wrap({
43057             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
43058         });
43059         
43060         this.editorcore.onRender(ct, position);
43061          
43062         if (this.resizable) {
43063             this.resizeEl = new Roo.Resizable(this.wrap, {
43064                 pinned : true,
43065                 wrap: true,
43066                 dynamic : true,
43067                 minHeight : this.height,
43068                 height: this.height,
43069                 handles : this.resizable,
43070                 width: this.width,
43071                 listeners : {
43072                     resize : function(r, w, h) {
43073                         _t.onResize(w,h); // -something
43074                     }
43075                 }
43076             });
43077             
43078         }
43079         this.createToolbar(this);
43080        
43081         
43082         if(!this.width){
43083             this.setSize(this.wrap.getSize());
43084         }
43085         if (this.resizeEl) {
43086             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43087             // should trigger onReize..
43088         }
43089         
43090         this.keyNav = new Roo.KeyNav(this.el, {
43091             
43092             "tab" : function(e){
43093                 e.preventDefault();
43094                 
43095                 var value = this.getValue();
43096                 
43097                 var start = this.el.dom.selectionStart;
43098                 var end = this.el.dom.selectionEnd;
43099                 
43100                 if(!e.shiftKey){
43101                     
43102                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43103                     this.el.dom.setSelectionRange(end + 1, end + 1);
43104                     return;
43105                 }
43106                 
43107                 var f = value.substring(0, start).split("\t");
43108                 
43109                 if(f.pop().length != 0){
43110                     return;
43111                 }
43112                 
43113                 this.setValue(f.join("\t") + value.substring(end));
43114                 this.el.dom.setSelectionRange(start - 1, start - 1);
43115                 
43116             },
43117             
43118             "home" : function(e){
43119                 e.preventDefault();
43120                 
43121                 var curr = this.el.dom.selectionStart;
43122                 var lines = this.getValue().split("\n");
43123                 
43124                 if(!lines.length){
43125                     return;
43126                 }
43127                 
43128                 if(e.ctrlKey){
43129                     this.el.dom.setSelectionRange(0, 0);
43130                     return;
43131                 }
43132                 
43133                 var pos = 0;
43134                 
43135                 for (var i = 0; i < lines.length;i++) {
43136                     pos += lines[i].length;
43137                     
43138                     if(i != 0){
43139                         pos += 1;
43140                     }
43141                     
43142                     if(pos < curr){
43143                         continue;
43144                     }
43145                     
43146                     pos -= lines[i].length;
43147                     
43148                     break;
43149                 }
43150                 
43151                 if(!e.shiftKey){
43152                     this.el.dom.setSelectionRange(pos, pos);
43153                     return;
43154                 }
43155                 
43156                 this.el.dom.selectionStart = pos;
43157                 this.el.dom.selectionEnd = curr;
43158             },
43159             
43160             "end" : function(e){
43161                 e.preventDefault();
43162                 
43163                 var curr = this.el.dom.selectionStart;
43164                 var lines = this.getValue().split("\n");
43165                 
43166                 if(!lines.length){
43167                     return;
43168                 }
43169                 
43170                 if(e.ctrlKey){
43171                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43172                     return;
43173                 }
43174                 
43175                 var pos = 0;
43176                 
43177                 for (var i = 0; i < lines.length;i++) {
43178                     
43179                     pos += lines[i].length;
43180                     
43181                     if(i != 0){
43182                         pos += 1;
43183                     }
43184                     
43185                     if(pos < curr){
43186                         continue;
43187                     }
43188                     
43189                     break;
43190                 }
43191                 
43192                 if(!e.shiftKey){
43193                     this.el.dom.setSelectionRange(pos, pos);
43194                     return;
43195                 }
43196                 
43197                 this.el.dom.selectionStart = curr;
43198                 this.el.dom.selectionEnd = pos;
43199             },
43200
43201             scope : this,
43202
43203             doRelay : function(foo, bar, hname){
43204                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43205             },
43206
43207             forceKeyDown: true
43208         });
43209         
43210 //        if(this.autosave && this.w){
43211 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43212 //        }
43213     },
43214
43215     // private
43216     onResize : function(w, h)
43217     {
43218         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43219         var ew = false;
43220         var eh = false;
43221         
43222         if(this.el ){
43223             if(typeof w == 'number'){
43224                 var aw = w - this.wrap.getFrameWidth('lr');
43225                 this.el.setWidth(this.adjustWidth('textarea', aw));
43226                 ew = aw;
43227             }
43228             if(typeof h == 'number'){
43229                 var tbh = 0;
43230                 for (var i =0; i < this.toolbars.length;i++) {
43231                     // fixme - ask toolbars for heights?
43232                     tbh += this.toolbars[i].tb.el.getHeight();
43233                     if (this.toolbars[i].footer) {
43234                         tbh += this.toolbars[i].footer.el.getHeight();
43235                     }
43236                 }
43237                 
43238                 
43239                 
43240                 
43241                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43242                 ah -= 5; // knock a few pixes off for look..
43243 //                Roo.log(ah);
43244                 this.el.setHeight(this.adjustWidth('textarea', ah));
43245                 var eh = ah;
43246             }
43247         }
43248         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43249         this.editorcore.onResize(ew,eh);
43250         
43251     },
43252
43253     /**
43254      * Toggles the editor between standard and source edit mode.
43255      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43256      */
43257     toggleSourceEdit : function(sourceEditMode)
43258     {
43259         this.editorcore.toggleSourceEdit(sourceEditMode);
43260         
43261         if(this.editorcore.sourceEditMode){
43262             Roo.log('editor - showing textarea');
43263             
43264 //            Roo.log('in');
43265 //            Roo.log(this.syncValue());
43266             this.editorcore.syncValue();
43267             this.el.removeClass('x-hidden');
43268             this.el.dom.removeAttribute('tabIndex');
43269             this.el.focus();
43270             
43271             for (var i = 0; i < this.toolbars.length; i++) {
43272                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43273                     this.toolbars[i].tb.hide();
43274                     this.toolbars[i].footer.hide();
43275                 }
43276             }
43277             
43278         }else{
43279             Roo.log('editor - hiding textarea');
43280 //            Roo.log('out')
43281 //            Roo.log(this.pushValue()); 
43282             this.editorcore.pushValue();
43283             
43284             this.el.addClass('x-hidden');
43285             this.el.dom.setAttribute('tabIndex', -1);
43286             
43287             for (var i = 0; i < this.toolbars.length; i++) {
43288                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43289                     this.toolbars[i].tb.show();
43290                     this.toolbars[i].footer.show();
43291                 }
43292             }
43293             
43294             //this.deferFocus();
43295         }
43296         
43297         this.setSize(this.wrap.getSize());
43298         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43299         
43300         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43301     },
43302  
43303     // private (for BoxComponent)
43304     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43305
43306     // private (for BoxComponent)
43307     getResizeEl : function(){
43308         return this.wrap;
43309     },
43310
43311     // private (for BoxComponent)
43312     getPositionEl : function(){
43313         return this.wrap;
43314     },
43315
43316     // private
43317     initEvents : function(){
43318         this.originalValue = this.getValue();
43319     },
43320
43321     /**
43322      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43323      * @method
43324      */
43325     markInvalid : Roo.emptyFn,
43326     /**
43327      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43328      * @method
43329      */
43330     clearInvalid : Roo.emptyFn,
43331
43332     setValue : function(v){
43333         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43334         this.editorcore.pushValue();
43335     },
43336
43337      
43338     // private
43339     deferFocus : function(){
43340         this.focus.defer(10, this);
43341     },
43342
43343     // doc'ed in Field
43344     focus : function(){
43345         this.editorcore.focus();
43346         
43347     },
43348       
43349
43350     // private
43351     onDestroy : function(){
43352         
43353         
43354         
43355         if(this.rendered){
43356             
43357             for (var i =0; i < this.toolbars.length;i++) {
43358                 // fixme - ask toolbars for heights?
43359                 this.toolbars[i].onDestroy();
43360             }
43361             
43362             this.wrap.dom.innerHTML = '';
43363             this.wrap.remove();
43364         }
43365     },
43366
43367     // private
43368     onFirstFocus : function(){
43369         //Roo.log("onFirstFocus");
43370         this.editorcore.onFirstFocus();
43371          for (var i =0; i < this.toolbars.length;i++) {
43372             this.toolbars[i].onFirstFocus();
43373         }
43374         
43375     },
43376     
43377     // private
43378     syncValue : function()
43379     {
43380         this.editorcore.syncValue();
43381     },
43382     
43383     pushValue : function()
43384     {
43385         this.editorcore.pushValue();
43386     },
43387     
43388     setStylesheets : function(stylesheets)
43389     {
43390         this.editorcore.setStylesheets(stylesheets);
43391     },
43392     
43393     removeStylesheets : function()
43394     {
43395         this.editorcore.removeStylesheets();
43396     }
43397      
43398     
43399     // hide stuff that is not compatible
43400     /**
43401      * @event blur
43402      * @hide
43403      */
43404     /**
43405      * @event change
43406      * @hide
43407      */
43408     /**
43409      * @event focus
43410      * @hide
43411      */
43412     /**
43413      * @event specialkey
43414      * @hide
43415      */
43416     /**
43417      * @cfg {String} fieldClass @hide
43418      */
43419     /**
43420      * @cfg {String} focusClass @hide
43421      */
43422     /**
43423      * @cfg {String} autoCreate @hide
43424      */
43425     /**
43426      * @cfg {String} inputType @hide
43427      */
43428     /**
43429      * @cfg {String} invalidClass @hide
43430      */
43431     /**
43432      * @cfg {String} invalidText @hide
43433      */
43434     /**
43435      * @cfg {String} msgFx @hide
43436      */
43437     /**
43438      * @cfg {String} validateOnBlur @hide
43439      */
43440 });
43441  
43442     // <script type="text/javascript">
43443 /*
43444  * Based on
43445  * Ext JS Library 1.1.1
43446  * Copyright(c) 2006-2007, Ext JS, LLC.
43447  *  
43448  
43449  */
43450
43451 /**
43452  * @class Roo.form.HtmlEditorToolbar1
43453  * Basic Toolbar
43454  * 
43455  * Usage:
43456  *
43457  new Roo.form.HtmlEditor({
43458     ....
43459     toolbars : [
43460         new Roo.form.HtmlEditorToolbar1({
43461             disable : { fonts: 1 , format: 1, ..., ... , ...],
43462             btns : [ .... ]
43463         })
43464     }
43465      
43466  * 
43467  * @cfg {Object} disable List of elements to disable..
43468  * @cfg {Array} btns List of additional buttons.
43469  * 
43470  * 
43471  * NEEDS Extra CSS? 
43472  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43473  */
43474  
43475 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43476 {
43477     
43478     Roo.apply(this, config);
43479     
43480     // default disabled, based on 'good practice'..
43481     this.disable = this.disable || {};
43482     Roo.applyIf(this.disable, {
43483         fontSize : true,
43484         colors : true,
43485         specialElements : true
43486     });
43487     
43488     
43489     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43490     // dont call parent... till later.
43491 }
43492
43493 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43494     
43495     tb: false,
43496     
43497     rendered: false,
43498     
43499     editor : false,
43500     editorcore : false,
43501     /**
43502      * @cfg {Object} disable  List of toolbar elements to disable
43503          
43504      */
43505     disable : false,
43506     
43507     
43508      /**
43509      * @cfg {String} createLinkText The default text for the create link prompt
43510      */
43511     createLinkText : 'Please enter the URL for the link:',
43512     /**
43513      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43514      */
43515     defaultLinkValue : 'http:/'+'/',
43516    
43517     
43518       /**
43519      * @cfg {Array} fontFamilies An array of available font families
43520      */
43521     fontFamilies : [
43522         'Arial',
43523         'Courier New',
43524         'Tahoma',
43525         'Times New Roman',
43526         'Verdana'
43527     ],
43528     
43529     specialChars : [
43530            "&#169;",
43531           "&#174;",     
43532           "&#8482;",    
43533           "&#163;" ,    
43534          // "&#8212;",    
43535           "&#8230;",    
43536           "&#247;" ,    
43537         //  "&#225;" ,     ?? a acute?
43538            "&#8364;"    , //Euro
43539        //   "&#8220;"    ,
43540         //  "&#8221;"    ,
43541         //  "&#8226;"    ,
43542           "&#176;"  //   , // degrees
43543
43544          // "&#233;"     , // e ecute
43545          // "&#250;"     , // u ecute?
43546     ],
43547     
43548     specialElements : [
43549         {
43550             text: "Insert Table",
43551             xtype: 'MenuItem',
43552             xns : Roo.Menu,
43553             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43554                 
43555         },
43556         {    
43557             text: "Insert Image",
43558             xtype: 'MenuItem',
43559             xns : Roo.Menu,
43560             ihtml : '<img src="about:blank"/>'
43561             
43562         }
43563         
43564          
43565     ],
43566     
43567     
43568     inputElements : [ 
43569             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43570             "input:submit", "input:button", "select", "textarea", "label" ],
43571     formats : [
43572         ["p"] ,  
43573         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43574         ["pre"],[ "code"], 
43575         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43576         ['div'],['span']
43577     ],
43578     
43579     cleanStyles : [
43580         "font-size"
43581     ],
43582      /**
43583      * @cfg {String} defaultFont default font to use.
43584      */
43585     defaultFont: 'tahoma',
43586    
43587     fontSelect : false,
43588     
43589     
43590     formatCombo : false,
43591     
43592     init : function(editor)
43593     {
43594         this.editor = editor;
43595         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43596         var editorcore = this.editorcore;
43597         
43598         var _t = this;
43599         
43600         var fid = editorcore.frameId;
43601         var etb = this;
43602         function btn(id, toggle, handler){
43603             var xid = fid + '-'+ id ;
43604             return {
43605                 id : xid,
43606                 cmd : id,
43607                 cls : 'x-btn-icon x-edit-'+id,
43608                 enableToggle:toggle !== false,
43609                 scope: _t, // was editor...
43610                 handler:handler||_t.relayBtnCmd,
43611                 clickEvent:'mousedown',
43612                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43613                 tabIndex:-1
43614             };
43615         }
43616         
43617         
43618         
43619         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43620         this.tb = tb;
43621          // stop form submits
43622         tb.el.on('click', function(e){
43623             e.preventDefault(); // what does this do?
43624         });
43625
43626         if(!this.disable.font) { // && !Roo.isSafari){
43627             /* why no safari for fonts 
43628             editor.fontSelect = tb.el.createChild({
43629                 tag:'select',
43630                 tabIndex: -1,
43631                 cls:'x-font-select',
43632                 html: this.createFontOptions()
43633             });
43634             
43635             editor.fontSelect.on('change', function(){
43636                 var font = editor.fontSelect.dom.value;
43637                 editor.relayCmd('fontname', font);
43638                 editor.deferFocus();
43639             }, editor);
43640             
43641             tb.add(
43642                 editor.fontSelect.dom,
43643                 '-'
43644             );
43645             */
43646             
43647         };
43648         if(!this.disable.formats){
43649             this.formatCombo = new Roo.form.ComboBox({
43650                 store: new Roo.data.SimpleStore({
43651                     id : 'tag',
43652                     fields: ['tag'],
43653                     data : this.formats // from states.js
43654                 }),
43655                 blockFocus : true,
43656                 name : '',
43657                 //autoCreate : {tag: "div",  size: "20"},
43658                 displayField:'tag',
43659                 typeAhead: false,
43660                 mode: 'local',
43661                 editable : false,
43662                 triggerAction: 'all',
43663                 emptyText:'Add tag',
43664                 selectOnFocus:true,
43665                 width:135,
43666                 listeners : {
43667                     'select': function(c, r, i) {
43668                         editorcore.insertTag(r.get('tag'));
43669                         editor.focus();
43670                     }
43671                 }
43672
43673             });
43674             tb.addField(this.formatCombo);
43675             
43676         }
43677         
43678         if(!this.disable.format){
43679             tb.add(
43680                 btn('bold'),
43681                 btn('italic'),
43682                 btn('underline'),
43683                 btn('strikethrough')
43684             );
43685         };
43686         if(!this.disable.fontSize){
43687             tb.add(
43688                 '-',
43689                 
43690                 
43691                 btn('increasefontsize', false, editorcore.adjustFont),
43692                 btn('decreasefontsize', false, editorcore.adjustFont)
43693             );
43694         };
43695         
43696         
43697         if(!this.disable.colors){
43698             tb.add(
43699                 '-', {
43700                     id:editorcore.frameId +'-forecolor',
43701                     cls:'x-btn-icon x-edit-forecolor',
43702                     clickEvent:'mousedown',
43703                     tooltip: this.buttonTips['forecolor'] || undefined,
43704                     tabIndex:-1,
43705                     menu : new Roo.menu.ColorMenu({
43706                         allowReselect: true,
43707                         focus: Roo.emptyFn,
43708                         value:'000000',
43709                         plain:true,
43710                         selectHandler: function(cp, color){
43711                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43712                             editor.deferFocus();
43713                         },
43714                         scope: editorcore,
43715                         clickEvent:'mousedown'
43716                     })
43717                 }, {
43718                     id:editorcore.frameId +'backcolor',
43719                     cls:'x-btn-icon x-edit-backcolor',
43720                     clickEvent:'mousedown',
43721                     tooltip: this.buttonTips['backcolor'] || undefined,
43722                     tabIndex:-1,
43723                     menu : new Roo.menu.ColorMenu({
43724                         focus: Roo.emptyFn,
43725                         value:'FFFFFF',
43726                         plain:true,
43727                         allowReselect: true,
43728                         selectHandler: function(cp, color){
43729                             if(Roo.isGecko){
43730                                 editorcore.execCmd('useCSS', false);
43731                                 editorcore.execCmd('hilitecolor', color);
43732                                 editorcore.execCmd('useCSS', true);
43733                                 editor.deferFocus();
43734                             }else{
43735                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43736                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43737                                 editor.deferFocus();
43738                             }
43739                         },
43740                         scope:editorcore,
43741                         clickEvent:'mousedown'
43742                     })
43743                 }
43744             );
43745         };
43746         // now add all the items...
43747         
43748
43749         if(!this.disable.alignments){
43750             tb.add(
43751                 '-',
43752                 btn('justifyleft'),
43753                 btn('justifycenter'),
43754                 btn('justifyright')
43755             );
43756         };
43757
43758         //if(!Roo.isSafari){
43759             if(!this.disable.links){
43760                 tb.add(
43761                     '-',
43762                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43763                 );
43764             };
43765
43766             if(!this.disable.lists){
43767                 tb.add(
43768                     '-',
43769                     btn('insertorderedlist'),
43770                     btn('insertunorderedlist')
43771                 );
43772             }
43773             if(!this.disable.sourceEdit){
43774                 tb.add(
43775                     '-',
43776                     btn('sourceedit', true, function(btn){
43777                         this.toggleSourceEdit(btn.pressed);
43778                     })
43779                 );
43780             }
43781         //}
43782         
43783         var smenu = { };
43784         // special menu.. - needs to be tidied up..
43785         if (!this.disable.special) {
43786             smenu = {
43787                 text: "&#169;",
43788                 cls: 'x-edit-none',
43789                 
43790                 menu : {
43791                     items : []
43792                 }
43793             };
43794             for (var i =0; i < this.specialChars.length; i++) {
43795                 smenu.menu.items.push({
43796                     
43797                     html: this.specialChars[i],
43798                     handler: function(a,b) {
43799                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43800                         //editor.insertAtCursor(a.html);
43801                         
43802                     },
43803                     tabIndex:-1
43804                 });
43805             }
43806             
43807             
43808             tb.add(smenu);
43809             
43810             
43811         }
43812         
43813         var cmenu = { };
43814         if (!this.disable.cleanStyles) {
43815             cmenu = {
43816                 cls: 'x-btn-icon x-btn-clear',
43817                 
43818                 menu : {
43819                     items : []
43820                 }
43821             };
43822             for (var i =0; i < this.cleanStyles.length; i++) {
43823                 cmenu.menu.items.push({
43824                     actiontype : this.cleanStyles[i],
43825                     html: 'Remove ' + this.cleanStyles[i],
43826                     handler: function(a,b) {
43827 //                        Roo.log(a);
43828 //                        Roo.log(b);
43829                         var c = Roo.get(editorcore.doc.body);
43830                         c.select('[style]').each(function(s) {
43831                             s.dom.style.removeProperty(a.actiontype);
43832                         });
43833                         editorcore.syncValue();
43834                     },
43835                     tabIndex:-1
43836                 });
43837             }
43838              cmenu.menu.items.push({
43839                 actiontype : 'tablewidths',
43840                 html: 'Remove Table Widths',
43841                 handler: function(a,b) {
43842                     editorcore.cleanTableWidths();
43843                     editorcore.syncValue();
43844                 },
43845                 tabIndex:-1
43846             });
43847             cmenu.menu.items.push({
43848                 actiontype : 'word',
43849                 html: 'Remove MS Word Formating',
43850                 handler: function(a,b) {
43851                     editorcore.cleanWord();
43852                     editorcore.syncValue();
43853                 },
43854                 tabIndex:-1
43855             });
43856             
43857             cmenu.menu.items.push({
43858                 actiontype : 'all',
43859                 html: 'Remove All Styles',
43860                 handler: function(a,b) {
43861                     
43862                     var c = Roo.get(editorcore.doc.body);
43863                     c.select('[style]').each(function(s) {
43864                         s.dom.removeAttribute('style');
43865                     });
43866                     editorcore.syncValue();
43867                 },
43868                 tabIndex:-1
43869             });
43870             
43871             cmenu.menu.items.push({
43872                 actiontype : 'all',
43873                 html: 'Remove All CSS Classes',
43874                 handler: function(a,b) {
43875                     
43876                     var c = Roo.get(editorcore.doc.body);
43877                     c.select('[class]').each(function(s) {
43878                         s.dom.className = '';
43879                     });
43880                     editorcore.syncValue();
43881                 },
43882                 tabIndex:-1
43883             });
43884             
43885              cmenu.menu.items.push({
43886                 actiontype : 'tidy',
43887                 html: 'Tidy HTML Source',
43888                 handler: function(a,b) {
43889                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43890                     editorcore.syncValue();
43891                 },
43892                 tabIndex:-1
43893             });
43894             
43895             
43896             tb.add(cmenu);
43897         }
43898          
43899         if (!this.disable.specialElements) {
43900             var semenu = {
43901                 text: "Other;",
43902                 cls: 'x-edit-none',
43903                 menu : {
43904                     items : []
43905                 }
43906             };
43907             for (var i =0; i < this.specialElements.length; i++) {
43908                 semenu.menu.items.push(
43909                     Roo.apply({ 
43910                         handler: function(a,b) {
43911                             editor.insertAtCursor(this.ihtml);
43912                         }
43913                     }, this.specialElements[i])
43914                 );
43915                     
43916             }
43917             
43918             tb.add(semenu);
43919             
43920             
43921         }
43922          
43923         
43924         if (this.btns) {
43925             for(var i =0; i< this.btns.length;i++) {
43926                 var b = Roo.factory(this.btns[i],Roo.form);
43927                 b.cls =  'x-edit-none';
43928                 
43929                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43930                     b.cls += ' x-init-enable';
43931                 }
43932                 
43933                 b.scope = editorcore;
43934                 tb.add(b);
43935             }
43936         
43937         }
43938         
43939         
43940         
43941         // disable everything...
43942         
43943         this.tb.items.each(function(item){
43944             
43945            if(
43946                 item.id != editorcore.frameId+ '-sourceedit' && 
43947                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43948             ){
43949                 
43950                 item.disable();
43951             }
43952         });
43953         this.rendered = true;
43954         
43955         // the all the btns;
43956         editor.on('editorevent', this.updateToolbar, this);
43957         // other toolbars need to implement this..
43958         //editor.on('editmodechange', this.updateToolbar, this);
43959     },
43960     
43961     
43962     relayBtnCmd : function(btn) {
43963         this.editorcore.relayCmd(btn.cmd);
43964     },
43965     // private used internally
43966     createLink : function(){
43967         Roo.log("create link?");
43968         var url = prompt(this.createLinkText, this.defaultLinkValue);
43969         if(url && url != 'http:/'+'/'){
43970             this.editorcore.relayCmd('createlink', url);
43971         }
43972     },
43973
43974     
43975     /**
43976      * Protected method that will not generally be called directly. It triggers
43977      * a toolbar update by reading the markup state of the current selection in the editor.
43978      */
43979     updateToolbar: function(){
43980
43981         if(!this.editorcore.activated){
43982             this.editor.onFirstFocus();
43983             return;
43984         }
43985
43986         var btns = this.tb.items.map, 
43987             doc = this.editorcore.doc,
43988             frameId = this.editorcore.frameId;
43989
43990         if(!this.disable.font && !Roo.isSafari){
43991             /*
43992             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43993             if(name != this.fontSelect.dom.value){
43994                 this.fontSelect.dom.value = name;
43995             }
43996             */
43997         }
43998         if(!this.disable.format){
43999             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
44000             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
44001             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
44002             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
44003         }
44004         if(!this.disable.alignments){
44005             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
44006             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
44007             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
44008         }
44009         if(!Roo.isSafari && !this.disable.lists){
44010             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
44011             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
44012         }
44013         
44014         var ans = this.editorcore.getAllAncestors();
44015         if (this.formatCombo) {
44016             
44017             
44018             var store = this.formatCombo.store;
44019             this.formatCombo.setValue("");
44020             for (var i =0; i < ans.length;i++) {
44021                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
44022                     // select it..
44023                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
44024                     break;
44025                 }
44026             }
44027         }
44028         
44029         
44030         
44031         // hides menus... - so this cant be on a menu...
44032         Roo.menu.MenuMgr.hideAll();
44033
44034         //this.editorsyncValue();
44035     },
44036    
44037     
44038     createFontOptions : function(){
44039         var buf = [], fs = this.fontFamilies, ff, lc;
44040         
44041         
44042         
44043         for(var i = 0, len = fs.length; i< len; i++){
44044             ff = fs[i];
44045             lc = ff.toLowerCase();
44046             buf.push(
44047                 '<option value="',lc,'" style="font-family:',ff,';"',
44048                     (this.defaultFont == lc ? ' selected="true">' : '>'),
44049                     ff,
44050                 '</option>'
44051             );
44052         }
44053         return buf.join('');
44054     },
44055     
44056     toggleSourceEdit : function(sourceEditMode){
44057         
44058         Roo.log("toolbar toogle");
44059         if(sourceEditMode === undefined){
44060             sourceEditMode = !this.sourceEditMode;
44061         }
44062         this.sourceEditMode = sourceEditMode === true;
44063         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
44064         // just toggle the button?
44065         if(btn.pressed !== this.sourceEditMode){
44066             btn.toggle(this.sourceEditMode);
44067             return;
44068         }
44069         
44070         if(sourceEditMode){
44071             Roo.log("disabling buttons");
44072             this.tb.items.each(function(item){
44073                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
44074                     item.disable();
44075                 }
44076             });
44077           
44078         }else{
44079             Roo.log("enabling buttons");
44080             if(this.editorcore.initialized){
44081                 this.tb.items.each(function(item){
44082                     item.enable();
44083                 });
44084             }
44085             
44086         }
44087         Roo.log("calling toggole on editor");
44088         // tell the editor that it's been pressed..
44089         this.editor.toggleSourceEdit(sourceEditMode);
44090        
44091     },
44092      /**
44093      * Object collection of toolbar tooltips for the buttons in the editor. The key
44094      * is the command id associated with that button and the value is a valid QuickTips object.
44095      * For example:
44096 <pre><code>
44097 {
44098     bold : {
44099         title: 'Bold (Ctrl+B)',
44100         text: 'Make the selected text bold.',
44101         cls: 'x-html-editor-tip'
44102     },
44103     italic : {
44104         title: 'Italic (Ctrl+I)',
44105         text: 'Make the selected text italic.',
44106         cls: 'x-html-editor-tip'
44107     },
44108     ...
44109 </code></pre>
44110     * @type Object
44111      */
44112     buttonTips : {
44113         bold : {
44114             title: 'Bold (Ctrl+B)',
44115             text: 'Make the selected text bold.',
44116             cls: 'x-html-editor-tip'
44117         },
44118         italic : {
44119             title: 'Italic (Ctrl+I)',
44120             text: 'Make the selected text italic.',
44121             cls: 'x-html-editor-tip'
44122         },
44123         underline : {
44124             title: 'Underline (Ctrl+U)',
44125             text: 'Underline the selected text.',
44126             cls: 'x-html-editor-tip'
44127         },
44128         strikethrough : {
44129             title: 'Strikethrough',
44130             text: 'Strikethrough the selected text.',
44131             cls: 'x-html-editor-tip'
44132         },
44133         increasefontsize : {
44134             title: 'Grow Text',
44135             text: 'Increase the font size.',
44136             cls: 'x-html-editor-tip'
44137         },
44138         decreasefontsize : {
44139             title: 'Shrink Text',
44140             text: 'Decrease the font size.',
44141             cls: 'x-html-editor-tip'
44142         },
44143         backcolor : {
44144             title: 'Text Highlight Color',
44145             text: 'Change the background color of the selected text.',
44146             cls: 'x-html-editor-tip'
44147         },
44148         forecolor : {
44149             title: 'Font Color',
44150             text: 'Change the color of the selected text.',
44151             cls: 'x-html-editor-tip'
44152         },
44153         justifyleft : {
44154             title: 'Align Text Left',
44155             text: 'Align text to the left.',
44156             cls: 'x-html-editor-tip'
44157         },
44158         justifycenter : {
44159             title: 'Center Text',
44160             text: 'Center text in the editor.',
44161             cls: 'x-html-editor-tip'
44162         },
44163         justifyright : {
44164             title: 'Align Text Right',
44165             text: 'Align text to the right.',
44166             cls: 'x-html-editor-tip'
44167         },
44168         insertunorderedlist : {
44169             title: 'Bullet List',
44170             text: 'Start a bulleted list.',
44171             cls: 'x-html-editor-tip'
44172         },
44173         insertorderedlist : {
44174             title: 'Numbered List',
44175             text: 'Start a numbered list.',
44176             cls: 'x-html-editor-tip'
44177         },
44178         createlink : {
44179             title: 'Hyperlink',
44180             text: 'Make the selected text a hyperlink.',
44181             cls: 'x-html-editor-tip'
44182         },
44183         sourceedit : {
44184             title: 'Source Edit',
44185             text: 'Switch to source editing mode.',
44186             cls: 'x-html-editor-tip'
44187         }
44188     },
44189     // private
44190     onDestroy : function(){
44191         if(this.rendered){
44192             
44193             this.tb.items.each(function(item){
44194                 if(item.menu){
44195                     item.menu.removeAll();
44196                     if(item.menu.el){
44197                         item.menu.el.destroy();
44198                     }
44199                 }
44200                 item.destroy();
44201             });
44202              
44203         }
44204     },
44205     onFirstFocus: function() {
44206         this.tb.items.each(function(item){
44207            item.enable();
44208         });
44209     }
44210 });
44211
44212
44213
44214
44215 // <script type="text/javascript">
44216 /*
44217  * Based on
44218  * Ext JS Library 1.1.1
44219  * Copyright(c) 2006-2007, Ext JS, LLC.
44220  *  
44221  
44222  */
44223
44224  
44225 /**
44226  * @class Roo.form.HtmlEditor.ToolbarContext
44227  * Context Toolbar
44228  * 
44229  * Usage:
44230  *
44231  new Roo.form.HtmlEditor({
44232     ....
44233     toolbars : [
44234         { xtype: 'ToolbarStandard', styles : {} }
44235         { xtype: 'ToolbarContext', disable : {} }
44236     ]
44237 })
44238
44239      
44240  * 
44241  * @config : {Object} disable List of elements to disable.. (not done yet.)
44242  * @config : {Object} styles  Map of styles available.
44243  * 
44244  */
44245
44246 Roo.form.HtmlEditor.ToolbarContext = function(config)
44247 {
44248     
44249     Roo.apply(this, config);
44250     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44251     // dont call parent... till later.
44252     this.styles = this.styles || {};
44253 }
44254
44255  
44256
44257 Roo.form.HtmlEditor.ToolbarContext.types = {
44258     'IMG' : {
44259         width : {
44260             title: "Width",
44261             width: 40
44262         },
44263         height:  {
44264             title: "Height",
44265             width: 40
44266         },
44267         align: {
44268             title: "Align",
44269             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44270             width : 80
44271             
44272         },
44273         border: {
44274             title: "Border",
44275             width: 40
44276         },
44277         alt: {
44278             title: "Alt",
44279             width: 120
44280         },
44281         src : {
44282             title: "Src",
44283             width: 220
44284         }
44285         
44286     },
44287     'A' : {
44288         name : {
44289             title: "Name",
44290             width: 50
44291         },
44292         target:  {
44293             title: "Target",
44294             width: 120
44295         },
44296         href:  {
44297             title: "Href",
44298             width: 220
44299         } // border?
44300         
44301     },
44302     'TABLE' : {
44303         rows : {
44304             title: "Rows",
44305             width: 20
44306         },
44307         cols : {
44308             title: "Cols",
44309             width: 20
44310         },
44311         width : {
44312             title: "Width",
44313             width: 40
44314         },
44315         height : {
44316             title: "Height",
44317             width: 40
44318         },
44319         border : {
44320             title: "Border",
44321             width: 20
44322         }
44323     },
44324     'TD' : {
44325         width : {
44326             title: "Width",
44327             width: 40
44328         },
44329         height : {
44330             title: "Height",
44331             width: 40
44332         },   
44333         align: {
44334             title: "Align",
44335             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44336             width: 80
44337         },
44338         valign: {
44339             title: "Valign",
44340             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44341             width: 80
44342         },
44343         colspan: {
44344             title: "Colspan",
44345             width: 20
44346             
44347         },
44348          'font-family'  : {
44349             title : "Font",
44350             style : 'fontFamily',
44351             displayField: 'display',
44352             optname : 'font-family',
44353             width: 140
44354         }
44355     },
44356     'INPUT' : {
44357         name : {
44358             title: "name",
44359             width: 120
44360         },
44361         value : {
44362             title: "Value",
44363             width: 120
44364         },
44365         width : {
44366             title: "Width",
44367             width: 40
44368         }
44369     },
44370     'LABEL' : {
44371         'for' : {
44372             title: "For",
44373             width: 120
44374         }
44375     },
44376     'TEXTAREA' : {
44377           name : {
44378             title: "name",
44379             width: 120
44380         },
44381         rows : {
44382             title: "Rows",
44383             width: 20
44384         },
44385         cols : {
44386             title: "Cols",
44387             width: 20
44388         }
44389     },
44390     'SELECT' : {
44391         name : {
44392             title: "name",
44393             width: 120
44394         },
44395         selectoptions : {
44396             title: "Options",
44397             width: 200
44398         }
44399     },
44400     
44401     // should we really allow this??
44402     // should this just be 
44403     'BODY' : {
44404         title : {
44405             title: "Title",
44406             width: 200,
44407             disabled : true
44408         }
44409     },
44410     'SPAN' : {
44411         'font-family'  : {
44412             title : "Font",
44413             style : 'fontFamily',
44414             displayField: 'display',
44415             optname : 'font-family',
44416             width: 140
44417         }
44418     },
44419     'DIV' : {
44420         'font-family'  : {
44421             title : "Font",
44422             style : 'fontFamily',
44423             displayField: 'display',
44424             optname : 'font-family',
44425             width: 140
44426         }
44427     },
44428      'P' : {
44429         'font-family'  : {
44430             title : "Font",
44431             style : 'fontFamily',
44432             displayField: 'display',
44433             optname : 'font-family',
44434             width: 140
44435         }
44436     },
44437     
44438     '*' : {
44439         // empty..
44440     }
44441
44442 };
44443
44444 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44445 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44446
44447 Roo.form.HtmlEditor.ToolbarContext.options = {
44448         'font-family'  : [ 
44449                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44450                 [ 'Courier New', 'Courier New'],
44451                 [ 'Tahoma', 'Tahoma'],
44452                 [ 'Times New Roman,serif', 'Times'],
44453                 [ 'Verdana','Verdana' ]
44454         ]
44455 };
44456
44457 // fixme - these need to be configurable..
44458  
44459
44460 //Roo.form.HtmlEditor.ToolbarContext.types
44461
44462
44463 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44464     
44465     tb: false,
44466     
44467     rendered: false,
44468     
44469     editor : false,
44470     editorcore : false,
44471     /**
44472      * @cfg {Object} disable  List of toolbar elements to disable
44473          
44474      */
44475     disable : false,
44476     /**
44477      * @cfg {Object} styles List of styles 
44478      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44479      *
44480      * These must be defined in the page, so they get rendered correctly..
44481      * .headline { }
44482      * TD.underline { }
44483      * 
44484      */
44485     styles : false,
44486     
44487     options: false,
44488     
44489     toolbars : false,
44490     
44491     init : function(editor)
44492     {
44493         this.editor = editor;
44494         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44495         var editorcore = this.editorcore;
44496         
44497         var fid = editorcore.frameId;
44498         var etb = this;
44499         function btn(id, toggle, handler){
44500             var xid = fid + '-'+ id ;
44501             return {
44502                 id : xid,
44503                 cmd : id,
44504                 cls : 'x-btn-icon x-edit-'+id,
44505                 enableToggle:toggle !== false,
44506                 scope: editorcore, // was editor...
44507                 handler:handler||editorcore.relayBtnCmd,
44508                 clickEvent:'mousedown',
44509                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44510                 tabIndex:-1
44511             };
44512         }
44513         // create a new element.
44514         var wdiv = editor.wrap.createChild({
44515                 tag: 'div'
44516             }, editor.wrap.dom.firstChild.nextSibling, true);
44517         
44518         // can we do this more than once??
44519         
44520          // stop form submits
44521       
44522  
44523         // disable everything...
44524         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44525         this.toolbars = {};
44526            
44527         for (var i in  ty) {
44528           
44529             this.toolbars[i] = this.buildToolbar(ty[i],i);
44530         }
44531         this.tb = this.toolbars.BODY;
44532         this.tb.el.show();
44533         this.buildFooter();
44534         this.footer.show();
44535         editor.on('hide', function( ) { this.footer.hide() }, this);
44536         editor.on('show', function( ) { this.footer.show() }, this);
44537         
44538          
44539         this.rendered = true;
44540         
44541         // the all the btns;
44542         editor.on('editorevent', this.updateToolbar, this);
44543         // other toolbars need to implement this..
44544         //editor.on('editmodechange', this.updateToolbar, this);
44545     },
44546     
44547     
44548     
44549     /**
44550      * Protected method that will not generally be called directly. It triggers
44551      * a toolbar update by reading the markup state of the current selection in the editor.
44552      *
44553      * Note you can force an update by calling on('editorevent', scope, false)
44554      */
44555     updateToolbar: function(editor,ev,sel){
44556
44557         //Roo.log(ev);
44558         // capture mouse up - this is handy for selecting images..
44559         // perhaps should go somewhere else...
44560         if(!this.editorcore.activated){
44561              this.editor.onFirstFocus();
44562             return;
44563         }
44564         
44565         
44566         
44567         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44568         // selectNode - might want to handle IE?
44569         if (ev &&
44570             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44571             ev.target && ev.target.tagName == 'IMG') {
44572             // they have click on an image...
44573             // let's see if we can change the selection...
44574             sel = ev.target;
44575          
44576               var nodeRange = sel.ownerDocument.createRange();
44577             try {
44578                 nodeRange.selectNode(sel);
44579             } catch (e) {
44580                 nodeRange.selectNodeContents(sel);
44581             }
44582             //nodeRange.collapse(true);
44583             var s = this.editorcore.win.getSelection();
44584             s.removeAllRanges();
44585             s.addRange(nodeRange);
44586         }  
44587         
44588       
44589         var updateFooter = sel ? false : true;
44590         
44591         
44592         var ans = this.editorcore.getAllAncestors();
44593         
44594         // pick
44595         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44596         
44597         if (!sel) { 
44598             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44599             sel = sel ? sel : this.editorcore.doc.body;
44600             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44601             
44602         }
44603         // pick a menu that exists..
44604         var tn = sel.tagName.toUpperCase();
44605         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44606         
44607         tn = sel.tagName.toUpperCase();
44608         
44609         var lastSel = this.tb.selectedNode;
44610         
44611         this.tb.selectedNode = sel;
44612         
44613         // if current menu does not match..
44614         
44615         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44616                 
44617             this.tb.el.hide();
44618             ///console.log("show: " + tn);
44619             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44620             this.tb.el.show();
44621             // update name
44622             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44623             
44624             
44625             // update attributes
44626             if (this.tb.fields) {
44627                 this.tb.fields.each(function(e) {
44628                     if (e.stylename) {
44629                         e.setValue(sel.style[e.stylename]);
44630                         return;
44631                     } 
44632                    e.setValue(sel.getAttribute(e.attrname));
44633                 });
44634             }
44635             
44636             var hasStyles = false;
44637             for(var i in this.styles) {
44638                 hasStyles = true;
44639                 break;
44640             }
44641             
44642             // update styles
44643             if (hasStyles) { 
44644                 var st = this.tb.fields.item(0);
44645                 
44646                 st.store.removeAll();
44647                
44648                 
44649                 var cn = sel.className.split(/\s+/);
44650                 
44651                 var avs = [];
44652                 if (this.styles['*']) {
44653                     
44654                     Roo.each(this.styles['*'], function(v) {
44655                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44656                     });
44657                 }
44658                 if (this.styles[tn]) { 
44659                     Roo.each(this.styles[tn], function(v) {
44660                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44661                     });
44662                 }
44663                 
44664                 st.store.loadData(avs);
44665                 st.collapse();
44666                 st.setValue(cn);
44667             }
44668             // flag our selected Node.
44669             this.tb.selectedNode = sel;
44670            
44671            
44672             Roo.menu.MenuMgr.hideAll();
44673
44674         }
44675         
44676         if (!updateFooter) {
44677             //this.footDisp.dom.innerHTML = ''; 
44678             return;
44679         }
44680         // update the footer
44681         //
44682         var html = '';
44683         
44684         this.footerEls = ans.reverse();
44685         Roo.each(this.footerEls, function(a,i) {
44686             if (!a) { return; }
44687             html += html.length ? ' &gt; '  :  '';
44688             
44689             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44690             
44691         });
44692        
44693         // 
44694         var sz = this.footDisp.up('td').getSize();
44695         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44696         this.footDisp.dom.style.marginLeft = '5px';
44697         
44698         this.footDisp.dom.style.overflow = 'hidden';
44699         
44700         this.footDisp.dom.innerHTML = html;
44701             
44702         //this.editorsyncValue();
44703     },
44704      
44705     
44706    
44707        
44708     // private
44709     onDestroy : function(){
44710         if(this.rendered){
44711             
44712             this.tb.items.each(function(item){
44713                 if(item.menu){
44714                     item.menu.removeAll();
44715                     if(item.menu.el){
44716                         item.menu.el.destroy();
44717                     }
44718                 }
44719                 item.destroy();
44720             });
44721              
44722         }
44723     },
44724     onFirstFocus: function() {
44725         // need to do this for all the toolbars..
44726         this.tb.items.each(function(item){
44727            item.enable();
44728         });
44729     },
44730     buildToolbar: function(tlist, nm)
44731     {
44732         var editor = this.editor;
44733         var editorcore = this.editorcore;
44734          // create a new element.
44735         var wdiv = editor.wrap.createChild({
44736                 tag: 'div'
44737             }, editor.wrap.dom.firstChild.nextSibling, true);
44738         
44739        
44740         var tb = new Roo.Toolbar(wdiv);
44741         // add the name..
44742         
44743         tb.add(nm+ ":&nbsp;");
44744         
44745         var styles = [];
44746         for(var i in this.styles) {
44747             styles.push(i);
44748         }
44749         
44750         // styles...
44751         if (styles && styles.length) {
44752             
44753             // this needs a multi-select checkbox...
44754             tb.addField( new Roo.form.ComboBox({
44755                 store: new Roo.data.SimpleStore({
44756                     id : 'val',
44757                     fields: ['val', 'selected'],
44758                     data : [] 
44759                 }),
44760                 name : '-roo-edit-className',
44761                 attrname : 'className',
44762                 displayField: 'val',
44763                 typeAhead: false,
44764                 mode: 'local',
44765                 editable : false,
44766                 triggerAction: 'all',
44767                 emptyText:'Select Style',
44768                 selectOnFocus:true,
44769                 width: 130,
44770                 listeners : {
44771                     'select': function(c, r, i) {
44772                         // initial support only for on class per el..
44773                         tb.selectedNode.className =  r ? r.get('val') : '';
44774                         editorcore.syncValue();
44775                     }
44776                 }
44777     
44778             }));
44779         }
44780         
44781         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44782         var tbops = tbc.options;
44783         
44784         for (var i in tlist) {
44785             
44786             var item = tlist[i];
44787             tb.add(item.title + ":&nbsp;");
44788             
44789             
44790             //optname == used so you can configure the options available..
44791             var opts = item.opts ? item.opts : false;
44792             if (item.optname) {
44793                 opts = tbops[item.optname];
44794            
44795             }
44796             
44797             if (opts) {
44798                 // opts == pulldown..
44799                 tb.addField( new Roo.form.ComboBox({
44800                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44801                         id : 'val',
44802                         fields: ['val', 'display'],
44803                         data : opts  
44804                     }),
44805                     name : '-roo-edit-' + i,
44806                     attrname : i,
44807                     stylename : item.style ? item.style : false,
44808                     displayField: item.displayField ? item.displayField : 'val',
44809                     valueField :  'val',
44810                     typeAhead: false,
44811                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44812                     editable : false,
44813                     triggerAction: 'all',
44814                     emptyText:'Select',
44815                     selectOnFocus:true,
44816                     width: item.width ? item.width  : 130,
44817                     listeners : {
44818                         'select': function(c, r, i) {
44819                             if (c.stylename) {
44820                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44821                                 return;
44822                             }
44823                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44824                         }
44825                     }
44826
44827                 }));
44828                 continue;
44829                     
44830                  
44831                 
44832                 tb.addField( new Roo.form.TextField({
44833                     name: i,
44834                     width: 100,
44835                     //allowBlank:false,
44836                     value: ''
44837                 }));
44838                 continue;
44839             }
44840             tb.addField( new Roo.form.TextField({
44841                 name: '-roo-edit-' + i,
44842                 attrname : i,
44843                 
44844                 width: item.width,
44845                 //allowBlank:true,
44846                 value: '',
44847                 listeners: {
44848                     'change' : function(f, nv, ov) {
44849                         tb.selectedNode.setAttribute(f.attrname, nv);
44850                     }
44851                 }
44852             }));
44853              
44854         }
44855         
44856         var _this = this;
44857         
44858         if(nm == 'BODY'){
44859             tb.addSeparator();
44860         
44861             tb.addButton( {
44862                 text: 'Stylesheets',
44863
44864                 listeners : {
44865                     click : function ()
44866                     {
44867                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44868                     }
44869                 }
44870             });
44871         }
44872         
44873         tb.addFill();
44874         tb.addButton( {
44875             text: 'Remove Tag',
44876     
44877             listeners : {
44878                 click : function ()
44879                 {
44880                     // remove
44881                     // undo does not work.
44882                      
44883                     var sn = tb.selectedNode;
44884                     
44885                     var pn = sn.parentNode;
44886                     
44887                     var stn =  sn.childNodes[0];
44888                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44889                     while (sn.childNodes.length) {
44890                         var node = sn.childNodes[0];
44891                         sn.removeChild(node);
44892                         //Roo.log(node);
44893                         pn.insertBefore(node, sn);
44894                         
44895                     }
44896                     pn.removeChild(sn);
44897                     var range = editorcore.createRange();
44898         
44899                     range.setStart(stn,0);
44900                     range.setEnd(en,0); //????
44901                     //range.selectNode(sel);
44902                     
44903                     
44904                     var selection = editorcore.getSelection();
44905                     selection.removeAllRanges();
44906                     selection.addRange(range);
44907                     
44908                     
44909                     
44910                     //_this.updateToolbar(null, null, pn);
44911                     _this.updateToolbar(null, null, null);
44912                     _this.footDisp.dom.innerHTML = ''; 
44913                 }
44914             }
44915             
44916                     
44917                 
44918             
44919         });
44920         
44921         
44922         tb.el.on('click', function(e){
44923             e.preventDefault(); // what does this do?
44924         });
44925         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44926         tb.el.hide();
44927         tb.name = nm;
44928         // dont need to disable them... as they will get hidden
44929         return tb;
44930          
44931         
44932     },
44933     buildFooter : function()
44934     {
44935         
44936         var fel = this.editor.wrap.createChild();
44937         this.footer = new Roo.Toolbar(fel);
44938         // toolbar has scrolly on left / right?
44939         var footDisp= new Roo.Toolbar.Fill();
44940         var _t = this;
44941         this.footer.add(
44942             {
44943                 text : '&lt;',
44944                 xtype: 'Button',
44945                 handler : function() {
44946                     _t.footDisp.scrollTo('left',0,true)
44947                 }
44948             }
44949         );
44950         this.footer.add( footDisp );
44951         this.footer.add( 
44952             {
44953                 text : '&gt;',
44954                 xtype: 'Button',
44955                 handler : function() {
44956                     // no animation..
44957                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44958                 }
44959             }
44960         );
44961         var fel = Roo.get(footDisp.el);
44962         fel.addClass('x-editor-context');
44963         this.footDispWrap = fel; 
44964         this.footDispWrap.overflow  = 'hidden';
44965         
44966         this.footDisp = fel.createChild();
44967         this.footDispWrap.on('click', this.onContextClick, this)
44968         
44969         
44970     },
44971     onContextClick : function (ev,dom)
44972     {
44973         ev.preventDefault();
44974         var  cn = dom.className;
44975         //Roo.log(cn);
44976         if (!cn.match(/x-ed-loc-/)) {
44977             return;
44978         }
44979         var n = cn.split('-').pop();
44980         var ans = this.footerEls;
44981         var sel = ans[n];
44982         
44983          // pick
44984         var range = this.editorcore.createRange();
44985         
44986         range.selectNodeContents(sel);
44987         //range.selectNode(sel);
44988         
44989         
44990         var selection = this.editorcore.getSelection();
44991         selection.removeAllRanges();
44992         selection.addRange(range);
44993         
44994         
44995         
44996         this.updateToolbar(null, null, sel);
44997         
44998         
44999     }
45000     
45001     
45002     
45003     
45004     
45005 });
45006
45007
45008
45009
45010
45011 /*
45012  * Based on:
45013  * Ext JS Library 1.1.1
45014  * Copyright(c) 2006-2007, Ext JS, LLC.
45015  *
45016  * Originally Released Under LGPL - original licence link has changed is not relivant.
45017  *
45018  * Fork - LGPL
45019  * <script type="text/javascript">
45020  */
45021  
45022 /**
45023  * @class Roo.form.BasicForm
45024  * @extends Roo.util.Observable
45025  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
45026  * @constructor
45027  * @param {String/HTMLElement/Roo.Element} el The form element or its id
45028  * @param {Object} config Configuration options
45029  */
45030 Roo.form.BasicForm = function(el, config){
45031     this.allItems = [];
45032     this.childForms = [];
45033     Roo.apply(this, config);
45034     /*
45035      * The Roo.form.Field items in this form.
45036      * @type MixedCollection
45037      */
45038      
45039      
45040     this.items = new Roo.util.MixedCollection(false, function(o){
45041         return o.id || (o.id = Roo.id());
45042     });
45043     this.addEvents({
45044         /**
45045          * @event beforeaction
45046          * Fires before any action is performed. Return false to cancel the action.
45047          * @param {Form} this
45048          * @param {Action} action The action to be performed
45049          */
45050         beforeaction: true,
45051         /**
45052          * @event actionfailed
45053          * Fires when an action fails.
45054          * @param {Form} this
45055          * @param {Action} action The action that failed
45056          */
45057         actionfailed : true,
45058         /**
45059          * @event actioncomplete
45060          * Fires when an action is completed.
45061          * @param {Form} this
45062          * @param {Action} action The action that completed
45063          */
45064         actioncomplete : true
45065     });
45066     if(el){
45067         this.initEl(el);
45068     }
45069     Roo.form.BasicForm.superclass.constructor.call(this);
45070 };
45071
45072 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
45073     /**
45074      * @cfg {String} method
45075      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
45076      */
45077     /**
45078      * @cfg {DataReader} reader
45079      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
45080      * This is optional as there is built-in support for processing JSON.
45081      */
45082     /**
45083      * @cfg {DataReader} errorReader
45084      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
45085      * This is completely optional as there is built-in support for processing JSON.
45086      */
45087     /**
45088      * @cfg {String} url
45089      * The URL to use for form actions if one isn't supplied in the action options.
45090      */
45091     /**
45092      * @cfg {Boolean} fileUpload
45093      * Set to true if this form is a file upload.
45094      */
45095      
45096     /**
45097      * @cfg {Object} baseParams
45098      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
45099      */
45100      /**
45101      
45102     /**
45103      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
45104      */
45105     timeout: 30,
45106
45107     // private
45108     activeAction : null,
45109
45110     /**
45111      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
45112      * or setValues() data instead of when the form was first created.
45113      */
45114     trackResetOnLoad : false,
45115     
45116     
45117     /**
45118      * childForms - used for multi-tab forms
45119      * @type {Array}
45120      */
45121     childForms : false,
45122     
45123     /**
45124      * allItems - full list of fields.
45125      * @type {Array}
45126      */
45127     allItems : false,
45128     
45129     /**
45130      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45131      * element by passing it or its id or mask the form itself by passing in true.
45132      * @type Mixed
45133      */
45134     waitMsgTarget : false,
45135
45136     // private
45137     initEl : function(el){
45138         this.el = Roo.get(el);
45139         this.id = this.el.id || Roo.id();
45140         this.el.on('submit', this.onSubmit, this);
45141         this.el.addClass('x-form');
45142     },
45143
45144     // private
45145     onSubmit : function(e){
45146         e.stopEvent();
45147     },
45148
45149     /**
45150      * Returns true if client-side validation on the form is successful.
45151      * @return Boolean
45152      */
45153     isValid : function(){
45154         var valid = true;
45155         this.items.each(function(f){
45156            if(!f.validate()){
45157                valid = false;
45158            }
45159         });
45160         return valid;
45161     },
45162
45163     /**
45164      * Returns true if any fields in this form have changed since their original load.
45165      * @return Boolean
45166      */
45167     isDirty : function(){
45168         var dirty = false;
45169         this.items.each(function(f){
45170            if(f.isDirty()){
45171                dirty = true;
45172                return false;
45173            }
45174         });
45175         return dirty;
45176     },
45177
45178     /**
45179      * Performs a predefined action (submit or load) or custom actions you define on this form.
45180      * @param {String} actionName The name of the action type
45181      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45182      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45183      * accept other config options):
45184      * <pre>
45185 Property          Type             Description
45186 ----------------  ---------------  ----------------------------------------------------------------------------------
45187 url               String           The url for the action (defaults to the form's url)
45188 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45189 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45190 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45191                                    validate the form on the client (defaults to false)
45192      * </pre>
45193      * @return {BasicForm} this
45194      */
45195     doAction : function(action, options){
45196         if(typeof action == 'string'){
45197             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45198         }
45199         if(this.fireEvent('beforeaction', this, action) !== false){
45200             this.beforeAction(action);
45201             action.run.defer(100, action);
45202         }
45203         return this;
45204     },
45205
45206     /**
45207      * Shortcut to do a submit action.
45208      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45209      * @return {BasicForm} this
45210      */
45211     submit : function(options){
45212         this.doAction('submit', options);
45213         return this;
45214     },
45215
45216     /**
45217      * Shortcut to do a load action.
45218      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45219      * @return {BasicForm} this
45220      */
45221     load : function(options){
45222         this.doAction('load', options);
45223         return this;
45224     },
45225
45226     /**
45227      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45228      * @param {Record} record The record to edit
45229      * @return {BasicForm} this
45230      */
45231     updateRecord : function(record){
45232         record.beginEdit();
45233         var fs = record.fields;
45234         fs.each(function(f){
45235             var field = this.findField(f.name);
45236             if(field){
45237                 record.set(f.name, field.getValue());
45238             }
45239         }, this);
45240         record.endEdit();
45241         return this;
45242     },
45243
45244     /**
45245      * Loads an Roo.data.Record into this form.
45246      * @param {Record} record The record to load
45247      * @return {BasicForm} this
45248      */
45249     loadRecord : function(record){
45250         this.setValues(record.data);
45251         return this;
45252     },
45253
45254     // private
45255     beforeAction : function(action){
45256         var o = action.options;
45257         
45258        
45259         if(this.waitMsgTarget === true){
45260             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45261         }else if(this.waitMsgTarget){
45262             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45263             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45264         }else {
45265             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45266         }
45267          
45268     },
45269
45270     // private
45271     afterAction : function(action, success){
45272         this.activeAction = null;
45273         var o = action.options;
45274         
45275         if(this.waitMsgTarget === true){
45276             this.el.unmask();
45277         }else if(this.waitMsgTarget){
45278             this.waitMsgTarget.unmask();
45279         }else{
45280             Roo.MessageBox.updateProgress(1);
45281             Roo.MessageBox.hide();
45282         }
45283          
45284         if(success){
45285             if(o.reset){
45286                 this.reset();
45287             }
45288             Roo.callback(o.success, o.scope, [this, action]);
45289             this.fireEvent('actioncomplete', this, action);
45290             
45291         }else{
45292             
45293             // failure condition..
45294             // we have a scenario where updates need confirming.
45295             // eg. if a locking scenario exists..
45296             // we look for { errors : { needs_confirm : true }} in the response.
45297             if (
45298                 (typeof(action.result) != 'undefined')  &&
45299                 (typeof(action.result.errors) != 'undefined')  &&
45300                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45301            ){
45302                 var _t = this;
45303                 Roo.MessageBox.confirm(
45304                     "Change requires confirmation",
45305                     action.result.errorMsg,
45306                     function(r) {
45307                         if (r != 'yes') {
45308                             return;
45309                         }
45310                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45311                     }
45312                     
45313                 );
45314                 
45315                 
45316                 
45317                 return;
45318             }
45319             
45320             Roo.callback(o.failure, o.scope, [this, action]);
45321             // show an error message if no failed handler is set..
45322             if (!this.hasListener('actionfailed')) {
45323                 Roo.MessageBox.alert("Error",
45324                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45325                         action.result.errorMsg :
45326                         "Saving Failed, please check your entries or try again"
45327                 );
45328             }
45329             
45330             this.fireEvent('actionfailed', this, action);
45331         }
45332         
45333     },
45334
45335     /**
45336      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45337      * @param {String} id The value to search for
45338      * @return Field
45339      */
45340     findField : function(id){
45341         var field = this.items.get(id);
45342         if(!field){
45343             this.items.each(function(f){
45344                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45345                     field = f;
45346                     return false;
45347                 }
45348             });
45349         }
45350         return field || null;
45351     },
45352
45353     /**
45354      * Add a secondary form to this one, 
45355      * Used to provide tabbed forms. One form is primary, with hidden values 
45356      * which mirror the elements from the other forms.
45357      * 
45358      * @param {Roo.form.Form} form to add.
45359      * 
45360      */
45361     addForm : function(form)
45362     {
45363        
45364         if (this.childForms.indexOf(form) > -1) {
45365             // already added..
45366             return;
45367         }
45368         this.childForms.push(form);
45369         var n = '';
45370         Roo.each(form.allItems, function (fe) {
45371             
45372             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45373             if (this.findField(n)) { // already added..
45374                 return;
45375             }
45376             var add = new Roo.form.Hidden({
45377                 name : n
45378             });
45379             add.render(this.el);
45380             
45381             this.add( add );
45382         }, this);
45383         
45384     },
45385     /**
45386      * Mark fields in this form invalid in bulk.
45387      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45388      * @return {BasicForm} this
45389      */
45390     markInvalid : function(errors){
45391         if(errors instanceof Array){
45392             for(var i = 0, len = errors.length; i < len; i++){
45393                 var fieldError = errors[i];
45394                 var f = this.findField(fieldError.id);
45395                 if(f){
45396                     f.markInvalid(fieldError.msg);
45397                 }
45398             }
45399         }else{
45400             var field, id;
45401             for(id in errors){
45402                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45403                     field.markInvalid(errors[id]);
45404                 }
45405             }
45406         }
45407         Roo.each(this.childForms || [], function (f) {
45408             f.markInvalid(errors);
45409         });
45410         
45411         return this;
45412     },
45413
45414     /**
45415      * Set values for fields in this form in bulk.
45416      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45417      * @return {BasicForm} this
45418      */
45419     setValues : function(values){
45420         if(values instanceof Array){ // array of objects
45421             for(var i = 0, len = values.length; i < len; i++){
45422                 var v = values[i];
45423                 var f = this.findField(v.id);
45424                 if(f){
45425                     f.setValue(v.value);
45426                     if(this.trackResetOnLoad){
45427                         f.originalValue = f.getValue();
45428                     }
45429                 }
45430             }
45431         }else{ // object hash
45432             var field, id;
45433             for(id in values){
45434                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45435                     
45436                     if (field.setFromData && 
45437                         field.valueField && 
45438                         field.displayField &&
45439                         // combos' with local stores can 
45440                         // be queried via setValue()
45441                         // to set their value..
45442                         (field.store && !field.store.isLocal)
45443                         ) {
45444                         // it's a combo
45445                         var sd = { };
45446                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45447                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45448                         field.setFromData(sd);
45449                         
45450                     } else {
45451                         field.setValue(values[id]);
45452                     }
45453                     
45454                     
45455                     if(this.trackResetOnLoad){
45456                         field.originalValue = field.getValue();
45457                     }
45458                 }
45459             }
45460         }
45461          
45462         Roo.each(this.childForms || [], function (f) {
45463             f.setValues(values);
45464         });
45465                 
45466         return this;
45467     },
45468
45469     /**
45470      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45471      * they are returned as an array.
45472      * @param {Boolean} asString
45473      * @return {Object}
45474      */
45475     getValues : function(asString){
45476         if (this.childForms) {
45477             // copy values from the child forms
45478             Roo.each(this.childForms, function (f) {
45479                 this.setValues(f.getValues());
45480             }, this);
45481         }
45482         
45483         
45484         
45485         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45486         if(asString === true){
45487             return fs;
45488         }
45489         return Roo.urlDecode(fs);
45490     },
45491     
45492     /**
45493      * Returns the fields in this form as an object with key/value pairs. 
45494      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45495      * @return {Object}
45496      */
45497     getFieldValues : function(with_hidden)
45498     {
45499         if (this.childForms) {
45500             // copy values from the child forms
45501             // should this call getFieldValues - probably not as we do not currently copy
45502             // hidden fields when we generate..
45503             Roo.each(this.childForms, function (f) {
45504                 this.setValues(f.getValues());
45505             }, this);
45506         }
45507         
45508         var ret = {};
45509         this.items.each(function(f){
45510             if (!f.getName()) {
45511                 return;
45512             }
45513             var v = f.getValue();
45514             if (f.inputType =='radio') {
45515                 if (typeof(ret[f.getName()]) == 'undefined') {
45516                     ret[f.getName()] = ''; // empty..
45517                 }
45518                 
45519                 if (!f.el.dom.checked) {
45520                     return;
45521                     
45522                 }
45523                 v = f.el.dom.value;
45524                 
45525             }
45526             
45527             // not sure if this supported any more..
45528             if ((typeof(v) == 'object') && f.getRawValue) {
45529                 v = f.getRawValue() ; // dates..
45530             }
45531             // combo boxes where name != hiddenName...
45532             if (f.name != f.getName()) {
45533                 ret[f.name] = f.getRawValue();
45534             }
45535             ret[f.getName()] = v;
45536         });
45537         
45538         return ret;
45539     },
45540
45541     /**
45542      * Clears all invalid messages in this form.
45543      * @return {BasicForm} this
45544      */
45545     clearInvalid : function(){
45546         this.items.each(function(f){
45547            f.clearInvalid();
45548         });
45549         
45550         Roo.each(this.childForms || [], function (f) {
45551             f.clearInvalid();
45552         });
45553         
45554         
45555         return this;
45556     },
45557
45558     /**
45559      * Resets this form.
45560      * @return {BasicForm} this
45561      */
45562     reset : function(){
45563         this.items.each(function(f){
45564             f.reset();
45565         });
45566         
45567         Roo.each(this.childForms || [], function (f) {
45568             f.reset();
45569         });
45570        
45571         
45572         return this;
45573     },
45574
45575     /**
45576      * Add Roo.form components to this form.
45577      * @param {Field} field1
45578      * @param {Field} field2 (optional)
45579      * @param {Field} etc (optional)
45580      * @return {BasicForm} this
45581      */
45582     add : function(){
45583         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45584         return this;
45585     },
45586
45587
45588     /**
45589      * Removes a field from the items collection (does NOT remove its markup).
45590      * @param {Field} field
45591      * @return {BasicForm} this
45592      */
45593     remove : function(field){
45594         this.items.remove(field);
45595         return this;
45596     },
45597
45598     /**
45599      * Looks at the fields in this form, checks them for an id attribute,
45600      * and calls applyTo on the existing dom element with that id.
45601      * @return {BasicForm} this
45602      */
45603     render : function(){
45604         this.items.each(function(f){
45605             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45606                 f.applyTo(f.id);
45607             }
45608         });
45609         return this;
45610     },
45611
45612     /**
45613      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45614      * @param {Object} values
45615      * @return {BasicForm} this
45616      */
45617     applyToFields : function(o){
45618         this.items.each(function(f){
45619            Roo.apply(f, o);
45620         });
45621         return this;
45622     },
45623
45624     /**
45625      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45626      * @param {Object} values
45627      * @return {BasicForm} this
45628      */
45629     applyIfToFields : function(o){
45630         this.items.each(function(f){
45631            Roo.applyIf(f, o);
45632         });
45633         return this;
45634     }
45635 });
45636
45637 // back compat
45638 Roo.BasicForm = Roo.form.BasicForm;/*
45639  * Based on:
45640  * Ext JS Library 1.1.1
45641  * Copyright(c) 2006-2007, Ext JS, LLC.
45642  *
45643  * Originally Released Under LGPL - original licence link has changed is not relivant.
45644  *
45645  * Fork - LGPL
45646  * <script type="text/javascript">
45647  */
45648
45649 /**
45650  * @class Roo.form.Form
45651  * @extends Roo.form.BasicForm
45652  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45653  * @constructor
45654  * @param {Object} config Configuration options
45655  */
45656 Roo.form.Form = function(config){
45657     var xitems =  [];
45658     if (config.items) {
45659         xitems = config.items;
45660         delete config.items;
45661     }
45662    
45663     
45664     Roo.form.Form.superclass.constructor.call(this, null, config);
45665     this.url = this.url || this.action;
45666     if(!this.root){
45667         this.root = new Roo.form.Layout(Roo.applyIf({
45668             id: Roo.id()
45669         }, config));
45670     }
45671     this.active = this.root;
45672     /**
45673      * Array of all the buttons that have been added to this form via {@link addButton}
45674      * @type Array
45675      */
45676     this.buttons = [];
45677     this.allItems = [];
45678     this.addEvents({
45679         /**
45680          * @event clientvalidation
45681          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45682          * @param {Form} this
45683          * @param {Boolean} valid true if the form has passed client-side validation
45684          */
45685         clientvalidation: true,
45686         /**
45687          * @event rendered
45688          * Fires when the form is rendered
45689          * @param {Roo.form.Form} form
45690          */
45691         rendered : true
45692     });
45693     
45694     if (this.progressUrl) {
45695             // push a hidden field onto the list of fields..
45696             this.addxtype( {
45697                     xns: Roo.form, 
45698                     xtype : 'Hidden', 
45699                     name : 'UPLOAD_IDENTIFIER' 
45700             });
45701         }
45702         
45703     
45704     Roo.each(xitems, this.addxtype, this);
45705     
45706     
45707     
45708 };
45709
45710 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45711     /**
45712      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45713      */
45714     /**
45715      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45716      */
45717     /**
45718      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45719      */
45720     buttonAlign:'center',
45721
45722     /**
45723      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45724      */
45725     minButtonWidth:75,
45726
45727     /**
45728      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45729      * This property cascades to child containers if not set.
45730      */
45731     labelAlign:'left',
45732
45733     /**
45734      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45735      * fires a looping event with that state. This is required to bind buttons to the valid
45736      * state using the config value formBind:true on the button.
45737      */
45738     monitorValid : false,
45739
45740     /**
45741      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45742      */
45743     monitorPoll : 200,
45744     
45745     /**
45746      * @cfg {String} progressUrl - Url to return progress data 
45747      */
45748     
45749     progressUrl : false,
45750   
45751     /**
45752      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45753      * fields are added and the column is closed. If no fields are passed the column remains open
45754      * until end() is called.
45755      * @param {Object} config The config to pass to the column
45756      * @param {Field} field1 (optional)
45757      * @param {Field} field2 (optional)
45758      * @param {Field} etc (optional)
45759      * @return Column The column container object
45760      */
45761     column : function(c){
45762         var col = new Roo.form.Column(c);
45763         this.start(col);
45764         if(arguments.length > 1){ // duplicate code required because of Opera
45765             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45766             this.end();
45767         }
45768         return col;
45769     },
45770
45771     /**
45772      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45773      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45774      * until end() is called.
45775      * @param {Object} config The config to pass to the fieldset
45776      * @param {Field} field1 (optional)
45777      * @param {Field} field2 (optional)
45778      * @param {Field} etc (optional)
45779      * @return FieldSet The fieldset container object
45780      */
45781     fieldset : function(c){
45782         var fs = new Roo.form.FieldSet(c);
45783         this.start(fs);
45784         if(arguments.length > 1){ // duplicate code required because of Opera
45785             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45786             this.end();
45787         }
45788         return fs;
45789     },
45790
45791     /**
45792      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45793      * fields are added and the container is closed. If no fields are passed the container remains open
45794      * until end() is called.
45795      * @param {Object} config The config to pass to the Layout
45796      * @param {Field} field1 (optional)
45797      * @param {Field} field2 (optional)
45798      * @param {Field} etc (optional)
45799      * @return Layout The container object
45800      */
45801     container : function(c){
45802         var l = new Roo.form.Layout(c);
45803         this.start(l);
45804         if(arguments.length > 1){ // duplicate code required because of Opera
45805             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45806             this.end();
45807         }
45808         return l;
45809     },
45810
45811     /**
45812      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45813      * @param {Object} container A Roo.form.Layout or subclass of Layout
45814      * @return {Form} this
45815      */
45816     start : function(c){
45817         // cascade label info
45818         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45819         this.active.stack.push(c);
45820         c.ownerCt = this.active;
45821         this.active = c;
45822         return this;
45823     },
45824
45825     /**
45826      * Closes the current open container
45827      * @return {Form} this
45828      */
45829     end : function(){
45830         if(this.active == this.root){
45831             return this;
45832         }
45833         this.active = this.active.ownerCt;
45834         return this;
45835     },
45836
45837     /**
45838      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45839      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45840      * as the label of the field.
45841      * @param {Field} field1
45842      * @param {Field} field2 (optional)
45843      * @param {Field} etc. (optional)
45844      * @return {Form} this
45845      */
45846     add : function(){
45847         this.active.stack.push.apply(this.active.stack, arguments);
45848         this.allItems.push.apply(this.allItems,arguments);
45849         var r = [];
45850         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45851             if(a[i].isFormField){
45852                 r.push(a[i]);
45853             }
45854         }
45855         if(r.length > 0){
45856             Roo.form.Form.superclass.add.apply(this, r);
45857         }
45858         return this;
45859     },
45860     
45861
45862     
45863     
45864     
45865      /**
45866      * Find any element that has been added to a form, using it's ID or name
45867      * This can include framesets, columns etc. along with regular fields..
45868      * @param {String} id - id or name to find.
45869      
45870      * @return {Element} e - or false if nothing found.
45871      */
45872     findbyId : function(id)
45873     {
45874         var ret = false;
45875         if (!id) {
45876             return ret;
45877         }
45878         Roo.each(this.allItems, function(f){
45879             if (f.id == id || f.name == id ){
45880                 ret = f;
45881                 return false;
45882             }
45883         });
45884         return ret;
45885     },
45886
45887     
45888     
45889     /**
45890      * Render this form into the passed container. This should only be called once!
45891      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45892      * @return {Form} this
45893      */
45894     render : function(ct)
45895     {
45896         
45897         
45898         
45899         ct = Roo.get(ct);
45900         var o = this.autoCreate || {
45901             tag: 'form',
45902             method : this.method || 'POST',
45903             id : this.id || Roo.id()
45904         };
45905         this.initEl(ct.createChild(o));
45906
45907         this.root.render(this.el);
45908         
45909        
45910              
45911         this.items.each(function(f){
45912             f.render('x-form-el-'+f.id);
45913         });
45914
45915         if(this.buttons.length > 0){
45916             // tables are required to maintain order and for correct IE layout
45917             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45918                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45919                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45920             }}, null, true);
45921             var tr = tb.getElementsByTagName('tr')[0];
45922             for(var i = 0, len = this.buttons.length; i < len; i++) {
45923                 var b = this.buttons[i];
45924                 var td = document.createElement('td');
45925                 td.className = 'x-form-btn-td';
45926                 b.render(tr.appendChild(td));
45927             }
45928         }
45929         if(this.monitorValid){ // initialize after render
45930             this.startMonitoring();
45931         }
45932         this.fireEvent('rendered', this);
45933         return this;
45934     },
45935
45936     /**
45937      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45938      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45939      * object or a valid Roo.DomHelper element config
45940      * @param {Function} handler The function called when the button is clicked
45941      * @param {Object} scope (optional) The scope of the handler function
45942      * @return {Roo.Button}
45943      */
45944     addButton : function(config, handler, scope){
45945         var bc = {
45946             handler: handler,
45947             scope: scope,
45948             minWidth: this.minButtonWidth,
45949             hideParent:true
45950         };
45951         if(typeof config == "string"){
45952             bc.text = config;
45953         }else{
45954             Roo.apply(bc, config);
45955         }
45956         var btn = new Roo.Button(null, bc);
45957         this.buttons.push(btn);
45958         return btn;
45959     },
45960
45961      /**
45962      * Adds a series of form elements (using the xtype property as the factory method.
45963      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45964      * @param {Object} config 
45965      */
45966     
45967     addxtype : function()
45968     {
45969         var ar = Array.prototype.slice.call(arguments, 0);
45970         var ret = false;
45971         for(var i = 0; i < ar.length; i++) {
45972             if (!ar[i]) {
45973                 continue; // skip -- if this happends something invalid got sent, we 
45974                 // should ignore it, as basically that interface element will not show up
45975                 // and that should be pretty obvious!!
45976             }
45977             
45978             if (Roo.form[ar[i].xtype]) {
45979                 ar[i].form = this;
45980                 var fe = Roo.factory(ar[i], Roo.form);
45981                 if (!ret) {
45982                     ret = fe;
45983                 }
45984                 fe.form = this;
45985                 if (fe.store) {
45986                     fe.store.form = this;
45987                 }
45988                 if (fe.isLayout) {  
45989                          
45990                     this.start(fe);
45991                     this.allItems.push(fe);
45992                     if (fe.items && fe.addxtype) {
45993                         fe.addxtype.apply(fe, fe.items);
45994                         delete fe.items;
45995                     }
45996                      this.end();
45997                     continue;
45998                 }
45999                 
46000                 
46001                  
46002                 this.add(fe);
46003               //  console.log('adding ' + ar[i].xtype);
46004             }
46005             if (ar[i].xtype == 'Button') {  
46006                 //console.log('adding button');
46007                 //console.log(ar[i]);
46008                 this.addButton(ar[i]);
46009                 this.allItems.push(fe);
46010                 continue;
46011             }
46012             
46013             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
46014                 alert('end is not supported on xtype any more, use items');
46015             //    this.end();
46016             //    //console.log('adding end');
46017             }
46018             
46019         }
46020         return ret;
46021     },
46022     
46023     /**
46024      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
46025      * option "monitorValid"
46026      */
46027     startMonitoring : function(){
46028         if(!this.bound){
46029             this.bound = true;
46030             Roo.TaskMgr.start({
46031                 run : this.bindHandler,
46032                 interval : this.monitorPoll || 200,
46033                 scope: this
46034             });
46035         }
46036     },
46037
46038     /**
46039      * Stops monitoring of the valid state of this form
46040      */
46041     stopMonitoring : function(){
46042         this.bound = false;
46043     },
46044
46045     // private
46046     bindHandler : function(){
46047         if(!this.bound){
46048             return false; // stops binding
46049         }
46050         var valid = true;
46051         this.items.each(function(f){
46052             if(!f.isValid(true)){
46053                 valid = false;
46054                 return false;
46055             }
46056         });
46057         for(var i = 0, len = this.buttons.length; i < len; i++){
46058             var btn = this.buttons[i];
46059             if(btn.formBind === true && btn.disabled === valid){
46060                 btn.setDisabled(!valid);
46061             }
46062         }
46063         this.fireEvent('clientvalidation', this, valid);
46064     }
46065     
46066     
46067     
46068     
46069     
46070     
46071     
46072     
46073 });
46074
46075
46076 // back compat
46077 Roo.Form = Roo.form.Form;
46078 /*
46079  * Based on:
46080  * Ext JS Library 1.1.1
46081  * Copyright(c) 2006-2007, Ext JS, LLC.
46082  *
46083  * Originally Released Under LGPL - original licence link has changed is not relivant.
46084  *
46085  * Fork - LGPL
46086  * <script type="text/javascript">
46087  */
46088
46089 // as we use this in bootstrap.
46090 Roo.namespace('Roo.form');
46091  /**
46092  * @class Roo.form.Action
46093  * Internal Class used to handle form actions
46094  * @constructor
46095  * @param {Roo.form.BasicForm} el The form element or its id
46096  * @param {Object} config Configuration options
46097  */
46098
46099  
46100  
46101 // define the action interface
46102 Roo.form.Action = function(form, options){
46103     this.form = form;
46104     this.options = options || {};
46105 };
46106 /**
46107  * Client Validation Failed
46108  * @const 
46109  */
46110 Roo.form.Action.CLIENT_INVALID = 'client';
46111 /**
46112  * Server Validation Failed
46113  * @const 
46114  */
46115 Roo.form.Action.SERVER_INVALID = 'server';
46116  /**
46117  * Connect to Server Failed
46118  * @const 
46119  */
46120 Roo.form.Action.CONNECT_FAILURE = 'connect';
46121 /**
46122  * Reading Data from Server Failed
46123  * @const 
46124  */
46125 Roo.form.Action.LOAD_FAILURE = 'load';
46126
46127 Roo.form.Action.prototype = {
46128     type : 'default',
46129     failureType : undefined,
46130     response : undefined,
46131     result : undefined,
46132
46133     // interface method
46134     run : function(options){
46135
46136     },
46137
46138     // interface method
46139     success : function(response){
46140
46141     },
46142
46143     // interface method
46144     handleResponse : function(response){
46145
46146     },
46147
46148     // default connection failure
46149     failure : function(response){
46150         
46151         this.response = response;
46152         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46153         this.form.afterAction(this, false);
46154     },
46155
46156     processResponse : function(response){
46157         this.response = response;
46158         if(!response.responseText){
46159             return true;
46160         }
46161         this.result = this.handleResponse(response);
46162         return this.result;
46163     },
46164
46165     // utility functions used internally
46166     getUrl : function(appendParams){
46167         var url = this.options.url || this.form.url || this.form.el.dom.action;
46168         if(appendParams){
46169             var p = this.getParams();
46170             if(p){
46171                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46172             }
46173         }
46174         return url;
46175     },
46176
46177     getMethod : function(){
46178         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46179     },
46180
46181     getParams : function(){
46182         var bp = this.form.baseParams;
46183         var p = this.options.params;
46184         if(p){
46185             if(typeof p == "object"){
46186                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46187             }else if(typeof p == 'string' && bp){
46188                 p += '&' + Roo.urlEncode(bp);
46189             }
46190         }else if(bp){
46191             p = Roo.urlEncode(bp);
46192         }
46193         return p;
46194     },
46195
46196     createCallback : function(){
46197         return {
46198             success: this.success,
46199             failure: this.failure,
46200             scope: this,
46201             timeout: (this.form.timeout*1000),
46202             upload: this.form.fileUpload ? this.success : undefined
46203         };
46204     }
46205 };
46206
46207 Roo.form.Action.Submit = function(form, options){
46208     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46209 };
46210
46211 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46212     type : 'submit',
46213
46214     haveProgress : false,
46215     uploadComplete : false,
46216     
46217     // uploadProgress indicator.
46218     uploadProgress : function()
46219     {
46220         if (!this.form.progressUrl) {
46221             return;
46222         }
46223         
46224         if (!this.haveProgress) {
46225             Roo.MessageBox.progress("Uploading", "Uploading");
46226         }
46227         if (this.uploadComplete) {
46228            Roo.MessageBox.hide();
46229            return;
46230         }
46231         
46232         this.haveProgress = true;
46233    
46234         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46235         
46236         var c = new Roo.data.Connection();
46237         c.request({
46238             url : this.form.progressUrl,
46239             params: {
46240                 id : uid
46241             },
46242             method: 'GET',
46243             success : function(req){
46244                //console.log(data);
46245                 var rdata = false;
46246                 var edata;
46247                 try  {
46248                    rdata = Roo.decode(req.responseText)
46249                 } catch (e) {
46250                     Roo.log("Invalid data from server..");
46251                     Roo.log(edata);
46252                     return;
46253                 }
46254                 if (!rdata || !rdata.success) {
46255                     Roo.log(rdata);
46256                     Roo.MessageBox.alert(Roo.encode(rdata));
46257                     return;
46258                 }
46259                 var data = rdata.data;
46260                 
46261                 if (this.uploadComplete) {
46262                    Roo.MessageBox.hide();
46263                    return;
46264                 }
46265                    
46266                 if (data){
46267                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46268                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46269                     );
46270                 }
46271                 this.uploadProgress.defer(2000,this);
46272             },
46273        
46274             failure: function(data) {
46275                 Roo.log('progress url failed ');
46276                 Roo.log(data);
46277             },
46278             scope : this
46279         });
46280            
46281     },
46282     
46283     
46284     run : function()
46285     {
46286         // run get Values on the form, so it syncs any secondary forms.
46287         this.form.getValues();
46288         
46289         var o = this.options;
46290         var method = this.getMethod();
46291         var isPost = method == 'POST';
46292         if(o.clientValidation === false || this.form.isValid()){
46293             
46294             if (this.form.progressUrl) {
46295                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46296                     (new Date() * 1) + '' + Math.random());
46297                     
46298             } 
46299             
46300             
46301             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46302                 form:this.form.el.dom,
46303                 url:this.getUrl(!isPost),
46304                 method: method,
46305                 params:isPost ? this.getParams() : null,
46306                 isUpload: this.form.fileUpload
46307             }));
46308             
46309             this.uploadProgress();
46310
46311         }else if (o.clientValidation !== false){ // client validation failed
46312             this.failureType = Roo.form.Action.CLIENT_INVALID;
46313             this.form.afterAction(this, false);
46314         }
46315     },
46316
46317     success : function(response)
46318     {
46319         this.uploadComplete= true;
46320         if (this.haveProgress) {
46321             Roo.MessageBox.hide();
46322         }
46323         
46324         
46325         var result = this.processResponse(response);
46326         if(result === true || result.success){
46327             this.form.afterAction(this, true);
46328             return;
46329         }
46330         if(result.errors){
46331             this.form.markInvalid(result.errors);
46332             this.failureType = Roo.form.Action.SERVER_INVALID;
46333         }
46334         this.form.afterAction(this, false);
46335     },
46336     failure : function(response)
46337     {
46338         this.uploadComplete= true;
46339         if (this.haveProgress) {
46340             Roo.MessageBox.hide();
46341         }
46342         
46343         this.response = response;
46344         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46345         this.form.afterAction(this, false);
46346     },
46347     
46348     handleResponse : function(response){
46349         if(this.form.errorReader){
46350             var rs = this.form.errorReader.read(response);
46351             var errors = [];
46352             if(rs.records){
46353                 for(var i = 0, len = rs.records.length; i < len; i++) {
46354                     var r = rs.records[i];
46355                     errors[i] = r.data;
46356                 }
46357             }
46358             if(errors.length < 1){
46359                 errors = null;
46360             }
46361             return {
46362                 success : rs.success,
46363                 errors : errors
46364             };
46365         }
46366         var ret = false;
46367         try {
46368             ret = Roo.decode(response.responseText);
46369         } catch (e) {
46370             ret = {
46371                 success: false,
46372                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46373                 errors : []
46374             };
46375         }
46376         return ret;
46377         
46378     }
46379 });
46380
46381
46382 Roo.form.Action.Load = function(form, options){
46383     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46384     this.reader = this.form.reader;
46385 };
46386
46387 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46388     type : 'load',
46389
46390     run : function(){
46391         
46392         Roo.Ajax.request(Roo.apply(
46393                 this.createCallback(), {
46394                     method:this.getMethod(),
46395                     url:this.getUrl(false),
46396                     params:this.getParams()
46397         }));
46398     },
46399
46400     success : function(response){
46401         
46402         var result = this.processResponse(response);
46403         if(result === true || !result.success || !result.data){
46404             this.failureType = Roo.form.Action.LOAD_FAILURE;
46405             this.form.afterAction(this, false);
46406             return;
46407         }
46408         this.form.clearInvalid();
46409         this.form.setValues(result.data);
46410         this.form.afterAction(this, true);
46411     },
46412
46413     handleResponse : function(response){
46414         if(this.form.reader){
46415             var rs = this.form.reader.read(response);
46416             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46417             return {
46418                 success : rs.success,
46419                 data : data
46420             };
46421         }
46422         return Roo.decode(response.responseText);
46423     }
46424 });
46425
46426 Roo.form.Action.ACTION_TYPES = {
46427     'load' : Roo.form.Action.Load,
46428     'submit' : Roo.form.Action.Submit
46429 };/*
46430  * Based on:
46431  * Ext JS Library 1.1.1
46432  * Copyright(c) 2006-2007, Ext JS, LLC.
46433  *
46434  * Originally Released Under LGPL - original licence link has changed is not relivant.
46435  *
46436  * Fork - LGPL
46437  * <script type="text/javascript">
46438  */
46439  
46440 /**
46441  * @class Roo.form.Layout
46442  * @extends Roo.Component
46443  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46444  * @constructor
46445  * @param {Object} config Configuration options
46446  */
46447 Roo.form.Layout = function(config){
46448     var xitems = [];
46449     if (config.items) {
46450         xitems = config.items;
46451         delete config.items;
46452     }
46453     Roo.form.Layout.superclass.constructor.call(this, config);
46454     this.stack = [];
46455     Roo.each(xitems, this.addxtype, this);
46456      
46457 };
46458
46459 Roo.extend(Roo.form.Layout, Roo.Component, {
46460     /**
46461      * @cfg {String/Object} autoCreate
46462      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46463      */
46464     /**
46465      * @cfg {String/Object/Function} style
46466      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46467      * a function which returns such a specification.
46468      */
46469     /**
46470      * @cfg {String} labelAlign
46471      * Valid values are "left," "top" and "right" (defaults to "left")
46472      */
46473     /**
46474      * @cfg {Number} labelWidth
46475      * Fixed width in pixels of all field labels (defaults to undefined)
46476      */
46477     /**
46478      * @cfg {Boolean} clear
46479      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46480      */
46481     clear : true,
46482     /**
46483      * @cfg {String} labelSeparator
46484      * The separator to use after field labels (defaults to ':')
46485      */
46486     labelSeparator : ':',
46487     /**
46488      * @cfg {Boolean} hideLabels
46489      * True to suppress the display of field labels in this layout (defaults to false)
46490      */
46491     hideLabels : false,
46492
46493     // private
46494     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46495     
46496     isLayout : true,
46497     
46498     // private
46499     onRender : function(ct, position){
46500         if(this.el){ // from markup
46501             this.el = Roo.get(this.el);
46502         }else {  // generate
46503             var cfg = this.getAutoCreate();
46504             this.el = ct.createChild(cfg, position);
46505         }
46506         if(this.style){
46507             this.el.applyStyles(this.style);
46508         }
46509         if(this.labelAlign){
46510             this.el.addClass('x-form-label-'+this.labelAlign);
46511         }
46512         if(this.hideLabels){
46513             this.labelStyle = "display:none";
46514             this.elementStyle = "padding-left:0;";
46515         }else{
46516             if(typeof this.labelWidth == 'number'){
46517                 this.labelStyle = "width:"+this.labelWidth+"px;";
46518                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46519             }
46520             if(this.labelAlign == 'top'){
46521                 this.labelStyle = "width:auto;";
46522                 this.elementStyle = "padding-left:0;";
46523             }
46524         }
46525         var stack = this.stack;
46526         var slen = stack.length;
46527         if(slen > 0){
46528             if(!this.fieldTpl){
46529                 var t = new Roo.Template(
46530                     '<div class="x-form-item {5}">',
46531                         '<label for="{0}" style="{2}">{1}{4}</label>',
46532                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46533                         '</div>',
46534                     '</div><div class="x-form-clear-left"></div>'
46535                 );
46536                 t.disableFormats = true;
46537                 t.compile();
46538                 Roo.form.Layout.prototype.fieldTpl = t;
46539             }
46540             for(var i = 0; i < slen; i++) {
46541                 if(stack[i].isFormField){
46542                     this.renderField(stack[i]);
46543                 }else{
46544                     this.renderComponent(stack[i]);
46545                 }
46546             }
46547         }
46548         if(this.clear){
46549             this.el.createChild({cls:'x-form-clear'});
46550         }
46551     },
46552
46553     // private
46554     renderField : function(f){
46555         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46556                f.id, //0
46557                f.fieldLabel, //1
46558                f.labelStyle||this.labelStyle||'', //2
46559                this.elementStyle||'', //3
46560                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46561                f.itemCls||this.itemCls||''  //5
46562        ], true).getPrevSibling());
46563     },
46564
46565     // private
46566     renderComponent : function(c){
46567         c.render(c.isLayout ? this.el : this.el.createChild());    
46568     },
46569     /**
46570      * Adds a object form elements (using the xtype property as the factory method.)
46571      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46572      * @param {Object} config 
46573      */
46574     addxtype : function(o)
46575     {
46576         // create the lement.
46577         o.form = this.form;
46578         var fe = Roo.factory(o, Roo.form);
46579         this.form.allItems.push(fe);
46580         this.stack.push(fe);
46581         
46582         if (fe.isFormField) {
46583             this.form.items.add(fe);
46584         }
46585          
46586         return fe;
46587     }
46588 });
46589
46590 /**
46591  * @class Roo.form.Column
46592  * @extends Roo.form.Layout
46593  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46594  * @constructor
46595  * @param {Object} config Configuration options
46596  */
46597 Roo.form.Column = function(config){
46598     Roo.form.Column.superclass.constructor.call(this, config);
46599 };
46600
46601 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46602     /**
46603      * @cfg {Number/String} width
46604      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46605      */
46606     /**
46607      * @cfg {String/Object} autoCreate
46608      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46609      */
46610
46611     // private
46612     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46613
46614     // private
46615     onRender : function(ct, position){
46616         Roo.form.Column.superclass.onRender.call(this, ct, position);
46617         if(this.width){
46618             this.el.setWidth(this.width);
46619         }
46620     }
46621 });
46622
46623
46624 /**
46625  * @class Roo.form.Row
46626  * @extends Roo.form.Layout
46627  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46628  * @constructor
46629  * @param {Object} config Configuration options
46630  */
46631
46632  
46633 Roo.form.Row = function(config){
46634     Roo.form.Row.superclass.constructor.call(this, config);
46635 };
46636  
46637 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46638       /**
46639      * @cfg {Number/String} width
46640      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46641      */
46642     /**
46643      * @cfg {Number/String} height
46644      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46645      */
46646     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46647     
46648     padWidth : 20,
46649     // private
46650     onRender : function(ct, position){
46651         //console.log('row render');
46652         if(!this.rowTpl){
46653             var t = new Roo.Template(
46654                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46655                     '<label for="{0}" style="{2}">{1}{4}</label>',
46656                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46657                     '</div>',
46658                 '</div>'
46659             );
46660             t.disableFormats = true;
46661             t.compile();
46662             Roo.form.Layout.prototype.rowTpl = t;
46663         }
46664         this.fieldTpl = this.rowTpl;
46665         
46666         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46667         var labelWidth = 100;
46668         
46669         if ((this.labelAlign != 'top')) {
46670             if (typeof this.labelWidth == 'number') {
46671                 labelWidth = this.labelWidth
46672             }
46673             this.padWidth =  20 + labelWidth;
46674             
46675         }
46676         
46677         Roo.form.Column.superclass.onRender.call(this, ct, position);
46678         if(this.width){
46679             this.el.setWidth(this.width);
46680         }
46681         if(this.height){
46682             this.el.setHeight(this.height);
46683         }
46684     },
46685     
46686     // private
46687     renderField : function(f){
46688         f.fieldEl = this.fieldTpl.append(this.el, [
46689                f.id, f.fieldLabel,
46690                f.labelStyle||this.labelStyle||'',
46691                this.elementStyle||'',
46692                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46693                f.itemCls||this.itemCls||'',
46694                f.width ? f.width + this.padWidth : 160 + this.padWidth
46695        ],true);
46696     }
46697 });
46698  
46699
46700 /**
46701  * @class Roo.form.FieldSet
46702  * @extends Roo.form.Layout
46703  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46704  * @constructor
46705  * @param {Object} config Configuration options
46706  */
46707 Roo.form.FieldSet = function(config){
46708     Roo.form.FieldSet.superclass.constructor.call(this, config);
46709 };
46710
46711 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46712     /**
46713      * @cfg {String} legend
46714      * The text to display as the legend for the FieldSet (defaults to '')
46715      */
46716     /**
46717      * @cfg {String/Object} autoCreate
46718      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46719      */
46720
46721     // private
46722     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46723
46724     // private
46725     onRender : function(ct, position){
46726         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46727         if(this.legend){
46728             this.setLegend(this.legend);
46729         }
46730     },
46731
46732     // private
46733     setLegend : function(text){
46734         if(this.rendered){
46735             this.el.child('legend').update(text);
46736         }
46737     }
46738 });/*
46739  * Based on:
46740  * Ext JS Library 1.1.1
46741  * Copyright(c) 2006-2007, Ext JS, LLC.
46742  *
46743  * Originally Released Under LGPL - original licence link has changed is not relivant.
46744  *
46745  * Fork - LGPL
46746  * <script type="text/javascript">
46747  */
46748 /**
46749  * @class Roo.form.VTypes
46750  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46751  * @singleton
46752  */
46753 Roo.form.VTypes = function(){
46754     // closure these in so they are only created once.
46755     var alpha = /^[a-zA-Z_]+$/;
46756     var alphanum = /^[a-zA-Z0-9_]+$/;
46757     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
46758     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46759
46760     // All these messages and functions are configurable
46761     return {
46762         /**
46763          * The function used to validate email addresses
46764          * @param {String} value The email address
46765          */
46766         'email' : function(v){
46767             return email.test(v);
46768         },
46769         /**
46770          * The error text to display when the email validation function returns false
46771          * @type String
46772          */
46773         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46774         /**
46775          * The keystroke filter mask to be applied on email input
46776          * @type RegExp
46777          */
46778         'emailMask' : /[a-z0-9_\.\-@]/i,
46779
46780         /**
46781          * The function used to validate URLs
46782          * @param {String} value The URL
46783          */
46784         'url' : function(v){
46785             return url.test(v);
46786         },
46787         /**
46788          * The error text to display when the url validation function returns false
46789          * @type String
46790          */
46791         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46792         
46793         /**
46794          * The function used to validate alpha values
46795          * @param {String} value The value
46796          */
46797         'alpha' : function(v){
46798             return alpha.test(v);
46799         },
46800         /**
46801          * The error text to display when the alpha validation function returns false
46802          * @type String
46803          */
46804         'alphaText' : 'This field should only contain letters and _',
46805         /**
46806          * The keystroke filter mask to be applied on alpha input
46807          * @type RegExp
46808          */
46809         'alphaMask' : /[a-z_]/i,
46810
46811         /**
46812          * The function used to validate alphanumeric values
46813          * @param {String} value The value
46814          */
46815         'alphanum' : function(v){
46816             return alphanum.test(v);
46817         },
46818         /**
46819          * The error text to display when the alphanumeric validation function returns false
46820          * @type String
46821          */
46822         'alphanumText' : 'This field should only contain letters, numbers and _',
46823         /**
46824          * The keystroke filter mask to be applied on alphanumeric input
46825          * @type RegExp
46826          */
46827         'alphanumMask' : /[a-z0-9_]/i
46828     };
46829 }();//<script type="text/javascript">
46830
46831 /**
46832  * @class Roo.form.FCKeditor
46833  * @extends Roo.form.TextArea
46834  * Wrapper around the FCKEditor http://www.fckeditor.net
46835  * @constructor
46836  * Creates a new FCKeditor
46837  * @param {Object} config Configuration options
46838  */
46839 Roo.form.FCKeditor = function(config){
46840     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46841     this.addEvents({
46842          /**
46843          * @event editorinit
46844          * Fired when the editor is initialized - you can add extra handlers here..
46845          * @param {FCKeditor} this
46846          * @param {Object} the FCK object.
46847          */
46848         editorinit : true
46849     });
46850     
46851     
46852 };
46853 Roo.form.FCKeditor.editors = { };
46854 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46855 {
46856     //defaultAutoCreate : {
46857     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46858     //},
46859     // private
46860     /**
46861      * @cfg {Object} fck options - see fck manual for details.
46862      */
46863     fckconfig : false,
46864     
46865     /**
46866      * @cfg {Object} fck toolbar set (Basic or Default)
46867      */
46868     toolbarSet : 'Basic',
46869     /**
46870      * @cfg {Object} fck BasePath
46871      */ 
46872     basePath : '/fckeditor/',
46873     
46874     
46875     frame : false,
46876     
46877     value : '',
46878     
46879    
46880     onRender : function(ct, position)
46881     {
46882         if(!this.el){
46883             this.defaultAutoCreate = {
46884                 tag: "textarea",
46885                 style:"width:300px;height:60px;",
46886                 autocomplete: "new-password"
46887             };
46888         }
46889         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46890         /*
46891         if(this.grow){
46892             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46893             if(this.preventScrollbars){
46894                 this.el.setStyle("overflow", "hidden");
46895             }
46896             this.el.setHeight(this.growMin);
46897         }
46898         */
46899         //console.log('onrender' + this.getId() );
46900         Roo.form.FCKeditor.editors[this.getId()] = this;
46901          
46902
46903         this.replaceTextarea() ;
46904         
46905     },
46906     
46907     getEditor : function() {
46908         return this.fckEditor;
46909     },
46910     /**
46911      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46912      * @param {Mixed} value The value to set
46913      */
46914     
46915     
46916     setValue : function(value)
46917     {
46918         //console.log('setValue: ' + value);
46919         
46920         if(typeof(value) == 'undefined') { // not sure why this is happending...
46921             return;
46922         }
46923         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46924         
46925         //if(!this.el || !this.getEditor()) {
46926         //    this.value = value;
46927             //this.setValue.defer(100,this,[value]);    
46928         //    return;
46929         //} 
46930         
46931         if(!this.getEditor()) {
46932             return;
46933         }
46934         
46935         this.getEditor().SetData(value);
46936         
46937         //
46938
46939     },
46940
46941     /**
46942      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46943      * @return {Mixed} value The field value
46944      */
46945     getValue : function()
46946     {
46947         
46948         if (this.frame && this.frame.dom.style.display == 'none') {
46949             return Roo.form.FCKeditor.superclass.getValue.call(this);
46950         }
46951         
46952         if(!this.el || !this.getEditor()) {
46953            
46954            // this.getValue.defer(100,this); 
46955             return this.value;
46956         }
46957        
46958         
46959         var value=this.getEditor().GetData();
46960         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46961         return Roo.form.FCKeditor.superclass.getValue.call(this);
46962         
46963
46964     },
46965
46966     /**
46967      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46968      * @return {Mixed} value The field value
46969      */
46970     getRawValue : function()
46971     {
46972         if (this.frame && this.frame.dom.style.display == 'none') {
46973             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46974         }
46975         
46976         if(!this.el || !this.getEditor()) {
46977             //this.getRawValue.defer(100,this); 
46978             return this.value;
46979             return;
46980         }
46981         
46982         
46983         
46984         var value=this.getEditor().GetData();
46985         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46986         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46987          
46988     },
46989     
46990     setSize : function(w,h) {
46991         
46992         
46993         
46994         //if (this.frame && this.frame.dom.style.display == 'none') {
46995         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46996         //    return;
46997         //}
46998         //if(!this.el || !this.getEditor()) {
46999         //    this.setSize.defer(100,this, [w,h]); 
47000         //    return;
47001         //}
47002         
47003         
47004         
47005         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
47006         
47007         this.frame.dom.setAttribute('width', w);
47008         this.frame.dom.setAttribute('height', h);
47009         this.frame.setSize(w,h);
47010         
47011     },
47012     
47013     toggleSourceEdit : function(value) {
47014         
47015       
47016          
47017         this.el.dom.style.display = value ? '' : 'none';
47018         this.frame.dom.style.display = value ?  'none' : '';
47019         
47020     },
47021     
47022     
47023     focus: function(tag)
47024     {
47025         if (this.frame.dom.style.display == 'none') {
47026             return Roo.form.FCKeditor.superclass.focus.call(this);
47027         }
47028         if(!this.el || !this.getEditor()) {
47029             this.focus.defer(100,this, [tag]); 
47030             return;
47031         }
47032         
47033         
47034         
47035         
47036         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
47037         this.getEditor().Focus();
47038         if (tgs.length) {
47039             if (!this.getEditor().Selection.GetSelection()) {
47040                 this.focus.defer(100,this, [tag]); 
47041                 return;
47042             }
47043             
47044             
47045             var r = this.getEditor().EditorDocument.createRange();
47046             r.setStart(tgs[0],0);
47047             r.setEnd(tgs[0],0);
47048             this.getEditor().Selection.GetSelection().removeAllRanges();
47049             this.getEditor().Selection.GetSelection().addRange(r);
47050             this.getEditor().Focus();
47051         }
47052         
47053     },
47054     
47055     
47056     
47057     replaceTextarea : function()
47058     {
47059         if ( document.getElementById( this.getId() + '___Frame' ) ) {
47060             return ;
47061         }
47062         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
47063         //{
47064             // We must check the elements firstly using the Id and then the name.
47065         var oTextarea = document.getElementById( this.getId() );
47066         
47067         var colElementsByName = document.getElementsByName( this.getId() ) ;
47068          
47069         oTextarea.style.display = 'none' ;
47070
47071         if ( oTextarea.tabIndex ) {            
47072             this.TabIndex = oTextarea.tabIndex ;
47073         }
47074         
47075         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
47076         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
47077         this.frame = Roo.get(this.getId() + '___Frame')
47078     },
47079     
47080     _getConfigHtml : function()
47081     {
47082         var sConfig = '' ;
47083
47084         for ( var o in this.fckconfig ) {
47085             sConfig += sConfig.length > 0  ? '&amp;' : '';
47086             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
47087         }
47088
47089         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
47090     },
47091     
47092     
47093     _getIFrameHtml : function()
47094     {
47095         var sFile = 'fckeditor.html' ;
47096         /* no idea what this is about..
47097         try
47098         {
47099             if ( (/fcksource=true/i).test( window.top.location.search ) )
47100                 sFile = 'fckeditor.original.html' ;
47101         }
47102         catch (e) { 
47103         */
47104
47105         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
47106         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
47107         
47108         
47109         var html = '<iframe id="' + this.getId() +
47110             '___Frame" src="' + sLink +
47111             '" width="' + this.width +
47112             '" height="' + this.height + '"' +
47113             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
47114             ' frameborder="0" scrolling="no"></iframe>' ;
47115
47116         return html ;
47117     },
47118     
47119     _insertHtmlBefore : function( html, element )
47120     {
47121         if ( element.insertAdjacentHTML )       {
47122             // IE
47123             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47124         } else { // Gecko
47125             var oRange = document.createRange() ;
47126             oRange.setStartBefore( element ) ;
47127             var oFragment = oRange.createContextualFragment( html );
47128             element.parentNode.insertBefore( oFragment, element ) ;
47129         }
47130     }
47131     
47132     
47133   
47134     
47135     
47136     
47137     
47138
47139 });
47140
47141 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47142
47143 function FCKeditor_OnComplete(editorInstance){
47144     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47145     f.fckEditor = editorInstance;
47146     //console.log("loaded");
47147     f.fireEvent('editorinit', f, editorInstance);
47148
47149   
47150
47151  
47152
47153
47154
47155
47156
47157
47158
47159
47160
47161
47162
47163
47164
47165
47166
47167 //<script type="text/javascript">
47168 /**
47169  * @class Roo.form.GridField
47170  * @extends Roo.form.Field
47171  * Embed a grid (or editable grid into a form)
47172  * STATUS ALPHA
47173  * 
47174  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47175  * it needs 
47176  * xgrid.store = Roo.data.Store
47177  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47178  * xgrid.store.reader = Roo.data.JsonReader 
47179  * 
47180  * 
47181  * @constructor
47182  * Creates a new GridField
47183  * @param {Object} config Configuration options
47184  */
47185 Roo.form.GridField = function(config){
47186     Roo.form.GridField.superclass.constructor.call(this, config);
47187      
47188 };
47189
47190 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47191     /**
47192      * @cfg {Number} width  - used to restrict width of grid..
47193      */
47194     width : 100,
47195     /**
47196      * @cfg {Number} height - used to restrict height of grid..
47197      */
47198     height : 50,
47199      /**
47200      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47201          * 
47202          *}
47203      */
47204     xgrid : false, 
47205     /**
47206      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47207      * {tag: "input", type: "checkbox", autocomplete: "off"})
47208      */
47209    // defaultAutoCreate : { tag: 'div' },
47210     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47211     /**
47212      * @cfg {String} addTitle Text to include for adding a title.
47213      */
47214     addTitle : false,
47215     //
47216     onResize : function(){
47217         Roo.form.Field.superclass.onResize.apply(this, arguments);
47218     },
47219
47220     initEvents : function(){
47221         // Roo.form.Checkbox.superclass.initEvents.call(this);
47222         // has no events...
47223        
47224     },
47225
47226
47227     getResizeEl : function(){
47228         return this.wrap;
47229     },
47230
47231     getPositionEl : function(){
47232         return this.wrap;
47233     },
47234
47235     // private
47236     onRender : function(ct, position){
47237         
47238         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47239         var style = this.style;
47240         delete this.style;
47241         
47242         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47243         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47244         this.viewEl = this.wrap.createChild({ tag: 'div' });
47245         if (style) {
47246             this.viewEl.applyStyles(style);
47247         }
47248         if (this.width) {
47249             this.viewEl.setWidth(this.width);
47250         }
47251         if (this.height) {
47252             this.viewEl.setHeight(this.height);
47253         }
47254         //if(this.inputValue !== undefined){
47255         //this.setValue(this.value);
47256         
47257         
47258         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47259         
47260         
47261         this.grid.render();
47262         this.grid.getDataSource().on('remove', this.refreshValue, this);
47263         this.grid.getDataSource().on('update', this.refreshValue, this);
47264         this.grid.on('afteredit', this.refreshValue, this);
47265  
47266     },
47267      
47268     
47269     /**
47270      * Sets the value of the item. 
47271      * @param {String} either an object  or a string..
47272      */
47273     setValue : function(v){
47274         //this.value = v;
47275         v = v || []; // empty set..
47276         // this does not seem smart - it really only affects memoryproxy grids..
47277         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47278             var ds = this.grid.getDataSource();
47279             // assumes a json reader..
47280             var data = {}
47281             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47282             ds.loadData( data);
47283         }
47284         // clear selection so it does not get stale.
47285         if (this.grid.sm) { 
47286             this.grid.sm.clearSelections();
47287         }
47288         
47289         Roo.form.GridField.superclass.setValue.call(this, v);
47290         this.refreshValue();
47291         // should load data in the grid really....
47292     },
47293     
47294     // private
47295     refreshValue: function() {
47296          var val = [];
47297         this.grid.getDataSource().each(function(r) {
47298             val.push(r.data);
47299         });
47300         this.el.dom.value = Roo.encode(val);
47301     }
47302     
47303      
47304     
47305     
47306 });/*
47307  * Based on:
47308  * Ext JS Library 1.1.1
47309  * Copyright(c) 2006-2007, Ext JS, LLC.
47310  *
47311  * Originally Released Under LGPL - original licence link has changed is not relivant.
47312  *
47313  * Fork - LGPL
47314  * <script type="text/javascript">
47315  */
47316 /**
47317  * @class Roo.form.DisplayField
47318  * @extends Roo.form.Field
47319  * A generic Field to display non-editable data.
47320  * @cfg {Boolean} closable (true|false) default false
47321  * @constructor
47322  * Creates a new Display Field item.
47323  * @param {Object} config Configuration options
47324  */
47325 Roo.form.DisplayField = function(config){
47326     Roo.form.DisplayField.superclass.constructor.call(this, config);
47327     
47328     this.addEvents({
47329         /**
47330          * @event close
47331          * Fires after the click the close btn
47332              * @param {Roo.form.DisplayField} this
47333              */
47334         close : true
47335     });
47336 };
47337
47338 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47339     inputType:      'hidden',
47340     allowBlank:     true,
47341     readOnly:         true,
47342     
47343  
47344     /**
47345      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47346      */
47347     focusClass : undefined,
47348     /**
47349      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47350      */
47351     fieldClass: 'x-form-field',
47352     
47353      /**
47354      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47355      */
47356     valueRenderer: undefined,
47357     
47358     width: 100,
47359     /**
47360      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47361      * {tag: "input", type: "checkbox", autocomplete: "off"})
47362      */
47363      
47364  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47365  
47366     closable : false,
47367     
47368     onResize : function(){
47369         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47370         
47371     },
47372
47373     initEvents : function(){
47374         // Roo.form.Checkbox.superclass.initEvents.call(this);
47375         // has no events...
47376         
47377         if(this.closable){
47378             this.closeEl.on('click', this.onClose, this);
47379         }
47380        
47381     },
47382
47383
47384     getResizeEl : function(){
47385         return this.wrap;
47386     },
47387
47388     getPositionEl : function(){
47389         return this.wrap;
47390     },
47391
47392     // private
47393     onRender : function(ct, position){
47394         
47395         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47396         //if(this.inputValue !== undefined){
47397         this.wrap = this.el.wrap();
47398         
47399         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47400         
47401         if(this.closable){
47402             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
47403         }
47404         
47405         if (this.bodyStyle) {
47406             this.viewEl.applyStyles(this.bodyStyle);
47407         }
47408         //this.viewEl.setStyle('padding', '2px');
47409         
47410         this.setValue(this.value);
47411         
47412     },
47413 /*
47414     // private
47415     initValue : Roo.emptyFn,
47416
47417   */
47418
47419         // private
47420     onClick : function(){
47421         
47422     },
47423
47424     /**
47425      * Sets the checked state of the checkbox.
47426      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47427      */
47428     setValue : function(v){
47429         this.value = v;
47430         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47431         // this might be called before we have a dom element..
47432         if (!this.viewEl) {
47433             return;
47434         }
47435         this.viewEl.dom.innerHTML = html;
47436         Roo.form.DisplayField.superclass.setValue.call(this, v);
47437
47438     },
47439     
47440     onClose : function(e)
47441     {
47442         e.preventDefault();
47443         
47444         this.fireEvent('close', this);
47445     }
47446 });/*
47447  * 
47448  * Licence- LGPL
47449  * 
47450  */
47451
47452 /**
47453  * @class Roo.form.DayPicker
47454  * @extends Roo.form.Field
47455  * A Day picker show [M] [T] [W] ....
47456  * @constructor
47457  * Creates a new Day Picker
47458  * @param {Object} config Configuration options
47459  */
47460 Roo.form.DayPicker= function(config){
47461     Roo.form.DayPicker.superclass.constructor.call(this, config);
47462      
47463 };
47464
47465 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47466     /**
47467      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47468      */
47469     focusClass : undefined,
47470     /**
47471      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47472      */
47473     fieldClass: "x-form-field",
47474    
47475     /**
47476      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47477      * {tag: "input", type: "checkbox", autocomplete: "off"})
47478      */
47479     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47480     
47481    
47482     actionMode : 'viewEl', 
47483     //
47484     // private
47485  
47486     inputType : 'hidden',
47487     
47488      
47489     inputElement: false, // real input element?
47490     basedOn: false, // ????
47491     
47492     isFormField: true, // not sure where this is needed!!!!
47493
47494     onResize : function(){
47495         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47496         if(!this.boxLabel){
47497             this.el.alignTo(this.wrap, 'c-c');
47498         }
47499     },
47500
47501     initEvents : function(){
47502         Roo.form.Checkbox.superclass.initEvents.call(this);
47503         this.el.on("click", this.onClick,  this);
47504         this.el.on("change", this.onClick,  this);
47505     },
47506
47507
47508     getResizeEl : function(){
47509         return this.wrap;
47510     },
47511
47512     getPositionEl : function(){
47513         return this.wrap;
47514     },
47515
47516     
47517     // private
47518     onRender : function(ct, position){
47519         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47520        
47521         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47522         
47523         var r1 = '<table><tr>';
47524         var r2 = '<tr class="x-form-daypick-icons">';
47525         for (var i=0; i < 7; i++) {
47526             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47527             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47528         }
47529         
47530         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47531         viewEl.select('img').on('click', this.onClick, this);
47532         this.viewEl = viewEl;   
47533         
47534         
47535         // this will not work on Chrome!!!
47536         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47537         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47538         
47539         
47540           
47541
47542     },
47543
47544     // private
47545     initValue : Roo.emptyFn,
47546
47547     /**
47548      * Returns the checked state of the checkbox.
47549      * @return {Boolean} True if checked, else false
47550      */
47551     getValue : function(){
47552         return this.el.dom.value;
47553         
47554     },
47555
47556         // private
47557     onClick : function(e){ 
47558         //this.setChecked(!this.checked);
47559         Roo.get(e.target).toggleClass('x-menu-item-checked');
47560         this.refreshValue();
47561         //if(this.el.dom.checked != this.checked){
47562         //    this.setValue(this.el.dom.checked);
47563        // }
47564     },
47565     
47566     // private
47567     refreshValue : function()
47568     {
47569         var val = '';
47570         this.viewEl.select('img',true).each(function(e,i,n)  {
47571             val += e.is(".x-menu-item-checked") ? String(n) : '';
47572         });
47573         this.setValue(val, true);
47574     },
47575
47576     /**
47577      * Sets the checked state of the checkbox.
47578      * On is always based on a string comparison between inputValue and the param.
47579      * @param {Boolean/String} value - the value to set 
47580      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47581      */
47582     setValue : function(v,suppressEvent){
47583         if (!this.el.dom) {
47584             return;
47585         }
47586         var old = this.el.dom.value ;
47587         this.el.dom.value = v;
47588         if (suppressEvent) {
47589             return ;
47590         }
47591          
47592         // update display..
47593         this.viewEl.select('img',true).each(function(e,i,n)  {
47594             
47595             var on = e.is(".x-menu-item-checked");
47596             var newv = v.indexOf(String(n)) > -1;
47597             if (on != newv) {
47598                 e.toggleClass('x-menu-item-checked');
47599             }
47600             
47601         });
47602         
47603         
47604         this.fireEvent('change', this, v, old);
47605         
47606         
47607     },
47608    
47609     // handle setting of hidden value by some other method!!?!?
47610     setFromHidden: function()
47611     {
47612         if(!this.el){
47613             return;
47614         }
47615         //console.log("SET FROM HIDDEN");
47616         //alert('setFrom hidden');
47617         this.setValue(this.el.dom.value);
47618     },
47619     
47620     onDestroy : function()
47621     {
47622         if(this.viewEl){
47623             Roo.get(this.viewEl).remove();
47624         }
47625          
47626         Roo.form.DayPicker.superclass.onDestroy.call(this);
47627     }
47628
47629 });/*
47630  * RooJS Library 1.1.1
47631  * Copyright(c) 2008-2011  Alan Knowles
47632  *
47633  * License - LGPL
47634  */
47635  
47636
47637 /**
47638  * @class Roo.form.ComboCheck
47639  * @extends Roo.form.ComboBox
47640  * A combobox for multiple select items.
47641  *
47642  * FIXME - could do with a reset button..
47643  * 
47644  * @constructor
47645  * Create a new ComboCheck
47646  * @param {Object} config Configuration options
47647  */
47648 Roo.form.ComboCheck = function(config){
47649     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47650     // should verify some data...
47651     // like
47652     // hiddenName = required..
47653     // displayField = required
47654     // valudField == required
47655     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47656     var _t = this;
47657     Roo.each(req, function(e) {
47658         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47659             throw "Roo.form.ComboCheck : missing value for: " + e;
47660         }
47661     });
47662     
47663     
47664 };
47665
47666 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47667      
47668      
47669     editable : false,
47670      
47671     selectedClass: 'x-menu-item-checked', 
47672     
47673     // private
47674     onRender : function(ct, position){
47675         var _t = this;
47676         
47677         
47678         
47679         if(!this.tpl){
47680             var cls = 'x-combo-list';
47681
47682             
47683             this.tpl =  new Roo.Template({
47684                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47685                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47686                    '<span>{' + this.displayField + '}</span>' +
47687                     '</div>' 
47688                 
47689             });
47690         }
47691  
47692         
47693         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47694         this.view.singleSelect = false;
47695         this.view.multiSelect = true;
47696         this.view.toggleSelect = true;
47697         this.pageTb.add(new Roo.Toolbar.Fill(), {
47698             
47699             text: 'Done',
47700             handler: function()
47701             {
47702                 _t.collapse();
47703             }
47704         });
47705     },
47706     
47707     onViewOver : function(e, t){
47708         // do nothing...
47709         return;
47710         
47711     },
47712     
47713     onViewClick : function(doFocus,index){
47714         return;
47715         
47716     },
47717     select: function () {
47718         //Roo.log("SELECT CALLED");
47719     },
47720      
47721     selectByValue : function(xv, scrollIntoView){
47722         var ar = this.getValueArray();
47723         var sels = [];
47724         
47725         Roo.each(ar, function(v) {
47726             if(v === undefined || v === null){
47727                 return;
47728             }
47729             var r = this.findRecord(this.valueField, v);
47730             if(r){
47731                 sels.push(this.store.indexOf(r))
47732                 
47733             }
47734         },this);
47735         this.view.select(sels);
47736         return false;
47737     },
47738     
47739     
47740     
47741     onSelect : function(record, index){
47742        // Roo.log("onselect Called");
47743        // this is only called by the clear button now..
47744         this.view.clearSelections();
47745         this.setValue('[]');
47746         if (this.value != this.valueBefore) {
47747             this.fireEvent('change', this, this.value, this.valueBefore);
47748             this.valueBefore = this.value;
47749         }
47750     },
47751     getValueArray : function()
47752     {
47753         var ar = [] ;
47754         
47755         try {
47756             //Roo.log(this.value);
47757             if (typeof(this.value) == 'undefined') {
47758                 return [];
47759             }
47760             var ar = Roo.decode(this.value);
47761             return  ar instanceof Array ? ar : []; //?? valid?
47762             
47763         } catch(e) {
47764             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47765             return [];
47766         }
47767          
47768     },
47769     expand : function ()
47770     {
47771         
47772         Roo.form.ComboCheck.superclass.expand.call(this);
47773         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47774         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47775         
47776
47777     },
47778     
47779     collapse : function(){
47780         Roo.form.ComboCheck.superclass.collapse.call(this);
47781         var sl = this.view.getSelectedIndexes();
47782         var st = this.store;
47783         var nv = [];
47784         var tv = [];
47785         var r;
47786         Roo.each(sl, function(i) {
47787             r = st.getAt(i);
47788             nv.push(r.get(this.valueField));
47789         },this);
47790         this.setValue(Roo.encode(nv));
47791         if (this.value != this.valueBefore) {
47792
47793             this.fireEvent('change', this, this.value, this.valueBefore);
47794             this.valueBefore = this.value;
47795         }
47796         
47797     },
47798     
47799     setValue : function(v){
47800         // Roo.log(v);
47801         this.value = v;
47802         
47803         var vals = this.getValueArray();
47804         var tv = [];
47805         Roo.each(vals, function(k) {
47806             var r = this.findRecord(this.valueField, k);
47807             if(r){
47808                 tv.push(r.data[this.displayField]);
47809             }else if(this.valueNotFoundText !== undefined){
47810                 tv.push( this.valueNotFoundText );
47811             }
47812         },this);
47813        // Roo.log(tv);
47814         
47815         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47816         this.hiddenField.value = v;
47817         this.value = v;
47818     }
47819     
47820 });/*
47821  * Based on:
47822  * Ext JS Library 1.1.1
47823  * Copyright(c) 2006-2007, Ext JS, LLC.
47824  *
47825  * Originally Released Under LGPL - original licence link has changed is not relivant.
47826  *
47827  * Fork - LGPL
47828  * <script type="text/javascript">
47829  */
47830  
47831 /**
47832  * @class Roo.form.Signature
47833  * @extends Roo.form.Field
47834  * Signature field.  
47835  * @constructor
47836  * 
47837  * @param {Object} config Configuration options
47838  */
47839
47840 Roo.form.Signature = function(config){
47841     Roo.form.Signature.superclass.constructor.call(this, config);
47842     
47843     this.addEvents({// not in used??
47844          /**
47845          * @event confirm
47846          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47847              * @param {Roo.form.Signature} combo This combo box
47848              */
47849         'confirm' : true,
47850         /**
47851          * @event reset
47852          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47853              * @param {Roo.form.ComboBox} combo This combo box
47854              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47855              */
47856         'reset' : true
47857     });
47858 };
47859
47860 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47861     /**
47862      * @cfg {Object} labels Label to use when rendering a form.
47863      * defaults to 
47864      * labels : { 
47865      *      clear : "Clear",
47866      *      confirm : "Confirm"
47867      *  }
47868      */
47869     labels : { 
47870         clear : "Clear",
47871         confirm : "Confirm"
47872     },
47873     /**
47874      * @cfg {Number} width The signature panel width (defaults to 300)
47875      */
47876     width: 300,
47877     /**
47878      * @cfg {Number} height The signature panel height (defaults to 100)
47879      */
47880     height : 100,
47881     /**
47882      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47883      */
47884     allowBlank : false,
47885     
47886     //private
47887     // {Object} signPanel The signature SVG panel element (defaults to {})
47888     signPanel : {},
47889     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47890     isMouseDown : false,
47891     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47892     isConfirmed : false,
47893     // {String} signatureTmp SVG mapping string (defaults to empty string)
47894     signatureTmp : '',
47895     
47896     
47897     defaultAutoCreate : { // modified by initCompnoent..
47898         tag: "input",
47899         type:"hidden"
47900     },
47901
47902     // private
47903     onRender : function(ct, position){
47904         
47905         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47906         
47907         this.wrap = this.el.wrap({
47908             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47909         });
47910         
47911         this.createToolbar(this);
47912         this.signPanel = this.wrap.createChild({
47913                 tag: 'div',
47914                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47915             }, this.el
47916         );
47917             
47918         this.svgID = Roo.id();
47919         this.svgEl = this.signPanel.createChild({
47920               xmlns : 'http://www.w3.org/2000/svg',
47921               tag : 'svg',
47922               id : this.svgID + "-svg",
47923               width: this.width,
47924               height: this.height,
47925               viewBox: '0 0 '+this.width+' '+this.height,
47926               cn : [
47927                 {
47928                     tag: "rect",
47929                     id: this.svgID + "-svg-r",
47930                     width: this.width,
47931                     height: this.height,
47932                     fill: "#ffa"
47933                 },
47934                 {
47935                     tag: "line",
47936                     id: this.svgID + "-svg-l",
47937                     x1: "0", // start
47938                     y1: (this.height*0.8), // start set the line in 80% of height
47939                     x2: this.width, // end
47940                     y2: (this.height*0.8), // end set the line in 80% of height
47941                     'stroke': "#666",
47942                     'stroke-width': "1",
47943                     'stroke-dasharray': "3",
47944                     'shape-rendering': "crispEdges",
47945                     'pointer-events': "none"
47946                 },
47947                 {
47948                     tag: "path",
47949                     id: this.svgID + "-svg-p",
47950                     'stroke': "navy",
47951                     'stroke-width': "3",
47952                     'fill': "none",
47953                     'pointer-events': 'none'
47954                 }
47955               ]
47956         });
47957         this.createSVG();
47958         this.svgBox = this.svgEl.dom.getScreenCTM();
47959     },
47960     createSVG : function(){ 
47961         var svg = this.signPanel;
47962         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47963         var t = this;
47964
47965         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47966         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47967         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47968         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47969         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47970         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47971         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47972         
47973     },
47974     isTouchEvent : function(e){
47975         return e.type.match(/^touch/);
47976     },
47977     getCoords : function (e) {
47978         var pt    = this.svgEl.dom.createSVGPoint();
47979         pt.x = e.clientX; 
47980         pt.y = e.clientY;
47981         if (this.isTouchEvent(e)) {
47982             pt.x =  e.targetTouches[0].clientX;
47983             pt.y = e.targetTouches[0].clientY;
47984         }
47985         var a = this.svgEl.dom.getScreenCTM();
47986         var b = a.inverse();
47987         var mx = pt.matrixTransform(b);
47988         return mx.x + ',' + mx.y;
47989     },
47990     //mouse event headler 
47991     down : function (e) {
47992         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47993         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47994         
47995         this.isMouseDown = true;
47996         
47997         e.preventDefault();
47998     },
47999     move : function (e) {
48000         if (this.isMouseDown) {
48001             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
48002             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
48003         }
48004         
48005         e.preventDefault();
48006     },
48007     up : function (e) {
48008         this.isMouseDown = false;
48009         var sp = this.signatureTmp.split(' ');
48010         
48011         if(sp.length > 1){
48012             if(!sp[sp.length-2].match(/^L/)){
48013                 sp.pop();
48014                 sp.pop();
48015                 sp.push("");
48016                 this.signatureTmp = sp.join(" ");
48017             }
48018         }
48019         if(this.getValue() != this.signatureTmp){
48020             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48021             this.isConfirmed = false;
48022         }
48023         e.preventDefault();
48024     },
48025     
48026     /**
48027      * Protected method that will not generally be called directly. It
48028      * is called when the editor creates its toolbar. Override this method if you need to
48029      * add custom toolbar buttons.
48030      * @param {HtmlEditor} editor
48031      */
48032     createToolbar : function(editor){
48033          function btn(id, toggle, handler){
48034             var xid = fid + '-'+ id ;
48035             return {
48036                 id : xid,
48037                 cmd : id,
48038                 cls : 'x-btn-icon x-edit-'+id,
48039                 enableToggle:toggle !== false,
48040                 scope: editor, // was editor...
48041                 handler:handler||editor.relayBtnCmd,
48042                 clickEvent:'mousedown',
48043                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48044                 tabIndex:-1
48045             };
48046         }
48047         
48048         
48049         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48050         this.tb = tb;
48051         this.tb.add(
48052            {
48053                 cls : ' x-signature-btn x-signature-'+id,
48054                 scope: editor, // was editor...
48055                 handler: this.reset,
48056                 clickEvent:'mousedown',
48057                 text: this.labels.clear
48058             },
48059             {
48060                  xtype : 'Fill',
48061                  xns: Roo.Toolbar
48062             }, 
48063             {
48064                 cls : '  x-signature-btn x-signature-'+id,
48065                 scope: editor, // was editor...
48066                 handler: this.confirmHandler,
48067                 clickEvent:'mousedown',
48068                 text: this.labels.confirm
48069             }
48070         );
48071     
48072     },
48073     //public
48074     /**
48075      * when user is clicked confirm then show this image.....
48076      * 
48077      * @return {String} Image Data URI
48078      */
48079     getImageDataURI : function(){
48080         var svg = this.svgEl.dom.parentNode.innerHTML;
48081         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
48082         return src; 
48083     },
48084     /**
48085      * 
48086      * @return {Boolean} this.isConfirmed
48087      */
48088     getConfirmed : function(){
48089         return this.isConfirmed;
48090     },
48091     /**
48092      * 
48093      * @return {Number} this.width
48094      */
48095     getWidth : function(){
48096         return this.width;
48097     },
48098     /**
48099      * 
48100      * @return {Number} this.height
48101      */
48102     getHeight : function(){
48103         return this.height;
48104     },
48105     // private
48106     getSignature : function(){
48107         return this.signatureTmp;
48108     },
48109     // private
48110     reset : function(){
48111         this.signatureTmp = '';
48112         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48113         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
48114         this.isConfirmed = false;
48115         Roo.form.Signature.superclass.reset.call(this);
48116     },
48117     setSignature : function(s){
48118         this.signatureTmp = s;
48119         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48120         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
48121         this.setValue(s);
48122         this.isConfirmed = false;
48123         Roo.form.Signature.superclass.reset.call(this);
48124     }, 
48125     test : function(){
48126 //        Roo.log(this.signPanel.dom.contentWindow.up())
48127     },
48128     //private
48129     setConfirmed : function(){
48130         
48131         
48132         
48133 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
48134     },
48135     // private
48136     confirmHandler : function(){
48137         if(!this.getSignature()){
48138             return;
48139         }
48140         
48141         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
48142         this.setValue(this.getSignature());
48143         this.isConfirmed = true;
48144         
48145         this.fireEvent('confirm', this);
48146     },
48147     // private
48148     // Subclasses should provide the validation implementation by overriding this
48149     validateValue : function(value){
48150         if(this.allowBlank){
48151             return true;
48152         }
48153         
48154         if(this.isConfirmed){
48155             return true;
48156         }
48157         return false;
48158     }
48159 });/*
48160  * Based on:
48161  * Ext JS Library 1.1.1
48162  * Copyright(c) 2006-2007, Ext JS, LLC.
48163  *
48164  * Originally Released Under LGPL - original licence link has changed is not relivant.
48165  *
48166  * Fork - LGPL
48167  * <script type="text/javascript">
48168  */
48169  
48170
48171 /**
48172  * @class Roo.form.ComboBox
48173  * @extends Roo.form.TriggerField
48174  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48175  * @constructor
48176  * Create a new ComboBox.
48177  * @param {Object} config Configuration options
48178  */
48179 Roo.form.Select = function(config){
48180     Roo.form.Select.superclass.constructor.call(this, config);
48181      
48182 };
48183
48184 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48185     /**
48186      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48187      */
48188     /**
48189      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48190      * rendering into an Roo.Editor, defaults to false)
48191      */
48192     /**
48193      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48194      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48195      */
48196     /**
48197      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48198      */
48199     /**
48200      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48201      * the dropdown list (defaults to undefined, with no header element)
48202      */
48203
48204      /**
48205      * @cfg {String/Roo.Template} tpl The template to use to render the output
48206      */
48207      
48208     // private
48209     defaultAutoCreate : {tag: "select"  },
48210     /**
48211      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48212      */
48213     listWidth: undefined,
48214     /**
48215      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48216      * mode = 'remote' or 'text' if mode = 'local')
48217      */
48218     displayField: undefined,
48219     /**
48220      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48221      * mode = 'remote' or 'value' if mode = 'local'). 
48222      * Note: use of a valueField requires the user make a selection
48223      * in order for a value to be mapped.
48224      */
48225     valueField: undefined,
48226     
48227     
48228     /**
48229      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48230      * field's data value (defaults to the underlying DOM element's name)
48231      */
48232     hiddenName: undefined,
48233     /**
48234      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48235      */
48236     listClass: '',
48237     /**
48238      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48239      */
48240     selectedClass: 'x-combo-selected',
48241     /**
48242      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48243      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48244      * which displays a downward arrow icon).
48245      */
48246     triggerClass : 'x-form-arrow-trigger',
48247     /**
48248      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48249      */
48250     shadow:'sides',
48251     /**
48252      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48253      * anchor positions (defaults to 'tl-bl')
48254      */
48255     listAlign: 'tl-bl?',
48256     /**
48257      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48258      */
48259     maxHeight: 300,
48260     /**
48261      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48262      * query specified by the allQuery config option (defaults to 'query')
48263      */
48264     triggerAction: 'query',
48265     /**
48266      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48267      * (defaults to 4, does not apply if editable = false)
48268      */
48269     minChars : 4,
48270     /**
48271      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48272      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48273      */
48274     typeAhead: false,
48275     /**
48276      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48277      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48278      */
48279     queryDelay: 500,
48280     /**
48281      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48282      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48283      */
48284     pageSize: 0,
48285     /**
48286      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48287      * when editable = true (defaults to false)
48288      */
48289     selectOnFocus:false,
48290     /**
48291      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48292      */
48293     queryParam: 'query',
48294     /**
48295      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48296      * when mode = 'remote' (defaults to 'Loading...')
48297      */
48298     loadingText: 'Loading...',
48299     /**
48300      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48301      */
48302     resizable: false,
48303     /**
48304      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48305      */
48306     handleHeight : 8,
48307     /**
48308      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48309      * traditional select (defaults to true)
48310      */
48311     editable: true,
48312     /**
48313      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48314      */
48315     allQuery: '',
48316     /**
48317      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48318      */
48319     mode: 'remote',
48320     /**
48321      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48322      * listWidth has a higher value)
48323      */
48324     minListWidth : 70,
48325     /**
48326      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48327      * allow the user to set arbitrary text into the field (defaults to false)
48328      */
48329     forceSelection:false,
48330     /**
48331      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48332      * if typeAhead = true (defaults to 250)
48333      */
48334     typeAheadDelay : 250,
48335     /**
48336      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48337      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48338      */
48339     valueNotFoundText : undefined,
48340     
48341     /**
48342      * @cfg {String} defaultValue The value displayed after loading the store.
48343      */
48344     defaultValue: '',
48345     
48346     /**
48347      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48348      */
48349     blockFocus : false,
48350     
48351     /**
48352      * @cfg {Boolean} disableClear Disable showing of clear button.
48353      */
48354     disableClear : false,
48355     /**
48356      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48357      */
48358     alwaysQuery : false,
48359     
48360     //private
48361     addicon : false,
48362     editicon: false,
48363     
48364     // element that contains real text value.. (when hidden is used..)
48365      
48366     // private
48367     onRender : function(ct, position){
48368         Roo.form.Field.prototype.onRender.call(this, ct, position);
48369         
48370         if(this.store){
48371             this.store.on('beforeload', this.onBeforeLoad, this);
48372             this.store.on('load', this.onLoad, this);
48373             this.store.on('loadexception', this.onLoadException, this);
48374             this.store.load({});
48375         }
48376         
48377         
48378         
48379     },
48380
48381     // private
48382     initEvents : function(){
48383         //Roo.form.ComboBox.superclass.initEvents.call(this);
48384  
48385     },
48386
48387     onDestroy : function(){
48388        
48389         if(this.store){
48390             this.store.un('beforeload', this.onBeforeLoad, this);
48391             this.store.un('load', this.onLoad, this);
48392             this.store.un('loadexception', this.onLoadException, this);
48393         }
48394         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48395     },
48396
48397     // private
48398     fireKey : function(e){
48399         if(e.isNavKeyPress() && !this.list.isVisible()){
48400             this.fireEvent("specialkey", this, e);
48401         }
48402     },
48403
48404     // private
48405     onResize: function(w, h){
48406         
48407         return; 
48408     
48409         
48410     },
48411
48412     /**
48413      * Allow or prevent the user from directly editing the field text.  If false is passed,
48414      * the user will only be able to select from the items defined in the dropdown list.  This method
48415      * is the runtime equivalent of setting the 'editable' config option at config time.
48416      * @param {Boolean} value True to allow the user to directly edit the field text
48417      */
48418     setEditable : function(value){
48419          
48420     },
48421
48422     // private
48423     onBeforeLoad : function(){
48424         
48425         Roo.log("Select before load");
48426         return;
48427     
48428         this.innerList.update(this.loadingText ?
48429                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48430         //this.restrictHeight();
48431         this.selectedIndex = -1;
48432     },
48433
48434     // private
48435     onLoad : function(){
48436
48437     
48438         var dom = this.el.dom;
48439         dom.innerHTML = '';
48440          var od = dom.ownerDocument;
48441          
48442         if (this.emptyText) {
48443             var op = od.createElement('option');
48444             op.setAttribute('value', '');
48445             op.innerHTML = String.format('{0}', this.emptyText);
48446             dom.appendChild(op);
48447         }
48448         if(this.store.getCount() > 0){
48449            
48450             var vf = this.valueField;
48451             var df = this.displayField;
48452             this.store.data.each(function(r) {
48453                 // which colmsn to use... testing - cdoe / title..
48454                 var op = od.createElement('option');
48455                 op.setAttribute('value', r.data[vf]);
48456                 op.innerHTML = String.format('{0}', r.data[df]);
48457                 dom.appendChild(op);
48458             });
48459             if (typeof(this.defaultValue != 'undefined')) {
48460                 this.setValue(this.defaultValue);
48461             }
48462             
48463              
48464         }else{
48465             //this.onEmptyResults();
48466         }
48467         //this.el.focus();
48468     },
48469     // private
48470     onLoadException : function()
48471     {
48472         dom.innerHTML = '';
48473             
48474         Roo.log("Select on load exception");
48475         return;
48476     
48477         this.collapse();
48478         Roo.log(this.store.reader.jsonData);
48479         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48480             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48481         }
48482         
48483         
48484     },
48485     // private
48486     onTypeAhead : function(){
48487          
48488     },
48489
48490     // private
48491     onSelect : function(record, index){
48492         Roo.log('on select?');
48493         return;
48494         if(this.fireEvent('beforeselect', this, record, index) !== false){
48495             this.setFromData(index > -1 ? record.data : false);
48496             this.collapse();
48497             this.fireEvent('select', this, record, index);
48498         }
48499     },
48500
48501     /**
48502      * Returns the currently selected field value or empty string if no value is set.
48503      * @return {String} value The selected value
48504      */
48505     getValue : function(){
48506         var dom = this.el.dom;
48507         this.value = dom.options[dom.selectedIndex].value;
48508         return this.value;
48509         
48510     },
48511
48512     /**
48513      * Clears any text/value currently set in the field
48514      */
48515     clearValue : function(){
48516         this.value = '';
48517         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48518         
48519     },
48520
48521     /**
48522      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48523      * will be displayed in the field.  If the value does not match the data value of an existing item,
48524      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48525      * Otherwise the field will be blank (although the value will still be set).
48526      * @param {String} value The value to match
48527      */
48528     setValue : function(v){
48529         var d = this.el.dom;
48530         for (var i =0; i < d.options.length;i++) {
48531             if (v == d.options[i].value) {
48532                 d.selectedIndex = i;
48533                 this.value = v;
48534                 return;
48535             }
48536         }
48537         this.clearValue();
48538     },
48539     /**
48540      * @property {Object} the last set data for the element
48541      */
48542     
48543     lastData : false,
48544     /**
48545      * Sets the value of the field based on a object which is related to the record format for the store.
48546      * @param {Object} value the value to set as. or false on reset?
48547      */
48548     setFromData : function(o){
48549         Roo.log('setfrom data?');
48550          
48551         
48552         
48553     },
48554     // private
48555     reset : function(){
48556         this.clearValue();
48557     },
48558     // private
48559     findRecord : function(prop, value){
48560         
48561         return false;
48562     
48563         var record;
48564         if(this.store.getCount() > 0){
48565             this.store.each(function(r){
48566                 if(r.data[prop] == value){
48567                     record = r;
48568                     return false;
48569                 }
48570                 return true;
48571             });
48572         }
48573         return record;
48574     },
48575     
48576     getName: function()
48577     {
48578         // returns hidden if it's set..
48579         if (!this.rendered) {return ''};
48580         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48581         
48582     },
48583      
48584
48585     
48586
48587     // private
48588     onEmptyResults : function(){
48589         Roo.log('empty results');
48590         //this.collapse();
48591     },
48592
48593     /**
48594      * Returns true if the dropdown list is expanded, else false.
48595      */
48596     isExpanded : function(){
48597         return false;
48598     },
48599
48600     /**
48601      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48602      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48603      * @param {String} value The data value of the item to select
48604      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48605      * selected item if it is not currently in view (defaults to true)
48606      * @return {Boolean} True if the value matched an item in the list, else false
48607      */
48608     selectByValue : function(v, scrollIntoView){
48609         Roo.log('select By Value');
48610         return false;
48611     
48612         if(v !== undefined && v !== null){
48613             var r = this.findRecord(this.valueField || this.displayField, v);
48614             if(r){
48615                 this.select(this.store.indexOf(r), scrollIntoView);
48616                 return true;
48617             }
48618         }
48619         return false;
48620     },
48621
48622     /**
48623      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48624      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48625      * @param {Number} index The zero-based index of the list item to select
48626      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48627      * selected item if it is not currently in view (defaults to true)
48628      */
48629     select : function(index, scrollIntoView){
48630         Roo.log('select ');
48631         return  ;
48632         
48633         this.selectedIndex = index;
48634         this.view.select(index);
48635         if(scrollIntoView !== false){
48636             var el = this.view.getNode(index);
48637             if(el){
48638                 this.innerList.scrollChildIntoView(el, false);
48639             }
48640         }
48641     },
48642
48643       
48644
48645     // private
48646     validateBlur : function(){
48647         
48648         return;
48649         
48650     },
48651
48652     // private
48653     initQuery : function(){
48654         this.doQuery(this.getRawValue());
48655     },
48656
48657     // private
48658     doForce : function(){
48659         if(this.el.dom.value.length > 0){
48660             this.el.dom.value =
48661                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48662              
48663         }
48664     },
48665
48666     /**
48667      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48668      * query allowing the query action to be canceled if needed.
48669      * @param {String} query The SQL query to execute
48670      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48671      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48672      * saved in the current store (defaults to false)
48673      */
48674     doQuery : function(q, forceAll){
48675         
48676         Roo.log('doQuery?');
48677         if(q === undefined || q === null){
48678             q = '';
48679         }
48680         var qe = {
48681             query: q,
48682             forceAll: forceAll,
48683             combo: this,
48684             cancel:false
48685         };
48686         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48687             return false;
48688         }
48689         q = qe.query;
48690         forceAll = qe.forceAll;
48691         if(forceAll === true || (q.length >= this.minChars)){
48692             if(this.lastQuery != q || this.alwaysQuery){
48693                 this.lastQuery = q;
48694                 if(this.mode == 'local'){
48695                     this.selectedIndex = -1;
48696                     if(forceAll){
48697                         this.store.clearFilter();
48698                     }else{
48699                         this.store.filter(this.displayField, q);
48700                     }
48701                     this.onLoad();
48702                 }else{
48703                     this.store.baseParams[this.queryParam] = q;
48704                     this.store.load({
48705                         params: this.getParams(q)
48706                     });
48707                     this.expand();
48708                 }
48709             }else{
48710                 this.selectedIndex = -1;
48711                 this.onLoad();   
48712             }
48713         }
48714     },
48715
48716     // private
48717     getParams : function(q){
48718         var p = {};
48719         //p[this.queryParam] = q;
48720         if(this.pageSize){
48721             p.start = 0;
48722             p.limit = this.pageSize;
48723         }
48724         return p;
48725     },
48726
48727     /**
48728      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48729      */
48730     collapse : function(){
48731         
48732     },
48733
48734     // private
48735     collapseIf : function(e){
48736         
48737     },
48738
48739     /**
48740      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48741      */
48742     expand : function(){
48743         
48744     } ,
48745
48746     // private
48747      
48748
48749     /** 
48750     * @cfg {Boolean} grow 
48751     * @hide 
48752     */
48753     /** 
48754     * @cfg {Number} growMin 
48755     * @hide 
48756     */
48757     /** 
48758     * @cfg {Number} growMax 
48759     * @hide 
48760     */
48761     /**
48762      * @hide
48763      * @method autoSize
48764      */
48765     
48766     setWidth : function()
48767     {
48768         
48769     },
48770     getResizeEl : function(){
48771         return this.el;
48772     }
48773 });//<script type="text/javasscript">
48774  
48775
48776 /**
48777  * @class Roo.DDView
48778  * A DnD enabled version of Roo.View.
48779  * @param {Element/String} container The Element in which to create the View.
48780  * @param {String} tpl The template string used to create the markup for each element of the View
48781  * @param {Object} config The configuration properties. These include all the config options of
48782  * {@link Roo.View} plus some specific to this class.<br>
48783  * <p>
48784  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48785  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48786  * <p>
48787  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48788 .x-view-drag-insert-above {
48789         border-top:1px dotted #3366cc;
48790 }
48791 .x-view-drag-insert-below {
48792         border-bottom:1px dotted #3366cc;
48793 }
48794 </code></pre>
48795  * 
48796  */
48797  
48798 Roo.DDView = function(container, tpl, config) {
48799     Roo.DDView.superclass.constructor.apply(this, arguments);
48800     this.getEl().setStyle("outline", "0px none");
48801     this.getEl().unselectable();
48802     if (this.dragGroup) {
48803                 this.setDraggable(this.dragGroup.split(","));
48804     }
48805     if (this.dropGroup) {
48806                 this.setDroppable(this.dropGroup.split(","));
48807     }
48808     if (this.deletable) {
48809         this.setDeletable();
48810     }
48811     this.isDirtyFlag = false;
48812         this.addEvents({
48813                 "drop" : true
48814         });
48815 };
48816
48817 Roo.extend(Roo.DDView, Roo.View, {
48818 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48819 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48820 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48821 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48822
48823         isFormField: true,
48824
48825         reset: Roo.emptyFn,
48826         
48827         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48828
48829         validate: function() {
48830                 return true;
48831         },
48832         
48833         destroy: function() {
48834                 this.purgeListeners();
48835                 this.getEl.removeAllListeners();
48836                 this.getEl().remove();
48837                 if (this.dragZone) {
48838                         if (this.dragZone.destroy) {
48839                                 this.dragZone.destroy();
48840                         }
48841                 }
48842                 if (this.dropZone) {
48843                         if (this.dropZone.destroy) {
48844                                 this.dropZone.destroy();
48845                         }
48846                 }
48847         },
48848
48849 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48850         getName: function() {
48851                 return this.name;
48852         },
48853
48854 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48855         setValue: function(v) {
48856                 if (!this.store) {
48857                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48858                 }
48859                 var data = {};
48860                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48861                 this.store.proxy = new Roo.data.MemoryProxy(data);
48862                 this.store.load();
48863         },
48864
48865 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48866         getValue: function() {
48867                 var result = '(';
48868                 this.store.each(function(rec) {
48869                         result += rec.id + ',';
48870                 });
48871                 return result.substr(0, result.length - 1) + ')';
48872         },
48873         
48874         getIds: function() {
48875                 var i = 0, result = new Array(this.store.getCount());
48876                 this.store.each(function(rec) {
48877                         result[i++] = rec.id;
48878                 });
48879                 return result;
48880         },
48881         
48882         isDirty: function() {
48883                 return this.isDirtyFlag;
48884         },
48885
48886 /**
48887  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48888  *      whole Element becomes the target, and this causes the drop gesture to append.
48889  */
48890     getTargetFromEvent : function(e) {
48891                 var target = e.getTarget();
48892                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48893                 target = target.parentNode;
48894                 }
48895                 if (!target) {
48896                         target = this.el.dom.lastChild || this.el.dom;
48897                 }
48898                 return target;
48899     },
48900
48901 /**
48902  *      Create the drag data which consists of an object which has the property "ddel" as
48903  *      the drag proxy element. 
48904  */
48905     getDragData : function(e) {
48906         var target = this.findItemFromChild(e.getTarget());
48907                 if(target) {
48908                         this.handleSelection(e);
48909                         var selNodes = this.getSelectedNodes();
48910             var dragData = {
48911                 source: this,
48912                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48913                 nodes: selNodes,
48914                 records: []
48915                         };
48916                         var selectedIndices = this.getSelectedIndexes();
48917                         for (var i = 0; i < selectedIndices.length; i++) {
48918                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48919                         }
48920                         if (selNodes.length == 1) {
48921                                 dragData.ddel = target.cloneNode(true); // the div element
48922                         } else {
48923                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48924                                 div.className = 'multi-proxy';
48925                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48926                                         div.appendChild(selNodes[i].cloneNode(true));
48927                                 }
48928                                 dragData.ddel = div;
48929                         }
48930             //console.log(dragData)
48931             //console.log(dragData.ddel.innerHTML)
48932                         return dragData;
48933                 }
48934         //console.log('nodragData')
48935                 return false;
48936     },
48937     
48938 /**     Specify to which ddGroup items in this DDView may be dragged. */
48939     setDraggable: function(ddGroup) {
48940         if (ddGroup instanceof Array) {
48941                 Roo.each(ddGroup, this.setDraggable, this);
48942                 return;
48943         }
48944         if (this.dragZone) {
48945                 this.dragZone.addToGroup(ddGroup);
48946         } else {
48947                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48948                                 containerScroll: true,
48949                                 ddGroup: ddGroup 
48950
48951                         });
48952 //                      Draggability implies selection. DragZone's mousedown selects the element.
48953                         if (!this.multiSelect) { this.singleSelect = true; }
48954
48955 //                      Wire the DragZone's handlers up to methods in *this*
48956                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48957                 }
48958     },
48959
48960 /**     Specify from which ddGroup this DDView accepts drops. */
48961     setDroppable: function(ddGroup) {
48962         if (ddGroup instanceof Array) {
48963                 Roo.each(ddGroup, this.setDroppable, this);
48964                 return;
48965         }
48966         if (this.dropZone) {
48967                 this.dropZone.addToGroup(ddGroup);
48968         } else {
48969                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48970                                 containerScroll: true,
48971                                 ddGroup: ddGroup
48972                         });
48973
48974 //                      Wire the DropZone's handlers up to methods in *this*
48975                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48976                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48977                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48978                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48979                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48980                 }
48981     },
48982
48983 /**     Decide whether to drop above or below a View node. */
48984     getDropPoint : function(e, n, dd){
48985         if (n == this.el.dom) { return "above"; }
48986                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48987                 var c = t + (b - t) / 2;
48988                 var y = Roo.lib.Event.getPageY(e);
48989                 if(y <= c) {
48990                         return "above";
48991                 }else{
48992                         return "below";
48993                 }
48994     },
48995
48996     onNodeEnter : function(n, dd, e, data){
48997                 return false;
48998     },
48999     
49000     onNodeOver : function(n, dd, e, data){
49001                 var pt = this.getDropPoint(e, n, dd);
49002                 // set the insert point style on the target node
49003                 var dragElClass = this.dropNotAllowed;
49004                 if (pt) {
49005                         var targetElClass;
49006                         if (pt == "above"){
49007                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
49008                                 targetElClass = "x-view-drag-insert-above";
49009                         } else {
49010                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
49011                                 targetElClass = "x-view-drag-insert-below";
49012                         }
49013                         if (this.lastInsertClass != targetElClass){
49014                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
49015                                 this.lastInsertClass = targetElClass;
49016                         }
49017                 }
49018                 return dragElClass;
49019         },
49020
49021     onNodeOut : function(n, dd, e, data){
49022                 this.removeDropIndicators(n);
49023     },
49024
49025     onNodeDrop : function(n, dd, e, data){
49026         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
49027                 return false;
49028         }
49029         var pt = this.getDropPoint(e, n, dd);
49030                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
49031                 if (pt == "below") { insertAt++; }
49032                 for (var i = 0; i < data.records.length; i++) {
49033                         var r = data.records[i];
49034                         var dup = this.store.getById(r.id);
49035                         if (dup && (dd != this.dragZone)) {
49036                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
49037                         } else {
49038                                 if (data.copy) {
49039                                         this.store.insert(insertAt++, r.copy());
49040                                 } else {
49041                                         data.source.isDirtyFlag = true;
49042                                         r.store.remove(r);
49043                                         this.store.insert(insertAt++, r);
49044                                 }
49045                                 this.isDirtyFlag = true;
49046                         }
49047                 }
49048                 this.dragZone.cachedTarget = null;
49049                 return true;
49050     },
49051
49052     removeDropIndicators : function(n){
49053                 if(n){
49054                         Roo.fly(n).removeClass([
49055                                 "x-view-drag-insert-above",
49056                                 "x-view-drag-insert-below"]);
49057                         this.lastInsertClass = "_noclass";
49058                 }
49059     },
49060
49061 /**
49062  *      Utility method. Add a delete option to the DDView's context menu.
49063  *      @param {String} imageUrl The URL of the "delete" icon image.
49064  */
49065         setDeletable: function(imageUrl) {
49066                 if (!this.singleSelect && !this.multiSelect) {
49067                         this.singleSelect = true;
49068                 }
49069                 var c = this.getContextMenu();
49070                 this.contextMenu.on("itemclick", function(item) {
49071                         switch (item.id) {
49072                                 case "delete":
49073                                         this.remove(this.getSelectedIndexes());
49074                                         break;
49075                         }
49076                 }, this);
49077                 this.contextMenu.add({
49078                         icon: imageUrl,
49079                         id: "delete",
49080                         text: 'Delete'
49081                 });
49082         },
49083         
49084 /**     Return the context menu for this DDView. */
49085         getContextMenu: function() {
49086                 if (!this.contextMenu) {
49087 //                      Create the View's context menu
49088                         this.contextMenu = new Roo.menu.Menu({
49089                                 id: this.id + "-contextmenu"
49090                         });
49091                         this.el.on("contextmenu", this.showContextMenu, this);
49092                 }
49093                 return this.contextMenu;
49094         },
49095         
49096         disableContextMenu: function() {
49097                 if (this.contextMenu) {
49098                         this.el.un("contextmenu", this.showContextMenu, this);
49099                 }
49100         },
49101
49102         showContextMenu: function(e, item) {
49103         item = this.findItemFromChild(e.getTarget());
49104                 if (item) {
49105                         e.stopEvent();
49106                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
49107                         this.contextMenu.showAt(e.getXY());
49108             }
49109     },
49110
49111 /**
49112  *      Remove {@link Roo.data.Record}s at the specified indices.
49113  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
49114  */
49115     remove: function(selectedIndices) {
49116                 selectedIndices = [].concat(selectedIndices);
49117                 for (var i = 0; i < selectedIndices.length; i++) {
49118                         var rec = this.store.getAt(selectedIndices[i]);
49119                         this.store.remove(rec);
49120                 }
49121     },
49122
49123 /**
49124  *      Double click fires the event, but also, if this is draggable, and there is only one other
49125  *      related DropZone, it transfers the selected node.
49126  */
49127     onDblClick : function(e){
49128         var item = this.findItemFromChild(e.getTarget());
49129         if(item){
49130             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
49131                 return false;
49132             }
49133             if (this.dragGroup) {
49134                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
49135                     while (targets.indexOf(this.dropZone) > -1) {
49136                             targets.remove(this.dropZone);
49137                                 }
49138                     if (targets.length == 1) {
49139                                         this.dragZone.cachedTarget = null;
49140                         var el = Roo.get(targets[0].getEl());
49141                         var box = el.getBox(true);
49142                         targets[0].onNodeDrop(el.dom, {
49143                                 target: el.dom,
49144                                 xy: [box.x, box.y + box.height - 1]
49145                         }, null, this.getDragData(e));
49146                     }
49147                 }
49148         }
49149     },
49150     
49151     handleSelection: function(e) {
49152                 this.dragZone.cachedTarget = null;
49153         var item = this.findItemFromChild(e.getTarget());
49154         if (!item) {
49155                 this.clearSelections(true);
49156                 return;
49157         }
49158                 if (item && (this.multiSelect || this.singleSelect)){
49159                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49160                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49161                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49162                                 this.unselect(item);
49163                         } else {
49164                                 this.select(item, this.multiSelect && e.ctrlKey);
49165                                 this.lastSelection = item;
49166                         }
49167                 }
49168     },
49169
49170     onItemClick : function(item, index, e){
49171                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49172                         return false;
49173                 }
49174                 return true;
49175     },
49176
49177     unselect : function(nodeInfo, suppressEvent){
49178                 var node = this.getNode(nodeInfo);
49179                 if(node && this.isSelected(node)){
49180                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49181                                 Roo.fly(node).removeClass(this.selectedClass);
49182                                 this.selections.remove(node);
49183                                 if(!suppressEvent){
49184                                         this.fireEvent("selectionchange", this, this.selections);
49185                                 }
49186                         }
49187                 }
49188     }
49189 });
49190 /*
49191  * Based on:
49192  * Ext JS Library 1.1.1
49193  * Copyright(c) 2006-2007, Ext JS, LLC.
49194  *
49195  * Originally Released Under LGPL - original licence link has changed is not relivant.
49196  *
49197  * Fork - LGPL
49198  * <script type="text/javascript">
49199  */
49200  
49201 /**
49202  * @class Roo.LayoutManager
49203  * @extends Roo.util.Observable
49204  * Base class for layout managers.
49205  */
49206 Roo.LayoutManager = function(container, config){
49207     Roo.LayoutManager.superclass.constructor.call(this);
49208     this.el = Roo.get(container);
49209     // ie scrollbar fix
49210     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49211         document.body.scroll = "no";
49212     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49213         this.el.position('relative');
49214     }
49215     this.id = this.el.id;
49216     this.el.addClass("x-layout-container");
49217     /** false to disable window resize monitoring @type Boolean */
49218     this.monitorWindowResize = true;
49219     this.regions = {};
49220     this.addEvents({
49221         /**
49222          * @event layout
49223          * Fires when a layout is performed. 
49224          * @param {Roo.LayoutManager} this
49225          */
49226         "layout" : true,
49227         /**
49228          * @event regionresized
49229          * Fires when the user resizes a region. 
49230          * @param {Roo.LayoutRegion} region The resized region
49231          * @param {Number} newSize The new size (width for east/west, height for north/south)
49232          */
49233         "regionresized" : true,
49234         /**
49235          * @event regioncollapsed
49236          * Fires when a region is collapsed. 
49237          * @param {Roo.LayoutRegion} region The collapsed region
49238          */
49239         "regioncollapsed" : true,
49240         /**
49241          * @event regionexpanded
49242          * Fires when a region is expanded.  
49243          * @param {Roo.LayoutRegion} region The expanded region
49244          */
49245         "regionexpanded" : true
49246     });
49247     this.updating = false;
49248     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49249 };
49250
49251 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49252     /**
49253      * Returns true if this layout is currently being updated
49254      * @return {Boolean}
49255      */
49256     isUpdating : function(){
49257         return this.updating; 
49258     },
49259     
49260     /**
49261      * Suspend the LayoutManager from doing auto-layouts while
49262      * making multiple add or remove calls
49263      */
49264     beginUpdate : function(){
49265         this.updating = true;    
49266     },
49267     
49268     /**
49269      * Restore auto-layouts and optionally disable the manager from performing a layout
49270      * @param {Boolean} noLayout true to disable a layout update 
49271      */
49272     endUpdate : function(noLayout){
49273         this.updating = false;
49274         if(!noLayout){
49275             this.layout();
49276         }    
49277     },
49278     
49279     layout: function(){
49280         
49281     },
49282     
49283     onRegionResized : function(region, newSize){
49284         this.fireEvent("regionresized", region, newSize);
49285         this.layout();
49286     },
49287     
49288     onRegionCollapsed : function(region){
49289         this.fireEvent("regioncollapsed", region);
49290     },
49291     
49292     onRegionExpanded : function(region){
49293         this.fireEvent("regionexpanded", region);
49294     },
49295         
49296     /**
49297      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49298      * performs box-model adjustments.
49299      * @return {Object} The size as an object {width: (the width), height: (the height)}
49300      */
49301     getViewSize : function(){
49302         var size;
49303         if(this.el.dom != document.body){
49304             size = this.el.getSize();
49305         }else{
49306             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49307         }
49308         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49309         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49310         return size;
49311     },
49312     
49313     /**
49314      * Returns the Element this layout is bound to.
49315      * @return {Roo.Element}
49316      */
49317     getEl : function(){
49318         return this.el;
49319     },
49320     
49321     /**
49322      * Returns the specified region.
49323      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49324      * @return {Roo.LayoutRegion}
49325      */
49326     getRegion : function(target){
49327         return this.regions[target.toLowerCase()];
49328     },
49329     
49330     onWindowResize : function(){
49331         if(this.monitorWindowResize){
49332             this.layout();
49333         }
49334     }
49335 });/*
49336  * Based on:
49337  * Ext JS Library 1.1.1
49338  * Copyright(c) 2006-2007, Ext JS, LLC.
49339  *
49340  * Originally Released Under LGPL - original licence link has changed is not relivant.
49341  *
49342  * Fork - LGPL
49343  * <script type="text/javascript">
49344  */
49345 /**
49346  * @class Roo.BorderLayout
49347  * @extends Roo.LayoutManager
49348  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49349  * please see: <br><br>
49350  * <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>
49351  * <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>
49352  * Example:
49353  <pre><code>
49354  var layout = new Roo.BorderLayout(document.body, {
49355     north: {
49356         initialSize: 25,
49357         titlebar: false
49358     },
49359     west: {
49360         split:true,
49361         initialSize: 200,
49362         minSize: 175,
49363         maxSize: 400,
49364         titlebar: true,
49365         collapsible: true
49366     },
49367     east: {
49368         split:true,
49369         initialSize: 202,
49370         minSize: 175,
49371         maxSize: 400,
49372         titlebar: true,
49373         collapsible: true
49374     },
49375     south: {
49376         split:true,
49377         initialSize: 100,
49378         minSize: 100,
49379         maxSize: 200,
49380         titlebar: true,
49381         collapsible: true
49382     },
49383     center: {
49384         titlebar: true,
49385         autoScroll:true,
49386         resizeTabs: true,
49387         minTabWidth: 50,
49388         preferredTabWidth: 150
49389     }
49390 });
49391
49392 // shorthand
49393 var CP = Roo.ContentPanel;
49394
49395 layout.beginUpdate();
49396 layout.add("north", new CP("north", "North"));
49397 layout.add("south", new CP("south", {title: "South", closable: true}));
49398 layout.add("west", new CP("west", {title: "West"}));
49399 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49400 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49401 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49402 layout.getRegion("center").showPanel("center1");
49403 layout.endUpdate();
49404 </code></pre>
49405
49406 <b>The container the layout is rendered into can be either the body element or any other element.
49407 If it is not the body element, the container needs to either be an absolute positioned element,
49408 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49409 the container size if it is not the body element.</b>
49410
49411 * @constructor
49412 * Create a new BorderLayout
49413 * @param {String/HTMLElement/Element} container The container this layout is bound to
49414 * @param {Object} config Configuration options
49415  */
49416 Roo.BorderLayout = function(container, config){
49417     config = config || {};
49418     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49419     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49420     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49421         var target = this.factory.validRegions[i];
49422         if(config[target]){
49423             this.addRegion(target, config[target]);
49424         }
49425     }
49426 };
49427
49428 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49429     /**
49430      * Creates and adds a new region if it doesn't already exist.
49431      * @param {String} target The target region key (north, south, east, west or center).
49432      * @param {Object} config The regions config object
49433      * @return {BorderLayoutRegion} The new region
49434      */
49435     addRegion : function(target, config){
49436         if(!this.regions[target]){
49437             var r = this.factory.create(target, this, config);
49438             this.bindRegion(target, r);
49439         }
49440         return this.regions[target];
49441     },
49442
49443     // private (kinda)
49444     bindRegion : function(name, r){
49445         this.regions[name] = r;
49446         r.on("visibilitychange", this.layout, this);
49447         r.on("paneladded", this.layout, this);
49448         r.on("panelremoved", this.layout, this);
49449         r.on("invalidated", this.layout, this);
49450         r.on("resized", this.onRegionResized, this);
49451         r.on("collapsed", this.onRegionCollapsed, this);
49452         r.on("expanded", this.onRegionExpanded, this);
49453     },
49454
49455     /**
49456      * Performs a layout update.
49457      */
49458     layout : function(){
49459         if(this.updating) {
49460             return;
49461         }
49462         var size = this.getViewSize();
49463         var w = size.width;
49464         var h = size.height;
49465         var centerW = w;
49466         var centerH = h;
49467         var centerY = 0;
49468         var centerX = 0;
49469         //var x = 0, y = 0;
49470
49471         var rs = this.regions;
49472         var north = rs["north"];
49473         var south = rs["south"]; 
49474         var west = rs["west"];
49475         var east = rs["east"];
49476         var center = rs["center"];
49477         //if(this.hideOnLayout){ // not supported anymore
49478             //c.el.setStyle("display", "none");
49479         //}
49480         if(north && north.isVisible()){
49481             var b = north.getBox();
49482             var m = north.getMargins();
49483             b.width = w - (m.left+m.right);
49484             b.x = m.left;
49485             b.y = m.top;
49486             centerY = b.height + b.y + m.bottom;
49487             centerH -= centerY;
49488             north.updateBox(this.safeBox(b));
49489         }
49490         if(south && south.isVisible()){
49491             var b = south.getBox();
49492             var m = south.getMargins();
49493             b.width = w - (m.left+m.right);
49494             b.x = m.left;
49495             var totalHeight = (b.height + m.top + m.bottom);
49496             b.y = h - totalHeight + m.top;
49497             centerH -= totalHeight;
49498             south.updateBox(this.safeBox(b));
49499         }
49500         if(west && west.isVisible()){
49501             var b = west.getBox();
49502             var m = west.getMargins();
49503             b.height = centerH - (m.top+m.bottom);
49504             b.x = m.left;
49505             b.y = centerY + m.top;
49506             var totalWidth = (b.width + m.left + m.right);
49507             centerX += totalWidth;
49508             centerW -= totalWidth;
49509             west.updateBox(this.safeBox(b));
49510         }
49511         if(east && east.isVisible()){
49512             var b = east.getBox();
49513             var m = east.getMargins();
49514             b.height = centerH - (m.top+m.bottom);
49515             var totalWidth = (b.width + m.left + m.right);
49516             b.x = w - totalWidth + m.left;
49517             b.y = centerY + m.top;
49518             centerW -= totalWidth;
49519             east.updateBox(this.safeBox(b));
49520         }
49521         if(center){
49522             var m = center.getMargins();
49523             var centerBox = {
49524                 x: centerX + m.left,
49525                 y: centerY + m.top,
49526                 width: centerW - (m.left+m.right),
49527                 height: centerH - (m.top+m.bottom)
49528             };
49529             //if(this.hideOnLayout){
49530                 //center.el.setStyle("display", "block");
49531             //}
49532             center.updateBox(this.safeBox(centerBox));
49533         }
49534         this.el.repaint();
49535         this.fireEvent("layout", this);
49536     },
49537
49538     // private
49539     safeBox : function(box){
49540         box.width = Math.max(0, box.width);
49541         box.height = Math.max(0, box.height);
49542         return box;
49543     },
49544
49545     /**
49546      * Adds a ContentPanel (or subclass) to this layout.
49547      * @param {String} target The target region key (north, south, east, west or center).
49548      * @param {Roo.ContentPanel} panel The panel to add
49549      * @return {Roo.ContentPanel} The added panel
49550      */
49551     add : function(target, panel){
49552          
49553         target = target.toLowerCase();
49554         return this.regions[target].add(panel);
49555     },
49556
49557     /**
49558      * Remove a ContentPanel (or subclass) to this layout.
49559      * @param {String} target The target region key (north, south, east, west or center).
49560      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49561      * @return {Roo.ContentPanel} The removed panel
49562      */
49563     remove : function(target, panel){
49564         target = target.toLowerCase();
49565         return this.regions[target].remove(panel);
49566     },
49567
49568     /**
49569      * Searches all regions for a panel with the specified id
49570      * @param {String} panelId
49571      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49572      */
49573     findPanel : function(panelId){
49574         var rs = this.regions;
49575         for(var target in rs){
49576             if(typeof rs[target] != "function"){
49577                 var p = rs[target].getPanel(panelId);
49578                 if(p){
49579                     return p;
49580                 }
49581             }
49582         }
49583         return null;
49584     },
49585
49586     /**
49587      * Searches all regions for a panel with the specified id and activates (shows) it.
49588      * @param {String/ContentPanel} panelId The panels id or the panel itself
49589      * @return {Roo.ContentPanel} The shown panel or null
49590      */
49591     showPanel : function(panelId) {
49592       var rs = this.regions;
49593       for(var target in rs){
49594          var r = rs[target];
49595          if(typeof r != "function"){
49596             if(r.hasPanel(panelId)){
49597                return r.showPanel(panelId);
49598             }
49599          }
49600       }
49601       return null;
49602    },
49603
49604    /**
49605      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49606      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49607      */
49608     restoreState : function(provider){
49609         if(!provider){
49610             provider = Roo.state.Manager;
49611         }
49612         var sm = new Roo.LayoutStateManager();
49613         sm.init(this, provider);
49614     },
49615
49616     /**
49617      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49618      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49619      * a valid ContentPanel config object.  Example:
49620      * <pre><code>
49621 // Create the main layout
49622 var layout = new Roo.BorderLayout('main-ct', {
49623     west: {
49624         split:true,
49625         minSize: 175,
49626         titlebar: true
49627     },
49628     center: {
49629         title:'Components'
49630     }
49631 }, 'main-ct');
49632
49633 // Create and add multiple ContentPanels at once via configs
49634 layout.batchAdd({
49635    west: {
49636        id: 'source-files',
49637        autoCreate:true,
49638        title:'Ext Source Files',
49639        autoScroll:true,
49640        fitToFrame:true
49641    },
49642    center : {
49643        el: cview,
49644        autoScroll:true,
49645        fitToFrame:true,
49646        toolbar: tb,
49647        resizeEl:'cbody'
49648    }
49649 });
49650 </code></pre>
49651      * @param {Object} regions An object containing ContentPanel configs by region name
49652      */
49653     batchAdd : function(regions){
49654         this.beginUpdate();
49655         for(var rname in regions){
49656             var lr = this.regions[rname];
49657             if(lr){
49658                 this.addTypedPanels(lr, regions[rname]);
49659             }
49660         }
49661         this.endUpdate();
49662     },
49663
49664     // private
49665     addTypedPanels : function(lr, ps){
49666         if(typeof ps == 'string'){
49667             lr.add(new Roo.ContentPanel(ps));
49668         }
49669         else if(ps instanceof Array){
49670             for(var i =0, len = ps.length; i < len; i++){
49671                 this.addTypedPanels(lr, ps[i]);
49672             }
49673         }
49674         else if(!ps.events){ // raw config?
49675             var el = ps.el;
49676             delete ps.el; // prevent conflict
49677             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49678         }
49679         else {  // panel object assumed!
49680             lr.add(ps);
49681         }
49682     },
49683     /**
49684      * Adds a xtype elements to the layout.
49685      * <pre><code>
49686
49687 layout.addxtype({
49688        xtype : 'ContentPanel',
49689        region: 'west',
49690        items: [ .... ]
49691    }
49692 );
49693
49694 layout.addxtype({
49695         xtype : 'NestedLayoutPanel',
49696         region: 'west',
49697         layout: {
49698            center: { },
49699            west: { }   
49700         },
49701         items : [ ... list of content panels or nested layout panels.. ]
49702    }
49703 );
49704 </code></pre>
49705      * @param {Object} cfg Xtype definition of item to add.
49706      */
49707     addxtype : function(cfg)
49708     {
49709         // basically accepts a pannel...
49710         // can accept a layout region..!?!?
49711         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49712         
49713         if (!cfg.xtype.match(/Panel$/)) {
49714             return false;
49715         }
49716         var ret = false;
49717         
49718         if (typeof(cfg.region) == 'undefined') {
49719             Roo.log("Failed to add Panel, region was not set");
49720             Roo.log(cfg);
49721             return false;
49722         }
49723         var region = cfg.region;
49724         delete cfg.region;
49725         
49726           
49727         var xitems = [];
49728         if (cfg.items) {
49729             xitems = cfg.items;
49730             delete cfg.items;
49731         }
49732         var nb = false;
49733         
49734         switch(cfg.xtype) 
49735         {
49736             case 'ContentPanel':  // ContentPanel (el, cfg)
49737             case 'ScrollPanel':  // ContentPanel (el, cfg)
49738             case 'ViewPanel': 
49739                 if(cfg.autoCreate) {
49740                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49741                 } else {
49742                     var el = this.el.createChild();
49743                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49744                 }
49745                 
49746                 this.add(region, ret);
49747                 break;
49748             
49749             
49750             case 'TreePanel': // our new panel!
49751                 cfg.el = this.el.createChild();
49752                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49753                 this.add(region, ret);
49754                 break;
49755             
49756             case 'NestedLayoutPanel': 
49757                 // create a new Layout (which is  a Border Layout...
49758                 var el = this.el.createChild();
49759                 var clayout = cfg.layout;
49760                 delete cfg.layout;
49761                 clayout.items   = clayout.items  || [];
49762                 // replace this exitems with the clayout ones..
49763                 xitems = clayout.items;
49764                  
49765                 
49766                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49767                     cfg.background = false;
49768                 }
49769                 var layout = new Roo.BorderLayout(el, clayout);
49770                 
49771                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49772                 //console.log('adding nested layout panel '  + cfg.toSource());
49773                 this.add(region, ret);
49774                 nb = {}; /// find first...
49775                 break;
49776                 
49777             case 'GridPanel': 
49778             
49779                 // needs grid and region
49780                 
49781                 //var el = this.getRegion(region).el.createChild();
49782                 var el = this.el.createChild();
49783                 // create the grid first...
49784                 
49785                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49786                 delete cfg.grid;
49787                 if (region == 'center' && this.active ) {
49788                     cfg.background = false;
49789                 }
49790                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49791                 
49792                 this.add(region, ret);
49793                 if (cfg.background) {
49794                     ret.on('activate', function(gp) {
49795                         if (!gp.grid.rendered) {
49796                             gp.grid.render();
49797                         }
49798                     });
49799                 } else {
49800                     grid.render();
49801                 }
49802                 break;
49803            
49804            
49805            
49806                 
49807                 
49808                 
49809             default:
49810                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49811                     
49812                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49813                     this.add(region, ret);
49814                 } else {
49815                 
49816                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49817                     return null;
49818                 }
49819                 
49820              // GridPanel (grid, cfg)
49821             
49822         }
49823         this.beginUpdate();
49824         // add children..
49825         var region = '';
49826         var abn = {};
49827         Roo.each(xitems, function(i)  {
49828             region = nb && i.region ? i.region : false;
49829             
49830             var add = ret.addxtype(i);
49831            
49832             if (region) {
49833                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49834                 if (!i.background) {
49835                     abn[region] = nb[region] ;
49836                 }
49837             }
49838             
49839         });
49840         this.endUpdate();
49841
49842         // make the last non-background panel active..
49843         //if (nb) { Roo.log(abn); }
49844         if (nb) {
49845             
49846             for(var r in abn) {
49847                 region = this.getRegion(r);
49848                 if (region) {
49849                     // tried using nb[r], but it does not work..
49850                      
49851                     region.showPanel(abn[r]);
49852                    
49853                 }
49854             }
49855         }
49856         return ret;
49857         
49858     }
49859 });
49860
49861 /**
49862  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49863  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49864  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49865  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49866  * <pre><code>
49867 // shorthand
49868 var CP = Roo.ContentPanel;
49869
49870 var layout = Roo.BorderLayout.create({
49871     north: {
49872         initialSize: 25,
49873         titlebar: false,
49874         panels: [new CP("north", "North")]
49875     },
49876     west: {
49877         split:true,
49878         initialSize: 200,
49879         minSize: 175,
49880         maxSize: 400,
49881         titlebar: true,
49882         collapsible: true,
49883         panels: [new CP("west", {title: "West"})]
49884     },
49885     east: {
49886         split:true,
49887         initialSize: 202,
49888         minSize: 175,
49889         maxSize: 400,
49890         titlebar: true,
49891         collapsible: true,
49892         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49893     },
49894     south: {
49895         split:true,
49896         initialSize: 100,
49897         minSize: 100,
49898         maxSize: 200,
49899         titlebar: true,
49900         collapsible: true,
49901         panels: [new CP("south", {title: "South", closable: true})]
49902     },
49903     center: {
49904         titlebar: true,
49905         autoScroll:true,
49906         resizeTabs: true,
49907         minTabWidth: 50,
49908         preferredTabWidth: 150,
49909         panels: [
49910             new CP("center1", {title: "Close Me", closable: true}),
49911             new CP("center2", {title: "Center Panel", closable: false})
49912         ]
49913     }
49914 }, document.body);
49915
49916 layout.getRegion("center").showPanel("center1");
49917 </code></pre>
49918  * @param config
49919  * @param targetEl
49920  */
49921 Roo.BorderLayout.create = function(config, targetEl){
49922     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49923     layout.beginUpdate();
49924     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49925     for(var j = 0, jlen = regions.length; j < jlen; j++){
49926         var lr = regions[j];
49927         if(layout.regions[lr] && config[lr].panels){
49928             var r = layout.regions[lr];
49929             var ps = config[lr].panels;
49930             layout.addTypedPanels(r, ps);
49931         }
49932     }
49933     layout.endUpdate();
49934     return layout;
49935 };
49936
49937 // private
49938 Roo.BorderLayout.RegionFactory = {
49939     // private
49940     validRegions : ["north","south","east","west","center"],
49941
49942     // private
49943     create : function(target, mgr, config){
49944         target = target.toLowerCase();
49945         if(config.lightweight || config.basic){
49946             return new Roo.BasicLayoutRegion(mgr, config, target);
49947         }
49948         switch(target){
49949             case "north":
49950                 return new Roo.NorthLayoutRegion(mgr, config);
49951             case "south":
49952                 return new Roo.SouthLayoutRegion(mgr, config);
49953             case "east":
49954                 return new Roo.EastLayoutRegion(mgr, config);
49955             case "west":
49956                 return new Roo.WestLayoutRegion(mgr, config);
49957             case "center":
49958                 return new Roo.CenterLayoutRegion(mgr, config);
49959         }
49960         throw 'Layout region "'+target+'" not supported.';
49961     }
49962 };/*
49963  * Based on:
49964  * Ext JS Library 1.1.1
49965  * Copyright(c) 2006-2007, Ext JS, LLC.
49966  *
49967  * Originally Released Under LGPL - original licence link has changed is not relivant.
49968  *
49969  * Fork - LGPL
49970  * <script type="text/javascript">
49971  */
49972  
49973 /**
49974  * @class Roo.BasicLayoutRegion
49975  * @extends Roo.util.Observable
49976  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49977  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49978  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49979  */
49980 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49981     this.mgr = mgr;
49982     this.position  = pos;
49983     this.events = {
49984         /**
49985          * @scope Roo.BasicLayoutRegion
49986          */
49987         
49988         /**
49989          * @event beforeremove
49990          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49991          * @param {Roo.LayoutRegion} this
49992          * @param {Roo.ContentPanel} panel The panel
49993          * @param {Object} e The cancel event object
49994          */
49995         "beforeremove" : true,
49996         /**
49997          * @event invalidated
49998          * Fires when the layout for this region is changed.
49999          * @param {Roo.LayoutRegion} this
50000          */
50001         "invalidated" : true,
50002         /**
50003          * @event visibilitychange
50004          * Fires when this region is shown or hidden 
50005          * @param {Roo.LayoutRegion} this
50006          * @param {Boolean} visibility true or false
50007          */
50008         "visibilitychange" : true,
50009         /**
50010          * @event paneladded
50011          * Fires when a panel is added. 
50012          * @param {Roo.LayoutRegion} this
50013          * @param {Roo.ContentPanel} panel The panel
50014          */
50015         "paneladded" : true,
50016         /**
50017          * @event panelremoved
50018          * Fires when a panel is removed. 
50019          * @param {Roo.LayoutRegion} this
50020          * @param {Roo.ContentPanel} panel The panel
50021          */
50022         "panelremoved" : true,
50023         /**
50024          * @event collapsed
50025          * Fires when this region is collapsed.
50026          * @param {Roo.LayoutRegion} this
50027          */
50028         "collapsed" : true,
50029         /**
50030          * @event expanded
50031          * Fires when this region is expanded.
50032          * @param {Roo.LayoutRegion} this
50033          */
50034         "expanded" : true,
50035         /**
50036          * @event slideshow
50037          * Fires when this region is slid into view.
50038          * @param {Roo.LayoutRegion} this
50039          */
50040         "slideshow" : true,
50041         /**
50042          * @event slidehide
50043          * Fires when this region slides out of view. 
50044          * @param {Roo.LayoutRegion} this
50045          */
50046         "slidehide" : true,
50047         /**
50048          * @event panelactivated
50049          * Fires when a panel is activated. 
50050          * @param {Roo.LayoutRegion} this
50051          * @param {Roo.ContentPanel} panel The activated panel
50052          */
50053         "panelactivated" : true,
50054         /**
50055          * @event resized
50056          * Fires when the user resizes this region. 
50057          * @param {Roo.LayoutRegion} this
50058          * @param {Number} newSize The new size (width for east/west, height for north/south)
50059          */
50060         "resized" : true
50061     };
50062     /** A collection of panels in this region. @type Roo.util.MixedCollection */
50063     this.panels = new Roo.util.MixedCollection();
50064     this.panels.getKey = this.getPanelId.createDelegate(this);
50065     this.box = null;
50066     this.activePanel = null;
50067     // ensure listeners are added...
50068     
50069     if (config.listeners || config.events) {
50070         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
50071             listeners : config.listeners || {},
50072             events : config.events || {}
50073         });
50074     }
50075     
50076     if(skipConfig !== true){
50077         this.applyConfig(config);
50078     }
50079 };
50080
50081 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
50082     getPanelId : function(p){
50083         return p.getId();
50084     },
50085     
50086     applyConfig : function(config){
50087         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50088         this.config = config;
50089         
50090     },
50091     
50092     /**
50093      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
50094      * the width, for horizontal (north, south) the height.
50095      * @param {Number} newSize The new width or height
50096      */
50097     resizeTo : function(newSize){
50098         var el = this.el ? this.el :
50099                  (this.activePanel ? this.activePanel.getEl() : null);
50100         if(el){
50101             switch(this.position){
50102                 case "east":
50103                 case "west":
50104                     el.setWidth(newSize);
50105                     this.fireEvent("resized", this, newSize);
50106                 break;
50107                 case "north":
50108                 case "south":
50109                     el.setHeight(newSize);
50110                     this.fireEvent("resized", this, newSize);
50111                 break;                
50112             }
50113         }
50114     },
50115     
50116     getBox : function(){
50117         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
50118     },
50119     
50120     getMargins : function(){
50121         return this.margins;
50122     },
50123     
50124     updateBox : function(box){
50125         this.box = box;
50126         var el = this.activePanel.getEl();
50127         el.dom.style.left = box.x + "px";
50128         el.dom.style.top = box.y + "px";
50129         this.activePanel.setSize(box.width, box.height);
50130     },
50131     
50132     /**
50133      * Returns the container element for this region.
50134      * @return {Roo.Element}
50135      */
50136     getEl : function(){
50137         return this.activePanel;
50138     },
50139     
50140     /**
50141      * Returns true if this region is currently visible.
50142      * @return {Boolean}
50143      */
50144     isVisible : function(){
50145         return this.activePanel ? true : false;
50146     },
50147     
50148     setActivePanel : function(panel){
50149         panel = this.getPanel(panel);
50150         if(this.activePanel && this.activePanel != panel){
50151             this.activePanel.setActiveState(false);
50152             this.activePanel.getEl().setLeftTop(-10000,-10000);
50153         }
50154         this.activePanel = panel;
50155         panel.setActiveState(true);
50156         if(this.box){
50157             panel.setSize(this.box.width, this.box.height);
50158         }
50159         this.fireEvent("panelactivated", this, panel);
50160         this.fireEvent("invalidated");
50161     },
50162     
50163     /**
50164      * Show the specified panel.
50165      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50166      * @return {Roo.ContentPanel} The shown panel or null
50167      */
50168     showPanel : function(panel){
50169         if(panel = this.getPanel(panel)){
50170             this.setActivePanel(panel);
50171         }
50172         return panel;
50173     },
50174     
50175     /**
50176      * Get the active panel for this region.
50177      * @return {Roo.ContentPanel} The active panel or null
50178      */
50179     getActivePanel : function(){
50180         return this.activePanel;
50181     },
50182     
50183     /**
50184      * Add the passed ContentPanel(s)
50185      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50186      * @return {Roo.ContentPanel} The panel added (if only one was added)
50187      */
50188     add : function(panel){
50189         if(arguments.length > 1){
50190             for(var i = 0, len = arguments.length; i < len; i++) {
50191                 this.add(arguments[i]);
50192             }
50193             return null;
50194         }
50195         if(this.hasPanel(panel)){
50196             this.showPanel(panel);
50197             return panel;
50198         }
50199         var el = panel.getEl();
50200         if(el.dom.parentNode != this.mgr.el.dom){
50201             this.mgr.el.dom.appendChild(el.dom);
50202         }
50203         if(panel.setRegion){
50204             panel.setRegion(this);
50205         }
50206         this.panels.add(panel);
50207         el.setStyle("position", "absolute");
50208         if(!panel.background){
50209             this.setActivePanel(panel);
50210             if(this.config.initialSize && this.panels.getCount()==1){
50211                 this.resizeTo(this.config.initialSize);
50212             }
50213         }
50214         this.fireEvent("paneladded", this, panel);
50215         return panel;
50216     },
50217     
50218     /**
50219      * Returns true if the panel is in this region.
50220      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50221      * @return {Boolean}
50222      */
50223     hasPanel : function(panel){
50224         if(typeof panel == "object"){ // must be panel obj
50225             panel = panel.getId();
50226         }
50227         return this.getPanel(panel) ? true : false;
50228     },
50229     
50230     /**
50231      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50232      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50233      * @param {Boolean} preservePanel Overrides the config preservePanel option
50234      * @return {Roo.ContentPanel} The panel that was removed
50235      */
50236     remove : function(panel, preservePanel){
50237         panel = this.getPanel(panel);
50238         if(!panel){
50239             return null;
50240         }
50241         var e = {};
50242         this.fireEvent("beforeremove", this, panel, e);
50243         if(e.cancel === true){
50244             return null;
50245         }
50246         var panelId = panel.getId();
50247         this.panels.removeKey(panelId);
50248         return panel;
50249     },
50250     
50251     /**
50252      * Returns the panel specified or null if it's not in this region.
50253      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50254      * @return {Roo.ContentPanel}
50255      */
50256     getPanel : function(id){
50257         if(typeof id == "object"){ // must be panel obj
50258             return id;
50259         }
50260         return this.panels.get(id);
50261     },
50262     
50263     /**
50264      * Returns this regions position (north/south/east/west/center).
50265      * @return {String} 
50266      */
50267     getPosition: function(){
50268         return this.position;    
50269     }
50270 });/*
50271  * Based on:
50272  * Ext JS Library 1.1.1
50273  * Copyright(c) 2006-2007, Ext JS, LLC.
50274  *
50275  * Originally Released Under LGPL - original licence link has changed is not relivant.
50276  *
50277  * Fork - LGPL
50278  * <script type="text/javascript">
50279  */
50280  
50281 /**
50282  * @class Roo.LayoutRegion
50283  * @extends Roo.BasicLayoutRegion
50284  * This class represents a region in a layout manager.
50285  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50286  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50287  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50288  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50289  * @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})
50290  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50291  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50292  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50293  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50294  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50295  * @cfg {String}    title           The title for the region (overrides panel titles)
50296  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50297  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50298  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50299  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50300  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50301  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50302  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50303  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50304  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50305  * @cfg {Boolean}   showPin         True to show a pin button
50306  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50307  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50308  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50309  * @cfg {Number}    width           For East/West panels
50310  * @cfg {Number}    height          For North/South panels
50311  * @cfg {Boolean}   split           To show the splitter
50312  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50313  */
50314 Roo.LayoutRegion = function(mgr, config, pos){
50315     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50316     var dh = Roo.DomHelper;
50317     /** This region's container element 
50318     * @type Roo.Element */
50319     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50320     /** This region's title element 
50321     * @type Roo.Element */
50322
50323     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50324         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50325         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50326     ]}, true);
50327     this.titleEl.enableDisplayMode();
50328     /** This region's title text element 
50329     * @type HTMLElement */
50330     this.titleTextEl = this.titleEl.dom.firstChild;
50331     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50332     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50333     this.closeBtn.enableDisplayMode();
50334     this.closeBtn.on("click", this.closeClicked, this);
50335     this.closeBtn.hide();
50336
50337     this.createBody(config);
50338     this.visible = true;
50339     this.collapsed = false;
50340
50341     if(config.hideWhenEmpty){
50342         this.hide();
50343         this.on("paneladded", this.validateVisibility, this);
50344         this.on("panelremoved", this.validateVisibility, this);
50345     }
50346     this.applyConfig(config);
50347 };
50348
50349 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50350
50351     createBody : function(){
50352         /** This region's body element 
50353         * @type Roo.Element */
50354         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50355     },
50356
50357     applyConfig : function(c){
50358         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50359             var dh = Roo.DomHelper;
50360             if(c.titlebar !== false){
50361                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50362                 this.collapseBtn.on("click", this.collapse, this);
50363                 this.collapseBtn.enableDisplayMode();
50364
50365                 if(c.showPin === true || this.showPin){
50366                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50367                     this.stickBtn.enableDisplayMode();
50368                     this.stickBtn.on("click", this.expand, this);
50369                     this.stickBtn.hide();
50370                 }
50371             }
50372             /** This region's collapsed element
50373             * @type Roo.Element */
50374             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50375                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50376             ]}, true);
50377             if(c.floatable !== false){
50378                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50379                this.collapsedEl.on("click", this.collapseClick, this);
50380             }
50381
50382             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50383                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50384                    id: "message", unselectable: "on", style:{"float":"left"}});
50385                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50386              }
50387             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50388             this.expandBtn.on("click", this.expand, this);
50389         }
50390         if(this.collapseBtn){
50391             this.collapseBtn.setVisible(c.collapsible == true);
50392         }
50393         this.cmargins = c.cmargins || this.cmargins ||
50394                          (this.position == "west" || this.position == "east" ?
50395                              {top: 0, left: 2, right:2, bottom: 0} :
50396                              {top: 2, left: 0, right:0, bottom: 2});
50397         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50398         this.bottomTabs = c.tabPosition != "top";
50399         this.autoScroll = c.autoScroll || false;
50400         if(this.autoScroll){
50401             this.bodyEl.setStyle("overflow", "auto");
50402         }else{
50403             this.bodyEl.setStyle("overflow", "hidden");
50404         }
50405         //if(c.titlebar !== false){
50406             if((!c.titlebar && !c.title) || c.titlebar === false){
50407                 this.titleEl.hide();
50408             }else{
50409                 this.titleEl.show();
50410                 if(c.title){
50411                     this.titleTextEl.innerHTML = c.title;
50412                 }
50413             }
50414         //}
50415         this.duration = c.duration || .30;
50416         this.slideDuration = c.slideDuration || .45;
50417         this.config = c;
50418         if(c.collapsed){
50419             this.collapse(true);
50420         }
50421         if(c.hidden){
50422             this.hide();
50423         }
50424     },
50425     /**
50426      * Returns true if this region is currently visible.
50427      * @return {Boolean}
50428      */
50429     isVisible : function(){
50430         return this.visible;
50431     },
50432
50433     /**
50434      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50435      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50436      */
50437     setCollapsedTitle : function(title){
50438         title = title || "&#160;";
50439         if(this.collapsedTitleTextEl){
50440             this.collapsedTitleTextEl.innerHTML = title;
50441         }
50442     },
50443
50444     getBox : function(){
50445         var b;
50446         if(!this.collapsed){
50447             b = this.el.getBox(false, true);
50448         }else{
50449             b = this.collapsedEl.getBox(false, true);
50450         }
50451         return b;
50452     },
50453
50454     getMargins : function(){
50455         return this.collapsed ? this.cmargins : this.margins;
50456     },
50457
50458     highlight : function(){
50459         this.el.addClass("x-layout-panel-dragover");
50460     },
50461
50462     unhighlight : function(){
50463         this.el.removeClass("x-layout-panel-dragover");
50464     },
50465
50466     updateBox : function(box){
50467         this.box = box;
50468         if(!this.collapsed){
50469             this.el.dom.style.left = box.x + "px";
50470             this.el.dom.style.top = box.y + "px";
50471             this.updateBody(box.width, box.height);
50472         }else{
50473             this.collapsedEl.dom.style.left = box.x + "px";
50474             this.collapsedEl.dom.style.top = box.y + "px";
50475             this.collapsedEl.setSize(box.width, box.height);
50476         }
50477         if(this.tabs){
50478             this.tabs.autoSizeTabs();
50479         }
50480     },
50481
50482     updateBody : function(w, h){
50483         if(w !== null){
50484             this.el.setWidth(w);
50485             w -= this.el.getBorderWidth("rl");
50486             if(this.config.adjustments){
50487                 w += this.config.adjustments[0];
50488             }
50489         }
50490         if(h !== null){
50491             this.el.setHeight(h);
50492             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50493             h -= this.el.getBorderWidth("tb");
50494             if(this.config.adjustments){
50495                 h += this.config.adjustments[1];
50496             }
50497             this.bodyEl.setHeight(h);
50498             if(this.tabs){
50499                 h = this.tabs.syncHeight(h);
50500             }
50501         }
50502         if(this.panelSize){
50503             w = w !== null ? w : this.panelSize.width;
50504             h = h !== null ? h : this.panelSize.height;
50505         }
50506         if(this.activePanel){
50507             var el = this.activePanel.getEl();
50508             w = w !== null ? w : el.getWidth();
50509             h = h !== null ? h : el.getHeight();
50510             this.panelSize = {width: w, height: h};
50511             this.activePanel.setSize(w, h);
50512         }
50513         if(Roo.isIE && this.tabs){
50514             this.tabs.el.repaint();
50515         }
50516     },
50517
50518     /**
50519      * Returns the container element for this region.
50520      * @return {Roo.Element}
50521      */
50522     getEl : function(){
50523         return this.el;
50524     },
50525
50526     /**
50527      * Hides this region.
50528      */
50529     hide : function(){
50530         if(!this.collapsed){
50531             this.el.dom.style.left = "-2000px";
50532             this.el.hide();
50533         }else{
50534             this.collapsedEl.dom.style.left = "-2000px";
50535             this.collapsedEl.hide();
50536         }
50537         this.visible = false;
50538         this.fireEvent("visibilitychange", this, false);
50539     },
50540
50541     /**
50542      * Shows this region if it was previously hidden.
50543      */
50544     show : function(){
50545         if(!this.collapsed){
50546             this.el.show();
50547         }else{
50548             this.collapsedEl.show();
50549         }
50550         this.visible = true;
50551         this.fireEvent("visibilitychange", this, true);
50552     },
50553
50554     closeClicked : function(){
50555         if(this.activePanel){
50556             this.remove(this.activePanel);
50557         }
50558     },
50559
50560     collapseClick : function(e){
50561         if(this.isSlid){
50562            e.stopPropagation();
50563            this.slideIn();
50564         }else{
50565            e.stopPropagation();
50566            this.slideOut();
50567         }
50568     },
50569
50570     /**
50571      * Collapses this region.
50572      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50573      */
50574     collapse : function(skipAnim){
50575         if(this.collapsed) {
50576             return;
50577         }
50578         this.collapsed = true;
50579         if(this.split){
50580             this.split.el.hide();
50581         }
50582         if(this.config.animate && skipAnim !== true){
50583             this.fireEvent("invalidated", this);
50584             this.animateCollapse();
50585         }else{
50586             this.el.setLocation(-20000,-20000);
50587             this.el.hide();
50588             this.collapsedEl.show();
50589             this.fireEvent("collapsed", this);
50590             this.fireEvent("invalidated", this);
50591         }
50592     },
50593
50594     animateCollapse : function(){
50595         // overridden
50596     },
50597
50598     /**
50599      * Expands this region if it was previously collapsed.
50600      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50601      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50602      */
50603     expand : function(e, skipAnim){
50604         if(e) {
50605             e.stopPropagation();
50606         }
50607         if(!this.collapsed || this.el.hasActiveFx()) {
50608             return;
50609         }
50610         if(this.isSlid){
50611             this.afterSlideIn();
50612             skipAnim = true;
50613         }
50614         this.collapsed = false;
50615         if(this.config.animate && skipAnim !== true){
50616             this.animateExpand();
50617         }else{
50618             this.el.show();
50619             if(this.split){
50620                 this.split.el.show();
50621             }
50622             this.collapsedEl.setLocation(-2000,-2000);
50623             this.collapsedEl.hide();
50624             this.fireEvent("invalidated", this);
50625             this.fireEvent("expanded", this);
50626         }
50627     },
50628
50629     animateExpand : function(){
50630         // overridden
50631     },
50632
50633     initTabs : function()
50634     {
50635         this.bodyEl.setStyle("overflow", "hidden");
50636         var ts = new Roo.TabPanel(
50637                 this.bodyEl.dom,
50638                 {
50639                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50640                     disableTooltips: this.config.disableTabTips,
50641                     toolbar : this.config.toolbar
50642                 }
50643         );
50644         if(this.config.hideTabs){
50645             ts.stripWrap.setDisplayed(false);
50646         }
50647         this.tabs = ts;
50648         ts.resizeTabs = this.config.resizeTabs === true;
50649         ts.minTabWidth = this.config.minTabWidth || 40;
50650         ts.maxTabWidth = this.config.maxTabWidth || 250;
50651         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50652         ts.monitorResize = false;
50653         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50654         ts.bodyEl.addClass('x-layout-tabs-body');
50655         this.panels.each(this.initPanelAsTab, this);
50656     },
50657
50658     initPanelAsTab : function(panel){
50659         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50660                     this.config.closeOnTab && panel.isClosable());
50661         if(panel.tabTip !== undefined){
50662             ti.setTooltip(panel.tabTip);
50663         }
50664         ti.on("activate", function(){
50665               this.setActivePanel(panel);
50666         }, this);
50667         if(this.config.closeOnTab){
50668             ti.on("beforeclose", function(t, e){
50669                 e.cancel = true;
50670                 this.remove(panel);
50671             }, this);
50672         }
50673         return ti;
50674     },
50675
50676     updatePanelTitle : function(panel, title){
50677         if(this.activePanel == panel){
50678             this.updateTitle(title);
50679         }
50680         if(this.tabs){
50681             var ti = this.tabs.getTab(panel.getEl().id);
50682             ti.setText(title);
50683             if(panel.tabTip !== undefined){
50684                 ti.setTooltip(panel.tabTip);
50685             }
50686         }
50687     },
50688
50689     updateTitle : function(title){
50690         if(this.titleTextEl && !this.config.title){
50691             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50692         }
50693     },
50694
50695     setActivePanel : function(panel){
50696         panel = this.getPanel(panel);
50697         if(this.activePanel && this.activePanel != panel){
50698             this.activePanel.setActiveState(false);
50699         }
50700         this.activePanel = panel;
50701         panel.setActiveState(true);
50702         if(this.panelSize){
50703             panel.setSize(this.panelSize.width, this.panelSize.height);
50704         }
50705         if(this.closeBtn){
50706             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50707         }
50708         this.updateTitle(panel.getTitle());
50709         if(this.tabs){
50710             this.fireEvent("invalidated", this);
50711         }
50712         this.fireEvent("panelactivated", this, panel);
50713     },
50714
50715     /**
50716      * Shows the specified panel.
50717      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50718      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50719      */
50720     showPanel : function(panel)
50721     {
50722         panel = this.getPanel(panel);
50723         if(panel){
50724             if(this.tabs){
50725                 var tab = this.tabs.getTab(panel.getEl().id);
50726                 if(tab.isHidden()){
50727                     this.tabs.unhideTab(tab.id);
50728                 }
50729                 tab.activate();
50730             }else{
50731                 this.setActivePanel(panel);
50732             }
50733         }
50734         return panel;
50735     },
50736
50737     /**
50738      * Get the active panel for this region.
50739      * @return {Roo.ContentPanel} The active panel or null
50740      */
50741     getActivePanel : function(){
50742         return this.activePanel;
50743     },
50744
50745     validateVisibility : function(){
50746         if(this.panels.getCount() < 1){
50747             this.updateTitle("&#160;");
50748             this.closeBtn.hide();
50749             this.hide();
50750         }else{
50751             if(!this.isVisible()){
50752                 this.show();
50753             }
50754         }
50755     },
50756
50757     /**
50758      * Adds the passed ContentPanel(s) to this region.
50759      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50760      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50761      */
50762     add : function(panel){
50763         if(arguments.length > 1){
50764             for(var i = 0, len = arguments.length; i < len; i++) {
50765                 this.add(arguments[i]);
50766             }
50767             return null;
50768         }
50769         if(this.hasPanel(panel)){
50770             this.showPanel(panel);
50771             return panel;
50772         }
50773         panel.setRegion(this);
50774         this.panels.add(panel);
50775         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50776             this.bodyEl.dom.appendChild(panel.getEl().dom);
50777             if(panel.background !== true){
50778                 this.setActivePanel(panel);
50779             }
50780             this.fireEvent("paneladded", this, panel);
50781             return panel;
50782         }
50783         if(!this.tabs){
50784             this.initTabs();
50785         }else{
50786             this.initPanelAsTab(panel);
50787         }
50788         if(panel.background !== true){
50789             this.tabs.activate(panel.getEl().id);
50790         }
50791         this.fireEvent("paneladded", this, panel);
50792         return panel;
50793     },
50794
50795     /**
50796      * Hides the tab for the specified panel.
50797      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50798      */
50799     hidePanel : function(panel){
50800         if(this.tabs && (panel = this.getPanel(panel))){
50801             this.tabs.hideTab(panel.getEl().id);
50802         }
50803     },
50804
50805     /**
50806      * Unhides the tab for a previously hidden panel.
50807      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50808      */
50809     unhidePanel : function(panel){
50810         if(this.tabs && (panel = this.getPanel(panel))){
50811             this.tabs.unhideTab(panel.getEl().id);
50812         }
50813     },
50814
50815     clearPanels : function(){
50816         while(this.panels.getCount() > 0){
50817              this.remove(this.panels.first());
50818         }
50819     },
50820
50821     /**
50822      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50823      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50824      * @param {Boolean} preservePanel Overrides the config preservePanel option
50825      * @return {Roo.ContentPanel} The panel that was removed
50826      */
50827     remove : function(panel, preservePanel){
50828         panel = this.getPanel(panel);
50829         if(!panel){
50830             return null;
50831         }
50832         var e = {};
50833         this.fireEvent("beforeremove", this, panel, e);
50834         if(e.cancel === true){
50835             return null;
50836         }
50837         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50838         var panelId = panel.getId();
50839         this.panels.removeKey(panelId);
50840         if(preservePanel){
50841             document.body.appendChild(panel.getEl().dom);
50842         }
50843         if(this.tabs){
50844             this.tabs.removeTab(panel.getEl().id);
50845         }else if (!preservePanel){
50846             this.bodyEl.dom.removeChild(panel.getEl().dom);
50847         }
50848         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50849             var p = this.panels.first();
50850             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50851             tempEl.appendChild(p.getEl().dom);
50852             this.bodyEl.update("");
50853             this.bodyEl.dom.appendChild(p.getEl().dom);
50854             tempEl = null;
50855             this.updateTitle(p.getTitle());
50856             this.tabs = null;
50857             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50858             this.setActivePanel(p);
50859         }
50860         panel.setRegion(null);
50861         if(this.activePanel == panel){
50862             this.activePanel = null;
50863         }
50864         if(this.config.autoDestroy !== false && preservePanel !== true){
50865             try{panel.destroy();}catch(e){}
50866         }
50867         this.fireEvent("panelremoved", this, panel);
50868         return panel;
50869     },
50870
50871     /**
50872      * Returns the TabPanel component used by this region
50873      * @return {Roo.TabPanel}
50874      */
50875     getTabs : function(){
50876         return this.tabs;
50877     },
50878
50879     createTool : function(parentEl, className){
50880         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50881             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50882         btn.addClassOnOver("x-layout-tools-button-over");
50883         return btn;
50884     }
50885 });/*
50886  * Based on:
50887  * Ext JS Library 1.1.1
50888  * Copyright(c) 2006-2007, Ext JS, LLC.
50889  *
50890  * Originally Released Under LGPL - original licence link has changed is not relivant.
50891  *
50892  * Fork - LGPL
50893  * <script type="text/javascript">
50894  */
50895  
50896
50897
50898 /**
50899  * @class Roo.SplitLayoutRegion
50900  * @extends Roo.LayoutRegion
50901  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50902  */
50903 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50904     this.cursor = cursor;
50905     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50906 };
50907
50908 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50909     splitTip : "Drag to resize.",
50910     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50911     useSplitTips : false,
50912
50913     applyConfig : function(config){
50914         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50915         if(config.split){
50916             if(!this.split){
50917                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50918                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50919                 /** The SplitBar for this region 
50920                 * @type Roo.SplitBar */
50921                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50922                 this.split.on("moved", this.onSplitMove, this);
50923                 this.split.useShim = config.useShim === true;
50924                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50925                 if(this.useSplitTips){
50926                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50927                 }
50928                 if(config.collapsible){
50929                     this.split.el.on("dblclick", this.collapse,  this);
50930                 }
50931             }
50932             if(typeof config.minSize != "undefined"){
50933                 this.split.minSize = config.minSize;
50934             }
50935             if(typeof config.maxSize != "undefined"){
50936                 this.split.maxSize = config.maxSize;
50937             }
50938             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50939                 this.hideSplitter();
50940             }
50941         }
50942     },
50943
50944     getHMaxSize : function(){
50945          var cmax = this.config.maxSize || 10000;
50946          var center = this.mgr.getRegion("center");
50947          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50948     },
50949
50950     getVMaxSize : function(){
50951          var cmax = this.config.maxSize || 10000;
50952          var center = this.mgr.getRegion("center");
50953          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50954     },
50955
50956     onSplitMove : function(split, newSize){
50957         this.fireEvent("resized", this, newSize);
50958     },
50959     
50960     /** 
50961      * Returns the {@link Roo.SplitBar} for this region.
50962      * @return {Roo.SplitBar}
50963      */
50964     getSplitBar : function(){
50965         return this.split;
50966     },
50967     
50968     hide : function(){
50969         this.hideSplitter();
50970         Roo.SplitLayoutRegion.superclass.hide.call(this);
50971     },
50972
50973     hideSplitter : function(){
50974         if(this.split){
50975             this.split.el.setLocation(-2000,-2000);
50976             this.split.el.hide();
50977         }
50978     },
50979
50980     show : function(){
50981         if(this.split){
50982             this.split.el.show();
50983         }
50984         Roo.SplitLayoutRegion.superclass.show.call(this);
50985     },
50986     
50987     beforeSlide: function(){
50988         if(Roo.isGecko){// firefox overflow auto bug workaround
50989             this.bodyEl.clip();
50990             if(this.tabs) {
50991                 this.tabs.bodyEl.clip();
50992             }
50993             if(this.activePanel){
50994                 this.activePanel.getEl().clip();
50995                 
50996                 if(this.activePanel.beforeSlide){
50997                     this.activePanel.beforeSlide();
50998                 }
50999             }
51000         }
51001     },
51002     
51003     afterSlide : function(){
51004         if(Roo.isGecko){// firefox overflow auto bug workaround
51005             this.bodyEl.unclip();
51006             if(this.tabs) {
51007                 this.tabs.bodyEl.unclip();
51008             }
51009             if(this.activePanel){
51010                 this.activePanel.getEl().unclip();
51011                 if(this.activePanel.afterSlide){
51012                     this.activePanel.afterSlide();
51013                 }
51014             }
51015         }
51016     },
51017
51018     initAutoHide : function(){
51019         if(this.autoHide !== false){
51020             if(!this.autoHideHd){
51021                 var st = new Roo.util.DelayedTask(this.slideIn, this);
51022                 this.autoHideHd = {
51023                     "mouseout": function(e){
51024                         if(!e.within(this.el, true)){
51025                             st.delay(500);
51026                         }
51027                     },
51028                     "mouseover" : function(e){
51029                         st.cancel();
51030                     },
51031                     scope : this
51032                 };
51033             }
51034             this.el.on(this.autoHideHd);
51035         }
51036     },
51037
51038     clearAutoHide : function(){
51039         if(this.autoHide !== false){
51040             this.el.un("mouseout", this.autoHideHd.mouseout);
51041             this.el.un("mouseover", this.autoHideHd.mouseover);
51042         }
51043     },
51044
51045     clearMonitor : function(){
51046         Roo.get(document).un("click", this.slideInIf, this);
51047     },
51048
51049     // these names are backwards but not changed for compat
51050     slideOut : function(){
51051         if(this.isSlid || this.el.hasActiveFx()){
51052             return;
51053         }
51054         this.isSlid = true;
51055         if(this.collapseBtn){
51056             this.collapseBtn.hide();
51057         }
51058         this.closeBtnState = this.closeBtn.getStyle('display');
51059         this.closeBtn.hide();
51060         if(this.stickBtn){
51061             this.stickBtn.show();
51062         }
51063         this.el.show();
51064         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
51065         this.beforeSlide();
51066         this.el.setStyle("z-index", 10001);
51067         this.el.slideIn(this.getSlideAnchor(), {
51068             callback: function(){
51069                 this.afterSlide();
51070                 this.initAutoHide();
51071                 Roo.get(document).on("click", this.slideInIf, this);
51072                 this.fireEvent("slideshow", this);
51073             },
51074             scope: this,
51075             block: true
51076         });
51077     },
51078
51079     afterSlideIn : function(){
51080         this.clearAutoHide();
51081         this.isSlid = false;
51082         this.clearMonitor();
51083         this.el.setStyle("z-index", "");
51084         if(this.collapseBtn){
51085             this.collapseBtn.show();
51086         }
51087         this.closeBtn.setStyle('display', this.closeBtnState);
51088         if(this.stickBtn){
51089             this.stickBtn.hide();
51090         }
51091         this.fireEvent("slidehide", this);
51092     },
51093
51094     slideIn : function(cb){
51095         if(!this.isSlid || this.el.hasActiveFx()){
51096             Roo.callback(cb);
51097             return;
51098         }
51099         this.isSlid = false;
51100         this.beforeSlide();
51101         this.el.slideOut(this.getSlideAnchor(), {
51102             callback: function(){
51103                 this.el.setLeftTop(-10000, -10000);
51104                 this.afterSlide();
51105                 this.afterSlideIn();
51106                 Roo.callback(cb);
51107             },
51108             scope: this,
51109             block: true
51110         });
51111     },
51112     
51113     slideInIf : function(e){
51114         if(!e.within(this.el)){
51115             this.slideIn();
51116         }
51117     },
51118
51119     animateCollapse : function(){
51120         this.beforeSlide();
51121         this.el.setStyle("z-index", 20000);
51122         var anchor = this.getSlideAnchor();
51123         this.el.slideOut(anchor, {
51124             callback : function(){
51125                 this.el.setStyle("z-index", "");
51126                 this.collapsedEl.slideIn(anchor, {duration:.3});
51127                 this.afterSlide();
51128                 this.el.setLocation(-10000,-10000);
51129                 this.el.hide();
51130                 this.fireEvent("collapsed", this);
51131             },
51132             scope: this,
51133             block: true
51134         });
51135     },
51136
51137     animateExpand : function(){
51138         this.beforeSlide();
51139         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
51140         this.el.setStyle("z-index", 20000);
51141         this.collapsedEl.hide({
51142             duration:.1
51143         });
51144         this.el.slideIn(this.getSlideAnchor(), {
51145             callback : function(){
51146                 this.el.setStyle("z-index", "");
51147                 this.afterSlide();
51148                 if(this.split){
51149                     this.split.el.show();
51150                 }
51151                 this.fireEvent("invalidated", this);
51152                 this.fireEvent("expanded", this);
51153             },
51154             scope: this,
51155             block: true
51156         });
51157     },
51158
51159     anchors : {
51160         "west" : "left",
51161         "east" : "right",
51162         "north" : "top",
51163         "south" : "bottom"
51164     },
51165
51166     sanchors : {
51167         "west" : "l",
51168         "east" : "r",
51169         "north" : "t",
51170         "south" : "b"
51171     },
51172
51173     canchors : {
51174         "west" : "tl-tr",
51175         "east" : "tr-tl",
51176         "north" : "tl-bl",
51177         "south" : "bl-tl"
51178     },
51179
51180     getAnchor : function(){
51181         return this.anchors[this.position];
51182     },
51183
51184     getCollapseAnchor : function(){
51185         return this.canchors[this.position];
51186     },
51187
51188     getSlideAnchor : function(){
51189         return this.sanchors[this.position];
51190     },
51191
51192     getAlignAdj : function(){
51193         var cm = this.cmargins;
51194         switch(this.position){
51195             case "west":
51196                 return [0, 0];
51197             break;
51198             case "east":
51199                 return [0, 0];
51200             break;
51201             case "north":
51202                 return [0, 0];
51203             break;
51204             case "south":
51205                 return [0, 0];
51206             break;
51207         }
51208     },
51209
51210     getExpandAdj : function(){
51211         var c = this.collapsedEl, cm = this.cmargins;
51212         switch(this.position){
51213             case "west":
51214                 return [-(cm.right+c.getWidth()+cm.left), 0];
51215             break;
51216             case "east":
51217                 return [cm.right+c.getWidth()+cm.left, 0];
51218             break;
51219             case "north":
51220                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51221             break;
51222             case "south":
51223                 return [0, cm.top+cm.bottom+c.getHeight()];
51224             break;
51225         }
51226     }
51227 });/*
51228  * Based on:
51229  * Ext JS Library 1.1.1
51230  * Copyright(c) 2006-2007, Ext JS, LLC.
51231  *
51232  * Originally Released Under LGPL - original licence link has changed is not relivant.
51233  *
51234  * Fork - LGPL
51235  * <script type="text/javascript">
51236  */
51237 /*
51238  * These classes are private internal classes
51239  */
51240 Roo.CenterLayoutRegion = function(mgr, config){
51241     Roo.LayoutRegion.call(this, mgr, config, "center");
51242     this.visible = true;
51243     this.minWidth = config.minWidth || 20;
51244     this.minHeight = config.minHeight || 20;
51245 };
51246
51247 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51248     hide : function(){
51249         // center panel can't be hidden
51250     },
51251     
51252     show : function(){
51253         // center panel can't be hidden
51254     },
51255     
51256     getMinWidth: function(){
51257         return this.minWidth;
51258     },
51259     
51260     getMinHeight: function(){
51261         return this.minHeight;
51262     }
51263 });
51264
51265
51266 Roo.NorthLayoutRegion = function(mgr, config){
51267     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51268     if(this.split){
51269         this.split.placement = Roo.SplitBar.TOP;
51270         this.split.orientation = Roo.SplitBar.VERTICAL;
51271         this.split.el.addClass("x-layout-split-v");
51272     }
51273     var size = config.initialSize || config.height;
51274     if(typeof size != "undefined"){
51275         this.el.setHeight(size);
51276     }
51277 };
51278 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51279     orientation: Roo.SplitBar.VERTICAL,
51280     getBox : function(){
51281         if(this.collapsed){
51282             return this.collapsedEl.getBox();
51283         }
51284         var box = this.el.getBox();
51285         if(this.split){
51286             box.height += this.split.el.getHeight();
51287         }
51288         return box;
51289     },
51290     
51291     updateBox : function(box){
51292         if(this.split && !this.collapsed){
51293             box.height -= this.split.el.getHeight();
51294             this.split.el.setLeft(box.x);
51295             this.split.el.setTop(box.y+box.height);
51296             this.split.el.setWidth(box.width);
51297         }
51298         if(this.collapsed){
51299             this.updateBody(box.width, null);
51300         }
51301         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51302     }
51303 });
51304
51305 Roo.SouthLayoutRegion = function(mgr, config){
51306     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51307     if(this.split){
51308         this.split.placement = Roo.SplitBar.BOTTOM;
51309         this.split.orientation = Roo.SplitBar.VERTICAL;
51310         this.split.el.addClass("x-layout-split-v");
51311     }
51312     var size = config.initialSize || config.height;
51313     if(typeof size != "undefined"){
51314         this.el.setHeight(size);
51315     }
51316 };
51317 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51318     orientation: Roo.SplitBar.VERTICAL,
51319     getBox : function(){
51320         if(this.collapsed){
51321             return this.collapsedEl.getBox();
51322         }
51323         var box = this.el.getBox();
51324         if(this.split){
51325             var sh = this.split.el.getHeight();
51326             box.height += sh;
51327             box.y -= sh;
51328         }
51329         return box;
51330     },
51331     
51332     updateBox : function(box){
51333         if(this.split && !this.collapsed){
51334             var sh = this.split.el.getHeight();
51335             box.height -= sh;
51336             box.y += sh;
51337             this.split.el.setLeft(box.x);
51338             this.split.el.setTop(box.y-sh);
51339             this.split.el.setWidth(box.width);
51340         }
51341         if(this.collapsed){
51342             this.updateBody(box.width, null);
51343         }
51344         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51345     }
51346 });
51347
51348 Roo.EastLayoutRegion = function(mgr, config){
51349     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51350     if(this.split){
51351         this.split.placement = Roo.SplitBar.RIGHT;
51352         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51353         this.split.el.addClass("x-layout-split-h");
51354     }
51355     var size = config.initialSize || config.width;
51356     if(typeof size != "undefined"){
51357         this.el.setWidth(size);
51358     }
51359 };
51360 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51361     orientation: Roo.SplitBar.HORIZONTAL,
51362     getBox : function(){
51363         if(this.collapsed){
51364             return this.collapsedEl.getBox();
51365         }
51366         var box = this.el.getBox();
51367         if(this.split){
51368             var sw = this.split.el.getWidth();
51369             box.width += sw;
51370             box.x -= sw;
51371         }
51372         return box;
51373     },
51374
51375     updateBox : function(box){
51376         if(this.split && !this.collapsed){
51377             var sw = this.split.el.getWidth();
51378             box.width -= sw;
51379             this.split.el.setLeft(box.x);
51380             this.split.el.setTop(box.y);
51381             this.split.el.setHeight(box.height);
51382             box.x += sw;
51383         }
51384         if(this.collapsed){
51385             this.updateBody(null, box.height);
51386         }
51387         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51388     }
51389 });
51390
51391 Roo.WestLayoutRegion = function(mgr, config){
51392     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51393     if(this.split){
51394         this.split.placement = Roo.SplitBar.LEFT;
51395         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51396         this.split.el.addClass("x-layout-split-h");
51397     }
51398     var size = config.initialSize || config.width;
51399     if(typeof size != "undefined"){
51400         this.el.setWidth(size);
51401     }
51402 };
51403 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51404     orientation: Roo.SplitBar.HORIZONTAL,
51405     getBox : function(){
51406         if(this.collapsed){
51407             return this.collapsedEl.getBox();
51408         }
51409         var box = this.el.getBox();
51410         if(this.split){
51411             box.width += this.split.el.getWidth();
51412         }
51413         return box;
51414     },
51415     
51416     updateBox : function(box){
51417         if(this.split && !this.collapsed){
51418             var sw = this.split.el.getWidth();
51419             box.width -= sw;
51420             this.split.el.setLeft(box.x+box.width);
51421             this.split.el.setTop(box.y);
51422             this.split.el.setHeight(box.height);
51423         }
51424         if(this.collapsed){
51425             this.updateBody(null, box.height);
51426         }
51427         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51428     }
51429 });
51430 /*
51431  * Based on:
51432  * Ext JS Library 1.1.1
51433  * Copyright(c) 2006-2007, Ext JS, LLC.
51434  *
51435  * Originally Released Under LGPL - original licence link has changed is not relivant.
51436  *
51437  * Fork - LGPL
51438  * <script type="text/javascript">
51439  */
51440  
51441  
51442 /*
51443  * Private internal class for reading and applying state
51444  */
51445 Roo.LayoutStateManager = function(layout){
51446      // default empty state
51447      this.state = {
51448         north: {},
51449         south: {},
51450         east: {},
51451         west: {}       
51452     };
51453 };
51454
51455 Roo.LayoutStateManager.prototype = {
51456     init : function(layout, provider){
51457         this.provider = provider;
51458         var state = provider.get(layout.id+"-layout-state");
51459         if(state){
51460             var wasUpdating = layout.isUpdating();
51461             if(!wasUpdating){
51462                 layout.beginUpdate();
51463             }
51464             for(var key in state){
51465                 if(typeof state[key] != "function"){
51466                     var rstate = state[key];
51467                     var r = layout.getRegion(key);
51468                     if(r && rstate){
51469                         if(rstate.size){
51470                             r.resizeTo(rstate.size);
51471                         }
51472                         if(rstate.collapsed == true){
51473                             r.collapse(true);
51474                         }else{
51475                             r.expand(null, true);
51476                         }
51477                     }
51478                 }
51479             }
51480             if(!wasUpdating){
51481                 layout.endUpdate();
51482             }
51483             this.state = state; 
51484         }
51485         this.layout = layout;
51486         layout.on("regionresized", this.onRegionResized, this);
51487         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51488         layout.on("regionexpanded", this.onRegionExpanded, this);
51489     },
51490     
51491     storeState : function(){
51492         this.provider.set(this.layout.id+"-layout-state", this.state);
51493     },
51494     
51495     onRegionResized : function(region, newSize){
51496         this.state[region.getPosition()].size = newSize;
51497         this.storeState();
51498     },
51499     
51500     onRegionCollapsed : function(region){
51501         this.state[region.getPosition()].collapsed = true;
51502         this.storeState();
51503     },
51504     
51505     onRegionExpanded : function(region){
51506         this.state[region.getPosition()].collapsed = false;
51507         this.storeState();
51508     }
51509 };/*
51510  * Based on:
51511  * Ext JS Library 1.1.1
51512  * Copyright(c) 2006-2007, Ext JS, LLC.
51513  *
51514  * Originally Released Under LGPL - original licence link has changed is not relivant.
51515  *
51516  * Fork - LGPL
51517  * <script type="text/javascript">
51518  */
51519 /**
51520  * @class Roo.ContentPanel
51521  * @extends Roo.util.Observable
51522  * A basic ContentPanel element.
51523  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51524  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51525  * @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
51526  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51527  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51528  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51529  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51530  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51531  * @cfg {String} title          The title for this panel
51532  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51533  * @cfg {String} url            Calls {@link #setUrl} with this value
51534  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51535  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51536  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51537  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51538
51539  * @constructor
51540  * Create a new ContentPanel.
51541  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51542  * @param {String/Object} config A string to set only the title or a config object
51543  * @param {String} content (optional) Set the HTML content for this panel
51544  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51545  */
51546 Roo.ContentPanel = function(el, config, content){
51547     
51548      
51549     /*
51550     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51551         config = el;
51552         el = Roo.id();
51553     }
51554     if (config && config.parentLayout) { 
51555         el = config.parentLayout.el.createChild(); 
51556     }
51557     */
51558     if(el.autoCreate){ // xtype is available if this is called from factory
51559         config = el;
51560         el = Roo.id();
51561     }
51562     this.el = Roo.get(el);
51563     if(!this.el && config && config.autoCreate){
51564         if(typeof config.autoCreate == "object"){
51565             if(!config.autoCreate.id){
51566                 config.autoCreate.id = config.id||el;
51567             }
51568             this.el = Roo.DomHelper.append(document.body,
51569                         config.autoCreate, true);
51570         }else{
51571             this.el = Roo.DomHelper.append(document.body,
51572                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51573         }
51574     }
51575     this.closable = false;
51576     this.loaded = false;
51577     this.active = false;
51578     if(typeof config == "string"){
51579         this.title = config;
51580     }else{
51581         Roo.apply(this, config);
51582     }
51583     
51584     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51585         this.wrapEl = this.el.wrap();
51586         this.toolbar.container = this.el.insertSibling(false, 'before');
51587         this.toolbar = new Roo.Toolbar(this.toolbar);
51588     }
51589     
51590     // xtype created footer. - not sure if will work as we normally have to render first..
51591     if (this.footer && !this.footer.el && this.footer.xtype) {
51592         if (!this.wrapEl) {
51593             this.wrapEl = this.el.wrap();
51594         }
51595     
51596         this.footer.container = this.wrapEl.createChild();
51597          
51598         this.footer = Roo.factory(this.footer, Roo);
51599         
51600     }
51601     
51602     if(this.resizeEl){
51603         this.resizeEl = Roo.get(this.resizeEl, true);
51604     }else{
51605         this.resizeEl = this.el;
51606     }
51607     // handle view.xtype
51608     
51609  
51610     
51611     
51612     this.addEvents({
51613         /**
51614          * @event activate
51615          * Fires when this panel is activated. 
51616          * @param {Roo.ContentPanel} this
51617          */
51618         "activate" : true,
51619         /**
51620          * @event deactivate
51621          * Fires when this panel is activated. 
51622          * @param {Roo.ContentPanel} this
51623          */
51624         "deactivate" : true,
51625
51626         /**
51627          * @event resize
51628          * Fires when this panel is resized if fitToFrame is true.
51629          * @param {Roo.ContentPanel} this
51630          * @param {Number} width The width after any component adjustments
51631          * @param {Number} height The height after any component adjustments
51632          */
51633         "resize" : true,
51634         
51635          /**
51636          * @event render
51637          * Fires when this tab is created
51638          * @param {Roo.ContentPanel} this
51639          */
51640         "render" : true
51641         
51642         
51643         
51644     });
51645     
51646
51647     
51648     
51649     if(this.autoScroll){
51650         this.resizeEl.setStyle("overflow", "auto");
51651     } else {
51652         // fix randome scrolling
51653         this.el.on('scroll', function() {
51654             Roo.log('fix random scolling');
51655             this.scrollTo('top',0); 
51656         });
51657     }
51658     content = content || this.content;
51659     if(content){
51660         this.setContent(content);
51661     }
51662     if(config && config.url){
51663         this.setUrl(this.url, this.params, this.loadOnce);
51664     }
51665     
51666     
51667     
51668     Roo.ContentPanel.superclass.constructor.call(this);
51669     
51670     if (this.view && typeof(this.view.xtype) != 'undefined') {
51671         this.view.el = this.el.appendChild(document.createElement("div"));
51672         this.view = Roo.factory(this.view); 
51673         this.view.render  &&  this.view.render(false, '');  
51674     }
51675     
51676     
51677     this.fireEvent('render', this);
51678 };
51679
51680 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51681     tabTip:'',
51682     setRegion : function(region){
51683         this.region = region;
51684         if(region){
51685            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51686         }else{
51687            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51688         } 
51689     },
51690     
51691     /**
51692      * Returns the toolbar for this Panel if one was configured. 
51693      * @return {Roo.Toolbar} 
51694      */
51695     getToolbar : function(){
51696         return this.toolbar;
51697     },
51698     
51699     setActiveState : function(active){
51700         this.active = active;
51701         if(!active){
51702             this.fireEvent("deactivate", this);
51703         }else{
51704             this.fireEvent("activate", this);
51705         }
51706     },
51707     /**
51708      * Updates this panel's element
51709      * @param {String} content The new content
51710      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51711     */
51712     setContent : function(content, loadScripts){
51713         this.el.update(content, loadScripts);
51714     },
51715
51716     ignoreResize : function(w, h){
51717         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51718             return true;
51719         }else{
51720             this.lastSize = {width: w, height: h};
51721             return false;
51722         }
51723     },
51724     /**
51725      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51726      * @return {Roo.UpdateManager} The UpdateManager
51727      */
51728     getUpdateManager : function(){
51729         return this.el.getUpdateManager();
51730     },
51731      /**
51732      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51733      * @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:
51734 <pre><code>
51735 panel.load({
51736     url: "your-url.php",
51737     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51738     callback: yourFunction,
51739     scope: yourObject, //(optional scope)
51740     discardUrl: false,
51741     nocache: false,
51742     text: "Loading...",
51743     timeout: 30,
51744     scripts: false
51745 });
51746 </code></pre>
51747      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51748      * 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.
51749      * @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}
51750      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51751      * @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.
51752      * @return {Roo.ContentPanel} this
51753      */
51754     load : function(){
51755         var um = this.el.getUpdateManager();
51756         um.update.apply(um, arguments);
51757         return this;
51758     },
51759
51760
51761     /**
51762      * 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.
51763      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51764      * @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)
51765      * @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)
51766      * @return {Roo.UpdateManager} The UpdateManager
51767      */
51768     setUrl : function(url, params, loadOnce){
51769         if(this.refreshDelegate){
51770             this.removeListener("activate", this.refreshDelegate);
51771         }
51772         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51773         this.on("activate", this.refreshDelegate);
51774         return this.el.getUpdateManager();
51775     },
51776     
51777     _handleRefresh : function(url, params, loadOnce){
51778         if(!loadOnce || !this.loaded){
51779             var updater = this.el.getUpdateManager();
51780             updater.update(url, params, this._setLoaded.createDelegate(this));
51781         }
51782     },
51783     
51784     _setLoaded : function(){
51785         this.loaded = true;
51786     }, 
51787     
51788     /**
51789      * Returns this panel's id
51790      * @return {String} 
51791      */
51792     getId : function(){
51793         return this.el.id;
51794     },
51795     
51796     /** 
51797      * Returns this panel's element - used by regiosn to add.
51798      * @return {Roo.Element} 
51799      */
51800     getEl : function(){
51801         return this.wrapEl || this.el;
51802     },
51803     
51804     adjustForComponents : function(width, height)
51805     {
51806         //Roo.log('adjustForComponents ');
51807         if(this.resizeEl != this.el){
51808             width -= this.el.getFrameWidth('lr');
51809             height -= this.el.getFrameWidth('tb');
51810         }
51811         if(this.toolbar){
51812             var te = this.toolbar.getEl();
51813             height -= te.getHeight();
51814             te.setWidth(width);
51815         }
51816         if(this.footer){
51817             var te = this.footer.getEl();
51818             Roo.log("footer:" + te.getHeight());
51819             
51820             height -= te.getHeight();
51821             te.setWidth(width);
51822         }
51823         
51824         
51825         if(this.adjustments){
51826             width += this.adjustments[0];
51827             height += this.adjustments[1];
51828         }
51829         return {"width": width, "height": height};
51830     },
51831     
51832     setSize : function(width, height){
51833         if(this.fitToFrame && !this.ignoreResize(width, height)){
51834             if(this.fitContainer && this.resizeEl != this.el){
51835                 this.el.setSize(width, height);
51836             }
51837             var size = this.adjustForComponents(width, height);
51838             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51839             this.fireEvent('resize', this, size.width, size.height);
51840         }
51841     },
51842     
51843     /**
51844      * Returns this panel's title
51845      * @return {String} 
51846      */
51847     getTitle : function(){
51848         return this.title;
51849     },
51850     
51851     /**
51852      * Set this panel's title
51853      * @param {String} title
51854      */
51855     setTitle : function(title){
51856         this.title = title;
51857         if(this.region){
51858             this.region.updatePanelTitle(this, title);
51859         }
51860     },
51861     
51862     /**
51863      * Returns true is this panel was configured to be closable
51864      * @return {Boolean} 
51865      */
51866     isClosable : function(){
51867         return this.closable;
51868     },
51869     
51870     beforeSlide : function(){
51871         this.el.clip();
51872         this.resizeEl.clip();
51873     },
51874     
51875     afterSlide : function(){
51876         this.el.unclip();
51877         this.resizeEl.unclip();
51878     },
51879     
51880     /**
51881      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51882      *   Will fail silently if the {@link #setUrl} method has not been called.
51883      *   This does not activate the panel, just updates its content.
51884      */
51885     refresh : function(){
51886         if(this.refreshDelegate){
51887            this.loaded = false;
51888            this.refreshDelegate();
51889         }
51890     },
51891     
51892     /**
51893      * Destroys this panel
51894      */
51895     destroy : function(){
51896         this.el.removeAllListeners();
51897         var tempEl = document.createElement("span");
51898         tempEl.appendChild(this.el.dom);
51899         tempEl.innerHTML = "";
51900         this.el.remove();
51901         this.el = null;
51902     },
51903     
51904     /**
51905      * form - if the content panel contains a form - this is a reference to it.
51906      * @type {Roo.form.Form}
51907      */
51908     form : false,
51909     /**
51910      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51911      *    This contains a reference to it.
51912      * @type {Roo.View}
51913      */
51914     view : false,
51915     
51916       /**
51917      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51918      * <pre><code>
51919
51920 layout.addxtype({
51921        xtype : 'Form',
51922        items: [ .... ]
51923    }
51924 );
51925
51926 </code></pre>
51927      * @param {Object} cfg Xtype definition of item to add.
51928      */
51929     
51930     addxtype : function(cfg) {
51931         // add form..
51932         if (cfg.xtype.match(/^Form$/)) {
51933             
51934             var el;
51935             //if (this.footer) {
51936             //    el = this.footer.container.insertSibling(false, 'before');
51937             //} else {
51938                 el = this.el.createChild();
51939             //}
51940
51941             this.form = new  Roo.form.Form(cfg);
51942             
51943             
51944             if ( this.form.allItems.length) {
51945                 this.form.render(el.dom);
51946             }
51947             return this.form;
51948         }
51949         // should only have one of theses..
51950         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51951             // views.. should not be just added - used named prop 'view''
51952             
51953             cfg.el = this.el.appendChild(document.createElement("div"));
51954             // factory?
51955             
51956             var ret = new Roo.factory(cfg);
51957              
51958              ret.render && ret.render(false, ''); // render blank..
51959             this.view = ret;
51960             return ret;
51961         }
51962         return false;
51963     }
51964 });
51965
51966 /**
51967  * @class Roo.GridPanel
51968  * @extends Roo.ContentPanel
51969  * @constructor
51970  * Create a new GridPanel.
51971  * @param {Roo.grid.Grid} grid The grid for this panel
51972  * @param {String/Object} config A string to set only the panel's title, or a config object
51973  */
51974 Roo.GridPanel = function(grid, config){
51975     
51976   
51977     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51978         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51979         
51980     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51981     
51982     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51983     
51984     if(this.toolbar){
51985         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51986     }
51987     // xtype created footer. - not sure if will work as we normally have to render first..
51988     if (this.footer && !this.footer.el && this.footer.xtype) {
51989         
51990         this.footer.container = this.grid.getView().getFooterPanel(true);
51991         this.footer.dataSource = this.grid.dataSource;
51992         this.footer = Roo.factory(this.footer, Roo);
51993         
51994     }
51995     
51996     grid.monitorWindowResize = false; // turn off autosizing
51997     grid.autoHeight = false;
51998     grid.autoWidth = false;
51999     this.grid = grid;
52000     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
52001 };
52002
52003 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
52004     getId : function(){
52005         return this.grid.id;
52006     },
52007     
52008     /**
52009      * Returns the grid for this panel
52010      * @return {Roo.grid.Grid} 
52011      */
52012     getGrid : function(){
52013         return this.grid;    
52014     },
52015     
52016     setSize : function(width, height){
52017         if(!this.ignoreResize(width, height)){
52018             var grid = this.grid;
52019             var size = this.adjustForComponents(width, height);
52020             grid.getGridEl().setSize(size.width, size.height);
52021             grid.autoSize();
52022         }
52023     },
52024     
52025     beforeSlide : function(){
52026         this.grid.getView().scroller.clip();
52027     },
52028     
52029     afterSlide : function(){
52030         this.grid.getView().scroller.unclip();
52031     },
52032     
52033     destroy : function(){
52034         this.grid.destroy();
52035         delete this.grid;
52036         Roo.GridPanel.superclass.destroy.call(this); 
52037     }
52038 });
52039
52040
52041 /**
52042  * @class Roo.NestedLayoutPanel
52043  * @extends Roo.ContentPanel
52044  * @constructor
52045  * Create a new NestedLayoutPanel.
52046  * 
52047  * 
52048  * @param {Roo.BorderLayout} layout The layout for this panel
52049  * @param {String/Object} config A string to set only the title or a config object
52050  */
52051 Roo.NestedLayoutPanel = function(layout, config)
52052 {
52053     // construct with only one argument..
52054     /* FIXME - implement nicer consturctors
52055     if (layout.layout) {
52056         config = layout;
52057         layout = config.layout;
52058         delete config.layout;
52059     }
52060     if (layout.xtype && !layout.getEl) {
52061         // then layout needs constructing..
52062         layout = Roo.factory(layout, Roo);
52063     }
52064     */
52065     
52066     
52067     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
52068     
52069     layout.monitorWindowResize = false; // turn off autosizing
52070     this.layout = layout;
52071     this.layout.getEl().addClass("x-layout-nested-layout");
52072     
52073     
52074     
52075     
52076 };
52077
52078 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
52079
52080     setSize : function(width, height){
52081         if(!this.ignoreResize(width, height)){
52082             var size = this.adjustForComponents(width, height);
52083             var el = this.layout.getEl();
52084             el.setSize(size.width, size.height);
52085             var touch = el.dom.offsetWidth;
52086             this.layout.layout();
52087             // ie requires a double layout on the first pass
52088             if(Roo.isIE && !this.initialized){
52089                 this.initialized = true;
52090                 this.layout.layout();
52091             }
52092         }
52093     },
52094     
52095     // activate all subpanels if not currently active..
52096     
52097     setActiveState : function(active){
52098         this.active = active;
52099         if(!active){
52100             this.fireEvent("deactivate", this);
52101             return;
52102         }
52103         
52104         this.fireEvent("activate", this);
52105         // not sure if this should happen before or after..
52106         if (!this.layout) {
52107             return; // should not happen..
52108         }
52109         var reg = false;
52110         for (var r in this.layout.regions) {
52111             reg = this.layout.getRegion(r);
52112             if (reg.getActivePanel()) {
52113                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
52114                 reg.setActivePanel(reg.getActivePanel());
52115                 continue;
52116             }
52117             if (!reg.panels.length) {
52118                 continue;
52119             }
52120             reg.showPanel(reg.getPanel(0));
52121         }
52122         
52123         
52124         
52125         
52126     },
52127     
52128     /**
52129      * Returns the nested BorderLayout for this panel
52130      * @return {Roo.BorderLayout} 
52131      */
52132     getLayout : function(){
52133         return this.layout;
52134     },
52135     
52136      /**
52137      * Adds a xtype elements to the layout of the nested panel
52138      * <pre><code>
52139
52140 panel.addxtype({
52141        xtype : 'ContentPanel',
52142        region: 'west',
52143        items: [ .... ]
52144    }
52145 );
52146
52147 panel.addxtype({
52148         xtype : 'NestedLayoutPanel',
52149         region: 'west',
52150         layout: {
52151            center: { },
52152            west: { }   
52153         },
52154         items : [ ... list of content panels or nested layout panels.. ]
52155    }
52156 );
52157 </code></pre>
52158      * @param {Object} cfg Xtype definition of item to add.
52159      */
52160     addxtype : function(cfg) {
52161         return this.layout.addxtype(cfg);
52162     
52163     }
52164 });
52165
52166 Roo.ScrollPanel = function(el, config, content){
52167     config = config || {};
52168     config.fitToFrame = true;
52169     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52170     
52171     this.el.dom.style.overflow = "hidden";
52172     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52173     this.el.removeClass("x-layout-inactive-content");
52174     this.el.on("mousewheel", this.onWheel, this);
52175
52176     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52177     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52178     up.unselectable(); down.unselectable();
52179     up.on("click", this.scrollUp, this);
52180     down.on("click", this.scrollDown, this);
52181     up.addClassOnOver("x-scroller-btn-over");
52182     down.addClassOnOver("x-scroller-btn-over");
52183     up.addClassOnClick("x-scroller-btn-click");
52184     down.addClassOnClick("x-scroller-btn-click");
52185     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52186
52187     this.resizeEl = this.el;
52188     this.el = wrap; this.up = up; this.down = down;
52189 };
52190
52191 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52192     increment : 100,
52193     wheelIncrement : 5,
52194     scrollUp : function(){
52195         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52196     },
52197
52198     scrollDown : function(){
52199         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52200     },
52201
52202     afterScroll : function(){
52203         var el = this.resizeEl;
52204         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52205         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52206         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52207     },
52208
52209     setSize : function(){
52210         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52211         this.afterScroll();
52212     },
52213
52214     onWheel : function(e){
52215         var d = e.getWheelDelta();
52216         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52217         this.afterScroll();
52218         e.stopEvent();
52219     },
52220
52221     setContent : function(content, loadScripts){
52222         this.resizeEl.update(content, loadScripts);
52223     }
52224
52225 });
52226
52227
52228
52229
52230
52231
52232
52233
52234
52235 /**
52236  * @class Roo.TreePanel
52237  * @extends Roo.ContentPanel
52238  * @constructor
52239  * Create a new TreePanel. - defaults to fit/scoll contents.
52240  * @param {String/Object} config A string to set only the panel's title, or a config object
52241  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52242  */
52243 Roo.TreePanel = function(config){
52244     var el = config.el;
52245     var tree = config.tree;
52246     delete config.tree; 
52247     delete config.el; // hopefull!
52248     
52249     // wrapper for IE7 strict & safari scroll issue
52250     
52251     var treeEl = el.createChild();
52252     config.resizeEl = treeEl;
52253     
52254     
52255     
52256     Roo.TreePanel.superclass.constructor.call(this, el, config);
52257  
52258  
52259     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52260     //console.log(tree);
52261     this.on('activate', function()
52262     {
52263         if (this.tree.rendered) {
52264             return;
52265         }
52266         //console.log('render tree');
52267         this.tree.render();
52268     });
52269     // this should not be needed.. - it's actually the 'el' that resizes?
52270     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52271     
52272     //this.on('resize',  function (cp, w, h) {
52273     //        this.tree.innerCt.setWidth(w);
52274     //        this.tree.innerCt.setHeight(h);
52275     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52276     //});
52277
52278         
52279     
52280 };
52281
52282 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52283     fitToFrame : true,
52284     autoScroll : true
52285 });
52286
52287
52288
52289
52290
52291
52292
52293
52294
52295
52296
52297 /*
52298  * Based on:
52299  * Ext JS Library 1.1.1
52300  * Copyright(c) 2006-2007, Ext JS, LLC.
52301  *
52302  * Originally Released Under LGPL - original licence link has changed is not relivant.
52303  *
52304  * Fork - LGPL
52305  * <script type="text/javascript">
52306  */
52307  
52308
52309 /**
52310  * @class Roo.ReaderLayout
52311  * @extends Roo.BorderLayout
52312  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52313  * center region containing two nested regions (a top one for a list view and one for item preview below),
52314  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52315  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52316  * expedites the setup of the overall layout and regions for this common application style.
52317  * Example:
52318  <pre><code>
52319 var reader = new Roo.ReaderLayout();
52320 var CP = Roo.ContentPanel;  // shortcut for adding
52321
52322 reader.beginUpdate();
52323 reader.add("north", new CP("north", "North"));
52324 reader.add("west", new CP("west", {title: "West"}));
52325 reader.add("east", new CP("east", {title: "East"}));
52326
52327 reader.regions.listView.add(new CP("listView", "List"));
52328 reader.regions.preview.add(new CP("preview", "Preview"));
52329 reader.endUpdate();
52330 </code></pre>
52331 * @constructor
52332 * Create a new ReaderLayout
52333 * @param {Object} config Configuration options
52334 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52335 * document.body if omitted)
52336 */
52337 Roo.ReaderLayout = function(config, renderTo){
52338     var c = config || {size:{}};
52339     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52340         north: c.north !== false ? Roo.apply({
52341             split:false,
52342             initialSize: 32,
52343             titlebar: false
52344         }, c.north) : false,
52345         west: c.west !== false ? Roo.apply({
52346             split:true,
52347             initialSize: 200,
52348             minSize: 175,
52349             maxSize: 400,
52350             titlebar: true,
52351             collapsible: true,
52352             animate: true,
52353             margins:{left:5,right:0,bottom:5,top:5},
52354             cmargins:{left:5,right:5,bottom:5,top:5}
52355         }, c.west) : false,
52356         east: c.east !== false ? Roo.apply({
52357             split:true,
52358             initialSize: 200,
52359             minSize: 175,
52360             maxSize: 400,
52361             titlebar: true,
52362             collapsible: true,
52363             animate: true,
52364             margins:{left:0,right:5,bottom:5,top:5},
52365             cmargins:{left:5,right:5,bottom:5,top:5}
52366         }, c.east) : false,
52367         center: Roo.apply({
52368             tabPosition: 'top',
52369             autoScroll:false,
52370             closeOnTab: true,
52371             titlebar:false,
52372             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52373         }, c.center)
52374     });
52375
52376     this.el.addClass('x-reader');
52377
52378     this.beginUpdate();
52379
52380     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52381         south: c.preview !== false ? Roo.apply({
52382             split:true,
52383             initialSize: 200,
52384             minSize: 100,
52385             autoScroll:true,
52386             collapsible:true,
52387             titlebar: true,
52388             cmargins:{top:5,left:0, right:0, bottom:0}
52389         }, c.preview) : false,
52390         center: Roo.apply({
52391             autoScroll:false,
52392             titlebar:false,
52393             minHeight:200
52394         }, c.listView)
52395     });
52396     this.add('center', new Roo.NestedLayoutPanel(inner,
52397             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52398
52399     this.endUpdate();
52400
52401     this.regions.preview = inner.getRegion('south');
52402     this.regions.listView = inner.getRegion('center');
52403 };
52404
52405 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52406  * Based on:
52407  * Ext JS Library 1.1.1
52408  * Copyright(c) 2006-2007, Ext JS, LLC.
52409  *
52410  * Originally Released Under LGPL - original licence link has changed is not relivant.
52411  *
52412  * Fork - LGPL
52413  * <script type="text/javascript">
52414  */
52415  
52416 /**
52417  * @class Roo.grid.Grid
52418  * @extends Roo.util.Observable
52419  * This class represents the primary interface of a component based grid control.
52420  * <br><br>Usage:<pre><code>
52421  var grid = new Roo.grid.Grid("my-container-id", {
52422      ds: myDataStore,
52423      cm: myColModel,
52424      selModel: mySelectionModel,
52425      autoSizeColumns: true,
52426      monitorWindowResize: false,
52427      trackMouseOver: true
52428  });
52429  // set any options
52430  grid.render();
52431  * </code></pre>
52432  * <b>Common Problems:</b><br/>
52433  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52434  * element will correct this<br/>
52435  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52436  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52437  * are unpredictable.<br/>
52438  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52439  * grid to calculate dimensions/offsets.<br/>
52440   * @constructor
52441  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52442  * The container MUST have some type of size defined for the grid to fill. The container will be
52443  * automatically set to position relative if it isn't already.
52444  * @param {Object} config A config object that sets properties on this grid.
52445  */
52446 Roo.grid.Grid = function(container, config){
52447         // initialize the container
52448         this.container = Roo.get(container);
52449         this.container.update("");
52450         this.container.setStyle("overflow", "hidden");
52451     this.container.addClass('x-grid-container');
52452
52453     this.id = this.container.id;
52454
52455     Roo.apply(this, config);
52456     // check and correct shorthanded configs
52457     if(this.ds){
52458         this.dataSource = this.ds;
52459         delete this.ds;
52460     }
52461     if(this.cm){
52462         this.colModel = this.cm;
52463         delete this.cm;
52464     }
52465     if(this.sm){
52466         this.selModel = this.sm;
52467         delete this.sm;
52468     }
52469
52470     if (this.selModel) {
52471         this.selModel = Roo.factory(this.selModel, Roo.grid);
52472         this.sm = this.selModel;
52473         this.sm.xmodule = this.xmodule || false;
52474     }
52475     if (typeof(this.colModel.config) == 'undefined') {
52476         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52477         this.cm = this.colModel;
52478         this.cm.xmodule = this.xmodule || false;
52479     }
52480     if (this.dataSource) {
52481         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52482         this.ds = this.dataSource;
52483         this.ds.xmodule = this.xmodule || false;
52484          
52485     }
52486     
52487     
52488     
52489     if(this.width){
52490         this.container.setWidth(this.width);
52491     }
52492
52493     if(this.height){
52494         this.container.setHeight(this.height);
52495     }
52496     /** @private */
52497         this.addEvents({
52498         // raw events
52499         /**
52500          * @event click
52501          * The raw click event for the entire grid.
52502          * @param {Roo.EventObject} e
52503          */
52504         "click" : true,
52505         /**
52506          * @event dblclick
52507          * The raw dblclick event for the entire grid.
52508          * @param {Roo.EventObject} e
52509          */
52510         "dblclick" : true,
52511         /**
52512          * @event contextmenu
52513          * The raw contextmenu event for the entire grid.
52514          * @param {Roo.EventObject} e
52515          */
52516         "contextmenu" : true,
52517         /**
52518          * @event mousedown
52519          * The raw mousedown event for the entire grid.
52520          * @param {Roo.EventObject} e
52521          */
52522         "mousedown" : true,
52523         /**
52524          * @event mouseup
52525          * The raw mouseup event for the entire grid.
52526          * @param {Roo.EventObject} e
52527          */
52528         "mouseup" : true,
52529         /**
52530          * @event mouseover
52531          * The raw mouseover event for the entire grid.
52532          * @param {Roo.EventObject} e
52533          */
52534         "mouseover" : true,
52535         /**
52536          * @event mouseout
52537          * The raw mouseout event for the entire grid.
52538          * @param {Roo.EventObject} e
52539          */
52540         "mouseout" : true,
52541         /**
52542          * @event keypress
52543          * The raw keypress event for the entire grid.
52544          * @param {Roo.EventObject} e
52545          */
52546         "keypress" : true,
52547         /**
52548          * @event keydown
52549          * The raw keydown event for the entire grid.
52550          * @param {Roo.EventObject} e
52551          */
52552         "keydown" : true,
52553
52554         // custom events
52555
52556         /**
52557          * @event cellclick
52558          * Fires when a cell is clicked
52559          * @param {Grid} this
52560          * @param {Number} rowIndex
52561          * @param {Number} columnIndex
52562          * @param {Roo.EventObject} e
52563          */
52564         "cellclick" : true,
52565         /**
52566          * @event celldblclick
52567          * Fires when a cell is double clicked
52568          * @param {Grid} this
52569          * @param {Number} rowIndex
52570          * @param {Number} columnIndex
52571          * @param {Roo.EventObject} e
52572          */
52573         "celldblclick" : true,
52574         /**
52575          * @event rowclick
52576          * Fires when a row is clicked
52577          * @param {Grid} this
52578          * @param {Number} rowIndex
52579          * @param {Roo.EventObject} e
52580          */
52581         "rowclick" : true,
52582         /**
52583          * @event rowdblclick
52584          * Fires when a row is double clicked
52585          * @param {Grid} this
52586          * @param {Number} rowIndex
52587          * @param {Roo.EventObject} e
52588          */
52589         "rowdblclick" : true,
52590         /**
52591          * @event headerclick
52592          * Fires when a header is clicked
52593          * @param {Grid} this
52594          * @param {Number} columnIndex
52595          * @param {Roo.EventObject} e
52596          */
52597         "headerclick" : true,
52598         /**
52599          * @event headerdblclick
52600          * Fires when a header cell is double clicked
52601          * @param {Grid} this
52602          * @param {Number} columnIndex
52603          * @param {Roo.EventObject} e
52604          */
52605         "headerdblclick" : true,
52606         /**
52607          * @event rowcontextmenu
52608          * Fires when a row is right clicked
52609          * @param {Grid} this
52610          * @param {Number} rowIndex
52611          * @param {Roo.EventObject} e
52612          */
52613         "rowcontextmenu" : true,
52614         /**
52615          * @event cellcontextmenu
52616          * Fires when a cell is right clicked
52617          * @param {Grid} this
52618          * @param {Number} rowIndex
52619          * @param {Number} cellIndex
52620          * @param {Roo.EventObject} e
52621          */
52622          "cellcontextmenu" : true,
52623         /**
52624          * @event headercontextmenu
52625          * Fires when a header is right clicked
52626          * @param {Grid} this
52627          * @param {Number} columnIndex
52628          * @param {Roo.EventObject} e
52629          */
52630         "headercontextmenu" : true,
52631         /**
52632          * @event bodyscroll
52633          * Fires when the body element is scrolled
52634          * @param {Number} scrollLeft
52635          * @param {Number} scrollTop
52636          */
52637         "bodyscroll" : true,
52638         /**
52639          * @event columnresize
52640          * Fires when the user resizes a column
52641          * @param {Number} columnIndex
52642          * @param {Number} newSize
52643          */
52644         "columnresize" : true,
52645         /**
52646          * @event columnmove
52647          * Fires when the user moves a column
52648          * @param {Number} oldIndex
52649          * @param {Number} newIndex
52650          */
52651         "columnmove" : true,
52652         /**
52653          * @event startdrag
52654          * Fires when row(s) start being dragged
52655          * @param {Grid} this
52656          * @param {Roo.GridDD} dd The drag drop object
52657          * @param {event} e The raw browser event
52658          */
52659         "startdrag" : true,
52660         /**
52661          * @event enddrag
52662          * Fires when a drag operation is complete
52663          * @param {Grid} this
52664          * @param {Roo.GridDD} dd The drag drop object
52665          * @param {event} e The raw browser event
52666          */
52667         "enddrag" : true,
52668         /**
52669          * @event dragdrop
52670          * Fires when dragged row(s) are dropped on a valid DD target
52671          * @param {Grid} this
52672          * @param {Roo.GridDD} dd The drag drop object
52673          * @param {String} targetId The target drag drop object
52674          * @param {event} e The raw browser event
52675          */
52676         "dragdrop" : true,
52677         /**
52678          * @event dragover
52679          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52680          * @param {Grid} this
52681          * @param {Roo.GridDD} dd The drag drop object
52682          * @param {String} targetId The target drag drop object
52683          * @param {event} e The raw browser event
52684          */
52685         "dragover" : true,
52686         /**
52687          * @event dragenter
52688          *  Fires when the dragged row(s) first cross another DD target while being dragged
52689          * @param {Grid} this
52690          * @param {Roo.GridDD} dd The drag drop object
52691          * @param {String} targetId The target drag drop object
52692          * @param {event} e The raw browser event
52693          */
52694         "dragenter" : true,
52695         /**
52696          * @event dragout
52697          * Fires when the dragged row(s) leave another DD target while being dragged
52698          * @param {Grid} this
52699          * @param {Roo.GridDD} dd The drag drop object
52700          * @param {String} targetId The target drag drop object
52701          * @param {event} e The raw browser event
52702          */
52703         "dragout" : true,
52704         /**
52705          * @event rowclass
52706          * Fires when a row is rendered, so you can change add a style to it.
52707          * @param {GridView} gridview   The grid view
52708          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52709          */
52710         'rowclass' : true,
52711
52712         /**
52713          * @event render
52714          * Fires when the grid is rendered
52715          * @param {Grid} grid
52716          */
52717         'render' : true
52718     });
52719
52720     Roo.grid.Grid.superclass.constructor.call(this);
52721 };
52722 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52723     
52724     /**
52725      * @cfg {String} ddGroup - drag drop group.
52726      */
52727
52728     /**
52729      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52730      */
52731     minColumnWidth : 25,
52732
52733     /**
52734      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52735      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52736      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52737      */
52738     autoSizeColumns : false,
52739
52740     /**
52741      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52742      */
52743     autoSizeHeaders : true,
52744
52745     /**
52746      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52747      */
52748     monitorWindowResize : true,
52749
52750     /**
52751      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52752      * rows measured to get a columns size. Default is 0 (all rows).
52753      */
52754     maxRowsToMeasure : 0,
52755
52756     /**
52757      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52758      */
52759     trackMouseOver : true,
52760
52761     /**
52762     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52763     */
52764     
52765     /**
52766     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52767     */
52768     enableDragDrop : false,
52769     
52770     /**
52771     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52772     */
52773     enableColumnMove : true,
52774     
52775     /**
52776     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52777     */
52778     enableColumnHide : true,
52779     
52780     /**
52781     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52782     */
52783     enableRowHeightSync : false,
52784     
52785     /**
52786     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52787     */
52788     stripeRows : true,
52789     
52790     /**
52791     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52792     */
52793     autoHeight : false,
52794
52795     /**
52796      * @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.
52797      */
52798     autoExpandColumn : false,
52799
52800     /**
52801     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52802     * Default is 50.
52803     */
52804     autoExpandMin : 50,
52805
52806     /**
52807     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52808     */
52809     autoExpandMax : 1000,
52810
52811     /**
52812     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52813     */
52814     view : null,
52815
52816     /**
52817     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52818     */
52819     loadMask : false,
52820     /**
52821     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52822     */
52823     dropTarget: false,
52824     
52825    
52826     
52827     // private
52828     rendered : false,
52829
52830     /**
52831     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52832     * of a fixed width. Default is false.
52833     */
52834     /**
52835     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52836     */
52837     /**
52838      * Called once after all setup has been completed and the grid is ready to be rendered.
52839      * @return {Roo.grid.Grid} this
52840      */
52841     render : function()
52842     {
52843         var c = this.container;
52844         // try to detect autoHeight/width mode
52845         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52846             this.autoHeight = true;
52847         }
52848         var view = this.getView();
52849         view.init(this);
52850
52851         c.on("click", this.onClick, this);
52852         c.on("dblclick", this.onDblClick, this);
52853         c.on("contextmenu", this.onContextMenu, this);
52854         c.on("keydown", this.onKeyDown, this);
52855         if (Roo.isTouch) {
52856             c.on("touchstart", this.onTouchStart, this);
52857         }
52858
52859         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52860
52861         this.getSelectionModel().init(this);
52862
52863         view.render();
52864
52865         if(this.loadMask){
52866             this.loadMask = new Roo.LoadMask(this.container,
52867                     Roo.apply({store:this.dataSource}, this.loadMask));
52868         }
52869         
52870         
52871         if (this.toolbar && this.toolbar.xtype) {
52872             this.toolbar.container = this.getView().getHeaderPanel(true);
52873             this.toolbar = new Roo.Toolbar(this.toolbar);
52874         }
52875         if (this.footer && this.footer.xtype) {
52876             this.footer.dataSource = this.getDataSource();
52877             this.footer.container = this.getView().getFooterPanel(true);
52878             this.footer = Roo.factory(this.footer, Roo);
52879         }
52880         if (this.dropTarget && this.dropTarget.xtype) {
52881             delete this.dropTarget.xtype;
52882             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52883         }
52884         
52885         
52886         this.rendered = true;
52887         this.fireEvent('render', this);
52888         return this;
52889     },
52890
52891         /**
52892          * Reconfigures the grid to use a different Store and Column Model.
52893          * The View will be bound to the new objects and refreshed.
52894          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52895          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52896          */
52897     reconfigure : function(dataSource, colModel){
52898         if(this.loadMask){
52899             this.loadMask.destroy();
52900             this.loadMask = new Roo.LoadMask(this.container,
52901                     Roo.apply({store:dataSource}, this.loadMask));
52902         }
52903         this.view.bind(dataSource, colModel);
52904         this.dataSource = dataSource;
52905         this.colModel = colModel;
52906         this.view.refresh(true);
52907     },
52908
52909     // private
52910     onKeyDown : function(e){
52911         this.fireEvent("keydown", e);
52912     },
52913
52914     /**
52915      * Destroy this grid.
52916      * @param {Boolean} removeEl True to remove the element
52917      */
52918     destroy : function(removeEl, keepListeners){
52919         if(this.loadMask){
52920             this.loadMask.destroy();
52921         }
52922         var c = this.container;
52923         c.removeAllListeners();
52924         this.view.destroy();
52925         this.colModel.purgeListeners();
52926         if(!keepListeners){
52927             this.purgeListeners();
52928         }
52929         c.update("");
52930         if(removeEl === true){
52931             c.remove();
52932         }
52933     },
52934
52935     // private
52936     processEvent : function(name, e){
52937         // does this fire select???
52938         //Roo.log('grid:processEvent '  + name);
52939         
52940         if (name != 'touchstart' ) {
52941             this.fireEvent(name, e);    
52942         }
52943         
52944         var t = e.getTarget();
52945         var v = this.view;
52946         var header = v.findHeaderIndex(t);
52947         if(header !== false){
52948             var ename = name == 'touchstart' ? 'click' : name;
52949              
52950             this.fireEvent("header" + ename, this, header, e);
52951         }else{
52952             var row = v.findRowIndex(t);
52953             var cell = v.findCellIndex(t);
52954             if (name == 'touchstart') {
52955                 // first touch is always a click.
52956                 // hopefull this happens after selection is updated.?
52957                 name = false;
52958                 
52959                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52960                     var cs = this.selModel.getSelectedCell();
52961                     if (row == cs[0] && cell == cs[1]){
52962                         name = 'dblclick';
52963                     }
52964                 }
52965                 if (typeof(this.selModel.getSelections) != 'undefined') {
52966                     var cs = this.selModel.getSelections();
52967                     var ds = this.dataSource;
52968                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52969                         name = 'dblclick';
52970                     }
52971                 }
52972                 if (!name) {
52973                     return;
52974                 }
52975             }
52976             
52977             
52978             if(row !== false){
52979                 this.fireEvent("row" + name, this, row, e);
52980                 if(cell !== false){
52981                     this.fireEvent("cell" + name, this, row, cell, e);
52982                 }
52983             }
52984         }
52985     },
52986
52987     // private
52988     onClick : function(e){
52989         this.processEvent("click", e);
52990     },
52991    // private
52992     onTouchStart : function(e){
52993         this.processEvent("touchstart", e);
52994     },
52995
52996     // private
52997     onContextMenu : function(e, t){
52998         this.processEvent("contextmenu", e);
52999     },
53000
53001     // private
53002     onDblClick : function(e){
53003         this.processEvent("dblclick", e);
53004     },
53005
53006     // private
53007     walkCells : function(row, col, step, fn, scope){
53008         var cm = this.colModel, clen = cm.getColumnCount();
53009         var ds = this.dataSource, rlen = ds.getCount(), first = true;
53010         if(step < 0){
53011             if(col < 0){
53012                 row--;
53013                 first = false;
53014             }
53015             while(row >= 0){
53016                 if(!first){
53017                     col = clen-1;
53018                 }
53019                 first = false;
53020                 while(col >= 0){
53021                     if(fn.call(scope || this, row, col, cm) === true){
53022                         return [row, col];
53023                     }
53024                     col--;
53025                 }
53026                 row--;
53027             }
53028         } else {
53029             if(col >= clen){
53030                 row++;
53031                 first = false;
53032             }
53033             while(row < rlen){
53034                 if(!first){
53035                     col = 0;
53036                 }
53037                 first = false;
53038                 while(col < clen){
53039                     if(fn.call(scope || this, row, col, cm) === true){
53040                         return [row, col];
53041                     }
53042                     col++;
53043                 }
53044                 row++;
53045             }
53046         }
53047         return null;
53048     },
53049
53050     // private
53051     getSelections : function(){
53052         return this.selModel.getSelections();
53053     },
53054
53055     /**
53056      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
53057      * but if manual update is required this method will initiate it.
53058      */
53059     autoSize : function(){
53060         if(this.rendered){
53061             this.view.layout();
53062             if(this.view.adjustForScroll){
53063                 this.view.adjustForScroll();
53064             }
53065         }
53066     },
53067
53068     /**
53069      * Returns the grid's underlying element.
53070      * @return {Element} The element
53071      */
53072     getGridEl : function(){
53073         return this.container;
53074     },
53075
53076     // private for compatibility, overridden by editor grid
53077     stopEditing : function(){},
53078
53079     /**
53080      * Returns the grid's SelectionModel.
53081      * @return {SelectionModel}
53082      */
53083     getSelectionModel : function(){
53084         if(!this.selModel){
53085             this.selModel = new Roo.grid.RowSelectionModel();
53086         }
53087         return this.selModel;
53088     },
53089
53090     /**
53091      * Returns the grid's DataSource.
53092      * @return {DataSource}
53093      */
53094     getDataSource : function(){
53095         return this.dataSource;
53096     },
53097
53098     /**
53099      * Returns the grid's ColumnModel.
53100      * @return {ColumnModel}
53101      */
53102     getColumnModel : function(){
53103         return this.colModel;
53104     },
53105
53106     /**
53107      * Returns the grid's GridView object.
53108      * @return {GridView}
53109      */
53110     getView : function(){
53111         if(!this.view){
53112             this.view = new Roo.grid.GridView(this.viewConfig);
53113         }
53114         return this.view;
53115     },
53116     /**
53117      * Called to get grid's drag proxy text, by default returns this.ddText.
53118      * @return {String}
53119      */
53120     getDragDropText : function(){
53121         var count = this.selModel.getCount();
53122         return String.format(this.ddText, count, count == 1 ? '' : 's');
53123     }
53124 });
53125 /**
53126  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
53127  * %0 is replaced with the number of selected rows.
53128  * @type String
53129  */
53130 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
53131  * Based on:
53132  * Ext JS Library 1.1.1
53133  * Copyright(c) 2006-2007, Ext JS, LLC.
53134  *
53135  * Originally Released Under LGPL - original licence link has changed is not relivant.
53136  *
53137  * Fork - LGPL
53138  * <script type="text/javascript">
53139  */
53140  
53141 Roo.grid.AbstractGridView = function(){
53142         this.grid = null;
53143         
53144         this.events = {
53145             "beforerowremoved" : true,
53146             "beforerowsinserted" : true,
53147             "beforerefresh" : true,
53148             "rowremoved" : true,
53149             "rowsinserted" : true,
53150             "rowupdated" : true,
53151             "refresh" : true
53152         };
53153     Roo.grid.AbstractGridView.superclass.constructor.call(this);
53154 };
53155
53156 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
53157     rowClass : "x-grid-row",
53158     cellClass : "x-grid-cell",
53159     tdClass : "x-grid-td",
53160     hdClass : "x-grid-hd",
53161     splitClass : "x-grid-hd-split",
53162     
53163     init: function(grid){
53164         this.grid = grid;
53165                 var cid = this.grid.getGridEl().id;
53166         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53167         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53168         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53169         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53170         },
53171         
53172     getColumnRenderers : function(){
53173         var renderers = [];
53174         var cm = this.grid.colModel;
53175         var colCount = cm.getColumnCount();
53176         for(var i = 0; i < colCount; i++){
53177             renderers[i] = cm.getRenderer(i);
53178         }
53179         return renderers;
53180     },
53181     
53182     getColumnIds : function(){
53183         var ids = [];
53184         var cm = this.grid.colModel;
53185         var colCount = cm.getColumnCount();
53186         for(var i = 0; i < colCount; i++){
53187             ids[i] = cm.getColumnId(i);
53188         }
53189         return ids;
53190     },
53191     
53192     getDataIndexes : function(){
53193         if(!this.indexMap){
53194             this.indexMap = this.buildIndexMap();
53195         }
53196         return this.indexMap.colToData;
53197     },
53198     
53199     getColumnIndexByDataIndex : function(dataIndex){
53200         if(!this.indexMap){
53201             this.indexMap = this.buildIndexMap();
53202         }
53203         return this.indexMap.dataToCol[dataIndex];
53204     },
53205     
53206     /**
53207      * Set a css style for a column dynamically. 
53208      * @param {Number} colIndex The index of the column
53209      * @param {String} name The css property name
53210      * @param {String} value The css value
53211      */
53212     setCSSStyle : function(colIndex, name, value){
53213         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53214         Roo.util.CSS.updateRule(selector, name, value);
53215     },
53216     
53217     generateRules : function(cm){
53218         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53219         Roo.util.CSS.removeStyleSheet(rulesId);
53220         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53221             var cid = cm.getColumnId(i);
53222             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53223                          this.tdSelector, cid, " {\n}\n",
53224                          this.hdSelector, cid, " {\n}\n",
53225                          this.splitSelector, cid, " {\n}\n");
53226         }
53227         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53228     }
53229 });/*
53230  * Based on:
53231  * Ext JS Library 1.1.1
53232  * Copyright(c) 2006-2007, Ext JS, LLC.
53233  *
53234  * Originally Released Under LGPL - original licence link has changed is not relivant.
53235  *
53236  * Fork - LGPL
53237  * <script type="text/javascript">
53238  */
53239
53240 // private
53241 // This is a support class used internally by the Grid components
53242 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53243     this.grid = grid;
53244     this.view = grid.getView();
53245     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53246     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53247     if(hd2){
53248         this.setHandleElId(Roo.id(hd));
53249         this.setOuterHandleElId(Roo.id(hd2));
53250     }
53251     this.scroll = false;
53252 };
53253 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53254     maxDragWidth: 120,
53255     getDragData : function(e){
53256         var t = Roo.lib.Event.getTarget(e);
53257         var h = this.view.findHeaderCell(t);
53258         if(h){
53259             return {ddel: h.firstChild, header:h};
53260         }
53261         return false;
53262     },
53263
53264     onInitDrag : function(e){
53265         this.view.headersDisabled = true;
53266         var clone = this.dragData.ddel.cloneNode(true);
53267         clone.id = Roo.id();
53268         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53269         this.proxy.update(clone);
53270         return true;
53271     },
53272
53273     afterValidDrop : function(){
53274         var v = this.view;
53275         setTimeout(function(){
53276             v.headersDisabled = false;
53277         }, 50);
53278     },
53279
53280     afterInvalidDrop : function(){
53281         var v = this.view;
53282         setTimeout(function(){
53283             v.headersDisabled = false;
53284         }, 50);
53285     }
53286 });
53287 /*
53288  * Based on:
53289  * Ext JS Library 1.1.1
53290  * Copyright(c) 2006-2007, Ext JS, LLC.
53291  *
53292  * Originally Released Under LGPL - original licence link has changed is not relivant.
53293  *
53294  * Fork - LGPL
53295  * <script type="text/javascript">
53296  */
53297 // private
53298 // This is a support class used internally by the Grid components
53299 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53300     this.grid = grid;
53301     this.view = grid.getView();
53302     // split the proxies so they don't interfere with mouse events
53303     this.proxyTop = Roo.DomHelper.append(document.body, {
53304         cls:"col-move-top", html:"&#160;"
53305     }, true);
53306     this.proxyBottom = Roo.DomHelper.append(document.body, {
53307         cls:"col-move-bottom", html:"&#160;"
53308     }, true);
53309     this.proxyTop.hide = this.proxyBottom.hide = function(){
53310         this.setLeftTop(-100,-100);
53311         this.setStyle("visibility", "hidden");
53312     };
53313     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53314     // temporarily disabled
53315     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53316     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53317 };
53318 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53319     proxyOffsets : [-4, -9],
53320     fly: Roo.Element.fly,
53321
53322     getTargetFromEvent : function(e){
53323         var t = Roo.lib.Event.getTarget(e);
53324         var cindex = this.view.findCellIndex(t);
53325         if(cindex !== false){
53326             return this.view.getHeaderCell(cindex);
53327         }
53328         return null;
53329     },
53330
53331     nextVisible : function(h){
53332         var v = this.view, cm = this.grid.colModel;
53333         h = h.nextSibling;
53334         while(h){
53335             if(!cm.isHidden(v.getCellIndex(h))){
53336                 return h;
53337             }
53338             h = h.nextSibling;
53339         }
53340         return null;
53341     },
53342
53343     prevVisible : function(h){
53344         var v = this.view, cm = this.grid.colModel;
53345         h = h.prevSibling;
53346         while(h){
53347             if(!cm.isHidden(v.getCellIndex(h))){
53348                 return h;
53349             }
53350             h = h.prevSibling;
53351         }
53352         return null;
53353     },
53354
53355     positionIndicator : function(h, n, e){
53356         var x = Roo.lib.Event.getPageX(e);
53357         var r = Roo.lib.Dom.getRegion(n.firstChild);
53358         var px, pt, py = r.top + this.proxyOffsets[1];
53359         if((r.right - x) <= (r.right-r.left)/2){
53360             px = r.right+this.view.borderWidth;
53361             pt = "after";
53362         }else{
53363             px = r.left;
53364             pt = "before";
53365         }
53366         var oldIndex = this.view.getCellIndex(h);
53367         var newIndex = this.view.getCellIndex(n);
53368
53369         if(this.grid.colModel.isFixed(newIndex)){
53370             return false;
53371         }
53372
53373         var locked = this.grid.colModel.isLocked(newIndex);
53374
53375         if(pt == "after"){
53376             newIndex++;
53377         }
53378         if(oldIndex < newIndex){
53379             newIndex--;
53380         }
53381         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53382             return false;
53383         }
53384         px +=  this.proxyOffsets[0];
53385         this.proxyTop.setLeftTop(px, py);
53386         this.proxyTop.show();
53387         if(!this.bottomOffset){
53388             this.bottomOffset = this.view.mainHd.getHeight();
53389         }
53390         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53391         this.proxyBottom.show();
53392         return pt;
53393     },
53394
53395     onNodeEnter : function(n, dd, e, data){
53396         if(data.header != n){
53397             this.positionIndicator(data.header, n, e);
53398         }
53399     },
53400
53401     onNodeOver : function(n, dd, e, data){
53402         var result = false;
53403         if(data.header != n){
53404             result = this.positionIndicator(data.header, n, e);
53405         }
53406         if(!result){
53407             this.proxyTop.hide();
53408             this.proxyBottom.hide();
53409         }
53410         return result ? this.dropAllowed : this.dropNotAllowed;
53411     },
53412
53413     onNodeOut : function(n, dd, e, data){
53414         this.proxyTop.hide();
53415         this.proxyBottom.hide();
53416     },
53417
53418     onNodeDrop : function(n, dd, e, data){
53419         var h = data.header;
53420         if(h != n){
53421             var cm = this.grid.colModel;
53422             var x = Roo.lib.Event.getPageX(e);
53423             var r = Roo.lib.Dom.getRegion(n.firstChild);
53424             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53425             var oldIndex = this.view.getCellIndex(h);
53426             var newIndex = this.view.getCellIndex(n);
53427             var locked = cm.isLocked(newIndex);
53428             if(pt == "after"){
53429                 newIndex++;
53430             }
53431             if(oldIndex < newIndex){
53432                 newIndex--;
53433             }
53434             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53435                 return false;
53436             }
53437             cm.setLocked(oldIndex, locked, true);
53438             cm.moveColumn(oldIndex, newIndex);
53439             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53440             return true;
53441         }
53442         return false;
53443     }
53444 });
53445 /*
53446  * Based on:
53447  * Ext JS Library 1.1.1
53448  * Copyright(c) 2006-2007, Ext JS, LLC.
53449  *
53450  * Originally Released Under LGPL - original licence link has changed is not relivant.
53451  *
53452  * Fork - LGPL
53453  * <script type="text/javascript">
53454  */
53455   
53456 /**
53457  * @class Roo.grid.GridView
53458  * @extends Roo.util.Observable
53459  *
53460  * @constructor
53461  * @param {Object} config
53462  */
53463 Roo.grid.GridView = function(config){
53464     Roo.grid.GridView.superclass.constructor.call(this);
53465     this.el = null;
53466
53467     Roo.apply(this, config);
53468 };
53469
53470 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53471
53472     unselectable :  'unselectable="on"',
53473     unselectableCls :  'x-unselectable',
53474     
53475     
53476     rowClass : "x-grid-row",
53477
53478     cellClass : "x-grid-col",
53479
53480     tdClass : "x-grid-td",
53481
53482     hdClass : "x-grid-hd",
53483
53484     splitClass : "x-grid-split",
53485
53486     sortClasses : ["sort-asc", "sort-desc"],
53487
53488     enableMoveAnim : false,
53489
53490     hlColor: "C3DAF9",
53491
53492     dh : Roo.DomHelper,
53493
53494     fly : Roo.Element.fly,
53495
53496     css : Roo.util.CSS,
53497
53498     borderWidth: 1,
53499
53500     splitOffset: 3,
53501
53502     scrollIncrement : 22,
53503
53504     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53505
53506     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53507
53508     bind : function(ds, cm){
53509         if(this.ds){
53510             this.ds.un("load", this.onLoad, this);
53511             this.ds.un("datachanged", this.onDataChange, this);
53512             this.ds.un("add", this.onAdd, this);
53513             this.ds.un("remove", this.onRemove, this);
53514             this.ds.un("update", this.onUpdate, this);
53515             this.ds.un("clear", this.onClear, this);
53516         }
53517         if(ds){
53518             ds.on("load", this.onLoad, this);
53519             ds.on("datachanged", this.onDataChange, this);
53520             ds.on("add", this.onAdd, this);
53521             ds.on("remove", this.onRemove, this);
53522             ds.on("update", this.onUpdate, this);
53523             ds.on("clear", this.onClear, this);
53524         }
53525         this.ds = ds;
53526
53527         if(this.cm){
53528             this.cm.un("widthchange", this.onColWidthChange, this);
53529             this.cm.un("headerchange", this.onHeaderChange, this);
53530             this.cm.un("hiddenchange", this.onHiddenChange, this);
53531             this.cm.un("columnmoved", this.onColumnMove, this);
53532             this.cm.un("columnlockchange", this.onColumnLock, this);
53533         }
53534         if(cm){
53535             this.generateRules(cm);
53536             cm.on("widthchange", this.onColWidthChange, this);
53537             cm.on("headerchange", this.onHeaderChange, this);
53538             cm.on("hiddenchange", this.onHiddenChange, this);
53539             cm.on("columnmoved", this.onColumnMove, this);
53540             cm.on("columnlockchange", this.onColumnLock, this);
53541         }
53542         this.cm = cm;
53543     },
53544
53545     init: function(grid){
53546         Roo.grid.GridView.superclass.init.call(this, grid);
53547
53548         this.bind(grid.dataSource, grid.colModel);
53549
53550         grid.on("headerclick", this.handleHeaderClick, this);
53551
53552         if(grid.trackMouseOver){
53553             grid.on("mouseover", this.onRowOver, this);
53554             grid.on("mouseout", this.onRowOut, this);
53555         }
53556         grid.cancelTextSelection = function(){};
53557         this.gridId = grid.id;
53558
53559         var tpls = this.templates || {};
53560
53561         if(!tpls.master){
53562             tpls.master = new Roo.Template(
53563                '<div class="x-grid" hidefocus="true">',
53564                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53565                   '<div class="x-grid-topbar"></div>',
53566                   '<div class="x-grid-scroller"><div></div></div>',
53567                   '<div class="x-grid-locked">',
53568                       '<div class="x-grid-header">{lockedHeader}</div>',
53569                       '<div class="x-grid-body">{lockedBody}</div>',
53570                   "</div>",
53571                   '<div class="x-grid-viewport">',
53572                       '<div class="x-grid-header">{header}</div>',
53573                       '<div class="x-grid-body">{body}</div>',
53574                   "</div>",
53575                   '<div class="x-grid-bottombar"></div>',
53576                  
53577                   '<div class="x-grid-resize-proxy">&#160;</div>',
53578                "</div>"
53579             );
53580             tpls.master.disableformats = true;
53581         }
53582
53583         if(!tpls.header){
53584             tpls.header = new Roo.Template(
53585                '<table border="0" cellspacing="0" cellpadding="0">',
53586                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53587                "</table>{splits}"
53588             );
53589             tpls.header.disableformats = true;
53590         }
53591         tpls.header.compile();
53592
53593         if(!tpls.hcell){
53594             tpls.hcell = new Roo.Template(
53595                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div qtip="{title}" title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53596                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53597                 "</div></td>"
53598              );
53599              tpls.hcell.disableFormats = true;
53600         }
53601         tpls.hcell.compile();
53602
53603         if(!tpls.hsplit){
53604             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53605                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53606             tpls.hsplit.disableFormats = true;
53607         }
53608         tpls.hsplit.compile();
53609
53610         if(!tpls.body){
53611             tpls.body = new Roo.Template(
53612                '<table border="0" cellspacing="0" cellpadding="0">',
53613                "<tbody>{rows}</tbody>",
53614                "</table>"
53615             );
53616             tpls.body.disableFormats = true;
53617         }
53618         tpls.body.compile();
53619
53620         if(!tpls.row){
53621             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53622             tpls.row.disableFormats = true;
53623         }
53624         tpls.row.compile();
53625
53626         if(!tpls.cell){
53627             tpls.cell = new Roo.Template(
53628                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53629                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53630                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53631                 "</td>"
53632             );
53633             tpls.cell.disableFormats = true;
53634         }
53635         tpls.cell.compile();
53636
53637         this.templates = tpls;
53638     },
53639
53640     // remap these for backwards compat
53641     onColWidthChange : function(){
53642         this.updateColumns.apply(this, arguments);
53643     },
53644     onHeaderChange : function(){
53645         this.updateHeaders.apply(this, arguments);
53646     }, 
53647     onHiddenChange : function(){
53648         this.handleHiddenChange.apply(this, arguments);
53649     },
53650     onColumnMove : function(){
53651         this.handleColumnMove.apply(this, arguments);
53652     },
53653     onColumnLock : function(){
53654         this.handleLockChange.apply(this, arguments);
53655     },
53656
53657     onDataChange : function(){
53658         this.refresh();
53659         this.updateHeaderSortState();
53660     },
53661
53662     onClear : function(){
53663         this.refresh();
53664     },
53665
53666     onUpdate : function(ds, record){
53667         this.refreshRow(record);
53668     },
53669
53670     refreshRow : function(record){
53671         var ds = this.ds, index;
53672         if(typeof record == 'number'){
53673             index = record;
53674             record = ds.getAt(index);
53675         }else{
53676             index = ds.indexOf(record);
53677         }
53678         this.insertRows(ds, index, index, true);
53679         this.onRemove(ds, record, index+1, true);
53680         this.syncRowHeights(index, index);
53681         this.layout();
53682         this.fireEvent("rowupdated", this, index, record);
53683     },
53684
53685     onAdd : function(ds, records, index){
53686         this.insertRows(ds, index, index + (records.length-1));
53687     },
53688
53689     onRemove : function(ds, record, index, isUpdate){
53690         if(isUpdate !== true){
53691             this.fireEvent("beforerowremoved", this, index, record);
53692         }
53693         var bt = this.getBodyTable(), lt = this.getLockedTable();
53694         if(bt.rows[index]){
53695             bt.firstChild.removeChild(bt.rows[index]);
53696         }
53697         if(lt.rows[index]){
53698             lt.firstChild.removeChild(lt.rows[index]);
53699         }
53700         if(isUpdate !== true){
53701             this.stripeRows(index);
53702             this.syncRowHeights(index, index);
53703             this.layout();
53704             this.fireEvent("rowremoved", this, index, record);
53705         }
53706     },
53707
53708     onLoad : function(){
53709         this.scrollToTop();
53710     },
53711
53712     /**
53713      * Scrolls the grid to the top
53714      */
53715     scrollToTop : function(){
53716         if(this.scroller){
53717             this.scroller.dom.scrollTop = 0;
53718             this.syncScroll();
53719         }
53720     },
53721
53722     /**
53723      * Gets a panel in the header of the grid that can be used for toolbars etc.
53724      * After modifying the contents of this panel a call to grid.autoSize() may be
53725      * required to register any changes in size.
53726      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53727      * @return Roo.Element
53728      */
53729     getHeaderPanel : function(doShow){
53730         if(doShow){
53731             this.headerPanel.show();
53732         }
53733         return this.headerPanel;
53734     },
53735
53736     /**
53737      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53738      * After modifying the contents of this panel a call to grid.autoSize() may be
53739      * required to register any changes in size.
53740      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53741      * @return Roo.Element
53742      */
53743     getFooterPanel : function(doShow){
53744         if(doShow){
53745             this.footerPanel.show();
53746         }
53747         return this.footerPanel;
53748     },
53749
53750     initElements : function(){
53751         var E = Roo.Element;
53752         var el = this.grid.getGridEl().dom.firstChild;
53753         var cs = el.childNodes;
53754
53755         this.el = new E(el);
53756         
53757          this.focusEl = new E(el.firstChild);
53758         this.focusEl.swallowEvent("click", true);
53759         
53760         this.headerPanel = new E(cs[1]);
53761         this.headerPanel.enableDisplayMode("block");
53762
53763         this.scroller = new E(cs[2]);
53764         this.scrollSizer = new E(this.scroller.dom.firstChild);
53765
53766         this.lockedWrap = new E(cs[3]);
53767         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53768         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53769
53770         this.mainWrap = new E(cs[4]);
53771         this.mainHd = new E(this.mainWrap.dom.firstChild);
53772         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53773
53774         this.footerPanel = new E(cs[5]);
53775         this.footerPanel.enableDisplayMode("block");
53776
53777         this.resizeProxy = new E(cs[6]);
53778
53779         this.headerSelector = String.format(
53780            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53781            this.lockedHd.id, this.mainHd.id
53782         );
53783
53784         this.splitterSelector = String.format(
53785            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53786            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53787         );
53788     },
53789     idToCssName : function(s)
53790     {
53791         return s.replace(/[^a-z0-9]+/ig, '-');
53792     },
53793
53794     getHeaderCell : function(index){
53795         return Roo.DomQuery.select(this.headerSelector)[index];
53796     },
53797
53798     getHeaderCellMeasure : function(index){
53799         return this.getHeaderCell(index).firstChild;
53800     },
53801
53802     getHeaderCellText : function(index){
53803         return this.getHeaderCell(index).firstChild.firstChild;
53804     },
53805
53806     getLockedTable : function(){
53807         return this.lockedBody.dom.firstChild;
53808     },
53809
53810     getBodyTable : function(){
53811         return this.mainBody.dom.firstChild;
53812     },
53813
53814     getLockedRow : function(index){
53815         return this.getLockedTable().rows[index];
53816     },
53817
53818     getRow : function(index){
53819         return this.getBodyTable().rows[index];
53820     },
53821
53822     getRowComposite : function(index){
53823         if(!this.rowEl){
53824             this.rowEl = new Roo.CompositeElementLite();
53825         }
53826         var els = [], lrow, mrow;
53827         if(lrow = this.getLockedRow(index)){
53828             els.push(lrow);
53829         }
53830         if(mrow = this.getRow(index)){
53831             els.push(mrow);
53832         }
53833         this.rowEl.elements = els;
53834         return this.rowEl;
53835     },
53836     /**
53837      * Gets the 'td' of the cell
53838      * 
53839      * @param {Integer} rowIndex row to select
53840      * @param {Integer} colIndex column to select
53841      * 
53842      * @return {Object} 
53843      */
53844     getCell : function(rowIndex, colIndex){
53845         var locked = this.cm.getLockedCount();
53846         var source;
53847         if(colIndex < locked){
53848             source = this.lockedBody.dom.firstChild;
53849         }else{
53850             source = this.mainBody.dom.firstChild;
53851             colIndex -= locked;
53852         }
53853         return source.rows[rowIndex].childNodes[colIndex];
53854     },
53855
53856     getCellText : function(rowIndex, colIndex){
53857         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53858     },
53859
53860     getCellBox : function(cell){
53861         var b = this.fly(cell).getBox();
53862         if(Roo.isOpera){ // opera fails to report the Y
53863             b.y = cell.offsetTop + this.mainBody.getY();
53864         }
53865         return b;
53866     },
53867
53868     getCellIndex : function(cell){
53869         var id = String(cell.className).match(this.cellRE);
53870         if(id){
53871             return parseInt(id[1], 10);
53872         }
53873         return 0;
53874     },
53875
53876     findHeaderIndex : function(n){
53877         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53878         return r ? this.getCellIndex(r) : false;
53879     },
53880
53881     findHeaderCell : function(n){
53882         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53883         return r ? r : false;
53884     },
53885
53886     findRowIndex : function(n){
53887         if(!n){
53888             return false;
53889         }
53890         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53891         return r ? r.rowIndex : false;
53892     },
53893
53894     findCellIndex : function(node){
53895         var stop = this.el.dom;
53896         while(node && node != stop){
53897             if(this.findRE.test(node.className)){
53898                 return this.getCellIndex(node);
53899             }
53900             node = node.parentNode;
53901         }
53902         return false;
53903     },
53904
53905     getColumnId : function(index){
53906         return this.cm.getColumnId(index);
53907     },
53908
53909     getSplitters : function()
53910     {
53911         if(this.splitterSelector){
53912            return Roo.DomQuery.select(this.splitterSelector);
53913         }else{
53914             return null;
53915       }
53916     },
53917
53918     getSplitter : function(index){
53919         return this.getSplitters()[index];
53920     },
53921
53922     onRowOver : function(e, t){
53923         var row;
53924         if((row = this.findRowIndex(t)) !== false){
53925             this.getRowComposite(row).addClass("x-grid-row-over");
53926         }
53927     },
53928
53929     onRowOut : function(e, t){
53930         var row;
53931         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53932             this.getRowComposite(row).removeClass("x-grid-row-over");
53933         }
53934     },
53935
53936     renderHeaders : function(){
53937         var cm = this.cm;
53938         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53939         var cb = [], lb = [], sb = [], lsb = [], p = {};
53940         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53941             p.cellId = "x-grid-hd-0-" + i;
53942             p.splitId = "x-grid-csplit-0-" + i;
53943             p.id = cm.getColumnId(i);
53944             p.title = cm.getColumnTooltip(i) || "";
53945             p.value = cm.getColumnHeader(i) || "";
53946             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53947             if(!cm.isLocked(i)){
53948                 cb[cb.length] = ct.apply(p);
53949                 sb[sb.length] = st.apply(p);
53950             }else{
53951                 lb[lb.length] = ct.apply(p);
53952                 lsb[lsb.length] = st.apply(p);
53953             }
53954         }
53955         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53956                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53957     },
53958
53959     updateHeaders : function(){
53960         var html = this.renderHeaders();
53961         this.lockedHd.update(html[0]);
53962         this.mainHd.update(html[1]);
53963     },
53964
53965     /**
53966      * Focuses the specified row.
53967      * @param {Number} row The row index
53968      */
53969     focusRow : function(row)
53970     {
53971         //Roo.log('GridView.focusRow');
53972         var x = this.scroller.dom.scrollLeft;
53973         this.focusCell(row, 0, false);
53974         this.scroller.dom.scrollLeft = x;
53975     },
53976
53977     /**
53978      * Focuses the specified cell.
53979      * @param {Number} row The row index
53980      * @param {Number} col The column index
53981      * @param {Boolean} hscroll false to disable horizontal scrolling
53982      */
53983     focusCell : function(row, col, hscroll)
53984     {
53985         //Roo.log('GridView.focusCell');
53986         var el = this.ensureVisible(row, col, hscroll);
53987         this.focusEl.alignTo(el, "tl-tl");
53988         if(Roo.isGecko){
53989             this.focusEl.focus();
53990         }else{
53991             this.focusEl.focus.defer(1, this.focusEl);
53992         }
53993     },
53994
53995     /**
53996      * Scrolls the specified cell into view
53997      * @param {Number} row The row index
53998      * @param {Number} col The column index
53999      * @param {Boolean} hscroll false to disable horizontal scrolling
54000      */
54001     ensureVisible : function(row, col, hscroll)
54002     {
54003         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
54004         //return null; //disable for testing.
54005         if(typeof row != "number"){
54006             row = row.rowIndex;
54007         }
54008         if(row < 0 && row >= this.ds.getCount()){
54009             return  null;
54010         }
54011         col = (col !== undefined ? col : 0);
54012         var cm = this.grid.colModel;
54013         while(cm.isHidden(col)){
54014             col++;
54015         }
54016
54017         var el = this.getCell(row, col);
54018         if(!el){
54019             return null;
54020         }
54021         var c = this.scroller.dom;
54022
54023         var ctop = parseInt(el.offsetTop, 10);
54024         var cleft = parseInt(el.offsetLeft, 10);
54025         var cbot = ctop + el.offsetHeight;
54026         var cright = cleft + el.offsetWidth;
54027         
54028         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
54029         var stop = parseInt(c.scrollTop, 10);
54030         var sleft = parseInt(c.scrollLeft, 10);
54031         var sbot = stop + ch;
54032         var sright = sleft + c.clientWidth;
54033         /*
54034         Roo.log('GridView.ensureVisible:' +
54035                 ' ctop:' + ctop +
54036                 ' c.clientHeight:' + c.clientHeight +
54037                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
54038                 ' stop:' + stop +
54039                 ' cbot:' + cbot +
54040                 ' sbot:' + sbot +
54041                 ' ch:' + ch  
54042                 );
54043         */
54044         if(ctop < stop){
54045              c.scrollTop = ctop;
54046             //Roo.log("set scrolltop to ctop DISABLE?");
54047         }else if(cbot > sbot){
54048             //Roo.log("set scrolltop to cbot-ch");
54049             c.scrollTop = cbot-ch;
54050         }
54051         
54052         if(hscroll !== false){
54053             if(cleft < sleft){
54054                 c.scrollLeft = cleft;
54055             }else if(cright > sright){
54056                 c.scrollLeft = cright-c.clientWidth;
54057             }
54058         }
54059          
54060         return el;
54061     },
54062
54063     updateColumns : function(){
54064         this.grid.stopEditing();
54065         var cm = this.grid.colModel, colIds = this.getColumnIds();
54066         //var totalWidth = cm.getTotalWidth();
54067         var pos = 0;
54068         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54069             //if(cm.isHidden(i)) continue;
54070             var w = cm.getColumnWidth(i);
54071             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
54072             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
54073         }
54074         this.updateSplitters();
54075     },
54076
54077     generateRules : function(cm){
54078         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
54079         Roo.util.CSS.removeStyleSheet(rulesId);
54080         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54081             var cid = cm.getColumnId(i);
54082             var align = '';
54083             if(cm.config[i].align){
54084                 align = 'text-align:'+cm.config[i].align+';';
54085             }
54086             var hidden = '';
54087             if(cm.isHidden(i)){
54088                 hidden = 'display:none;';
54089             }
54090             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
54091             ruleBuf.push(
54092                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
54093                     this.hdSelector, cid, " {\n", align, width, "}\n",
54094                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
54095                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
54096         }
54097         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54098     },
54099
54100     updateSplitters : function(){
54101         var cm = this.cm, s = this.getSplitters();
54102         if(s){ // splitters not created yet
54103             var pos = 0, locked = true;
54104             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54105                 if(cm.isHidden(i)) {
54106                     continue;
54107                 }
54108                 var w = cm.getColumnWidth(i); // make sure it's a number
54109                 if(!cm.isLocked(i) && locked){
54110                     pos = 0;
54111                     locked = false;
54112                 }
54113                 pos += w;
54114                 s[i].style.left = (pos-this.splitOffset) + "px";
54115             }
54116         }
54117     },
54118
54119     handleHiddenChange : function(colModel, colIndex, hidden){
54120         if(hidden){
54121             this.hideColumn(colIndex);
54122         }else{
54123             this.unhideColumn(colIndex);
54124         }
54125     },
54126
54127     hideColumn : function(colIndex){
54128         var cid = this.getColumnId(colIndex);
54129         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
54130         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
54131         if(Roo.isSafari){
54132             this.updateHeaders();
54133         }
54134         this.updateSplitters();
54135         this.layout();
54136     },
54137
54138     unhideColumn : function(colIndex){
54139         var cid = this.getColumnId(colIndex);
54140         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
54141         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
54142
54143         if(Roo.isSafari){
54144             this.updateHeaders();
54145         }
54146         this.updateSplitters();
54147         this.layout();
54148     },
54149
54150     insertRows : function(dm, firstRow, lastRow, isUpdate){
54151         if(firstRow == 0 && lastRow == dm.getCount()-1){
54152             this.refresh();
54153         }else{
54154             if(!isUpdate){
54155                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
54156             }
54157             var s = this.getScrollState();
54158             var markup = this.renderRows(firstRow, lastRow);
54159             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
54160             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54161             this.restoreScroll(s);
54162             if(!isUpdate){
54163                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54164                 this.syncRowHeights(firstRow, lastRow);
54165                 this.stripeRows(firstRow);
54166                 this.layout();
54167             }
54168         }
54169     },
54170
54171     bufferRows : function(markup, target, index){
54172         var before = null, trows = target.rows, tbody = target.tBodies[0];
54173         if(index < trows.length){
54174             before = trows[index];
54175         }
54176         var b = document.createElement("div");
54177         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54178         var rows = b.firstChild.rows;
54179         for(var i = 0, len = rows.length; i < len; i++){
54180             if(before){
54181                 tbody.insertBefore(rows[0], before);
54182             }else{
54183                 tbody.appendChild(rows[0]);
54184             }
54185         }
54186         b.innerHTML = "";
54187         b = null;
54188     },
54189
54190     deleteRows : function(dm, firstRow, lastRow){
54191         if(dm.getRowCount()<1){
54192             this.fireEvent("beforerefresh", this);
54193             this.mainBody.update("");
54194             this.lockedBody.update("");
54195             this.fireEvent("refresh", this);
54196         }else{
54197             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54198             var bt = this.getBodyTable();
54199             var tbody = bt.firstChild;
54200             var rows = bt.rows;
54201             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54202                 tbody.removeChild(rows[firstRow]);
54203             }
54204             this.stripeRows(firstRow);
54205             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54206         }
54207     },
54208
54209     updateRows : function(dataSource, firstRow, lastRow){
54210         var s = this.getScrollState();
54211         this.refresh();
54212         this.restoreScroll(s);
54213     },
54214
54215     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54216         if(!noRefresh){
54217            this.refresh();
54218         }
54219         this.updateHeaderSortState();
54220     },
54221
54222     getScrollState : function(){
54223         
54224         var sb = this.scroller.dom;
54225         return {left: sb.scrollLeft, top: sb.scrollTop};
54226     },
54227
54228     stripeRows : function(startRow){
54229         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54230             return;
54231         }
54232         startRow = startRow || 0;
54233         var rows = this.getBodyTable().rows;
54234         var lrows = this.getLockedTable().rows;
54235         var cls = ' x-grid-row-alt ';
54236         for(var i = startRow, len = rows.length; i < len; i++){
54237             var row = rows[i], lrow = lrows[i];
54238             var isAlt = ((i+1) % 2 == 0);
54239             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54240             if(isAlt == hasAlt){
54241                 continue;
54242             }
54243             if(isAlt){
54244                 row.className += " x-grid-row-alt";
54245             }else{
54246                 row.className = row.className.replace("x-grid-row-alt", "");
54247             }
54248             if(lrow){
54249                 lrow.className = row.className;
54250             }
54251         }
54252     },
54253
54254     restoreScroll : function(state){
54255         //Roo.log('GridView.restoreScroll');
54256         var sb = this.scroller.dom;
54257         sb.scrollLeft = state.left;
54258         sb.scrollTop = state.top;
54259         this.syncScroll();
54260     },
54261
54262     syncScroll : function(){
54263         //Roo.log('GridView.syncScroll');
54264         var sb = this.scroller.dom;
54265         var sh = this.mainHd.dom;
54266         var bs = this.mainBody.dom;
54267         var lv = this.lockedBody.dom;
54268         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54269         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54270     },
54271
54272     handleScroll : function(e){
54273         this.syncScroll();
54274         var sb = this.scroller.dom;
54275         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54276         e.stopEvent();
54277     },
54278
54279     handleWheel : function(e){
54280         var d = e.getWheelDelta();
54281         this.scroller.dom.scrollTop -= d*22;
54282         // set this here to prevent jumpy scrolling on large tables
54283         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54284         e.stopEvent();
54285     },
54286
54287     renderRows : function(startRow, endRow){
54288         // pull in all the crap needed to render rows
54289         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54290         var colCount = cm.getColumnCount();
54291
54292         if(ds.getCount() < 1){
54293             return ["", ""];
54294         }
54295
54296         // build a map for all the columns
54297         var cs = [];
54298         for(var i = 0; i < colCount; i++){
54299             var name = cm.getDataIndex(i);
54300             cs[i] = {
54301                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54302                 renderer : cm.getRenderer(i),
54303                 id : cm.getColumnId(i),
54304                 locked : cm.isLocked(i),
54305                 has_editor : cm.isCellEditable(i)
54306             };
54307         }
54308
54309         startRow = startRow || 0;
54310         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54311
54312         // records to render
54313         var rs = ds.getRange(startRow, endRow);
54314
54315         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54316     },
54317
54318     // As much as I hate to duplicate code, this was branched because FireFox really hates
54319     // [].join("") on strings. The performance difference was substantial enough to
54320     // branch this function
54321     doRender : Roo.isGecko ?
54322             function(cs, rs, ds, startRow, colCount, stripe){
54323                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54324                 // buffers
54325                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54326                 
54327                 var hasListener = this.grid.hasListener('rowclass');
54328                 var rowcfg = {};
54329                 for(var j = 0, len = rs.length; j < len; j++){
54330                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54331                     for(var i = 0; i < colCount; i++){
54332                         c = cs[i];
54333                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54334                         p.id = c.id;
54335                         p.css = p.attr = "";
54336                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54337                         if(p.value == undefined || p.value === "") {
54338                             p.value = "&#160;";
54339                         }
54340                         if(c.has_editor){
54341                             p.css += ' x-grid-editable-cell';
54342                         }
54343                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
54344                             p.css +=  ' x-grid-dirty-cell';
54345                         }
54346                         var markup = ct.apply(p);
54347                         if(!c.locked){
54348                             cb+= markup;
54349                         }else{
54350                             lcb+= markup;
54351                         }
54352                     }
54353                     var alt = [];
54354                     if(stripe && ((rowIndex+1) % 2 == 0)){
54355                         alt.push("x-grid-row-alt")
54356                     }
54357                     if(r.dirty){
54358                         alt.push(  " x-grid-dirty-row");
54359                     }
54360                     rp.cells = lcb;
54361                     if(this.getRowClass){
54362                         alt.push(this.getRowClass(r, rowIndex));
54363                     }
54364                     if (hasListener) {
54365                         rowcfg = {
54366                              
54367                             record: r,
54368                             rowIndex : rowIndex,
54369                             rowClass : ''
54370                         };
54371                         this.grid.fireEvent('rowclass', this, rowcfg);
54372                         alt.push(rowcfg.rowClass);
54373                     }
54374                     rp.alt = alt.join(" ");
54375                     lbuf+= rt.apply(rp);
54376                     rp.cells = cb;
54377                     buf+=  rt.apply(rp);
54378                 }
54379                 return [lbuf, buf];
54380             } :
54381             function(cs, rs, ds, startRow, colCount, stripe){
54382                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54383                 // buffers
54384                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54385                 var hasListener = this.grid.hasListener('rowclass');
54386  
54387                 var rowcfg = {};
54388                 for(var j = 0, len = rs.length; j < len; j++){
54389                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54390                     for(var i = 0; i < colCount; i++){
54391                         c = cs[i];
54392                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54393                         p.id = c.id;
54394                         p.css = p.attr = "";
54395                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54396                         if(p.value == undefined || p.value === "") {
54397                             p.value = "&#160;";
54398                         }
54399                         //Roo.log(c);
54400                          if(c.has_editor){
54401                             p.css += ' x-grid-editable-cell';
54402                         }
54403                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54404                             p.css += ' x-grid-dirty-cell' 
54405                         }
54406                         
54407                         var markup = ct.apply(p);
54408                         if(!c.locked){
54409                             cb[cb.length] = markup;
54410                         }else{
54411                             lcb[lcb.length] = markup;
54412                         }
54413                     }
54414                     var alt = [];
54415                     if(stripe && ((rowIndex+1) % 2 == 0)){
54416                         alt.push( "x-grid-row-alt");
54417                     }
54418                     if(r.dirty){
54419                         alt.push(" x-grid-dirty-row");
54420                     }
54421                     rp.cells = lcb;
54422                     if(this.getRowClass){
54423                         alt.push( this.getRowClass(r, rowIndex));
54424                     }
54425                     if (hasListener) {
54426                         rowcfg = {
54427                              
54428                             record: r,
54429                             rowIndex : rowIndex,
54430                             rowClass : ''
54431                         };
54432                         this.grid.fireEvent('rowclass', this, rowcfg);
54433                         alt.push(rowcfg.rowClass);
54434                     }
54435                     
54436                     rp.alt = alt.join(" ");
54437                     rp.cells = lcb.join("");
54438                     lbuf[lbuf.length] = rt.apply(rp);
54439                     rp.cells = cb.join("");
54440                     buf[buf.length] =  rt.apply(rp);
54441                 }
54442                 return [lbuf.join(""), buf.join("")];
54443             },
54444
54445     renderBody : function(){
54446         var markup = this.renderRows();
54447         var bt = this.templates.body;
54448         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54449     },
54450
54451     /**
54452      * Refreshes the grid
54453      * @param {Boolean} headersToo
54454      */
54455     refresh : function(headersToo){
54456         this.fireEvent("beforerefresh", this);
54457         this.grid.stopEditing();
54458         var result = this.renderBody();
54459         this.lockedBody.update(result[0]);
54460         this.mainBody.update(result[1]);
54461         if(headersToo === true){
54462             this.updateHeaders();
54463             this.updateColumns();
54464             this.updateSplitters();
54465             this.updateHeaderSortState();
54466         }
54467         this.syncRowHeights();
54468         this.layout();
54469         this.fireEvent("refresh", this);
54470     },
54471
54472     handleColumnMove : function(cm, oldIndex, newIndex){
54473         this.indexMap = null;
54474         var s = this.getScrollState();
54475         this.refresh(true);
54476         this.restoreScroll(s);
54477         this.afterMove(newIndex);
54478     },
54479
54480     afterMove : function(colIndex){
54481         if(this.enableMoveAnim && Roo.enableFx){
54482             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54483         }
54484         // if multisort - fix sortOrder, and reload..
54485         if (this.grid.dataSource.multiSort) {
54486             // the we can call sort again..
54487             var dm = this.grid.dataSource;
54488             var cm = this.grid.colModel;
54489             var so = [];
54490             for(var i = 0; i < cm.config.length; i++ ) {
54491                 
54492                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54493                     continue; // dont' bother, it's not in sort list or being set.
54494                 }
54495                 
54496                 so.push(cm.config[i].dataIndex);
54497             };
54498             dm.sortOrder = so;
54499             dm.load(dm.lastOptions);
54500             
54501             
54502         }
54503         
54504     },
54505
54506     updateCell : function(dm, rowIndex, dataIndex){
54507         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54508         if(typeof colIndex == "undefined"){ // not present in grid
54509             return;
54510         }
54511         var cm = this.grid.colModel;
54512         var cell = this.getCell(rowIndex, colIndex);
54513         var cellText = this.getCellText(rowIndex, colIndex);
54514
54515         var p = {
54516             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54517             id : cm.getColumnId(colIndex),
54518             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54519         };
54520         var renderer = cm.getRenderer(colIndex);
54521         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54522         if(typeof val == "undefined" || val === "") {
54523             val = "&#160;";
54524         }
54525         cellText.innerHTML = val;
54526         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54527         this.syncRowHeights(rowIndex, rowIndex);
54528     },
54529
54530     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54531         var maxWidth = 0;
54532         if(this.grid.autoSizeHeaders){
54533             var h = this.getHeaderCellMeasure(colIndex);
54534             maxWidth = Math.max(maxWidth, h.scrollWidth);
54535         }
54536         var tb, index;
54537         if(this.cm.isLocked(colIndex)){
54538             tb = this.getLockedTable();
54539             index = colIndex;
54540         }else{
54541             tb = this.getBodyTable();
54542             index = colIndex - this.cm.getLockedCount();
54543         }
54544         if(tb && tb.rows){
54545             var rows = tb.rows;
54546             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54547             for(var i = 0; i < stopIndex; i++){
54548                 var cell = rows[i].childNodes[index].firstChild;
54549                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54550             }
54551         }
54552         return maxWidth + /*margin for error in IE*/ 5;
54553     },
54554     /**
54555      * Autofit a column to its content.
54556      * @param {Number} colIndex
54557      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54558      */
54559      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54560          if(this.cm.isHidden(colIndex)){
54561              return; // can't calc a hidden column
54562          }
54563         if(forceMinSize){
54564             var cid = this.cm.getColumnId(colIndex);
54565             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54566            if(this.grid.autoSizeHeaders){
54567                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54568            }
54569         }
54570         var newWidth = this.calcColumnWidth(colIndex);
54571         this.cm.setColumnWidth(colIndex,
54572             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54573         if(!suppressEvent){
54574             this.grid.fireEvent("columnresize", colIndex, newWidth);
54575         }
54576     },
54577
54578     /**
54579      * Autofits all columns to their content and then expands to fit any extra space in the grid
54580      */
54581      autoSizeColumns : function(){
54582         var cm = this.grid.colModel;
54583         var colCount = cm.getColumnCount();
54584         for(var i = 0; i < colCount; i++){
54585             this.autoSizeColumn(i, true, true);
54586         }
54587         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54588             this.fitColumns();
54589         }else{
54590             this.updateColumns();
54591             this.layout();
54592         }
54593     },
54594
54595     /**
54596      * Autofits all columns to the grid's width proportionate with their current size
54597      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54598      */
54599     fitColumns : function(reserveScrollSpace){
54600         var cm = this.grid.colModel;
54601         var colCount = cm.getColumnCount();
54602         var cols = [];
54603         var width = 0;
54604         var i, w;
54605         for (i = 0; i < colCount; i++){
54606             if(!cm.isHidden(i) && !cm.isFixed(i)){
54607                 w = cm.getColumnWidth(i);
54608                 cols.push(i);
54609                 cols.push(w);
54610                 width += w;
54611             }
54612         }
54613         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54614         if(reserveScrollSpace){
54615             avail -= 17;
54616         }
54617         var frac = (avail - cm.getTotalWidth())/width;
54618         while (cols.length){
54619             w = cols.pop();
54620             i = cols.pop();
54621             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54622         }
54623         this.updateColumns();
54624         this.layout();
54625     },
54626
54627     onRowSelect : function(rowIndex){
54628         var row = this.getRowComposite(rowIndex);
54629         row.addClass("x-grid-row-selected");
54630     },
54631
54632     onRowDeselect : function(rowIndex){
54633         var row = this.getRowComposite(rowIndex);
54634         row.removeClass("x-grid-row-selected");
54635     },
54636
54637     onCellSelect : function(row, col){
54638         var cell = this.getCell(row, col);
54639         if(cell){
54640             Roo.fly(cell).addClass("x-grid-cell-selected");
54641         }
54642     },
54643
54644     onCellDeselect : function(row, col){
54645         var cell = this.getCell(row, col);
54646         if(cell){
54647             Roo.fly(cell).removeClass("x-grid-cell-selected");
54648         }
54649     },
54650
54651     updateHeaderSortState : function(){
54652         
54653         // sort state can be single { field: xxx, direction : yyy}
54654         // or   { xxx=>ASC , yyy : DESC ..... }
54655         
54656         var mstate = {};
54657         if (!this.ds.multiSort) { 
54658             var state = this.ds.getSortState();
54659             if(!state){
54660                 return;
54661             }
54662             mstate[state.field] = state.direction;
54663             // FIXME... - this is not used here.. but might be elsewhere..
54664             this.sortState = state;
54665             
54666         } else {
54667             mstate = this.ds.sortToggle;
54668         }
54669         //remove existing sort classes..
54670         
54671         var sc = this.sortClasses;
54672         var hds = this.el.select(this.headerSelector).removeClass(sc);
54673         
54674         for(var f in mstate) {
54675         
54676             var sortColumn = this.cm.findColumnIndex(f);
54677             
54678             if(sortColumn != -1){
54679                 var sortDir = mstate[f];        
54680                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54681             }
54682         }
54683         
54684          
54685         
54686     },
54687
54688
54689     handleHeaderClick : function(g, index,e){
54690         
54691         Roo.log("header click");
54692         
54693         if (Roo.isTouch) {
54694             // touch events on header are handled by context
54695             this.handleHdCtx(g,index,e);
54696             return;
54697         }
54698         
54699         
54700         if(this.headersDisabled){
54701             return;
54702         }
54703         var dm = g.dataSource, cm = g.colModel;
54704         if(!cm.isSortable(index)){
54705             return;
54706         }
54707         g.stopEditing();
54708         
54709         if (dm.multiSort) {
54710             // update the sortOrder
54711             var so = [];
54712             for(var i = 0; i < cm.config.length; i++ ) {
54713                 
54714                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54715                     continue; // dont' bother, it's not in sort list or being set.
54716                 }
54717                 
54718                 so.push(cm.config[i].dataIndex);
54719             };
54720             dm.sortOrder = so;
54721         }
54722         
54723         
54724         dm.sort(cm.getDataIndex(index));
54725     },
54726
54727
54728     destroy : function(){
54729         if(this.colMenu){
54730             this.colMenu.removeAll();
54731             Roo.menu.MenuMgr.unregister(this.colMenu);
54732             this.colMenu.getEl().remove();
54733             delete this.colMenu;
54734         }
54735         if(this.hmenu){
54736             this.hmenu.removeAll();
54737             Roo.menu.MenuMgr.unregister(this.hmenu);
54738             this.hmenu.getEl().remove();
54739             delete this.hmenu;
54740         }
54741         if(this.grid.enableColumnMove){
54742             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54743             if(dds){
54744                 for(var dd in dds){
54745                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54746                         var elid = dds[dd].dragElId;
54747                         dds[dd].unreg();
54748                         Roo.get(elid).remove();
54749                     } else if(dds[dd].config.isTarget){
54750                         dds[dd].proxyTop.remove();
54751                         dds[dd].proxyBottom.remove();
54752                         dds[dd].unreg();
54753                     }
54754                     if(Roo.dd.DDM.locationCache[dd]){
54755                         delete Roo.dd.DDM.locationCache[dd];
54756                     }
54757                 }
54758                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54759             }
54760         }
54761         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54762         this.bind(null, null);
54763         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54764     },
54765
54766     handleLockChange : function(){
54767         this.refresh(true);
54768     },
54769
54770     onDenyColumnLock : function(){
54771
54772     },
54773
54774     onDenyColumnHide : function(){
54775
54776     },
54777
54778     handleHdMenuClick : function(item){
54779         var index = this.hdCtxIndex;
54780         var cm = this.cm, ds = this.ds;
54781         switch(item.id){
54782             case "asc":
54783                 ds.sort(cm.getDataIndex(index), "ASC");
54784                 break;
54785             case "desc":
54786                 ds.sort(cm.getDataIndex(index), "DESC");
54787                 break;
54788             case "lock":
54789                 var lc = cm.getLockedCount();
54790                 if(cm.getColumnCount(true) <= lc+1){
54791                     this.onDenyColumnLock();
54792                     return;
54793                 }
54794                 if(lc != index){
54795                     cm.setLocked(index, true, true);
54796                     cm.moveColumn(index, lc);
54797                     this.grid.fireEvent("columnmove", index, lc);
54798                 }else{
54799                     cm.setLocked(index, true);
54800                 }
54801             break;
54802             case "unlock":
54803                 var lc = cm.getLockedCount();
54804                 if((lc-1) != index){
54805                     cm.setLocked(index, false, true);
54806                     cm.moveColumn(index, lc-1);
54807                     this.grid.fireEvent("columnmove", index, lc-1);
54808                 }else{
54809                     cm.setLocked(index, false);
54810                 }
54811             break;
54812             case 'wider': // used to expand cols on touch..
54813             case 'narrow':
54814                 var cw = cm.getColumnWidth(index);
54815                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54816                 cw = Math.max(0, cw);
54817                 cw = Math.min(cw,4000);
54818                 cm.setColumnWidth(index, cw);
54819                 break;
54820                 
54821             default:
54822                 index = cm.getIndexById(item.id.substr(4));
54823                 if(index != -1){
54824                     if(item.checked && cm.getColumnCount(true) <= 1){
54825                         this.onDenyColumnHide();
54826                         return false;
54827                     }
54828                     cm.setHidden(index, item.checked);
54829                 }
54830         }
54831         return true;
54832     },
54833
54834     beforeColMenuShow : function(){
54835         var cm = this.cm,  colCount = cm.getColumnCount();
54836         this.colMenu.removeAll();
54837         for(var i = 0; i < colCount; i++){
54838             this.colMenu.add(new Roo.menu.CheckItem({
54839                 id: "col-"+cm.getColumnId(i),
54840                 text: cm.getColumnHeader(i),
54841                 checked: !cm.isHidden(i),
54842                 hideOnClick:false
54843             }));
54844         }
54845     },
54846
54847     handleHdCtx : function(g, index, e){
54848         e.stopEvent();
54849         var hd = this.getHeaderCell(index);
54850         this.hdCtxIndex = index;
54851         var ms = this.hmenu.items, cm = this.cm;
54852         ms.get("asc").setDisabled(!cm.isSortable(index));
54853         ms.get("desc").setDisabled(!cm.isSortable(index));
54854         if(this.grid.enableColLock !== false){
54855             ms.get("lock").setDisabled(cm.isLocked(index));
54856             ms.get("unlock").setDisabled(!cm.isLocked(index));
54857         }
54858         this.hmenu.show(hd, "tl-bl");
54859     },
54860
54861     handleHdOver : function(e){
54862         var hd = this.findHeaderCell(e.getTarget());
54863         if(hd && !this.headersDisabled){
54864             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54865                this.fly(hd).addClass("x-grid-hd-over");
54866             }
54867         }
54868     },
54869
54870     handleHdOut : function(e){
54871         var hd = this.findHeaderCell(e.getTarget());
54872         if(hd){
54873             this.fly(hd).removeClass("x-grid-hd-over");
54874         }
54875     },
54876
54877     handleSplitDblClick : function(e, t){
54878         var i = this.getCellIndex(t);
54879         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54880             this.autoSizeColumn(i, true);
54881             this.layout();
54882         }
54883     },
54884
54885     render : function(){
54886
54887         var cm = this.cm;
54888         var colCount = cm.getColumnCount();
54889
54890         if(this.grid.monitorWindowResize === true){
54891             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54892         }
54893         var header = this.renderHeaders();
54894         var body = this.templates.body.apply({rows:""});
54895         var html = this.templates.master.apply({
54896             lockedBody: body,
54897             body: body,
54898             lockedHeader: header[0],
54899             header: header[1]
54900         });
54901
54902         //this.updateColumns();
54903
54904         this.grid.getGridEl().dom.innerHTML = html;
54905
54906         this.initElements();
54907         
54908         // a kludge to fix the random scolling effect in webkit
54909         this.el.on("scroll", function() {
54910             this.el.dom.scrollTop=0; // hopefully not recursive..
54911         },this);
54912
54913         this.scroller.on("scroll", this.handleScroll, this);
54914         this.lockedBody.on("mousewheel", this.handleWheel, this);
54915         this.mainBody.on("mousewheel", this.handleWheel, this);
54916
54917         this.mainHd.on("mouseover", this.handleHdOver, this);
54918         this.mainHd.on("mouseout", this.handleHdOut, this);
54919         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54920                 {delegate: "."+this.splitClass});
54921
54922         this.lockedHd.on("mouseover", this.handleHdOver, this);
54923         this.lockedHd.on("mouseout", this.handleHdOut, this);
54924         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54925                 {delegate: "."+this.splitClass});
54926
54927         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54928             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54929         }
54930
54931         this.updateSplitters();
54932
54933         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54934             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54935             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54936         }
54937
54938         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54939             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54940             this.hmenu.add(
54941                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54942                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54943             );
54944             if(this.grid.enableColLock !== false){
54945                 this.hmenu.add('-',
54946                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54947                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54948                 );
54949             }
54950             if (Roo.isTouch) {
54951                  this.hmenu.add('-',
54952                     {id:"wider", text: this.columnsWiderText},
54953                     {id:"narrow", text: this.columnsNarrowText }
54954                 );
54955                 
54956                  
54957             }
54958             
54959             if(this.grid.enableColumnHide !== false){
54960
54961                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54962                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54963                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54964
54965                 this.hmenu.add('-',
54966                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54967                 );
54968             }
54969             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54970
54971             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54972         }
54973
54974         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54975             this.dd = new Roo.grid.GridDragZone(this.grid, {
54976                 ddGroup : this.grid.ddGroup || 'GridDD'
54977             });
54978             
54979         }
54980
54981         /*
54982         for(var i = 0; i < colCount; i++){
54983             if(cm.isHidden(i)){
54984                 this.hideColumn(i);
54985             }
54986             if(cm.config[i].align){
54987                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54988                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54989             }
54990         }*/
54991         
54992         this.updateHeaderSortState();
54993
54994         this.beforeInitialResize();
54995         this.layout(true);
54996
54997         // two part rendering gives faster view to the user
54998         this.renderPhase2.defer(1, this);
54999     },
55000
55001     renderPhase2 : function(){
55002         // render the rows now
55003         this.refresh();
55004         if(this.grid.autoSizeColumns){
55005             this.autoSizeColumns();
55006         }
55007     },
55008
55009     beforeInitialResize : function(){
55010
55011     },
55012
55013     onColumnSplitterMoved : function(i, w){
55014         this.userResized = true;
55015         var cm = this.grid.colModel;
55016         cm.setColumnWidth(i, w, true);
55017         var cid = cm.getColumnId(i);
55018         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
55019         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
55020         this.updateSplitters();
55021         this.layout();
55022         this.grid.fireEvent("columnresize", i, w);
55023     },
55024
55025     syncRowHeights : function(startIndex, endIndex){
55026         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
55027             startIndex = startIndex || 0;
55028             var mrows = this.getBodyTable().rows;
55029             var lrows = this.getLockedTable().rows;
55030             var len = mrows.length-1;
55031             endIndex = Math.min(endIndex || len, len);
55032             for(var i = startIndex; i <= endIndex; i++){
55033                 var m = mrows[i], l = lrows[i];
55034                 var h = Math.max(m.offsetHeight, l.offsetHeight);
55035                 m.style.height = l.style.height = h + "px";
55036             }
55037         }
55038     },
55039
55040     layout : function(initialRender, is2ndPass){
55041         var g = this.grid;
55042         var auto = g.autoHeight;
55043         var scrollOffset = 16;
55044         var c = g.getGridEl(), cm = this.cm,
55045                 expandCol = g.autoExpandColumn,
55046                 gv = this;
55047         //c.beginMeasure();
55048
55049         if(!c.dom.offsetWidth){ // display:none?
55050             if(initialRender){
55051                 this.lockedWrap.show();
55052                 this.mainWrap.show();
55053             }
55054             return;
55055         }
55056
55057         var hasLock = this.cm.isLocked(0);
55058
55059         var tbh = this.headerPanel.getHeight();
55060         var bbh = this.footerPanel.getHeight();
55061
55062         if(auto){
55063             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
55064             var newHeight = ch + c.getBorderWidth("tb");
55065             if(g.maxHeight){
55066                 newHeight = Math.min(g.maxHeight, newHeight);
55067             }
55068             c.setHeight(newHeight);
55069         }
55070
55071         if(g.autoWidth){
55072             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
55073         }
55074
55075         var s = this.scroller;
55076
55077         var csize = c.getSize(true);
55078
55079         this.el.setSize(csize.width, csize.height);
55080
55081         this.headerPanel.setWidth(csize.width);
55082         this.footerPanel.setWidth(csize.width);
55083
55084         var hdHeight = this.mainHd.getHeight();
55085         var vw = csize.width;
55086         var vh = csize.height - (tbh + bbh);
55087
55088         s.setSize(vw, vh);
55089
55090         var bt = this.getBodyTable();
55091         var ltWidth = hasLock ?
55092                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
55093
55094         var scrollHeight = bt.offsetHeight;
55095         var scrollWidth = ltWidth + bt.offsetWidth;
55096         var vscroll = false, hscroll = false;
55097
55098         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
55099
55100         var lw = this.lockedWrap, mw = this.mainWrap;
55101         var lb = this.lockedBody, mb = this.mainBody;
55102
55103         setTimeout(function(){
55104             var t = s.dom.offsetTop;
55105             var w = s.dom.clientWidth,
55106                 h = s.dom.clientHeight;
55107
55108             lw.setTop(t);
55109             lw.setSize(ltWidth, h);
55110
55111             mw.setLeftTop(ltWidth, t);
55112             mw.setSize(w-ltWidth, h);
55113
55114             lb.setHeight(h-hdHeight);
55115             mb.setHeight(h-hdHeight);
55116
55117             if(is2ndPass !== true && !gv.userResized && expandCol){
55118                 // high speed resize without full column calculation
55119                 
55120                 var ci = cm.getIndexById(expandCol);
55121                 if (ci < 0) {
55122                     ci = cm.findColumnIndex(expandCol);
55123                 }
55124                 ci = Math.max(0, ci); // make sure it's got at least the first col.
55125                 var expandId = cm.getColumnId(ci);
55126                 var  tw = cm.getTotalWidth(false);
55127                 var currentWidth = cm.getColumnWidth(ci);
55128                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
55129                 if(currentWidth != cw){
55130                     cm.setColumnWidth(ci, cw, true);
55131                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55132                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55133                     gv.updateSplitters();
55134                     gv.layout(false, true);
55135                 }
55136             }
55137
55138             if(initialRender){
55139                 lw.show();
55140                 mw.show();
55141             }
55142             //c.endMeasure();
55143         }, 10);
55144     },
55145
55146     onWindowResize : function(){
55147         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
55148             return;
55149         }
55150         this.layout();
55151     },
55152
55153     appendFooter : function(parentEl){
55154         return null;
55155     },
55156
55157     sortAscText : "Sort Ascending",
55158     sortDescText : "Sort Descending",
55159     lockText : "Lock Column",
55160     unlockText : "Unlock Column",
55161     columnsText : "Columns",
55162  
55163     columnsWiderText : "Wider",
55164     columnsNarrowText : "Thinner"
55165 });
55166
55167
55168 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
55169     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
55170     this.proxy.el.addClass('x-grid3-col-dd');
55171 };
55172
55173 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
55174     handleMouseDown : function(e){
55175
55176     },
55177
55178     callHandleMouseDown : function(e){
55179         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55180     }
55181 });
55182 /*
55183  * Based on:
55184  * Ext JS Library 1.1.1
55185  * Copyright(c) 2006-2007, Ext JS, LLC.
55186  *
55187  * Originally Released Under LGPL - original licence link has changed is not relivant.
55188  *
55189  * Fork - LGPL
55190  * <script type="text/javascript">
55191  */
55192  
55193 // private
55194 // This is a support class used internally by the Grid components
55195 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55196     this.grid = grid;
55197     this.view = grid.getView();
55198     this.proxy = this.view.resizeProxy;
55199     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55200         "gridSplitters" + this.grid.getGridEl().id, {
55201         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55202     });
55203     this.setHandleElId(Roo.id(hd));
55204     this.setOuterHandleElId(Roo.id(hd2));
55205     this.scroll = false;
55206 };
55207 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55208     fly: Roo.Element.fly,
55209
55210     b4StartDrag : function(x, y){
55211         this.view.headersDisabled = true;
55212         this.proxy.setHeight(this.view.mainWrap.getHeight());
55213         var w = this.cm.getColumnWidth(this.cellIndex);
55214         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55215         this.resetConstraints();
55216         this.setXConstraint(minw, 1000);
55217         this.setYConstraint(0, 0);
55218         this.minX = x - minw;
55219         this.maxX = x + 1000;
55220         this.startPos = x;
55221         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55222     },
55223
55224
55225     handleMouseDown : function(e){
55226         ev = Roo.EventObject.setEvent(e);
55227         var t = this.fly(ev.getTarget());
55228         if(t.hasClass("x-grid-split")){
55229             this.cellIndex = this.view.getCellIndex(t.dom);
55230             this.split = t.dom;
55231             this.cm = this.grid.colModel;
55232             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55233                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55234             }
55235         }
55236     },
55237
55238     endDrag : function(e){
55239         this.view.headersDisabled = false;
55240         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55241         var diff = endX - this.startPos;
55242         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55243     },
55244
55245     autoOffset : function(){
55246         this.setDelta(0,0);
55247     }
55248 });/*
55249  * Based on:
55250  * Ext JS Library 1.1.1
55251  * Copyright(c) 2006-2007, Ext JS, LLC.
55252  *
55253  * Originally Released Under LGPL - original licence link has changed is not relivant.
55254  *
55255  * Fork - LGPL
55256  * <script type="text/javascript">
55257  */
55258  
55259 // private
55260 // This is a support class used internally by the Grid components
55261 Roo.grid.GridDragZone = function(grid, config){
55262     this.view = grid.getView();
55263     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55264     if(this.view.lockedBody){
55265         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55266         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55267     }
55268     this.scroll = false;
55269     this.grid = grid;
55270     this.ddel = document.createElement('div');
55271     this.ddel.className = 'x-grid-dd-wrap';
55272 };
55273
55274 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55275     ddGroup : "GridDD",
55276
55277     getDragData : function(e){
55278         var t = Roo.lib.Event.getTarget(e);
55279         var rowIndex = this.view.findRowIndex(t);
55280         var sm = this.grid.selModel;
55281             
55282         //Roo.log(rowIndex);
55283         
55284         if (sm.getSelectedCell) {
55285             // cell selection..
55286             if (!sm.getSelectedCell()) {
55287                 return false;
55288             }
55289             if (rowIndex != sm.getSelectedCell()[0]) {
55290                 return false;
55291             }
55292         
55293         }
55294         
55295         if(rowIndex !== false){
55296             
55297             // if editorgrid.. 
55298             
55299             
55300             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55301                
55302             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55303               //  
55304             //}
55305             if (e.hasModifier()){
55306                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55307             }
55308             
55309             Roo.log("getDragData");
55310             
55311             return {
55312                 grid: this.grid,
55313                 ddel: this.ddel,
55314                 rowIndex: rowIndex,
55315                 selections:sm.getSelections ? sm.getSelections() : (
55316                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55317                 )
55318             };
55319         }
55320         return false;
55321     },
55322
55323     onInitDrag : function(e){
55324         var data = this.dragData;
55325         this.ddel.innerHTML = this.grid.getDragDropText();
55326         this.proxy.update(this.ddel);
55327         // fire start drag?
55328     },
55329
55330     afterRepair : function(){
55331         this.dragging = false;
55332     },
55333
55334     getRepairXY : function(e, data){
55335         return false;
55336     },
55337
55338     onEndDrag : function(data, e){
55339         // fire end drag?
55340     },
55341
55342     onValidDrop : function(dd, e, id){
55343         // fire drag drop?
55344         this.hideProxy();
55345     },
55346
55347     beforeInvalidDrop : function(e, id){
55348
55349     }
55350 });/*
55351  * Based on:
55352  * Ext JS Library 1.1.1
55353  * Copyright(c) 2006-2007, Ext JS, LLC.
55354  *
55355  * Originally Released Under LGPL - original licence link has changed is not relivant.
55356  *
55357  * Fork - LGPL
55358  * <script type="text/javascript">
55359  */
55360  
55361
55362 /**
55363  * @class Roo.grid.ColumnModel
55364  * @extends Roo.util.Observable
55365  * This is the default implementation of a ColumnModel used by the Grid. It defines
55366  * the columns in the grid.
55367  * <br>Usage:<br>
55368  <pre><code>
55369  var colModel = new Roo.grid.ColumnModel([
55370         {header: "Ticker", width: 60, sortable: true, locked: true},
55371         {header: "Company Name", width: 150, sortable: true},
55372         {header: "Market Cap.", width: 100, sortable: true},
55373         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55374         {header: "Employees", width: 100, sortable: true, resizable: false}
55375  ]);
55376  </code></pre>
55377  * <p>
55378  
55379  * The config options listed for this class are options which may appear in each
55380  * individual column definition.
55381  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55382  * @constructor
55383  * @param {Object} config An Array of column config objects. See this class's
55384  * config objects for details.
55385 */
55386 Roo.grid.ColumnModel = function(config){
55387         /**
55388      * The config passed into the constructor
55389      */
55390     this.config = config;
55391     this.lookup = {};
55392
55393     // if no id, create one
55394     // if the column does not have a dataIndex mapping,
55395     // map it to the order it is in the config
55396     for(var i = 0, len = config.length; i < len; i++){
55397         var c = config[i];
55398         if(typeof c.dataIndex == "undefined"){
55399             c.dataIndex = i;
55400         }
55401         if(typeof c.renderer == "string"){
55402             c.renderer = Roo.util.Format[c.renderer];
55403         }
55404         if(typeof c.id == "undefined"){
55405             c.id = Roo.id();
55406         }
55407         if(c.editor && c.editor.xtype){
55408             c.editor  = Roo.factory(c.editor, Roo.grid);
55409         }
55410         if(c.editor && c.editor.isFormField){
55411             c.editor = new Roo.grid.GridEditor(c.editor);
55412         }
55413         this.lookup[c.id] = c;
55414     }
55415
55416     /**
55417      * The width of columns which have no width specified (defaults to 100)
55418      * @type Number
55419      */
55420     this.defaultWidth = 100;
55421
55422     /**
55423      * Default sortable of columns which have no sortable specified (defaults to false)
55424      * @type Boolean
55425      */
55426     this.defaultSortable = false;
55427
55428     this.addEvents({
55429         /**
55430              * @event widthchange
55431              * Fires when the width of a column changes.
55432              * @param {ColumnModel} this
55433              * @param {Number} columnIndex The column index
55434              * @param {Number} newWidth The new width
55435              */
55436             "widthchange": true,
55437         /**
55438              * @event headerchange
55439              * Fires when the text of a header changes.
55440              * @param {ColumnModel} this
55441              * @param {Number} columnIndex The column index
55442              * @param {Number} newText The new header text
55443              */
55444             "headerchange": true,
55445         /**
55446              * @event hiddenchange
55447              * Fires when a column is hidden or "unhidden".
55448              * @param {ColumnModel} this
55449              * @param {Number} columnIndex The column index
55450              * @param {Boolean} hidden true if hidden, false otherwise
55451              */
55452             "hiddenchange": true,
55453             /**
55454          * @event columnmoved
55455          * Fires when a column is moved.
55456          * @param {ColumnModel} this
55457          * @param {Number} oldIndex
55458          * @param {Number} newIndex
55459          */
55460         "columnmoved" : true,
55461         /**
55462          * @event columlockchange
55463          * Fires when a column's locked state is changed
55464          * @param {ColumnModel} this
55465          * @param {Number} colIndex
55466          * @param {Boolean} locked true if locked
55467          */
55468         "columnlockchange" : true
55469     });
55470     Roo.grid.ColumnModel.superclass.constructor.call(this);
55471 };
55472 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55473     /**
55474      * @cfg {String} header The header text to display in the Grid view.
55475      */
55476     /**
55477      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55478      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55479      * specified, the column's index is used as an index into the Record's data Array.
55480      */
55481     /**
55482      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55483      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55484      */
55485     /**
55486      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55487      * Defaults to the value of the {@link #defaultSortable} property.
55488      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55489      */
55490     /**
55491      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55492      */
55493     /**
55494      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55495      */
55496     /**
55497      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55498      */
55499     /**
55500      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55501      */
55502     /**
55503      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55504      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55505      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55506      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55507      */
55508        /**
55509      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55510      */
55511     /**
55512      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55513      */
55514     /**
55515      * @cfg {String} cursor (Optional)
55516      */
55517     /**
55518      * @cfg {String} tooltip (Optional)
55519      */
55520     /**
55521      * @cfg {Number} xs (Optional)
55522      */
55523     /**
55524      * @cfg {Number} sm (Optional)
55525      */
55526     /**
55527      * @cfg {Number} md (Optional)
55528      */
55529     /**
55530      * @cfg {Number} lg (Optional)
55531      */
55532     /**
55533      * Returns the id of the column at the specified index.
55534      * @param {Number} index The column index
55535      * @return {String} the id
55536      */
55537     getColumnId : function(index){
55538         return this.config[index].id;
55539     },
55540
55541     /**
55542      * Returns the column for a specified id.
55543      * @param {String} id The column id
55544      * @return {Object} the column
55545      */
55546     getColumnById : function(id){
55547         return this.lookup[id];
55548     },
55549
55550     
55551     /**
55552      * Returns the column for a specified dataIndex.
55553      * @param {String} dataIndex The column dataIndex
55554      * @return {Object|Boolean} the column or false if not found
55555      */
55556     getColumnByDataIndex: function(dataIndex){
55557         var index = this.findColumnIndex(dataIndex);
55558         return index > -1 ? this.config[index] : false;
55559     },
55560     
55561     /**
55562      * Returns the index for a specified column id.
55563      * @param {String} id The column id
55564      * @return {Number} the index, or -1 if not found
55565      */
55566     getIndexById : function(id){
55567         for(var i = 0, len = this.config.length; i < len; i++){
55568             if(this.config[i].id == id){
55569                 return i;
55570             }
55571         }
55572         return -1;
55573     },
55574     
55575     /**
55576      * Returns the index for a specified column dataIndex.
55577      * @param {String} dataIndex The column dataIndex
55578      * @return {Number} the index, or -1 if not found
55579      */
55580     
55581     findColumnIndex : function(dataIndex){
55582         for(var i = 0, len = this.config.length; i < len; i++){
55583             if(this.config[i].dataIndex == dataIndex){
55584                 return i;
55585             }
55586         }
55587         return -1;
55588     },
55589     
55590     
55591     moveColumn : function(oldIndex, newIndex){
55592         var c = this.config[oldIndex];
55593         this.config.splice(oldIndex, 1);
55594         this.config.splice(newIndex, 0, c);
55595         this.dataMap = null;
55596         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55597     },
55598
55599     isLocked : function(colIndex){
55600         return this.config[colIndex].locked === true;
55601     },
55602
55603     setLocked : function(colIndex, value, suppressEvent){
55604         if(this.isLocked(colIndex) == value){
55605             return;
55606         }
55607         this.config[colIndex].locked = value;
55608         if(!suppressEvent){
55609             this.fireEvent("columnlockchange", this, colIndex, value);
55610         }
55611     },
55612
55613     getTotalLockedWidth : function(){
55614         var totalWidth = 0;
55615         for(var i = 0; i < this.config.length; i++){
55616             if(this.isLocked(i) && !this.isHidden(i)){
55617                 this.totalWidth += this.getColumnWidth(i);
55618             }
55619         }
55620         return totalWidth;
55621     },
55622
55623     getLockedCount : function(){
55624         for(var i = 0, len = this.config.length; i < len; i++){
55625             if(!this.isLocked(i)){
55626                 return i;
55627             }
55628         }
55629     },
55630
55631     /**
55632      * Returns the number of columns.
55633      * @return {Number}
55634      */
55635     getColumnCount : function(visibleOnly){
55636         if(visibleOnly === true){
55637             var c = 0;
55638             for(var i = 0, len = this.config.length; i < len; i++){
55639                 if(!this.isHidden(i)){
55640                     c++;
55641                 }
55642             }
55643             return c;
55644         }
55645         return this.config.length;
55646     },
55647
55648     /**
55649      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55650      * @param {Function} fn
55651      * @param {Object} scope (optional)
55652      * @return {Array} result
55653      */
55654     getColumnsBy : function(fn, scope){
55655         var r = [];
55656         for(var i = 0, len = this.config.length; i < len; i++){
55657             var c = this.config[i];
55658             if(fn.call(scope||this, c, i) === true){
55659                 r[r.length] = c;
55660             }
55661         }
55662         return r;
55663     },
55664
55665     /**
55666      * Returns true if the specified column is sortable.
55667      * @param {Number} col The column index
55668      * @return {Boolean}
55669      */
55670     isSortable : function(col){
55671         if(typeof this.config[col].sortable == "undefined"){
55672             return this.defaultSortable;
55673         }
55674         return this.config[col].sortable;
55675     },
55676
55677     /**
55678      * Returns the rendering (formatting) function defined for the column.
55679      * @param {Number} col The column index.
55680      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55681      */
55682     getRenderer : function(col){
55683         if(!this.config[col].renderer){
55684             return Roo.grid.ColumnModel.defaultRenderer;
55685         }
55686         return this.config[col].renderer;
55687     },
55688
55689     /**
55690      * Sets the rendering (formatting) function for a column.
55691      * @param {Number} col The column index
55692      * @param {Function} fn The function to use to process the cell's raw data
55693      * to return HTML markup for the grid view. The render function is called with
55694      * the following parameters:<ul>
55695      * <li>Data value.</li>
55696      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55697      * <li>css A CSS style string to apply to the table cell.</li>
55698      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55699      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55700      * <li>Row index</li>
55701      * <li>Column index</li>
55702      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55703      */
55704     setRenderer : function(col, fn){
55705         this.config[col].renderer = fn;
55706     },
55707
55708     /**
55709      * Returns the width for the specified column.
55710      * @param {Number} col The column index
55711      * @return {Number}
55712      */
55713     getColumnWidth : function(col){
55714         return this.config[col].width * 1 || this.defaultWidth;
55715     },
55716
55717     /**
55718      * Sets the width for a column.
55719      * @param {Number} col The column index
55720      * @param {Number} width The new width
55721      */
55722     setColumnWidth : function(col, width, suppressEvent){
55723         this.config[col].width = width;
55724         this.totalWidth = null;
55725         if(!suppressEvent){
55726              this.fireEvent("widthchange", this, col, width);
55727         }
55728     },
55729
55730     /**
55731      * Returns the total width of all columns.
55732      * @param {Boolean} includeHidden True to include hidden column widths
55733      * @return {Number}
55734      */
55735     getTotalWidth : function(includeHidden){
55736         if(!this.totalWidth){
55737             this.totalWidth = 0;
55738             for(var i = 0, len = this.config.length; i < len; i++){
55739                 if(includeHidden || !this.isHidden(i)){
55740                     this.totalWidth += this.getColumnWidth(i);
55741                 }
55742             }
55743         }
55744         return this.totalWidth;
55745     },
55746
55747     /**
55748      * Returns the header for the specified column.
55749      * @param {Number} col The column index
55750      * @return {String}
55751      */
55752     getColumnHeader : function(col){
55753         return this.config[col].header;
55754     },
55755
55756     /**
55757      * Sets the header for a column.
55758      * @param {Number} col The column index
55759      * @param {String} header The new header
55760      */
55761     setColumnHeader : function(col, header){
55762         this.config[col].header = header;
55763         this.fireEvent("headerchange", this, col, header);
55764     },
55765
55766     /**
55767      * Returns the tooltip for the specified column.
55768      * @param {Number} col The column index
55769      * @return {String}
55770      */
55771     getColumnTooltip : function(col){
55772             return this.config[col].tooltip;
55773     },
55774     /**
55775      * Sets the tooltip for a column.
55776      * @param {Number} col The column index
55777      * @param {String} tooltip The new tooltip
55778      */
55779     setColumnTooltip : function(col, tooltip){
55780             this.config[col].tooltip = tooltip;
55781     },
55782
55783     /**
55784      * Returns the dataIndex for the specified column.
55785      * @param {Number} col The column index
55786      * @return {Number}
55787      */
55788     getDataIndex : function(col){
55789         return this.config[col].dataIndex;
55790     },
55791
55792     /**
55793      * Sets the dataIndex for a column.
55794      * @param {Number} col The column index
55795      * @param {Number} dataIndex The new dataIndex
55796      */
55797     setDataIndex : function(col, dataIndex){
55798         this.config[col].dataIndex = dataIndex;
55799     },
55800
55801     
55802     
55803     /**
55804      * Returns true if the cell is editable.
55805      * @param {Number} colIndex The column index
55806      * @param {Number} rowIndex The row index - this is nto actually used..?
55807      * @return {Boolean}
55808      */
55809     isCellEditable : function(colIndex, rowIndex){
55810         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55811     },
55812
55813     /**
55814      * Returns the editor defined for the cell/column.
55815      * return false or null to disable editing.
55816      * @param {Number} colIndex The column index
55817      * @param {Number} rowIndex The row index
55818      * @return {Object}
55819      */
55820     getCellEditor : function(colIndex, rowIndex){
55821         return this.config[colIndex].editor;
55822     },
55823
55824     /**
55825      * Sets if a column is editable.
55826      * @param {Number} col The column index
55827      * @param {Boolean} editable True if the column is editable
55828      */
55829     setEditable : function(col, editable){
55830         this.config[col].editable = editable;
55831     },
55832
55833
55834     /**
55835      * Returns true if the column is hidden.
55836      * @param {Number} colIndex The column index
55837      * @return {Boolean}
55838      */
55839     isHidden : function(colIndex){
55840         return this.config[colIndex].hidden;
55841     },
55842
55843
55844     /**
55845      * Returns true if the column width cannot be changed
55846      */
55847     isFixed : function(colIndex){
55848         return this.config[colIndex].fixed;
55849     },
55850
55851     /**
55852      * Returns true if the column can be resized
55853      * @return {Boolean}
55854      */
55855     isResizable : function(colIndex){
55856         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55857     },
55858     /**
55859      * Sets if a column is hidden.
55860      * @param {Number} colIndex The column index
55861      * @param {Boolean} hidden True if the column is hidden
55862      */
55863     setHidden : function(colIndex, hidden){
55864         this.config[colIndex].hidden = hidden;
55865         this.totalWidth = null;
55866         this.fireEvent("hiddenchange", this, colIndex, hidden);
55867     },
55868
55869     /**
55870      * Sets the editor for a column.
55871      * @param {Number} col The column index
55872      * @param {Object} editor The editor object
55873      */
55874     setEditor : function(col, editor){
55875         this.config[col].editor = editor;
55876     }
55877 });
55878
55879 Roo.grid.ColumnModel.defaultRenderer = function(value){
55880         if(typeof value == "string" && value.length < 1){
55881             return "&#160;";
55882         }
55883         return value;
55884 };
55885
55886 // Alias for backwards compatibility
55887 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55888 /*
55889  * Based on:
55890  * Ext JS Library 1.1.1
55891  * Copyright(c) 2006-2007, Ext JS, LLC.
55892  *
55893  * Originally Released Under LGPL - original licence link has changed is not relivant.
55894  *
55895  * Fork - LGPL
55896  * <script type="text/javascript">
55897  */
55898
55899 /**
55900  * @class Roo.grid.AbstractSelectionModel
55901  * @extends Roo.util.Observable
55902  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55903  * implemented by descendant classes.  This class should not be directly instantiated.
55904  * @constructor
55905  */
55906 Roo.grid.AbstractSelectionModel = function(){
55907     this.locked = false;
55908     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55909 };
55910
55911 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55912     /** @ignore Called by the grid automatically. Do not call directly. */
55913     init : function(grid){
55914         this.grid = grid;
55915         this.initEvents();
55916     },
55917
55918     /**
55919      * Locks the selections.
55920      */
55921     lock : function(){
55922         this.locked = true;
55923     },
55924
55925     /**
55926      * Unlocks the selections.
55927      */
55928     unlock : function(){
55929         this.locked = false;
55930     },
55931
55932     /**
55933      * Returns true if the selections are locked.
55934      * @return {Boolean}
55935      */
55936     isLocked : function(){
55937         return this.locked;
55938     }
55939 });/*
55940  * Based on:
55941  * Ext JS Library 1.1.1
55942  * Copyright(c) 2006-2007, Ext JS, LLC.
55943  *
55944  * Originally Released Under LGPL - original licence link has changed is not relivant.
55945  *
55946  * Fork - LGPL
55947  * <script type="text/javascript">
55948  */
55949 /**
55950  * @extends Roo.grid.AbstractSelectionModel
55951  * @class Roo.grid.RowSelectionModel
55952  * The default SelectionModel used by {@link Roo.grid.Grid}.
55953  * It supports multiple selections and keyboard selection/navigation. 
55954  * @constructor
55955  * @param {Object} config
55956  */
55957 Roo.grid.RowSelectionModel = function(config){
55958     Roo.apply(this, config);
55959     this.selections = new Roo.util.MixedCollection(false, function(o){
55960         return o.id;
55961     });
55962
55963     this.last = false;
55964     this.lastActive = false;
55965
55966     this.addEvents({
55967         /**
55968              * @event selectionchange
55969              * Fires when the selection changes
55970              * @param {SelectionModel} this
55971              */
55972             "selectionchange" : true,
55973         /**
55974              * @event afterselectionchange
55975              * Fires after the selection changes (eg. by key press or clicking)
55976              * @param {SelectionModel} this
55977              */
55978             "afterselectionchange" : true,
55979         /**
55980              * @event beforerowselect
55981              * Fires when a row is selected being selected, return false to cancel.
55982              * @param {SelectionModel} this
55983              * @param {Number} rowIndex The selected index
55984              * @param {Boolean} keepExisting False if other selections will be cleared
55985              */
55986             "beforerowselect" : true,
55987         /**
55988              * @event rowselect
55989              * Fires when a row is selected.
55990              * @param {SelectionModel} this
55991              * @param {Number} rowIndex The selected index
55992              * @param {Roo.data.Record} r The record
55993              */
55994             "rowselect" : true,
55995         /**
55996              * @event rowdeselect
55997              * Fires when a row is deselected.
55998              * @param {SelectionModel} this
55999              * @param {Number} rowIndex The selected index
56000              */
56001         "rowdeselect" : true
56002     });
56003     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
56004     this.locked = false;
56005 };
56006
56007 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
56008     /**
56009      * @cfg {Boolean} singleSelect
56010      * True to allow selection of only one row at a time (defaults to false)
56011      */
56012     singleSelect : false,
56013
56014     // private
56015     initEvents : function(){
56016
56017         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
56018             this.grid.on("mousedown", this.handleMouseDown, this);
56019         }else{ // allow click to work like normal
56020             this.grid.on("rowclick", this.handleDragableRowClick, this);
56021         }
56022
56023         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
56024             "up" : function(e){
56025                 if(!e.shiftKey){
56026                     this.selectPrevious(e.shiftKey);
56027                 }else if(this.last !== false && this.lastActive !== false){
56028                     var last = this.last;
56029                     this.selectRange(this.last,  this.lastActive-1);
56030                     this.grid.getView().focusRow(this.lastActive);
56031                     if(last !== false){
56032                         this.last = last;
56033                     }
56034                 }else{
56035                     this.selectFirstRow();
56036                 }
56037                 this.fireEvent("afterselectionchange", this);
56038             },
56039             "down" : function(e){
56040                 if(!e.shiftKey){
56041                     this.selectNext(e.shiftKey);
56042                 }else if(this.last !== false && this.lastActive !== false){
56043                     var last = this.last;
56044                     this.selectRange(this.last,  this.lastActive+1);
56045                     this.grid.getView().focusRow(this.lastActive);
56046                     if(last !== false){
56047                         this.last = last;
56048                     }
56049                 }else{
56050                     this.selectFirstRow();
56051                 }
56052                 this.fireEvent("afterselectionchange", this);
56053             },
56054             scope: this
56055         });
56056
56057         var view = this.grid.view;
56058         view.on("refresh", this.onRefresh, this);
56059         view.on("rowupdated", this.onRowUpdated, this);
56060         view.on("rowremoved", this.onRemove, this);
56061     },
56062
56063     // private
56064     onRefresh : function(){
56065         var ds = this.grid.dataSource, i, v = this.grid.view;
56066         var s = this.selections;
56067         s.each(function(r){
56068             if((i = ds.indexOfId(r.id)) != -1){
56069                 v.onRowSelect(i);
56070                 s.add(ds.getAt(i)); // updating the selection relate data
56071             }else{
56072                 s.remove(r);
56073             }
56074         });
56075     },
56076
56077     // private
56078     onRemove : function(v, index, r){
56079         this.selections.remove(r);
56080     },
56081
56082     // private
56083     onRowUpdated : function(v, index, r){
56084         if(this.isSelected(r)){
56085             v.onRowSelect(index);
56086         }
56087     },
56088
56089     /**
56090      * Select records.
56091      * @param {Array} records The records to select
56092      * @param {Boolean} keepExisting (optional) True to keep existing selections
56093      */
56094     selectRecords : function(records, keepExisting){
56095         if(!keepExisting){
56096             this.clearSelections();
56097         }
56098         var ds = this.grid.dataSource;
56099         for(var i = 0, len = records.length; i < len; i++){
56100             this.selectRow(ds.indexOf(records[i]), true);
56101         }
56102     },
56103
56104     /**
56105      * Gets the number of selected rows.
56106      * @return {Number}
56107      */
56108     getCount : function(){
56109         return this.selections.length;
56110     },
56111
56112     /**
56113      * Selects the first row in the grid.
56114      */
56115     selectFirstRow : function(){
56116         this.selectRow(0);
56117     },
56118
56119     /**
56120      * Select the last row.
56121      * @param {Boolean} keepExisting (optional) True to keep existing selections
56122      */
56123     selectLastRow : function(keepExisting){
56124         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
56125     },
56126
56127     /**
56128      * Selects the row immediately following the last selected row.
56129      * @param {Boolean} keepExisting (optional) True to keep existing selections
56130      */
56131     selectNext : function(keepExisting){
56132         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
56133             this.selectRow(this.last+1, keepExisting);
56134             this.grid.getView().focusRow(this.last);
56135         }
56136     },
56137
56138     /**
56139      * Selects the row that precedes the last selected row.
56140      * @param {Boolean} keepExisting (optional) True to keep existing selections
56141      */
56142     selectPrevious : function(keepExisting){
56143         if(this.last){
56144             this.selectRow(this.last-1, keepExisting);
56145             this.grid.getView().focusRow(this.last);
56146         }
56147     },
56148
56149     /**
56150      * Returns the selected records
56151      * @return {Array} Array of selected records
56152      */
56153     getSelections : function(){
56154         return [].concat(this.selections.items);
56155     },
56156
56157     /**
56158      * Returns the first selected record.
56159      * @return {Record}
56160      */
56161     getSelected : function(){
56162         return this.selections.itemAt(0);
56163     },
56164
56165
56166     /**
56167      * Clears all selections.
56168      */
56169     clearSelections : function(fast){
56170         if(this.locked) {
56171             return;
56172         }
56173         if(fast !== true){
56174             var ds = this.grid.dataSource;
56175             var s = this.selections;
56176             s.each(function(r){
56177                 this.deselectRow(ds.indexOfId(r.id));
56178             }, this);
56179             s.clear();
56180         }else{
56181             this.selections.clear();
56182         }
56183         this.last = false;
56184     },
56185
56186
56187     /**
56188      * Selects all rows.
56189      */
56190     selectAll : function(){
56191         if(this.locked) {
56192             return;
56193         }
56194         this.selections.clear();
56195         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56196             this.selectRow(i, true);
56197         }
56198     },
56199
56200     /**
56201      * Returns True if there is a selection.
56202      * @return {Boolean}
56203      */
56204     hasSelection : function(){
56205         return this.selections.length > 0;
56206     },
56207
56208     /**
56209      * Returns True if the specified row is selected.
56210      * @param {Number/Record} record The record or index of the record to check
56211      * @return {Boolean}
56212      */
56213     isSelected : function(index){
56214         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56215         return (r && this.selections.key(r.id) ? true : false);
56216     },
56217
56218     /**
56219      * Returns True if the specified record id is selected.
56220      * @param {String} id The id of record to check
56221      * @return {Boolean}
56222      */
56223     isIdSelected : function(id){
56224         return (this.selections.key(id) ? true : false);
56225     },
56226
56227     // private
56228     handleMouseDown : function(e, t){
56229         var view = this.grid.getView(), rowIndex;
56230         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56231             return;
56232         };
56233         if(e.shiftKey && this.last !== false){
56234             var last = this.last;
56235             this.selectRange(last, rowIndex, e.ctrlKey);
56236             this.last = last; // reset the last
56237             view.focusRow(rowIndex);
56238         }else{
56239             var isSelected = this.isSelected(rowIndex);
56240             if(e.button !== 0 && isSelected){
56241                 view.focusRow(rowIndex);
56242             }else if(e.ctrlKey && isSelected){
56243                 this.deselectRow(rowIndex);
56244             }else if(!isSelected){
56245                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56246                 view.focusRow(rowIndex);
56247             }
56248         }
56249         this.fireEvent("afterselectionchange", this);
56250     },
56251     // private
56252     handleDragableRowClick :  function(grid, rowIndex, e) 
56253     {
56254         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56255             this.selectRow(rowIndex, false);
56256             grid.view.focusRow(rowIndex);
56257              this.fireEvent("afterselectionchange", this);
56258         }
56259     },
56260     
56261     /**
56262      * Selects multiple rows.
56263      * @param {Array} rows Array of the indexes of the row to select
56264      * @param {Boolean} keepExisting (optional) True to keep existing selections
56265      */
56266     selectRows : function(rows, keepExisting){
56267         if(!keepExisting){
56268             this.clearSelections();
56269         }
56270         for(var i = 0, len = rows.length; i < len; i++){
56271             this.selectRow(rows[i], true);
56272         }
56273     },
56274
56275     /**
56276      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56277      * @param {Number} startRow The index of the first row in the range
56278      * @param {Number} endRow The index of the last row in the range
56279      * @param {Boolean} keepExisting (optional) True to retain existing selections
56280      */
56281     selectRange : function(startRow, endRow, keepExisting){
56282         if(this.locked) {
56283             return;
56284         }
56285         if(!keepExisting){
56286             this.clearSelections();
56287         }
56288         if(startRow <= endRow){
56289             for(var i = startRow; i <= endRow; i++){
56290                 this.selectRow(i, true);
56291             }
56292         }else{
56293             for(var i = startRow; i >= endRow; i--){
56294                 this.selectRow(i, true);
56295             }
56296         }
56297     },
56298
56299     /**
56300      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56301      * @param {Number} startRow The index of the first row in the range
56302      * @param {Number} endRow The index of the last row in the range
56303      */
56304     deselectRange : function(startRow, endRow, preventViewNotify){
56305         if(this.locked) {
56306             return;
56307         }
56308         for(var i = startRow; i <= endRow; i++){
56309             this.deselectRow(i, preventViewNotify);
56310         }
56311     },
56312
56313     /**
56314      * Selects a row.
56315      * @param {Number} row The index of the row to select
56316      * @param {Boolean} keepExisting (optional) True to keep existing selections
56317      */
56318     selectRow : function(index, keepExisting, preventViewNotify){
56319         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
56320             return;
56321         }
56322         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56323             if(!keepExisting || this.singleSelect){
56324                 this.clearSelections();
56325             }
56326             var r = this.grid.dataSource.getAt(index);
56327             this.selections.add(r);
56328             this.last = this.lastActive = index;
56329             if(!preventViewNotify){
56330                 this.grid.getView().onRowSelect(index);
56331             }
56332             this.fireEvent("rowselect", this, index, r);
56333             this.fireEvent("selectionchange", this);
56334         }
56335     },
56336
56337     /**
56338      * Deselects a row.
56339      * @param {Number} row The index of the row to deselect
56340      */
56341     deselectRow : function(index, preventViewNotify){
56342         if(this.locked) {
56343             return;
56344         }
56345         if(this.last == index){
56346             this.last = false;
56347         }
56348         if(this.lastActive == index){
56349             this.lastActive = false;
56350         }
56351         var r = this.grid.dataSource.getAt(index);
56352         this.selections.remove(r);
56353         if(!preventViewNotify){
56354             this.grid.getView().onRowDeselect(index);
56355         }
56356         this.fireEvent("rowdeselect", this, index);
56357         this.fireEvent("selectionchange", this);
56358     },
56359
56360     // private
56361     restoreLast : function(){
56362         if(this._last){
56363             this.last = this._last;
56364         }
56365     },
56366
56367     // private
56368     acceptsNav : function(row, col, cm){
56369         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56370     },
56371
56372     // private
56373     onEditorKey : function(field, e){
56374         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56375         if(k == e.TAB){
56376             e.stopEvent();
56377             ed.completeEdit();
56378             if(e.shiftKey){
56379                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56380             }else{
56381                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56382             }
56383         }else if(k == e.ENTER && !e.ctrlKey){
56384             e.stopEvent();
56385             ed.completeEdit();
56386             if(e.shiftKey){
56387                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56388             }else{
56389                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56390             }
56391         }else if(k == e.ESC){
56392             ed.cancelEdit();
56393         }
56394         if(newCell){
56395             g.startEditing(newCell[0], newCell[1]);
56396         }
56397     }
56398 });/*
56399  * Based on:
56400  * Ext JS Library 1.1.1
56401  * Copyright(c) 2006-2007, Ext JS, LLC.
56402  *
56403  * Originally Released Under LGPL - original licence link has changed is not relivant.
56404  *
56405  * Fork - LGPL
56406  * <script type="text/javascript">
56407  */
56408 /**
56409  * @class Roo.grid.CellSelectionModel
56410  * @extends Roo.grid.AbstractSelectionModel
56411  * This class provides the basic implementation for cell selection in a grid.
56412  * @constructor
56413  * @param {Object} config The object containing the configuration of this model.
56414  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56415  */
56416 Roo.grid.CellSelectionModel = function(config){
56417     Roo.apply(this, config);
56418
56419     this.selection = null;
56420
56421     this.addEvents({
56422         /**
56423              * @event beforerowselect
56424              * Fires before a cell is selected.
56425              * @param {SelectionModel} this
56426              * @param {Number} rowIndex The selected row index
56427              * @param {Number} colIndex The selected cell index
56428              */
56429             "beforecellselect" : true,
56430         /**
56431              * @event cellselect
56432              * Fires when a cell is selected.
56433              * @param {SelectionModel} this
56434              * @param {Number} rowIndex The selected row index
56435              * @param {Number} colIndex The selected cell index
56436              */
56437             "cellselect" : true,
56438         /**
56439              * @event selectionchange
56440              * Fires when the active selection changes.
56441              * @param {SelectionModel} this
56442              * @param {Object} selection null for no selection or an object (o) with two properties
56443                 <ul>
56444                 <li>o.record: the record object for the row the selection is in</li>
56445                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56446                 </ul>
56447              */
56448             "selectionchange" : true,
56449         /**
56450              * @event tabend
56451              * Fires when the tab (or enter) was pressed on the last editable cell
56452              * You can use this to trigger add new row.
56453              * @param {SelectionModel} this
56454              */
56455             "tabend" : true,
56456          /**
56457              * @event beforeeditnext
56458              * Fires before the next editable sell is made active
56459              * You can use this to skip to another cell or fire the tabend
56460              *    if you set cell to false
56461              * @param {Object} eventdata object : { cell : [ row, col ] } 
56462              */
56463             "beforeeditnext" : true
56464     });
56465     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56466 };
56467
56468 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56469     
56470     enter_is_tab: false,
56471
56472     /** @ignore */
56473     initEvents : function(){
56474         this.grid.on("mousedown", this.handleMouseDown, this);
56475         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56476         var view = this.grid.view;
56477         view.on("refresh", this.onViewChange, this);
56478         view.on("rowupdated", this.onRowUpdated, this);
56479         view.on("beforerowremoved", this.clearSelections, this);
56480         view.on("beforerowsinserted", this.clearSelections, this);
56481         if(this.grid.isEditor){
56482             this.grid.on("beforeedit", this.beforeEdit,  this);
56483         }
56484     },
56485
56486         //private
56487     beforeEdit : function(e){
56488         this.select(e.row, e.column, false, true, e.record);
56489     },
56490
56491         //private
56492     onRowUpdated : function(v, index, r){
56493         if(this.selection && this.selection.record == r){
56494             v.onCellSelect(index, this.selection.cell[1]);
56495         }
56496     },
56497
56498         //private
56499     onViewChange : function(){
56500         this.clearSelections(true);
56501     },
56502
56503         /**
56504          * Returns the currently selected cell,.
56505          * @return {Array} The selected cell (row, column) or null if none selected.
56506          */
56507     getSelectedCell : function(){
56508         return this.selection ? this.selection.cell : null;
56509     },
56510
56511     /**
56512      * Clears all selections.
56513      * @param {Boolean} true to prevent the gridview from being notified about the change.
56514      */
56515     clearSelections : function(preventNotify){
56516         var s = this.selection;
56517         if(s){
56518             if(preventNotify !== true){
56519                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56520             }
56521             this.selection = null;
56522             this.fireEvent("selectionchange", this, null);
56523         }
56524     },
56525
56526     /**
56527      * Returns true if there is a selection.
56528      * @return {Boolean}
56529      */
56530     hasSelection : function(){
56531         return this.selection ? true : false;
56532     },
56533
56534     /** @ignore */
56535     handleMouseDown : function(e, t){
56536         var v = this.grid.getView();
56537         if(this.isLocked()){
56538             return;
56539         };
56540         var row = v.findRowIndex(t);
56541         var cell = v.findCellIndex(t);
56542         if(row !== false && cell !== false){
56543             this.select(row, cell);
56544         }
56545     },
56546
56547     /**
56548      * Selects a cell.
56549      * @param {Number} rowIndex
56550      * @param {Number} collIndex
56551      */
56552     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56553         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56554             this.clearSelections();
56555             r = r || this.grid.dataSource.getAt(rowIndex);
56556             this.selection = {
56557                 record : r,
56558                 cell : [rowIndex, colIndex]
56559             };
56560             if(!preventViewNotify){
56561                 var v = this.grid.getView();
56562                 v.onCellSelect(rowIndex, colIndex);
56563                 if(preventFocus !== true){
56564                     v.focusCell(rowIndex, colIndex);
56565                 }
56566             }
56567             this.fireEvent("cellselect", this, rowIndex, colIndex);
56568             this.fireEvent("selectionchange", this, this.selection);
56569         }
56570     },
56571
56572         //private
56573     isSelectable : function(rowIndex, colIndex, cm){
56574         return !cm.isHidden(colIndex);
56575     },
56576
56577     /** @ignore */
56578     handleKeyDown : function(e){
56579         //Roo.log('Cell Sel Model handleKeyDown');
56580         if(!e.isNavKeyPress()){
56581             return;
56582         }
56583         var g = this.grid, s = this.selection;
56584         if(!s){
56585             e.stopEvent();
56586             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56587             if(cell){
56588                 this.select(cell[0], cell[1]);
56589             }
56590             return;
56591         }
56592         var sm = this;
56593         var walk = function(row, col, step){
56594             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56595         };
56596         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56597         var newCell;
56598
56599       
56600
56601         switch(k){
56602             case e.TAB:
56603                 // handled by onEditorKey
56604                 if (g.isEditor && g.editing) {
56605                     return;
56606                 }
56607                 if(e.shiftKey) {
56608                     newCell = walk(r, c-1, -1);
56609                 } else {
56610                     newCell = walk(r, c+1, 1);
56611                 }
56612                 break;
56613             
56614             case e.DOWN:
56615                newCell = walk(r+1, c, 1);
56616                 break;
56617             
56618             case e.UP:
56619                 newCell = walk(r-1, c, -1);
56620                 break;
56621             
56622             case e.RIGHT:
56623                 newCell = walk(r, c+1, 1);
56624                 break;
56625             
56626             case e.LEFT:
56627                 newCell = walk(r, c-1, -1);
56628                 break;
56629             
56630             case e.ENTER:
56631                 
56632                 if(g.isEditor && !g.editing){
56633                    g.startEditing(r, c);
56634                    e.stopEvent();
56635                    return;
56636                 }
56637                 
56638                 
56639              break;
56640         };
56641         if(newCell){
56642             this.select(newCell[0], newCell[1]);
56643             e.stopEvent();
56644             
56645         }
56646     },
56647
56648     acceptsNav : function(row, col, cm){
56649         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56650     },
56651     /**
56652      * Selects a cell.
56653      * @param {Number} field (not used) - as it's normally used as a listener
56654      * @param {Number} e - event - fake it by using
56655      *
56656      * var e = Roo.EventObjectImpl.prototype;
56657      * e.keyCode = e.TAB
56658      *
56659      * 
56660      */
56661     onEditorKey : function(field, e){
56662         
56663         var k = e.getKey(),
56664             newCell,
56665             g = this.grid,
56666             ed = g.activeEditor,
56667             forward = false;
56668         ///Roo.log('onEditorKey' + k);
56669         
56670         
56671         if (this.enter_is_tab && k == e.ENTER) {
56672             k = e.TAB;
56673         }
56674         
56675         if(k == e.TAB){
56676             if(e.shiftKey){
56677                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56678             }else{
56679                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56680                 forward = true;
56681             }
56682             
56683             e.stopEvent();
56684             
56685         } else if(k == e.ENTER &&  !e.ctrlKey){
56686             ed.completeEdit();
56687             e.stopEvent();
56688             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56689         
56690                 } else if(k == e.ESC){
56691             ed.cancelEdit();
56692         }
56693                 
56694         if (newCell) {
56695             var ecall = { cell : newCell, forward : forward };
56696             this.fireEvent('beforeeditnext', ecall );
56697             newCell = ecall.cell;
56698                         forward = ecall.forward;
56699         }
56700                 
56701         if(newCell){
56702             //Roo.log('next cell after edit');
56703             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56704         } else if (forward) {
56705             // tabbed past last
56706             this.fireEvent.defer(100, this, ['tabend',this]);
56707         }
56708     }
56709 });/*
56710  * Based on:
56711  * Ext JS Library 1.1.1
56712  * Copyright(c) 2006-2007, Ext JS, LLC.
56713  *
56714  * Originally Released Under LGPL - original licence link has changed is not relivant.
56715  *
56716  * Fork - LGPL
56717  * <script type="text/javascript">
56718  */
56719  
56720 /**
56721  * @class Roo.grid.EditorGrid
56722  * @extends Roo.grid.Grid
56723  * Class for creating and editable grid.
56724  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56725  * The container MUST have some type of size defined for the grid to fill. The container will be 
56726  * automatically set to position relative if it isn't already.
56727  * @param {Object} dataSource The data model to bind to
56728  * @param {Object} colModel The column model with info about this grid's columns
56729  */
56730 Roo.grid.EditorGrid = function(container, config){
56731     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56732     this.getGridEl().addClass("xedit-grid");
56733
56734     if(!this.selModel){
56735         this.selModel = new Roo.grid.CellSelectionModel();
56736     }
56737
56738     this.activeEditor = null;
56739
56740         this.addEvents({
56741             /**
56742              * @event beforeedit
56743              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56744              * <ul style="padding:5px;padding-left:16px;">
56745              * <li>grid - This grid</li>
56746              * <li>record - The record being edited</li>
56747              * <li>field - The field name being edited</li>
56748              * <li>value - The value for the field being edited.</li>
56749              * <li>row - The grid row index</li>
56750              * <li>column - The grid column index</li>
56751              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56752              * </ul>
56753              * @param {Object} e An edit event (see above for description)
56754              */
56755             "beforeedit" : true,
56756             /**
56757              * @event afteredit
56758              * Fires after a cell is edited. <br />
56759              * <ul style="padding:5px;padding-left:16px;">
56760              * <li>grid - This grid</li>
56761              * <li>record - The record being edited</li>
56762              * <li>field - The field name being edited</li>
56763              * <li>value - The value being set</li>
56764              * <li>originalValue - The original value for the field, before the edit.</li>
56765              * <li>row - The grid row index</li>
56766              * <li>column - The grid column index</li>
56767              * </ul>
56768              * @param {Object} e An edit event (see above for description)
56769              */
56770             "afteredit" : true,
56771             /**
56772              * @event validateedit
56773              * Fires after a cell is edited, but before the value is set in the record. 
56774          * You can use this to modify the value being set in the field, Return false
56775              * to cancel the change. The edit event object has the following properties <br />
56776              * <ul style="padding:5px;padding-left:16px;">
56777          * <li>editor - This editor</li>
56778              * <li>grid - This grid</li>
56779              * <li>record - The record being edited</li>
56780              * <li>field - The field name being edited</li>
56781              * <li>value - The value being set</li>
56782              * <li>originalValue - The original value for the field, before the edit.</li>
56783              * <li>row - The grid row index</li>
56784              * <li>column - The grid column index</li>
56785              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56786              * </ul>
56787              * @param {Object} e An edit event (see above for description)
56788              */
56789             "validateedit" : true
56790         });
56791     this.on("bodyscroll", this.stopEditing,  this);
56792     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56793 };
56794
56795 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56796     /**
56797      * @cfg {Number} clicksToEdit
56798      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56799      */
56800     clicksToEdit: 2,
56801
56802     // private
56803     isEditor : true,
56804     // private
56805     trackMouseOver: false, // causes very odd FF errors
56806
56807     onCellDblClick : function(g, row, col){
56808         this.startEditing(row, col);
56809     },
56810
56811     onEditComplete : function(ed, value, startValue){
56812         this.editing = false;
56813         this.activeEditor = null;
56814         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56815         var r = ed.record;
56816         var field = this.colModel.getDataIndex(ed.col);
56817         var e = {
56818             grid: this,
56819             record: r,
56820             field: field,
56821             originalValue: startValue,
56822             value: value,
56823             row: ed.row,
56824             column: ed.col,
56825             cancel:false,
56826             editor: ed
56827         };
56828         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
56829         cell.show();
56830           
56831         if(String(value) !== String(startValue)){
56832             
56833             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56834                 r.set(field, e.value);
56835                 // if we are dealing with a combo box..
56836                 // then we also set the 'name' colum to be the displayField
56837                 if (ed.field.displayField && ed.field.name) {
56838                     r.set(ed.field.name, ed.field.el.dom.value);
56839                 }
56840                 
56841                 delete e.cancel; //?? why!!!
56842                 this.fireEvent("afteredit", e);
56843             }
56844         } else {
56845             this.fireEvent("afteredit", e); // always fire it!
56846         }
56847         this.view.focusCell(ed.row, ed.col);
56848     },
56849
56850     /**
56851      * Starts editing the specified for the specified row/column
56852      * @param {Number} rowIndex
56853      * @param {Number} colIndex
56854      */
56855     startEditing : function(row, col){
56856         this.stopEditing();
56857         if(this.colModel.isCellEditable(col, row)){
56858             this.view.ensureVisible(row, col, true);
56859           
56860             var r = this.dataSource.getAt(row);
56861             var field = this.colModel.getDataIndex(col);
56862             var cell = Roo.get(this.view.getCell(row,col));
56863             var e = {
56864                 grid: this,
56865                 record: r,
56866                 field: field,
56867                 value: r.data[field],
56868                 row: row,
56869                 column: col,
56870                 cancel:false 
56871             };
56872             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56873                 this.editing = true;
56874                 var ed = this.colModel.getCellEditor(col, row);
56875                 
56876                 if (!ed) {
56877                     return;
56878                 }
56879                 if(!ed.rendered){
56880                     ed.render(ed.parentEl || document.body);
56881                 }
56882                 ed.field.reset();
56883                
56884                 cell.hide();
56885                 
56886                 (function(){ // complex but required for focus issues in safari, ie and opera
56887                     ed.row = row;
56888                     ed.col = col;
56889                     ed.record = r;
56890                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56891                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56892                     this.activeEditor = ed;
56893                     var v = r.data[field];
56894                     ed.startEdit(this.view.getCell(row, col), v);
56895                     // combo's with 'displayField and name set
56896                     if (ed.field.displayField && ed.field.name) {
56897                         ed.field.el.dom.value = r.data[ed.field.name];
56898                     }
56899                     
56900                     
56901                 }).defer(50, this);
56902             }
56903         }
56904     },
56905         
56906     /**
56907      * Stops any active editing
56908      */
56909     stopEditing : function(){
56910         if(this.activeEditor){
56911             this.activeEditor.completeEdit();
56912         }
56913         this.activeEditor = null;
56914     },
56915         
56916          /**
56917      * Called to get grid's drag proxy text, by default returns this.ddText.
56918      * @return {String}
56919      */
56920     getDragDropText : function(){
56921         var count = this.selModel.getSelectedCell() ? 1 : 0;
56922         return String.format(this.ddText, count, count == 1 ? '' : 's');
56923     }
56924         
56925 });/*
56926  * Based on:
56927  * Ext JS Library 1.1.1
56928  * Copyright(c) 2006-2007, Ext JS, LLC.
56929  *
56930  * Originally Released Under LGPL - original licence link has changed is not relivant.
56931  *
56932  * Fork - LGPL
56933  * <script type="text/javascript">
56934  */
56935
56936 // private - not really -- you end up using it !
56937 // This is a support class used internally by the Grid components
56938
56939 /**
56940  * @class Roo.grid.GridEditor
56941  * @extends Roo.Editor
56942  * Class for creating and editable grid elements.
56943  * @param {Object} config any settings (must include field)
56944  */
56945 Roo.grid.GridEditor = function(field, config){
56946     if (!config && field.field) {
56947         config = field;
56948         field = Roo.factory(config.field, Roo.form);
56949     }
56950     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56951     field.monitorTab = false;
56952 };
56953
56954 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56955     
56956     /**
56957      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56958      */
56959     
56960     alignment: "tl-tl",
56961     autoSize: "width",
56962     hideEl : false,
56963     cls: "x-small-editor x-grid-editor",
56964     shim:false,
56965     shadow:"frame"
56966 });/*
56967  * Based on:
56968  * Ext JS Library 1.1.1
56969  * Copyright(c) 2006-2007, Ext JS, LLC.
56970  *
56971  * Originally Released Under LGPL - original licence link has changed is not relivant.
56972  *
56973  * Fork - LGPL
56974  * <script type="text/javascript">
56975  */
56976   
56977
56978   
56979 Roo.grid.PropertyRecord = Roo.data.Record.create([
56980     {name:'name',type:'string'},  'value'
56981 ]);
56982
56983
56984 Roo.grid.PropertyStore = function(grid, source){
56985     this.grid = grid;
56986     this.store = new Roo.data.Store({
56987         recordType : Roo.grid.PropertyRecord
56988     });
56989     this.store.on('update', this.onUpdate,  this);
56990     if(source){
56991         this.setSource(source);
56992     }
56993     Roo.grid.PropertyStore.superclass.constructor.call(this);
56994 };
56995
56996
56997
56998 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56999     setSource : function(o){
57000         this.source = o;
57001         this.store.removeAll();
57002         var data = [];
57003         for(var k in o){
57004             if(this.isEditableValue(o[k])){
57005                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
57006             }
57007         }
57008         this.store.loadRecords({records: data}, {}, true);
57009     },
57010
57011     onUpdate : function(ds, record, type){
57012         if(type == Roo.data.Record.EDIT){
57013             var v = record.data['value'];
57014             var oldValue = record.modified['value'];
57015             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
57016                 this.source[record.id] = v;
57017                 record.commit();
57018                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
57019             }else{
57020                 record.reject();
57021             }
57022         }
57023     },
57024
57025     getProperty : function(row){
57026        return this.store.getAt(row);
57027     },
57028
57029     isEditableValue: function(val){
57030         if(val && val instanceof Date){
57031             return true;
57032         }else if(typeof val == 'object' || typeof val == 'function'){
57033             return false;
57034         }
57035         return true;
57036     },
57037
57038     setValue : function(prop, value){
57039         this.source[prop] = value;
57040         this.store.getById(prop).set('value', value);
57041     },
57042
57043     getSource : function(){
57044         return this.source;
57045     }
57046 });
57047
57048 Roo.grid.PropertyColumnModel = function(grid, store){
57049     this.grid = grid;
57050     var g = Roo.grid;
57051     g.PropertyColumnModel.superclass.constructor.call(this, [
57052         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
57053         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
57054     ]);
57055     this.store = store;
57056     this.bselect = Roo.DomHelper.append(document.body, {
57057         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
57058             {tag: 'option', value: 'true', html: 'true'},
57059             {tag: 'option', value: 'false', html: 'false'}
57060         ]
57061     });
57062     Roo.id(this.bselect);
57063     var f = Roo.form;
57064     this.editors = {
57065         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
57066         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
57067         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
57068         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
57069         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
57070     };
57071     this.renderCellDelegate = this.renderCell.createDelegate(this);
57072     this.renderPropDelegate = this.renderProp.createDelegate(this);
57073 };
57074
57075 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
57076     
57077     
57078     nameText : 'Name',
57079     valueText : 'Value',
57080     
57081     dateFormat : 'm/j/Y',
57082     
57083     
57084     renderDate : function(dateVal){
57085         return dateVal.dateFormat(this.dateFormat);
57086     },
57087
57088     renderBool : function(bVal){
57089         return bVal ? 'true' : 'false';
57090     },
57091
57092     isCellEditable : function(colIndex, rowIndex){
57093         return colIndex == 1;
57094     },
57095
57096     getRenderer : function(col){
57097         return col == 1 ?
57098             this.renderCellDelegate : this.renderPropDelegate;
57099     },
57100
57101     renderProp : function(v){
57102         return this.getPropertyName(v);
57103     },
57104
57105     renderCell : function(val){
57106         var rv = val;
57107         if(val instanceof Date){
57108             rv = this.renderDate(val);
57109         }else if(typeof val == 'boolean'){
57110             rv = this.renderBool(val);
57111         }
57112         return Roo.util.Format.htmlEncode(rv);
57113     },
57114
57115     getPropertyName : function(name){
57116         var pn = this.grid.propertyNames;
57117         return pn && pn[name] ? pn[name] : name;
57118     },
57119
57120     getCellEditor : function(colIndex, rowIndex){
57121         var p = this.store.getProperty(rowIndex);
57122         var n = p.data['name'], val = p.data['value'];
57123         
57124         if(typeof(this.grid.customEditors[n]) == 'string'){
57125             return this.editors[this.grid.customEditors[n]];
57126         }
57127         if(typeof(this.grid.customEditors[n]) != 'undefined'){
57128             return this.grid.customEditors[n];
57129         }
57130         if(val instanceof Date){
57131             return this.editors['date'];
57132         }else if(typeof val == 'number'){
57133             return this.editors['number'];
57134         }else if(typeof val == 'boolean'){
57135             return this.editors['boolean'];
57136         }else{
57137             return this.editors['string'];
57138         }
57139     }
57140 });
57141
57142 /**
57143  * @class Roo.grid.PropertyGrid
57144  * @extends Roo.grid.EditorGrid
57145  * This class represents the  interface of a component based property grid control.
57146  * <br><br>Usage:<pre><code>
57147  var grid = new Roo.grid.PropertyGrid("my-container-id", {
57148       
57149  });
57150  // set any options
57151  grid.render();
57152  * </code></pre>
57153   
57154  * @constructor
57155  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57156  * The container MUST have some type of size defined for the grid to fill. The container will be
57157  * automatically set to position relative if it isn't already.
57158  * @param {Object} config A config object that sets properties on this grid.
57159  */
57160 Roo.grid.PropertyGrid = function(container, config){
57161     config = config || {};
57162     var store = new Roo.grid.PropertyStore(this);
57163     this.store = store;
57164     var cm = new Roo.grid.PropertyColumnModel(this, store);
57165     store.store.sort('name', 'ASC');
57166     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
57167         ds: store.store,
57168         cm: cm,
57169         enableColLock:false,
57170         enableColumnMove:false,
57171         stripeRows:false,
57172         trackMouseOver: false,
57173         clicksToEdit:1
57174     }, config));
57175     this.getGridEl().addClass('x-props-grid');
57176     this.lastEditRow = null;
57177     this.on('columnresize', this.onColumnResize, this);
57178     this.addEvents({
57179          /**
57180              * @event beforepropertychange
57181              * Fires before a property changes (return false to stop?)
57182              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57183              * @param {String} id Record Id
57184              * @param {String} newval New Value
57185          * @param {String} oldval Old Value
57186              */
57187         "beforepropertychange": true,
57188         /**
57189              * @event propertychange
57190              * Fires after a property changes
57191              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57192              * @param {String} id Record Id
57193              * @param {String} newval New Value
57194          * @param {String} oldval Old Value
57195              */
57196         "propertychange": true
57197     });
57198     this.customEditors = this.customEditors || {};
57199 };
57200 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57201     
57202      /**
57203      * @cfg {Object} customEditors map of colnames=> custom editors.
57204      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57205      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57206      * false disables editing of the field.
57207          */
57208     
57209       /**
57210      * @cfg {Object} propertyNames map of property Names to their displayed value
57211          */
57212     
57213     render : function(){
57214         Roo.grid.PropertyGrid.superclass.render.call(this);
57215         this.autoSize.defer(100, this);
57216     },
57217
57218     autoSize : function(){
57219         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57220         if(this.view){
57221             this.view.fitColumns();
57222         }
57223     },
57224
57225     onColumnResize : function(){
57226         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57227         this.autoSize();
57228     },
57229     /**
57230      * Sets the data for the Grid
57231      * accepts a Key => Value object of all the elements avaiable.
57232      * @param {Object} data  to appear in grid.
57233      */
57234     setSource : function(source){
57235         this.store.setSource(source);
57236         //this.autoSize();
57237     },
57238     /**
57239      * Gets all the data from the grid.
57240      * @return {Object} data  data stored in grid
57241      */
57242     getSource : function(){
57243         return this.store.getSource();
57244     }
57245 });/*
57246   
57247  * Licence LGPL
57248  
57249  */
57250  
57251 /**
57252  * @class Roo.grid.Calendar
57253  * @extends Roo.util.Grid
57254  * This class extends the Grid to provide a calendar widget
57255  * <br><br>Usage:<pre><code>
57256  var grid = new Roo.grid.Calendar("my-container-id", {
57257      ds: myDataStore,
57258      cm: myColModel,
57259      selModel: mySelectionModel,
57260      autoSizeColumns: true,
57261      monitorWindowResize: false,
57262      trackMouseOver: true
57263      eventstore : real data store..
57264  });
57265  // set any options
57266  grid.render();
57267   
57268   * @constructor
57269  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57270  * The container MUST have some type of size defined for the grid to fill. The container will be
57271  * automatically set to position relative if it isn't already.
57272  * @param {Object} config A config object that sets properties on this grid.
57273  */
57274 Roo.grid.Calendar = function(container, config){
57275         // initialize the container
57276         this.container = Roo.get(container);
57277         this.container.update("");
57278         this.container.setStyle("overflow", "hidden");
57279     this.container.addClass('x-grid-container');
57280
57281     this.id = this.container.id;
57282
57283     Roo.apply(this, config);
57284     // check and correct shorthanded configs
57285     
57286     var rows = [];
57287     var d =1;
57288     for (var r = 0;r < 6;r++) {
57289         
57290         rows[r]=[];
57291         for (var c =0;c < 7;c++) {
57292             rows[r][c]= '';
57293         }
57294     }
57295     if (this.eventStore) {
57296         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57297         this.eventStore.on('load',this.onLoad, this);
57298         this.eventStore.on('beforeload',this.clearEvents, this);
57299          
57300     }
57301     
57302     this.dataSource = new Roo.data.Store({
57303             proxy: new Roo.data.MemoryProxy(rows),
57304             reader: new Roo.data.ArrayReader({}, [
57305                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57306     });
57307
57308     this.dataSource.load();
57309     this.ds = this.dataSource;
57310     this.ds.xmodule = this.xmodule || false;
57311     
57312     
57313     var cellRender = function(v,x,r)
57314     {
57315         return String.format(
57316             '<div class="fc-day  fc-widget-content"><div>' +
57317                 '<div class="fc-event-container"></div>' +
57318                 '<div class="fc-day-number">{0}</div>'+
57319                 
57320                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57321             '</div></div>', v);
57322     
57323     }
57324     
57325     
57326     this.colModel = new Roo.grid.ColumnModel( [
57327         {
57328             xtype: 'ColumnModel',
57329             xns: Roo.grid,
57330             dataIndex : 'weekday0',
57331             header : 'Sunday',
57332             renderer : cellRender
57333         },
57334         {
57335             xtype: 'ColumnModel',
57336             xns: Roo.grid,
57337             dataIndex : 'weekday1',
57338             header : 'Monday',
57339             renderer : cellRender
57340         },
57341         {
57342             xtype: 'ColumnModel',
57343             xns: Roo.grid,
57344             dataIndex : 'weekday2',
57345             header : 'Tuesday',
57346             renderer : cellRender
57347         },
57348         {
57349             xtype: 'ColumnModel',
57350             xns: Roo.grid,
57351             dataIndex : 'weekday3',
57352             header : 'Wednesday',
57353             renderer : cellRender
57354         },
57355         {
57356             xtype: 'ColumnModel',
57357             xns: Roo.grid,
57358             dataIndex : 'weekday4',
57359             header : 'Thursday',
57360             renderer : cellRender
57361         },
57362         {
57363             xtype: 'ColumnModel',
57364             xns: Roo.grid,
57365             dataIndex : 'weekday5',
57366             header : 'Friday',
57367             renderer : cellRender
57368         },
57369         {
57370             xtype: 'ColumnModel',
57371             xns: Roo.grid,
57372             dataIndex : 'weekday6',
57373             header : 'Saturday',
57374             renderer : cellRender
57375         }
57376     ]);
57377     this.cm = this.colModel;
57378     this.cm.xmodule = this.xmodule || false;
57379  
57380         
57381           
57382     //this.selModel = new Roo.grid.CellSelectionModel();
57383     //this.sm = this.selModel;
57384     //this.selModel.init(this);
57385     
57386     
57387     if(this.width){
57388         this.container.setWidth(this.width);
57389     }
57390
57391     if(this.height){
57392         this.container.setHeight(this.height);
57393     }
57394     /** @private */
57395         this.addEvents({
57396         // raw events
57397         /**
57398          * @event click
57399          * The raw click event for the entire grid.
57400          * @param {Roo.EventObject} e
57401          */
57402         "click" : true,
57403         /**
57404          * @event dblclick
57405          * The raw dblclick event for the entire grid.
57406          * @param {Roo.EventObject} e
57407          */
57408         "dblclick" : true,
57409         /**
57410          * @event contextmenu
57411          * The raw contextmenu event for the entire grid.
57412          * @param {Roo.EventObject} e
57413          */
57414         "contextmenu" : true,
57415         /**
57416          * @event mousedown
57417          * The raw mousedown event for the entire grid.
57418          * @param {Roo.EventObject} e
57419          */
57420         "mousedown" : true,
57421         /**
57422          * @event mouseup
57423          * The raw mouseup event for the entire grid.
57424          * @param {Roo.EventObject} e
57425          */
57426         "mouseup" : true,
57427         /**
57428          * @event mouseover
57429          * The raw mouseover event for the entire grid.
57430          * @param {Roo.EventObject} e
57431          */
57432         "mouseover" : true,
57433         /**
57434          * @event mouseout
57435          * The raw mouseout event for the entire grid.
57436          * @param {Roo.EventObject} e
57437          */
57438         "mouseout" : true,
57439         /**
57440          * @event keypress
57441          * The raw keypress event for the entire grid.
57442          * @param {Roo.EventObject} e
57443          */
57444         "keypress" : true,
57445         /**
57446          * @event keydown
57447          * The raw keydown event for the entire grid.
57448          * @param {Roo.EventObject} e
57449          */
57450         "keydown" : true,
57451
57452         // custom events
57453
57454         /**
57455          * @event cellclick
57456          * Fires when a cell is clicked
57457          * @param {Grid} this
57458          * @param {Number} rowIndex
57459          * @param {Number} columnIndex
57460          * @param {Roo.EventObject} e
57461          */
57462         "cellclick" : true,
57463         /**
57464          * @event celldblclick
57465          * Fires when a cell is double clicked
57466          * @param {Grid} this
57467          * @param {Number} rowIndex
57468          * @param {Number} columnIndex
57469          * @param {Roo.EventObject} e
57470          */
57471         "celldblclick" : true,
57472         /**
57473          * @event rowclick
57474          * Fires when a row is clicked
57475          * @param {Grid} this
57476          * @param {Number} rowIndex
57477          * @param {Roo.EventObject} e
57478          */
57479         "rowclick" : true,
57480         /**
57481          * @event rowdblclick
57482          * Fires when a row is double clicked
57483          * @param {Grid} this
57484          * @param {Number} rowIndex
57485          * @param {Roo.EventObject} e
57486          */
57487         "rowdblclick" : true,
57488         /**
57489          * @event headerclick
57490          * Fires when a header is clicked
57491          * @param {Grid} this
57492          * @param {Number} columnIndex
57493          * @param {Roo.EventObject} e
57494          */
57495         "headerclick" : true,
57496         /**
57497          * @event headerdblclick
57498          * Fires when a header cell is double clicked
57499          * @param {Grid} this
57500          * @param {Number} columnIndex
57501          * @param {Roo.EventObject} e
57502          */
57503         "headerdblclick" : true,
57504         /**
57505          * @event rowcontextmenu
57506          * Fires when a row is right clicked
57507          * @param {Grid} this
57508          * @param {Number} rowIndex
57509          * @param {Roo.EventObject} e
57510          */
57511         "rowcontextmenu" : true,
57512         /**
57513          * @event cellcontextmenu
57514          * Fires when a cell is right clicked
57515          * @param {Grid} this
57516          * @param {Number} rowIndex
57517          * @param {Number} cellIndex
57518          * @param {Roo.EventObject} e
57519          */
57520          "cellcontextmenu" : true,
57521         /**
57522          * @event headercontextmenu
57523          * Fires when a header is right clicked
57524          * @param {Grid} this
57525          * @param {Number} columnIndex
57526          * @param {Roo.EventObject} e
57527          */
57528         "headercontextmenu" : true,
57529         /**
57530          * @event bodyscroll
57531          * Fires when the body element is scrolled
57532          * @param {Number} scrollLeft
57533          * @param {Number} scrollTop
57534          */
57535         "bodyscroll" : true,
57536         /**
57537          * @event columnresize
57538          * Fires when the user resizes a column
57539          * @param {Number} columnIndex
57540          * @param {Number} newSize
57541          */
57542         "columnresize" : true,
57543         /**
57544          * @event columnmove
57545          * Fires when the user moves a column
57546          * @param {Number} oldIndex
57547          * @param {Number} newIndex
57548          */
57549         "columnmove" : true,
57550         /**
57551          * @event startdrag
57552          * Fires when row(s) start being dragged
57553          * @param {Grid} this
57554          * @param {Roo.GridDD} dd The drag drop object
57555          * @param {event} e The raw browser event
57556          */
57557         "startdrag" : true,
57558         /**
57559          * @event enddrag
57560          * Fires when a drag operation is complete
57561          * @param {Grid} this
57562          * @param {Roo.GridDD} dd The drag drop object
57563          * @param {event} e The raw browser event
57564          */
57565         "enddrag" : true,
57566         /**
57567          * @event dragdrop
57568          * Fires when dragged row(s) are dropped on a valid DD target
57569          * @param {Grid} this
57570          * @param {Roo.GridDD} dd The drag drop object
57571          * @param {String} targetId The target drag drop object
57572          * @param {event} e The raw browser event
57573          */
57574         "dragdrop" : true,
57575         /**
57576          * @event dragover
57577          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57578          * @param {Grid} this
57579          * @param {Roo.GridDD} dd The drag drop object
57580          * @param {String} targetId The target drag drop object
57581          * @param {event} e The raw browser event
57582          */
57583         "dragover" : true,
57584         /**
57585          * @event dragenter
57586          *  Fires when the dragged row(s) first cross another DD target while being dragged
57587          * @param {Grid} this
57588          * @param {Roo.GridDD} dd The drag drop object
57589          * @param {String} targetId The target drag drop object
57590          * @param {event} e The raw browser event
57591          */
57592         "dragenter" : true,
57593         /**
57594          * @event dragout
57595          * Fires when the dragged row(s) leave another DD target while being dragged
57596          * @param {Grid} this
57597          * @param {Roo.GridDD} dd The drag drop object
57598          * @param {String} targetId The target drag drop object
57599          * @param {event} e The raw browser event
57600          */
57601         "dragout" : true,
57602         /**
57603          * @event rowclass
57604          * Fires when a row is rendered, so you can change add a style to it.
57605          * @param {GridView} gridview   The grid view
57606          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57607          */
57608         'rowclass' : true,
57609
57610         /**
57611          * @event render
57612          * Fires when the grid is rendered
57613          * @param {Grid} grid
57614          */
57615         'render' : true,
57616             /**
57617              * @event select
57618              * Fires when a date is selected
57619              * @param {DatePicker} this
57620              * @param {Date} date The selected date
57621              */
57622         'select': true,
57623         /**
57624              * @event monthchange
57625              * Fires when the displayed month changes 
57626              * @param {DatePicker} this
57627              * @param {Date} date The selected month
57628              */
57629         'monthchange': true,
57630         /**
57631              * @event evententer
57632              * Fires when mouse over an event
57633              * @param {Calendar} this
57634              * @param {event} Event
57635              */
57636         'evententer': true,
57637         /**
57638              * @event eventleave
57639              * Fires when the mouse leaves an
57640              * @param {Calendar} this
57641              * @param {event}
57642              */
57643         'eventleave': true,
57644         /**
57645              * @event eventclick
57646              * Fires when the mouse click an
57647              * @param {Calendar} this
57648              * @param {event}
57649              */
57650         'eventclick': true,
57651         /**
57652              * @event eventrender
57653              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57654              * @param {Calendar} this
57655              * @param {data} data to be modified
57656              */
57657         'eventrender': true
57658         
57659     });
57660
57661     Roo.grid.Grid.superclass.constructor.call(this);
57662     this.on('render', function() {
57663         this.view.el.addClass('x-grid-cal'); 
57664         
57665         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57666
57667     },this);
57668     
57669     if (!Roo.grid.Calendar.style) {
57670         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57671             
57672             
57673             '.x-grid-cal .x-grid-col' :  {
57674                 height: 'auto !important',
57675                 'vertical-align': 'top'
57676             },
57677             '.x-grid-cal  .fc-event-hori' : {
57678                 height: '14px'
57679             }
57680              
57681             
57682         }, Roo.id());
57683     }
57684
57685     
57686     
57687 };
57688 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57689     /**
57690      * @cfg {Store} eventStore The store that loads events.
57691      */
57692     eventStore : 25,
57693
57694      
57695     activeDate : false,
57696     startDay : 0,
57697     autoWidth : true,
57698     monitorWindowResize : false,
57699
57700     
57701     resizeColumns : function() {
57702         var col = (this.view.el.getWidth() / 7) - 3;
57703         // loop through cols, and setWidth
57704         for(var i =0 ; i < 7 ; i++){
57705             this.cm.setColumnWidth(i, col);
57706         }
57707     },
57708      setDate :function(date) {
57709         
57710         Roo.log('setDate?');
57711         
57712         this.resizeColumns();
57713         var vd = this.activeDate;
57714         this.activeDate = date;
57715 //        if(vd && this.el){
57716 //            var t = date.getTime();
57717 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57718 //                Roo.log('using add remove');
57719 //                
57720 //                this.fireEvent('monthchange', this, date);
57721 //                
57722 //                this.cells.removeClass("fc-state-highlight");
57723 //                this.cells.each(function(c){
57724 //                   if(c.dateValue == t){
57725 //                       c.addClass("fc-state-highlight");
57726 //                       setTimeout(function(){
57727 //                            try{c.dom.firstChild.focus();}catch(e){}
57728 //                       }, 50);
57729 //                       return false;
57730 //                   }
57731 //                   return true;
57732 //                });
57733 //                return;
57734 //            }
57735 //        }
57736         
57737         var days = date.getDaysInMonth();
57738         
57739         var firstOfMonth = date.getFirstDateOfMonth();
57740         var startingPos = firstOfMonth.getDay()-this.startDay;
57741         
57742         if(startingPos < this.startDay){
57743             startingPos += 7;
57744         }
57745         
57746         var pm = date.add(Date.MONTH, -1);
57747         var prevStart = pm.getDaysInMonth()-startingPos;
57748 //        
57749         
57750         
57751         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57752         
57753         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57754         //this.cells.addClassOnOver('fc-state-hover');
57755         
57756         var cells = this.cells.elements;
57757         var textEls = this.textNodes;
57758         
57759         //Roo.each(cells, function(cell){
57760         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57761         //});
57762         
57763         days += startingPos;
57764
57765         // convert everything to numbers so it's fast
57766         var day = 86400000;
57767         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57768         //Roo.log(d);
57769         //Roo.log(pm);
57770         //Roo.log(prevStart);
57771         
57772         var today = new Date().clearTime().getTime();
57773         var sel = date.clearTime().getTime();
57774         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57775         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57776         var ddMatch = this.disabledDatesRE;
57777         var ddText = this.disabledDatesText;
57778         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57779         var ddaysText = this.disabledDaysText;
57780         var format = this.format;
57781         
57782         var setCellClass = function(cal, cell){
57783             
57784             //Roo.log('set Cell Class');
57785             cell.title = "";
57786             var t = d.getTime();
57787             
57788             //Roo.log(d);
57789             
57790             
57791             cell.dateValue = t;
57792             if(t == today){
57793                 cell.className += " fc-today";
57794                 cell.className += " fc-state-highlight";
57795                 cell.title = cal.todayText;
57796             }
57797             if(t == sel){
57798                 // disable highlight in other month..
57799                 cell.className += " fc-state-highlight";
57800                 
57801             }
57802             // disabling
57803             if(t < min) {
57804                 //cell.className = " fc-state-disabled";
57805                 cell.title = cal.minText;
57806                 return;
57807             }
57808             if(t > max) {
57809                 //cell.className = " fc-state-disabled";
57810                 cell.title = cal.maxText;
57811                 return;
57812             }
57813             if(ddays){
57814                 if(ddays.indexOf(d.getDay()) != -1){
57815                     // cell.title = ddaysText;
57816                    // cell.className = " fc-state-disabled";
57817                 }
57818             }
57819             if(ddMatch && format){
57820                 var fvalue = d.dateFormat(format);
57821                 if(ddMatch.test(fvalue)){
57822                     cell.title = ddText.replace("%0", fvalue);
57823                    cell.className = " fc-state-disabled";
57824                 }
57825             }
57826             
57827             if (!cell.initialClassName) {
57828                 cell.initialClassName = cell.dom.className;
57829             }
57830             
57831             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57832         };
57833
57834         var i = 0;
57835         
57836         for(; i < startingPos; i++) {
57837             cells[i].dayName =  (++prevStart);
57838             Roo.log(textEls[i]);
57839             d.setDate(d.getDate()+1);
57840             
57841             //cells[i].className = "fc-past fc-other-month";
57842             setCellClass(this, cells[i]);
57843         }
57844         
57845         var intDay = 0;
57846         
57847         for(; i < days; i++){
57848             intDay = i - startingPos + 1;
57849             cells[i].dayName =  (intDay);
57850             d.setDate(d.getDate()+1);
57851             
57852             cells[i].className = ''; // "x-date-active";
57853             setCellClass(this, cells[i]);
57854         }
57855         var extraDays = 0;
57856         
57857         for(; i < 42; i++) {
57858             //textEls[i].innerHTML = (++extraDays);
57859             
57860             d.setDate(d.getDate()+1);
57861             cells[i].dayName = (++extraDays);
57862             cells[i].className = "fc-future fc-other-month";
57863             setCellClass(this, cells[i]);
57864         }
57865         
57866         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57867         
57868         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57869         
57870         // this will cause all the cells to mis
57871         var rows= [];
57872         var i =0;
57873         for (var r = 0;r < 6;r++) {
57874             for (var c =0;c < 7;c++) {
57875                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57876             }    
57877         }
57878         
57879         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57880         for(i=0;i<cells.length;i++) {
57881             
57882             this.cells.elements[i].dayName = cells[i].dayName ;
57883             this.cells.elements[i].className = cells[i].className;
57884             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57885             this.cells.elements[i].title = cells[i].title ;
57886             this.cells.elements[i].dateValue = cells[i].dateValue ;
57887         }
57888         
57889         
57890         
57891         
57892         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57893         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57894         
57895         ////if(totalRows != 6){
57896             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57897            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57898        // }
57899         
57900         this.fireEvent('monthchange', this, date);
57901         
57902         
57903     },
57904  /**
57905      * Returns the grid's SelectionModel.
57906      * @return {SelectionModel}
57907      */
57908     getSelectionModel : function(){
57909         if(!this.selModel){
57910             this.selModel = new Roo.grid.CellSelectionModel();
57911         }
57912         return this.selModel;
57913     },
57914
57915     load: function() {
57916         this.eventStore.load()
57917         
57918         
57919         
57920     },
57921     
57922     findCell : function(dt) {
57923         dt = dt.clearTime().getTime();
57924         var ret = false;
57925         this.cells.each(function(c){
57926             //Roo.log("check " +c.dateValue + '?=' + dt);
57927             if(c.dateValue == dt){
57928                 ret = c;
57929                 return false;
57930             }
57931             return true;
57932         });
57933         
57934         return ret;
57935     },
57936     
57937     findCells : function(rec) {
57938         var s = rec.data.start_dt.clone().clearTime().getTime();
57939        // Roo.log(s);
57940         var e= rec.data.end_dt.clone().clearTime().getTime();
57941        // Roo.log(e);
57942         var ret = [];
57943         this.cells.each(function(c){
57944              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57945             
57946             if(c.dateValue > e){
57947                 return ;
57948             }
57949             if(c.dateValue < s){
57950                 return ;
57951             }
57952             ret.push(c);
57953         });
57954         
57955         return ret;    
57956     },
57957     
57958     findBestRow: function(cells)
57959     {
57960         var ret = 0;
57961         
57962         for (var i =0 ; i < cells.length;i++) {
57963             ret  = Math.max(cells[i].rows || 0,ret);
57964         }
57965         return ret;
57966         
57967     },
57968     
57969     
57970     addItem : function(rec)
57971     {
57972         // look for vertical location slot in
57973         var cells = this.findCells(rec);
57974         
57975         rec.row = this.findBestRow(cells);
57976         
57977         // work out the location.
57978         
57979         var crow = false;
57980         var rows = [];
57981         for(var i =0; i < cells.length; i++) {
57982             if (!crow) {
57983                 crow = {
57984                     start : cells[i],
57985                     end :  cells[i]
57986                 };
57987                 continue;
57988             }
57989             if (crow.start.getY() == cells[i].getY()) {
57990                 // on same row.
57991                 crow.end = cells[i];
57992                 continue;
57993             }
57994             // different row.
57995             rows.push(crow);
57996             crow = {
57997                 start: cells[i],
57998                 end : cells[i]
57999             };
58000             
58001         }
58002         
58003         rows.push(crow);
58004         rec.els = [];
58005         rec.rows = rows;
58006         rec.cells = cells;
58007         for (var i = 0; i < cells.length;i++) {
58008             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
58009             
58010         }
58011         
58012         
58013     },
58014     
58015     clearEvents: function() {
58016         
58017         if (!this.eventStore.getCount()) {
58018             return;
58019         }
58020         // reset number of rows in cells.
58021         Roo.each(this.cells.elements, function(c){
58022             c.rows = 0;
58023         });
58024         
58025         this.eventStore.each(function(e) {
58026             this.clearEvent(e);
58027         },this);
58028         
58029     },
58030     
58031     clearEvent : function(ev)
58032     {
58033         if (ev.els) {
58034             Roo.each(ev.els, function(el) {
58035                 el.un('mouseenter' ,this.onEventEnter, this);
58036                 el.un('mouseleave' ,this.onEventLeave, this);
58037                 el.remove();
58038             },this);
58039             ev.els = [];
58040         }
58041     },
58042     
58043     
58044     renderEvent : function(ev,ctr) {
58045         if (!ctr) {
58046              ctr = this.view.el.select('.fc-event-container',true).first();
58047         }
58048         
58049          
58050         this.clearEvent(ev);
58051             //code
58052        
58053         
58054         
58055         ev.els = [];
58056         var cells = ev.cells;
58057         var rows = ev.rows;
58058         this.fireEvent('eventrender', this, ev);
58059         
58060         for(var i =0; i < rows.length; i++) {
58061             
58062             cls = '';
58063             if (i == 0) {
58064                 cls += ' fc-event-start';
58065             }
58066             if ((i+1) == rows.length) {
58067                 cls += ' fc-event-end';
58068             }
58069             
58070             //Roo.log(ev.data);
58071             // how many rows should it span..
58072             var cg = this.eventTmpl.append(ctr,Roo.apply({
58073                 fccls : cls
58074                 
58075             }, ev.data) , true);
58076             
58077             
58078             cg.on('mouseenter' ,this.onEventEnter, this, ev);
58079             cg.on('mouseleave' ,this.onEventLeave, this, ev);
58080             cg.on('click', this.onEventClick, this, ev);
58081             
58082             ev.els.push(cg);
58083             
58084             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
58085             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
58086             //Roo.log(cg);
58087              
58088             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
58089             cg.setWidth(ebox.right - sbox.x -2);
58090         }
58091     },
58092     
58093     renderEvents: function()
58094     {   
58095         // first make sure there is enough space..
58096         
58097         if (!this.eventTmpl) {
58098             this.eventTmpl = new Roo.Template(
58099                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
58100                     '<div class="fc-event-inner">' +
58101                         '<span class="fc-event-time">{time}</span>' +
58102                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
58103                     '</div>' +
58104                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
58105                 '</div>'
58106             );
58107                 
58108         }
58109                
58110         
58111         
58112         this.cells.each(function(c) {
58113             //Roo.log(c.select('.fc-day-content div',true).first());
58114             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
58115         });
58116         
58117         var ctr = this.view.el.select('.fc-event-container',true).first();
58118         
58119         var cls;
58120         this.eventStore.each(function(ev){
58121             
58122             this.renderEvent(ev);
58123              
58124              
58125         }, this);
58126         this.view.layout();
58127         
58128     },
58129     
58130     onEventEnter: function (e, el,event,d) {
58131         this.fireEvent('evententer', this, el, event);
58132     },
58133     
58134     onEventLeave: function (e, el,event,d) {
58135         this.fireEvent('eventleave', this, el, event);
58136     },
58137     
58138     onEventClick: function (e, el,event,d) {
58139         this.fireEvent('eventclick', this, el, event);
58140     },
58141     
58142     onMonthChange: function () {
58143         this.store.load();
58144     },
58145     
58146     onLoad: function () {
58147         
58148         //Roo.log('calendar onload');
58149 //         
58150         if(this.eventStore.getCount() > 0){
58151             
58152            
58153             
58154             this.eventStore.each(function(d){
58155                 
58156                 
58157                 // FIXME..
58158                 var add =   d.data;
58159                 if (typeof(add.end_dt) == 'undefined')  {
58160                     Roo.log("Missing End time in calendar data: ");
58161                     Roo.log(d);
58162                     return;
58163                 }
58164                 if (typeof(add.start_dt) == 'undefined')  {
58165                     Roo.log("Missing Start time in calendar data: ");
58166                     Roo.log(d);
58167                     return;
58168                 }
58169                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
58170                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
58171                 add.id = add.id || d.id;
58172                 add.title = add.title || '??';
58173                 
58174                 this.addItem(d);
58175                 
58176              
58177             },this);
58178         }
58179         
58180         this.renderEvents();
58181     }
58182     
58183
58184 });
58185 /*
58186  grid : {
58187                 xtype: 'Grid',
58188                 xns: Roo.grid,
58189                 listeners : {
58190                     render : function ()
58191                     {
58192                         _this.grid = this;
58193                         
58194                         if (!this.view.el.hasClass('course-timesheet')) {
58195                             this.view.el.addClass('course-timesheet');
58196                         }
58197                         if (this.tsStyle) {
58198                             this.ds.load({});
58199                             return; 
58200                         }
58201                         Roo.log('width');
58202                         Roo.log(_this.grid.view.el.getWidth());
58203                         
58204                         
58205                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58206                             '.course-timesheet .x-grid-row' : {
58207                                 height: '80px'
58208                             },
58209                             '.x-grid-row td' : {
58210                                 'vertical-align' : 0
58211                             },
58212                             '.course-edit-link' : {
58213                                 'color' : 'blue',
58214                                 'text-overflow' : 'ellipsis',
58215                                 'overflow' : 'hidden',
58216                                 'white-space' : 'nowrap',
58217                                 'cursor' : 'pointer'
58218                             },
58219                             '.sub-link' : {
58220                                 'color' : 'green'
58221                             },
58222                             '.de-act-sup-link' : {
58223                                 'color' : 'purple',
58224                                 'text-decoration' : 'line-through'
58225                             },
58226                             '.de-act-link' : {
58227                                 'color' : 'red',
58228                                 'text-decoration' : 'line-through'
58229                             },
58230                             '.course-timesheet .course-highlight' : {
58231                                 'border-top-style': 'dashed !important',
58232                                 'border-bottom-bottom': 'dashed !important'
58233                             },
58234                             '.course-timesheet .course-item' : {
58235                                 'font-family'   : 'tahoma, arial, helvetica',
58236                                 'font-size'     : '11px',
58237                                 'overflow'      : 'hidden',
58238                                 'padding-left'  : '10px',
58239                                 'padding-right' : '10px',
58240                                 'padding-top' : '10px' 
58241                             }
58242                             
58243                         }, Roo.id());
58244                                 this.ds.load({});
58245                     }
58246                 },
58247                 autoWidth : true,
58248                 monitorWindowResize : false,
58249                 cellrenderer : function(v,x,r)
58250                 {
58251                     return v;
58252                 },
58253                 sm : {
58254                     xtype: 'CellSelectionModel',
58255                     xns: Roo.grid
58256                 },
58257                 dataSource : {
58258                     xtype: 'Store',
58259                     xns: Roo.data,
58260                     listeners : {
58261                         beforeload : function (_self, options)
58262                         {
58263                             options.params = options.params || {};
58264                             options.params._month = _this.monthField.getValue();
58265                             options.params.limit = 9999;
58266                             options.params['sort'] = 'when_dt';    
58267                             options.params['dir'] = 'ASC';    
58268                             this.proxy.loadResponse = this.loadResponse;
58269                             Roo.log("load?");
58270                             //this.addColumns();
58271                         },
58272                         load : function (_self, records, options)
58273                         {
58274                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58275                                 // if you click on the translation.. you can edit it...
58276                                 var el = Roo.get(this);
58277                                 var id = el.dom.getAttribute('data-id');
58278                                 var d = el.dom.getAttribute('data-date');
58279                                 var t = el.dom.getAttribute('data-time');
58280                                 //var id = this.child('span').dom.textContent;
58281                                 
58282                                 //Roo.log(this);
58283                                 Pman.Dialog.CourseCalendar.show({
58284                                     id : id,
58285                                     when_d : d,
58286                                     when_t : t,
58287                                     productitem_active : id ? 1 : 0
58288                                 }, function() {
58289                                     _this.grid.ds.load({});
58290                                 });
58291                            
58292                            });
58293                            
58294                            _this.panel.fireEvent('resize', [ '', '' ]);
58295                         }
58296                     },
58297                     loadResponse : function(o, success, response){
58298                             // this is overridden on before load..
58299                             
58300                             Roo.log("our code?");       
58301                             //Roo.log(success);
58302                             //Roo.log(response)
58303                             delete this.activeRequest;
58304                             if(!success){
58305                                 this.fireEvent("loadexception", this, o, response);
58306                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58307                                 return;
58308                             }
58309                             var result;
58310                             try {
58311                                 result = o.reader.read(response);
58312                             }catch(e){
58313                                 Roo.log("load exception?");
58314                                 this.fireEvent("loadexception", this, o, response, e);
58315                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58316                                 return;
58317                             }
58318                             Roo.log("ready...");        
58319                             // loop through result.records;
58320                             // and set this.tdate[date] = [] << array of records..
58321                             _this.tdata  = {};
58322                             Roo.each(result.records, function(r){
58323                                 //Roo.log(r.data);
58324                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58325                                     _this.tdata[r.data.when_dt.format('j')] = [];
58326                                 }
58327                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58328                             });
58329                             
58330                             //Roo.log(_this.tdata);
58331                             
58332                             result.records = [];
58333                             result.totalRecords = 6;
58334                     
58335                             // let's generate some duumy records for the rows.
58336                             //var st = _this.dateField.getValue();
58337                             
58338                             // work out monday..
58339                             //st = st.add(Date.DAY, -1 * st.format('w'));
58340                             
58341                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58342                             
58343                             var firstOfMonth = date.getFirstDayOfMonth();
58344                             var days = date.getDaysInMonth();
58345                             var d = 1;
58346                             var firstAdded = false;
58347                             for (var i = 0; i < result.totalRecords ; i++) {
58348                                 //var d= st.add(Date.DAY, i);
58349                                 var row = {};
58350                                 var added = 0;
58351                                 for(var w = 0 ; w < 7 ; w++){
58352                                     if(!firstAdded && firstOfMonth != w){
58353                                         continue;
58354                                     }
58355                                     if(d > days){
58356                                         continue;
58357                                     }
58358                                     firstAdded = true;
58359                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58360                                     row['weekday'+w] = String.format(
58361                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58362                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58363                                                     d,
58364                                                     date.format('Y-m-')+dd
58365                                                 );
58366                                     added++;
58367                                     if(typeof(_this.tdata[d]) != 'undefined'){
58368                                         Roo.each(_this.tdata[d], function(r){
58369                                             var is_sub = '';
58370                                             var deactive = '';
58371                                             var id = r.id;
58372                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58373                                             if(r.parent_id*1>0){
58374                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58375                                                 id = r.parent_id;
58376                                             }
58377                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58378                                                 deactive = 'de-act-link';
58379                                             }
58380                                             
58381                                             row['weekday'+w] += String.format(
58382                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58383                                                     id, //0
58384                                                     r.product_id_name, //1
58385                                                     r.when_dt.format('h:ia'), //2
58386                                                     is_sub, //3
58387                                                     deactive, //4
58388                                                     desc // 5
58389                                             );
58390                                         });
58391                                     }
58392                                     d++;
58393                                 }
58394                                 
58395                                 // only do this if something added..
58396                                 if(added > 0){ 
58397                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58398                                 }
58399                                 
58400                                 
58401                                 // push it twice. (second one with an hour..
58402                                 
58403                             }
58404                             //Roo.log(result);
58405                             this.fireEvent("load", this, o, o.request.arg);
58406                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58407                         },
58408                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58409                     proxy : {
58410                         xtype: 'HttpProxy',
58411                         xns: Roo.data,
58412                         method : 'GET',
58413                         url : baseURL + '/Roo/Shop_course.php'
58414                     },
58415                     reader : {
58416                         xtype: 'JsonReader',
58417                         xns: Roo.data,
58418                         id : 'id',
58419                         fields : [
58420                             {
58421                                 'name': 'id',
58422                                 'type': 'int'
58423                             },
58424                             {
58425                                 'name': 'when_dt',
58426                                 'type': 'string'
58427                             },
58428                             {
58429                                 'name': 'end_dt',
58430                                 'type': 'string'
58431                             },
58432                             {
58433                                 'name': 'parent_id',
58434                                 'type': 'int'
58435                             },
58436                             {
58437                                 'name': 'product_id',
58438                                 'type': 'int'
58439                             },
58440                             {
58441                                 'name': 'productitem_id',
58442                                 'type': 'int'
58443                             },
58444                             {
58445                                 'name': 'guid',
58446                                 'type': 'int'
58447                             }
58448                         ]
58449                     }
58450                 },
58451                 toolbar : {
58452                     xtype: 'Toolbar',
58453                     xns: Roo,
58454                     items : [
58455                         {
58456                             xtype: 'Button',
58457                             xns: Roo.Toolbar,
58458                             listeners : {
58459                                 click : function (_self, e)
58460                                 {
58461                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58462                                     sd.setMonth(sd.getMonth()-1);
58463                                     _this.monthField.setValue(sd.format('Y-m-d'));
58464                                     _this.grid.ds.load({});
58465                                 }
58466                             },
58467                             text : "Back"
58468                         },
58469                         {
58470                             xtype: 'Separator',
58471                             xns: Roo.Toolbar
58472                         },
58473                         {
58474                             xtype: 'MonthField',
58475                             xns: Roo.form,
58476                             listeners : {
58477                                 render : function (_self)
58478                                 {
58479                                     _this.monthField = _self;
58480                                    // _this.monthField.set  today
58481                                 },
58482                                 select : function (combo, date)
58483                                 {
58484                                     _this.grid.ds.load({});
58485                                 }
58486                             },
58487                             value : (function() { return new Date(); })()
58488                         },
58489                         {
58490                             xtype: 'Separator',
58491                             xns: Roo.Toolbar
58492                         },
58493                         {
58494                             xtype: 'TextItem',
58495                             xns: Roo.Toolbar,
58496                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58497                         },
58498                         {
58499                             xtype: 'Fill',
58500                             xns: Roo.Toolbar
58501                         },
58502                         {
58503                             xtype: 'Button',
58504                             xns: Roo.Toolbar,
58505                             listeners : {
58506                                 click : function (_self, e)
58507                                 {
58508                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58509                                     sd.setMonth(sd.getMonth()+1);
58510                                     _this.monthField.setValue(sd.format('Y-m-d'));
58511                                     _this.grid.ds.load({});
58512                                 }
58513                             },
58514                             text : "Next"
58515                         }
58516                     ]
58517                 },
58518                  
58519             }
58520         };
58521         
58522         *//*
58523  * Based on:
58524  * Ext JS Library 1.1.1
58525  * Copyright(c) 2006-2007, Ext JS, LLC.
58526  *
58527  * Originally Released Under LGPL - original licence link has changed is not relivant.
58528  *
58529  * Fork - LGPL
58530  * <script type="text/javascript">
58531  */
58532  
58533 /**
58534  * @class Roo.LoadMask
58535  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58536  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58537  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58538  * element's UpdateManager load indicator and will be destroyed after the initial load.
58539  * @constructor
58540  * Create a new LoadMask
58541  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58542  * @param {Object} config The config object
58543  */
58544 Roo.LoadMask = function(el, config){
58545     this.el = Roo.get(el);
58546     Roo.apply(this, config);
58547     if(this.store){
58548         this.store.on('beforeload', this.onBeforeLoad, this);
58549         this.store.on('load', this.onLoad, this);
58550         this.store.on('loadexception', this.onLoadException, this);
58551         this.removeMask = false;
58552     }else{
58553         var um = this.el.getUpdateManager();
58554         um.showLoadIndicator = false; // disable the default indicator
58555         um.on('beforeupdate', this.onBeforeLoad, this);
58556         um.on('update', this.onLoad, this);
58557         um.on('failure', this.onLoad, this);
58558         this.removeMask = true;
58559     }
58560 };
58561
58562 Roo.LoadMask.prototype = {
58563     /**
58564      * @cfg {Boolean} removeMask
58565      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58566      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58567      */
58568     /**
58569      * @cfg {String} msg
58570      * The text to display in a centered loading message box (defaults to 'Loading...')
58571      */
58572     msg : 'Loading...',
58573     /**
58574      * @cfg {String} msgCls
58575      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58576      */
58577     msgCls : 'x-mask-loading',
58578
58579     /**
58580      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58581      * @type Boolean
58582      */
58583     disabled: false,
58584
58585     /**
58586      * Disables the mask to prevent it from being displayed
58587      */
58588     disable : function(){
58589        this.disabled = true;
58590     },
58591
58592     /**
58593      * Enables the mask so that it can be displayed
58594      */
58595     enable : function(){
58596         this.disabled = false;
58597     },
58598     
58599     onLoadException : function()
58600     {
58601         Roo.log(arguments);
58602         
58603         if (typeof(arguments[3]) != 'undefined') {
58604             Roo.MessageBox.alert("Error loading",arguments[3]);
58605         } 
58606         /*
58607         try {
58608             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58609                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58610             }   
58611         } catch(e) {
58612             
58613         }
58614         */
58615     
58616         
58617         
58618         this.el.unmask(this.removeMask);
58619     },
58620     // private
58621     onLoad : function()
58622     {
58623         this.el.unmask(this.removeMask);
58624     },
58625
58626     // private
58627     onBeforeLoad : function(){
58628         if(!this.disabled){
58629             this.el.mask(this.msg, this.msgCls);
58630         }
58631     },
58632
58633     // private
58634     destroy : function(){
58635         if(this.store){
58636             this.store.un('beforeload', this.onBeforeLoad, this);
58637             this.store.un('load', this.onLoad, this);
58638             this.store.un('loadexception', this.onLoadException, this);
58639         }else{
58640             var um = this.el.getUpdateManager();
58641             um.un('beforeupdate', this.onBeforeLoad, this);
58642             um.un('update', this.onLoad, this);
58643             um.un('failure', this.onLoad, this);
58644         }
58645     }
58646 };/*
58647  * Based on:
58648  * Ext JS Library 1.1.1
58649  * Copyright(c) 2006-2007, Ext JS, LLC.
58650  *
58651  * Originally Released Under LGPL - original licence link has changed is not relivant.
58652  *
58653  * Fork - LGPL
58654  * <script type="text/javascript">
58655  */
58656
58657
58658 /**
58659  * @class Roo.XTemplate
58660  * @extends Roo.Template
58661  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58662 <pre><code>
58663 var t = new Roo.XTemplate(
58664         '&lt;select name="{name}"&gt;',
58665                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58666         '&lt;/select&gt;'
58667 );
58668  
58669 // then append, applying the master template values
58670  </code></pre>
58671  *
58672  * Supported features:
58673  *
58674  *  Tags:
58675
58676 <pre><code>
58677       {a_variable} - output encoded.
58678       {a_variable.format:("Y-m-d")} - call a method on the variable
58679       {a_variable:raw} - unencoded output
58680       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58681       {a_variable:this.method_on_template(...)} - call a method on the template object.
58682  
58683 </code></pre>
58684  *  The tpl tag:
58685 <pre><code>
58686         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58687         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58688         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58689         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58690   
58691         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58692         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58693 </code></pre>
58694  *      
58695  */
58696 Roo.XTemplate = function()
58697 {
58698     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58699     if (this.html) {
58700         this.compile();
58701     }
58702 };
58703
58704
58705 Roo.extend(Roo.XTemplate, Roo.Template, {
58706
58707     /**
58708      * The various sub templates
58709      */
58710     tpls : false,
58711     /**
58712      *
58713      * basic tag replacing syntax
58714      * WORD:WORD()
58715      *
58716      * // you can fake an object call by doing this
58717      *  x.t:(test,tesT) 
58718      * 
58719      */
58720     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58721
58722     /**
58723      * compile the template
58724      *
58725      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58726      *
58727      */
58728     compile: function()
58729     {
58730         var s = this.html;
58731      
58732         s = ['<tpl>', s, '</tpl>'].join('');
58733     
58734         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58735             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58736             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58737             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58738             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58739             m,
58740             id     = 0,
58741             tpls   = [];
58742     
58743         while(true == !!(m = s.match(re))){
58744             var forMatch   = m[0].match(nameRe),
58745                 ifMatch   = m[0].match(ifRe),
58746                 execMatch   = m[0].match(execRe),
58747                 namedMatch   = m[0].match(namedRe),
58748                 
58749                 exp  = null, 
58750                 fn   = null,
58751                 exec = null,
58752                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58753                 
58754             if (ifMatch) {
58755                 // if - puts fn into test..
58756                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58757                 if(exp){
58758                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58759                 }
58760             }
58761             
58762             if (execMatch) {
58763                 // exec - calls a function... returns empty if true is  returned.
58764                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58765                 if(exp){
58766                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58767                 }
58768             }
58769             
58770             
58771             if (name) {
58772                 // for = 
58773                 switch(name){
58774                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58775                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58776                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58777                 }
58778             }
58779             var uid = namedMatch ? namedMatch[1] : id;
58780             
58781             
58782             tpls.push({
58783                 id:     namedMatch ? namedMatch[1] : id,
58784                 target: name,
58785                 exec:   exec,
58786                 test:   fn,
58787                 body:   m[1] || ''
58788             });
58789             if (namedMatch) {
58790                 s = s.replace(m[0], '');
58791             } else { 
58792                 s = s.replace(m[0], '{xtpl'+ id + '}');
58793             }
58794             ++id;
58795         }
58796         this.tpls = [];
58797         for(var i = tpls.length-1; i >= 0; --i){
58798             this.compileTpl(tpls[i]);
58799             this.tpls[tpls[i].id] = tpls[i];
58800         }
58801         this.master = tpls[tpls.length-1];
58802         return this;
58803     },
58804     /**
58805      * same as applyTemplate, except it's done to one of the subTemplates
58806      * when using named templates, you can do:
58807      *
58808      * var str = pl.applySubTemplate('your-name', values);
58809      *
58810      * 
58811      * @param {Number} id of the template
58812      * @param {Object} values to apply to template
58813      * @param {Object} parent (normaly the instance of this object)
58814      */
58815     applySubTemplate : function(id, values, parent)
58816     {
58817         
58818         
58819         var t = this.tpls[id];
58820         
58821         
58822         try { 
58823             if(t.test && !t.test.call(this, values, parent)){
58824                 return '';
58825             }
58826         } catch(e) {
58827             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58828             Roo.log(e.toString());
58829             Roo.log(t.test);
58830             return ''
58831         }
58832         try { 
58833             
58834             if(t.exec && t.exec.call(this, values, parent)){
58835                 return '';
58836             }
58837         } catch(e) {
58838             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58839             Roo.log(e.toString());
58840             Roo.log(t.exec);
58841             return ''
58842         }
58843         try {
58844             var vs = t.target ? t.target.call(this, values, parent) : values;
58845             parent = t.target ? values : parent;
58846             if(t.target && vs instanceof Array){
58847                 var buf = [];
58848                 for(var i = 0, len = vs.length; i < len; i++){
58849                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58850                 }
58851                 return buf.join('');
58852             }
58853             return t.compiled.call(this, vs, parent);
58854         } catch (e) {
58855             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58856             Roo.log(e.toString());
58857             Roo.log(t.compiled);
58858             return '';
58859         }
58860     },
58861
58862     compileTpl : function(tpl)
58863     {
58864         var fm = Roo.util.Format;
58865         var useF = this.disableFormats !== true;
58866         var sep = Roo.isGecko ? "+" : ",";
58867         var undef = function(str) {
58868             Roo.log("Property not found :"  + str);
58869             return '';
58870         };
58871         
58872         var fn = function(m, name, format, args)
58873         {
58874             //Roo.log(arguments);
58875             args = args ? args.replace(/\\'/g,"'") : args;
58876             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58877             if (typeof(format) == 'undefined') {
58878                 format= 'htmlEncode';
58879             }
58880             if (format == 'raw' ) {
58881                 format = false;
58882             }
58883             
58884             if(name.substr(0, 4) == 'xtpl'){
58885                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58886             }
58887             
58888             // build an array of options to determine if value is undefined..
58889             
58890             // basically get 'xxxx.yyyy' then do
58891             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58892             //    (function () { Roo.log("Property not found"); return ''; })() :
58893             //    ......
58894             
58895             var udef_ar = [];
58896             var lookfor = '';
58897             Roo.each(name.split('.'), function(st) {
58898                 lookfor += (lookfor.length ? '.': '') + st;
58899                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58900             });
58901             
58902             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58903             
58904             
58905             if(format && useF){
58906                 
58907                 args = args ? ',' + args : "";
58908                  
58909                 if(format.substr(0, 5) != "this."){
58910                     format = "fm." + format + '(';
58911                 }else{
58912                     format = 'this.call("'+ format.substr(5) + '", ';
58913                     args = ", values";
58914                 }
58915                 
58916                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58917             }
58918              
58919             if (args.length) {
58920                 // called with xxyx.yuu:(test,test)
58921                 // change to ()
58922                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58923             }
58924             // raw.. - :raw modifier..
58925             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58926             
58927         };
58928         var body;
58929         // branched to use + in gecko and [].join() in others
58930         if(Roo.isGecko){
58931             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58932                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58933                     "';};};";
58934         }else{
58935             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58936             body.push(tpl.body.replace(/(\r\n|\n)/g,
58937                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58938             body.push("'].join('');};};");
58939             body = body.join('');
58940         }
58941         
58942         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58943        
58944         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58945         eval(body);
58946         
58947         return this;
58948     },
58949
58950     applyTemplate : function(values){
58951         return this.master.compiled.call(this, values, {});
58952         //var s = this.subs;
58953     },
58954
58955     apply : function(){
58956         return this.applyTemplate.apply(this, arguments);
58957     }
58958
58959  });
58960
58961 Roo.XTemplate.from = function(el){
58962     el = Roo.getDom(el);
58963     return new Roo.XTemplate(el.value || el.innerHTML);
58964 };