24f576aa81f52d5349814a76c76509225c25a286
[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                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
70                     window.addEventListener('touchstart', function __set_has_touch__ () {
71                         Roo.isTouch = true;
72                         window.removeEventListener('touchstart', __set_has_touch__);
73                     });
74                     return false; // no touch on chrome!?
75                 }
76                 document.createEvent("TouchEvent");  
77                 return true;  
78             } catch (e) {  
79                 return false;  
80             } 
81             
82         })();
83     // remove css image flicker
84         if(isIE && !isIE7){
85         try{
86             document.execCommand("BackgroundImageCache", false, true);
87         }catch(e){}
88     }
89     
90     Roo.apply(Roo, {
91         /**
92          * True if the browser is in strict mode
93          * @type Boolean
94          */
95         isStrict : isStrict,
96         /**
97          * True if the page is running over SSL
98          * @type Boolean
99          */
100         isSecure : isSecure,
101         /**
102          * True when the document is fully initialized and ready for action
103          * @type Boolean
104          */
105         isReady : false,
106         /**
107          * Turn on debugging output (currently only the factory uses this)
108          * @type Boolean
109          */
110         
111         debug: false,
112
113         /**
114          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
115          * @type Boolean
116          */
117         enableGarbageCollector : true,
118
119         /**
120          * True to automatically purge event listeners after uncaching an element (defaults to false).
121          * Note: this only happens if enableGarbageCollector is true.
122          * @type Boolean
123          */
124         enableListenerCollection:false,
125
126         /**
127          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
128          * the IE insecure content warning (defaults to javascript:false).
129          * @type String
130          */
131         SSL_SECURE_URL : "javascript:false",
132
133         /**
134          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
135          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
136          * @type String
137          */
138         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
139
140         emptyFn : function(){},
141         
142         /**
143          * Copies all the properties of config to obj if they don't already exist.
144          * @param {Object} obj The receiver of the properties
145          * @param {Object} config The source of the properties
146          * @return {Object} returns obj
147          */
148         applyIf : function(o, c){
149             if(o && c){
150                 for(var p in c){
151                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
152                 }
153             }
154             return o;
155         },
156
157         /**
158          * Applies event listeners to elements by selectors when the document is ready.
159          * The event name is specified with an @ suffix.
160 <pre><code>
161 Roo.addBehaviors({
162    // add a listener for click on all anchors in element with id foo
163    '#foo a@click' : function(e, t){
164        // do something
165    },
166
167    // add the same listener to multiple selectors (separated by comma BEFORE the @)
168    '#foo a, #bar span.some-class@mouseover' : function(){
169        // do something
170    }
171 });
172 </code></pre>
173          * @param {Object} obj The list of behaviors to apply
174          */
175         addBehaviors : function(o){
176             if(!Roo.isReady){
177                 Roo.onReady(function(){
178                     Roo.addBehaviors(o);
179                 });
180                 return;
181             }
182             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
183             for(var b in o){
184                 var parts = b.split('@');
185                 if(parts[1]){ // for Object prototype breakers
186                     var s = parts[0];
187                     if(!cache[s]){
188                         cache[s] = Roo.select(s);
189                     }
190                     cache[s].on(parts[1], o[b]);
191                 }
192             }
193             cache = null;
194         },
195
196         /**
197          * Generates unique ids. If the element already has an id, it is unchanged
198          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
199          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
200          * @return {String} The generated Id.
201          */
202         id : function(el, prefix){
203             prefix = prefix || "roo-gen";
204             el = Roo.getDom(el);
205             var id = prefix + (++idSeed);
206             return el ? (el.id ? el.id : (el.id = id)) : id;
207         },
208          
209        
210         /**
211          * Extends one class with another class and optionally overrides members with the passed literal. This class
212          * also adds the function "override()" to the class that can be used to override
213          * members on an instance.
214          * @param {Object} subclass The class inheriting the functionality
215          * @param {Object} superclass The class being extended
216          * @param {Object} overrides (optional) A literal with members
217          * @method extend
218          */
219         extend : function(){
220             // inline overrides
221             var io = function(o){
222                 for(var m in o){
223                     this[m] = o[m];
224                 }
225             };
226             return function(sb, sp, overrides){
227                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
228                     overrides = sp;
229                     sp = sb;
230                     sb = function(){sp.apply(this, arguments);};
231                 }
232                 var F = function(){}, sbp, spp = sp.prototype;
233                 F.prototype = spp;
234                 sbp = sb.prototype = new F();
235                 sbp.constructor=sb;
236                 sb.superclass=spp;
237                 
238                 if(spp.constructor == Object.prototype.constructor){
239                     spp.constructor=sp;
240                    
241                 }
242                 
243                 sb.override = function(o){
244                     Roo.override(sb, o);
245                 };
246                 sbp.override = io;
247                 Roo.override(sb, overrides);
248                 return sb;
249             };
250         }(),
251
252         /**
253          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
254          * Usage:<pre><code>
255 Roo.override(MyClass, {
256     newMethod1: function(){
257         // etc.
258     },
259     newMethod2: function(foo){
260         // etc.
261     }
262 });
263  </code></pre>
264          * @param {Object} origclass The class to override
265          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
266          * containing one or more methods.
267          * @method override
268          */
269         override : function(origclass, overrides){
270             if(overrides){
271                 var p = origclass.prototype;
272                 for(var method in overrides){
273                     p[method] = overrides[method];
274                 }
275             }
276         },
277         /**
278          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
279          * <pre><code>
280 Roo.namespace('Company', 'Company.data');
281 Company.Widget = function() { ... }
282 Company.data.CustomStore = function(config) { ... }
283 </code></pre>
284          * @param {String} namespace1
285          * @param {String} namespace2
286          * @param {String} etc
287          * @method namespace
288          */
289         namespace : function(){
290             var a=arguments, o=null, i, j, d, rt;
291             for (i=0; i<a.length; ++i) {
292                 d=a[i].split(".");
293                 rt = d[0];
294                 /** eval:var:o */
295                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
296                 for (j=1; j<d.length; ++j) {
297                     o[d[j]]=o[d[j]] || {};
298                     o=o[d[j]];
299                 }
300             }
301         },
302         /**
303          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
304          * <pre><code>
305 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
306 Roo.factory(conf, Roo.data);
307 </code></pre>
308          * @param {String} classname
309          * @param {String} namespace (optional)
310          * @method factory
311          */
312          
313         factory : function(c, ns)
314         {
315             // no xtype, no ns or c.xns - or forced off by c.xns
316             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
317                 return c;
318             }
319             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
320             if (c.constructor == ns[c.xtype]) {// already created...
321                 return c;
322             }
323             if (ns[c.xtype]) {
324                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
325                 var ret = new ns[c.xtype](c);
326                 ret.xns = false;
327                 return ret;
328             }
329             c.xns = false; // prevent recursion..
330             return c;
331         },
332          /**
333          * Logs to console if it can.
334          *
335          * @param {String|Object} string
336          * @method log
337          */
338         log : function(s)
339         {
340             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
341                 return; // alerT?
342             }
343             console.log(s);
344             
345         },
346         /**
347          * 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.
348          * @param {Object} o
349          * @return {String}
350          */
351         urlEncode : function(o){
352             if(!o){
353                 return "";
354             }
355             var buf = [];
356             for(var key in o){
357                 var ov = o[key], k = Roo.encodeURIComponent(key);
358                 var type = typeof ov;
359                 if(type == 'undefined'){
360                     buf.push(k, "=&");
361                 }else if(type != "function" && type != "object"){
362                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
363                 }else if(ov instanceof Array){
364                     if (ov.length) {
365                             for(var i = 0, len = ov.length; i < len; i++) {
366                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
367                             }
368                         } else {
369                             buf.push(k, "=&");
370                         }
371                 }
372             }
373             buf.pop();
374             return buf.join("");
375         },
376          /**
377          * Safe version of encodeURIComponent
378          * @param {String} data 
379          * @return {String} 
380          */
381         
382         encodeURIComponent : function (data)
383         {
384             try {
385                 return encodeURIComponent(data);
386             } catch(e) {} // should be an uri encode error.
387             
388             if (data == '' || data == null){
389                return '';
390             }
391             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
392             function nibble_to_hex(nibble){
393                 var chars = '0123456789ABCDEF';
394                 return chars.charAt(nibble);
395             }
396             data = data.toString();
397             var buffer = '';
398             for(var i=0; i<data.length; i++){
399                 var c = data.charCodeAt(i);
400                 var bs = new Array();
401                 if (c > 0x10000){
402                         // 4 bytes
403                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
404                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
405                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
406                     bs[3] = 0x80 | (c & 0x3F);
407                 }else if (c > 0x800){
408                          // 3 bytes
409                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
410                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
411                     bs[2] = 0x80 | (c & 0x3F);
412                 }else if (c > 0x80){
413                        // 2 bytes
414                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
415                     bs[1] = 0x80 | (c & 0x3F);
416                 }else{
417                         // 1 byte
418                     bs[0] = c;
419                 }
420                 for(var j=0; j<bs.length; j++){
421                     var b = bs[j];
422                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
423                             + nibble_to_hex(b &0x0F);
424                     buffer += '%'+hex;
425                }
426             }
427             return buffer;    
428              
429         },
430
431         /**
432          * 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]}.
433          * @param {String} string
434          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
435          * @return {Object} A literal with members
436          */
437         urlDecode : function(string, overwrite){
438             if(!string || !string.length){
439                 return {};
440             }
441             var obj = {};
442             var pairs = string.split('&');
443             var pair, name, value;
444             for(var i = 0, len = pairs.length; i < len; i++){
445                 pair = pairs[i].split('=');
446                 name = decodeURIComponent(pair[0]);
447                 value = decodeURIComponent(pair[1]);
448                 if(overwrite !== true){
449                     if(typeof obj[name] == "undefined"){
450                         obj[name] = value;
451                     }else if(typeof obj[name] == "string"){
452                         obj[name] = [obj[name]];
453                         obj[name].push(value);
454                     }else{
455                         obj[name].push(value);
456                     }
457                 }else{
458                     obj[name] = value;
459                 }
460             }
461             return obj;
462         },
463
464         /**
465          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
466          * passed array is not really an array, your function is called once with it.
467          * The supplied function is called with (Object item, Number index, Array allItems).
468          * @param {Array/NodeList/Mixed} array
469          * @param {Function} fn
470          * @param {Object} scope
471          */
472         each : function(array, fn, scope){
473             if(typeof array.length == "undefined" || typeof array == "string"){
474                 array = [array];
475             }
476             for(var i = 0, len = array.length; i < len; i++){
477                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
478             }
479         },
480
481         // deprecated
482         combine : function(){
483             var as = arguments, l = as.length, r = [];
484             for(var i = 0; i < l; i++){
485                 var a = as[i];
486                 if(a instanceof Array){
487                     r = r.concat(a);
488                 }else if(a.length !== undefined && !a.substr){
489                     r = r.concat(Array.prototype.slice.call(a, 0));
490                 }else{
491                     r.push(a);
492                 }
493             }
494             return r;
495         },
496
497         /**
498          * Escapes the passed string for use in a regular expression
499          * @param {String} str
500          * @return {String}
501          */
502         escapeRe : function(s) {
503             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
504         },
505
506         // internal
507         callback : function(cb, scope, args, delay){
508             if(typeof cb == "function"){
509                 if(delay){
510                     cb.defer(delay, scope, args || []);
511                 }else{
512                     cb.apply(scope, args || []);
513                 }
514             }
515         },
516
517         /**
518          * Return the dom node for the passed string (id), dom node, or Roo.Element
519          * @param {String/HTMLElement/Roo.Element} el
520          * @return HTMLElement
521          */
522         getDom : function(el){
523             if(!el){
524                 return null;
525             }
526             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
527         },
528
529         /**
530         * Shorthand for {@link Roo.ComponentMgr#get}
531         * @param {String} id
532         * @return Roo.Component
533         */
534         getCmp : function(id){
535             return Roo.ComponentMgr.get(id);
536         },
537          
538         num : function(v, defaultValue){
539             if(typeof v != 'number'){
540                 return defaultValue;
541             }
542             return v;
543         },
544
545         destroy : function(){
546             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
547                 var as = a[i];
548                 if(as){
549                     if(as.dom){
550                         as.removeAllListeners();
551                         as.remove();
552                         continue;
553                     }
554                     if(typeof as.purgeListeners == 'function'){
555                         as.purgeListeners();
556                     }
557                     if(typeof as.destroy == 'function'){
558                         as.destroy();
559                     }
560                 }
561             }
562         },
563
564         // inpired by a similar function in mootools library
565         /**
566          * Returns the type of object that is passed in. If the object passed in is null or undefined it
567          * return false otherwise it returns one of the following values:<ul>
568          * <li><b>string</b>: If the object passed is a string</li>
569          * <li><b>number</b>: If the object passed is a number</li>
570          * <li><b>boolean</b>: If the object passed is a boolean value</li>
571          * <li><b>function</b>: If the object passed is a function reference</li>
572          * <li><b>object</b>: If the object passed is an object</li>
573          * <li><b>array</b>: If the object passed is an array</li>
574          * <li><b>regexp</b>: If the object passed is a regular expression</li>
575          * <li><b>element</b>: If the object passed is a DOM Element</li>
576          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
577          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
578          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
579          * @param {Mixed} object
580          * @return {String}
581          */
582         type : function(o){
583             if(o === undefined || o === null){
584                 return false;
585             }
586             if(o.htmlElement){
587                 return 'element';
588             }
589             var t = typeof o;
590             if(t == 'object' && o.nodeName) {
591                 switch(o.nodeType) {
592                     case 1: return 'element';
593                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
594                 }
595             }
596             if(t == 'object' || t == 'function') {
597                 switch(o.constructor) {
598                     case Array: return 'array';
599                     case RegExp: return 'regexp';
600                 }
601                 if(typeof o.length == 'number' && typeof o.item == 'function') {
602                     return 'nodelist';
603                 }
604             }
605             return t;
606         },
607
608         /**
609          * Returns true if the passed value is null, undefined or an empty string (optional).
610          * @param {Mixed} value The value to test
611          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
612          * @return {Boolean}
613          */
614         isEmpty : function(v, allowBlank){
615             return v === null || v === undefined || (!allowBlank ? v === '' : false);
616         },
617         
618         /** @type Boolean */
619         isOpera : isOpera,
620         /** @type Boolean */
621         isSafari : isSafari,
622         /** @type Boolean */
623         isFirefox : isFirefox,
624         /** @type Boolean */
625         isIE : isIE,
626         /** @type Boolean */
627         isIE7 : isIE7,
628         /** @type Boolean */
629         isIE11 : isIE11,
630         /** @type Boolean */
631         isGecko : isGecko,
632         /** @type Boolean */
633         isBorderBox : isBorderBox,
634         /** @type Boolean */
635         isWindows : isWindows,
636         /** @type Boolean */
637         isLinux : isLinux,
638         /** @type Boolean */
639         isMac : isMac,
640         /** @type Boolean */
641         isIOS : isIOS,
642         /** @type Boolean */
643         isTouch : isTouch,
644
645         /**
646          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
647          * you may want to set this to true.
648          * @type Boolean
649          */
650         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
651         
652         
653                 
654         /**
655          * Selects a single element as a Roo Element
656          * This is about as close as you can get to jQuery's $('do crazy stuff')
657          * @param {String} selector The selector/xpath query
658          * @param {Node} root (optional) The start of the query (defaults to document).
659          * @return {Roo.Element}
660          */
661         selectNode : function(selector, root) 
662         {
663             var node = Roo.DomQuery.selectNode(selector,root);
664             return node ? Roo.get(node) : new Roo.Element(false);
665         }
666         
667     });
668
669
670 })();
671
672 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
673                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
674                 "Roo.app", "Roo.ux",
675                 "Roo.bootstrap",
676                 "Roo.bootstrap.dash");
677 /*
678  * Based on:
679  * Ext JS Library 1.1.1
680  * Copyright(c) 2006-2007, Ext JS, LLC.
681  *
682  * Originally Released Under LGPL - original licence link has changed is not relivant.
683  *
684  * Fork - LGPL
685  * <script type="text/javascript">
686  */
687
688 (function() {    
689     // wrappedn so fnCleanup is not in global scope...
690     if(Roo.isIE) {
691         function fnCleanUp() {
692             var p = Function.prototype;
693             delete p.createSequence;
694             delete p.defer;
695             delete p.createDelegate;
696             delete p.createCallback;
697             delete p.createInterceptor;
698
699             window.detachEvent("onunload", fnCleanUp);
700         }
701         window.attachEvent("onunload", fnCleanUp);
702     }
703 })();
704
705
706 /**
707  * @class Function
708  * These functions are available on every Function object (any JavaScript function).
709  */
710 Roo.apply(Function.prototype, {
711      /**
712      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
713      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
714      * Will create a function that is bound to those 2 args.
715      * @return {Function} The new function
716     */
717     createCallback : function(/*args...*/){
718         // make args available, in function below
719         var args = arguments;
720         var method = this;
721         return function() {
722             return method.apply(window, args);
723         };
724     },
725
726     /**
727      * Creates a delegate (callback) that sets the scope to obj.
728      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
729      * Will create a function that is automatically scoped to this.
730      * @param {Object} obj (optional) The object for which the scope is set
731      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
732      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
733      *                                             if a number the args are inserted at the specified position
734      * @return {Function} The new function
735      */
736     createDelegate : function(obj, args, appendArgs){
737         var method = this;
738         return function() {
739             var callArgs = args || arguments;
740             if(appendArgs === true){
741                 callArgs = Array.prototype.slice.call(arguments, 0);
742                 callArgs = callArgs.concat(args);
743             }else if(typeof appendArgs == "number"){
744                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
745                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
746                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
747             }
748             return method.apply(obj || window, callArgs);
749         };
750     },
751
752     /**
753      * Calls this function after the number of millseconds specified.
754      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
755      * @param {Object} obj (optional) The object for which the scope is set
756      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
757      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
758      *                                             if a number the args are inserted at the specified position
759      * @return {Number} The timeout id that can be used with clearTimeout
760      */
761     defer : function(millis, obj, args, appendArgs){
762         var fn = this.createDelegate(obj, args, appendArgs);
763         if(millis){
764             return setTimeout(fn, millis);
765         }
766         fn();
767         return 0;
768     },
769     /**
770      * Create a combined function call sequence of the original function + the passed function.
771      * The resulting function returns the results of the original function.
772      * The passed fcn is called with the parameters of the original function
773      * @param {Function} fcn The function to sequence
774      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
775      * @return {Function} The new function
776      */
777     createSequence : function(fcn, scope){
778         if(typeof fcn != "function"){
779             return this;
780         }
781         var method = this;
782         return function() {
783             var retval = method.apply(this || window, arguments);
784             fcn.apply(scope || this || window, arguments);
785             return retval;
786         };
787     },
788
789     /**
790      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
791      * The resulting function returns the results of the original function.
792      * The passed fcn is called with the parameters of the original function.
793      * @addon
794      * @param {Function} fcn The function to call before the original
795      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
796      * @return {Function} The new function
797      */
798     createInterceptor : function(fcn, scope){
799         if(typeof fcn != "function"){
800             return this;
801         }
802         var method = this;
803         return function() {
804             fcn.target = this;
805             fcn.method = method;
806             if(fcn.apply(scope || this || window, arguments) === false){
807                 return;
808             }
809             return method.apply(this || window, arguments);
810         };
811     }
812 });
813 /*
814  * Based on:
815  * Ext JS Library 1.1.1
816  * Copyright(c) 2006-2007, Ext JS, LLC.
817  *
818  * Originally Released Under LGPL - original licence link has changed is not relivant.
819  *
820  * Fork - LGPL
821  * <script type="text/javascript">
822  */
823
824 Roo.applyIf(String, {
825     
826     /** @scope String */
827     
828     /**
829      * Escapes the passed string for ' and \
830      * @param {String} string The string to escape
831      * @return {String} The escaped string
832      * @static
833      */
834     escape : function(string) {
835         return string.replace(/('|\\)/g, "\\$1");
836     },
837
838     /**
839      * Pads the left side of a string with a specified character.  This is especially useful
840      * for normalizing number and date strings.  Example usage:
841      * <pre><code>
842 var s = String.leftPad('123', 5, '0');
843 // s now contains the string: '00123'
844 </code></pre>
845      * @param {String} string The original string
846      * @param {Number} size The total length of the output string
847      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
848      * @return {String} The padded string
849      * @static
850      */
851     leftPad : function (val, size, ch) {
852         var result = new String(val);
853         if(ch === null || ch === undefined || ch === '') {
854             ch = " ";
855         }
856         while (result.length < size) {
857             result = ch + result;
858         }
859         return result;
860     },
861
862     /**
863      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
864      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
865      * <pre><code>
866 var cls = 'my-class', text = 'Some text';
867 var s = String.format('<div class="{0}">{1}</div>', cls, text);
868 // s now contains the string: '<div class="my-class">Some text</div>'
869 </code></pre>
870      * @param {String} string The tokenized string to be formatted
871      * @param {String} value1 The value to replace token {0}
872      * @param {String} value2 Etc...
873      * @return {String} The formatted string
874      * @static
875      */
876     format : function(format){
877         var args = Array.prototype.slice.call(arguments, 1);
878         return format.replace(/\{(\d+)\}/g, function(m, i){
879             return Roo.util.Format.htmlEncode(args[i]);
880         });
881     }
882 });
883
884 /**
885  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
886  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
887  * they are already different, the first value passed in is returned.  Note that this method returns the new value
888  * but does not change the current string.
889  * <pre><code>
890 // alternate sort directions
891 sort = sort.toggle('ASC', 'DESC');
892
893 // instead of conditional logic:
894 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
895 </code></pre>
896  * @param {String} value The value to compare to the current string
897  * @param {String} other The new value to use if the string already equals the first value passed in
898  * @return {String} The new value
899  */
900  
901 String.prototype.toggle = function(value, other){
902     return this == value ? other : value;
903 };/*
904  * Based on:
905  * Ext JS Library 1.1.1
906  * Copyright(c) 2006-2007, Ext JS, LLC.
907  *
908  * Originally Released Under LGPL - original licence link has changed is not relivant.
909  *
910  * Fork - LGPL
911  * <script type="text/javascript">
912  */
913
914  /**
915  * @class Number
916  */
917 Roo.applyIf(Number.prototype, {
918     /**
919      * Checks whether or not the current number is within a desired range.  If the number is already within the
920      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
921      * exceeded.  Note that this method returns the constrained value but does not change the current number.
922      * @param {Number} min The minimum number in the range
923      * @param {Number} max The maximum number in the range
924      * @return {Number} The constrained value if outside the range, otherwise the current value
925      */
926     constrain : function(min, max){
927         return Math.min(Math.max(this, min), max);
928     }
929 });/*
930  * Based on:
931  * Ext JS Library 1.1.1
932  * Copyright(c) 2006-2007, Ext JS, LLC.
933  *
934  * Originally Released Under LGPL - original licence link has changed is not relivant.
935  *
936  * Fork - LGPL
937  * <script type="text/javascript">
938  */
939  /**
940  * @class Array
941  */
942 Roo.applyIf(Array.prototype, {
943     /**
944      * 
945      * Checks whether or not the specified object exists in the array.
946      * @param {Object} o The object to check for
947      * @return {Number} The index of o in the array (or -1 if it is not found)
948      */
949     indexOf : function(o){
950        for (var i = 0, len = this.length; i < len; i++){
951               if(this[i] == o) { return i; }
952        }
953            return -1;
954     },
955
956     /**
957      * Removes the specified object from the array.  If the object is not found nothing happens.
958      * @param {Object} o The object to remove
959      */
960     remove : function(o){
961        var index = this.indexOf(o);
962        if(index != -1){
963            this.splice(index, 1);
964        }
965     },
966     /**
967      * Map (JS 1.6 compatibility)
968      * @param {Function} function  to call
969      */
970     map : function(fun )
971     {
972         var len = this.length >>> 0;
973         if (typeof fun != "function") {
974             throw new TypeError();
975         }
976         var res = new Array(len);
977         var thisp = arguments[1];
978         for (var i = 0; i < len; i++)
979         {
980             if (i in this) {
981                 res[i] = fun.call(thisp, this[i], i, this);
982             }
983         }
984
985         return res;
986     }
987     
988 });
989
990
991  
992 /*
993  * Based on:
994  * Ext JS Library 1.1.1
995  * Copyright(c) 2006-2007, Ext JS, LLC.
996  *
997  * Originally Released Under LGPL - original licence link has changed is not relivant.
998  *
999  * Fork - LGPL
1000  * <script type="text/javascript">
1001  */
1002
1003 /**
1004  * @class Date
1005  *
1006  * The date parsing and format syntax is a subset of
1007  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1008  * supported will provide results equivalent to their PHP versions.
1009  *
1010  * Following is the list of all currently supported formats:
1011  *<pre>
1012 Sample date:
1013 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1014
1015 Format  Output      Description
1016 ------  ----------  --------------------------------------------------------------
1017   d      10         Day of the month, 2 digits with leading zeros
1018   D      Wed        A textual representation of a day, three letters
1019   j      10         Day of the month without leading zeros
1020   l      Wednesday  A full textual representation of the day of the week
1021   S      th         English ordinal day of month suffix, 2 chars (use with j)
1022   w      3          Numeric representation of the day of the week
1023   z      9          The julian date, or day of the year (0-365)
1024   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1025   F      January    A full textual representation of the month
1026   m      01         Numeric representation of a month, with leading zeros
1027   M      Jan        Month name abbreviation, three letters
1028   n      1          Numeric representation of a month, without leading zeros
1029   t      31         Number of days in the given month
1030   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1031   Y      2007       A full numeric representation of a year, 4 digits
1032   y      07         A two digit representation of a year
1033   a      pm         Lowercase Ante meridiem and Post meridiem
1034   A      PM         Uppercase Ante meridiem and Post meridiem
1035   g      3          12-hour format of an hour without leading zeros
1036   G      15         24-hour format of an hour without leading zeros
1037   h      03         12-hour format of an hour with leading zeros
1038   H      15         24-hour format of an hour with leading zeros
1039   i      05         Minutes with leading zeros
1040   s      01         Seconds, with leading zeros
1041   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1042   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1043   T      CST        Timezone setting of the machine running the code
1044   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1045 </pre>
1046  *
1047  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1048  * <pre><code>
1049 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1050 document.write(dt.format('Y-m-d'));                         //2007-01-10
1051 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1052 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
1053  </code></pre>
1054  *
1055  * Here are some standard date/time patterns that you might find helpful.  They
1056  * are not part of the source of Date.js, but to use them you can simply copy this
1057  * block of code into any script that is included after Date.js and they will also become
1058  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1059  * <pre><code>
1060 Date.patterns = {
1061     ISO8601Long:"Y-m-d H:i:s",
1062     ISO8601Short:"Y-m-d",
1063     ShortDate: "n/j/Y",
1064     LongDate: "l, F d, Y",
1065     FullDateTime: "l, F d, Y g:i:s A",
1066     MonthDay: "F d",
1067     ShortTime: "g:i A",
1068     LongTime: "g:i:s A",
1069     SortableDateTime: "Y-m-d\\TH:i:s",
1070     UniversalSortableDateTime: "Y-m-d H:i:sO",
1071     YearMonth: "F, Y"
1072 };
1073 </code></pre>
1074  *
1075  * Example usage:
1076  * <pre><code>
1077 var dt = new Date();
1078 document.write(dt.format(Date.patterns.ShortDate));
1079  </code></pre>
1080  */
1081
1082 /*
1083  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1084  * They generate precompiled functions from date formats instead of parsing and
1085  * processing the pattern every time you format a date.  These functions are available
1086  * on every Date object (any javascript function).
1087  *
1088  * The original article and download are here:
1089  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1090  *
1091  */
1092  
1093  
1094  // was in core
1095 /**
1096  Returns the number of milliseconds between this date and date
1097  @param {Date} date (optional) Defaults to now
1098  @return {Number} The diff in milliseconds
1099  @member Date getElapsed
1100  */
1101 Date.prototype.getElapsed = function(date) {
1102         return Math.abs((date || new Date()).getTime()-this.getTime());
1103 };
1104 // was in date file..
1105
1106
1107 // private
1108 Date.parseFunctions = {count:0};
1109 // private
1110 Date.parseRegexes = [];
1111 // private
1112 Date.formatFunctions = {count:0};
1113
1114 // private
1115 Date.prototype.dateFormat = function(format) {
1116     if (Date.formatFunctions[format] == null) {
1117         Date.createNewFormat(format);
1118     }
1119     var func = Date.formatFunctions[format];
1120     return this[func]();
1121 };
1122
1123
1124 /**
1125  * Formats a date given the supplied format string
1126  * @param {String} format The format string
1127  * @return {String} The formatted date
1128  * @method
1129  */
1130 Date.prototype.format = Date.prototype.dateFormat;
1131
1132 // private
1133 Date.createNewFormat = function(format) {
1134     var funcName = "format" + Date.formatFunctions.count++;
1135     Date.formatFunctions[format] = funcName;
1136     var code = "Date.prototype." + funcName + " = function(){return ";
1137     var special = false;
1138     var ch = '';
1139     for (var i = 0; i < format.length; ++i) {
1140         ch = format.charAt(i);
1141         if (!special && ch == "\\") {
1142             special = true;
1143         }
1144         else if (special) {
1145             special = false;
1146             code += "'" + String.escape(ch) + "' + ";
1147         }
1148         else {
1149             code += Date.getFormatCode(ch);
1150         }
1151     }
1152     /** eval:var:zzzzzzzzzzzzz */
1153     eval(code.substring(0, code.length - 3) + ";}");
1154 };
1155
1156 // private
1157 Date.getFormatCode = function(character) {
1158     switch (character) {
1159     case "d":
1160         return "String.leftPad(this.getDate(), 2, '0') + ";
1161     case "D":
1162         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1163     case "j":
1164         return "this.getDate() + ";
1165     case "l":
1166         return "Date.dayNames[this.getDay()] + ";
1167     case "S":
1168         return "this.getSuffix() + ";
1169     case "w":
1170         return "this.getDay() + ";
1171     case "z":
1172         return "this.getDayOfYear() + ";
1173     case "W":
1174         return "this.getWeekOfYear() + ";
1175     case "F":
1176         return "Date.monthNames[this.getMonth()] + ";
1177     case "m":
1178         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1179     case "M":
1180         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1181     case "n":
1182         return "(this.getMonth() + 1) + ";
1183     case "t":
1184         return "this.getDaysInMonth() + ";
1185     case "L":
1186         return "(this.isLeapYear() ? 1 : 0) + ";
1187     case "Y":
1188         return "this.getFullYear() + ";
1189     case "y":
1190         return "('' + this.getFullYear()).substring(2, 4) + ";
1191     case "a":
1192         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1193     case "A":
1194         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1195     case "g":
1196         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1197     case "G":
1198         return "this.getHours() + ";
1199     case "h":
1200         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1201     case "H":
1202         return "String.leftPad(this.getHours(), 2, '0') + ";
1203     case "i":
1204         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1205     case "s":
1206         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1207     case "O":
1208         return "this.getGMTOffset() + ";
1209     case "P":
1210         return "this.getGMTColonOffset() + ";
1211     case "T":
1212         return "this.getTimezone() + ";
1213     case "Z":
1214         return "(this.getTimezoneOffset() * -60) + ";
1215     default:
1216         return "'" + String.escape(character) + "' + ";
1217     }
1218 };
1219
1220 /**
1221  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1222  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1223  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1224  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1225  * string or the parse operation will fail.
1226  * Example Usage:
1227 <pre><code>
1228 //dt = Fri May 25 2007 (current date)
1229 var dt = new Date();
1230
1231 //dt = Thu May 25 2006 (today's month/day in 2006)
1232 dt = Date.parseDate("2006", "Y");
1233
1234 //dt = Sun Jan 15 2006 (all date parts specified)
1235 dt = Date.parseDate("2006-1-15", "Y-m-d");
1236
1237 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1238 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1239 </code></pre>
1240  * @param {String} input The unparsed date as a string
1241  * @param {String} format The format the date is in
1242  * @return {Date} The parsed date
1243  * @static
1244  */
1245 Date.parseDate = function(input, format) {
1246     if (Date.parseFunctions[format] == null) {
1247         Date.createParser(format);
1248     }
1249     var func = Date.parseFunctions[format];
1250     return Date[func](input);
1251 };
1252 /**
1253  * @private
1254  */
1255
1256 Date.createParser = function(format) {
1257     var funcName = "parse" + Date.parseFunctions.count++;
1258     var regexNum = Date.parseRegexes.length;
1259     var currentGroup = 1;
1260     Date.parseFunctions[format] = funcName;
1261
1262     var code = "Date." + funcName + " = function(input){\n"
1263         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1264         + "var d = new Date();\n"
1265         + "y = d.getFullYear();\n"
1266         + "m = d.getMonth();\n"
1267         + "d = d.getDate();\n"
1268         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1269         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1270         + "if (results && results.length > 0) {";
1271     var regex = "";
1272
1273     var special = false;
1274     var ch = '';
1275     for (var i = 0; i < format.length; ++i) {
1276         ch = format.charAt(i);
1277         if (!special && ch == "\\") {
1278             special = true;
1279         }
1280         else if (special) {
1281             special = false;
1282             regex += String.escape(ch);
1283         }
1284         else {
1285             var obj = Date.formatCodeToRegex(ch, currentGroup);
1286             currentGroup += obj.g;
1287             regex += obj.s;
1288             if (obj.g && obj.c) {
1289                 code += obj.c;
1290             }
1291         }
1292     }
1293
1294     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1295         + "{v = new Date(y, m, d, h, i, s);}\n"
1296         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1297         + "{v = new Date(y, m, d, h, i);}\n"
1298         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1299         + "{v = new Date(y, m, d, h);}\n"
1300         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1301         + "{v = new Date(y, m, d);}\n"
1302         + "else if (y >= 0 && m >= 0)\n"
1303         + "{v = new Date(y, m);}\n"
1304         + "else if (y >= 0)\n"
1305         + "{v = new Date(y);}\n"
1306         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1307         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1308         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1309         + ";}";
1310
1311     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1312     /** eval:var:zzzzzzzzzzzzz */
1313     eval(code);
1314 };
1315
1316 // private
1317 Date.formatCodeToRegex = function(character, currentGroup) {
1318     switch (character) {
1319     case "D":
1320         return {g:0,
1321         c:null,
1322         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1323     case "j":
1324         return {g:1,
1325             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1326             s:"(\\d{1,2})"}; // day of month without leading zeroes
1327     case "d":
1328         return {g:1,
1329             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1330             s:"(\\d{2})"}; // day of month with leading zeroes
1331     case "l":
1332         return {g:0,
1333             c:null,
1334             s:"(?:" + Date.dayNames.join("|") + ")"};
1335     case "S":
1336         return {g:0,
1337             c:null,
1338             s:"(?:st|nd|rd|th)"};
1339     case "w":
1340         return {g:0,
1341             c:null,
1342             s:"\\d"};
1343     case "z":
1344         return {g:0,
1345             c:null,
1346             s:"(?:\\d{1,3})"};
1347     case "W":
1348         return {g:0,
1349             c:null,
1350             s:"(?:\\d{2})"};
1351     case "F":
1352         return {g:1,
1353             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1354             s:"(" + Date.monthNames.join("|") + ")"};
1355     case "M":
1356         return {g:1,
1357             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1358             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1359     case "n":
1360         return {g:1,
1361             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1362             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1363     case "m":
1364         return {g:1,
1365             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1366             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1367     case "t":
1368         return {g:0,
1369             c:null,
1370             s:"\\d{1,2}"};
1371     case "L":
1372         return {g:0,
1373             c:null,
1374             s:"(?:1|0)"};
1375     case "Y":
1376         return {g:1,
1377             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1378             s:"(\\d{4})"};
1379     case "y":
1380         return {g:1,
1381             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1382                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1383             s:"(\\d{1,2})"};
1384     case "a":
1385         return {g:1,
1386             c:"if (results[" + currentGroup + "] == 'am') {\n"
1387                 + "if (h == 12) { h = 0; }\n"
1388                 + "} else { if (h < 12) { h += 12; }}",
1389             s:"(am|pm)"};
1390     case "A":
1391         return {g:1,
1392             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1393                 + "if (h == 12) { h = 0; }\n"
1394                 + "} else { if (h < 12) { h += 12; }}",
1395             s:"(AM|PM)"};
1396     case "g":
1397     case "G":
1398         return {g:1,
1399             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1400             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1401     case "h":
1402     case "H":
1403         return {g:1,
1404             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1405             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1406     case "i":
1407         return {g:1,
1408             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1409             s:"(\\d{2})"};
1410     case "s":
1411         return {g:1,
1412             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1413             s:"(\\d{2})"};
1414     case "O":
1415         return {g:1,
1416             c:[
1417                 "o = results[", currentGroup, "];\n",
1418                 "var sn = o.substring(0,1);\n", // get + / - sign
1419                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1420                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1421                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1422                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1423             ].join(""),
1424             s:"([+\-]\\d{2,4})"};
1425     
1426     
1427     case "P":
1428         return {g:1,
1429                 c:[
1430                    "o = results[", currentGroup, "];\n",
1431                    "var sn = o.substring(0,1);\n",
1432                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1433                    "var mn = o.substring(4,6) % 60;\n",
1434                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1435                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1436             ].join(""),
1437             s:"([+\-]\\d{4})"};
1438     case "T":
1439         return {g:0,
1440             c:null,
1441             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1442     case "Z":
1443         return {g:1,
1444             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1445                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1446             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1447     default:
1448         return {g:0,
1449             c:null,
1450             s:String.escape(character)};
1451     }
1452 };
1453
1454 /**
1455  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1456  * @return {String} The abbreviated timezone name (e.g. 'CST')
1457  */
1458 Date.prototype.getTimezone = function() {
1459     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1460 };
1461
1462 /**
1463  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1464  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1465  */
1466 Date.prototype.getGMTOffset = function() {
1467     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1468         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1469         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1470 };
1471
1472 /**
1473  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1474  * @return {String} 2-characters representing hours and 2-characters representing minutes
1475  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1476  */
1477 Date.prototype.getGMTColonOffset = function() {
1478         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1479                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1480                 + ":"
1481                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1482 }
1483
1484 /**
1485  * Get the numeric day number of the year, adjusted for leap year.
1486  * @return {Number} 0 through 364 (365 in leap years)
1487  */
1488 Date.prototype.getDayOfYear = function() {
1489     var num = 0;
1490     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1491     for (var i = 0; i < this.getMonth(); ++i) {
1492         num += Date.daysInMonth[i];
1493     }
1494     return num + this.getDate() - 1;
1495 };
1496
1497 /**
1498  * Get the string representation of the numeric week number of the year
1499  * (equivalent to the format specifier 'W').
1500  * @return {String} '00' through '52'
1501  */
1502 Date.prototype.getWeekOfYear = function() {
1503     // Skip to Thursday of this week
1504     var now = this.getDayOfYear() + (4 - this.getDay());
1505     // Find the first Thursday of the year
1506     var jan1 = new Date(this.getFullYear(), 0, 1);
1507     var then = (7 - jan1.getDay() + 4);
1508     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1509 };
1510
1511 /**
1512  * Whether or not the current date is in a leap year.
1513  * @return {Boolean} True if the current date is in a leap year, else false
1514  */
1515 Date.prototype.isLeapYear = function() {
1516     var year = this.getFullYear();
1517     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1518 };
1519
1520 /**
1521  * Get the first day of the current month, adjusted for leap year.  The returned value
1522  * is the numeric day index within the week (0-6) which can be used in conjunction with
1523  * the {@link #monthNames} array to retrieve the textual day name.
1524  * Example:
1525  *<pre><code>
1526 var dt = new Date('1/10/2007');
1527 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1528 </code></pre>
1529  * @return {Number} The day number (0-6)
1530  */
1531 Date.prototype.getFirstDayOfMonth = function() {
1532     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1533     return (day < 0) ? (day + 7) : day;
1534 };
1535
1536 /**
1537  * Get the last day of the current month, adjusted for leap year.  The returned value
1538  * is the numeric day index within the week (0-6) which can be used in conjunction with
1539  * the {@link #monthNames} array to retrieve the textual day name.
1540  * Example:
1541  *<pre><code>
1542 var dt = new Date('1/10/2007');
1543 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1544 </code></pre>
1545  * @return {Number} The day number (0-6)
1546  */
1547 Date.prototype.getLastDayOfMonth = function() {
1548     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1549     return (day < 0) ? (day + 7) : day;
1550 };
1551
1552
1553 /**
1554  * Get the first date of this date's month
1555  * @return {Date}
1556  */
1557 Date.prototype.getFirstDateOfMonth = function() {
1558     return new Date(this.getFullYear(), this.getMonth(), 1);
1559 };
1560
1561 /**
1562  * Get the last date of this date's month
1563  * @return {Date}
1564  */
1565 Date.prototype.getLastDateOfMonth = function() {
1566     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1567 };
1568 /**
1569  * Get the number of days in the current month, adjusted for leap year.
1570  * @return {Number} The number of days in the month
1571  */
1572 Date.prototype.getDaysInMonth = function() {
1573     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1574     return Date.daysInMonth[this.getMonth()];
1575 };
1576
1577 /**
1578  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1579  * @return {String} 'st, 'nd', 'rd' or 'th'
1580  */
1581 Date.prototype.getSuffix = function() {
1582     switch (this.getDate()) {
1583         case 1:
1584         case 21:
1585         case 31:
1586             return "st";
1587         case 2:
1588         case 22:
1589             return "nd";
1590         case 3:
1591         case 23:
1592             return "rd";
1593         default:
1594             return "th";
1595     }
1596 };
1597
1598 // private
1599 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1600
1601 /**
1602  * An array of textual month names.
1603  * Override these values for international dates, for example...
1604  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1605  * @type Array
1606  * @static
1607  */
1608 Date.monthNames =
1609    ["January",
1610     "February",
1611     "March",
1612     "April",
1613     "May",
1614     "June",
1615     "July",
1616     "August",
1617     "September",
1618     "October",
1619     "November",
1620     "December"];
1621
1622 /**
1623  * An array of textual day names.
1624  * Override these values for international dates, for example...
1625  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1626  * @type Array
1627  * @static
1628  */
1629 Date.dayNames =
1630    ["Sunday",
1631     "Monday",
1632     "Tuesday",
1633     "Wednesday",
1634     "Thursday",
1635     "Friday",
1636     "Saturday"];
1637
1638 // private
1639 Date.y2kYear = 50;
1640 // private
1641 Date.monthNumbers = {
1642     Jan:0,
1643     Feb:1,
1644     Mar:2,
1645     Apr:3,
1646     May:4,
1647     Jun:5,
1648     Jul:6,
1649     Aug:7,
1650     Sep:8,
1651     Oct:9,
1652     Nov:10,
1653     Dec:11};
1654
1655 /**
1656  * Creates and returns a new Date instance with the exact same date value as the called instance.
1657  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1658  * variable will also be changed.  When the intention is to create a new variable that will not
1659  * modify the original instance, you should create a clone.
1660  *
1661  * Example of correctly cloning a date:
1662  * <pre><code>
1663 //wrong way:
1664 var orig = new Date('10/1/2006');
1665 var copy = orig;
1666 copy.setDate(5);
1667 document.write(orig);  //returns 'Thu Oct 05 2006'!
1668
1669 //correct way:
1670 var orig = new Date('10/1/2006');
1671 var copy = orig.clone();
1672 copy.setDate(5);
1673 document.write(orig);  //returns 'Thu Oct 01 2006'
1674 </code></pre>
1675  * @return {Date} The new Date instance
1676  */
1677 Date.prototype.clone = function() {
1678         return new Date(this.getTime());
1679 };
1680
1681 /**
1682  * Clears any time information from this date
1683  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1684  @return {Date} this or the clone
1685  */
1686 Date.prototype.clearTime = function(clone){
1687     if(clone){
1688         return this.clone().clearTime();
1689     }
1690     this.setHours(0);
1691     this.setMinutes(0);
1692     this.setSeconds(0);
1693     this.setMilliseconds(0);
1694     return this;
1695 };
1696
1697 // private
1698 // safari setMonth is broken -- check that this is only donw once...
1699 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1700     Date.brokenSetMonth = Date.prototype.setMonth;
1701         Date.prototype.setMonth = function(num){
1702                 if(num <= -1){
1703                         var n = Math.ceil(-num);
1704                         var back_year = Math.ceil(n/12);
1705                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1706                         this.setFullYear(this.getFullYear() - back_year);
1707                         return Date.brokenSetMonth.call(this, month);
1708                 } else {
1709                         return Date.brokenSetMonth.apply(this, arguments);
1710                 }
1711         };
1712 }
1713
1714 /** Date interval constant 
1715 * @static 
1716 * @type String */
1717 Date.MILLI = "ms";
1718 /** Date interval constant 
1719 * @static 
1720 * @type String */
1721 Date.SECOND = "s";
1722 /** Date interval constant 
1723 * @static 
1724 * @type String */
1725 Date.MINUTE = "mi";
1726 /** Date interval constant 
1727 * @static 
1728 * @type String */
1729 Date.HOUR = "h";
1730 /** Date interval constant 
1731 * @static 
1732 * @type String */
1733 Date.DAY = "d";
1734 /** Date interval constant 
1735 * @static 
1736 * @type String */
1737 Date.MONTH = "mo";
1738 /** Date interval constant 
1739 * @static 
1740 * @type String */
1741 Date.YEAR = "y";
1742
1743 /**
1744  * Provides a convenient method of performing basic date arithmetic.  This method
1745  * does not modify the Date instance being called - it creates and returns
1746  * a new Date instance containing the resulting date value.
1747  *
1748  * Examples:
1749  * <pre><code>
1750 //Basic usage:
1751 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1752 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1753
1754 //Negative values will subtract correctly:
1755 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1756 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1757
1758 //You can even chain several calls together in one line!
1759 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1760 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1761  </code></pre>
1762  *
1763  * @param {String} interval   A valid date interval enum value
1764  * @param {Number} value      The amount to add to the current date
1765  * @return {Date} The new Date instance
1766  */
1767 Date.prototype.add = function(interval, value){
1768   var d = this.clone();
1769   if (!interval || value === 0) { return d; }
1770   switch(interval.toLowerCase()){
1771     case Date.MILLI:
1772       d.setMilliseconds(this.getMilliseconds() + value);
1773       break;
1774     case Date.SECOND:
1775       d.setSeconds(this.getSeconds() + value);
1776       break;
1777     case Date.MINUTE:
1778       d.setMinutes(this.getMinutes() + value);
1779       break;
1780     case Date.HOUR:
1781       d.setHours(this.getHours() + value);
1782       break;
1783     case Date.DAY:
1784       d.setDate(this.getDate() + value);
1785       break;
1786     case Date.MONTH:
1787       var day = this.getDate();
1788       if(day > 28){
1789           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1790       }
1791       d.setDate(day);
1792       d.setMonth(this.getMonth() + value);
1793       break;
1794     case Date.YEAR:
1795       d.setFullYear(this.getFullYear() + value);
1796       break;
1797   }
1798   return d;
1799 };
1800 /*
1801  * Based on:
1802  * Ext JS Library 1.1.1
1803  * Copyright(c) 2006-2007, Ext JS, LLC.
1804  *
1805  * Originally Released Under LGPL - original licence link has changed is not relivant.
1806  *
1807  * Fork - LGPL
1808  * <script type="text/javascript">
1809  */
1810
1811 /**
1812  * @class Roo.lib.Dom
1813  * @static
1814  * 
1815  * Dom utils (from YIU afaik)
1816  * 
1817  **/
1818 Roo.lib.Dom = {
1819     /**
1820      * Get the view width
1821      * @param {Boolean} full True will get the full document, otherwise it's the view width
1822      * @return {Number} The width
1823      */
1824      
1825     getViewWidth : function(full) {
1826         return full ? this.getDocumentWidth() : this.getViewportWidth();
1827     },
1828     /**
1829      * Get the view height
1830      * @param {Boolean} full True will get the full document, otherwise it's the view height
1831      * @return {Number} The height
1832      */
1833     getViewHeight : function(full) {
1834         return full ? this.getDocumentHeight() : this.getViewportHeight();
1835     },
1836
1837     getDocumentHeight: function() {
1838         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1839         return Math.max(scrollHeight, this.getViewportHeight());
1840     },
1841
1842     getDocumentWidth: function() {
1843         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1844         return Math.max(scrollWidth, this.getViewportWidth());
1845     },
1846
1847     getViewportHeight: function() {
1848         var height = self.innerHeight;
1849         var mode = document.compatMode;
1850
1851         if ((mode || Roo.isIE) && !Roo.isOpera) {
1852             height = (mode == "CSS1Compat") ?
1853                      document.documentElement.clientHeight :
1854                      document.body.clientHeight;
1855         }
1856
1857         return height;
1858     },
1859
1860     getViewportWidth: function() {
1861         var width = self.innerWidth;
1862         var mode = document.compatMode;
1863
1864         if (mode || Roo.isIE) {
1865             width = (mode == "CSS1Compat") ?
1866                     document.documentElement.clientWidth :
1867                     document.body.clientWidth;
1868         }
1869         return width;
1870     },
1871
1872     isAncestor : function(p, c) {
1873         p = Roo.getDom(p);
1874         c = Roo.getDom(c);
1875         if (!p || !c) {
1876             return false;
1877         }
1878
1879         if (p.contains && !Roo.isSafari) {
1880             return p.contains(c);
1881         } else if (p.compareDocumentPosition) {
1882             return !!(p.compareDocumentPosition(c) & 16);
1883         } else {
1884             var parent = c.parentNode;
1885             while (parent) {
1886                 if (parent == p) {
1887                     return true;
1888                 }
1889                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1890                     return false;
1891                 }
1892                 parent = parent.parentNode;
1893             }
1894             return false;
1895         }
1896     },
1897
1898     getRegion : function(el) {
1899         return Roo.lib.Region.getRegion(el);
1900     },
1901
1902     getY : function(el) {
1903         return this.getXY(el)[1];
1904     },
1905
1906     getX : function(el) {
1907         return this.getXY(el)[0];
1908     },
1909
1910     getXY : function(el) {
1911         var p, pe, b, scroll, bd = document.body;
1912         el = Roo.getDom(el);
1913         var fly = Roo.lib.AnimBase.fly;
1914         if (el.getBoundingClientRect) {
1915             b = el.getBoundingClientRect();
1916             scroll = fly(document).getScroll();
1917             return [b.left + scroll.left, b.top + scroll.top];
1918         }
1919         var x = 0, y = 0;
1920
1921         p = el;
1922
1923         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1924
1925         while (p) {
1926
1927             x += p.offsetLeft;
1928             y += p.offsetTop;
1929
1930             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1931                 hasAbsolute = true;
1932             }
1933
1934             if (Roo.isGecko) {
1935                 pe = fly(p);
1936
1937                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1938                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1939
1940
1941                 x += bl;
1942                 y += bt;
1943
1944
1945                 if (p != el && pe.getStyle('overflow') != 'visible') {
1946                     x += bl;
1947                     y += bt;
1948                 }
1949             }
1950             p = p.offsetParent;
1951         }
1952
1953         if (Roo.isSafari && hasAbsolute) {
1954             x -= bd.offsetLeft;
1955             y -= bd.offsetTop;
1956         }
1957
1958         if (Roo.isGecko && !hasAbsolute) {
1959             var dbd = fly(bd);
1960             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1961             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1962         }
1963
1964         p = el.parentNode;
1965         while (p && p != bd) {
1966             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1967                 x -= p.scrollLeft;
1968                 y -= p.scrollTop;
1969             }
1970             p = p.parentNode;
1971         }
1972         return [x, y];
1973     },
1974  
1975   
1976
1977
1978     setXY : function(el, xy) {
1979         el = Roo.fly(el, '_setXY');
1980         el.position();
1981         var pts = el.translatePoints(xy);
1982         if (xy[0] !== false) {
1983             el.dom.style.left = pts.left + "px";
1984         }
1985         if (xy[1] !== false) {
1986             el.dom.style.top = pts.top + "px";
1987         }
1988     },
1989
1990     setX : function(el, x) {
1991         this.setXY(el, [x, false]);
1992     },
1993
1994     setY : function(el, y) {
1995         this.setXY(el, [false, y]);
1996     }
1997 };
1998 /*
1999  * Portions of this file are based on pieces of Yahoo User Interface Library
2000  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2001  * YUI licensed under the BSD License:
2002  * http://developer.yahoo.net/yui/license.txt
2003  * <script type="text/javascript">
2004  *
2005  */
2006
2007 Roo.lib.Event = function() {
2008     var loadComplete = false;
2009     var listeners = [];
2010     var unloadListeners = [];
2011     var retryCount = 0;
2012     var onAvailStack = [];
2013     var counter = 0;
2014     var lastError = null;
2015
2016     return {
2017         POLL_RETRYS: 200,
2018         POLL_INTERVAL: 20,
2019         EL: 0,
2020         TYPE: 1,
2021         FN: 2,
2022         WFN: 3,
2023         OBJ: 3,
2024         ADJ_SCOPE: 4,
2025         _interval: null,
2026
2027         startInterval: function() {
2028             if (!this._interval) {
2029                 var self = this;
2030                 var callback = function() {
2031                     self._tryPreloadAttach();
2032                 };
2033                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2034
2035             }
2036         },
2037
2038         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2039             onAvailStack.push({ id:         p_id,
2040                 fn:         p_fn,
2041                 obj:        p_obj,
2042                 override:   p_override,
2043                 checkReady: false    });
2044
2045             retryCount = this.POLL_RETRYS;
2046             this.startInterval();
2047         },
2048
2049
2050         addListener: function(el, eventName, fn) {
2051             el = Roo.getDom(el);
2052             if (!el || !fn) {
2053                 return false;
2054             }
2055
2056             if ("unload" == eventName) {
2057                 unloadListeners[unloadListeners.length] =
2058                 [el, eventName, fn];
2059                 return true;
2060             }
2061
2062             var wrappedFn = function(e) {
2063                 return fn(Roo.lib.Event.getEvent(e));
2064             };
2065
2066             var li = [el, eventName, fn, wrappedFn];
2067
2068             var index = listeners.length;
2069             listeners[index] = li;
2070
2071             this.doAdd(el, eventName, wrappedFn, false);
2072             return true;
2073
2074         },
2075
2076
2077         removeListener: function(el, eventName, fn) {
2078             var i, len;
2079
2080             el = Roo.getDom(el);
2081
2082             if(!fn) {
2083                 return this.purgeElement(el, false, eventName);
2084             }
2085
2086
2087             if ("unload" == eventName) {
2088
2089                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2090                     var li = unloadListeners[i];
2091                     if (li &&
2092                         li[0] == el &&
2093                         li[1] == eventName &&
2094                         li[2] == fn) {
2095                         unloadListeners.splice(i, 1);
2096                         return true;
2097                     }
2098                 }
2099
2100                 return false;
2101             }
2102
2103             var cacheItem = null;
2104
2105
2106             var index = arguments[3];
2107
2108             if ("undefined" == typeof index) {
2109                 index = this._getCacheIndex(el, eventName, fn);
2110             }
2111
2112             if (index >= 0) {
2113                 cacheItem = listeners[index];
2114             }
2115
2116             if (!el || !cacheItem) {
2117                 return false;
2118             }
2119
2120             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2121
2122             delete listeners[index][this.WFN];
2123             delete listeners[index][this.FN];
2124             listeners.splice(index, 1);
2125
2126             return true;
2127
2128         },
2129
2130
2131         getTarget: function(ev, resolveTextNode) {
2132             ev = ev.browserEvent || ev;
2133             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2134             var t = ev.target || ev.srcElement;
2135             return this.resolveTextNode(t);
2136         },
2137
2138
2139         resolveTextNode: function(node) {
2140             if (Roo.isSafari && node && 3 == node.nodeType) {
2141                 return node.parentNode;
2142             } else {
2143                 return node;
2144             }
2145         },
2146
2147
2148         getPageX: function(ev) {
2149             ev = ev.browserEvent || ev;
2150             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2151             var x = ev.pageX;
2152             if (!x && 0 !== x) {
2153                 x = ev.clientX || 0;
2154
2155                 if (Roo.isIE) {
2156                     x += this.getScroll()[1];
2157                 }
2158             }
2159
2160             return x;
2161         },
2162
2163
2164         getPageY: function(ev) {
2165             ev = ev.browserEvent || ev;
2166             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2167             var y = ev.pageY;
2168             if (!y && 0 !== y) {
2169                 y = ev.clientY || 0;
2170
2171                 if (Roo.isIE) {
2172                     y += this.getScroll()[0];
2173                 }
2174             }
2175
2176
2177             return y;
2178         },
2179
2180
2181         getXY: function(ev) {
2182             ev = ev.browserEvent || ev;
2183             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2184             return [this.getPageX(ev), this.getPageY(ev)];
2185         },
2186
2187
2188         getRelatedTarget: function(ev) {
2189             ev = ev.browserEvent || ev;
2190             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2191             var t = ev.relatedTarget;
2192             if (!t) {
2193                 if (ev.type == "mouseout") {
2194                     t = ev.toElement;
2195                 } else if (ev.type == "mouseover") {
2196                     t = ev.fromElement;
2197                 }
2198             }
2199
2200             return this.resolveTextNode(t);
2201         },
2202
2203
2204         getTime: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2207             if (!ev.time) {
2208                 var t = new Date().getTime();
2209                 try {
2210                     ev.time = t;
2211                 } catch(ex) {
2212                     this.lastError = ex;
2213                     return t;
2214                 }
2215             }
2216
2217             return ev.time;
2218         },
2219
2220
2221         stopEvent: function(ev) {
2222             this.stopPropagation(ev);
2223             this.preventDefault(ev);
2224         },
2225
2226
2227         stopPropagation: function(ev) {
2228             ev = ev.browserEvent || ev;
2229             if (ev.stopPropagation) {
2230                 ev.stopPropagation();
2231             } else {
2232                 ev.cancelBubble = true;
2233             }
2234         },
2235
2236
2237         preventDefault: function(ev) {
2238             ev = ev.browserEvent || ev;
2239             if(ev.preventDefault) {
2240                 ev.preventDefault();
2241             } else {
2242                 ev.returnValue = false;
2243             }
2244         },
2245
2246
2247         getEvent: function(e) {
2248             var ev = e || window.event;
2249             if (!ev) {
2250                 var c = this.getEvent.caller;
2251                 while (c) {
2252                     ev = c.arguments[0];
2253                     if (ev && Event == ev.constructor) {
2254                         break;
2255                     }
2256                     c = c.caller;
2257                 }
2258             }
2259             return ev;
2260         },
2261
2262
2263         getCharCode: function(ev) {
2264             ev = ev.browserEvent || ev;
2265             return ev.charCode || ev.keyCode || 0;
2266         },
2267
2268
2269         _getCacheIndex: function(el, eventName, fn) {
2270             for (var i = 0,len = listeners.length; i < len; ++i) {
2271                 var li = listeners[i];
2272                 if (li &&
2273                     li[this.FN] == fn &&
2274                     li[this.EL] == el &&
2275                     li[this.TYPE] == eventName) {
2276                     return i;
2277                 }
2278             }
2279
2280             return -1;
2281         },
2282
2283
2284         elCache: {},
2285
2286
2287         getEl: function(id) {
2288             return document.getElementById(id);
2289         },
2290
2291
2292         clearCache: function() {
2293         },
2294
2295
2296         _load: function(e) {
2297             loadComplete = true;
2298             var EU = Roo.lib.Event;
2299
2300
2301             if (Roo.isIE) {
2302                 EU.doRemove(window, "load", EU._load);
2303             }
2304         },
2305
2306
2307         _tryPreloadAttach: function() {
2308
2309             if (this.locked) {
2310                 return false;
2311             }
2312
2313             this.locked = true;
2314
2315
2316             var tryAgain = !loadComplete;
2317             if (!tryAgain) {
2318                 tryAgain = (retryCount > 0);
2319             }
2320
2321
2322             var notAvail = [];
2323             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2324                 var item = onAvailStack[i];
2325                 if (item) {
2326                     var el = this.getEl(item.id);
2327
2328                     if (el) {
2329                         if (!item.checkReady ||
2330                             loadComplete ||
2331                             el.nextSibling ||
2332                             (document && document.body)) {
2333
2334                             var scope = el;
2335                             if (item.override) {
2336                                 if (item.override === true) {
2337                                     scope = item.obj;
2338                                 } else {
2339                                     scope = item.override;
2340                                 }
2341                             }
2342                             item.fn.call(scope, item.obj);
2343                             onAvailStack[i] = null;
2344                         }
2345                     } else {
2346                         notAvail.push(item);
2347                     }
2348                 }
2349             }
2350
2351             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2352
2353             if (tryAgain) {
2354
2355                 this.startInterval();
2356             } else {
2357                 clearInterval(this._interval);
2358                 this._interval = null;
2359             }
2360
2361             this.locked = false;
2362
2363             return true;
2364
2365         },
2366
2367
2368         purgeElement: function(el, recurse, eventName) {
2369             var elListeners = this.getListeners(el, eventName);
2370             if (elListeners) {
2371                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2372                     var l = elListeners[i];
2373                     this.removeListener(el, l.type, l.fn);
2374                 }
2375             }
2376
2377             if (recurse && el && el.childNodes) {
2378                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2379                     this.purgeElement(el.childNodes[i], recurse, eventName);
2380                 }
2381             }
2382         },
2383
2384
2385         getListeners: function(el, eventName) {
2386             var results = [], searchLists;
2387             if (!eventName) {
2388                 searchLists = [listeners, unloadListeners];
2389             } else if (eventName == "unload") {
2390                 searchLists = [unloadListeners];
2391             } else {
2392                 searchLists = [listeners];
2393             }
2394
2395             for (var j = 0; j < searchLists.length; ++j) {
2396                 var searchList = searchLists[j];
2397                 if (searchList && searchList.length > 0) {
2398                     for (var i = 0,len = searchList.length; i < len; ++i) {
2399                         var l = searchList[i];
2400                         if (l && l[this.EL] === el &&
2401                             (!eventName || eventName === l[this.TYPE])) {
2402                             results.push({
2403                                 type:   l[this.TYPE],
2404                                 fn:     l[this.FN],
2405                                 obj:    l[this.OBJ],
2406                                 adjust: l[this.ADJ_SCOPE],
2407                                 index:  i
2408                             });
2409                         }
2410                     }
2411                 }
2412             }
2413
2414             return (results.length) ? results : null;
2415         },
2416
2417
2418         _unload: function(e) {
2419
2420             var EU = Roo.lib.Event, i, j, l, len, index;
2421
2422             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2423                 l = unloadListeners[i];
2424                 if (l) {
2425                     var scope = window;
2426                     if (l[EU.ADJ_SCOPE]) {
2427                         if (l[EU.ADJ_SCOPE] === true) {
2428                             scope = l[EU.OBJ];
2429                         } else {
2430                             scope = l[EU.ADJ_SCOPE];
2431                         }
2432                     }
2433                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2434                     unloadListeners[i] = null;
2435                     l = null;
2436                     scope = null;
2437                 }
2438             }
2439
2440             unloadListeners = null;
2441
2442             if (listeners && listeners.length > 0) {
2443                 j = listeners.length;
2444                 while (j) {
2445                     index = j - 1;
2446                     l = listeners[index];
2447                     if (l) {
2448                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2449                                 l[EU.FN], index);
2450                     }
2451                     j = j - 1;
2452                 }
2453                 l = null;
2454
2455                 EU.clearCache();
2456             }
2457
2458             EU.doRemove(window, "unload", EU._unload);
2459
2460         },
2461
2462
2463         getScroll: function() {
2464             var dd = document.documentElement, db = document.body;
2465             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2466                 return [dd.scrollTop, dd.scrollLeft];
2467             } else if (db) {
2468                 return [db.scrollTop, db.scrollLeft];
2469             } else {
2470                 return [0, 0];
2471             }
2472         },
2473
2474
2475         doAdd: function () {
2476             if (window.addEventListener) {
2477                 return function(el, eventName, fn, capture) {
2478                     el.addEventListener(eventName, fn, (capture));
2479                 };
2480             } else if (window.attachEvent) {
2481                 return function(el, eventName, fn, capture) {
2482                     el.attachEvent("on" + eventName, fn);
2483                 };
2484             } else {
2485                 return function() {
2486                 };
2487             }
2488         }(),
2489
2490
2491         doRemove: function() {
2492             if (window.removeEventListener) {
2493                 return function (el, eventName, fn, capture) {
2494                     el.removeEventListener(eventName, fn, (capture));
2495                 };
2496             } else if (window.detachEvent) {
2497                 return function (el, eventName, fn) {
2498                     el.detachEvent("on" + eventName, fn);
2499                 };
2500             } else {
2501                 return function() {
2502                 };
2503             }
2504         }()
2505     };
2506     
2507 }();
2508 (function() {     
2509    
2510     var E = Roo.lib.Event;
2511     E.on = E.addListener;
2512     E.un = E.removeListener;
2513
2514     if (document && document.body) {
2515         E._load();
2516     } else {
2517         E.doAdd(window, "load", E._load);
2518     }
2519     E.doAdd(window, "unload", E._unload);
2520     E._tryPreloadAttach();
2521 })();
2522
2523 /*
2524  * Portions of this file are based on pieces of Yahoo User Interface Library
2525  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2526  * YUI licensed under the BSD License:
2527  * http://developer.yahoo.net/yui/license.txt
2528  * <script type="text/javascript">
2529  *
2530  */
2531
2532 (function() {
2533     /**
2534      * @class Roo.lib.Ajax
2535      *
2536      */
2537     Roo.lib.Ajax = {
2538         /**
2539          * @static 
2540          */
2541         request : function(method, uri, cb, data, options) {
2542             if(options){
2543                 var hs = options.headers;
2544                 if(hs){
2545                     for(var h in hs){
2546                         if(hs.hasOwnProperty(h)){
2547                             this.initHeader(h, hs[h], false);
2548                         }
2549                     }
2550                 }
2551                 if(options.xmlData){
2552                     this.initHeader('Content-Type', 'text/xml', false);
2553                     method = 'POST';
2554                     data = options.xmlData;
2555                 }
2556             }
2557
2558             return this.asyncRequest(method, uri, cb, data);
2559         },
2560
2561         serializeForm : function(form) {
2562             if(typeof form == 'string') {
2563                 form = (document.getElementById(form) || document.forms[form]);
2564             }
2565
2566             var el, name, val, disabled, data = '', hasSubmit = false;
2567             for (var i = 0; i < form.elements.length; i++) {
2568                 el = form.elements[i];
2569                 disabled = form.elements[i].disabled;
2570                 name = form.elements[i].name;
2571                 val = form.elements[i].value;
2572
2573                 if (!disabled && name){
2574                     switch (el.type)
2575                             {
2576                         case 'select-one':
2577                         case 'select-multiple':
2578                             for (var j = 0; j < el.options.length; j++) {
2579                                 if (el.options[j].selected) {
2580                                     if (Roo.isIE) {
2581                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2582                                     }
2583                                     else {
2584                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2585                                     }
2586                                 }
2587                             }
2588                             break;
2589                         case 'radio':
2590                         case 'checkbox':
2591                             if (el.checked) {
2592                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2593                             }
2594                             break;
2595                         case 'file':
2596
2597                         case undefined:
2598
2599                         case 'reset':
2600
2601                         case 'button':
2602
2603                             break;
2604                         case 'submit':
2605                             if(hasSubmit == false) {
2606                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2607                                 hasSubmit = true;
2608                             }
2609                             break;
2610                         default:
2611                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2612                             break;
2613                     }
2614                 }
2615             }
2616             data = data.substr(0, data.length - 1);
2617             return data;
2618         },
2619
2620         headers:{},
2621
2622         hasHeaders:false,
2623
2624         useDefaultHeader:true,
2625
2626         defaultPostHeader:'application/x-www-form-urlencoded',
2627
2628         useDefaultXhrHeader:true,
2629
2630         defaultXhrHeader:'XMLHttpRequest',
2631
2632         hasDefaultHeaders:true,
2633
2634         defaultHeaders:{},
2635
2636         poll:{},
2637
2638         timeout:{},
2639
2640         pollInterval:50,
2641
2642         transactionId:0,
2643
2644         setProgId:function(id)
2645         {
2646             this.activeX.unshift(id);
2647         },
2648
2649         setDefaultPostHeader:function(b)
2650         {
2651             this.useDefaultHeader = b;
2652         },
2653
2654         setDefaultXhrHeader:function(b)
2655         {
2656             this.useDefaultXhrHeader = b;
2657         },
2658
2659         setPollingInterval:function(i)
2660         {
2661             if (typeof i == 'number' && isFinite(i)) {
2662                 this.pollInterval = i;
2663             }
2664         },
2665
2666         createXhrObject:function(transactionId)
2667         {
2668             var obj,http;
2669             try
2670             {
2671
2672                 http = new XMLHttpRequest();
2673
2674                 obj = { conn:http, tId:transactionId };
2675             }
2676             catch(e)
2677             {
2678                 for (var i = 0; i < this.activeX.length; ++i) {
2679                     try
2680                     {
2681
2682                         http = new ActiveXObject(this.activeX[i]);
2683
2684                         obj = { conn:http, tId:transactionId };
2685                         break;
2686                     }
2687                     catch(e) {
2688                     }
2689                 }
2690             }
2691             finally
2692             {
2693                 return obj;
2694             }
2695         },
2696
2697         getConnectionObject:function()
2698         {
2699             var o;
2700             var tId = this.transactionId;
2701
2702             try
2703             {
2704                 o = this.createXhrObject(tId);
2705                 if (o) {
2706                     this.transactionId++;
2707                 }
2708             }
2709             catch(e) {
2710             }
2711             finally
2712             {
2713                 return o;
2714             }
2715         },
2716
2717         asyncRequest:function(method, uri, callback, postData)
2718         {
2719             var o = this.getConnectionObject();
2720
2721             if (!o) {
2722                 return null;
2723             }
2724             else {
2725                 o.conn.open(method, uri, true);
2726
2727                 if (this.useDefaultXhrHeader) {
2728                     if (!this.defaultHeaders['X-Requested-With']) {
2729                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2730                     }
2731                 }
2732
2733                 if(postData && this.useDefaultHeader){
2734                     this.initHeader('Content-Type', this.defaultPostHeader);
2735                 }
2736
2737                  if (this.hasDefaultHeaders || this.hasHeaders) {
2738                     this.setHeader(o);
2739                 }
2740
2741                 this.handleReadyState(o, callback);
2742                 o.conn.send(postData || null);
2743
2744                 return o;
2745             }
2746         },
2747
2748         handleReadyState:function(o, callback)
2749         {
2750             var oConn = this;
2751
2752             if (callback && callback.timeout) {
2753                 
2754                 this.timeout[o.tId] = window.setTimeout(function() {
2755                     oConn.abort(o, callback, true);
2756                 }, callback.timeout);
2757             }
2758
2759             this.poll[o.tId] = window.setInterval(
2760                     function() {
2761                         if (o.conn && o.conn.readyState == 4) {
2762                             window.clearInterval(oConn.poll[o.tId]);
2763                             delete oConn.poll[o.tId];
2764
2765                             if(callback && callback.timeout) {
2766                                 window.clearTimeout(oConn.timeout[o.tId]);
2767                                 delete oConn.timeout[o.tId];
2768                             }
2769
2770                             oConn.handleTransactionResponse(o, callback);
2771                         }
2772                     }
2773                     , this.pollInterval);
2774         },
2775
2776         handleTransactionResponse:function(o, callback, isAbort)
2777         {
2778
2779             if (!callback) {
2780                 this.releaseObject(o);
2781                 return;
2782             }
2783
2784             var httpStatus, responseObject;
2785
2786             try
2787             {
2788                 if (o.conn.status !== undefined && o.conn.status != 0) {
2789                     httpStatus = o.conn.status;
2790                 }
2791                 else {
2792                     httpStatus = 13030;
2793                 }
2794             }
2795             catch(e) {
2796
2797
2798                 httpStatus = 13030;
2799             }
2800
2801             if (httpStatus >= 200 && httpStatus < 300) {
2802                 responseObject = this.createResponseObject(o, callback.argument);
2803                 if (callback.success) {
2804                     if (!callback.scope) {
2805                         callback.success(responseObject);
2806                     }
2807                     else {
2808
2809
2810                         callback.success.apply(callback.scope, [responseObject]);
2811                     }
2812                 }
2813             }
2814             else {
2815                 switch (httpStatus) {
2816
2817                     case 12002:
2818                     case 12029:
2819                     case 12030:
2820                     case 12031:
2821                     case 12152:
2822                     case 13030:
2823                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2824                         if (callback.failure) {
2825                             if (!callback.scope) {
2826                                 callback.failure(responseObject);
2827                             }
2828                             else {
2829                                 callback.failure.apply(callback.scope, [responseObject]);
2830                             }
2831                         }
2832                         break;
2833                     default:
2834                         responseObject = this.createResponseObject(o, callback.argument);
2835                         if (callback.failure) {
2836                             if (!callback.scope) {
2837                                 callback.failure(responseObject);
2838                             }
2839                             else {
2840                                 callback.failure.apply(callback.scope, [responseObject]);
2841                             }
2842                         }
2843                 }
2844             }
2845
2846             this.releaseObject(o);
2847             responseObject = null;
2848         },
2849
2850         createResponseObject:function(o, callbackArg)
2851         {
2852             var obj = {};
2853             var headerObj = {};
2854
2855             try
2856             {
2857                 var headerStr = o.conn.getAllResponseHeaders();
2858                 var header = headerStr.split('\n');
2859                 for (var i = 0; i < header.length; i++) {
2860                     var delimitPos = header[i].indexOf(':');
2861                     if (delimitPos != -1) {
2862                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2863                     }
2864                 }
2865             }
2866             catch(e) {
2867             }
2868
2869             obj.tId = o.tId;
2870             obj.status = o.conn.status;
2871             obj.statusText = o.conn.statusText;
2872             obj.getResponseHeader = headerObj;
2873             obj.getAllResponseHeaders = headerStr;
2874             obj.responseText = o.conn.responseText;
2875             obj.responseXML = o.conn.responseXML;
2876
2877             if (typeof callbackArg !== undefined) {
2878                 obj.argument = callbackArg;
2879             }
2880
2881             return obj;
2882         },
2883
2884         createExceptionObject:function(tId, callbackArg, isAbort)
2885         {
2886             var COMM_CODE = 0;
2887             var COMM_ERROR = 'communication failure';
2888             var ABORT_CODE = -1;
2889             var ABORT_ERROR = 'transaction aborted';
2890
2891             var obj = {};
2892
2893             obj.tId = tId;
2894             if (isAbort) {
2895                 obj.status = ABORT_CODE;
2896                 obj.statusText = ABORT_ERROR;
2897             }
2898             else {
2899                 obj.status = COMM_CODE;
2900                 obj.statusText = COMM_ERROR;
2901             }
2902
2903             if (callbackArg) {
2904                 obj.argument = callbackArg;
2905             }
2906
2907             return obj;
2908         },
2909
2910         initHeader:function(label, value, isDefault)
2911         {
2912             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2913
2914             if (headerObj[label] === undefined) {
2915                 headerObj[label] = value;
2916             }
2917             else {
2918
2919
2920                 headerObj[label] = value + "," + headerObj[label];
2921             }
2922
2923             if (isDefault) {
2924                 this.hasDefaultHeaders = true;
2925             }
2926             else {
2927                 this.hasHeaders = true;
2928             }
2929         },
2930
2931
2932         setHeader:function(o)
2933         {
2934             if (this.hasDefaultHeaders) {
2935                 for (var prop in this.defaultHeaders) {
2936                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2937                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2938                     }
2939                 }
2940             }
2941
2942             if (this.hasHeaders) {
2943                 for (var prop in this.headers) {
2944                     if (this.headers.hasOwnProperty(prop)) {
2945                         o.conn.setRequestHeader(prop, this.headers[prop]);
2946                     }
2947                 }
2948                 this.headers = {};
2949                 this.hasHeaders = false;
2950             }
2951         },
2952
2953         resetDefaultHeaders:function() {
2954             delete this.defaultHeaders;
2955             this.defaultHeaders = {};
2956             this.hasDefaultHeaders = false;
2957         },
2958
2959         abort:function(o, callback, isTimeout)
2960         {
2961             if(this.isCallInProgress(o)) {
2962                 o.conn.abort();
2963                 window.clearInterval(this.poll[o.tId]);
2964                 delete this.poll[o.tId];
2965                 if (isTimeout) {
2966                     delete this.timeout[o.tId];
2967                 }
2968
2969                 this.handleTransactionResponse(o, callback, true);
2970
2971                 return true;
2972             }
2973             else {
2974                 return false;
2975             }
2976         },
2977
2978
2979         isCallInProgress:function(o)
2980         {
2981             if (o && o.conn) {
2982                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2983             }
2984             else {
2985
2986                 return false;
2987             }
2988         },
2989
2990
2991         releaseObject:function(o)
2992         {
2993
2994             o.conn = null;
2995
2996             o = null;
2997         },
2998
2999         activeX:[
3000         'MSXML2.XMLHTTP.3.0',
3001         'MSXML2.XMLHTTP',
3002         'Microsoft.XMLHTTP'
3003         ]
3004
3005
3006     };
3007 })();/*
3008  * Portions of this file are based on pieces of Yahoo User Interface Library
3009  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3010  * YUI licensed under the BSD License:
3011  * http://developer.yahoo.net/yui/license.txt
3012  * <script type="text/javascript">
3013  *
3014  */
3015
3016 Roo.lib.Region = function(t, r, b, l) {
3017     this.top = t;
3018     this[1] = t;
3019     this.right = r;
3020     this.bottom = b;
3021     this.left = l;
3022     this[0] = l;
3023 };
3024
3025
3026 Roo.lib.Region.prototype = {
3027     contains : function(region) {
3028         return ( region.left >= this.left &&
3029                  region.right <= this.right &&
3030                  region.top >= this.top &&
3031                  region.bottom <= this.bottom    );
3032
3033     },
3034
3035     getArea : function() {
3036         return ( (this.bottom - this.top) * (this.right - this.left) );
3037     },
3038
3039     intersect : function(region) {
3040         var t = Math.max(this.top, region.top);
3041         var r = Math.min(this.right, region.right);
3042         var b = Math.min(this.bottom, region.bottom);
3043         var l = Math.max(this.left, region.left);
3044
3045         if (b >= t && r >= l) {
3046             return new Roo.lib.Region(t, r, b, l);
3047         } else {
3048             return null;
3049         }
3050     },
3051     union : function(region) {
3052         var t = Math.min(this.top, region.top);
3053         var r = Math.max(this.right, region.right);
3054         var b = Math.max(this.bottom, region.bottom);
3055         var l = Math.min(this.left, region.left);
3056
3057         return new Roo.lib.Region(t, r, b, l);
3058     },
3059
3060     adjust : function(t, l, b, r) {
3061         this.top += t;
3062         this.left += l;
3063         this.right += r;
3064         this.bottom += b;
3065         return this;
3066     }
3067 };
3068
3069 Roo.lib.Region.getRegion = function(el) {
3070     var p = Roo.lib.Dom.getXY(el);
3071
3072     var t = p[1];
3073     var r = p[0] + el.offsetWidth;
3074     var b = p[1] + el.offsetHeight;
3075     var l = p[0];
3076
3077     return new Roo.lib.Region(t, r, b, l);
3078 };
3079 /*
3080  * Portions of this file are based on pieces of Yahoo User Interface Library
3081  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3082  * YUI licensed under the BSD License:
3083  * http://developer.yahoo.net/yui/license.txt
3084  * <script type="text/javascript">
3085  *
3086  */
3087 //@@dep Roo.lib.Region
3088
3089
3090 Roo.lib.Point = function(x, y) {
3091     if (x instanceof Array) {
3092         y = x[1];
3093         x = x[0];
3094     }
3095     this.x = this.right = this.left = this[0] = x;
3096     this.y = this.top = this.bottom = this[1] = y;
3097 };
3098
3099 Roo.lib.Point.prototype = new Roo.lib.Region();
3100 /*
3101  * Portions of this file are based on pieces of Yahoo User Interface Library
3102  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3103  * YUI licensed under the BSD License:
3104  * http://developer.yahoo.net/yui/license.txt
3105  * <script type="text/javascript">
3106  *
3107  */
3108  
3109 (function() {   
3110
3111     Roo.lib.Anim = {
3112         scroll : function(el, args, duration, easing, cb, scope) {
3113             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3114         },
3115
3116         motion : function(el, args, duration, easing, cb, scope) {
3117             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3118         },
3119
3120         color : function(el, args, duration, easing, cb, scope) {
3121             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3122         },
3123
3124         run : function(el, args, duration, easing, cb, scope, type) {
3125             type = type || Roo.lib.AnimBase;
3126             if (typeof easing == "string") {
3127                 easing = Roo.lib.Easing[easing];
3128             }
3129             var anim = new type(el, args, duration, easing);
3130             anim.animateX(function() {
3131                 Roo.callback(cb, scope);
3132             });
3133             return anim;
3134         }
3135     };
3136 })();/*
3137  * Portions of this file are based on pieces of Yahoo User Interface Library
3138  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3139  * YUI licensed under the BSD License:
3140  * http://developer.yahoo.net/yui/license.txt
3141  * <script type="text/javascript">
3142  *
3143  */
3144
3145 (function() {    
3146     var libFlyweight;
3147     
3148     function fly(el) {
3149         if (!libFlyweight) {
3150             libFlyweight = new Roo.Element.Flyweight();
3151         }
3152         libFlyweight.dom = el;
3153         return libFlyweight;
3154     }
3155
3156     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3157     
3158    
3159     
3160     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3161         if (el) {
3162             this.init(el, attributes, duration, method);
3163         }
3164     };
3165
3166     Roo.lib.AnimBase.fly = fly;
3167     
3168     
3169     
3170     Roo.lib.AnimBase.prototype = {
3171
3172         toString: function() {
3173             var el = this.getEl();
3174             var id = el.id || el.tagName;
3175             return ("Anim " + id);
3176         },
3177
3178         patterns: {
3179             noNegatives:        /width|height|opacity|padding/i,
3180             offsetAttribute:  /^((width|height)|(top|left))$/,
3181             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3182             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3183         },
3184
3185
3186         doMethod: function(attr, start, end) {
3187             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3188         },
3189
3190
3191         setAttribute: function(attr, val, unit) {
3192             if (this.patterns.noNegatives.test(attr)) {
3193                 val = (val > 0) ? val : 0;
3194             }
3195
3196             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3197         },
3198
3199
3200         getAttribute: function(attr) {
3201             var el = this.getEl();
3202             var val = fly(el).getStyle(attr);
3203
3204             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3205                 return parseFloat(val);
3206             }
3207
3208             var a = this.patterns.offsetAttribute.exec(attr) || [];
3209             var pos = !!( a[3] );
3210             var box = !!( a[2] );
3211
3212
3213             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3214                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3215             } else {
3216                 val = 0;
3217             }
3218
3219             return val;
3220         },
3221
3222
3223         getDefaultUnit: function(attr) {
3224             if (this.patterns.defaultUnit.test(attr)) {
3225                 return 'px';
3226             }
3227
3228             return '';
3229         },
3230
3231         animateX : function(callback, scope) {
3232             var f = function() {
3233                 this.onComplete.removeListener(f);
3234                 if (typeof callback == "function") {
3235                     callback.call(scope || this, this);
3236                 }
3237             };
3238             this.onComplete.addListener(f, this);
3239             this.animate();
3240         },
3241
3242
3243         setRuntimeAttribute: function(attr) {
3244             var start;
3245             var end;
3246             var attributes = this.attributes;
3247
3248             this.runtimeAttributes[attr] = {};
3249
3250             var isset = function(prop) {
3251                 return (typeof prop !== 'undefined');
3252             };
3253
3254             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3255                 return false;
3256             }
3257
3258             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3259
3260
3261             if (isset(attributes[attr]['to'])) {
3262                 end = attributes[attr]['to'];
3263             } else if (isset(attributes[attr]['by'])) {
3264                 if (start.constructor == Array) {
3265                     end = [];
3266                     for (var i = 0, len = start.length; i < len; ++i) {
3267                         end[i] = start[i] + attributes[attr]['by'][i];
3268                     }
3269                 } else {
3270                     end = start + attributes[attr]['by'];
3271                 }
3272             }
3273
3274             this.runtimeAttributes[attr].start = start;
3275             this.runtimeAttributes[attr].end = end;
3276
3277
3278             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3279         },
3280
3281
3282         init: function(el, attributes, duration, method) {
3283
3284             var isAnimated = false;
3285
3286
3287             var startTime = null;
3288
3289
3290             var actualFrames = 0;
3291
3292
3293             el = Roo.getDom(el);
3294
3295
3296             this.attributes = attributes || {};
3297
3298
3299             this.duration = duration || 1;
3300
3301
3302             this.method = method || Roo.lib.Easing.easeNone;
3303
3304
3305             this.useSeconds = true;
3306
3307
3308             this.currentFrame = 0;
3309
3310
3311             this.totalFrames = Roo.lib.AnimMgr.fps;
3312
3313
3314             this.getEl = function() {
3315                 return el;
3316             };
3317
3318
3319             this.isAnimated = function() {
3320                 return isAnimated;
3321             };
3322
3323
3324             this.getStartTime = function() {
3325                 return startTime;
3326             };
3327
3328             this.runtimeAttributes = {};
3329
3330
3331             this.animate = function() {
3332                 if (this.isAnimated()) {
3333                     return false;
3334                 }
3335
3336                 this.currentFrame = 0;
3337
3338                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3339
3340                 Roo.lib.AnimMgr.registerElement(this);
3341             };
3342
3343
3344             this.stop = function(finish) {
3345                 if (finish) {
3346                     this.currentFrame = this.totalFrames;
3347                     this._onTween.fire();
3348                 }
3349                 Roo.lib.AnimMgr.stop(this);
3350             };
3351
3352             var onStart = function() {
3353                 this.onStart.fire();
3354
3355                 this.runtimeAttributes = {};
3356                 for (var attr in this.attributes) {
3357                     this.setRuntimeAttribute(attr);
3358                 }
3359
3360                 isAnimated = true;
3361                 actualFrames = 0;
3362                 startTime = new Date();
3363             };
3364
3365
3366             var onTween = function() {
3367                 var data = {
3368                     duration: new Date() - this.getStartTime(),
3369                     currentFrame: this.currentFrame
3370                 };
3371
3372                 data.toString = function() {
3373                     return (
3374                             'duration: ' + data.duration +
3375                             ', currentFrame: ' + data.currentFrame
3376                             );
3377                 };
3378
3379                 this.onTween.fire(data);
3380
3381                 var runtimeAttributes = this.runtimeAttributes;
3382
3383                 for (var attr in runtimeAttributes) {
3384                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3385                 }
3386
3387                 actualFrames += 1;
3388             };
3389
3390             var onComplete = function() {
3391                 var actual_duration = (new Date() - startTime) / 1000 ;
3392
3393                 var data = {
3394                     duration: actual_duration,
3395                     frames: actualFrames,
3396                     fps: actualFrames / actual_duration
3397                 };
3398
3399                 data.toString = function() {
3400                     return (
3401                             'duration: ' + data.duration +
3402                             ', frames: ' + data.frames +
3403                             ', fps: ' + data.fps
3404                             );
3405                 };
3406
3407                 isAnimated = false;
3408                 actualFrames = 0;
3409                 this.onComplete.fire(data);
3410             };
3411
3412
3413             this._onStart = new Roo.util.Event(this);
3414             this.onStart = new Roo.util.Event(this);
3415             this.onTween = new Roo.util.Event(this);
3416             this._onTween = new Roo.util.Event(this);
3417             this.onComplete = new Roo.util.Event(this);
3418             this._onComplete = new Roo.util.Event(this);
3419             this._onStart.addListener(onStart);
3420             this._onTween.addListener(onTween);
3421             this._onComplete.addListener(onComplete);
3422         }
3423     };
3424 })();
3425 /*
3426  * Portions of this file are based on pieces of Yahoo User Interface Library
3427  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3428  * YUI licensed under the BSD License:
3429  * http://developer.yahoo.net/yui/license.txt
3430  * <script type="text/javascript">
3431  *
3432  */
3433
3434 Roo.lib.AnimMgr = new function() {
3435
3436     var thread = null;
3437
3438
3439     var queue = [];
3440
3441
3442     var tweenCount = 0;
3443
3444
3445     this.fps = 1000;
3446
3447
3448     this.delay = 1;
3449
3450
3451     this.registerElement = function(tween) {
3452         queue[queue.length] = tween;
3453         tweenCount += 1;
3454         tween._onStart.fire();
3455         this.start();
3456     };
3457
3458
3459     this.unRegister = function(tween, index) {
3460         tween._onComplete.fire();
3461         index = index || getIndex(tween);
3462         if (index != -1) {
3463             queue.splice(index, 1);
3464         }
3465
3466         tweenCount -= 1;
3467         if (tweenCount <= 0) {
3468             this.stop();
3469         }
3470     };
3471
3472
3473     this.start = function() {
3474         if (thread === null) {
3475             thread = setInterval(this.run, this.delay);
3476         }
3477     };
3478
3479
3480     this.stop = function(tween) {
3481         if (!tween) {
3482             clearInterval(thread);
3483
3484             for (var i = 0, len = queue.length; i < len; ++i) {
3485                 if (queue[0].isAnimated()) {
3486                     this.unRegister(queue[0], 0);
3487                 }
3488             }
3489
3490             queue = [];
3491             thread = null;
3492             tweenCount = 0;
3493         }
3494         else {
3495             this.unRegister(tween);
3496         }
3497     };
3498
3499
3500     this.run = function() {
3501         for (var i = 0, len = queue.length; i < len; ++i) {
3502             var tween = queue[i];
3503             if (!tween || !tween.isAnimated()) {
3504                 continue;
3505             }
3506
3507             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3508             {
3509                 tween.currentFrame += 1;
3510
3511                 if (tween.useSeconds) {
3512                     correctFrame(tween);
3513                 }
3514                 tween._onTween.fire();
3515             }
3516             else {
3517                 Roo.lib.AnimMgr.stop(tween, i);
3518             }
3519         }
3520     };
3521
3522     var getIndex = function(anim) {
3523         for (var i = 0, len = queue.length; i < len; ++i) {
3524             if (queue[i] == anim) {
3525                 return i;
3526             }
3527         }
3528         return -1;
3529     };
3530
3531
3532     var correctFrame = function(tween) {
3533         var frames = tween.totalFrames;
3534         var frame = tween.currentFrame;
3535         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3536         var elapsed = (new Date() - tween.getStartTime());
3537         var tweak = 0;
3538
3539         if (elapsed < tween.duration * 1000) {
3540             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3541         } else {
3542             tweak = frames - (frame + 1);
3543         }
3544         if (tweak > 0 && isFinite(tweak)) {
3545             if (tween.currentFrame + tweak >= frames) {
3546                 tweak = frames - (frame + 1);
3547             }
3548
3549             tween.currentFrame += tweak;
3550         }
3551     };
3552 };
3553
3554     /*
3555  * Portions of this file are based on pieces of Yahoo User Interface Library
3556  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3557  * YUI licensed under the BSD License:
3558  * http://developer.yahoo.net/yui/license.txt
3559  * <script type="text/javascript">
3560  *
3561  */
3562 Roo.lib.Bezier = new function() {
3563
3564         this.getPosition = function(points, t) {
3565             var n = points.length;
3566             var tmp = [];
3567
3568             for (var i = 0; i < n; ++i) {
3569                 tmp[i] = [points[i][0], points[i][1]];
3570             }
3571
3572             for (var j = 1; j < n; ++j) {
3573                 for (i = 0; i < n - j; ++i) {
3574                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3575                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3576                 }
3577             }
3578
3579             return [ tmp[0][0], tmp[0][1] ];
3580
3581         };
3582     };/*
3583  * Portions of this file are based on pieces of Yahoo User Interface Library
3584  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3585  * YUI licensed under the BSD License:
3586  * http://developer.yahoo.net/yui/license.txt
3587  * <script type="text/javascript">
3588  *
3589  */
3590 (function() {
3591
3592     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3593         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3594     };
3595
3596     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3597
3598     var fly = Roo.lib.AnimBase.fly;
3599     var Y = Roo.lib;
3600     var superclass = Y.ColorAnim.superclass;
3601     var proto = Y.ColorAnim.prototype;
3602
3603     proto.toString = function() {
3604         var el = this.getEl();
3605         var id = el.id || el.tagName;
3606         return ("ColorAnim " + id);
3607     };
3608
3609     proto.patterns.color = /color$/i;
3610     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3611     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3612     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3613     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3614
3615
3616     proto.parseColor = function(s) {
3617         if (s.length == 3) {
3618             return s;
3619         }
3620
3621         var c = this.patterns.hex.exec(s);
3622         if (c && c.length == 4) {
3623             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3624         }
3625
3626         c = this.patterns.rgb.exec(s);
3627         if (c && c.length == 4) {
3628             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3629         }
3630
3631         c = this.patterns.hex3.exec(s);
3632         if (c && c.length == 4) {
3633             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3634         }
3635
3636         return null;
3637     };
3638     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3639     proto.getAttribute = function(attr) {
3640         var el = this.getEl();
3641         if (this.patterns.color.test(attr)) {
3642             var val = fly(el).getStyle(attr);
3643
3644             if (this.patterns.transparent.test(val)) {
3645                 var parent = el.parentNode;
3646                 val = fly(parent).getStyle(attr);
3647
3648                 while (parent && this.patterns.transparent.test(val)) {
3649                     parent = parent.parentNode;
3650                     val = fly(parent).getStyle(attr);
3651                     if (parent.tagName.toUpperCase() == 'HTML') {
3652                         val = '#fff';
3653                     }
3654                 }
3655             }
3656         } else {
3657             val = superclass.getAttribute.call(this, attr);
3658         }
3659
3660         return val;
3661     };
3662     proto.getAttribute = function(attr) {
3663         var el = this.getEl();
3664         if (this.patterns.color.test(attr)) {
3665             var val = fly(el).getStyle(attr);
3666
3667             if (this.patterns.transparent.test(val)) {
3668                 var parent = el.parentNode;
3669                 val = fly(parent).getStyle(attr);
3670
3671                 while (parent && this.patterns.transparent.test(val)) {
3672                     parent = parent.parentNode;
3673                     val = fly(parent).getStyle(attr);
3674                     if (parent.tagName.toUpperCase() == 'HTML') {
3675                         val = '#fff';
3676                     }
3677                 }
3678             }
3679         } else {
3680             val = superclass.getAttribute.call(this, attr);
3681         }
3682
3683         return val;
3684     };
3685
3686     proto.doMethod = function(attr, start, end) {
3687         var val;
3688
3689         if (this.patterns.color.test(attr)) {
3690             val = [];
3691             for (var i = 0, len = start.length; i < len; ++i) {
3692                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3693             }
3694
3695             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3696         }
3697         else {
3698             val = superclass.doMethod.call(this, attr, start, end);
3699         }
3700
3701         return val;
3702     };
3703
3704     proto.setRuntimeAttribute = function(attr) {
3705         superclass.setRuntimeAttribute.call(this, attr);
3706
3707         if (this.patterns.color.test(attr)) {
3708             var attributes = this.attributes;
3709             var start = this.parseColor(this.runtimeAttributes[attr].start);
3710             var end = this.parseColor(this.runtimeAttributes[attr].end);
3711
3712             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3713                 end = this.parseColor(attributes[attr].by);
3714
3715                 for (var i = 0, len = start.length; i < len; ++i) {
3716                     end[i] = start[i] + end[i];
3717                 }
3718             }
3719
3720             this.runtimeAttributes[attr].start = start;
3721             this.runtimeAttributes[attr].end = end;
3722         }
3723     };
3724 })();
3725
3726 /*
3727  * Portions of this file are based on pieces of Yahoo User Interface Library
3728  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3729  * YUI licensed under the BSD License:
3730  * http://developer.yahoo.net/yui/license.txt
3731  * <script type="text/javascript">
3732  *
3733  */
3734 Roo.lib.Easing = {
3735
3736
3737     easeNone: function (t, b, c, d) {
3738         return c * t / d + b;
3739     },
3740
3741
3742     easeIn: function (t, b, c, d) {
3743         return c * (t /= d) * t + b;
3744     },
3745
3746
3747     easeOut: function (t, b, c, d) {
3748         return -c * (t /= d) * (t - 2) + b;
3749     },
3750
3751
3752     easeBoth: function (t, b, c, d) {
3753         if ((t /= d / 2) < 1) {
3754             return c / 2 * t * t + b;
3755         }
3756
3757         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3758     },
3759
3760
3761     easeInStrong: function (t, b, c, d) {
3762         return c * (t /= d) * t * t * t + b;
3763     },
3764
3765
3766     easeOutStrong: function (t, b, c, d) {
3767         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3768     },
3769
3770
3771     easeBothStrong: function (t, b, c, d) {
3772         if ((t /= d / 2) < 1) {
3773             return c / 2 * t * t * t * t + b;
3774         }
3775
3776         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3777     },
3778
3779
3780
3781     elasticIn: function (t, b, c, d, a, p) {
3782         if (t == 0) {
3783             return b;
3784         }
3785         if ((t /= d) == 1) {
3786             return b + c;
3787         }
3788         if (!p) {
3789             p = d * .3;
3790         }
3791
3792         if (!a || a < Math.abs(c)) {
3793             a = c;
3794             var s = p / 4;
3795         }
3796         else {
3797             var s = p / (2 * Math.PI) * Math.asin(c / a);
3798         }
3799
3800         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3801     },
3802
3803
3804     elasticOut: function (t, b, c, d, a, p) {
3805         if (t == 0) {
3806             return b;
3807         }
3808         if ((t /= d) == 1) {
3809             return b + c;
3810         }
3811         if (!p) {
3812             p = d * .3;
3813         }
3814
3815         if (!a || a < Math.abs(c)) {
3816             a = c;
3817             var s = p / 4;
3818         }
3819         else {
3820             var s = p / (2 * Math.PI) * Math.asin(c / a);
3821         }
3822
3823         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3824     },
3825
3826
3827     elasticBoth: function (t, b, c, d, a, p) {
3828         if (t == 0) {
3829             return b;
3830         }
3831
3832         if ((t /= d / 2) == 2) {
3833             return b + c;
3834         }
3835
3836         if (!p) {
3837             p = d * (.3 * 1.5);
3838         }
3839
3840         if (!a || a < Math.abs(c)) {
3841             a = c;
3842             var s = p / 4;
3843         }
3844         else {
3845             var s = p / (2 * Math.PI) * Math.asin(c / a);
3846         }
3847
3848         if (t < 1) {
3849             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3850                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3851         }
3852         return a * Math.pow(2, -10 * (t -= 1)) *
3853                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3854     },
3855
3856
3857
3858     backIn: function (t, b, c, d, s) {
3859         if (typeof s == 'undefined') {
3860             s = 1.70158;
3861         }
3862         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3863     },
3864
3865
3866     backOut: function (t, b, c, d, s) {
3867         if (typeof s == 'undefined') {
3868             s = 1.70158;
3869         }
3870         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3871     },
3872
3873
3874     backBoth: function (t, b, c, d, s) {
3875         if (typeof s == 'undefined') {
3876             s = 1.70158;
3877         }
3878
3879         if ((t /= d / 2 ) < 1) {
3880             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3881         }
3882         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3883     },
3884
3885
3886     bounceIn: function (t, b, c, d) {
3887         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3888     },
3889
3890
3891     bounceOut: function (t, b, c, d) {
3892         if ((t /= d) < (1 / 2.75)) {
3893             return c * (7.5625 * t * t) + b;
3894         } else if (t < (2 / 2.75)) {
3895             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3896         } else if (t < (2.5 / 2.75)) {
3897             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3898         }
3899         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3900     },
3901
3902
3903     bounceBoth: function (t, b, c, d) {
3904         if (t < d / 2) {
3905             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3906         }
3907         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3908     }
3909 };/*
3910  * Portions of this file are based on pieces of Yahoo User Interface Library
3911  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3912  * YUI licensed under the BSD License:
3913  * http://developer.yahoo.net/yui/license.txt
3914  * <script type="text/javascript">
3915  *
3916  */
3917     (function() {
3918         Roo.lib.Motion = function(el, attributes, duration, method) {
3919             if (el) {
3920                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3921             }
3922         };
3923
3924         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3925
3926
3927         var Y = Roo.lib;
3928         var superclass = Y.Motion.superclass;
3929         var proto = Y.Motion.prototype;
3930
3931         proto.toString = function() {
3932             var el = this.getEl();
3933             var id = el.id || el.tagName;
3934             return ("Motion " + id);
3935         };
3936
3937         proto.patterns.points = /^points$/i;
3938
3939         proto.setAttribute = function(attr, val, unit) {
3940             if (this.patterns.points.test(attr)) {
3941                 unit = unit || 'px';
3942                 superclass.setAttribute.call(this, 'left', val[0], unit);
3943                 superclass.setAttribute.call(this, 'top', val[1], unit);
3944             } else {
3945                 superclass.setAttribute.call(this, attr, val, unit);
3946             }
3947         };
3948
3949         proto.getAttribute = function(attr) {
3950             if (this.patterns.points.test(attr)) {
3951                 var val = [
3952                         superclass.getAttribute.call(this, 'left'),
3953                         superclass.getAttribute.call(this, 'top')
3954                         ];
3955             } else {
3956                 val = superclass.getAttribute.call(this, attr);
3957             }
3958
3959             return val;
3960         };
3961
3962         proto.doMethod = function(attr, start, end) {
3963             var val = null;
3964
3965             if (this.patterns.points.test(attr)) {
3966                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3967                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3968             } else {
3969                 val = superclass.doMethod.call(this, attr, start, end);
3970             }
3971             return val;
3972         };
3973
3974         proto.setRuntimeAttribute = function(attr) {
3975             if (this.patterns.points.test(attr)) {
3976                 var el = this.getEl();
3977                 var attributes = this.attributes;
3978                 var start;
3979                 var control = attributes['points']['control'] || [];
3980                 var end;
3981                 var i, len;
3982
3983                 if (control.length > 0 && !(control[0] instanceof Array)) {
3984                     control = [control];
3985                 } else {
3986                     var tmp = [];
3987                     for (i = 0,len = control.length; i < len; ++i) {
3988                         tmp[i] = control[i];
3989                     }
3990                     control = tmp;
3991                 }
3992
3993                 Roo.fly(el).position();
3994
3995                 if (isset(attributes['points']['from'])) {
3996                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3997                 }
3998                 else {
3999                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4000                 }
4001
4002                 start = this.getAttribute('points');
4003
4004
4005                 if (isset(attributes['points']['to'])) {
4006                     end = translateValues.call(this, attributes['points']['to'], start);
4007
4008                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4009                     for (i = 0,len = control.length; i < len; ++i) {
4010                         control[i] = translateValues.call(this, control[i], start);
4011                     }
4012
4013
4014                 } else if (isset(attributes['points']['by'])) {
4015                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4016
4017                     for (i = 0,len = control.length; i < len; ++i) {
4018                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4019                     }
4020                 }
4021
4022                 this.runtimeAttributes[attr] = [start];
4023
4024                 if (control.length > 0) {
4025                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4026                 }
4027
4028                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4029             }
4030             else {
4031                 superclass.setRuntimeAttribute.call(this, attr);
4032             }
4033         };
4034
4035         var translateValues = function(val, start) {
4036             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4037             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4038
4039             return val;
4040         };
4041
4042         var isset = function(prop) {
4043             return (typeof prop !== 'undefined');
4044         };
4045     })();
4046 /*
4047  * Portions of this file are based on pieces of Yahoo User Interface Library
4048  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4049  * YUI licensed under the BSD License:
4050  * http://developer.yahoo.net/yui/license.txt
4051  * <script type="text/javascript">
4052  *
4053  */
4054     (function() {
4055         Roo.lib.Scroll = function(el, attributes, duration, method) {
4056             if (el) {
4057                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4058             }
4059         };
4060
4061         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4062
4063
4064         var Y = Roo.lib;
4065         var superclass = Y.Scroll.superclass;
4066         var proto = Y.Scroll.prototype;
4067
4068         proto.toString = function() {
4069             var el = this.getEl();
4070             var id = el.id || el.tagName;
4071             return ("Scroll " + id);
4072         };
4073
4074         proto.doMethod = function(attr, start, end) {
4075             var val = null;
4076
4077             if (attr == 'scroll') {
4078                 val = [
4079                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4080                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4081                         ];
4082
4083             } else {
4084                 val = superclass.doMethod.call(this, attr, start, end);
4085             }
4086             return val;
4087         };
4088
4089         proto.getAttribute = function(attr) {
4090             var val = null;
4091             var el = this.getEl();
4092
4093             if (attr == 'scroll') {
4094                 val = [ el.scrollLeft, el.scrollTop ];
4095             } else {
4096                 val = superclass.getAttribute.call(this, attr);
4097             }
4098
4099             return val;
4100         };
4101
4102         proto.setAttribute = function(attr, val, unit) {
4103             var el = this.getEl();
4104
4105             if (attr == 'scroll') {
4106                 el.scrollLeft = val[0];
4107                 el.scrollTop = val[1];
4108             } else {
4109                 superclass.setAttribute.call(this, attr, val, unit);
4110             }
4111         };
4112     })();
4113 /*
4114  * Based on:
4115  * Ext JS Library 1.1.1
4116  * Copyright(c) 2006-2007, Ext JS, LLC.
4117  *
4118  * Originally Released Under LGPL - original licence link has changed is not relivant.
4119  *
4120  * Fork - LGPL
4121  * <script type="text/javascript">
4122  */
4123
4124
4125 // nasty IE9 hack - what a pile of crap that is..
4126
4127  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4128     Range.prototype.createContextualFragment = function (html) {
4129         var doc = window.document;
4130         var container = doc.createElement("div");
4131         container.innerHTML = html;
4132         var frag = doc.createDocumentFragment(), n;
4133         while ((n = container.firstChild)) {
4134             frag.appendChild(n);
4135         }
4136         return frag;
4137     };
4138 }
4139
4140 /**
4141  * @class Roo.DomHelper
4142  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4143  * 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>.
4144  * @singleton
4145  */
4146 Roo.DomHelper = function(){
4147     var tempTableEl = null;
4148     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4149     var tableRe = /^table|tbody|tr|td$/i;
4150     var xmlns = {};
4151     // build as innerHTML where available
4152     /** @ignore */
4153     var createHtml = function(o){
4154         if(typeof o == 'string'){
4155             return o;
4156         }
4157         var b = "";
4158         if(!o.tag){
4159             o.tag = "div";
4160         }
4161         b += "<" + o.tag;
4162         for(var attr in o){
4163             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4164             if(attr == "style"){
4165                 var s = o["style"];
4166                 if(typeof s == "function"){
4167                     s = s.call();
4168                 }
4169                 if(typeof s == "string"){
4170                     b += ' style="' + s + '"';
4171                 }else if(typeof s == "object"){
4172                     b += ' style="';
4173                     for(var key in s){
4174                         if(typeof s[key] != "function"){
4175                             b += key + ":" + s[key] + ";";
4176                         }
4177                     }
4178                     b += '"';
4179                 }
4180             }else{
4181                 if(attr == "cls"){
4182                     b += ' class="' + o["cls"] + '"';
4183                 }else if(attr == "htmlFor"){
4184                     b += ' for="' + o["htmlFor"] + '"';
4185                 }else{
4186                     b += " " + attr + '="' + o[attr] + '"';
4187                 }
4188             }
4189         }
4190         if(emptyTags.test(o.tag)){
4191             b += "/>";
4192         }else{
4193             b += ">";
4194             var cn = o.children || o.cn;
4195             if(cn){
4196                 //http://bugs.kde.org/show_bug.cgi?id=71506
4197                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                     for(var i = 0, len = cn.length; i < len; i++) {
4199                         b += createHtml(cn[i], b);
4200                     }
4201                 }else{
4202                     b += createHtml(cn, b);
4203                 }
4204             }
4205             if(o.html){
4206                 b += o.html;
4207             }
4208             b += "</" + o.tag + ">";
4209         }
4210         return b;
4211     };
4212
4213     // build as dom
4214     /** @ignore */
4215     var createDom = function(o, parentNode){
4216          
4217         // defininition craeted..
4218         var ns = false;
4219         if (o.ns && o.ns != 'html') {
4220                
4221             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4222                 xmlns[o.ns] = o.xmlns;
4223                 ns = o.xmlns;
4224             }
4225             if (typeof(xmlns[o.ns]) == 'undefined') {
4226                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4227             }
4228             ns = xmlns[o.ns];
4229         }
4230         
4231         
4232         if (typeof(o) == 'string') {
4233             return parentNode.appendChild(document.createTextNode(o));
4234         }
4235         o.tag = o.tag || div;
4236         if (o.ns && Roo.isIE) {
4237             ns = false;
4238             o.tag = o.ns + ':' + o.tag;
4239             
4240         }
4241         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4242         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4243         for(var attr in o){
4244             
4245             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4246                     attr == "style" || typeof o[attr] == "function") { continue; }
4247                     
4248             if(attr=="cls" && Roo.isIE){
4249                 el.className = o["cls"];
4250             }else{
4251                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4252                 else { 
4253                     el[attr] = o[attr];
4254                 }
4255             }
4256         }
4257         Roo.DomHelper.applyStyles(el, o.style);
4258         var cn = o.children || o.cn;
4259         if(cn){
4260             //http://bugs.kde.org/show_bug.cgi?id=71506
4261              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4262                 for(var i = 0, len = cn.length; i < len; i++) {
4263                     createDom(cn[i], el);
4264                 }
4265             }else{
4266                 createDom(cn, el);
4267             }
4268         }
4269         if(o.html){
4270             el.innerHTML = o.html;
4271         }
4272         if(parentNode){
4273            parentNode.appendChild(el);
4274         }
4275         return el;
4276     };
4277
4278     var ieTable = function(depth, s, h, e){
4279         tempTableEl.innerHTML = [s, h, e].join('');
4280         var i = -1, el = tempTableEl;
4281         while(++i < depth){
4282             el = el.firstChild;
4283         }
4284         return el;
4285     };
4286
4287     // kill repeat to save bytes
4288     var ts = '<table>',
4289         te = '</table>',
4290         tbs = ts+'<tbody>',
4291         tbe = '</tbody>'+te,
4292         trs = tbs + '<tr>',
4293         tre = '</tr>'+tbe;
4294
4295     /**
4296      * @ignore
4297      * Nasty code for IE's broken table implementation
4298      */
4299     var insertIntoTable = function(tag, where, el, html){
4300         if(!tempTableEl){
4301             tempTableEl = document.createElement('div');
4302         }
4303         var node;
4304         var before = null;
4305         if(tag == 'td'){
4306             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4307                 return;
4308             }
4309             if(where == 'beforebegin'){
4310                 before = el;
4311                 el = el.parentNode;
4312             } else{
4313                 before = el.nextSibling;
4314                 el = el.parentNode;
4315             }
4316             node = ieTable(4, trs, html, tre);
4317         }
4318         else if(tag == 'tr'){
4319             if(where == 'beforebegin'){
4320                 before = el;
4321                 el = el.parentNode;
4322                 node = ieTable(3, tbs, html, tbe);
4323             } else if(where == 'afterend'){
4324                 before = el.nextSibling;
4325                 el = el.parentNode;
4326                 node = ieTable(3, tbs, html, tbe);
4327             } else{ // INTO a TR
4328                 if(where == 'afterbegin'){
4329                     before = el.firstChild;
4330                 }
4331                 node = ieTable(4, trs, html, tre);
4332             }
4333         } else if(tag == 'tbody'){
4334             if(where == 'beforebegin'){
4335                 before = el;
4336                 el = el.parentNode;
4337                 node = ieTable(2, ts, html, te);
4338             } else if(where == 'afterend'){
4339                 before = el.nextSibling;
4340                 el = el.parentNode;
4341                 node = ieTable(2, ts, html, te);
4342             } else{
4343                 if(where == 'afterbegin'){
4344                     before = el.firstChild;
4345                 }
4346                 node = ieTable(3, tbs, html, tbe);
4347             }
4348         } else{ // TABLE
4349             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4350                 return;
4351             }
4352             if(where == 'afterbegin'){
4353                 before = el.firstChild;
4354             }
4355             node = ieTable(2, ts, html, te);
4356         }
4357         el.insertBefore(node, before);
4358         return node;
4359     };
4360
4361     return {
4362     /** True to force the use of DOM instead of html fragments @type Boolean */
4363     useDom : false,
4364
4365     /**
4366      * Returns the markup for the passed Element(s) config
4367      * @param {Object} o The Dom object spec (and children)
4368      * @return {String}
4369      */
4370     markup : function(o){
4371         return createHtml(o);
4372     },
4373
4374     /**
4375      * Applies a style specification to an element
4376      * @param {String/HTMLElement} el The element to apply styles to
4377      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4378      * a function which returns such a specification.
4379      */
4380     applyStyles : function(el, styles){
4381         if(styles){
4382            el = Roo.fly(el);
4383            if(typeof styles == "string"){
4384                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4385                var matches;
4386                while ((matches = re.exec(styles)) != null){
4387                    el.setStyle(matches[1], matches[2]);
4388                }
4389            }else if (typeof styles == "object"){
4390                for (var style in styles){
4391                   el.setStyle(style, styles[style]);
4392                }
4393            }else if (typeof styles == "function"){
4394                 Roo.DomHelper.applyStyles(el, styles.call());
4395            }
4396         }
4397     },
4398
4399     /**
4400      * Inserts an HTML fragment into the Dom
4401      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4402      * @param {HTMLElement} el The context element
4403      * @param {String} html The HTML fragmenet
4404      * @return {HTMLElement} The new node
4405      */
4406     insertHtml : function(where, el, html){
4407         where = where.toLowerCase();
4408         if(el.insertAdjacentHTML){
4409             if(tableRe.test(el.tagName)){
4410                 var rs;
4411                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4412                     return rs;
4413                 }
4414             }
4415             switch(where){
4416                 case "beforebegin":
4417                     el.insertAdjacentHTML('BeforeBegin', html);
4418                     return el.previousSibling;
4419                 case "afterbegin":
4420                     el.insertAdjacentHTML('AfterBegin', html);
4421                     return el.firstChild;
4422                 case "beforeend":
4423                     el.insertAdjacentHTML('BeforeEnd', html);
4424                     return el.lastChild;
4425                 case "afterend":
4426                     el.insertAdjacentHTML('AfterEnd', html);
4427                     return el.nextSibling;
4428             }
4429             throw 'Illegal insertion point -> "' + where + '"';
4430         }
4431         var range = el.ownerDocument.createRange();
4432         var frag;
4433         switch(where){
4434              case "beforebegin":
4435                 range.setStartBefore(el);
4436                 frag = range.createContextualFragment(html);
4437                 el.parentNode.insertBefore(frag, el);
4438                 return el.previousSibling;
4439              case "afterbegin":
4440                 if(el.firstChild){
4441                     range.setStartBefore(el.firstChild);
4442                     frag = range.createContextualFragment(html);
4443                     el.insertBefore(frag, el.firstChild);
4444                     return el.firstChild;
4445                 }else{
4446                     el.innerHTML = html;
4447                     return el.firstChild;
4448                 }
4449             case "beforeend":
4450                 if(el.lastChild){
4451                     range.setStartAfter(el.lastChild);
4452                     frag = range.createContextualFragment(html);
4453                     el.appendChild(frag);
4454                     return el.lastChild;
4455                 }else{
4456                     el.innerHTML = html;
4457                     return el.lastChild;
4458                 }
4459             case "afterend":
4460                 range.setStartAfter(el);
4461                 frag = range.createContextualFragment(html);
4462                 el.parentNode.insertBefore(frag, el.nextSibling);
4463                 return el.nextSibling;
4464             }
4465             throw 'Illegal insertion point -> "' + where + '"';
4466     },
4467
4468     /**
4469      * Creates new Dom element(s) and inserts them before el
4470      * @param {String/HTMLElement/Element} el The context element
4471      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4472      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4473      * @return {HTMLElement/Roo.Element} The new node
4474      */
4475     insertBefore : function(el, o, returnElement){
4476         return this.doInsert(el, o, returnElement, "beforeBegin");
4477     },
4478
4479     /**
4480      * Creates new Dom element(s) and inserts them after el
4481      * @param {String/HTMLElement/Element} el The context element
4482      * @param {Object} o The Dom object spec (and children)
4483      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4484      * @return {HTMLElement/Roo.Element} The new node
4485      */
4486     insertAfter : function(el, o, returnElement){
4487         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4488     },
4489
4490     /**
4491      * Creates new Dom element(s) and inserts them as the first child of el
4492      * @param {String/HTMLElement/Element} el The context element
4493      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4494      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4495      * @return {HTMLElement/Roo.Element} The new node
4496      */
4497     insertFirst : function(el, o, returnElement){
4498         return this.doInsert(el, o, returnElement, "afterBegin");
4499     },
4500
4501     // private
4502     doInsert : function(el, o, returnElement, pos, sibling){
4503         el = Roo.getDom(el);
4504         var newNode;
4505         if(this.useDom || o.ns){
4506             newNode = createDom(o, null);
4507             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4508         }else{
4509             var html = createHtml(o);
4510             newNode = this.insertHtml(pos, el, html);
4511         }
4512         return returnElement ? Roo.get(newNode, true) : newNode;
4513     },
4514
4515     /**
4516      * Creates new Dom element(s) and appends them to el
4517      * @param {String/HTMLElement/Element} el The context element
4518      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4519      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4520      * @return {HTMLElement/Roo.Element} The new node
4521      */
4522     append : function(el, o, returnElement){
4523         el = Roo.getDom(el);
4524         var newNode;
4525         if(this.useDom || o.ns){
4526             newNode = createDom(o, null);
4527             el.appendChild(newNode);
4528         }else{
4529             var html = createHtml(o);
4530             newNode = this.insertHtml("beforeEnd", el, html);
4531         }
4532         return returnElement ? Roo.get(newNode, true) : newNode;
4533     },
4534
4535     /**
4536      * Creates new Dom element(s) and overwrites the contents of el with them
4537      * @param {String/HTMLElement/Element} el The context element
4538      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4539      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4540      * @return {HTMLElement/Roo.Element} The new node
4541      */
4542     overwrite : function(el, o, returnElement){
4543         el = Roo.getDom(el);
4544         if (o.ns) {
4545           
4546             while (el.childNodes.length) {
4547                 el.removeChild(el.firstChild);
4548             }
4549             createDom(o, el);
4550         } else {
4551             el.innerHTML = createHtml(o);   
4552         }
4553         
4554         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4555     },
4556
4557     /**
4558      * Creates a new Roo.DomHelper.Template from the Dom object spec
4559      * @param {Object} o The Dom object spec (and children)
4560      * @return {Roo.DomHelper.Template} The new template
4561      */
4562     createTemplate : function(o){
4563         var html = createHtml(o);
4564         return new Roo.Template(html);
4565     }
4566     };
4567 }();
4568 /*
4569  * Based on:
4570  * Ext JS Library 1.1.1
4571  * Copyright(c) 2006-2007, Ext JS, LLC.
4572  *
4573  * Originally Released Under LGPL - original licence link has changed is not relivant.
4574  *
4575  * Fork - LGPL
4576  * <script type="text/javascript">
4577  */
4578  
4579 /**
4580 * @class Roo.Template
4581 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4582 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4583 * Usage:
4584 <pre><code>
4585 var t = new Roo.Template({
4586     html :  '&lt;div name="{id}"&gt;' + 
4587         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4588         '&lt;/div&gt;',
4589     myformat: function (value, allValues) {
4590         return 'XX' + value;
4591     }
4592 });
4593 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4594 </code></pre>
4595 * For more information see this blog post with examples:
4596 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4597      - Create Elements using DOM, HTML fragments and Templates</a>. 
4598 * @constructor
4599 * @param {Object} cfg - Configuration object.
4600 */
4601 Roo.Template = function(cfg){
4602     // BC!
4603     if(cfg instanceof Array){
4604         cfg = cfg.join("");
4605     }else if(arguments.length > 1){
4606         cfg = Array.prototype.join.call(arguments, "");
4607     }
4608     
4609     
4610     if (typeof(cfg) == 'object') {
4611         Roo.apply(this,cfg)
4612     } else {
4613         // bc
4614         this.html = cfg;
4615     }
4616     if (this.url) {
4617         this.load();
4618     }
4619     
4620 };
4621 Roo.Template.prototype = {
4622     
4623     /**
4624      * @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..
4625      *                    it should be fixed so that template is observable...
4626      */
4627     url : false,
4628     /**
4629      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4630      */
4631     html : '',
4632     /**
4633      * Returns an HTML fragment of this template with the specified values applied.
4634      * @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'})
4635      * @return {String} The HTML fragment
4636      */
4637     applyTemplate : function(values){
4638         try {
4639            
4640             if(this.compiled){
4641                 return this.compiled(values);
4642             }
4643             var useF = this.disableFormats !== true;
4644             var fm = Roo.util.Format, tpl = this;
4645             var fn = function(m, name, format, args){
4646                 if(format && useF){
4647                     if(format.substr(0, 5) == "this."){
4648                         return tpl.call(format.substr(5), values[name], values);
4649                     }else{
4650                         if(args){
4651                             // quoted values are required for strings in compiled templates, 
4652                             // but for non compiled we need to strip them
4653                             // quoted reversed for jsmin
4654                             var re = /^\s*['"](.*)["']\s*$/;
4655                             args = args.split(',');
4656                             for(var i = 0, len = args.length; i < len; i++){
4657                                 args[i] = args[i].replace(re, "$1");
4658                             }
4659                             args = [values[name]].concat(args);
4660                         }else{
4661                             args = [values[name]];
4662                         }
4663                         return fm[format].apply(fm, args);
4664                     }
4665                 }else{
4666                     return values[name] !== undefined ? values[name] : "";
4667                 }
4668             };
4669             return this.html.replace(this.re, fn);
4670         } catch (e) {
4671             Roo.log(e);
4672             throw e;
4673         }
4674          
4675     },
4676     
4677     loading : false,
4678       
4679     load : function ()
4680     {
4681          
4682         if (this.loading) {
4683             return;
4684         }
4685         var _t = this;
4686         
4687         this.loading = true;
4688         this.compiled = false;
4689         
4690         var cx = new Roo.data.Connection();
4691         cx.request({
4692             url : this.url,
4693             method : 'GET',
4694             success : function (response) {
4695                 _t.loading = false;
4696                 _t.html = response.responseText;
4697                 _t.url = false;
4698                 _t.compile();
4699              },
4700             failure : function(response) {
4701                 Roo.log("Template failed to load from " + _t.url);
4702                 _t.loading = false;
4703             }
4704         });
4705     },
4706
4707     /**
4708      * Sets the HTML used as the template and optionally compiles it.
4709      * @param {String} html
4710      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4711      * @return {Roo.Template} this
4712      */
4713     set : function(html, compile){
4714         this.html = html;
4715         this.compiled = null;
4716         if(compile){
4717             this.compile();
4718         }
4719         return this;
4720     },
4721     
4722     /**
4723      * True to disable format functions (defaults to false)
4724      * @type Boolean
4725      */
4726     disableFormats : false,
4727     
4728     /**
4729     * The regular expression used to match template variables 
4730     * @type RegExp
4731     * @property 
4732     */
4733     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4734     
4735     /**
4736      * Compiles the template into an internal function, eliminating the RegEx overhead.
4737      * @return {Roo.Template} this
4738      */
4739     compile : function(){
4740         var fm = Roo.util.Format;
4741         var useF = this.disableFormats !== true;
4742         var sep = Roo.isGecko ? "+" : ",";
4743         var fn = function(m, name, format, args){
4744             if(format && useF){
4745                 args = args ? ',' + args : "";
4746                 if(format.substr(0, 5) != "this."){
4747                     format = "fm." + format + '(';
4748                 }else{
4749                     format = 'this.call("'+ format.substr(5) + '", ';
4750                     args = ", values";
4751                 }
4752             }else{
4753                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4754             }
4755             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4756         };
4757         var body;
4758         // branched to use + in gecko and [].join() in others
4759         if(Roo.isGecko){
4760             body = "this.compiled = function(values){ return '" +
4761                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4762                     "';};";
4763         }else{
4764             body = ["this.compiled = function(values){ return ['"];
4765             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4766             body.push("'].join('');};");
4767             body = body.join('');
4768         }
4769         /**
4770          * eval:var:values
4771          * eval:var:fm
4772          */
4773         eval(body);
4774         return this;
4775     },
4776     
4777     // private function used to call members
4778     call : function(fnName, value, allValues){
4779         return this[fnName](value, allValues);
4780     },
4781     
4782     /**
4783      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4784      * @param {String/HTMLElement/Roo.Element} el The context element
4785      * @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'})
4786      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4787      * @return {HTMLElement/Roo.Element} The new node or Element
4788      */
4789     insertFirst: function(el, values, returnElement){
4790         return this.doInsert('afterBegin', el, values, returnElement);
4791     },
4792
4793     /**
4794      * Applies the supplied values to the template and inserts the new node(s) before el.
4795      * @param {String/HTMLElement/Roo.Element} el The context element
4796      * @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'})
4797      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4798      * @return {HTMLElement/Roo.Element} The new node or Element
4799      */
4800     insertBefore: function(el, values, returnElement){
4801         return this.doInsert('beforeBegin', el, values, returnElement);
4802     },
4803
4804     /**
4805      * Applies the supplied values to the template and inserts the new node(s) after el.
4806      * @param {String/HTMLElement/Roo.Element} el The context element
4807      * @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'})
4808      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4809      * @return {HTMLElement/Roo.Element} The new node or Element
4810      */
4811     insertAfter : function(el, values, returnElement){
4812         return this.doInsert('afterEnd', el, values, returnElement);
4813     },
4814     
4815     /**
4816      * Applies the supplied values to the template and appends the new node(s) to el.
4817      * @param {String/HTMLElement/Roo.Element} el The context element
4818      * @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'})
4819      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4820      * @return {HTMLElement/Roo.Element} The new node or Element
4821      */
4822     append : function(el, values, returnElement){
4823         return this.doInsert('beforeEnd', el, values, returnElement);
4824     },
4825
4826     doInsert : function(where, el, values, returnEl){
4827         el = Roo.getDom(el);
4828         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4829         return returnEl ? Roo.get(newNode, true) : newNode;
4830     },
4831
4832     /**
4833      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4834      * @param {String/HTMLElement/Roo.Element} el The context element
4835      * @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'})
4836      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4837      * @return {HTMLElement/Roo.Element} The new node or Element
4838      */
4839     overwrite : function(el, values, returnElement){
4840         el = Roo.getDom(el);
4841         el.innerHTML = this.applyTemplate(values);
4842         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4843     }
4844 };
4845 /**
4846  * Alias for {@link #applyTemplate}
4847  * @method
4848  */
4849 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4850
4851 // backwards compat
4852 Roo.DomHelper.Template = Roo.Template;
4853
4854 /**
4855  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4856  * @param {String/HTMLElement} el A DOM element or its id
4857  * @returns {Roo.Template} The created template
4858  * @static
4859  */
4860 Roo.Template.from = function(el){
4861     el = Roo.getDom(el);
4862     return new Roo.Template(el.value || el.innerHTML);
4863 };/*
4864  * Based on:
4865  * Ext JS Library 1.1.1
4866  * Copyright(c) 2006-2007, Ext JS, LLC.
4867  *
4868  * Originally Released Under LGPL - original licence link has changed is not relivant.
4869  *
4870  * Fork - LGPL
4871  * <script type="text/javascript">
4872  */
4873  
4874
4875 /*
4876  * This is code is also distributed under MIT license for use
4877  * with jQuery and prototype JavaScript libraries.
4878  */
4879 /**
4880  * @class Roo.DomQuery
4881 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).
4882 <p>
4883 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>
4884
4885 <p>
4886 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.
4887 </p>
4888 <h4>Element Selectors:</h4>
4889 <ul class="list">
4890     <li> <b>*</b> any element</li>
4891     <li> <b>E</b> an element with the tag E</li>
4892     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4893     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4894     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4895     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4896 </ul>
4897 <h4>Attribute Selectors:</h4>
4898 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4899 <ul class="list">
4900     <li> <b>E[foo]</b> has an attribute "foo"</li>
4901     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4902     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4903     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4904     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4905     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4906     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4907 </ul>
4908 <h4>Pseudo Classes:</h4>
4909 <ul class="list">
4910     <li> <b>E:first-child</b> E is the first child of its parent</li>
4911     <li> <b>E:last-child</b> E is the last child of its parent</li>
4912     <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>
4913     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4914     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4915     <li> <b>E:only-child</b> E is the only child of its parent</li>
4916     <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>
4917     <li> <b>E:first</b> the first E in the resultset</li>
4918     <li> <b>E:last</b> the last E in the resultset</li>
4919     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4920     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4921     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4922     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4923     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4924     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4925     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4926     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4927     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4928 </ul>
4929 <h4>CSS Value Selectors:</h4>
4930 <ul class="list">
4931     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4932     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4933     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4934     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4935     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4936     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4937 </ul>
4938  * @singleton
4939  */
4940 Roo.DomQuery = function(){
4941     var cache = {}, simpleCache = {}, valueCache = {};
4942     var nonSpace = /\S/;
4943     var trimRe = /^\s+|\s+$/g;
4944     var tplRe = /\{(\d+)\}/g;
4945     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4946     var tagTokenRe = /^(#)?([\w-\*]+)/;
4947     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4948
4949     function child(p, index){
4950         var i = 0;
4951         var n = p.firstChild;
4952         while(n){
4953             if(n.nodeType == 1){
4954                if(++i == index){
4955                    return n;
4956                }
4957             }
4958             n = n.nextSibling;
4959         }
4960         return null;
4961     };
4962
4963     function next(n){
4964         while((n = n.nextSibling) && n.nodeType != 1);
4965         return n;
4966     };
4967
4968     function prev(n){
4969         while((n = n.previousSibling) && n.nodeType != 1);
4970         return n;
4971     };
4972
4973     function children(d){
4974         var n = d.firstChild, ni = -1;
4975             while(n){
4976                 var nx = n.nextSibling;
4977                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4978                     d.removeChild(n);
4979                 }else{
4980                     n.nodeIndex = ++ni;
4981                 }
4982                 n = nx;
4983             }
4984             return this;
4985         };
4986
4987     function byClassName(c, a, v){
4988         if(!v){
4989             return c;
4990         }
4991         var r = [], ri = -1, cn;
4992         for(var i = 0, ci; ci = c[i]; i++){
4993             if((' '+ci.className+' ').indexOf(v) != -1){
4994                 r[++ri] = ci;
4995             }
4996         }
4997         return r;
4998     };
4999
5000     function attrValue(n, attr){
5001         if(!n.tagName && typeof n.length != "undefined"){
5002             n = n[0];
5003         }
5004         if(!n){
5005             return null;
5006         }
5007         if(attr == "for"){
5008             return n.htmlFor;
5009         }
5010         if(attr == "class" || attr == "className"){
5011             return n.className;
5012         }
5013         return n.getAttribute(attr) || n[attr];
5014
5015     };
5016
5017     function getNodes(ns, mode, tagName){
5018         var result = [], ri = -1, cs;
5019         if(!ns){
5020             return result;
5021         }
5022         tagName = tagName || "*";
5023         if(typeof ns.getElementsByTagName != "undefined"){
5024             ns = [ns];
5025         }
5026         if(!mode){
5027             for(var i = 0, ni; ni = ns[i]; i++){
5028                 cs = ni.getElementsByTagName(tagName);
5029                 for(var j = 0, ci; ci = cs[j]; j++){
5030                     result[++ri] = ci;
5031                 }
5032             }
5033         }else if(mode == "/" || mode == ">"){
5034             var utag = tagName.toUpperCase();
5035             for(var i = 0, ni, cn; ni = ns[i]; i++){
5036                 cn = ni.children || ni.childNodes;
5037                 for(var j = 0, cj; cj = cn[j]; j++){
5038                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5039                         result[++ri] = cj;
5040                     }
5041                 }
5042             }
5043         }else if(mode == "+"){
5044             var utag = tagName.toUpperCase();
5045             for(var i = 0, n; n = ns[i]; i++){
5046                 while((n = n.nextSibling) && n.nodeType != 1);
5047                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5048                     result[++ri] = n;
5049                 }
5050             }
5051         }else if(mode == "~"){
5052             for(var i = 0, n; n = ns[i]; i++){
5053                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5054                 if(n){
5055                     result[++ri] = n;
5056                 }
5057             }
5058         }
5059         return result;
5060     };
5061
5062     function concat(a, b){
5063         if(b.slice){
5064             return a.concat(b);
5065         }
5066         for(var i = 0, l = b.length; i < l; i++){
5067             a[a.length] = b[i];
5068         }
5069         return a;
5070     }
5071
5072     function byTag(cs, tagName){
5073         if(cs.tagName || cs == document){
5074             cs = [cs];
5075         }
5076         if(!tagName){
5077             return cs;
5078         }
5079         var r = [], ri = -1;
5080         tagName = tagName.toLowerCase();
5081         for(var i = 0, ci; ci = cs[i]; i++){
5082             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5083                 r[++ri] = ci;
5084             }
5085         }
5086         return r;
5087     };
5088
5089     function byId(cs, attr, id){
5090         if(cs.tagName || cs == document){
5091             cs = [cs];
5092         }
5093         if(!id){
5094             return cs;
5095         }
5096         var r = [], ri = -1;
5097         for(var i = 0,ci; ci = cs[i]; i++){
5098             if(ci && ci.id == id){
5099                 r[++ri] = ci;
5100                 return r;
5101             }
5102         }
5103         return r;
5104     };
5105
5106     function byAttribute(cs, attr, value, op, custom){
5107         var r = [], ri = -1, st = custom=="{";
5108         var f = Roo.DomQuery.operators[op];
5109         for(var i = 0, ci; ci = cs[i]; i++){
5110             var a;
5111             if(st){
5112                 a = Roo.DomQuery.getStyle(ci, attr);
5113             }
5114             else if(attr == "class" || attr == "className"){
5115                 a = ci.className;
5116             }else if(attr == "for"){
5117                 a = ci.htmlFor;
5118             }else if(attr == "href"){
5119                 a = ci.getAttribute("href", 2);
5120             }else{
5121                 a = ci.getAttribute(attr);
5122             }
5123             if((f && f(a, value)) || (!f && a)){
5124                 r[++ri] = ci;
5125             }
5126         }
5127         return r;
5128     };
5129
5130     function byPseudo(cs, name, value){
5131         return Roo.DomQuery.pseudos[name](cs, value);
5132     };
5133
5134     // This is for IE MSXML which does not support expandos.
5135     // IE runs the same speed using setAttribute, however FF slows way down
5136     // and Safari completely fails so they need to continue to use expandos.
5137     var isIE = window.ActiveXObject ? true : false;
5138
5139     // this eval is stop the compressor from
5140     // renaming the variable to something shorter
5141     
5142     /** eval:var:batch */
5143     var batch = 30803; 
5144
5145     var key = 30803;
5146
5147     function nodupIEXml(cs){
5148         var d = ++key;
5149         cs[0].setAttribute("_nodup", d);
5150         var r = [cs[0]];
5151         for(var i = 1, len = cs.length; i < len; i++){
5152             var c = cs[i];
5153             if(!c.getAttribute("_nodup") != d){
5154                 c.setAttribute("_nodup", d);
5155                 r[r.length] = c;
5156             }
5157         }
5158         for(var i = 0, len = cs.length; i < len; i++){
5159             cs[i].removeAttribute("_nodup");
5160         }
5161         return r;
5162     }
5163
5164     function nodup(cs){
5165         if(!cs){
5166             return [];
5167         }
5168         var len = cs.length, c, i, r = cs, cj, ri = -1;
5169         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5170             return cs;
5171         }
5172         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5173             return nodupIEXml(cs);
5174         }
5175         var d = ++key;
5176         cs[0]._nodup = d;
5177         for(i = 1; c = cs[i]; i++){
5178             if(c._nodup != d){
5179                 c._nodup = d;
5180             }else{
5181                 r = [];
5182                 for(var j = 0; j < i; j++){
5183                     r[++ri] = cs[j];
5184                 }
5185                 for(j = i+1; cj = cs[j]; j++){
5186                     if(cj._nodup != d){
5187                         cj._nodup = d;
5188                         r[++ri] = cj;
5189                     }
5190                 }
5191                 return r;
5192             }
5193         }
5194         return r;
5195     }
5196
5197     function quickDiffIEXml(c1, c2){
5198         var d = ++key;
5199         for(var i = 0, len = c1.length; i < len; i++){
5200             c1[i].setAttribute("_qdiff", d);
5201         }
5202         var r = [];
5203         for(var i = 0, len = c2.length; i < len; i++){
5204             if(c2[i].getAttribute("_qdiff") != d){
5205                 r[r.length] = c2[i];
5206             }
5207         }
5208         for(var i = 0, len = c1.length; i < len; i++){
5209            c1[i].removeAttribute("_qdiff");
5210         }
5211         return r;
5212     }
5213
5214     function quickDiff(c1, c2){
5215         var len1 = c1.length;
5216         if(!len1){
5217             return c2;
5218         }
5219         if(isIE && c1[0].selectSingleNode){
5220             return quickDiffIEXml(c1, c2);
5221         }
5222         var d = ++key;
5223         for(var i = 0; i < len1; i++){
5224             c1[i]._qdiff = d;
5225         }
5226         var r = [];
5227         for(var i = 0, len = c2.length; i < len; i++){
5228             if(c2[i]._qdiff != d){
5229                 r[r.length] = c2[i];
5230             }
5231         }
5232         return r;
5233     }
5234
5235     function quickId(ns, mode, root, id){
5236         if(ns == root){
5237            var d = root.ownerDocument || root;
5238            return d.getElementById(id);
5239         }
5240         ns = getNodes(ns, mode, "*");
5241         return byId(ns, null, id);
5242     }
5243
5244     return {
5245         getStyle : function(el, name){
5246             return Roo.fly(el).getStyle(name);
5247         },
5248         /**
5249          * Compiles a selector/xpath query into a reusable function. The returned function
5250          * takes one parameter "root" (optional), which is the context node from where the query should start.
5251          * @param {String} selector The selector/xpath query
5252          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5253          * @return {Function}
5254          */
5255         compile : function(path, type){
5256             type = type || "select";
5257             
5258             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5259             var q = path, mode, lq;
5260             var tk = Roo.DomQuery.matchers;
5261             var tklen = tk.length;
5262             var mm;
5263
5264             // accept leading mode switch
5265             var lmode = q.match(modeRe);
5266             if(lmode && lmode[1]){
5267                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5268                 q = q.replace(lmode[1], "");
5269             }
5270             // strip leading slashes
5271             while(path.substr(0, 1)=="/"){
5272                 path = path.substr(1);
5273             }
5274
5275             while(q && lq != q){
5276                 lq = q;
5277                 var tm = q.match(tagTokenRe);
5278                 if(type == "select"){
5279                     if(tm){
5280                         if(tm[1] == "#"){
5281                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5282                         }else{
5283                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5284                         }
5285                         q = q.replace(tm[0], "");
5286                     }else if(q.substr(0, 1) != '@'){
5287                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5288                     }
5289                 }else{
5290                     if(tm){
5291                         if(tm[1] == "#"){
5292                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5293                         }else{
5294                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5295                         }
5296                         q = q.replace(tm[0], "");
5297                     }
5298                 }
5299                 while(!(mm = q.match(modeRe))){
5300                     var matched = false;
5301                     for(var j = 0; j < tklen; j++){
5302                         var t = tk[j];
5303                         var m = q.match(t.re);
5304                         if(m){
5305                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5306                                                     return m[i];
5307                                                 });
5308                             q = q.replace(m[0], "");
5309                             matched = true;
5310                             break;
5311                         }
5312                     }
5313                     // prevent infinite loop on bad selector
5314                     if(!matched){
5315                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5316                     }
5317                 }
5318                 if(mm[1]){
5319                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5320                     q = q.replace(mm[1], "");
5321                 }
5322             }
5323             fn[fn.length] = "return nodup(n);\n}";
5324             
5325              /** 
5326               * list of variables that need from compression as they are used by eval.
5327              *  eval:var:batch 
5328              *  eval:var:nodup
5329              *  eval:var:byTag
5330              *  eval:var:ById
5331              *  eval:var:getNodes
5332              *  eval:var:quickId
5333              *  eval:var:mode
5334              *  eval:var:root
5335              *  eval:var:n
5336              *  eval:var:byClassName
5337              *  eval:var:byPseudo
5338              *  eval:var:byAttribute
5339              *  eval:var:attrValue
5340              * 
5341              **/ 
5342             eval(fn.join(""));
5343             return f;
5344         },
5345
5346         /**
5347          * Selects a group of elements.
5348          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @return {Array}
5351          */
5352         select : function(path, root, type){
5353             if(!root || root == document){
5354                 root = document;
5355             }
5356             if(typeof root == "string"){
5357                 root = document.getElementById(root);
5358             }
5359             var paths = path.split(",");
5360             var results = [];
5361             for(var i = 0, len = paths.length; i < len; i++){
5362                 var p = paths[i].replace(trimRe, "");
5363                 if(!cache[p]){
5364                     cache[p] = Roo.DomQuery.compile(p);
5365                     if(!cache[p]){
5366                         throw p + " is not a valid selector";
5367                     }
5368                 }
5369                 var result = cache[p](root);
5370                 if(result && result != document){
5371                     results = results.concat(result);
5372                 }
5373             }
5374             if(paths.length > 1){
5375                 return nodup(results);
5376             }
5377             return results;
5378         },
5379
5380         /**
5381          * Selects a single element.
5382          * @param {String} selector The selector/xpath query
5383          * @param {Node} root (optional) The start of the query (defaults to document).
5384          * @return {Element}
5385          */
5386         selectNode : function(path, root){
5387             return Roo.DomQuery.select(path, root)[0];
5388         },
5389
5390         /**
5391          * Selects the value of a node, optionally replacing null with the defaultValue.
5392          * @param {String} selector The selector/xpath query
5393          * @param {Node} root (optional) The start of the query (defaults to document).
5394          * @param {String} defaultValue
5395          */
5396         selectValue : function(path, root, defaultValue){
5397             path = path.replace(trimRe, "");
5398             if(!valueCache[path]){
5399                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5400             }
5401             var n = valueCache[path](root);
5402             n = n[0] ? n[0] : n;
5403             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5404             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5405         },
5406
5407         /**
5408          * Selects the value of a node, parsing integers and floats.
5409          * @param {String} selector The selector/xpath query
5410          * @param {Node} root (optional) The start of the query (defaults to document).
5411          * @param {Number} defaultValue
5412          * @return {Number}
5413          */
5414         selectNumber : function(path, root, defaultValue){
5415             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5416             return parseFloat(v);
5417         },
5418
5419         /**
5420          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5421          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5422          * @param {String} selector The simple selector to test
5423          * @return {Boolean}
5424          */
5425         is : function(el, ss){
5426             if(typeof el == "string"){
5427                 el = document.getElementById(el);
5428             }
5429             var isArray = (el instanceof Array);
5430             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5431             return isArray ? (result.length == el.length) : (result.length > 0);
5432         },
5433
5434         /**
5435          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5436          * @param {Array} el An array of elements to filter
5437          * @param {String} selector The simple selector to test
5438          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5439          * the selector instead of the ones that match
5440          * @return {Array}
5441          */
5442         filter : function(els, ss, nonMatches){
5443             ss = ss.replace(trimRe, "");
5444             if(!simpleCache[ss]){
5445                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5446             }
5447             var result = simpleCache[ss](els);
5448             return nonMatches ? quickDiff(result, els) : result;
5449         },
5450
5451         /**
5452          * Collection of matching regular expressions and code snippets.
5453          */
5454         matchers : [{
5455                 re: /^\.([\w-]+)/,
5456                 select: 'n = byClassName(n, null, " {1} ");'
5457             }, {
5458                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5459                 select: 'n = byPseudo(n, "{1}", "{2}");'
5460             },{
5461                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5462                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5463             }, {
5464                 re: /^#([\w-]+)/,
5465                 select: 'n = byId(n, null, "{1}");'
5466             },{
5467                 re: /^@([\w-]+)/,
5468                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5469             }
5470         ],
5471
5472         /**
5473          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5474          * 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;.
5475          */
5476         operators : {
5477             "=" : function(a, v){
5478                 return a == v;
5479             },
5480             "!=" : function(a, v){
5481                 return a != v;
5482             },
5483             "^=" : function(a, v){
5484                 return a && a.substr(0, v.length) == v;
5485             },
5486             "$=" : function(a, v){
5487                 return a && a.substr(a.length-v.length) == v;
5488             },
5489             "*=" : function(a, v){
5490                 return a && a.indexOf(v) !== -1;
5491             },
5492             "%=" : function(a, v){
5493                 return (a % v) == 0;
5494             },
5495             "|=" : function(a, v){
5496                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5497             },
5498             "~=" : function(a, v){
5499                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5500             }
5501         },
5502
5503         /**
5504          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5505          * and the argument (if any) supplied in the selector.
5506          */
5507         pseudos : {
5508             "first-child" : function(c){
5509                 var r = [], ri = -1, n;
5510                 for(var i = 0, ci; ci = n = c[i]; i++){
5511                     while((n = n.previousSibling) && n.nodeType != 1);
5512                     if(!n){
5513                         r[++ri] = ci;
5514                     }
5515                 }
5516                 return r;
5517             },
5518
5519             "last-child" : function(c){
5520                 var r = [], ri = -1, n;
5521                 for(var i = 0, ci; ci = n = c[i]; i++){
5522                     while((n = n.nextSibling) && n.nodeType != 1);
5523                     if(!n){
5524                         r[++ri] = ci;
5525                     }
5526                 }
5527                 return r;
5528             },
5529
5530             "nth-child" : function(c, a) {
5531                 var r = [], ri = -1;
5532                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5533                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5534                 for(var i = 0, n; n = c[i]; i++){
5535                     var pn = n.parentNode;
5536                     if (batch != pn._batch) {
5537                         var j = 0;
5538                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5539                             if(cn.nodeType == 1){
5540                                cn.nodeIndex = ++j;
5541                             }
5542                         }
5543                         pn._batch = batch;
5544                     }
5545                     if (f == 1) {
5546                         if (l == 0 || n.nodeIndex == l){
5547                             r[++ri] = n;
5548                         }
5549                     } else if ((n.nodeIndex + l) % f == 0){
5550                         r[++ri] = n;
5551                     }
5552                 }
5553
5554                 return r;
5555             },
5556
5557             "only-child" : function(c){
5558                 var r = [], ri = -1;;
5559                 for(var i = 0, ci; ci = c[i]; i++){
5560                     if(!prev(ci) && !next(ci)){
5561                         r[++ri] = ci;
5562                     }
5563                 }
5564                 return r;
5565             },
5566
5567             "empty" : function(c){
5568                 var r = [], ri = -1;
5569                 for(var i = 0, ci; ci = c[i]; i++){
5570                     var cns = ci.childNodes, j = 0, cn, empty = true;
5571                     while(cn = cns[j]){
5572                         ++j;
5573                         if(cn.nodeType == 1 || cn.nodeType == 3){
5574                             empty = false;
5575                             break;
5576                         }
5577                     }
5578                     if(empty){
5579                         r[++ri] = ci;
5580                     }
5581                 }
5582                 return r;
5583             },
5584
5585             "contains" : function(c, v){
5586                 var r = [], ri = -1;
5587                 for(var i = 0, ci; ci = c[i]; i++){
5588                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5589                         r[++ri] = ci;
5590                     }
5591                 }
5592                 return r;
5593             },
5594
5595             "nodeValue" : function(c, v){
5596                 var r = [], ri = -1;
5597                 for(var i = 0, ci; ci = c[i]; i++){
5598                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5599                         r[++ri] = ci;
5600                     }
5601                 }
5602                 return r;
5603             },
5604
5605             "checked" : function(c){
5606                 var r = [], ri = -1;
5607                 for(var i = 0, ci; ci = c[i]; i++){
5608                     if(ci.checked == true){
5609                         r[++ri] = ci;
5610                     }
5611                 }
5612                 return r;
5613             },
5614
5615             "not" : function(c, ss){
5616                 return Roo.DomQuery.filter(c, ss, true);
5617             },
5618
5619             "odd" : function(c){
5620                 return this["nth-child"](c, "odd");
5621             },
5622
5623             "even" : function(c){
5624                 return this["nth-child"](c, "even");
5625             },
5626
5627             "nth" : function(c, a){
5628                 return c[a-1] || [];
5629             },
5630
5631             "first" : function(c){
5632                 return c[0] || [];
5633             },
5634
5635             "last" : function(c){
5636                 return c[c.length-1] || [];
5637             },
5638
5639             "has" : function(c, ss){
5640                 var s = Roo.DomQuery.select;
5641                 var r = [], ri = -1;
5642                 for(var i = 0, ci; ci = c[i]; i++){
5643                     if(s(ss, ci).length > 0){
5644                         r[++ri] = ci;
5645                     }
5646                 }
5647                 return r;
5648             },
5649
5650             "next" : function(c, ss){
5651                 var is = Roo.DomQuery.is;
5652                 var r = [], ri = -1;
5653                 for(var i = 0, ci; ci = c[i]; i++){
5654                     var n = next(ci);
5655                     if(n && is(n, ss)){
5656                         r[++ri] = ci;
5657                     }
5658                 }
5659                 return r;
5660             },
5661
5662             "prev" : function(c, ss){
5663                 var is = Roo.DomQuery.is;
5664                 var r = [], ri = -1;
5665                 for(var i = 0, ci; ci = c[i]; i++){
5666                     var n = prev(ci);
5667                     if(n && is(n, ss)){
5668                         r[++ri] = ci;
5669                     }
5670                 }
5671                 return r;
5672             }
5673         }
5674     };
5675 }();
5676
5677 /**
5678  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5679  * @param {String} path The selector/xpath query
5680  * @param {Node} root (optional) The start of the query (defaults to document).
5681  * @return {Array}
5682  * @member Roo
5683  * @method query
5684  */
5685 Roo.query = Roo.DomQuery.select;
5686 /*
5687  * Based on:
5688  * Ext JS Library 1.1.1
5689  * Copyright(c) 2006-2007, Ext JS, LLC.
5690  *
5691  * Originally Released Under LGPL - original licence link has changed is not relivant.
5692  *
5693  * Fork - LGPL
5694  * <script type="text/javascript">
5695  */
5696
5697 /**
5698  * @class Roo.util.Observable
5699  * Base class that provides a common interface for publishing events. Subclasses are expected to
5700  * to have a property "events" with all the events defined.<br>
5701  * For example:
5702  * <pre><code>
5703  Employee = function(name){
5704     this.name = name;
5705     this.addEvents({
5706         "fired" : true,
5707         "quit" : true
5708     });
5709  }
5710  Roo.extend(Employee, Roo.util.Observable);
5711 </code></pre>
5712  * @param {Object} config properties to use (incuding events / listeners)
5713  */
5714
5715 Roo.util.Observable = function(cfg){
5716     
5717     cfg = cfg|| {};
5718     this.addEvents(cfg.events || {});
5719     if (cfg.events) {
5720         delete cfg.events; // make sure
5721     }
5722      
5723     Roo.apply(this, cfg);
5724     
5725     if(this.listeners){
5726         this.on(this.listeners);
5727         delete this.listeners;
5728     }
5729 };
5730 Roo.util.Observable.prototype = {
5731     /** 
5732  * @cfg {Object} listeners  list of events and functions to call for this object, 
5733  * For example :
5734  * <pre><code>
5735     listeners :  { 
5736        'click' : function(e) {
5737            ..... 
5738         } ,
5739         .... 
5740     } 
5741   </code></pre>
5742  */
5743     
5744     
5745     /**
5746      * Fires the specified event with the passed parameters (minus the event name).
5747      * @param {String} eventName
5748      * @param {Object...} args Variable number of parameters are passed to handlers
5749      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5750      */
5751     fireEvent : function(){
5752         var ce = this.events[arguments[0].toLowerCase()];
5753         if(typeof ce == "object"){
5754             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5755         }else{
5756             return true;
5757         }
5758     },
5759
5760     // private
5761     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5762
5763     /**
5764      * Appends an event handler to this component
5765      * @param {String}   eventName The type of event to listen for
5766      * @param {Function} handler The method the event invokes
5767      * @param {Object}   scope (optional) The scope in which to execute the handler
5768      * function. The handler function's "this" context.
5769      * @param {Object}   options (optional) An object containing handler configuration
5770      * properties. This may contain any of the following properties:<ul>
5771      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5772      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5773      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5774      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5775      * by the specified number of milliseconds. If the event fires again within that time, the original
5776      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5777      * </ul><br>
5778      * <p>
5779      * <b>Combining Options</b><br>
5780      * Using the options argument, it is possible to combine different types of listeners:<br>
5781      * <br>
5782      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5783                 <pre><code>
5784                 el.on('click', this.onClick, this, {
5785                         single: true,
5786                 delay: 100,
5787                 forumId: 4
5788                 });
5789                 </code></pre>
5790      * <p>
5791      * <b>Attaching multiple handlers in 1 call</b><br>
5792      * The method also allows for a single argument to be passed which is a config object containing properties
5793      * which specify multiple handlers.
5794      * <pre><code>
5795                 el.on({
5796                         'click': {
5797                         fn: this.onClick,
5798                         scope: this,
5799                         delay: 100
5800                 }, 
5801                 'mouseover': {
5802                         fn: this.onMouseOver,
5803                         scope: this
5804                 },
5805                 'mouseout': {
5806                         fn: this.onMouseOut,
5807                         scope: this
5808                 }
5809                 });
5810                 </code></pre>
5811      * <p>
5812      * Or a shorthand syntax which passes the same scope object to all handlers:
5813         <pre><code>
5814                 el.on({
5815                         'click': this.onClick,
5816                 'mouseover': this.onMouseOver,
5817                 'mouseout': this.onMouseOut,
5818                 scope: this
5819                 });
5820                 </code></pre>
5821      */
5822     addListener : function(eventName, fn, scope, o){
5823         if(typeof eventName == "object"){
5824             o = eventName;
5825             for(var e in o){
5826                 if(this.filterOptRe.test(e)){
5827                     continue;
5828                 }
5829                 if(typeof o[e] == "function"){
5830                     // shared options
5831                     this.addListener(e, o[e], o.scope,  o);
5832                 }else{
5833                     // individual options
5834                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5835                 }
5836             }
5837             return;
5838         }
5839         o = (!o || typeof o == "boolean") ? {} : o;
5840         eventName = eventName.toLowerCase();
5841         var ce = this.events[eventName] || true;
5842         if(typeof ce == "boolean"){
5843             ce = new Roo.util.Event(this, eventName);
5844             this.events[eventName] = ce;
5845         }
5846         ce.addListener(fn, scope, o);
5847     },
5848
5849     /**
5850      * Removes a listener
5851      * @param {String}   eventName     The type of event to listen for
5852      * @param {Function} handler        The handler to remove
5853      * @param {Object}   scope  (optional) The scope (this object) for the handler
5854      */
5855     removeListener : function(eventName, fn, scope){
5856         var ce = this.events[eventName.toLowerCase()];
5857         if(typeof ce == "object"){
5858             ce.removeListener(fn, scope);
5859         }
5860     },
5861
5862     /**
5863      * Removes all listeners for this object
5864      */
5865     purgeListeners : function(){
5866         for(var evt in this.events){
5867             if(typeof this.events[evt] == "object"){
5868                  this.events[evt].clearListeners();
5869             }
5870         }
5871     },
5872
5873     relayEvents : function(o, events){
5874         var createHandler = function(ename){
5875             return function(){
5876                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5877             };
5878         };
5879         for(var i = 0, len = events.length; i < len; i++){
5880             var ename = events[i];
5881             if(!this.events[ename]){ this.events[ename] = true; };
5882             o.on(ename, createHandler(ename), this);
5883         }
5884     },
5885
5886     /**
5887      * Used to define events on this Observable
5888      * @param {Object} object The object with the events defined
5889      */
5890     addEvents : function(o){
5891         if(!this.events){
5892             this.events = {};
5893         }
5894         Roo.applyIf(this.events, o);
5895     },
5896
5897     /**
5898      * Checks to see if this object has any listeners for a specified event
5899      * @param {String} eventName The name of the event to check for
5900      * @return {Boolean} True if the event is being listened for, else false
5901      */
5902     hasListener : function(eventName){
5903         var e = this.events[eventName];
5904         return typeof e == "object" && e.listeners.length > 0;
5905     }
5906 };
5907 /**
5908  * Appends an event handler to this element (shorthand for addListener)
5909  * @param {String}   eventName     The type of event to listen for
5910  * @param {Function} handler        The method the event invokes
5911  * @param {Object}   scope (optional) The scope in which to execute the handler
5912  * function. The handler function's "this" context.
5913  * @param {Object}   options  (optional)
5914  * @method
5915  */
5916 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5917 /**
5918  * Removes a listener (shorthand for removeListener)
5919  * @param {String}   eventName     The type of event to listen for
5920  * @param {Function} handler        The handler to remove
5921  * @param {Object}   scope  (optional) The scope (this object) for the handler
5922  * @method
5923  */
5924 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5925
5926 /**
5927  * Starts capture on the specified Observable. All events will be passed
5928  * to the supplied function with the event name + standard signature of the event
5929  * <b>before</b> the event is fired. If the supplied function returns false,
5930  * the event will not fire.
5931  * @param {Observable} o The Observable to capture
5932  * @param {Function} fn The function to call
5933  * @param {Object} scope (optional) The scope (this object) for the fn
5934  * @static
5935  */
5936 Roo.util.Observable.capture = function(o, fn, scope){
5937     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5938 };
5939
5940 /**
5941  * Removes <b>all</b> added captures from the Observable.
5942  * @param {Observable} o The Observable to release
5943  * @static
5944  */
5945 Roo.util.Observable.releaseCapture = function(o){
5946     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5947 };
5948
5949 (function(){
5950
5951     var createBuffered = function(h, o, scope){
5952         var task = new Roo.util.DelayedTask();
5953         return function(){
5954             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5955         };
5956     };
5957
5958     var createSingle = function(h, e, fn, scope){
5959         return function(){
5960             e.removeListener(fn, scope);
5961             return h.apply(scope, arguments);
5962         };
5963     };
5964
5965     var createDelayed = function(h, o, scope){
5966         return function(){
5967             var args = Array.prototype.slice.call(arguments, 0);
5968             setTimeout(function(){
5969                 h.apply(scope, args);
5970             }, o.delay || 10);
5971         };
5972     };
5973
5974     Roo.util.Event = function(obj, name){
5975         this.name = name;
5976         this.obj = obj;
5977         this.listeners = [];
5978     };
5979
5980     Roo.util.Event.prototype = {
5981         addListener : function(fn, scope, options){
5982             var o = options || {};
5983             scope = scope || this.obj;
5984             if(!this.isListening(fn, scope)){
5985                 var l = {fn: fn, scope: scope, options: o};
5986                 var h = fn;
5987                 if(o.delay){
5988                     h = createDelayed(h, o, scope);
5989                 }
5990                 if(o.single){
5991                     h = createSingle(h, this, fn, scope);
5992                 }
5993                 if(o.buffer){
5994                     h = createBuffered(h, o, scope);
5995                 }
5996                 l.fireFn = h;
5997                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5998                     this.listeners.push(l);
5999                 }else{
6000                     this.listeners = this.listeners.slice(0);
6001                     this.listeners.push(l);
6002                 }
6003             }
6004         },
6005
6006         findListener : function(fn, scope){
6007             scope = scope || this.obj;
6008             var ls = this.listeners;
6009             for(var i = 0, len = ls.length; i < len; i++){
6010                 var l = ls[i];
6011                 if(l.fn == fn && l.scope == scope){
6012                     return i;
6013                 }
6014             }
6015             return -1;
6016         },
6017
6018         isListening : function(fn, scope){
6019             return this.findListener(fn, scope) != -1;
6020         },
6021
6022         removeListener : function(fn, scope){
6023             var index;
6024             if((index = this.findListener(fn, scope)) != -1){
6025                 if(!this.firing){
6026                     this.listeners.splice(index, 1);
6027                 }else{
6028                     this.listeners = this.listeners.slice(0);
6029                     this.listeners.splice(index, 1);
6030                 }
6031                 return true;
6032             }
6033             return false;
6034         },
6035
6036         clearListeners : function(){
6037             this.listeners = [];
6038         },
6039
6040         fire : function(){
6041             var ls = this.listeners, scope, len = ls.length;
6042             if(len > 0){
6043                 this.firing = true;
6044                 var args = Array.prototype.slice.call(arguments, 0);
6045                 for(var i = 0; i < len; i++){
6046                     var l = ls[i];
6047                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6048                         this.firing = false;
6049                         return false;
6050                     }
6051                 }
6052                 this.firing = false;
6053             }
6054             return true;
6055         }
6056     };
6057 })();/*
6058  * RooJS Library 
6059  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6060  *
6061  * Licence LGPL 
6062  *
6063  */
6064  
6065 /**
6066  * @class Roo.Document
6067  * @extends Roo.util.Observable
6068  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6069  * 
6070  * @param {Object} config the methods and properties of the 'base' class for the application.
6071  * 
6072  *  Generic Page handler - implement this to start your app..
6073  * 
6074  * eg.
6075  *  MyProject = new Roo.Document({
6076         events : {
6077             'load' : true // your events..
6078         },
6079         listeners : {
6080             'ready' : function() {
6081                 // fired on Roo.onReady()
6082             }
6083         }
6084  * 
6085  */
6086 Roo.Document = function(cfg) {
6087      
6088     this.addEvents({ 
6089         'ready' : true
6090     });
6091     Roo.util.Observable.call(this,cfg);
6092     
6093     var _this = this;
6094     
6095     Roo.onReady(function() {
6096         _this.fireEvent('ready');
6097     },null,false);
6098     
6099     
6100 }
6101
6102 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6103  * Based on:
6104  * Ext JS Library 1.1.1
6105  * Copyright(c) 2006-2007, Ext JS, LLC.
6106  *
6107  * Originally Released Under LGPL - original licence link has changed is not relivant.
6108  *
6109  * Fork - LGPL
6110  * <script type="text/javascript">
6111  */
6112
6113 /**
6114  * @class Roo.EventManager
6115  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6116  * several useful events directly.
6117  * See {@link Roo.EventObject} for more details on normalized event objects.
6118  * @singleton
6119  */
6120 Roo.EventManager = function(){
6121     var docReadyEvent, docReadyProcId, docReadyState = false;
6122     var resizeEvent, resizeTask, textEvent, textSize;
6123     var E = Roo.lib.Event;
6124     var D = Roo.lib.Dom;
6125
6126     
6127     
6128
6129     var fireDocReady = function(){
6130         if(!docReadyState){
6131             docReadyState = true;
6132             Roo.isReady = true;
6133             if(docReadyProcId){
6134                 clearInterval(docReadyProcId);
6135             }
6136             if(Roo.isGecko || Roo.isOpera) {
6137                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6138             }
6139             if(Roo.isIE){
6140                 var defer = document.getElementById("ie-deferred-loader");
6141                 if(defer){
6142                     defer.onreadystatechange = null;
6143                     defer.parentNode.removeChild(defer);
6144                 }
6145             }
6146             if(docReadyEvent){
6147                 docReadyEvent.fire();
6148                 docReadyEvent.clearListeners();
6149             }
6150         }
6151     };
6152     
6153     var initDocReady = function(){
6154         docReadyEvent = new Roo.util.Event();
6155         if(Roo.isGecko || Roo.isOpera) {
6156             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6157         }else if(Roo.isIE){
6158             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6159             var defer = document.getElementById("ie-deferred-loader");
6160             defer.onreadystatechange = function(){
6161                 if(this.readyState == "complete"){
6162                     fireDocReady();
6163                 }
6164             };
6165         }else if(Roo.isSafari){ 
6166             docReadyProcId = setInterval(function(){
6167                 var rs = document.readyState;
6168                 if(rs == "complete") {
6169                     fireDocReady();     
6170                  }
6171             }, 10);
6172         }
6173         // no matter what, make sure it fires on load
6174         E.on(window, "load", fireDocReady);
6175     };
6176
6177     var createBuffered = function(h, o){
6178         var task = new Roo.util.DelayedTask(h);
6179         return function(e){
6180             // create new event object impl so new events don't wipe out properties
6181             e = new Roo.EventObjectImpl(e);
6182             task.delay(o.buffer, h, null, [e]);
6183         };
6184     };
6185
6186     var createSingle = function(h, el, ename, fn){
6187         return function(e){
6188             Roo.EventManager.removeListener(el, ename, fn);
6189             h(e);
6190         };
6191     };
6192
6193     var createDelayed = function(h, o){
6194         return function(e){
6195             // create new event object impl so new events don't wipe out properties
6196             e = new Roo.EventObjectImpl(e);
6197             setTimeout(function(){
6198                 h(e);
6199             }, o.delay || 10);
6200         };
6201     };
6202     var transitionEndVal = false;
6203     
6204     var transitionEnd = function()
6205     {
6206         if (transitionEndVal) {
6207             return transitionEndVal;
6208         }
6209         var el = document.createElement('div');
6210
6211         var transEndEventNames = {
6212             WebkitTransition : 'webkitTransitionEnd',
6213             MozTransition    : 'transitionend',
6214             OTransition      : 'oTransitionEnd otransitionend',
6215             transition       : 'transitionend'
6216         };
6217     
6218         for (var name in transEndEventNames) {
6219             if (el.style[name] !== undefined) {
6220                 transitionEndVal = transEndEventNames[name];
6221                 return  transitionEndVal ;
6222             }
6223         }
6224     }
6225     
6226
6227     var listen = function(element, ename, opt, fn, scope){
6228         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6229         fn = fn || o.fn; scope = scope || o.scope;
6230         var el = Roo.getDom(element);
6231         
6232         
6233         if(!el){
6234             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6235         }
6236         
6237         if (ename == 'transitionend') {
6238             ename = transitionEnd();
6239         }
6240         var h = function(e){
6241             e = Roo.EventObject.setEvent(e);
6242             var t;
6243             if(o.delegate){
6244                 t = e.getTarget(o.delegate, el);
6245                 if(!t){
6246                     return;
6247                 }
6248             }else{
6249                 t = e.target;
6250             }
6251             if(o.stopEvent === true){
6252                 e.stopEvent();
6253             }
6254             if(o.preventDefault === true){
6255                e.preventDefault();
6256             }
6257             if(o.stopPropagation === true){
6258                 e.stopPropagation();
6259             }
6260
6261             if(o.normalized === false){
6262                 e = e.browserEvent;
6263             }
6264
6265             fn.call(scope || el, e, t, o);
6266         };
6267         if(o.delay){
6268             h = createDelayed(h, o);
6269         }
6270         if(o.single){
6271             h = createSingle(h, el, ename, fn);
6272         }
6273         if(o.buffer){
6274             h = createBuffered(h, o);
6275         }
6276         fn._handlers = fn._handlers || [];
6277         
6278         
6279         fn._handlers.push([Roo.id(el), ename, h]);
6280         
6281         
6282          
6283         E.on(el, ename, h);
6284         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6285             el.addEventListener("DOMMouseScroll", h, false);
6286             E.on(window, 'unload', function(){
6287                 el.removeEventListener("DOMMouseScroll", h, false);
6288             });
6289         }
6290         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6291             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6292         }
6293         return h;
6294     };
6295
6296     var stopListening = function(el, ename, fn){
6297         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6298         if(hds){
6299             for(var i = 0, len = hds.length; i < len; i++){
6300                 var h = hds[i];
6301                 if(h[0] == id && h[1] == ename){
6302                     hd = h[2];
6303                     hds.splice(i, 1);
6304                     break;
6305                 }
6306             }
6307         }
6308         E.un(el, ename, hd);
6309         el = Roo.getDom(el);
6310         if(ename == "mousewheel" && el.addEventListener){
6311             el.removeEventListener("DOMMouseScroll", hd, false);
6312         }
6313         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6314             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6315         }
6316     };
6317
6318     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6319     
6320     var pub = {
6321         
6322         
6323         /** 
6324          * Fix for doc tools
6325          * @scope Roo.EventManager
6326          */
6327         
6328         
6329         /** 
6330          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6331          * object with a Roo.EventObject
6332          * @param {Function} fn        The method the event invokes
6333          * @param {Object}   scope    An object that becomes the scope of the handler
6334          * @param {boolean}  override If true, the obj passed in becomes
6335          *                             the execution scope of the listener
6336          * @return {Function} The wrapped function
6337          * @deprecated
6338          */
6339         wrap : function(fn, scope, override){
6340             return function(e){
6341                 Roo.EventObject.setEvent(e);
6342                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6343             };
6344         },
6345         
6346         /**
6347      * Appends an event handler to an element (shorthand for addListener)
6348      * @param {String/HTMLElement}   element        The html element or id to assign the
6349      * @param {String}   eventName The type of event to listen for
6350      * @param {Function} handler The method the event invokes
6351      * @param {Object}   scope (optional) The scope in which to execute the handler
6352      * function. The handler function's "this" context.
6353      * @param {Object}   options (optional) An object containing handler configuration
6354      * properties. This may contain any of the following properties:<ul>
6355      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6356      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6357      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6358      * <li>preventDefault {Boolean} True to prevent the default action</li>
6359      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6360      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6361      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6362      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6363      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6364      * by the specified number of milliseconds. If the event fires again within that time, the original
6365      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6366      * </ul><br>
6367      * <p>
6368      * <b>Combining Options</b><br>
6369      * Using the options argument, it is possible to combine different types of listeners:<br>
6370      * <br>
6371      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6372      * Code:<pre><code>
6373 el.on('click', this.onClick, this, {
6374     single: true,
6375     delay: 100,
6376     stopEvent : true,
6377     forumId: 4
6378 });</code></pre>
6379      * <p>
6380      * <b>Attaching multiple handlers in 1 call</b><br>
6381       * The method also allows for a single argument to be passed which is a config object containing properties
6382      * which specify multiple handlers.
6383      * <p>
6384      * Code:<pre><code>
6385 el.on({
6386     'click' : {
6387         fn: this.onClick
6388         scope: this,
6389         delay: 100
6390     },
6391     'mouseover' : {
6392         fn: this.onMouseOver
6393         scope: this
6394     },
6395     'mouseout' : {
6396         fn: this.onMouseOut
6397         scope: this
6398     }
6399 });</code></pre>
6400      * <p>
6401      * Or a shorthand syntax:<br>
6402      * Code:<pre><code>
6403 el.on({
6404     'click' : this.onClick,
6405     'mouseover' : this.onMouseOver,
6406     'mouseout' : this.onMouseOut
6407     scope: this
6408 });</code></pre>
6409      */
6410         addListener : function(element, eventName, fn, scope, options){
6411             if(typeof eventName == "object"){
6412                 var o = eventName;
6413                 for(var e in o){
6414                     if(propRe.test(e)){
6415                         continue;
6416                     }
6417                     if(typeof o[e] == "function"){
6418                         // shared options
6419                         listen(element, e, o, o[e], o.scope);
6420                     }else{
6421                         // individual options
6422                         listen(element, e, o[e]);
6423                     }
6424                 }
6425                 return;
6426             }
6427             return listen(element, eventName, options, fn, scope);
6428         },
6429         
6430         /**
6431          * Removes an event handler
6432          *
6433          * @param {String/HTMLElement}   element        The id or html element to remove the 
6434          *                             event from
6435          * @param {String}   eventName     The type of event
6436          * @param {Function} fn
6437          * @return {Boolean} True if a listener was actually removed
6438          */
6439         removeListener : function(element, eventName, fn){
6440             return stopListening(element, eventName, fn);
6441         },
6442         
6443         /**
6444          * Fires when the document is ready (before onload and before images are loaded). Can be 
6445          * accessed shorthanded Roo.onReady().
6446          * @param {Function} fn        The method the event invokes
6447          * @param {Object}   scope    An  object that becomes the scope of the handler
6448          * @param {boolean}  options
6449          */
6450         onDocumentReady : function(fn, scope, options){
6451             if(docReadyState){ // if it already fired
6452                 docReadyEvent.addListener(fn, scope, options);
6453                 docReadyEvent.fire();
6454                 docReadyEvent.clearListeners();
6455                 return;
6456             }
6457             if(!docReadyEvent){
6458                 initDocReady();
6459             }
6460             docReadyEvent.addListener(fn, scope, options);
6461         },
6462         
6463         /**
6464          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6465          * @param {Function} fn        The method the event invokes
6466          * @param {Object}   scope    An object that becomes the scope of the handler
6467          * @param {boolean}  options
6468          */
6469         onWindowResize : function(fn, scope, options){
6470             if(!resizeEvent){
6471                 resizeEvent = new Roo.util.Event();
6472                 resizeTask = new Roo.util.DelayedTask(function(){
6473                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6474                 });
6475                 E.on(window, "resize", function(){
6476                     if(Roo.isIE){
6477                         resizeTask.delay(50);
6478                     }else{
6479                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6480                     }
6481                 });
6482             }
6483             resizeEvent.addListener(fn, scope, options);
6484         },
6485
6486         /**
6487          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6488          * @param {Function} fn        The method the event invokes
6489          * @param {Object}   scope    An object that becomes the scope of the handler
6490          * @param {boolean}  options
6491          */
6492         onTextResize : function(fn, scope, options){
6493             if(!textEvent){
6494                 textEvent = new Roo.util.Event();
6495                 var textEl = new Roo.Element(document.createElement('div'));
6496                 textEl.dom.className = 'x-text-resize';
6497                 textEl.dom.innerHTML = 'X';
6498                 textEl.appendTo(document.body);
6499                 textSize = textEl.dom.offsetHeight;
6500                 setInterval(function(){
6501                     if(textEl.dom.offsetHeight != textSize){
6502                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6503                     }
6504                 }, this.textResizeInterval);
6505             }
6506             textEvent.addListener(fn, scope, options);
6507         },
6508
6509         /**
6510          * Removes the passed window resize listener.
6511          * @param {Function} fn        The method the event invokes
6512          * @param {Object}   scope    The scope of handler
6513          */
6514         removeResizeListener : function(fn, scope){
6515             if(resizeEvent){
6516                 resizeEvent.removeListener(fn, scope);
6517             }
6518         },
6519
6520         // private
6521         fireResize : function(){
6522             if(resizeEvent){
6523                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6524             }   
6525         },
6526         /**
6527          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6528          */
6529         ieDeferSrc : false,
6530         /**
6531          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6532          */
6533         textResizeInterval : 50
6534     };
6535     
6536     /**
6537      * Fix for doc tools
6538      * @scopeAlias pub=Roo.EventManager
6539      */
6540     
6541      /**
6542      * Appends an event handler to an element (shorthand for addListener)
6543      * @param {String/HTMLElement}   element        The html element or id to assign the
6544      * @param {String}   eventName The type of event to listen for
6545      * @param {Function} handler The method the event invokes
6546      * @param {Object}   scope (optional) The scope in which to execute the handler
6547      * function. The handler function's "this" context.
6548      * @param {Object}   options (optional) An object containing handler configuration
6549      * properties. This may contain any of the following properties:<ul>
6550      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6551      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6552      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6553      * <li>preventDefault {Boolean} True to prevent the default action</li>
6554      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6555      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6556      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6557      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6558      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6559      * by the specified number of milliseconds. If the event fires again within that time, the original
6560      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6561      * </ul><br>
6562      * <p>
6563      * <b>Combining Options</b><br>
6564      * Using the options argument, it is possible to combine different types of listeners:<br>
6565      * <br>
6566      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6567      * Code:<pre><code>
6568 el.on('click', this.onClick, this, {
6569     single: true,
6570     delay: 100,
6571     stopEvent : true,
6572     forumId: 4
6573 });</code></pre>
6574      * <p>
6575      * <b>Attaching multiple handlers in 1 call</b><br>
6576       * The method also allows for a single argument to be passed which is a config object containing properties
6577      * which specify multiple handlers.
6578      * <p>
6579      * Code:<pre><code>
6580 el.on({
6581     'click' : {
6582         fn: this.onClick
6583         scope: this,
6584         delay: 100
6585     },
6586     'mouseover' : {
6587         fn: this.onMouseOver
6588         scope: this
6589     },
6590     'mouseout' : {
6591         fn: this.onMouseOut
6592         scope: this
6593     }
6594 });</code></pre>
6595      * <p>
6596      * Or a shorthand syntax:<br>
6597      * Code:<pre><code>
6598 el.on({
6599     'click' : this.onClick,
6600     'mouseover' : this.onMouseOver,
6601     'mouseout' : this.onMouseOut
6602     scope: this
6603 });</code></pre>
6604      */
6605     pub.on = pub.addListener;
6606     pub.un = pub.removeListener;
6607
6608     pub.stoppedMouseDownEvent = new Roo.util.Event();
6609     return pub;
6610 }();
6611 /**
6612   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6613   * @param {Function} fn        The method the event invokes
6614   * @param {Object}   scope    An  object that becomes the scope of the handler
6615   * @param {boolean}  override If true, the obj passed in becomes
6616   *                             the execution scope of the listener
6617   * @member Roo
6618   * @method onReady
6619  */
6620 Roo.onReady = Roo.EventManager.onDocumentReady;
6621
6622 Roo.onReady(function(){
6623     var bd = Roo.get(document.body);
6624     if(!bd){ return; }
6625
6626     var cls = [
6627             Roo.isIE ? "roo-ie"
6628             : Roo.isGecko ? "roo-gecko"
6629             : Roo.isOpera ? "roo-opera"
6630             : Roo.isSafari ? "roo-safari" : ""];
6631
6632     if(Roo.isMac){
6633         cls.push("roo-mac");
6634     }
6635     if(Roo.isLinux){
6636         cls.push("roo-linux");
6637     }
6638     if(Roo.isIOS){
6639         cls.push("roo-ios");
6640     }
6641     if(Roo.isTouch){
6642         cls.push("roo-touch");
6643     }
6644     if(Roo.isBorderBox){
6645         cls.push('roo-border-box');
6646     }
6647     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6648         var p = bd.dom.parentNode;
6649         if(p){
6650             p.className += ' roo-strict';
6651         }
6652     }
6653     bd.addClass(cls.join(' '));
6654 });
6655
6656 /**
6657  * @class Roo.EventObject
6658  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6659  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6660  * Example:
6661  * <pre><code>
6662  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6663     e.preventDefault();
6664     var target = e.getTarget();
6665     ...
6666  }
6667  var myDiv = Roo.get("myDiv");
6668  myDiv.on("click", handleClick);
6669  //or
6670  Roo.EventManager.on("myDiv", 'click', handleClick);
6671  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6672  </code></pre>
6673  * @singleton
6674  */
6675 Roo.EventObject = function(){
6676     
6677     var E = Roo.lib.Event;
6678     
6679     // safari keypress events for special keys return bad keycodes
6680     var safariKeys = {
6681         63234 : 37, // left
6682         63235 : 39, // right
6683         63232 : 38, // up
6684         63233 : 40, // down
6685         63276 : 33, // page up
6686         63277 : 34, // page down
6687         63272 : 46, // delete
6688         63273 : 36, // home
6689         63275 : 35  // end
6690     };
6691
6692     // normalize button clicks
6693     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6694                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6695
6696     Roo.EventObjectImpl = function(e){
6697         if(e){
6698             this.setEvent(e.browserEvent || e);
6699         }
6700     };
6701     Roo.EventObjectImpl.prototype = {
6702         /**
6703          * Used to fix doc tools.
6704          * @scope Roo.EventObject.prototype
6705          */
6706             
6707
6708         
6709         
6710         /** The normal browser event */
6711         browserEvent : null,
6712         /** The button pressed in a mouse event */
6713         button : -1,
6714         /** True if the shift key was down during the event */
6715         shiftKey : false,
6716         /** True if the control key was down during the event */
6717         ctrlKey : false,
6718         /** True if the alt key was down during the event */
6719         altKey : false,
6720
6721         /** Key constant 
6722         * @type Number */
6723         BACKSPACE : 8,
6724         /** Key constant 
6725         * @type Number */
6726         TAB : 9,
6727         /** Key constant 
6728         * @type Number */
6729         RETURN : 13,
6730         /** Key constant 
6731         * @type Number */
6732         ENTER : 13,
6733         /** Key constant 
6734         * @type Number */
6735         SHIFT : 16,
6736         /** Key constant 
6737         * @type Number */
6738         CONTROL : 17,
6739         /** Key constant 
6740         * @type Number */
6741         ESC : 27,
6742         /** Key constant 
6743         * @type Number */
6744         SPACE : 32,
6745         /** Key constant 
6746         * @type Number */
6747         PAGEUP : 33,
6748         /** Key constant 
6749         * @type Number */
6750         PAGEDOWN : 34,
6751         /** Key constant 
6752         * @type Number */
6753         END : 35,
6754         /** Key constant 
6755         * @type Number */
6756         HOME : 36,
6757         /** Key constant 
6758         * @type Number */
6759         LEFT : 37,
6760         /** Key constant 
6761         * @type Number */
6762         UP : 38,
6763         /** Key constant 
6764         * @type Number */
6765         RIGHT : 39,
6766         /** Key constant 
6767         * @type Number */
6768         DOWN : 40,
6769         /** Key constant 
6770         * @type Number */
6771         DELETE : 46,
6772         /** Key constant 
6773         * @type Number */
6774         F5 : 116,
6775
6776            /** @private */
6777         setEvent : function(e){
6778             if(e == this || (e && e.browserEvent)){ // already wrapped
6779                 return e;
6780             }
6781             this.browserEvent = e;
6782             if(e){
6783                 // normalize buttons
6784                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6785                 if(e.type == 'click' && this.button == -1){
6786                     this.button = 0;
6787                 }
6788                 this.type = e.type;
6789                 this.shiftKey = e.shiftKey;
6790                 // mac metaKey behaves like ctrlKey
6791                 this.ctrlKey = e.ctrlKey || e.metaKey;
6792                 this.altKey = e.altKey;
6793                 // in getKey these will be normalized for the mac
6794                 this.keyCode = e.keyCode;
6795                 // keyup warnings on firefox.
6796                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6797                 // cache the target for the delayed and or buffered events
6798                 this.target = E.getTarget(e);
6799                 // same for XY
6800                 this.xy = E.getXY(e);
6801             }else{
6802                 this.button = -1;
6803                 this.shiftKey = false;
6804                 this.ctrlKey = false;
6805                 this.altKey = false;
6806                 this.keyCode = 0;
6807                 this.charCode =0;
6808                 this.target = null;
6809                 this.xy = [0, 0];
6810             }
6811             return this;
6812         },
6813
6814         /**
6815          * Stop the event (preventDefault and stopPropagation)
6816          */
6817         stopEvent : function(){
6818             if(this.browserEvent){
6819                 if(this.browserEvent.type == 'mousedown'){
6820                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6821                 }
6822                 E.stopEvent(this.browserEvent);
6823             }
6824         },
6825
6826         /**
6827          * Prevents the browsers default handling of the event.
6828          */
6829         preventDefault : function(){
6830             if(this.browserEvent){
6831                 E.preventDefault(this.browserEvent);
6832             }
6833         },
6834
6835         /** @private */
6836         isNavKeyPress : function(){
6837             var k = this.keyCode;
6838             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6839             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6840         },
6841
6842         isSpecialKey : function(){
6843             var k = this.keyCode;
6844             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6845             (k == 16) || (k == 17) ||
6846             (k >= 18 && k <= 20) ||
6847             (k >= 33 && k <= 35) ||
6848             (k >= 36 && k <= 39) ||
6849             (k >= 44 && k <= 45);
6850         },
6851         /**
6852          * Cancels bubbling of the event.
6853          */
6854         stopPropagation : function(){
6855             if(this.browserEvent){
6856                 if(this.type == 'mousedown'){
6857                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6858                 }
6859                 E.stopPropagation(this.browserEvent);
6860             }
6861         },
6862
6863         /**
6864          * Gets the key code for the event.
6865          * @return {Number}
6866          */
6867         getCharCode : function(){
6868             return this.charCode || this.keyCode;
6869         },
6870
6871         /**
6872          * Returns a normalized keyCode for the event.
6873          * @return {Number} The key code
6874          */
6875         getKey : function(){
6876             var k = this.keyCode || this.charCode;
6877             return Roo.isSafari ? (safariKeys[k] || k) : k;
6878         },
6879
6880         /**
6881          * Gets the x coordinate of the event.
6882          * @return {Number}
6883          */
6884         getPageX : function(){
6885             return this.xy[0];
6886         },
6887
6888         /**
6889          * Gets the y coordinate of the event.
6890          * @return {Number}
6891          */
6892         getPageY : function(){
6893             return this.xy[1];
6894         },
6895
6896         /**
6897          * Gets the time of the event.
6898          * @return {Number}
6899          */
6900         getTime : function(){
6901             if(this.browserEvent){
6902                 return E.getTime(this.browserEvent);
6903             }
6904             return null;
6905         },
6906
6907         /**
6908          * Gets the page coordinates of the event.
6909          * @return {Array} The xy values like [x, y]
6910          */
6911         getXY : function(){
6912             return this.xy;
6913         },
6914
6915         /**
6916          * Gets the target for the event.
6917          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6918          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6919                 search as a number or element (defaults to 10 || document.body)
6920          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6921          * @return {HTMLelement}
6922          */
6923         getTarget : function(selector, maxDepth, returnEl){
6924             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6925         },
6926         /**
6927          * Gets the related target.
6928          * @return {HTMLElement}
6929          */
6930         getRelatedTarget : function(){
6931             if(this.browserEvent){
6932                 return E.getRelatedTarget(this.browserEvent);
6933             }
6934             return null;
6935         },
6936
6937         /**
6938          * Normalizes mouse wheel delta across browsers
6939          * @return {Number} The delta
6940          */
6941         getWheelDelta : function(){
6942             var e = this.browserEvent;
6943             var delta = 0;
6944             if(e.wheelDelta){ /* IE/Opera. */
6945                 delta = e.wheelDelta/120;
6946             }else if(e.detail){ /* Mozilla case. */
6947                 delta = -e.detail/3;
6948             }
6949             return delta;
6950         },
6951
6952         /**
6953          * Returns true if the control, meta, shift or alt key was pressed during this event.
6954          * @return {Boolean}
6955          */
6956         hasModifier : function(){
6957             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6958         },
6959
6960         /**
6961          * Returns true if the target of this event equals el or is a child of el
6962          * @param {String/HTMLElement/Element} el
6963          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6964          * @return {Boolean}
6965          */
6966         within : function(el, related){
6967             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6968             return t && Roo.fly(el).contains(t);
6969         },
6970
6971         getPoint : function(){
6972             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6973         }
6974     };
6975
6976     return new Roo.EventObjectImpl();
6977 }();
6978             
6979     /*
6980  * Based on:
6981  * Ext JS Library 1.1.1
6982  * Copyright(c) 2006-2007, Ext JS, LLC.
6983  *
6984  * Originally Released Under LGPL - original licence link has changed is not relivant.
6985  *
6986  * Fork - LGPL
6987  * <script type="text/javascript">
6988  */
6989
6990  
6991 // was in Composite Element!??!?!
6992  
6993 (function(){
6994     var D = Roo.lib.Dom;
6995     var E = Roo.lib.Event;
6996     var A = Roo.lib.Anim;
6997
6998     // local style camelizing for speed
6999     var propCache = {};
7000     var camelRe = /(-[a-z])/gi;
7001     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7002     var view = document.defaultView;
7003
7004 /**
7005  * @class Roo.Element
7006  * Represents an Element in the DOM.<br><br>
7007  * Usage:<br>
7008 <pre><code>
7009 var el = Roo.get("my-div");
7010
7011 // or with getEl
7012 var el = getEl("my-div");
7013
7014 // or with a DOM element
7015 var el = Roo.get(myDivElement);
7016 </code></pre>
7017  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7018  * each call instead of constructing a new one.<br><br>
7019  * <b>Animations</b><br />
7020  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7021  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7022 <pre>
7023 Option    Default   Description
7024 --------- --------  ---------------------------------------------
7025 duration  .35       The duration of the animation in seconds
7026 easing    easeOut   The YUI easing method
7027 callback  none      A function to execute when the anim completes
7028 scope     this      The scope (this) of the callback function
7029 </pre>
7030 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7031 * manipulate the animation. Here's an example:
7032 <pre><code>
7033 var el = Roo.get("my-div");
7034
7035 // no animation
7036 el.setWidth(100);
7037
7038 // default animation
7039 el.setWidth(100, true);
7040
7041 // animation with some options set
7042 el.setWidth(100, {
7043     duration: 1,
7044     callback: this.foo,
7045     scope: this
7046 });
7047
7048 // using the "anim" property to get the Anim object
7049 var opt = {
7050     duration: 1,
7051     callback: this.foo,
7052     scope: this
7053 };
7054 el.setWidth(100, opt);
7055 ...
7056 if(opt.anim.isAnimated()){
7057     opt.anim.stop();
7058 }
7059 </code></pre>
7060 * <b> Composite (Collections of) Elements</b><br />
7061  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7062  * @constructor Create a new Element directly.
7063  * @param {String/HTMLElement} element
7064  * @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).
7065  */
7066     Roo.Element = function(element, forceNew){
7067         var dom = typeof element == "string" ?
7068                 document.getElementById(element) : element;
7069         if(!dom){ // invalid id/element
7070             return null;
7071         }
7072         var id = dom.id;
7073         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7074             return Roo.Element.cache[id];
7075         }
7076
7077         /**
7078          * The DOM element
7079          * @type HTMLElement
7080          */
7081         this.dom = dom;
7082
7083         /**
7084          * The DOM element ID
7085          * @type String
7086          */
7087         this.id = id || Roo.id(dom);
7088     };
7089
7090     var El = Roo.Element;
7091
7092     El.prototype = {
7093         /**
7094          * The element's default display mode  (defaults to "")
7095          * @type String
7096          */
7097         originalDisplay : "",
7098
7099         visibilityMode : 1,
7100         /**
7101          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7102          * @type String
7103          */
7104         defaultUnit : "px",
7105         
7106         /**
7107          * Sets the element's visibility mode. When setVisible() is called it
7108          * will use this to determine whether to set the visibility or the display property.
7109          * @param visMode Element.VISIBILITY or Element.DISPLAY
7110          * @return {Roo.Element} this
7111          */
7112         setVisibilityMode : function(visMode){
7113             this.visibilityMode = visMode;
7114             return this;
7115         },
7116         /**
7117          * Convenience method for setVisibilityMode(Element.DISPLAY)
7118          * @param {String} display (optional) What to set display to when visible
7119          * @return {Roo.Element} this
7120          */
7121         enableDisplayMode : function(display){
7122             this.setVisibilityMode(El.DISPLAY);
7123             if(typeof display != "undefined") { this.originalDisplay = display; }
7124             return this;
7125         },
7126
7127         /**
7128          * 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)
7129          * @param {String} selector The simple selector to test
7130          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7131                 search as a number or element (defaults to 10 || document.body)
7132          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7133          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7134          */
7135         findParent : function(simpleSelector, maxDepth, returnEl){
7136             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7137             maxDepth = maxDepth || 50;
7138             if(typeof maxDepth != "number"){
7139                 stopEl = Roo.getDom(maxDepth);
7140                 maxDepth = 10;
7141             }
7142             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7143                 if(dq.is(p, simpleSelector)){
7144                     return returnEl ? Roo.get(p) : p;
7145                 }
7146                 depth++;
7147                 p = p.parentNode;
7148             }
7149             return null;
7150         },
7151
7152
7153         /**
7154          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7155          * @param {String} selector The simple selector to test
7156          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7157                 search as a number or element (defaults to 10 || document.body)
7158          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7159          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7160          */
7161         findParentNode : function(simpleSelector, maxDepth, returnEl){
7162             var p = Roo.fly(this.dom.parentNode, '_internal');
7163             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7164         },
7165
7166         /**
7167          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7168          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7169          * @param {String} selector The simple selector to test
7170          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7171                 search as a number or element (defaults to 10 || document.body)
7172          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7173          */
7174         up : function(simpleSelector, maxDepth){
7175             return this.findParentNode(simpleSelector, maxDepth, true);
7176         },
7177
7178
7179
7180         /**
7181          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7182          * @param {String} selector The simple selector to test
7183          * @return {Boolean} True if this element matches the selector, else false
7184          */
7185         is : function(simpleSelector){
7186             return Roo.DomQuery.is(this.dom, simpleSelector);
7187         },
7188
7189         /**
7190          * Perform animation on this element.
7191          * @param {Object} args The YUI animation control args
7192          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7193          * @param {Function} onComplete (optional) Function to call when animation completes
7194          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7195          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7196          * @return {Roo.Element} this
7197          */
7198         animate : function(args, duration, onComplete, easing, animType){
7199             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7200             return this;
7201         },
7202
7203         /*
7204          * @private Internal animation call
7205          */
7206         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7207             animType = animType || 'run';
7208             opt = opt || {};
7209             var anim = Roo.lib.Anim[animType](
7210                 this.dom, args,
7211                 (opt.duration || defaultDur) || .35,
7212                 (opt.easing || defaultEase) || 'easeOut',
7213                 function(){
7214                     Roo.callback(cb, this);
7215                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7216                 },
7217                 this
7218             );
7219             opt.anim = anim;
7220             return anim;
7221         },
7222
7223         // private legacy anim prep
7224         preanim : function(a, i){
7225             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7226         },
7227
7228         /**
7229          * Removes worthless text nodes
7230          * @param {Boolean} forceReclean (optional) By default the element
7231          * keeps track if it has been cleaned already so
7232          * you can call this over and over. However, if you update the element and
7233          * need to force a reclean, you can pass true.
7234          */
7235         clean : function(forceReclean){
7236             if(this.isCleaned && forceReclean !== true){
7237                 return this;
7238             }
7239             var ns = /\S/;
7240             var d = this.dom, n = d.firstChild, ni = -1;
7241             while(n){
7242                 var nx = n.nextSibling;
7243                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7244                     d.removeChild(n);
7245                 }else{
7246                     n.nodeIndex = ++ni;
7247                 }
7248                 n = nx;
7249             }
7250             this.isCleaned = true;
7251             return this;
7252         },
7253
7254         // private
7255         calcOffsetsTo : function(el){
7256             el = Roo.get(el);
7257             var d = el.dom;
7258             var restorePos = false;
7259             if(el.getStyle('position') == 'static'){
7260                 el.position('relative');
7261                 restorePos = true;
7262             }
7263             var x = 0, y =0;
7264             var op = this.dom;
7265             while(op && op != d && op.tagName != 'HTML'){
7266                 x+= op.offsetLeft;
7267                 y+= op.offsetTop;
7268                 op = op.offsetParent;
7269             }
7270             if(restorePos){
7271                 el.position('static');
7272             }
7273             return [x, y];
7274         },
7275
7276         /**
7277          * Scrolls this element into view within the passed container.
7278          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7279          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7280          * @return {Roo.Element} this
7281          */
7282         scrollIntoView : function(container, hscroll){
7283             var c = Roo.getDom(container) || document.body;
7284             var el = this.dom;
7285
7286             var o = this.calcOffsetsTo(c),
7287                 l = o[0],
7288                 t = o[1],
7289                 b = t+el.offsetHeight,
7290                 r = l+el.offsetWidth;
7291
7292             var ch = c.clientHeight;
7293             var ct = parseInt(c.scrollTop, 10);
7294             var cl = parseInt(c.scrollLeft, 10);
7295             var cb = ct + ch;
7296             var cr = cl + c.clientWidth;
7297
7298             if(t < ct){
7299                 c.scrollTop = t;
7300             }else if(b > cb){
7301                 c.scrollTop = b-ch;
7302             }
7303
7304             if(hscroll !== false){
7305                 if(l < cl){
7306                     c.scrollLeft = l;
7307                 }else if(r > cr){
7308                     c.scrollLeft = r-c.clientWidth;
7309                 }
7310             }
7311             return this;
7312         },
7313
7314         // private
7315         scrollChildIntoView : function(child, hscroll){
7316             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7317         },
7318
7319         /**
7320          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7321          * the new height may not be available immediately.
7322          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7323          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7324          * @param {Function} onComplete (optional) Function to call when animation completes
7325          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7326          * @return {Roo.Element} this
7327          */
7328         autoHeight : function(animate, duration, onComplete, easing){
7329             var oldHeight = this.getHeight();
7330             this.clip();
7331             this.setHeight(1); // force clipping
7332             setTimeout(function(){
7333                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7334                 if(!animate){
7335                     this.setHeight(height);
7336                     this.unclip();
7337                     if(typeof onComplete == "function"){
7338                         onComplete();
7339                     }
7340                 }else{
7341                     this.setHeight(oldHeight); // restore original height
7342                     this.setHeight(height, animate, duration, function(){
7343                         this.unclip();
7344                         if(typeof onComplete == "function") { onComplete(); }
7345                     }.createDelegate(this), easing);
7346                 }
7347             }.createDelegate(this), 0);
7348             return this;
7349         },
7350
7351         /**
7352          * Returns true if this element is an ancestor of the passed element
7353          * @param {HTMLElement/String} el The element to check
7354          * @return {Boolean} True if this element is an ancestor of el, else false
7355          */
7356         contains : function(el){
7357             if(!el){return false;}
7358             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7359         },
7360
7361         /**
7362          * Checks whether the element is currently visible using both visibility and display properties.
7363          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7364          * @return {Boolean} True if the element is currently visible, else false
7365          */
7366         isVisible : function(deep) {
7367             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7368             if(deep !== true || !vis){
7369                 return vis;
7370             }
7371             var p = this.dom.parentNode;
7372             while(p && p.tagName.toLowerCase() != "body"){
7373                 if(!Roo.fly(p, '_isVisible').isVisible()){
7374                     return false;
7375                 }
7376                 p = p.parentNode;
7377             }
7378             return true;
7379         },
7380
7381         /**
7382          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7383          * @param {String} selector The CSS selector
7384          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7385          * @return {CompositeElement/CompositeElementLite} The composite element
7386          */
7387         select : function(selector, unique){
7388             return El.select(selector, unique, this.dom);
7389         },
7390
7391         /**
7392          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7393          * @param {String} selector The CSS selector
7394          * @return {Array} An array of the matched nodes
7395          */
7396         query : function(selector, unique){
7397             return Roo.DomQuery.select(selector, this.dom);
7398         },
7399
7400         /**
7401          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7402          * @param {String} selector The CSS selector
7403          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7404          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7405          */
7406         child : function(selector, returnDom){
7407             var n = Roo.DomQuery.selectNode(selector, this.dom);
7408             return returnDom ? n : Roo.get(n);
7409         },
7410
7411         /**
7412          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7413          * @param {String} selector The CSS selector
7414          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7415          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7416          */
7417         down : function(selector, returnDom){
7418             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7419             return returnDom ? n : Roo.get(n);
7420         },
7421
7422         /**
7423          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7424          * @param {String} group The group the DD object is member of
7425          * @param {Object} config The DD config object
7426          * @param {Object} overrides An object containing methods to override/implement on the DD object
7427          * @return {Roo.dd.DD} The DD object
7428          */
7429         initDD : function(group, config, overrides){
7430             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7431             return Roo.apply(dd, overrides);
7432         },
7433
7434         /**
7435          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7436          * @param {String} group The group the DDProxy object is member of
7437          * @param {Object} config The DDProxy config object
7438          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7439          * @return {Roo.dd.DDProxy} The DDProxy object
7440          */
7441         initDDProxy : function(group, config, overrides){
7442             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7443             return Roo.apply(dd, overrides);
7444         },
7445
7446         /**
7447          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7448          * @param {String} group The group the DDTarget object is member of
7449          * @param {Object} config The DDTarget config object
7450          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7451          * @return {Roo.dd.DDTarget} The DDTarget object
7452          */
7453         initDDTarget : function(group, config, overrides){
7454             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7455             return Roo.apply(dd, overrides);
7456         },
7457
7458         /**
7459          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7460          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7461          * @param {Boolean} visible Whether the element is visible
7462          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7463          * @return {Roo.Element} this
7464          */
7465          setVisible : function(visible, animate){
7466             if(!animate || !A){
7467                 if(this.visibilityMode == El.DISPLAY){
7468                     this.setDisplayed(visible);
7469                 }else{
7470                     this.fixDisplay();
7471                     this.dom.style.visibility = visible ? "visible" : "hidden";
7472                 }
7473             }else{
7474                 // closure for composites
7475                 var dom = this.dom;
7476                 var visMode = this.visibilityMode;
7477                 if(visible){
7478                     this.setOpacity(.01);
7479                     this.setVisible(true);
7480                 }
7481                 this.anim({opacity: { to: (visible?1:0) }},
7482                       this.preanim(arguments, 1),
7483                       null, .35, 'easeIn', function(){
7484                          if(!visible){
7485                              if(visMode == El.DISPLAY){
7486                                  dom.style.display = "none";
7487                              }else{
7488                                  dom.style.visibility = "hidden";
7489                              }
7490                              Roo.get(dom).setOpacity(1);
7491                          }
7492                      });
7493             }
7494             return this;
7495         },
7496
7497         /**
7498          * Returns true if display is not "none"
7499          * @return {Boolean}
7500          */
7501         isDisplayed : function() {
7502             return this.getStyle("display") != "none";
7503         },
7504
7505         /**
7506          * Toggles the element's visibility or display, depending on visibility mode.
7507          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7508          * @return {Roo.Element} this
7509          */
7510         toggle : function(animate){
7511             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7512             return this;
7513         },
7514
7515         /**
7516          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7517          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7518          * @return {Roo.Element} this
7519          */
7520         setDisplayed : function(value) {
7521             if(typeof value == "boolean"){
7522                value = value ? this.originalDisplay : "none";
7523             }
7524             this.setStyle("display", value);
7525             return this;
7526         },
7527
7528         /**
7529          * Tries to focus the element. Any exceptions are caught and ignored.
7530          * @return {Roo.Element} this
7531          */
7532         focus : function() {
7533             try{
7534                 this.dom.focus();
7535             }catch(e){}
7536             return this;
7537         },
7538
7539         /**
7540          * Tries to blur the element. Any exceptions are caught and ignored.
7541          * @return {Roo.Element} this
7542          */
7543         blur : function() {
7544             try{
7545                 this.dom.blur();
7546             }catch(e){}
7547             return this;
7548         },
7549
7550         /**
7551          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7552          * @param {String/Array} className The CSS class to add, or an array of classes
7553          * @return {Roo.Element} this
7554          */
7555         addClass : function(className){
7556             if(className instanceof Array){
7557                 for(var i = 0, len = className.length; i < len; i++) {
7558                     this.addClass(className[i]);
7559                 }
7560             }else{
7561                 if(className && !this.hasClass(className)){
7562                     this.dom.className = this.dom.className + " " + className;
7563                 }
7564             }
7565             return this;
7566         },
7567
7568         /**
7569          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7570          * @param {String/Array} className The CSS class to add, or an array of classes
7571          * @return {Roo.Element} this
7572          */
7573         radioClass : function(className){
7574             var siblings = this.dom.parentNode.childNodes;
7575             for(var i = 0; i < siblings.length; i++) {
7576                 var s = siblings[i];
7577                 if(s.nodeType == 1){
7578                     Roo.get(s).removeClass(className);
7579                 }
7580             }
7581             this.addClass(className);
7582             return this;
7583         },
7584
7585         /**
7586          * Removes one or more CSS classes from the element.
7587          * @param {String/Array} className The CSS class to remove, or an array of classes
7588          * @return {Roo.Element} this
7589          */
7590         removeClass : function(className){
7591             if(!className || !this.dom.className){
7592                 return this;
7593             }
7594             if(className instanceof Array){
7595                 for(var i = 0, len = className.length; i < len; i++) {
7596                     this.removeClass(className[i]);
7597                 }
7598             }else{
7599                 if(this.hasClass(className)){
7600                     var re = this.classReCache[className];
7601                     if (!re) {
7602                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7603                        this.classReCache[className] = re;
7604                     }
7605                     this.dom.className =
7606                         this.dom.className.replace(re, " ");
7607                 }
7608             }
7609             return this;
7610         },
7611
7612         // private
7613         classReCache: {},
7614
7615         /**
7616          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7617          * @param {String} className The CSS class to toggle
7618          * @return {Roo.Element} this
7619          */
7620         toggleClass : function(className){
7621             if(this.hasClass(className)){
7622                 this.removeClass(className);
7623             }else{
7624                 this.addClass(className);
7625             }
7626             return this;
7627         },
7628
7629         /**
7630          * Checks if the specified CSS class exists on this element's DOM node.
7631          * @param {String} className The CSS class to check for
7632          * @return {Boolean} True if the class exists, else false
7633          */
7634         hasClass : function(className){
7635             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7636         },
7637
7638         /**
7639          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7640          * @param {String} oldClassName The CSS class to replace
7641          * @param {String} newClassName The replacement CSS class
7642          * @return {Roo.Element} this
7643          */
7644         replaceClass : function(oldClassName, newClassName){
7645             this.removeClass(oldClassName);
7646             this.addClass(newClassName);
7647             return this;
7648         },
7649
7650         /**
7651          * Returns an object with properties matching the styles requested.
7652          * For example, el.getStyles('color', 'font-size', 'width') might return
7653          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7654          * @param {String} style1 A style name
7655          * @param {String} style2 A style name
7656          * @param {String} etc.
7657          * @return {Object} The style object
7658          */
7659         getStyles : function(){
7660             var a = arguments, len = a.length, r = {};
7661             for(var i = 0; i < len; i++){
7662                 r[a[i]] = this.getStyle(a[i]);
7663             }
7664             return r;
7665         },
7666
7667         /**
7668          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7669          * @param {String} property The style property whose value is returned.
7670          * @return {String} The current value of the style property for this element.
7671          */
7672         getStyle : function(){
7673             return view && view.getComputedStyle ?
7674                 function(prop){
7675                     var el = this.dom, v, cs, camel;
7676                     if(prop == 'float'){
7677                         prop = "cssFloat";
7678                     }
7679                     if(el.style && (v = el.style[prop])){
7680                         return v;
7681                     }
7682                     if(cs = view.getComputedStyle(el, "")){
7683                         if(!(camel = propCache[prop])){
7684                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7685                         }
7686                         return cs[camel];
7687                     }
7688                     return null;
7689                 } :
7690                 function(prop){
7691                     var el = this.dom, v, cs, camel;
7692                     if(prop == 'opacity'){
7693                         if(typeof el.style.filter == 'string'){
7694                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7695                             if(m){
7696                                 var fv = parseFloat(m[1]);
7697                                 if(!isNaN(fv)){
7698                                     return fv ? fv / 100 : 0;
7699                                 }
7700                             }
7701                         }
7702                         return 1;
7703                     }else if(prop == 'float'){
7704                         prop = "styleFloat";
7705                     }
7706                     if(!(camel = propCache[prop])){
7707                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7708                     }
7709                     if(v = el.style[camel]){
7710                         return v;
7711                     }
7712                     if(cs = el.currentStyle){
7713                         return cs[camel];
7714                     }
7715                     return null;
7716                 };
7717         }(),
7718
7719         /**
7720          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7721          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7722          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7723          * @return {Roo.Element} this
7724          */
7725         setStyle : function(prop, value){
7726             if(typeof prop == "string"){
7727                 
7728                 if (prop == 'float') {
7729                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7730                     return this;
7731                 }
7732                 
7733                 var camel;
7734                 if(!(camel = propCache[prop])){
7735                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7736                 }
7737                 
7738                 if(camel == 'opacity') {
7739                     this.setOpacity(value);
7740                 }else{
7741                     this.dom.style[camel] = value;
7742                 }
7743             }else{
7744                 for(var style in prop){
7745                     if(typeof prop[style] != "function"){
7746                        this.setStyle(style, prop[style]);
7747                     }
7748                 }
7749             }
7750             return this;
7751         },
7752
7753         /**
7754          * More flexible version of {@link #setStyle} for setting style properties.
7755          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7756          * a function which returns such a specification.
7757          * @return {Roo.Element} this
7758          */
7759         applyStyles : function(style){
7760             Roo.DomHelper.applyStyles(this.dom, style);
7761             return this;
7762         },
7763
7764         /**
7765           * 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).
7766           * @return {Number} The X position of the element
7767           */
7768         getX : function(){
7769             return D.getX(this.dom);
7770         },
7771
7772         /**
7773           * 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).
7774           * @return {Number} The Y position of the element
7775           */
7776         getY : function(){
7777             return D.getY(this.dom);
7778         },
7779
7780         /**
7781           * 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).
7782           * @return {Array} The XY position of the element
7783           */
7784         getXY : function(){
7785             return D.getXY(this.dom);
7786         },
7787
7788         /**
7789          * 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).
7790          * @param {Number} The X position of the element
7791          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7792          * @return {Roo.Element} this
7793          */
7794         setX : function(x, animate){
7795             if(!animate || !A){
7796                 D.setX(this.dom, x);
7797             }else{
7798                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7799             }
7800             return this;
7801         },
7802
7803         /**
7804          * 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).
7805          * @param {Number} The Y position of the element
7806          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7807          * @return {Roo.Element} this
7808          */
7809         setY : function(y, animate){
7810             if(!animate || !A){
7811                 D.setY(this.dom, y);
7812             }else{
7813                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7814             }
7815             return this;
7816         },
7817
7818         /**
7819          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7820          * @param {String} left The left CSS property value
7821          * @return {Roo.Element} this
7822          */
7823         setLeft : function(left){
7824             this.setStyle("left", this.addUnits(left));
7825             return this;
7826         },
7827
7828         /**
7829          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7830          * @param {String} top The top CSS property value
7831          * @return {Roo.Element} this
7832          */
7833         setTop : function(top){
7834             this.setStyle("top", this.addUnits(top));
7835             return this;
7836         },
7837
7838         /**
7839          * Sets the element's CSS right style.
7840          * @param {String} right The right CSS property value
7841          * @return {Roo.Element} this
7842          */
7843         setRight : function(right){
7844             this.setStyle("right", this.addUnits(right));
7845             return this;
7846         },
7847
7848         /**
7849          * Sets the element's CSS bottom style.
7850          * @param {String} bottom The bottom CSS property value
7851          * @return {Roo.Element} this
7852          */
7853         setBottom : function(bottom){
7854             this.setStyle("bottom", this.addUnits(bottom));
7855             return this;
7856         },
7857
7858         /**
7859          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7860          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7861          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7862          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7863          * @return {Roo.Element} this
7864          */
7865         setXY : function(pos, animate){
7866             if(!animate || !A){
7867                 D.setXY(this.dom, pos);
7868             }else{
7869                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7870             }
7871             return this;
7872         },
7873
7874         /**
7875          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7876          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7877          * @param {Number} x X value for new position (coordinates are page-based)
7878          * @param {Number} y Y value for new position (coordinates are page-based)
7879          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7880          * @return {Roo.Element} this
7881          */
7882         setLocation : function(x, y, animate){
7883             this.setXY([x, y], this.preanim(arguments, 2));
7884             return this;
7885         },
7886
7887         /**
7888          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7889          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7890          * @param {Number} x X value for new position (coordinates are page-based)
7891          * @param {Number} y Y value for new position (coordinates are page-based)
7892          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7893          * @return {Roo.Element} this
7894          */
7895         moveTo : function(x, y, animate){
7896             this.setXY([x, y], this.preanim(arguments, 2));
7897             return this;
7898         },
7899
7900         /**
7901          * Returns the region of the given element.
7902          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7903          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7904          */
7905         getRegion : function(){
7906             return D.getRegion(this.dom);
7907         },
7908
7909         /**
7910          * Returns the offset height of the element
7911          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7912          * @return {Number} The element's height
7913          */
7914         getHeight : function(contentHeight){
7915             var h = this.dom.offsetHeight || 0;
7916             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7917         },
7918
7919         /**
7920          * Returns the offset width of the element
7921          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7922          * @return {Number} The element's width
7923          */
7924         getWidth : function(contentWidth){
7925             var w = this.dom.offsetWidth || 0;
7926             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7927         },
7928
7929         /**
7930          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7931          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7932          * if a height has not been set using CSS.
7933          * @return {Number}
7934          */
7935         getComputedHeight : function(){
7936             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7937             if(!h){
7938                 h = parseInt(this.getStyle('height'), 10) || 0;
7939                 if(!this.isBorderBox()){
7940                     h += this.getFrameWidth('tb');
7941                 }
7942             }
7943             return h;
7944         },
7945
7946         /**
7947          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7948          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7949          * if a width has not been set using CSS.
7950          * @return {Number}
7951          */
7952         getComputedWidth : function(){
7953             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7954             if(!w){
7955                 w = parseInt(this.getStyle('width'), 10) || 0;
7956                 if(!this.isBorderBox()){
7957                     w += this.getFrameWidth('lr');
7958                 }
7959             }
7960             return w;
7961         },
7962
7963         /**
7964          * Returns the size of the element.
7965          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7966          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7967          */
7968         getSize : function(contentSize){
7969             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7970         },
7971
7972         /**
7973          * Returns the width and height of the viewport.
7974          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7975          */
7976         getViewSize : function(){
7977             var d = this.dom, doc = document, aw = 0, ah = 0;
7978             if(d == doc || d == doc.body){
7979                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7980             }else{
7981                 return {
7982                     width : d.clientWidth,
7983                     height: d.clientHeight
7984                 };
7985             }
7986         },
7987
7988         /**
7989          * Returns the value of the "value" attribute
7990          * @param {Boolean} asNumber true to parse the value as a number
7991          * @return {String/Number}
7992          */
7993         getValue : function(asNumber){
7994             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7995         },
7996
7997         // private
7998         adjustWidth : function(width){
7999             if(typeof width == "number"){
8000                 if(this.autoBoxAdjust && !this.isBorderBox()){
8001                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8002                 }
8003                 if(width < 0){
8004                     width = 0;
8005                 }
8006             }
8007             return width;
8008         },
8009
8010         // private
8011         adjustHeight : function(height){
8012             if(typeof height == "number"){
8013                if(this.autoBoxAdjust && !this.isBorderBox()){
8014                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8015                }
8016                if(height < 0){
8017                    height = 0;
8018                }
8019             }
8020             return height;
8021         },
8022
8023         /**
8024          * Set the width of the element
8025          * @param {Number} width The new width
8026          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8027          * @return {Roo.Element} this
8028          */
8029         setWidth : function(width, animate){
8030             width = this.adjustWidth(width);
8031             if(!animate || !A){
8032                 this.dom.style.width = this.addUnits(width);
8033             }else{
8034                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8035             }
8036             return this;
8037         },
8038
8039         /**
8040          * Set the height of the element
8041          * @param {Number} height The new height
8042          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8043          * @return {Roo.Element} this
8044          */
8045          setHeight : function(height, animate){
8046             height = this.adjustHeight(height);
8047             if(!animate || !A){
8048                 this.dom.style.height = this.addUnits(height);
8049             }else{
8050                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8051             }
8052             return this;
8053         },
8054
8055         /**
8056          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8057          * @param {Number} width The new width
8058          * @param {Number} height The new height
8059          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8060          * @return {Roo.Element} this
8061          */
8062          setSize : function(width, height, animate){
8063             if(typeof width == "object"){ // in case of object from getSize()
8064                 height = width.height; width = width.width;
8065             }
8066             width = this.adjustWidth(width); height = this.adjustHeight(height);
8067             if(!animate || !A){
8068                 this.dom.style.width = this.addUnits(width);
8069                 this.dom.style.height = this.addUnits(height);
8070             }else{
8071                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8072             }
8073             return this;
8074         },
8075
8076         /**
8077          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8078          * @param {Number} x X value for new position (coordinates are page-based)
8079          * @param {Number} y Y value for new position (coordinates are page-based)
8080          * @param {Number} width The new width
8081          * @param {Number} height The new height
8082          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8083          * @return {Roo.Element} this
8084          */
8085         setBounds : function(x, y, width, height, animate){
8086             if(!animate || !A){
8087                 this.setSize(width, height);
8088                 this.setLocation(x, y);
8089             }else{
8090                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8091                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8092                               this.preanim(arguments, 4), 'motion');
8093             }
8094             return this;
8095         },
8096
8097         /**
8098          * 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.
8099          * @param {Roo.lib.Region} region The region to fill
8100          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8101          * @return {Roo.Element} this
8102          */
8103         setRegion : function(region, animate){
8104             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8105             return this;
8106         },
8107
8108         /**
8109          * Appends an event handler
8110          *
8111          * @param {String}   eventName     The type of event to append
8112          * @param {Function} fn        The method the event invokes
8113          * @param {Object} scope       (optional) The scope (this object) of the fn
8114          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8115          */
8116         addListener : function(eventName, fn, scope, options){
8117             if (this.dom) {
8118                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8119             }
8120         },
8121
8122         /**
8123          * Removes an event handler from this element
8124          * @param {String} eventName the type of event to remove
8125          * @param {Function} fn the method the event invokes
8126          * @return {Roo.Element} this
8127          */
8128         removeListener : function(eventName, fn){
8129             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8130             return this;
8131         },
8132
8133         /**
8134          * Removes all previous added listeners from this element
8135          * @return {Roo.Element} this
8136          */
8137         removeAllListeners : function(){
8138             E.purgeElement(this.dom);
8139             return this;
8140         },
8141
8142         relayEvent : function(eventName, observable){
8143             this.on(eventName, function(e){
8144                 observable.fireEvent(eventName, e);
8145             });
8146         },
8147
8148         /**
8149          * Set the opacity of the element
8150          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8151          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8152          * @return {Roo.Element} this
8153          */
8154          setOpacity : function(opacity, animate){
8155             if(!animate || !A){
8156                 var s = this.dom.style;
8157                 if(Roo.isIE){
8158                     s.zoom = 1;
8159                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8160                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8161                 }else{
8162                     s.opacity = opacity;
8163                 }
8164             }else{
8165                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8166             }
8167             return this;
8168         },
8169
8170         /**
8171          * Gets the left X coordinate
8172          * @param {Boolean} local True to get the local css position instead of page coordinate
8173          * @return {Number}
8174          */
8175         getLeft : function(local){
8176             if(!local){
8177                 return this.getX();
8178             }else{
8179                 return parseInt(this.getStyle("left"), 10) || 0;
8180             }
8181         },
8182
8183         /**
8184          * Gets the right X coordinate of the element (element X position + element width)
8185          * @param {Boolean} local True to get the local css position instead of page coordinate
8186          * @return {Number}
8187          */
8188         getRight : function(local){
8189             if(!local){
8190                 return this.getX() + this.getWidth();
8191             }else{
8192                 return (this.getLeft(true) + this.getWidth()) || 0;
8193             }
8194         },
8195
8196         /**
8197          * Gets the top Y coordinate
8198          * @param {Boolean} local True to get the local css position instead of page coordinate
8199          * @return {Number}
8200          */
8201         getTop : function(local) {
8202             if(!local){
8203                 return this.getY();
8204             }else{
8205                 return parseInt(this.getStyle("top"), 10) || 0;
8206             }
8207         },
8208
8209         /**
8210          * Gets the bottom Y coordinate of the element (element Y position + element height)
8211          * @param {Boolean} local True to get the local css position instead of page coordinate
8212          * @return {Number}
8213          */
8214         getBottom : function(local){
8215             if(!local){
8216                 return this.getY() + this.getHeight();
8217             }else{
8218                 return (this.getTop(true) + this.getHeight()) || 0;
8219             }
8220         },
8221
8222         /**
8223         * Initializes positioning on this element. If a desired position is not passed, it will make the
8224         * the element positioned relative IF it is not already positioned.
8225         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8226         * @param {Number} zIndex (optional) The zIndex to apply
8227         * @param {Number} x (optional) Set the page X position
8228         * @param {Number} y (optional) Set the page Y position
8229         */
8230         position : function(pos, zIndex, x, y){
8231             if(!pos){
8232                if(this.getStyle('position') == 'static'){
8233                    this.setStyle('position', 'relative');
8234                }
8235             }else{
8236                 this.setStyle("position", pos);
8237             }
8238             if(zIndex){
8239                 this.setStyle("z-index", zIndex);
8240             }
8241             if(x !== undefined && y !== undefined){
8242                 this.setXY([x, y]);
8243             }else if(x !== undefined){
8244                 this.setX(x);
8245             }else if(y !== undefined){
8246                 this.setY(y);
8247             }
8248         },
8249
8250         /**
8251         * Clear positioning back to the default when the document was loaded
8252         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8253         * @return {Roo.Element} this
8254          */
8255         clearPositioning : function(value){
8256             value = value ||'';
8257             this.setStyle({
8258                 "left": value,
8259                 "right": value,
8260                 "top": value,
8261                 "bottom": value,
8262                 "z-index": "",
8263                 "position" : "static"
8264             });
8265             return this;
8266         },
8267
8268         /**
8269         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8270         * snapshot before performing an update and then restoring the element.
8271         * @return {Object}
8272         */
8273         getPositioning : function(){
8274             var l = this.getStyle("left");
8275             var t = this.getStyle("top");
8276             return {
8277                 "position" : this.getStyle("position"),
8278                 "left" : l,
8279                 "right" : l ? "" : this.getStyle("right"),
8280                 "top" : t,
8281                 "bottom" : t ? "" : this.getStyle("bottom"),
8282                 "z-index" : this.getStyle("z-index")
8283             };
8284         },
8285
8286         /**
8287          * Gets the width of the border(s) for the specified side(s)
8288          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8289          * passing lr would get the border (l)eft width + the border (r)ight width.
8290          * @return {Number} The width of the sides passed added together
8291          */
8292         getBorderWidth : function(side){
8293             return this.addStyles(side, El.borders);
8294         },
8295
8296         /**
8297          * Gets the width of the padding(s) for the specified side(s)
8298          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8299          * passing lr would get the padding (l)eft + the padding (r)ight.
8300          * @return {Number} The padding of the sides passed added together
8301          */
8302         getPadding : function(side){
8303             return this.addStyles(side, El.paddings);
8304         },
8305
8306         /**
8307         * Set positioning with an object returned by getPositioning().
8308         * @param {Object} posCfg
8309         * @return {Roo.Element} this
8310          */
8311         setPositioning : function(pc){
8312             this.applyStyles(pc);
8313             if(pc.right == "auto"){
8314                 this.dom.style.right = "";
8315             }
8316             if(pc.bottom == "auto"){
8317                 this.dom.style.bottom = "";
8318             }
8319             return this;
8320         },
8321
8322         // private
8323         fixDisplay : function(){
8324             if(this.getStyle("display") == "none"){
8325                 this.setStyle("visibility", "hidden");
8326                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8327                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8328                     this.setStyle("display", "block");
8329                 }
8330             }
8331         },
8332
8333         /**
8334          * Quick set left and top adding default units
8335          * @param {String} left The left CSS property value
8336          * @param {String} top The top CSS property value
8337          * @return {Roo.Element} this
8338          */
8339          setLeftTop : function(left, top){
8340             this.dom.style.left = this.addUnits(left);
8341             this.dom.style.top = this.addUnits(top);
8342             return this;
8343         },
8344
8345         /**
8346          * Move this element relative to its current position.
8347          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8348          * @param {Number} distance How far to move the element in pixels
8349          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8350          * @return {Roo.Element} this
8351          */
8352          move : function(direction, distance, animate){
8353             var xy = this.getXY();
8354             direction = direction.toLowerCase();
8355             switch(direction){
8356                 case "l":
8357                 case "left":
8358                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8359                     break;
8360                case "r":
8361                case "right":
8362                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8363                     break;
8364                case "t":
8365                case "top":
8366                case "up":
8367                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8368                     break;
8369                case "b":
8370                case "bottom":
8371                case "down":
8372                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8373                     break;
8374             }
8375             return this;
8376         },
8377
8378         /**
8379          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8380          * @return {Roo.Element} this
8381          */
8382         clip : function(){
8383             if(!this.isClipped){
8384                this.isClipped = true;
8385                this.originalClip = {
8386                    "o": this.getStyle("overflow"),
8387                    "x": this.getStyle("overflow-x"),
8388                    "y": this.getStyle("overflow-y")
8389                };
8390                this.setStyle("overflow", "hidden");
8391                this.setStyle("overflow-x", "hidden");
8392                this.setStyle("overflow-y", "hidden");
8393             }
8394             return this;
8395         },
8396
8397         /**
8398          *  Return clipping (overflow) to original clipping before clip() was called
8399          * @return {Roo.Element} this
8400          */
8401         unclip : function(){
8402             if(this.isClipped){
8403                 this.isClipped = false;
8404                 var o = this.originalClip;
8405                 if(o.o){this.setStyle("overflow", o.o);}
8406                 if(o.x){this.setStyle("overflow-x", o.x);}
8407                 if(o.y){this.setStyle("overflow-y", o.y);}
8408             }
8409             return this;
8410         },
8411
8412
8413         /**
8414          * Gets the x,y coordinates specified by the anchor position on the element.
8415          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8416          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8417          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8418          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8419          * @return {Array} [x, y] An array containing the element's x and y coordinates
8420          */
8421         getAnchorXY : function(anchor, local, s){
8422             //Passing a different size is useful for pre-calculating anchors,
8423             //especially for anchored animations that change the el size.
8424
8425             var w, h, vp = false;
8426             if(!s){
8427                 var d = this.dom;
8428                 if(d == document.body || d == document){
8429                     vp = true;
8430                     w = D.getViewWidth(); h = D.getViewHeight();
8431                 }else{
8432                     w = this.getWidth(); h = this.getHeight();
8433                 }
8434             }else{
8435                 w = s.width;  h = s.height;
8436             }
8437             var x = 0, y = 0, r = Math.round;
8438             switch((anchor || "tl").toLowerCase()){
8439                 case "c":
8440                     x = r(w*.5);
8441                     y = r(h*.5);
8442                 break;
8443                 case "t":
8444                     x = r(w*.5);
8445                     y = 0;
8446                 break;
8447                 case "l":
8448                     x = 0;
8449                     y = r(h*.5);
8450                 break;
8451                 case "r":
8452                     x = w;
8453                     y = r(h*.5);
8454                 break;
8455                 case "b":
8456                     x = r(w*.5);
8457                     y = h;
8458                 break;
8459                 case "tl":
8460                     x = 0;
8461                     y = 0;
8462                 break;
8463                 case "bl":
8464                     x = 0;
8465                     y = h;
8466                 break;
8467                 case "br":
8468                     x = w;
8469                     y = h;
8470                 break;
8471                 case "tr":
8472                     x = w;
8473                     y = 0;
8474                 break;
8475             }
8476             if(local === true){
8477                 return [x, y];
8478             }
8479             if(vp){
8480                 var sc = this.getScroll();
8481                 return [x + sc.left, y + sc.top];
8482             }
8483             //Add the element's offset xy
8484             var o = this.getXY();
8485             return [x+o[0], y+o[1]];
8486         },
8487
8488         /**
8489          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8490          * supported position values.
8491          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8492          * @param {String} position The position to align to.
8493          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8494          * @return {Array} [x, y]
8495          */
8496         getAlignToXY : function(el, p, o){
8497             el = Roo.get(el);
8498             var d = this.dom;
8499             if(!el.dom){
8500                 throw "Element.alignTo with an element that doesn't exist";
8501             }
8502             var c = false; //constrain to viewport
8503             var p1 = "", p2 = "";
8504             o = o || [0,0];
8505
8506             if(!p){
8507                 p = "tl-bl";
8508             }else if(p == "?"){
8509                 p = "tl-bl?";
8510             }else if(p.indexOf("-") == -1){
8511                 p = "tl-" + p;
8512             }
8513             p = p.toLowerCase();
8514             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8515             if(!m){
8516                throw "Element.alignTo with an invalid alignment " + p;
8517             }
8518             p1 = m[1]; p2 = m[2]; c = !!m[3];
8519
8520             //Subtract the aligned el's internal xy from the target's offset xy
8521             //plus custom offset to get the aligned el's new offset xy
8522             var a1 = this.getAnchorXY(p1, true);
8523             var a2 = el.getAnchorXY(p2, false);
8524             var x = a2[0] - a1[0] + o[0];
8525             var y = a2[1] - a1[1] + o[1];
8526             if(c){
8527                 //constrain the aligned el to viewport if necessary
8528                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8529                 // 5px of margin for ie
8530                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8531
8532                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8533                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8534                 //otherwise swap the aligned el to the opposite border of the target.
8535                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8536                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8537                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8538                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8539
8540                var doc = document;
8541                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8542                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8543
8544                if((x+w) > dw + scrollX){
8545                     x = swapX ? r.left-w : dw+scrollX-w;
8546                 }
8547                if(x < scrollX){
8548                    x = swapX ? r.right : scrollX;
8549                }
8550                if((y+h) > dh + scrollY){
8551                     y = swapY ? r.top-h : dh+scrollY-h;
8552                 }
8553                if (y < scrollY){
8554                    y = swapY ? r.bottom : scrollY;
8555                }
8556             }
8557             return [x,y];
8558         },
8559
8560         // private
8561         getConstrainToXY : function(){
8562             var os = {top:0, left:0, bottom:0, right: 0};
8563
8564             return function(el, local, offsets, proposedXY){
8565                 el = Roo.get(el);
8566                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8567
8568                 var vw, vh, vx = 0, vy = 0;
8569                 if(el.dom == document.body || el.dom == document){
8570                     vw = Roo.lib.Dom.getViewWidth();
8571                     vh = Roo.lib.Dom.getViewHeight();
8572                 }else{
8573                     vw = el.dom.clientWidth;
8574                     vh = el.dom.clientHeight;
8575                     if(!local){
8576                         var vxy = el.getXY();
8577                         vx = vxy[0];
8578                         vy = vxy[1];
8579                     }
8580                 }
8581
8582                 var s = el.getScroll();
8583
8584                 vx += offsets.left + s.left;
8585                 vy += offsets.top + s.top;
8586
8587                 vw -= offsets.right;
8588                 vh -= offsets.bottom;
8589
8590                 var vr = vx+vw;
8591                 var vb = vy+vh;
8592
8593                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8594                 var x = xy[0], y = xy[1];
8595                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8596
8597                 // only move it if it needs it
8598                 var moved = false;
8599
8600                 // first validate right/bottom
8601                 if((x + w) > vr){
8602                     x = vr - w;
8603                     moved = true;
8604                 }
8605                 if((y + h) > vb){
8606                     y = vb - h;
8607                     moved = true;
8608                 }
8609                 // then make sure top/left isn't negative
8610                 if(x < vx){
8611                     x = vx;
8612                     moved = true;
8613                 }
8614                 if(y < vy){
8615                     y = vy;
8616                     moved = true;
8617                 }
8618                 return moved ? [x, y] : false;
8619             };
8620         }(),
8621
8622         // private
8623         adjustForConstraints : function(xy, parent, offsets){
8624             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8625         },
8626
8627         /**
8628          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8629          * document it aligns it to the viewport.
8630          * The position parameter is optional, and can be specified in any one of the following formats:
8631          * <ul>
8632          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8633          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8634          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8635          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8636          *   <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
8637          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8638          * </ul>
8639          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8640          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8641          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8642          * that specified in order to enforce the viewport constraints.
8643          * Following are all of the supported anchor positions:
8644     <pre>
8645     Value  Description
8646     -----  -----------------------------
8647     tl     The top left corner (default)
8648     t      The center of the top edge
8649     tr     The top right corner
8650     l      The center of the left edge
8651     c      In the center of the element
8652     r      The center of the right edge
8653     bl     The bottom left corner
8654     b      The center of the bottom edge
8655     br     The bottom right corner
8656     </pre>
8657     Example Usage:
8658     <pre><code>
8659     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8660     el.alignTo("other-el");
8661
8662     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8663     el.alignTo("other-el", "tr?");
8664
8665     // align the bottom right corner of el with the center left edge of other-el
8666     el.alignTo("other-el", "br-l?");
8667
8668     // align the center of el with the bottom left corner of other-el and
8669     // adjust the x position by -6 pixels (and the y position by 0)
8670     el.alignTo("other-el", "c-bl", [-6, 0]);
8671     </code></pre>
8672          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8673          * @param {String} position The position to align to.
8674          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8675          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8676          * @return {Roo.Element} this
8677          */
8678         alignTo : function(element, position, offsets, animate){
8679             var xy = this.getAlignToXY(element, position, offsets);
8680             this.setXY(xy, this.preanim(arguments, 3));
8681             return this;
8682         },
8683
8684         /**
8685          * Anchors an element to another element and realigns it when the window is resized.
8686          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8687          * @param {String} position The position to align to.
8688          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8689          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8690          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8691          * is a number, it is used as the buffer delay (defaults to 50ms).
8692          * @param {Function} callback The function to call after the animation finishes
8693          * @return {Roo.Element} this
8694          */
8695         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8696             var action = function(){
8697                 this.alignTo(el, alignment, offsets, animate);
8698                 Roo.callback(callback, this);
8699             };
8700             Roo.EventManager.onWindowResize(action, this);
8701             var tm = typeof monitorScroll;
8702             if(tm != 'undefined'){
8703                 Roo.EventManager.on(window, 'scroll', action, this,
8704                     {buffer: tm == 'number' ? monitorScroll : 50});
8705             }
8706             action.call(this); // align immediately
8707             return this;
8708         },
8709         /**
8710          * Clears any opacity settings from this element. Required in some cases for IE.
8711          * @return {Roo.Element} this
8712          */
8713         clearOpacity : function(){
8714             if (window.ActiveXObject) {
8715                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8716                     this.dom.style.filter = "";
8717                 }
8718             } else {
8719                 this.dom.style.opacity = "";
8720                 this.dom.style["-moz-opacity"] = "";
8721                 this.dom.style["-khtml-opacity"] = "";
8722             }
8723             return this;
8724         },
8725
8726         /**
8727          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8728          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8729          * @return {Roo.Element} this
8730          */
8731         hide : function(animate){
8732             this.setVisible(false, this.preanim(arguments, 0));
8733             return this;
8734         },
8735
8736         /**
8737         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8738         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8739          * @return {Roo.Element} this
8740          */
8741         show : function(animate){
8742             this.setVisible(true, this.preanim(arguments, 0));
8743             return this;
8744         },
8745
8746         /**
8747          * @private Test if size has a unit, otherwise appends the default
8748          */
8749         addUnits : function(size){
8750             return Roo.Element.addUnits(size, this.defaultUnit);
8751         },
8752
8753         /**
8754          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8755          * @return {Roo.Element} this
8756          */
8757         beginMeasure : function(){
8758             var el = this.dom;
8759             if(el.offsetWidth || el.offsetHeight){
8760                 return this; // offsets work already
8761             }
8762             var changed = [];
8763             var p = this.dom, b = document.body; // start with this element
8764             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8765                 var pe = Roo.get(p);
8766                 if(pe.getStyle('display') == 'none'){
8767                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8768                     p.style.visibility = "hidden";
8769                     p.style.display = "block";
8770                 }
8771                 p = p.parentNode;
8772             }
8773             this._measureChanged = changed;
8774             return this;
8775
8776         },
8777
8778         /**
8779          * Restores displays to before beginMeasure was called
8780          * @return {Roo.Element} this
8781          */
8782         endMeasure : function(){
8783             var changed = this._measureChanged;
8784             if(changed){
8785                 for(var i = 0, len = changed.length; i < len; i++) {
8786                     var r = changed[i];
8787                     r.el.style.visibility = r.visibility;
8788                     r.el.style.display = "none";
8789                 }
8790                 this._measureChanged = null;
8791             }
8792             return this;
8793         },
8794
8795         /**
8796         * Update the innerHTML of this element, optionally searching for and processing scripts
8797         * @param {String} html The new HTML
8798         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8799         * @param {Function} callback For async script loading you can be noticed when the update completes
8800         * @return {Roo.Element} this
8801          */
8802         update : function(html, loadScripts, callback){
8803             if(typeof html == "undefined"){
8804                 html = "";
8805             }
8806             if(loadScripts !== true){
8807                 this.dom.innerHTML = html;
8808                 if(typeof callback == "function"){
8809                     callback();
8810                 }
8811                 return this;
8812             }
8813             var id = Roo.id();
8814             var dom = this.dom;
8815
8816             html += '<span id="' + id + '"></span>';
8817
8818             E.onAvailable(id, function(){
8819                 var hd = document.getElementsByTagName("head")[0];
8820                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8821                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8822                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8823
8824                 var match;
8825                 while(match = re.exec(html)){
8826                     var attrs = match[1];
8827                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8828                     if(srcMatch && srcMatch[2]){
8829                        var s = document.createElement("script");
8830                        s.src = srcMatch[2];
8831                        var typeMatch = attrs.match(typeRe);
8832                        if(typeMatch && typeMatch[2]){
8833                            s.type = typeMatch[2];
8834                        }
8835                        hd.appendChild(s);
8836                     }else if(match[2] && match[2].length > 0){
8837                         if(window.execScript) {
8838                            window.execScript(match[2]);
8839                         } else {
8840                             /**
8841                              * eval:var:id
8842                              * eval:var:dom
8843                              * eval:var:html
8844                              * 
8845                              */
8846                            window.eval(match[2]);
8847                         }
8848                     }
8849                 }
8850                 var el = document.getElementById(id);
8851                 if(el){el.parentNode.removeChild(el);}
8852                 if(typeof callback == "function"){
8853                     callback();
8854                 }
8855             });
8856             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8857             return this;
8858         },
8859
8860         /**
8861          * Direct access to the UpdateManager update() method (takes the same parameters).
8862          * @param {String/Function} url The url for this request or a function to call to get the url
8863          * @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}
8864          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8865          * @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.
8866          * @return {Roo.Element} this
8867          */
8868         load : function(){
8869             var um = this.getUpdateManager();
8870             um.update.apply(um, arguments);
8871             return this;
8872         },
8873
8874         /**
8875         * Gets this element's UpdateManager
8876         * @return {Roo.UpdateManager} The UpdateManager
8877         */
8878         getUpdateManager : function(){
8879             if(!this.updateManager){
8880                 this.updateManager = new Roo.UpdateManager(this);
8881             }
8882             return this.updateManager;
8883         },
8884
8885         /**
8886          * Disables text selection for this element (normalized across browsers)
8887          * @return {Roo.Element} this
8888          */
8889         unselectable : function(){
8890             this.dom.unselectable = "on";
8891             this.swallowEvent("selectstart", true);
8892             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8893             this.addClass("x-unselectable");
8894             return this;
8895         },
8896
8897         /**
8898         * Calculates the x, y to center this element on the screen
8899         * @return {Array} The x, y values [x, y]
8900         */
8901         getCenterXY : function(){
8902             return this.getAlignToXY(document, 'c-c');
8903         },
8904
8905         /**
8906         * Centers the Element in either the viewport, or another Element.
8907         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8908         */
8909         center : function(centerIn){
8910             this.alignTo(centerIn || document, 'c-c');
8911             return this;
8912         },
8913
8914         /**
8915          * Tests various css rules/browsers to determine if this element uses a border box
8916          * @return {Boolean}
8917          */
8918         isBorderBox : function(){
8919             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8920         },
8921
8922         /**
8923          * Return a box {x, y, width, height} that can be used to set another elements
8924          * size/location to match this element.
8925          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8926          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8927          * @return {Object} box An object in the format {x, y, width, height}
8928          */
8929         getBox : function(contentBox, local){
8930             var xy;
8931             if(!local){
8932                 xy = this.getXY();
8933             }else{
8934                 var left = parseInt(this.getStyle("left"), 10) || 0;
8935                 var top = parseInt(this.getStyle("top"), 10) || 0;
8936                 xy = [left, top];
8937             }
8938             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8939             if(!contentBox){
8940                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8941             }else{
8942                 var l = this.getBorderWidth("l")+this.getPadding("l");
8943                 var r = this.getBorderWidth("r")+this.getPadding("r");
8944                 var t = this.getBorderWidth("t")+this.getPadding("t");
8945                 var b = this.getBorderWidth("b")+this.getPadding("b");
8946                 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)};
8947             }
8948             bx.right = bx.x + bx.width;
8949             bx.bottom = bx.y + bx.height;
8950             return bx;
8951         },
8952
8953         /**
8954          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8955          for more information about the sides.
8956          * @param {String} sides
8957          * @return {Number}
8958          */
8959         getFrameWidth : function(sides, onlyContentBox){
8960             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8961         },
8962
8963         /**
8964          * 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.
8965          * @param {Object} box The box to fill {x, y, width, height}
8966          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8967          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8968          * @return {Roo.Element} this
8969          */
8970         setBox : function(box, adjust, animate){
8971             var w = box.width, h = box.height;
8972             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8973                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8974                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8975             }
8976             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8977             return this;
8978         },
8979
8980         /**
8981          * Forces the browser to repaint this element
8982          * @return {Roo.Element} this
8983          */
8984          repaint : function(){
8985             var dom = this.dom;
8986             this.addClass("x-repaint");
8987             setTimeout(function(){
8988                 Roo.get(dom).removeClass("x-repaint");
8989             }, 1);
8990             return this;
8991         },
8992
8993         /**
8994          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8995          * then it returns the calculated width of the sides (see getPadding)
8996          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8997          * @return {Object/Number}
8998          */
8999         getMargins : function(side){
9000             if(!side){
9001                 return {
9002                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9003                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9004                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9005                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9006                 };
9007             }else{
9008                 return this.addStyles(side, El.margins);
9009              }
9010         },
9011
9012         // private
9013         addStyles : function(sides, styles){
9014             var val = 0, v, w;
9015             for(var i = 0, len = sides.length; i < len; i++){
9016                 v = this.getStyle(styles[sides.charAt(i)]);
9017                 if(v){
9018                      w = parseInt(v, 10);
9019                      if(w){ val += w; }
9020                 }
9021             }
9022             return val;
9023         },
9024
9025         /**
9026          * Creates a proxy element of this element
9027          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9028          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9029          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9030          * @return {Roo.Element} The new proxy element
9031          */
9032         createProxy : function(config, renderTo, matchBox){
9033             if(renderTo){
9034                 renderTo = Roo.getDom(renderTo);
9035             }else{
9036                 renderTo = document.body;
9037             }
9038             config = typeof config == "object" ?
9039                 config : {tag : "div", cls: config};
9040             var proxy = Roo.DomHelper.append(renderTo, config, true);
9041             if(matchBox){
9042                proxy.setBox(this.getBox());
9043             }
9044             return proxy;
9045         },
9046
9047         /**
9048          * Puts a mask over this element to disable user interaction. Requires core.css.
9049          * This method can only be applied to elements which accept child nodes.
9050          * @param {String} msg (optional) A message to display in the mask
9051          * @param {String} msgCls (optional) A css class to apply to the msg element
9052          * @return {Element} The mask  element
9053          */
9054         mask : function(msg, msgCls)
9055         {
9056             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9057                 this.setStyle("position", "relative");
9058             }
9059             if(!this._mask){
9060                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9061             }
9062             this.addClass("x-masked");
9063             this._mask.setDisplayed(true);
9064             
9065             // we wander
9066             var z = 0;
9067             var dom = this.dom;
9068             while (dom && dom.style) {
9069                 if (!isNaN(parseInt(dom.style.zIndex))) {
9070                     z = Math.max(z, parseInt(dom.style.zIndex));
9071                 }
9072                 dom = dom.parentNode;
9073             }
9074             // if we are masking the body - then it hides everything..
9075             if (this.dom == document.body) {
9076                 z = 1000000;
9077                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9078                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9079             }
9080            
9081             if(typeof msg == 'string'){
9082                 if(!this._maskMsg){
9083                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9084                 }
9085                 var mm = this._maskMsg;
9086                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9087                 if (mm.dom.firstChild) { // weird IE issue?
9088                     mm.dom.firstChild.innerHTML = msg;
9089                 }
9090                 mm.setDisplayed(true);
9091                 mm.center(this);
9092                 mm.setStyle('z-index', z + 102);
9093             }
9094             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9095                 this._mask.setHeight(this.getHeight());
9096             }
9097             this._mask.setStyle('z-index', z + 100);
9098             
9099             return this._mask;
9100         },
9101
9102         /**
9103          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9104          * it is cached for reuse.
9105          */
9106         unmask : function(removeEl){
9107             if(this._mask){
9108                 if(removeEl === true){
9109                     this._mask.remove();
9110                     delete this._mask;
9111                     if(this._maskMsg){
9112                         this._maskMsg.remove();
9113                         delete this._maskMsg;
9114                     }
9115                 }else{
9116                     this._mask.setDisplayed(false);
9117                     if(this._maskMsg){
9118                         this._maskMsg.setDisplayed(false);
9119                     }
9120                 }
9121             }
9122             this.removeClass("x-masked");
9123         },
9124
9125         /**
9126          * Returns true if this element is masked
9127          * @return {Boolean}
9128          */
9129         isMasked : function(){
9130             return this._mask && this._mask.isVisible();
9131         },
9132
9133         /**
9134          * Creates an iframe shim for this element to keep selects and other windowed objects from
9135          * showing through.
9136          * @return {Roo.Element} The new shim element
9137          */
9138         createShim : function(){
9139             var el = document.createElement('iframe');
9140             el.frameBorder = 'no';
9141             el.className = 'roo-shim';
9142             if(Roo.isIE && Roo.isSecure){
9143                 el.src = Roo.SSL_SECURE_URL;
9144             }
9145             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9146             shim.autoBoxAdjust = false;
9147             return shim;
9148         },
9149
9150         /**
9151          * Removes this element from the DOM and deletes it from the cache
9152          */
9153         remove : function(){
9154             if(this.dom.parentNode){
9155                 this.dom.parentNode.removeChild(this.dom);
9156             }
9157             delete El.cache[this.dom.id];
9158         },
9159
9160         /**
9161          * Sets up event handlers to add and remove a css class when the mouse is over this element
9162          * @param {String} className
9163          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9164          * mouseout events for children elements
9165          * @return {Roo.Element} this
9166          */
9167         addClassOnOver : function(className, preventFlicker){
9168             this.on("mouseover", function(){
9169                 Roo.fly(this, '_internal').addClass(className);
9170             }, this.dom);
9171             var removeFn = function(e){
9172                 if(preventFlicker !== true || !e.within(this, true)){
9173                     Roo.fly(this, '_internal').removeClass(className);
9174                 }
9175             };
9176             this.on("mouseout", removeFn, this.dom);
9177             return this;
9178         },
9179
9180         /**
9181          * Sets up event handlers to add and remove a css class when this element has the focus
9182          * @param {String} className
9183          * @return {Roo.Element} this
9184          */
9185         addClassOnFocus : function(className){
9186             this.on("focus", function(){
9187                 Roo.fly(this, '_internal').addClass(className);
9188             }, this.dom);
9189             this.on("blur", function(){
9190                 Roo.fly(this, '_internal').removeClass(className);
9191             }, this.dom);
9192             return this;
9193         },
9194         /**
9195          * 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)
9196          * @param {String} className
9197          * @return {Roo.Element} this
9198          */
9199         addClassOnClick : function(className){
9200             var dom = this.dom;
9201             this.on("mousedown", function(){
9202                 Roo.fly(dom, '_internal').addClass(className);
9203                 var d = Roo.get(document);
9204                 var fn = function(){
9205                     Roo.fly(dom, '_internal').removeClass(className);
9206                     d.removeListener("mouseup", fn);
9207                 };
9208                 d.on("mouseup", fn);
9209             });
9210             return this;
9211         },
9212
9213         /**
9214          * Stops the specified event from bubbling and optionally prevents the default action
9215          * @param {String} eventName
9216          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9217          * @return {Roo.Element} this
9218          */
9219         swallowEvent : function(eventName, preventDefault){
9220             var fn = function(e){
9221                 e.stopPropagation();
9222                 if(preventDefault){
9223                     e.preventDefault();
9224                 }
9225             };
9226             if(eventName instanceof Array){
9227                 for(var i = 0, len = eventName.length; i < len; i++){
9228                      this.on(eventName[i], fn);
9229                 }
9230                 return this;
9231             }
9232             this.on(eventName, fn);
9233             return this;
9234         },
9235
9236         /**
9237          * @private
9238          */
9239       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9240
9241         /**
9242          * Sizes this element to its parent element's dimensions performing
9243          * neccessary box adjustments.
9244          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9245          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9246          * @return {Roo.Element} this
9247          */
9248         fitToParent : function(monitorResize, targetParent) {
9249           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9250           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9251           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9252             return;
9253           }
9254           var p = Roo.get(targetParent || this.dom.parentNode);
9255           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9256           if (monitorResize === true) {
9257             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9258             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9259           }
9260           return this;
9261         },
9262
9263         /**
9264          * Gets the next sibling, skipping text nodes
9265          * @return {HTMLElement} The next sibling or null
9266          */
9267         getNextSibling : function(){
9268             var n = this.dom.nextSibling;
9269             while(n && n.nodeType != 1){
9270                 n = n.nextSibling;
9271             }
9272             return n;
9273         },
9274
9275         /**
9276          * Gets the previous sibling, skipping text nodes
9277          * @return {HTMLElement} The previous sibling or null
9278          */
9279         getPrevSibling : function(){
9280             var n = this.dom.previousSibling;
9281             while(n && n.nodeType != 1){
9282                 n = n.previousSibling;
9283             }
9284             return n;
9285         },
9286
9287
9288         /**
9289          * Appends the passed element(s) to this element
9290          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9291          * @return {Roo.Element} this
9292          */
9293         appendChild: function(el){
9294             el = Roo.get(el);
9295             el.appendTo(this);
9296             return this;
9297         },
9298
9299         /**
9300          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9301          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9302          * automatically generated with the specified attributes.
9303          * @param {HTMLElement} insertBefore (optional) a child element of this element
9304          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9305          * @return {Roo.Element} The new child element
9306          */
9307         createChild: function(config, insertBefore, returnDom){
9308             config = config || {tag:'div'};
9309             if(insertBefore){
9310                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9311             }
9312             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9313         },
9314
9315         /**
9316          * Appends this element to the passed element
9317          * @param {String/HTMLElement/Element} el The new parent element
9318          * @return {Roo.Element} this
9319          */
9320         appendTo: function(el){
9321             el = Roo.getDom(el);
9322             el.appendChild(this.dom);
9323             return this;
9324         },
9325
9326         /**
9327          * Inserts this element before the passed element in the DOM
9328          * @param {String/HTMLElement/Element} el The element to insert before
9329          * @return {Roo.Element} this
9330          */
9331         insertBefore: function(el){
9332             el = Roo.getDom(el);
9333             el.parentNode.insertBefore(this.dom, el);
9334             return this;
9335         },
9336
9337         /**
9338          * Inserts this element after the passed element in the DOM
9339          * @param {String/HTMLElement/Element} el The element to insert after
9340          * @return {Roo.Element} this
9341          */
9342         insertAfter: function(el){
9343             el = Roo.getDom(el);
9344             el.parentNode.insertBefore(this.dom, el.nextSibling);
9345             return this;
9346         },
9347
9348         /**
9349          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9350          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9351          * @return {Roo.Element} The new child
9352          */
9353         insertFirst: function(el, returnDom){
9354             el = el || {};
9355             if(typeof el == 'object' && !el.nodeType){ // dh config
9356                 return this.createChild(el, this.dom.firstChild, returnDom);
9357             }else{
9358                 el = Roo.getDom(el);
9359                 this.dom.insertBefore(el, this.dom.firstChild);
9360                 return !returnDom ? Roo.get(el) : el;
9361             }
9362         },
9363
9364         /**
9365          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9366          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9367          * @param {String} where (optional) 'before' or 'after' defaults to before
9368          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9369          * @return {Roo.Element} the inserted Element
9370          */
9371         insertSibling: function(el, where, returnDom){
9372             where = where ? where.toLowerCase() : 'before';
9373             el = el || {};
9374             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9375
9376             if(typeof el == 'object' && !el.nodeType){ // dh config
9377                 if(where == 'after' && !this.dom.nextSibling){
9378                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9379                 }else{
9380                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9381                 }
9382
9383             }else{
9384                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9385                             where == 'before' ? this.dom : this.dom.nextSibling);
9386                 if(!returnDom){
9387                     rt = Roo.get(rt);
9388                 }
9389             }
9390             return rt;
9391         },
9392
9393         /**
9394          * Creates and wraps this element with another element
9395          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9396          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9397          * @return {HTMLElement/Element} The newly created wrapper element
9398          */
9399         wrap: function(config, returnDom){
9400             if(!config){
9401                 config = {tag: "div"};
9402             }
9403             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9404             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9405             return newEl;
9406         },
9407
9408         /**
9409          * Replaces the passed element with this element
9410          * @param {String/HTMLElement/Element} el The element to replace
9411          * @return {Roo.Element} this
9412          */
9413         replace: function(el){
9414             el = Roo.get(el);
9415             this.insertBefore(el);
9416             el.remove();
9417             return this;
9418         },
9419
9420         /**
9421          * Inserts an html fragment into this element
9422          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9423          * @param {String} html The HTML fragment
9424          * @param {Boolean} returnEl True to return an Roo.Element
9425          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9426          */
9427         insertHtml : function(where, html, returnEl){
9428             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9429             return returnEl ? Roo.get(el) : el;
9430         },
9431
9432         /**
9433          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9434          * @param {Object} o The object with the attributes
9435          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9436          * @return {Roo.Element} this
9437          */
9438         set : function(o, useSet){
9439             var el = this.dom;
9440             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9441             for(var attr in o){
9442                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9443                 if(attr=="cls"){
9444                     el.className = o["cls"];
9445                 }else{
9446                     if(useSet) {
9447                         el.setAttribute(attr, o[attr]);
9448                     } else {
9449                         el[attr] = o[attr];
9450                     }
9451                 }
9452             }
9453             if(o.style){
9454                 Roo.DomHelper.applyStyles(el, o.style);
9455             }
9456             return this;
9457         },
9458
9459         /**
9460          * Convenience method for constructing a KeyMap
9461          * @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:
9462          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9463          * @param {Function} fn The function to call
9464          * @param {Object} scope (optional) The scope of the function
9465          * @return {Roo.KeyMap} The KeyMap created
9466          */
9467         addKeyListener : function(key, fn, scope){
9468             var config;
9469             if(typeof key != "object" || key instanceof Array){
9470                 config = {
9471                     key: key,
9472                     fn: fn,
9473                     scope: scope
9474                 };
9475             }else{
9476                 config = {
9477                     key : key.key,
9478                     shift : key.shift,
9479                     ctrl : key.ctrl,
9480                     alt : key.alt,
9481                     fn: fn,
9482                     scope: scope
9483                 };
9484             }
9485             return new Roo.KeyMap(this, config);
9486         },
9487
9488         /**
9489          * Creates a KeyMap for this element
9490          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9491          * @return {Roo.KeyMap} The KeyMap created
9492          */
9493         addKeyMap : function(config){
9494             return new Roo.KeyMap(this, config);
9495         },
9496
9497         /**
9498          * Returns true if this element is scrollable.
9499          * @return {Boolean}
9500          */
9501          isScrollable : function(){
9502             var dom = this.dom;
9503             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9504         },
9505
9506         /**
9507          * 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().
9508          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9509          * @param {Number} value The new scroll value
9510          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9511          * @return {Element} this
9512          */
9513
9514         scrollTo : function(side, value, animate){
9515             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9516             if(!animate || !A){
9517                 this.dom[prop] = value;
9518             }else{
9519                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9520                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9521             }
9522             return this;
9523         },
9524
9525         /**
9526          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9527          * within this element's scrollable range.
9528          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9529          * @param {Number} distance How far to scroll the element in pixels
9530          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9531          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9532          * was scrolled as far as it could go.
9533          */
9534          scroll : function(direction, distance, animate){
9535              if(!this.isScrollable()){
9536                  return;
9537              }
9538              var el = this.dom;
9539              var l = el.scrollLeft, t = el.scrollTop;
9540              var w = el.scrollWidth, h = el.scrollHeight;
9541              var cw = el.clientWidth, ch = el.clientHeight;
9542              direction = direction.toLowerCase();
9543              var scrolled = false;
9544              var a = this.preanim(arguments, 2);
9545              switch(direction){
9546                  case "l":
9547                  case "left":
9548                      if(w - l > cw){
9549                          var v = Math.min(l + distance, w-cw);
9550                          this.scrollTo("left", v, a);
9551                          scrolled = true;
9552                      }
9553                      break;
9554                 case "r":
9555                 case "right":
9556                      if(l > 0){
9557                          var v = Math.max(l - distance, 0);
9558                          this.scrollTo("left", v, a);
9559                          scrolled = true;
9560                      }
9561                      break;
9562                 case "t":
9563                 case "top":
9564                 case "up":
9565                      if(t > 0){
9566                          var v = Math.max(t - distance, 0);
9567                          this.scrollTo("top", v, a);
9568                          scrolled = true;
9569                      }
9570                      break;
9571                 case "b":
9572                 case "bottom":
9573                 case "down":
9574                      if(h - t > ch){
9575                          var v = Math.min(t + distance, h-ch);
9576                          this.scrollTo("top", v, a);
9577                          scrolled = true;
9578                      }
9579                      break;
9580              }
9581              return scrolled;
9582         },
9583
9584         /**
9585          * Translates the passed page coordinates into left/top css values for this element
9586          * @param {Number/Array} x The page x or an array containing [x, y]
9587          * @param {Number} y The page y
9588          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9589          */
9590         translatePoints : function(x, y){
9591             if(typeof x == 'object' || x instanceof Array){
9592                 y = x[1]; x = x[0];
9593             }
9594             var p = this.getStyle('position');
9595             var o = this.getXY();
9596
9597             var l = parseInt(this.getStyle('left'), 10);
9598             var t = parseInt(this.getStyle('top'), 10);
9599
9600             if(isNaN(l)){
9601                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9602             }
9603             if(isNaN(t)){
9604                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9605             }
9606
9607             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9608         },
9609
9610         /**
9611          * Returns the current scroll position of the element.
9612          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9613          */
9614         getScroll : function(){
9615             var d = this.dom, doc = document;
9616             if(d == doc || d == doc.body){
9617                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9618                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9619                 return {left: l, top: t};
9620             }else{
9621                 return {left: d.scrollLeft, top: d.scrollTop};
9622             }
9623         },
9624
9625         /**
9626          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9627          * are convert to standard 6 digit hex color.
9628          * @param {String} attr The css attribute
9629          * @param {String} defaultValue The default value to use when a valid color isn't found
9630          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9631          * YUI color anims.
9632          */
9633         getColor : function(attr, defaultValue, prefix){
9634             var v = this.getStyle(attr);
9635             if(!v || v == "transparent" || v == "inherit") {
9636                 return defaultValue;
9637             }
9638             var color = typeof prefix == "undefined" ? "#" : prefix;
9639             if(v.substr(0, 4) == "rgb("){
9640                 var rvs = v.slice(4, v.length -1).split(",");
9641                 for(var i = 0; i < 3; i++){
9642                     var h = parseInt(rvs[i]).toString(16);
9643                     if(h < 16){
9644                         h = "0" + h;
9645                     }
9646                     color += h;
9647                 }
9648             } else {
9649                 if(v.substr(0, 1) == "#"){
9650                     if(v.length == 4) {
9651                         for(var i = 1; i < 4; i++){
9652                             var c = v.charAt(i);
9653                             color +=  c + c;
9654                         }
9655                     }else if(v.length == 7){
9656                         color += v.substr(1);
9657                     }
9658                 }
9659             }
9660             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9661         },
9662
9663         /**
9664          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9665          * gradient background, rounded corners and a 4-way shadow.
9666          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9667          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9668          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9669          * @return {Roo.Element} this
9670          */
9671         boxWrap : function(cls){
9672             cls = cls || 'x-box';
9673             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9674             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9675             return el;
9676         },
9677
9678         /**
9679          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9680          * @param {String} namespace The namespace in which to look for the attribute
9681          * @param {String} name The attribute name
9682          * @return {String} The attribute value
9683          */
9684         getAttributeNS : Roo.isIE ? function(ns, name){
9685             var d = this.dom;
9686             var type = typeof d[ns+":"+name];
9687             if(type != 'undefined' && type != 'unknown'){
9688                 return d[ns+":"+name];
9689             }
9690             return d[name];
9691         } : function(ns, name){
9692             var d = this.dom;
9693             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9694         },
9695         
9696         
9697         /**
9698          * Sets or Returns the value the dom attribute value
9699          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9700          * @param {String} value (optional) The value to set the attribute to
9701          * @return {String} The attribute value
9702          */
9703         attr : function(name){
9704             if (arguments.length > 1) {
9705                 this.dom.setAttribute(name, arguments[1]);
9706                 return arguments[1];
9707             }
9708             if (typeof(name) == 'object') {
9709                 for(var i in name) {
9710                     this.attr(i, name[i]);
9711                 }
9712                 return name;
9713             }
9714             
9715             
9716             if (!this.dom.hasAttribute(name)) {
9717                 return undefined;
9718             }
9719             return this.dom.getAttribute(name);
9720         }
9721         
9722         
9723         
9724     };
9725
9726     var ep = El.prototype;
9727
9728     /**
9729      * Appends an event handler (Shorthand for addListener)
9730      * @param {String}   eventName     The type of event to append
9731      * @param {Function} fn        The method the event invokes
9732      * @param {Object} scope       (optional) The scope (this object) of the fn
9733      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9734      * @method
9735      */
9736     ep.on = ep.addListener;
9737         // backwards compat
9738     ep.mon = ep.addListener;
9739
9740     /**
9741      * Removes an event handler from this element (shorthand for removeListener)
9742      * @param {String} eventName the type of event to remove
9743      * @param {Function} fn the method the event invokes
9744      * @return {Roo.Element} this
9745      * @method
9746      */
9747     ep.un = ep.removeListener;
9748
9749     /**
9750      * true to automatically adjust width and height settings for box-model issues (default to true)
9751      */
9752     ep.autoBoxAdjust = true;
9753
9754     // private
9755     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9756
9757     // private
9758     El.addUnits = function(v, defaultUnit){
9759         if(v === "" || v == "auto"){
9760             return v;
9761         }
9762         if(v === undefined){
9763             return '';
9764         }
9765         if(typeof v == "number" || !El.unitPattern.test(v)){
9766             return v + (defaultUnit || 'px');
9767         }
9768         return v;
9769     };
9770
9771     // special markup used throughout Roo when box wrapping elements
9772     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>';
9773     /**
9774      * Visibility mode constant - Use visibility to hide element
9775      * @static
9776      * @type Number
9777      */
9778     El.VISIBILITY = 1;
9779     /**
9780      * Visibility mode constant - Use display to hide element
9781      * @static
9782      * @type Number
9783      */
9784     El.DISPLAY = 2;
9785
9786     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9787     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9788     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9789
9790
9791
9792     /**
9793      * @private
9794      */
9795     El.cache = {};
9796
9797     var docEl;
9798
9799     /**
9800      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9801      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9802      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9803      * @return {Element} The Element object
9804      * @static
9805      */
9806     El.get = function(el){
9807         var ex, elm, id;
9808         if(!el){ return null; }
9809         if(typeof el == "string"){ // element id
9810             if(!(elm = document.getElementById(el))){
9811                 return null;
9812             }
9813             if(ex = El.cache[el]){
9814                 ex.dom = elm;
9815             }else{
9816                 ex = El.cache[el] = new El(elm);
9817             }
9818             return ex;
9819         }else if(el.tagName){ // dom element
9820             if(!(id = el.id)){
9821                 id = Roo.id(el);
9822             }
9823             if(ex = El.cache[id]){
9824                 ex.dom = el;
9825             }else{
9826                 ex = El.cache[id] = new El(el);
9827             }
9828             return ex;
9829         }else if(el instanceof El){
9830             if(el != docEl){
9831                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9832                                                               // catch case where it hasn't been appended
9833                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9834             }
9835             return el;
9836         }else if(el.isComposite){
9837             return el;
9838         }else if(el instanceof Array){
9839             return El.select(el);
9840         }else if(el == document){
9841             // create a bogus element object representing the document object
9842             if(!docEl){
9843                 var f = function(){};
9844                 f.prototype = El.prototype;
9845                 docEl = new f();
9846                 docEl.dom = document;
9847             }
9848             return docEl;
9849         }
9850         return null;
9851     };
9852
9853     // private
9854     El.uncache = function(el){
9855         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9856             if(a[i]){
9857                 delete El.cache[a[i].id || a[i]];
9858             }
9859         }
9860     };
9861
9862     // private
9863     // Garbage collection - uncache elements/purge listeners on orphaned elements
9864     // so we don't hold a reference and cause the browser to retain them
9865     El.garbageCollect = function(){
9866         if(!Roo.enableGarbageCollector){
9867             clearInterval(El.collectorThread);
9868             return;
9869         }
9870         for(var eid in El.cache){
9871             var el = El.cache[eid], d = el.dom;
9872             // -------------------------------------------------------
9873             // Determining what is garbage:
9874             // -------------------------------------------------------
9875             // !d
9876             // dom node is null, definitely garbage
9877             // -------------------------------------------------------
9878             // !d.parentNode
9879             // no parentNode == direct orphan, definitely garbage
9880             // -------------------------------------------------------
9881             // !d.offsetParent && !document.getElementById(eid)
9882             // display none elements have no offsetParent so we will
9883             // also try to look it up by it's id. However, check
9884             // offsetParent first so we don't do unneeded lookups.
9885             // This enables collection of elements that are not orphans
9886             // directly, but somewhere up the line they have an orphan
9887             // parent.
9888             // -------------------------------------------------------
9889             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9890                 delete El.cache[eid];
9891                 if(d && Roo.enableListenerCollection){
9892                     E.purgeElement(d);
9893                 }
9894             }
9895         }
9896     }
9897     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9898
9899
9900     // dom is optional
9901     El.Flyweight = function(dom){
9902         this.dom = dom;
9903     };
9904     El.Flyweight.prototype = El.prototype;
9905
9906     El._flyweights = {};
9907     /**
9908      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9909      * the dom node can be overwritten by other code.
9910      * @param {String/HTMLElement} el The dom node or id
9911      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9912      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9913      * @static
9914      * @return {Element} The shared Element object
9915      */
9916     El.fly = function(el, named){
9917         named = named || '_global';
9918         el = Roo.getDom(el);
9919         if(!el){
9920             return null;
9921         }
9922         if(!El._flyweights[named]){
9923             El._flyweights[named] = new El.Flyweight();
9924         }
9925         El._flyweights[named].dom = el;
9926         return El._flyweights[named];
9927     };
9928
9929     /**
9930      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9931      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9932      * Shorthand of {@link Roo.Element#get}
9933      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9934      * @return {Element} The Element object
9935      * @member Roo
9936      * @method get
9937      */
9938     Roo.get = El.get;
9939     /**
9940      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9941      * the dom node can be overwritten by other code.
9942      * Shorthand of {@link Roo.Element#fly}
9943      * @param {String/HTMLElement} el The dom node or id
9944      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9945      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9946      * @static
9947      * @return {Element} The shared Element object
9948      * @member Roo
9949      * @method fly
9950      */
9951     Roo.fly = El.fly;
9952
9953     // speedy lookup for elements never to box adjust
9954     var noBoxAdjust = Roo.isStrict ? {
9955         select:1
9956     } : {
9957         input:1, select:1, textarea:1
9958     };
9959     if(Roo.isIE || Roo.isGecko){
9960         noBoxAdjust['button'] = 1;
9961     }
9962
9963
9964     Roo.EventManager.on(window, 'unload', function(){
9965         delete El.cache;
9966         delete El._flyweights;
9967     });
9968 })();
9969
9970
9971
9972
9973 if(Roo.DomQuery){
9974     Roo.Element.selectorFunction = Roo.DomQuery.select;
9975 }
9976
9977 Roo.Element.select = function(selector, unique, root){
9978     var els;
9979     if(typeof selector == "string"){
9980         els = Roo.Element.selectorFunction(selector, root);
9981     }else if(selector.length !== undefined){
9982         els = selector;
9983     }else{
9984         throw "Invalid selector";
9985     }
9986     if(unique === true){
9987         return new Roo.CompositeElement(els);
9988     }else{
9989         return new Roo.CompositeElementLite(els);
9990     }
9991 };
9992 /**
9993  * Selects elements based on the passed CSS selector to enable working on them as 1.
9994  * @param {String/Array} selector The CSS selector or an array of elements
9995  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9996  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9997  * @return {CompositeElementLite/CompositeElement}
9998  * @member Roo
9999  * @method select
10000  */
10001 Roo.select = Roo.Element.select;
10002
10003
10004
10005
10006
10007
10008
10009
10010
10011
10012
10013
10014
10015
10016 /*
10017  * Based on:
10018  * Ext JS Library 1.1.1
10019  * Copyright(c) 2006-2007, Ext JS, LLC.
10020  *
10021  * Originally Released Under LGPL - original licence link has changed is not relivant.
10022  *
10023  * Fork - LGPL
10024  * <script type="text/javascript">
10025  */
10026
10027
10028
10029 //Notifies Element that fx methods are available
10030 Roo.enableFx = true;
10031
10032 /**
10033  * @class Roo.Fx
10034  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10035  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10036  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10037  * Element effects to work.</p><br/>
10038  *
10039  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10040  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10041  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10042  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10043  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10044  * expected results and should be done with care.</p><br/>
10045  *
10046  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10047  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10048 <pre>
10049 Value  Description
10050 -----  -----------------------------
10051 tl     The top left corner
10052 t      The center of the top edge
10053 tr     The top right corner
10054 l      The center of the left edge
10055 r      The center of the right edge
10056 bl     The bottom left corner
10057 b      The center of the bottom edge
10058 br     The bottom right corner
10059 </pre>
10060  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10061  * below are common options that can be passed to any Fx method.</b>
10062  * @cfg {Function} callback A function called when the effect is finished
10063  * @cfg {Object} scope The scope of the effect function
10064  * @cfg {String} easing A valid Easing value for the effect
10065  * @cfg {String} afterCls A css class to apply after the effect
10066  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10067  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10068  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10069  * effects that end with the element being visually hidden, ignored otherwise)
10070  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10071  * a function which returns such a specification that will be applied to the Element after the effect finishes
10072  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10073  * @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
10074  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10075  */
10076 Roo.Fx = {
10077         /**
10078          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10079          * origin for the slide effect.  This function automatically handles wrapping the element with
10080          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10081          * Usage:
10082          *<pre><code>
10083 // default: slide the element in from the top
10084 el.slideIn();
10085
10086 // custom: slide the element in from the right with a 2-second duration
10087 el.slideIn('r', { duration: 2 });
10088
10089 // common config options shown with default values
10090 el.slideIn('t', {
10091     easing: 'easeOut',
10092     duration: .5
10093 });
10094 </code></pre>
10095          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10096          * @param {Object} options (optional) Object literal with any of the Fx config options
10097          * @return {Roo.Element} The Element
10098          */
10099     slideIn : function(anchor, o){
10100         var el = this.getFxEl();
10101         o = o || {};
10102
10103         el.queueFx(o, function(){
10104
10105             anchor = anchor || "t";
10106
10107             // fix display to visibility
10108             this.fixDisplay();
10109
10110             // restore values after effect
10111             var r = this.getFxRestore();
10112             var b = this.getBox();
10113             // fixed size for slide
10114             this.setSize(b);
10115
10116             // wrap if needed
10117             var wrap = this.fxWrap(r.pos, o, "hidden");
10118
10119             var st = this.dom.style;
10120             st.visibility = "visible";
10121             st.position = "absolute";
10122
10123             // clear out temp styles after slide and unwrap
10124             var after = function(){
10125                 el.fxUnwrap(wrap, r.pos, o);
10126                 st.width = r.width;
10127                 st.height = r.height;
10128                 el.afterFx(o);
10129             };
10130             // time to calc the positions
10131             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10132
10133             switch(anchor.toLowerCase()){
10134                 case "t":
10135                     wrap.setSize(b.width, 0);
10136                     st.left = st.bottom = "0";
10137                     a = {height: bh};
10138                 break;
10139                 case "l":
10140                     wrap.setSize(0, b.height);
10141                     st.right = st.top = "0";
10142                     a = {width: bw};
10143                 break;
10144                 case "r":
10145                     wrap.setSize(0, b.height);
10146                     wrap.setX(b.right);
10147                     st.left = st.top = "0";
10148                     a = {width: bw, points: pt};
10149                 break;
10150                 case "b":
10151                     wrap.setSize(b.width, 0);
10152                     wrap.setY(b.bottom);
10153                     st.left = st.top = "0";
10154                     a = {height: bh, points: pt};
10155                 break;
10156                 case "tl":
10157                     wrap.setSize(0, 0);
10158                     st.right = st.bottom = "0";
10159                     a = {width: bw, height: bh};
10160                 break;
10161                 case "bl":
10162                     wrap.setSize(0, 0);
10163                     wrap.setY(b.y+b.height);
10164                     st.right = st.top = "0";
10165                     a = {width: bw, height: bh, points: pt};
10166                 break;
10167                 case "br":
10168                     wrap.setSize(0, 0);
10169                     wrap.setXY([b.right, b.bottom]);
10170                     st.left = st.top = "0";
10171                     a = {width: bw, height: bh, points: pt};
10172                 break;
10173                 case "tr":
10174                     wrap.setSize(0, 0);
10175                     wrap.setX(b.x+b.width);
10176                     st.left = st.bottom = "0";
10177                     a = {width: bw, height: bh, points: pt};
10178                 break;
10179             }
10180             this.dom.style.visibility = "visible";
10181             wrap.show();
10182
10183             arguments.callee.anim = wrap.fxanim(a,
10184                 o,
10185                 'motion',
10186                 .5,
10187                 'easeOut', after);
10188         });
10189         return this;
10190     },
10191     
10192         /**
10193          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10194          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10195          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10196          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10197          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10198          * Usage:
10199          *<pre><code>
10200 // default: slide the element out to the top
10201 el.slideOut();
10202
10203 // custom: slide the element out to the right with a 2-second duration
10204 el.slideOut('r', { duration: 2 });
10205
10206 // common config options shown with default values
10207 el.slideOut('t', {
10208     easing: 'easeOut',
10209     duration: .5,
10210     remove: false,
10211     useDisplay: false
10212 });
10213 </code></pre>
10214          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10215          * @param {Object} options (optional) Object literal with any of the Fx config options
10216          * @return {Roo.Element} The Element
10217          */
10218     slideOut : function(anchor, o){
10219         var el = this.getFxEl();
10220         o = o || {};
10221
10222         el.queueFx(o, function(){
10223
10224             anchor = anchor || "t";
10225
10226             // restore values after effect
10227             var r = this.getFxRestore();
10228             
10229             var b = this.getBox();
10230             // fixed size for slide
10231             this.setSize(b);
10232
10233             // wrap if needed
10234             var wrap = this.fxWrap(r.pos, o, "visible");
10235
10236             var st = this.dom.style;
10237             st.visibility = "visible";
10238             st.position = "absolute";
10239
10240             wrap.setSize(b);
10241
10242             var after = function(){
10243                 if(o.useDisplay){
10244                     el.setDisplayed(false);
10245                 }else{
10246                     el.hide();
10247                 }
10248
10249                 el.fxUnwrap(wrap, r.pos, o);
10250
10251                 st.width = r.width;
10252                 st.height = r.height;
10253
10254                 el.afterFx(o);
10255             };
10256
10257             var a, zero = {to: 0};
10258             switch(anchor.toLowerCase()){
10259                 case "t":
10260                     st.left = st.bottom = "0";
10261                     a = {height: zero};
10262                 break;
10263                 case "l":
10264                     st.right = st.top = "0";
10265                     a = {width: zero};
10266                 break;
10267                 case "r":
10268                     st.left = st.top = "0";
10269                     a = {width: zero, points: {to:[b.right, b.y]}};
10270                 break;
10271                 case "b":
10272                     st.left = st.top = "0";
10273                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10274                 break;
10275                 case "tl":
10276                     st.right = st.bottom = "0";
10277                     a = {width: zero, height: zero};
10278                 break;
10279                 case "bl":
10280                     st.right = st.top = "0";
10281                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10282                 break;
10283                 case "br":
10284                     st.left = st.top = "0";
10285                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10286                 break;
10287                 case "tr":
10288                     st.left = st.bottom = "0";
10289                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10290                 break;
10291             }
10292
10293             arguments.callee.anim = wrap.fxanim(a,
10294                 o,
10295                 'motion',
10296                 .5,
10297                 "easeOut", after);
10298         });
10299         return this;
10300     },
10301
10302         /**
10303          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10304          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10305          * The element must be removed from the DOM using the 'remove' config option if desired.
10306          * Usage:
10307          *<pre><code>
10308 // default
10309 el.puff();
10310
10311 // common config options shown with default values
10312 el.puff({
10313     easing: 'easeOut',
10314     duration: .5,
10315     remove: false,
10316     useDisplay: false
10317 });
10318 </code></pre>
10319          * @param {Object} options (optional) Object literal with any of the Fx config options
10320          * @return {Roo.Element} The Element
10321          */
10322     puff : function(o){
10323         var el = this.getFxEl();
10324         o = o || {};
10325
10326         el.queueFx(o, function(){
10327             this.clearOpacity();
10328             this.show();
10329
10330             // restore values after effect
10331             var r = this.getFxRestore();
10332             var st = this.dom.style;
10333
10334             var after = function(){
10335                 if(o.useDisplay){
10336                     el.setDisplayed(false);
10337                 }else{
10338                     el.hide();
10339                 }
10340
10341                 el.clearOpacity();
10342
10343                 el.setPositioning(r.pos);
10344                 st.width = r.width;
10345                 st.height = r.height;
10346                 st.fontSize = '';
10347                 el.afterFx(o);
10348             };
10349
10350             var width = this.getWidth();
10351             var height = this.getHeight();
10352
10353             arguments.callee.anim = this.fxanim({
10354                     width : {to: this.adjustWidth(width * 2)},
10355                     height : {to: this.adjustHeight(height * 2)},
10356                     points : {by: [-(width * .5), -(height * .5)]},
10357                     opacity : {to: 0},
10358                     fontSize: {to:200, unit: "%"}
10359                 },
10360                 o,
10361                 'motion',
10362                 .5,
10363                 "easeOut", after);
10364         });
10365         return this;
10366     },
10367
10368         /**
10369          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10370          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10371          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10372          * Usage:
10373          *<pre><code>
10374 // default
10375 el.switchOff();
10376
10377 // all config options shown with default values
10378 el.switchOff({
10379     easing: 'easeIn',
10380     duration: .3,
10381     remove: false,
10382     useDisplay: false
10383 });
10384 </code></pre>
10385          * @param {Object} options (optional) Object literal with any of the Fx config options
10386          * @return {Roo.Element} The Element
10387          */
10388     switchOff : function(o){
10389         var el = this.getFxEl();
10390         o = o || {};
10391
10392         el.queueFx(o, function(){
10393             this.clearOpacity();
10394             this.clip();
10395
10396             // restore values after effect
10397             var r = this.getFxRestore();
10398             var st = this.dom.style;
10399
10400             var after = function(){
10401                 if(o.useDisplay){
10402                     el.setDisplayed(false);
10403                 }else{
10404                     el.hide();
10405                 }
10406
10407                 el.clearOpacity();
10408                 el.setPositioning(r.pos);
10409                 st.width = r.width;
10410                 st.height = r.height;
10411
10412                 el.afterFx(o);
10413             };
10414
10415             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10416                 this.clearOpacity();
10417                 (function(){
10418                     this.fxanim({
10419                         height:{to:1},
10420                         points:{by:[0, this.getHeight() * .5]}
10421                     }, o, 'motion', 0.3, 'easeIn', after);
10422                 }).defer(100, this);
10423             });
10424         });
10425         return this;
10426     },
10427
10428     /**
10429      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10430      * changed using the "attr" config option) and then fading back to the original color. If no original
10431      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10432      * Usage:
10433 <pre><code>
10434 // default: highlight background to yellow
10435 el.highlight();
10436
10437 // custom: highlight foreground text to blue for 2 seconds
10438 el.highlight("0000ff", { attr: 'color', duration: 2 });
10439
10440 // common config options shown with default values
10441 el.highlight("ffff9c", {
10442     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10443     endColor: (current color) or "ffffff",
10444     easing: 'easeIn',
10445     duration: 1
10446 });
10447 </code></pre>
10448      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10449      * @param {Object} options (optional) Object literal with any of the Fx config options
10450      * @return {Roo.Element} The Element
10451      */ 
10452     highlight : function(color, o){
10453         var el = this.getFxEl();
10454         o = o || {};
10455
10456         el.queueFx(o, function(){
10457             color = color || "ffff9c";
10458             attr = o.attr || "backgroundColor";
10459
10460             this.clearOpacity();
10461             this.show();
10462
10463             var origColor = this.getColor(attr);
10464             var restoreColor = this.dom.style[attr];
10465             endColor = (o.endColor || origColor) || "ffffff";
10466
10467             var after = function(){
10468                 el.dom.style[attr] = restoreColor;
10469                 el.afterFx(o);
10470             };
10471
10472             var a = {};
10473             a[attr] = {from: color, to: endColor};
10474             arguments.callee.anim = this.fxanim(a,
10475                 o,
10476                 'color',
10477                 1,
10478                 'easeIn', after);
10479         });
10480         return this;
10481     },
10482
10483    /**
10484     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10485     * Usage:
10486 <pre><code>
10487 // default: a single light blue ripple
10488 el.frame();
10489
10490 // custom: 3 red ripples lasting 3 seconds total
10491 el.frame("ff0000", 3, { duration: 3 });
10492
10493 // common config options shown with default values
10494 el.frame("C3DAF9", 1, {
10495     duration: 1 //duration of entire animation (not each individual ripple)
10496     // Note: Easing is not configurable and will be ignored if included
10497 });
10498 </code></pre>
10499     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10500     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10501     * @param {Object} options (optional) Object literal with any of the Fx config options
10502     * @return {Roo.Element} The Element
10503     */
10504     frame : function(color, count, o){
10505         var el = this.getFxEl();
10506         o = o || {};
10507
10508         el.queueFx(o, function(){
10509             color = color || "#C3DAF9";
10510             if(color.length == 6){
10511                 color = "#" + color;
10512             }
10513             count = count || 1;
10514             duration = o.duration || 1;
10515             this.show();
10516
10517             var b = this.getBox();
10518             var animFn = function(){
10519                 var proxy = this.createProxy({
10520
10521                      style:{
10522                         visbility:"hidden",
10523                         position:"absolute",
10524                         "z-index":"35000", // yee haw
10525                         border:"0px solid " + color
10526                      }
10527                   });
10528                 var scale = Roo.isBorderBox ? 2 : 1;
10529                 proxy.animate({
10530                     top:{from:b.y, to:b.y - 20},
10531                     left:{from:b.x, to:b.x - 20},
10532                     borderWidth:{from:0, to:10},
10533                     opacity:{from:1, to:0},
10534                     height:{from:b.height, to:(b.height + (20*scale))},
10535                     width:{from:b.width, to:(b.width + (20*scale))}
10536                 }, duration, function(){
10537                     proxy.remove();
10538                 });
10539                 if(--count > 0){
10540                      animFn.defer((duration/2)*1000, this);
10541                 }else{
10542                     el.afterFx(o);
10543                 }
10544             };
10545             animFn.call(this);
10546         });
10547         return this;
10548     },
10549
10550    /**
10551     * Creates a pause before any subsequent queued effects begin.  If there are
10552     * no effects queued after the pause it will have no effect.
10553     * Usage:
10554 <pre><code>
10555 el.pause(1);
10556 </code></pre>
10557     * @param {Number} seconds The length of time to pause (in seconds)
10558     * @return {Roo.Element} The Element
10559     */
10560     pause : function(seconds){
10561         var el = this.getFxEl();
10562         var o = {};
10563
10564         el.queueFx(o, function(){
10565             setTimeout(function(){
10566                 el.afterFx(o);
10567             }, seconds * 1000);
10568         });
10569         return this;
10570     },
10571
10572    /**
10573     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10574     * using the "endOpacity" config option.
10575     * Usage:
10576 <pre><code>
10577 // default: fade in from opacity 0 to 100%
10578 el.fadeIn();
10579
10580 // custom: fade in from opacity 0 to 75% over 2 seconds
10581 el.fadeIn({ endOpacity: .75, duration: 2});
10582
10583 // common config options shown with default values
10584 el.fadeIn({
10585     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10586     easing: 'easeOut',
10587     duration: .5
10588 });
10589 </code></pre>
10590     * @param {Object} options (optional) Object literal with any of the Fx config options
10591     * @return {Roo.Element} The Element
10592     */
10593     fadeIn : function(o){
10594         var el = this.getFxEl();
10595         o = o || {};
10596         el.queueFx(o, function(){
10597             this.setOpacity(0);
10598             this.fixDisplay();
10599             this.dom.style.visibility = 'visible';
10600             var to = o.endOpacity || 1;
10601             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10602                 o, null, .5, "easeOut", function(){
10603                 if(to == 1){
10604                     this.clearOpacity();
10605                 }
10606                 el.afterFx(o);
10607             });
10608         });
10609         return this;
10610     },
10611
10612    /**
10613     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10614     * using the "endOpacity" config option.
10615     * Usage:
10616 <pre><code>
10617 // default: fade out from the element's current opacity to 0
10618 el.fadeOut();
10619
10620 // custom: fade out from the element's current opacity to 25% over 2 seconds
10621 el.fadeOut({ endOpacity: .25, duration: 2});
10622
10623 // common config options shown with default values
10624 el.fadeOut({
10625     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10626     easing: 'easeOut',
10627     duration: .5
10628     remove: false,
10629     useDisplay: false
10630 });
10631 </code></pre>
10632     * @param {Object} options (optional) Object literal with any of the Fx config options
10633     * @return {Roo.Element} The Element
10634     */
10635     fadeOut : function(o){
10636         var el = this.getFxEl();
10637         o = o || {};
10638         el.queueFx(o, function(){
10639             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10640                 o, null, .5, "easeOut", function(){
10641                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10642                      this.dom.style.display = "none";
10643                 }else{
10644                      this.dom.style.visibility = "hidden";
10645                 }
10646                 this.clearOpacity();
10647                 el.afterFx(o);
10648             });
10649         });
10650         return this;
10651     },
10652
10653    /**
10654     * Animates the transition of an element's dimensions from a starting height/width
10655     * to an ending height/width.
10656     * Usage:
10657 <pre><code>
10658 // change height and width to 100x100 pixels
10659 el.scale(100, 100);
10660
10661 // common config options shown with default values.  The height and width will default to
10662 // the element's existing values if passed as null.
10663 el.scale(
10664     [element's width],
10665     [element's height], {
10666     easing: 'easeOut',
10667     duration: .35
10668 });
10669 </code></pre>
10670     * @param {Number} width  The new width (pass undefined to keep the original width)
10671     * @param {Number} height  The new height (pass undefined to keep the original height)
10672     * @param {Object} options (optional) Object literal with any of the Fx config options
10673     * @return {Roo.Element} The Element
10674     */
10675     scale : function(w, h, o){
10676         this.shift(Roo.apply({}, o, {
10677             width: w,
10678             height: h
10679         }));
10680         return this;
10681     },
10682
10683    /**
10684     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10685     * Any of these properties not specified in the config object will not be changed.  This effect 
10686     * requires that at least one new dimension, position or opacity setting must be passed in on
10687     * the config object in order for the function to have any effect.
10688     * Usage:
10689 <pre><code>
10690 // slide the element horizontally to x position 200 while changing the height and opacity
10691 el.shift({ x: 200, height: 50, opacity: .8 });
10692
10693 // common config options shown with default values.
10694 el.shift({
10695     width: [element's width],
10696     height: [element's height],
10697     x: [element's x position],
10698     y: [element's y position],
10699     opacity: [element's opacity],
10700     easing: 'easeOut',
10701     duration: .35
10702 });
10703 </code></pre>
10704     * @param {Object} options  Object literal with any of the Fx config options
10705     * @return {Roo.Element} The Element
10706     */
10707     shift : function(o){
10708         var el = this.getFxEl();
10709         o = o || {};
10710         el.queueFx(o, function(){
10711             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10712             if(w !== undefined){
10713                 a.width = {to: this.adjustWidth(w)};
10714             }
10715             if(h !== undefined){
10716                 a.height = {to: this.adjustHeight(h)};
10717             }
10718             if(x !== undefined || y !== undefined){
10719                 a.points = {to: [
10720                     x !== undefined ? x : this.getX(),
10721                     y !== undefined ? y : this.getY()
10722                 ]};
10723             }
10724             if(op !== undefined){
10725                 a.opacity = {to: op};
10726             }
10727             if(o.xy !== undefined){
10728                 a.points = {to: o.xy};
10729             }
10730             arguments.callee.anim = this.fxanim(a,
10731                 o, 'motion', .35, "easeOut", function(){
10732                 el.afterFx(o);
10733             });
10734         });
10735         return this;
10736     },
10737
10738         /**
10739          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10740          * ending point of the effect.
10741          * Usage:
10742          *<pre><code>
10743 // default: slide the element downward while fading out
10744 el.ghost();
10745
10746 // custom: slide the element out to the right with a 2-second duration
10747 el.ghost('r', { duration: 2 });
10748
10749 // common config options shown with default values
10750 el.ghost('b', {
10751     easing: 'easeOut',
10752     duration: .5
10753     remove: false,
10754     useDisplay: false
10755 });
10756 </code></pre>
10757          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10758          * @param {Object} options (optional) Object literal with any of the Fx config options
10759          * @return {Roo.Element} The Element
10760          */
10761     ghost : function(anchor, o){
10762         var el = this.getFxEl();
10763         o = o || {};
10764
10765         el.queueFx(o, function(){
10766             anchor = anchor || "b";
10767
10768             // restore values after effect
10769             var r = this.getFxRestore();
10770             var w = this.getWidth(),
10771                 h = this.getHeight();
10772
10773             var st = this.dom.style;
10774
10775             var after = function(){
10776                 if(o.useDisplay){
10777                     el.setDisplayed(false);
10778                 }else{
10779                     el.hide();
10780                 }
10781
10782                 el.clearOpacity();
10783                 el.setPositioning(r.pos);
10784                 st.width = r.width;
10785                 st.height = r.height;
10786
10787                 el.afterFx(o);
10788             };
10789
10790             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10791             switch(anchor.toLowerCase()){
10792                 case "t":
10793                     pt.by = [0, -h];
10794                 break;
10795                 case "l":
10796                     pt.by = [-w, 0];
10797                 break;
10798                 case "r":
10799                     pt.by = [w, 0];
10800                 break;
10801                 case "b":
10802                     pt.by = [0, h];
10803                 break;
10804                 case "tl":
10805                     pt.by = [-w, -h];
10806                 break;
10807                 case "bl":
10808                     pt.by = [-w, h];
10809                 break;
10810                 case "br":
10811                     pt.by = [w, h];
10812                 break;
10813                 case "tr":
10814                     pt.by = [w, -h];
10815                 break;
10816             }
10817
10818             arguments.callee.anim = this.fxanim(a,
10819                 o,
10820                 'motion',
10821                 .5,
10822                 "easeOut", after);
10823         });
10824         return this;
10825     },
10826
10827         /**
10828          * Ensures that all effects queued after syncFx is called on the element are
10829          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10830          * @return {Roo.Element} The Element
10831          */
10832     syncFx : function(){
10833         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10834             block : false,
10835             concurrent : true,
10836             stopFx : false
10837         });
10838         return this;
10839     },
10840
10841         /**
10842          * Ensures that all effects queued after sequenceFx is called on the element are
10843          * run in sequence.  This is the opposite of {@link #syncFx}.
10844          * @return {Roo.Element} The Element
10845          */
10846     sequenceFx : function(){
10847         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10848             block : false,
10849             concurrent : false,
10850             stopFx : false
10851         });
10852         return this;
10853     },
10854
10855         /* @private */
10856     nextFx : function(){
10857         var ef = this.fxQueue[0];
10858         if(ef){
10859             ef.call(this);
10860         }
10861     },
10862
10863         /**
10864          * Returns true if the element has any effects actively running or queued, else returns false.
10865          * @return {Boolean} True if element has active effects, else false
10866          */
10867     hasActiveFx : function(){
10868         return this.fxQueue && this.fxQueue[0];
10869     },
10870
10871         /**
10872          * Stops any running effects and clears the element's internal effects queue if it contains
10873          * any additional effects that haven't started yet.
10874          * @return {Roo.Element} The Element
10875          */
10876     stopFx : function(){
10877         if(this.hasActiveFx()){
10878             var cur = this.fxQueue[0];
10879             if(cur && cur.anim && cur.anim.isAnimated()){
10880                 this.fxQueue = [cur]; // clear out others
10881                 cur.anim.stop(true);
10882             }
10883         }
10884         return this;
10885     },
10886
10887         /* @private */
10888     beforeFx : function(o){
10889         if(this.hasActiveFx() && !o.concurrent){
10890            if(o.stopFx){
10891                this.stopFx();
10892                return true;
10893            }
10894            return false;
10895         }
10896         return true;
10897     },
10898
10899         /**
10900          * Returns true if the element is currently blocking so that no other effect can be queued
10901          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10902          * used to ensure that an effect initiated by a user action runs to completion prior to the
10903          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10904          * @return {Boolean} True if blocking, else false
10905          */
10906     hasFxBlock : function(){
10907         var q = this.fxQueue;
10908         return q && q[0] && q[0].block;
10909     },
10910
10911         /* @private */
10912     queueFx : function(o, fn){
10913         if(!this.fxQueue){
10914             this.fxQueue = [];
10915         }
10916         if(!this.hasFxBlock()){
10917             Roo.applyIf(o, this.fxDefaults);
10918             if(!o.concurrent){
10919                 var run = this.beforeFx(o);
10920                 fn.block = o.block;
10921                 this.fxQueue.push(fn);
10922                 if(run){
10923                     this.nextFx();
10924                 }
10925             }else{
10926                 fn.call(this);
10927             }
10928         }
10929         return this;
10930     },
10931
10932         /* @private */
10933     fxWrap : function(pos, o, vis){
10934         var wrap;
10935         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10936             var wrapXY;
10937             if(o.fixPosition){
10938                 wrapXY = this.getXY();
10939             }
10940             var div = document.createElement("div");
10941             div.style.visibility = vis;
10942             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10943             wrap.setPositioning(pos);
10944             if(wrap.getStyle("position") == "static"){
10945                 wrap.position("relative");
10946             }
10947             this.clearPositioning('auto');
10948             wrap.clip();
10949             wrap.dom.appendChild(this.dom);
10950             if(wrapXY){
10951                 wrap.setXY(wrapXY);
10952             }
10953         }
10954         return wrap;
10955     },
10956
10957         /* @private */
10958     fxUnwrap : function(wrap, pos, o){
10959         this.clearPositioning();
10960         this.setPositioning(pos);
10961         if(!o.wrap){
10962             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10963             wrap.remove();
10964         }
10965     },
10966
10967         /* @private */
10968     getFxRestore : function(){
10969         var st = this.dom.style;
10970         return {pos: this.getPositioning(), width: st.width, height : st.height};
10971     },
10972
10973         /* @private */
10974     afterFx : function(o){
10975         if(o.afterStyle){
10976             this.applyStyles(o.afterStyle);
10977         }
10978         if(o.afterCls){
10979             this.addClass(o.afterCls);
10980         }
10981         if(o.remove === true){
10982             this.remove();
10983         }
10984         Roo.callback(o.callback, o.scope, [this]);
10985         if(!o.concurrent){
10986             this.fxQueue.shift();
10987             this.nextFx();
10988         }
10989     },
10990
10991         /* @private */
10992     getFxEl : function(){ // support for composite element fx
10993         return Roo.get(this.dom);
10994     },
10995
10996         /* @private */
10997     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10998         animType = animType || 'run';
10999         opt = opt || {};
11000         var anim = Roo.lib.Anim[animType](
11001             this.dom, args,
11002             (opt.duration || defaultDur) || .35,
11003             (opt.easing || defaultEase) || 'easeOut',
11004             function(){
11005                 Roo.callback(cb, this);
11006             },
11007             this
11008         );
11009         opt.anim = anim;
11010         return anim;
11011     }
11012 };
11013
11014 // backwords compat
11015 Roo.Fx.resize = Roo.Fx.scale;
11016
11017 //When included, Roo.Fx is automatically applied to Element so that all basic
11018 //effects are available directly via the Element API
11019 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11020  * Based on:
11021  * Ext JS Library 1.1.1
11022  * Copyright(c) 2006-2007, Ext JS, LLC.
11023  *
11024  * Originally Released Under LGPL - original licence link has changed is not relivant.
11025  *
11026  * Fork - LGPL
11027  * <script type="text/javascript">
11028  */
11029
11030
11031 /**
11032  * @class Roo.CompositeElement
11033  * Standard composite class. Creates a Roo.Element for every element in the collection.
11034  * <br><br>
11035  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11036  * actions will be performed on all the elements in this collection.</b>
11037  * <br><br>
11038  * All methods return <i>this</i> and can be chained.
11039  <pre><code>
11040  var els = Roo.select("#some-el div.some-class", true);
11041  // or select directly from an existing element
11042  var el = Roo.get('some-el');
11043  el.select('div.some-class', true);
11044
11045  els.setWidth(100); // all elements become 100 width
11046  els.hide(true); // all elements fade out and hide
11047  // or
11048  els.setWidth(100).hide(true);
11049  </code></pre>
11050  */
11051 Roo.CompositeElement = function(els){
11052     this.elements = [];
11053     this.addElements(els);
11054 };
11055 Roo.CompositeElement.prototype = {
11056     isComposite: true,
11057     addElements : function(els){
11058         if(!els) {
11059             return this;
11060         }
11061         if(typeof els == "string"){
11062             els = Roo.Element.selectorFunction(els);
11063         }
11064         var yels = this.elements;
11065         var index = yels.length-1;
11066         for(var i = 0, len = els.length; i < len; i++) {
11067                 yels[++index] = Roo.get(els[i]);
11068         }
11069         return this;
11070     },
11071
11072     /**
11073     * Clears this composite and adds the elements returned by the passed selector.
11074     * @param {String/Array} els A string CSS selector, an array of elements or an element
11075     * @return {CompositeElement} this
11076     */
11077     fill : function(els){
11078         this.elements = [];
11079         this.add(els);
11080         return this;
11081     },
11082
11083     /**
11084     * Filters this composite to only elements that match the passed selector.
11085     * @param {String} selector A string CSS selector
11086     * @param {Boolean} inverse return inverse filter (not matches)
11087     * @return {CompositeElement} this
11088     */
11089     filter : function(selector, inverse){
11090         var els = [];
11091         inverse = inverse || false;
11092         this.each(function(el){
11093             var match = inverse ? !el.is(selector) : el.is(selector);
11094             if(match){
11095                 els[els.length] = el.dom;
11096             }
11097         });
11098         this.fill(els);
11099         return this;
11100     },
11101
11102     invoke : function(fn, args){
11103         var els = this.elements;
11104         for(var i = 0, len = els.length; i < len; i++) {
11105                 Roo.Element.prototype[fn].apply(els[i], args);
11106         }
11107         return this;
11108     },
11109     /**
11110     * Adds elements to this composite.
11111     * @param {String/Array} els A string CSS selector, an array of elements or an element
11112     * @return {CompositeElement} this
11113     */
11114     add : function(els){
11115         if(typeof els == "string"){
11116             this.addElements(Roo.Element.selectorFunction(els));
11117         }else if(els.length !== undefined){
11118             this.addElements(els);
11119         }else{
11120             this.addElements([els]);
11121         }
11122         return this;
11123     },
11124     /**
11125     * Calls the passed function passing (el, this, index) for each element in this composite.
11126     * @param {Function} fn The function to call
11127     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11128     * @return {CompositeElement} this
11129     */
11130     each : function(fn, scope){
11131         var els = this.elements;
11132         for(var i = 0, len = els.length; i < len; i++){
11133             if(fn.call(scope || els[i], els[i], this, i) === false) {
11134                 break;
11135             }
11136         }
11137         return this;
11138     },
11139
11140     /**
11141      * Returns the Element object at the specified index
11142      * @param {Number} index
11143      * @return {Roo.Element}
11144      */
11145     item : function(index){
11146         return this.elements[index] || null;
11147     },
11148
11149     /**
11150      * Returns the first Element
11151      * @return {Roo.Element}
11152      */
11153     first : function(){
11154         return this.item(0);
11155     },
11156
11157     /**
11158      * Returns the last Element
11159      * @return {Roo.Element}
11160      */
11161     last : function(){
11162         return this.item(this.elements.length-1);
11163     },
11164
11165     /**
11166      * Returns the number of elements in this composite
11167      * @return Number
11168      */
11169     getCount : function(){
11170         return this.elements.length;
11171     },
11172
11173     /**
11174      * Returns true if this composite contains the passed element
11175      * @return Boolean
11176      */
11177     contains : function(el){
11178         return this.indexOf(el) !== -1;
11179     },
11180
11181     /**
11182      * Returns true if this composite contains the passed element
11183      * @return Boolean
11184      */
11185     indexOf : function(el){
11186         return this.elements.indexOf(Roo.get(el));
11187     },
11188
11189
11190     /**
11191     * Removes the specified element(s).
11192     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11193     * or an array of any of those.
11194     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11195     * @return {CompositeElement} this
11196     */
11197     removeElement : function(el, removeDom){
11198         if(el instanceof Array){
11199             for(var i = 0, len = el.length; i < len; i++){
11200                 this.removeElement(el[i]);
11201             }
11202             return this;
11203         }
11204         var index = typeof el == 'number' ? el : this.indexOf(el);
11205         if(index !== -1){
11206             if(removeDom){
11207                 var d = this.elements[index];
11208                 if(d.dom){
11209                     d.remove();
11210                 }else{
11211                     d.parentNode.removeChild(d);
11212                 }
11213             }
11214             this.elements.splice(index, 1);
11215         }
11216         return this;
11217     },
11218
11219     /**
11220     * Replaces the specified element with the passed element.
11221     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11222     * to replace.
11223     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11224     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11225     * @return {CompositeElement} this
11226     */
11227     replaceElement : function(el, replacement, domReplace){
11228         var index = typeof el == 'number' ? el : this.indexOf(el);
11229         if(index !== -1){
11230             if(domReplace){
11231                 this.elements[index].replaceWith(replacement);
11232             }else{
11233                 this.elements.splice(index, 1, Roo.get(replacement))
11234             }
11235         }
11236         return this;
11237     },
11238
11239     /**
11240      * Removes all elements.
11241      */
11242     clear : function(){
11243         this.elements = [];
11244     }
11245 };
11246 (function(){
11247     Roo.CompositeElement.createCall = function(proto, fnName){
11248         if(!proto[fnName]){
11249             proto[fnName] = function(){
11250                 return this.invoke(fnName, arguments);
11251             };
11252         }
11253     };
11254     for(var fnName in Roo.Element.prototype){
11255         if(typeof Roo.Element.prototype[fnName] == "function"){
11256             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11257         }
11258     };
11259 })();
11260 /*
11261  * Based on:
11262  * Ext JS Library 1.1.1
11263  * Copyright(c) 2006-2007, Ext JS, LLC.
11264  *
11265  * Originally Released Under LGPL - original licence link has changed is not relivant.
11266  *
11267  * Fork - LGPL
11268  * <script type="text/javascript">
11269  */
11270
11271 /**
11272  * @class Roo.CompositeElementLite
11273  * @extends Roo.CompositeElement
11274  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11275  <pre><code>
11276  var els = Roo.select("#some-el div.some-class");
11277  // or select directly from an existing element
11278  var el = Roo.get('some-el');
11279  el.select('div.some-class');
11280
11281  els.setWidth(100); // all elements become 100 width
11282  els.hide(true); // all elements fade out and hide
11283  // or
11284  els.setWidth(100).hide(true);
11285  </code></pre><br><br>
11286  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11287  * actions will be performed on all the elements in this collection.</b>
11288  */
11289 Roo.CompositeElementLite = function(els){
11290     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11291     this.el = new Roo.Element.Flyweight();
11292 };
11293 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11294     addElements : function(els){
11295         if(els){
11296             if(els instanceof Array){
11297                 this.elements = this.elements.concat(els);
11298             }else{
11299                 var yels = this.elements;
11300                 var index = yels.length-1;
11301                 for(var i = 0, len = els.length; i < len; i++) {
11302                     yels[++index] = els[i];
11303                 }
11304             }
11305         }
11306         return this;
11307     },
11308     invoke : function(fn, args){
11309         var els = this.elements;
11310         var el = this.el;
11311         for(var i = 0, len = els.length; i < len; i++) {
11312             el.dom = els[i];
11313                 Roo.Element.prototype[fn].apply(el, args);
11314         }
11315         return this;
11316     },
11317     /**
11318      * Returns a flyweight Element of the dom element object at the specified index
11319      * @param {Number} index
11320      * @return {Roo.Element}
11321      */
11322     item : function(index){
11323         if(!this.elements[index]){
11324             return null;
11325         }
11326         this.el.dom = this.elements[index];
11327         return this.el;
11328     },
11329
11330     // fixes scope with flyweight
11331     addListener : function(eventName, handler, scope, opt){
11332         var els = this.elements;
11333         for(var i = 0, len = els.length; i < len; i++) {
11334             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11335         }
11336         return this;
11337     },
11338
11339     /**
11340     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11341     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11342     * a reference to the dom node, use el.dom.</b>
11343     * @param {Function} fn The function to call
11344     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11345     * @return {CompositeElement} this
11346     */
11347     each : function(fn, scope){
11348         var els = this.elements;
11349         var el = this.el;
11350         for(var i = 0, len = els.length; i < len; i++){
11351             el.dom = els[i];
11352                 if(fn.call(scope || el, el, this, i) === false){
11353                 break;
11354             }
11355         }
11356         return this;
11357     },
11358
11359     indexOf : function(el){
11360         return this.elements.indexOf(Roo.getDom(el));
11361     },
11362
11363     replaceElement : function(el, replacement, domReplace){
11364         var index = typeof el == 'number' ? el : this.indexOf(el);
11365         if(index !== -1){
11366             replacement = Roo.getDom(replacement);
11367             if(domReplace){
11368                 var d = this.elements[index];
11369                 d.parentNode.insertBefore(replacement, d);
11370                 d.parentNode.removeChild(d);
11371             }
11372             this.elements.splice(index, 1, replacement);
11373         }
11374         return this;
11375     }
11376 });
11377 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11378
11379 /*
11380  * Based on:
11381  * Ext JS Library 1.1.1
11382  * Copyright(c) 2006-2007, Ext JS, LLC.
11383  *
11384  * Originally Released Under LGPL - original licence link has changed is not relivant.
11385  *
11386  * Fork - LGPL
11387  * <script type="text/javascript">
11388  */
11389
11390  
11391
11392 /**
11393  * @class Roo.data.Connection
11394  * @extends Roo.util.Observable
11395  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11396  * either to a configured URL, or to a URL specified at request time.<br><br>
11397  * <p>
11398  * Requests made by this class are asynchronous, and will return immediately. No data from
11399  * the server will be available to the statement immediately following the {@link #request} call.
11400  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11401  * <p>
11402  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11403  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11404  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11405  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11406  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11407  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11408  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11409  * standard DOM methods.
11410  * @constructor
11411  * @param {Object} config a configuration object.
11412  */
11413 Roo.data.Connection = function(config){
11414     Roo.apply(this, config);
11415     this.addEvents({
11416         /**
11417          * @event beforerequest
11418          * Fires before a network request is made to retrieve a data object.
11419          * @param {Connection} conn This Connection object.
11420          * @param {Object} options The options config object passed to the {@link #request} method.
11421          */
11422         "beforerequest" : true,
11423         /**
11424          * @event requestcomplete
11425          * Fires if the request was successfully completed.
11426          * @param {Connection} conn This Connection object.
11427          * @param {Object} response The XHR object containing the response data.
11428          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11429          * @param {Object} options The options config object passed to the {@link #request} method.
11430          */
11431         "requestcomplete" : true,
11432         /**
11433          * @event requestexception
11434          * Fires if an error HTTP status was returned from the server.
11435          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11436          * @param {Connection} conn This Connection object.
11437          * @param {Object} response The XHR object containing the response data.
11438          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11439          * @param {Object} options The options config object passed to the {@link #request} method.
11440          */
11441         "requestexception" : true
11442     });
11443     Roo.data.Connection.superclass.constructor.call(this);
11444 };
11445
11446 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11447     /**
11448      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11449      */
11450     /**
11451      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11452      * extra parameters to each request made by this object. (defaults to undefined)
11453      */
11454     /**
11455      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11456      *  to each request made by this object. (defaults to undefined)
11457      */
11458     /**
11459      * @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)
11460      */
11461     /**
11462      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11463      */
11464     timeout : 30000,
11465     /**
11466      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11467      * @type Boolean
11468      */
11469     autoAbort:false,
11470
11471     /**
11472      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11473      * @type Boolean
11474      */
11475     disableCaching: true,
11476
11477     /**
11478      * Sends an HTTP request to a remote server.
11479      * @param {Object} options An object which may contain the following properties:<ul>
11480      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11481      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11482      * request, a url encoded string or a function to call to get either.</li>
11483      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11484      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11485      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11486      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11487      * <li>options {Object} The parameter to the request call.</li>
11488      * <li>success {Boolean} True if the request succeeded.</li>
11489      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11490      * </ul></li>
11491      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11492      * The callback is passed the following parameters:<ul>
11493      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11494      * <li>options {Object} The parameter to the request call.</li>
11495      * </ul></li>
11496      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11497      * The callback is passed the following parameters:<ul>
11498      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11499      * <li>options {Object} The parameter to the request call.</li>
11500      * </ul></li>
11501      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11502      * for the callback function. Defaults to the browser window.</li>
11503      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11504      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11505      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11506      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11507      * params for the post data. Any params will be appended to the URL.</li>
11508      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11509      * </ul>
11510      * @return {Number} transactionId
11511      */
11512     request : function(o){
11513         if(this.fireEvent("beforerequest", this, o) !== false){
11514             var p = o.params;
11515
11516             if(typeof p == "function"){
11517                 p = p.call(o.scope||window, o);
11518             }
11519             if(typeof p == "object"){
11520                 p = Roo.urlEncode(o.params);
11521             }
11522             if(this.extraParams){
11523                 var extras = Roo.urlEncode(this.extraParams);
11524                 p = p ? (p + '&' + extras) : extras;
11525             }
11526
11527             var url = o.url || this.url;
11528             if(typeof url == 'function'){
11529                 url = url.call(o.scope||window, o);
11530             }
11531
11532             if(o.form){
11533                 var form = Roo.getDom(o.form);
11534                 url = url || form.action;
11535
11536                 var enctype = form.getAttribute("enctype");
11537                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11538                     return this.doFormUpload(o, p, url);
11539                 }
11540                 var f = Roo.lib.Ajax.serializeForm(form);
11541                 p = p ? (p + '&' + f) : f;
11542             }
11543
11544             var hs = o.headers;
11545             if(this.defaultHeaders){
11546                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11547                 if(!o.headers){
11548                     o.headers = hs;
11549                 }
11550             }
11551
11552             var cb = {
11553                 success: this.handleResponse,
11554                 failure: this.handleFailure,
11555                 scope: this,
11556                 argument: {options: o},
11557                 timeout : o.timeout || this.timeout
11558             };
11559
11560             var method = o.method||this.method||(p ? "POST" : "GET");
11561
11562             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11563                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11564             }
11565
11566             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11567                 if(o.autoAbort){
11568                     this.abort();
11569                 }
11570             }else if(this.autoAbort !== false){
11571                 this.abort();
11572             }
11573
11574             if((method == 'GET' && p) || o.xmlData){
11575                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11576                 p = '';
11577             }
11578             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11579             return this.transId;
11580         }else{
11581             Roo.callback(o.callback, o.scope, [o, null, null]);
11582             return null;
11583         }
11584     },
11585
11586     /**
11587      * Determine whether this object has a request outstanding.
11588      * @param {Number} transactionId (Optional) defaults to the last transaction
11589      * @return {Boolean} True if there is an outstanding request.
11590      */
11591     isLoading : function(transId){
11592         if(transId){
11593             return Roo.lib.Ajax.isCallInProgress(transId);
11594         }else{
11595             return this.transId ? true : false;
11596         }
11597     },
11598
11599     /**
11600      * Aborts any outstanding request.
11601      * @param {Number} transactionId (Optional) defaults to the last transaction
11602      */
11603     abort : function(transId){
11604         if(transId || this.isLoading()){
11605             Roo.lib.Ajax.abort(transId || this.transId);
11606         }
11607     },
11608
11609     // private
11610     handleResponse : function(response){
11611         this.transId = false;
11612         var options = response.argument.options;
11613         response.argument = options ? options.argument : null;
11614         this.fireEvent("requestcomplete", this, response, options);
11615         Roo.callback(options.success, options.scope, [response, options]);
11616         Roo.callback(options.callback, options.scope, [options, true, response]);
11617     },
11618
11619     // private
11620     handleFailure : function(response, e){
11621         this.transId = false;
11622         var options = response.argument.options;
11623         response.argument = options ? options.argument : null;
11624         this.fireEvent("requestexception", this, response, options, e);
11625         Roo.callback(options.failure, options.scope, [response, options]);
11626         Roo.callback(options.callback, options.scope, [options, false, response]);
11627     },
11628
11629     // private
11630     doFormUpload : function(o, ps, url){
11631         var id = Roo.id();
11632         var frame = document.createElement('iframe');
11633         frame.id = id;
11634         frame.name = id;
11635         frame.className = 'x-hidden';
11636         if(Roo.isIE){
11637             frame.src = Roo.SSL_SECURE_URL;
11638         }
11639         document.body.appendChild(frame);
11640
11641         if(Roo.isIE){
11642            document.frames[id].name = id;
11643         }
11644
11645         var form = Roo.getDom(o.form);
11646         form.target = id;
11647         form.method = 'POST';
11648         form.enctype = form.encoding = 'multipart/form-data';
11649         if(url){
11650             form.action = url;
11651         }
11652
11653         var hiddens, hd;
11654         if(ps){ // add dynamic params
11655             hiddens = [];
11656             ps = Roo.urlDecode(ps, false);
11657             for(var k in ps){
11658                 if(ps.hasOwnProperty(k)){
11659                     hd = document.createElement('input');
11660                     hd.type = 'hidden';
11661                     hd.name = k;
11662                     hd.value = ps[k];
11663                     form.appendChild(hd);
11664                     hiddens.push(hd);
11665                 }
11666             }
11667         }
11668
11669         function cb(){
11670             var r = {  // bogus response object
11671                 responseText : '',
11672                 responseXML : null
11673             };
11674
11675             r.argument = o ? o.argument : null;
11676
11677             try { //
11678                 var doc;
11679                 if(Roo.isIE){
11680                     doc = frame.contentWindow.document;
11681                 }else {
11682                     doc = (frame.contentDocument || window.frames[id].document);
11683                 }
11684                 if(doc && doc.body){
11685                     r.responseText = doc.body.innerHTML;
11686                 }
11687                 if(doc && doc.XMLDocument){
11688                     r.responseXML = doc.XMLDocument;
11689                 }else {
11690                     r.responseXML = doc;
11691                 }
11692             }
11693             catch(e) {
11694                 // ignore
11695             }
11696
11697             Roo.EventManager.removeListener(frame, 'load', cb, this);
11698
11699             this.fireEvent("requestcomplete", this, r, o);
11700             Roo.callback(o.success, o.scope, [r, o]);
11701             Roo.callback(o.callback, o.scope, [o, true, r]);
11702
11703             setTimeout(function(){document.body.removeChild(frame);}, 100);
11704         }
11705
11706         Roo.EventManager.on(frame, 'load', cb, this);
11707         form.submit();
11708
11709         if(hiddens){ // remove dynamic params
11710             for(var i = 0, len = hiddens.length; i < len; i++){
11711                 form.removeChild(hiddens[i]);
11712             }
11713         }
11714     }
11715 });
11716 /*
11717  * Based on:
11718  * Ext JS Library 1.1.1
11719  * Copyright(c) 2006-2007, Ext JS, LLC.
11720  *
11721  * Originally Released Under LGPL - original licence link has changed is not relivant.
11722  *
11723  * Fork - LGPL
11724  * <script type="text/javascript">
11725  */
11726  
11727 /**
11728  * Global Ajax request class.
11729  * 
11730  * @class Roo.Ajax
11731  * @extends Roo.data.Connection
11732  * @static
11733  * 
11734  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11735  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11736  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11737  * @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)
11738  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11739  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11740  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11741  */
11742 Roo.Ajax = new Roo.data.Connection({
11743     // fix up the docs
11744     /**
11745      * @scope Roo.Ajax
11746      * @type {Boolear} 
11747      */
11748     autoAbort : false,
11749
11750     /**
11751      * Serialize the passed form into a url encoded string
11752      * @scope Roo.Ajax
11753      * @param {String/HTMLElement} form
11754      * @return {String}
11755      */
11756     serializeForm : function(form){
11757         return Roo.lib.Ajax.serializeForm(form);
11758     }
11759 });/*
11760  * Based on:
11761  * Ext JS Library 1.1.1
11762  * Copyright(c) 2006-2007, Ext JS, LLC.
11763  *
11764  * Originally Released Under LGPL - original licence link has changed is not relivant.
11765  *
11766  * Fork - LGPL
11767  * <script type="text/javascript">
11768  */
11769
11770  
11771 /**
11772  * @class Roo.UpdateManager
11773  * @extends Roo.util.Observable
11774  * Provides AJAX-style update for Element object.<br><br>
11775  * Usage:<br>
11776  * <pre><code>
11777  * // Get it from a Roo.Element object
11778  * var el = Roo.get("foo");
11779  * var mgr = el.getUpdateManager();
11780  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11781  * ...
11782  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11783  * <br>
11784  * // or directly (returns the same UpdateManager instance)
11785  * var mgr = new Roo.UpdateManager("myElementId");
11786  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11787  * mgr.on("update", myFcnNeedsToKnow);
11788  * <br>
11789    // short handed call directly from the element object
11790    Roo.get("foo").load({
11791         url: "bar.php",
11792         scripts:true,
11793         params: "for=bar",
11794         text: "Loading Foo..."
11795    });
11796  * </code></pre>
11797  * @constructor
11798  * Create new UpdateManager directly.
11799  * @param {String/HTMLElement/Roo.Element} el The element to update
11800  * @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).
11801  */
11802 Roo.UpdateManager = function(el, forceNew){
11803     el = Roo.get(el);
11804     if(!forceNew && el.updateManager){
11805         return el.updateManager;
11806     }
11807     /**
11808      * The Element object
11809      * @type Roo.Element
11810      */
11811     this.el = el;
11812     /**
11813      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11814      * @type String
11815      */
11816     this.defaultUrl = null;
11817
11818     this.addEvents({
11819         /**
11820          * @event beforeupdate
11821          * Fired before an update is made, return false from your handler and the update is cancelled.
11822          * @param {Roo.Element} el
11823          * @param {String/Object/Function} url
11824          * @param {String/Object} params
11825          */
11826         "beforeupdate": true,
11827         /**
11828          * @event update
11829          * Fired after successful update is made.
11830          * @param {Roo.Element} el
11831          * @param {Object} oResponseObject The response Object
11832          */
11833         "update": true,
11834         /**
11835          * @event failure
11836          * Fired on update failure.
11837          * @param {Roo.Element} el
11838          * @param {Object} oResponseObject The response Object
11839          */
11840         "failure": true
11841     });
11842     var d = Roo.UpdateManager.defaults;
11843     /**
11844      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11845      * @type String
11846      */
11847     this.sslBlankUrl = d.sslBlankUrl;
11848     /**
11849      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11850      * @type Boolean
11851      */
11852     this.disableCaching = d.disableCaching;
11853     /**
11854      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11855      * @type String
11856      */
11857     this.indicatorText = d.indicatorText;
11858     /**
11859      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11860      * @type String
11861      */
11862     this.showLoadIndicator = d.showLoadIndicator;
11863     /**
11864      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11865      * @type Number
11866      */
11867     this.timeout = d.timeout;
11868
11869     /**
11870      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11871      * @type Boolean
11872      */
11873     this.loadScripts = d.loadScripts;
11874
11875     /**
11876      * Transaction object of current executing transaction
11877      */
11878     this.transaction = null;
11879
11880     /**
11881      * @private
11882      */
11883     this.autoRefreshProcId = null;
11884     /**
11885      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11886      * @type Function
11887      */
11888     this.refreshDelegate = this.refresh.createDelegate(this);
11889     /**
11890      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11891      * @type Function
11892      */
11893     this.updateDelegate = this.update.createDelegate(this);
11894     /**
11895      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11896      * @type Function
11897      */
11898     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11899     /**
11900      * @private
11901      */
11902     this.successDelegate = this.processSuccess.createDelegate(this);
11903     /**
11904      * @private
11905      */
11906     this.failureDelegate = this.processFailure.createDelegate(this);
11907
11908     if(!this.renderer){
11909      /**
11910       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11911       */
11912     this.renderer = new Roo.UpdateManager.BasicRenderer();
11913     }
11914     
11915     Roo.UpdateManager.superclass.constructor.call(this);
11916 };
11917
11918 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11919     /**
11920      * Get the Element this UpdateManager is bound to
11921      * @return {Roo.Element} The element
11922      */
11923     getEl : function(){
11924         return this.el;
11925     },
11926     /**
11927      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11928      * @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:
11929 <pre><code>
11930 um.update({<br/>
11931     url: "your-url.php",<br/>
11932     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11933     callback: yourFunction,<br/>
11934     scope: yourObject, //(optional scope)  <br/>
11935     discardUrl: false, <br/>
11936     nocache: false,<br/>
11937     text: "Loading...",<br/>
11938     timeout: 30,<br/>
11939     scripts: false<br/>
11940 });
11941 </code></pre>
11942      * The only required property is url. The optional properties nocache, text and scripts
11943      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11944      * @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}
11945      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11946      * @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.
11947      */
11948     update : function(url, params, callback, discardUrl){
11949         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11950             var method = this.method,
11951                 cfg;
11952             if(typeof url == "object"){ // must be config object
11953                 cfg = url;
11954                 url = cfg.url;
11955                 params = params || cfg.params;
11956                 callback = callback || cfg.callback;
11957                 discardUrl = discardUrl || cfg.discardUrl;
11958                 if(callback && cfg.scope){
11959                     callback = callback.createDelegate(cfg.scope);
11960                 }
11961                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11962                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11963                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11964                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11965                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11966             }
11967             this.showLoading();
11968             if(!discardUrl){
11969                 this.defaultUrl = url;
11970             }
11971             if(typeof url == "function"){
11972                 url = url.call(this);
11973             }
11974
11975             method = method || (params ? "POST" : "GET");
11976             if(method == "GET"){
11977                 url = this.prepareUrl(url);
11978             }
11979
11980             var o = Roo.apply(cfg ||{}, {
11981                 url : url,
11982                 params: params,
11983                 success: this.successDelegate,
11984                 failure: this.failureDelegate,
11985                 callback: undefined,
11986                 timeout: (this.timeout*1000),
11987                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11988             });
11989             Roo.log("updated manager called with timeout of " + o.timeout);
11990             this.transaction = Roo.Ajax.request(o);
11991         }
11992     },
11993
11994     /**
11995      * 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.
11996      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11997      * @param {String/HTMLElement} form The form Id or form element
11998      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11999      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12000      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12001      */
12002     formUpdate : function(form, url, reset, callback){
12003         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12004             if(typeof url == "function"){
12005                 url = url.call(this);
12006             }
12007             form = Roo.getDom(form);
12008             this.transaction = Roo.Ajax.request({
12009                 form: form,
12010                 url:url,
12011                 success: this.successDelegate,
12012                 failure: this.failureDelegate,
12013                 timeout: (this.timeout*1000),
12014                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12015             });
12016             this.showLoading.defer(1, this);
12017         }
12018     },
12019
12020     /**
12021      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12022      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12023      */
12024     refresh : function(callback){
12025         if(this.defaultUrl == null){
12026             return;
12027         }
12028         this.update(this.defaultUrl, null, callback, true);
12029     },
12030
12031     /**
12032      * Set this element to auto refresh.
12033      * @param {Number} interval How often to update (in seconds).
12034      * @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)
12035      * @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}
12036      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12037      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12038      */
12039     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12040         if(refreshNow){
12041             this.update(url || this.defaultUrl, params, callback, true);
12042         }
12043         if(this.autoRefreshProcId){
12044             clearInterval(this.autoRefreshProcId);
12045         }
12046         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12047     },
12048
12049     /**
12050      * Stop auto refresh on this element.
12051      */
12052      stopAutoRefresh : function(){
12053         if(this.autoRefreshProcId){
12054             clearInterval(this.autoRefreshProcId);
12055             delete this.autoRefreshProcId;
12056         }
12057     },
12058
12059     isAutoRefreshing : function(){
12060        return this.autoRefreshProcId ? true : false;
12061     },
12062     /**
12063      * Called to update the element to "Loading" state. Override to perform custom action.
12064      */
12065     showLoading : function(){
12066         if(this.showLoadIndicator){
12067             this.el.update(this.indicatorText);
12068         }
12069     },
12070
12071     /**
12072      * Adds unique parameter to query string if disableCaching = true
12073      * @private
12074      */
12075     prepareUrl : function(url){
12076         if(this.disableCaching){
12077             var append = "_dc=" + (new Date().getTime());
12078             if(url.indexOf("?") !== -1){
12079                 url += "&" + append;
12080             }else{
12081                 url += "?" + append;
12082             }
12083         }
12084         return url;
12085     },
12086
12087     /**
12088      * @private
12089      */
12090     processSuccess : function(response){
12091         this.transaction = null;
12092         if(response.argument.form && response.argument.reset){
12093             try{ // put in try/catch since some older FF releases had problems with this
12094                 response.argument.form.reset();
12095             }catch(e){}
12096         }
12097         if(this.loadScripts){
12098             this.renderer.render(this.el, response, this,
12099                 this.updateComplete.createDelegate(this, [response]));
12100         }else{
12101             this.renderer.render(this.el, response, this);
12102             this.updateComplete(response);
12103         }
12104     },
12105
12106     updateComplete : function(response){
12107         this.fireEvent("update", this.el, response);
12108         if(typeof response.argument.callback == "function"){
12109             response.argument.callback(this.el, true, response);
12110         }
12111     },
12112
12113     /**
12114      * @private
12115      */
12116     processFailure : function(response){
12117         this.transaction = null;
12118         this.fireEvent("failure", this.el, response);
12119         if(typeof response.argument.callback == "function"){
12120             response.argument.callback(this.el, false, response);
12121         }
12122     },
12123
12124     /**
12125      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12126      * @param {Object} renderer The object implementing the render() method
12127      */
12128     setRenderer : function(renderer){
12129         this.renderer = renderer;
12130     },
12131
12132     getRenderer : function(){
12133        return this.renderer;
12134     },
12135
12136     /**
12137      * Set the defaultUrl used for updates
12138      * @param {String/Function} defaultUrl The url or a function to call to get the url
12139      */
12140     setDefaultUrl : function(defaultUrl){
12141         this.defaultUrl = defaultUrl;
12142     },
12143
12144     /**
12145      * Aborts the executing transaction
12146      */
12147     abort : function(){
12148         if(this.transaction){
12149             Roo.Ajax.abort(this.transaction);
12150         }
12151     },
12152
12153     /**
12154      * Returns true if an update is in progress
12155      * @return {Boolean}
12156      */
12157     isUpdating : function(){
12158         if(this.transaction){
12159             return Roo.Ajax.isLoading(this.transaction);
12160         }
12161         return false;
12162     }
12163 });
12164
12165 /**
12166  * @class Roo.UpdateManager.defaults
12167  * @static (not really - but it helps the doc tool)
12168  * The defaults collection enables customizing the default properties of UpdateManager
12169  */
12170    Roo.UpdateManager.defaults = {
12171        /**
12172          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12173          * @type Number
12174          */
12175          timeout : 30,
12176
12177          /**
12178          * True to process scripts by default (Defaults to false).
12179          * @type Boolean
12180          */
12181         loadScripts : false,
12182
12183         /**
12184         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12185         * @type String
12186         */
12187         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12188         /**
12189          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12190          * @type Boolean
12191          */
12192         disableCaching : false,
12193         /**
12194          * Whether to show indicatorText when loading (Defaults to true).
12195          * @type Boolean
12196          */
12197         showLoadIndicator : true,
12198         /**
12199          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12200          * @type String
12201          */
12202         indicatorText : '<div class="loading-indicator">Loading...</div>'
12203    };
12204
12205 /**
12206  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12207  *Usage:
12208  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12209  * @param {String/HTMLElement/Roo.Element} el The element to update
12210  * @param {String} url The url
12211  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12212  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12213  * @static
12214  * @deprecated
12215  * @member Roo.UpdateManager
12216  */
12217 Roo.UpdateManager.updateElement = function(el, url, params, options){
12218     var um = Roo.get(el, true).getUpdateManager();
12219     Roo.apply(um, options);
12220     um.update(url, params, options ? options.callback : null);
12221 };
12222 // alias for backwards compat
12223 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12224 /**
12225  * @class Roo.UpdateManager.BasicRenderer
12226  * Default Content renderer. Updates the elements innerHTML with the responseText.
12227  */
12228 Roo.UpdateManager.BasicRenderer = function(){};
12229
12230 Roo.UpdateManager.BasicRenderer.prototype = {
12231     /**
12232      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12233      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12234      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12235      * @param {Roo.Element} el The element being rendered
12236      * @param {Object} response The YUI Connect response object
12237      * @param {UpdateManager} updateManager The calling update manager
12238      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12239      */
12240      render : function(el, response, updateManager, callback){
12241         el.update(response.responseText, updateManager.loadScripts, callback);
12242     }
12243 };
12244 /*
12245  * Based on:
12246  * Roo JS
12247  * (c)) Alan Knowles
12248  * Licence : LGPL
12249  */
12250
12251
12252 /**
12253  * @class Roo.DomTemplate
12254  * @extends Roo.Template
12255  * An effort at a dom based template engine..
12256  *
12257  * Similar to XTemplate, except it uses dom parsing to create the template..
12258  *
12259  * Supported features:
12260  *
12261  *  Tags:
12262
12263 <pre><code>
12264       {a_variable} - output encoded.
12265       {a_variable.format:("Y-m-d")} - call a method on the variable
12266       {a_variable:raw} - unencoded output
12267       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12268       {a_variable:this.method_on_template(...)} - call a method on the template object.
12269  
12270 </code></pre>
12271  *  The tpl tag:
12272 <pre><code>
12273         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12274         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12275         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12276         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12277   
12278 </code></pre>
12279  *      
12280  */
12281 Roo.DomTemplate = function()
12282 {
12283      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12284      if (this.html) {
12285         this.compile();
12286      }
12287 };
12288
12289
12290 Roo.extend(Roo.DomTemplate, Roo.Template, {
12291     /**
12292      * id counter for sub templates.
12293      */
12294     id : 0,
12295     /**
12296      * flag to indicate if dom parser is inside a pre,
12297      * it will strip whitespace if not.
12298      */
12299     inPre : false,
12300     
12301     /**
12302      * The various sub templates
12303      */
12304     tpls : false,
12305     
12306     
12307     
12308     /**
12309      *
12310      * basic tag replacing syntax
12311      * WORD:WORD()
12312      *
12313      * // you can fake an object call by doing this
12314      *  x.t:(test,tesT) 
12315      * 
12316      */
12317     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12318     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12319     
12320     iterChild : function (node, method) {
12321         
12322         var oldPre = this.inPre;
12323         if (node.tagName == 'PRE') {
12324             this.inPre = true;
12325         }
12326         for( var i = 0; i < node.childNodes.length; i++) {
12327             method.call(this, node.childNodes[i]);
12328         }
12329         this.inPre = oldPre;
12330     },
12331     
12332     
12333     
12334     /**
12335      * compile the template
12336      *
12337      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12338      *
12339      */
12340     compile: function()
12341     {
12342         var s = this.html;
12343         
12344         // covert the html into DOM...
12345         var doc = false;
12346         var div =false;
12347         try {
12348             doc = document.implementation.createHTMLDocument("");
12349             doc.documentElement.innerHTML =   this.html  ;
12350             div = doc.documentElement;
12351         } catch (e) {
12352             // old IE... - nasty -- it causes all sorts of issues.. with
12353             // images getting pulled from server..
12354             div = document.createElement('div');
12355             div.innerHTML = this.html;
12356         }
12357         //doc.documentElement.innerHTML = htmlBody
12358          
12359         
12360         
12361         this.tpls = [];
12362         var _t = this;
12363         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12364         
12365         var tpls = this.tpls;
12366         
12367         // create a top level template from the snippet..
12368         
12369         //Roo.log(div.innerHTML);
12370         
12371         var tpl = {
12372             uid : 'master',
12373             id : this.id++,
12374             attr : false,
12375             value : false,
12376             body : div.innerHTML,
12377             
12378             forCall : false,
12379             execCall : false,
12380             dom : div,
12381             isTop : true
12382             
12383         };
12384         tpls.unshift(tpl);
12385         
12386         
12387         // compile them...
12388         this.tpls = [];
12389         Roo.each(tpls, function(tp){
12390             this.compileTpl(tp);
12391             this.tpls[tp.id] = tp;
12392         }, this);
12393         
12394         this.master = tpls[0];
12395         return this;
12396         
12397         
12398     },
12399     
12400     compileNode : function(node, istop) {
12401         // test for
12402         //Roo.log(node);
12403         
12404         
12405         // skip anything not a tag..
12406         if (node.nodeType != 1) {
12407             if (node.nodeType == 3 && !this.inPre) {
12408                 // reduce white space..
12409                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12410                 
12411             }
12412             return;
12413         }
12414         
12415         var tpl = {
12416             uid : false,
12417             id : false,
12418             attr : false,
12419             value : false,
12420             body : '',
12421             
12422             forCall : false,
12423             execCall : false,
12424             dom : false,
12425             isTop : istop
12426             
12427             
12428         };
12429         
12430         
12431         switch(true) {
12432             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12433             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12434             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12435             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12436             // no default..
12437         }
12438         
12439         
12440         if (!tpl.attr) {
12441             // just itterate children..
12442             this.iterChild(node,this.compileNode);
12443             return;
12444         }
12445         tpl.uid = this.id++;
12446         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12447         node.removeAttribute('roo-'+ tpl.attr);
12448         if (tpl.attr != 'name') {
12449             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12450             node.parentNode.replaceChild(placeholder,  node);
12451         } else {
12452             
12453             var placeholder =  document.createElement('span');
12454             placeholder.className = 'roo-tpl-' + tpl.value;
12455             node.parentNode.replaceChild(placeholder,  node);
12456         }
12457         
12458         // parent now sees '{domtplXXXX}
12459         this.iterChild(node,this.compileNode);
12460         
12461         // we should now have node body...
12462         var div = document.createElement('div');
12463         div.appendChild(node);
12464         tpl.dom = node;
12465         // this has the unfortunate side effect of converting tagged attributes
12466         // eg. href="{...}" into %7C...%7D
12467         // this has been fixed by searching for those combo's although it's a bit hacky..
12468         
12469         
12470         tpl.body = div.innerHTML;
12471         
12472         
12473          
12474         tpl.id = tpl.uid;
12475         switch(tpl.attr) {
12476             case 'for' :
12477                 switch (tpl.value) {
12478                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12479                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12480                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12481                 }
12482                 break;
12483             
12484             case 'exec':
12485                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12486                 break;
12487             
12488             case 'if':     
12489                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12490                 break;
12491             
12492             case 'name':
12493                 tpl.id  = tpl.value; // replace non characters???
12494                 break;
12495             
12496         }
12497         
12498         
12499         this.tpls.push(tpl);
12500         
12501         
12502         
12503     },
12504     
12505     
12506     
12507     
12508     /**
12509      * Compile a segment of the template into a 'sub-template'
12510      *
12511      * 
12512      * 
12513      *
12514      */
12515     compileTpl : function(tpl)
12516     {
12517         var fm = Roo.util.Format;
12518         var useF = this.disableFormats !== true;
12519         
12520         var sep = Roo.isGecko ? "+\n" : ",\n";
12521         
12522         var undef = function(str) {
12523             Roo.debug && Roo.log("Property not found :"  + str);
12524             return '';
12525         };
12526           
12527         //Roo.log(tpl.body);
12528         
12529         
12530         
12531         var fn = function(m, lbrace, name, format, args)
12532         {
12533             //Roo.log("ARGS");
12534             //Roo.log(arguments);
12535             args = args ? args.replace(/\\'/g,"'") : args;
12536             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12537             if (typeof(format) == 'undefined') {
12538                 format =  'htmlEncode'; 
12539             }
12540             if (format == 'raw' ) {
12541                 format = false;
12542             }
12543             
12544             if(name.substr(0, 6) == 'domtpl'){
12545                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12546             }
12547             
12548             // build an array of options to determine if value is undefined..
12549             
12550             // basically get 'xxxx.yyyy' then do
12551             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12552             //    (function () { Roo.log("Property not found"); return ''; })() :
12553             //    ......
12554             
12555             var udef_ar = [];
12556             var lookfor = '';
12557             Roo.each(name.split('.'), function(st) {
12558                 lookfor += (lookfor.length ? '.': '') + st;
12559                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12560             });
12561             
12562             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12563             
12564             
12565             if(format && useF){
12566                 
12567                 args = args ? ',' + args : "";
12568                  
12569                 if(format.substr(0, 5) != "this."){
12570                     format = "fm." + format + '(';
12571                 }else{
12572                     format = 'this.call("'+ format.substr(5) + '", ';
12573                     args = ", values";
12574                 }
12575                 
12576                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12577             }
12578              
12579             if (args && args.length) {
12580                 // called with xxyx.yuu:(test,test)
12581                 // change to ()
12582                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12583             }
12584             // raw.. - :raw modifier..
12585             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12586             
12587         };
12588         var body;
12589         // branched to use + in gecko and [].join() in others
12590         if(Roo.isGecko){
12591             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12592                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12593                     "';};};";
12594         }else{
12595             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12596             body.push(tpl.body.replace(/(\r\n|\n)/g,
12597                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12598             body.push("'].join('');};};");
12599             body = body.join('');
12600         }
12601         
12602         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12603        
12604         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12605         eval(body);
12606         
12607         return this;
12608     },
12609      
12610     /**
12611      * same as applyTemplate, except it's done to one of the subTemplates
12612      * when using named templates, you can do:
12613      *
12614      * var str = pl.applySubTemplate('your-name', values);
12615      *
12616      * 
12617      * @param {Number} id of the template
12618      * @param {Object} values to apply to template
12619      * @param {Object} parent (normaly the instance of this object)
12620      */
12621     applySubTemplate : function(id, values, parent)
12622     {
12623         
12624         
12625         var t = this.tpls[id];
12626         
12627         
12628         try { 
12629             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12630                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12631                 return '';
12632             }
12633         } catch(e) {
12634             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12635             Roo.log(values);
12636           
12637             return '';
12638         }
12639         try { 
12640             
12641             if(t.execCall && t.execCall.call(this, values, parent)){
12642                 return '';
12643             }
12644         } catch(e) {
12645             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12646             Roo.log(values);
12647             return '';
12648         }
12649         
12650         try {
12651             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12652             parent = t.target ? values : parent;
12653             if(t.forCall && vs instanceof Array){
12654                 var buf = [];
12655                 for(var i = 0, len = vs.length; i < len; i++){
12656                     try {
12657                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12658                     } catch (e) {
12659                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12660                         Roo.log(e.body);
12661                         //Roo.log(t.compiled);
12662                         Roo.log(vs[i]);
12663                     }   
12664                 }
12665                 return buf.join('');
12666             }
12667         } catch (e) {
12668             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12669             Roo.log(values);
12670             return '';
12671         }
12672         try {
12673             return t.compiled.call(this, vs, parent);
12674         } catch (e) {
12675             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12676             Roo.log(e.body);
12677             //Roo.log(t.compiled);
12678             Roo.log(values);
12679             return '';
12680         }
12681     },
12682
12683    
12684
12685     applyTemplate : function(values){
12686         return this.master.compiled.call(this, values, {});
12687         //var s = this.subs;
12688     },
12689
12690     apply : function(){
12691         return this.applyTemplate.apply(this, arguments);
12692     }
12693
12694  });
12695
12696 Roo.DomTemplate.from = function(el){
12697     el = Roo.getDom(el);
12698     return new Roo.Domtemplate(el.value || el.innerHTML);
12699 };/*
12700  * Based on:
12701  * Ext JS Library 1.1.1
12702  * Copyright(c) 2006-2007, Ext JS, LLC.
12703  *
12704  * Originally Released Under LGPL - original licence link has changed is not relivant.
12705  *
12706  * Fork - LGPL
12707  * <script type="text/javascript">
12708  */
12709
12710 /**
12711  * @class Roo.util.DelayedTask
12712  * Provides a convenient method of performing setTimeout where a new
12713  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12714  * You can use this class to buffer
12715  * the keypress events for a certain number of milliseconds, and perform only if they stop
12716  * for that amount of time.
12717  * @constructor The parameters to this constructor serve as defaults and are not required.
12718  * @param {Function} fn (optional) The default function to timeout
12719  * @param {Object} scope (optional) The default scope of that timeout
12720  * @param {Array} args (optional) The default Array of arguments
12721  */
12722 Roo.util.DelayedTask = function(fn, scope, args){
12723     var id = null, d, t;
12724
12725     var call = function(){
12726         var now = new Date().getTime();
12727         if(now - t >= d){
12728             clearInterval(id);
12729             id = null;
12730             fn.apply(scope, args || []);
12731         }
12732     };
12733     /**
12734      * Cancels any pending timeout and queues a new one
12735      * @param {Number} delay The milliseconds to delay
12736      * @param {Function} newFn (optional) Overrides function passed to constructor
12737      * @param {Object} newScope (optional) Overrides scope passed to constructor
12738      * @param {Array} newArgs (optional) Overrides args passed to constructor
12739      */
12740     this.delay = function(delay, newFn, newScope, newArgs){
12741         if(id && delay != d){
12742             this.cancel();
12743         }
12744         d = delay;
12745         t = new Date().getTime();
12746         fn = newFn || fn;
12747         scope = newScope || scope;
12748         args = newArgs || args;
12749         if(!id){
12750             id = setInterval(call, d);
12751         }
12752     };
12753
12754     /**
12755      * Cancel the last queued timeout
12756      */
12757     this.cancel = function(){
12758         if(id){
12759             clearInterval(id);
12760             id = null;
12761         }
12762     };
12763 };/*
12764  * Based on:
12765  * Ext JS Library 1.1.1
12766  * Copyright(c) 2006-2007, Ext JS, LLC.
12767  *
12768  * Originally Released Under LGPL - original licence link has changed is not relivant.
12769  *
12770  * Fork - LGPL
12771  * <script type="text/javascript">
12772  */
12773  
12774  
12775 Roo.util.TaskRunner = function(interval){
12776     interval = interval || 10;
12777     var tasks = [], removeQueue = [];
12778     var id = 0;
12779     var running = false;
12780
12781     var stopThread = function(){
12782         running = false;
12783         clearInterval(id);
12784         id = 0;
12785     };
12786
12787     var startThread = function(){
12788         if(!running){
12789             running = true;
12790             id = setInterval(runTasks, interval);
12791         }
12792     };
12793
12794     var removeTask = function(task){
12795         removeQueue.push(task);
12796         if(task.onStop){
12797             task.onStop();
12798         }
12799     };
12800
12801     var runTasks = function(){
12802         if(removeQueue.length > 0){
12803             for(var i = 0, len = removeQueue.length; i < len; i++){
12804                 tasks.remove(removeQueue[i]);
12805             }
12806             removeQueue = [];
12807             if(tasks.length < 1){
12808                 stopThread();
12809                 return;
12810             }
12811         }
12812         var now = new Date().getTime();
12813         for(var i = 0, len = tasks.length; i < len; ++i){
12814             var t = tasks[i];
12815             var itime = now - t.taskRunTime;
12816             if(t.interval <= itime){
12817                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12818                 t.taskRunTime = now;
12819                 if(rt === false || t.taskRunCount === t.repeat){
12820                     removeTask(t);
12821                     return;
12822                 }
12823             }
12824             if(t.duration && t.duration <= (now - t.taskStartTime)){
12825                 removeTask(t);
12826             }
12827         }
12828     };
12829
12830     /**
12831      * Queues a new task.
12832      * @param {Object} task
12833      */
12834     this.start = function(task){
12835         tasks.push(task);
12836         task.taskStartTime = new Date().getTime();
12837         task.taskRunTime = 0;
12838         task.taskRunCount = 0;
12839         startThread();
12840         return task;
12841     };
12842
12843     this.stop = function(task){
12844         removeTask(task);
12845         return task;
12846     };
12847
12848     this.stopAll = function(){
12849         stopThread();
12850         for(var i = 0, len = tasks.length; i < len; i++){
12851             if(tasks[i].onStop){
12852                 tasks[i].onStop();
12853             }
12854         }
12855         tasks = [];
12856         removeQueue = [];
12857     };
12858 };
12859
12860 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12861  * Based on:
12862  * Ext JS Library 1.1.1
12863  * Copyright(c) 2006-2007, Ext JS, LLC.
12864  *
12865  * Originally Released Under LGPL - original licence link has changed is not relivant.
12866  *
12867  * Fork - LGPL
12868  * <script type="text/javascript">
12869  */
12870
12871  
12872 /**
12873  * @class Roo.util.MixedCollection
12874  * @extends Roo.util.Observable
12875  * A Collection class that maintains both numeric indexes and keys and exposes events.
12876  * @constructor
12877  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12878  * collection (defaults to false)
12879  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12880  * and return the key value for that item.  This is used when available to look up the key on items that
12881  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12882  * equivalent to providing an implementation for the {@link #getKey} method.
12883  */
12884 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12885     this.items = [];
12886     this.map = {};
12887     this.keys = [];
12888     this.length = 0;
12889     this.addEvents({
12890         /**
12891          * @event clear
12892          * Fires when the collection is cleared.
12893          */
12894         "clear" : true,
12895         /**
12896          * @event add
12897          * Fires when an item is added to the collection.
12898          * @param {Number} index The index at which the item was added.
12899          * @param {Object} o The item added.
12900          * @param {String} key The key associated with the added item.
12901          */
12902         "add" : true,
12903         /**
12904          * @event replace
12905          * Fires when an item is replaced in the collection.
12906          * @param {String} key he key associated with the new added.
12907          * @param {Object} old The item being replaced.
12908          * @param {Object} new The new item.
12909          */
12910         "replace" : true,
12911         /**
12912          * @event remove
12913          * Fires when an item is removed from the collection.
12914          * @param {Object} o The item being removed.
12915          * @param {String} key (optional) The key associated with the removed item.
12916          */
12917         "remove" : true,
12918         "sort" : true
12919     });
12920     this.allowFunctions = allowFunctions === true;
12921     if(keyFn){
12922         this.getKey = keyFn;
12923     }
12924     Roo.util.MixedCollection.superclass.constructor.call(this);
12925 };
12926
12927 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12928     allowFunctions : false,
12929     
12930 /**
12931  * Adds an item to the collection.
12932  * @param {String} key The key to associate with the item
12933  * @param {Object} o The item to add.
12934  * @return {Object} The item added.
12935  */
12936     add : function(key, o){
12937         if(arguments.length == 1){
12938             o = arguments[0];
12939             key = this.getKey(o);
12940         }
12941         if(typeof key == "undefined" || key === null){
12942             this.length++;
12943             this.items.push(o);
12944             this.keys.push(null);
12945         }else{
12946             var old = this.map[key];
12947             if(old){
12948                 return this.replace(key, o);
12949             }
12950             this.length++;
12951             this.items.push(o);
12952             this.map[key] = o;
12953             this.keys.push(key);
12954         }
12955         this.fireEvent("add", this.length-1, o, key);
12956         return o;
12957     },
12958        
12959 /**
12960   * MixedCollection has a generic way to fetch keys if you implement getKey.
12961 <pre><code>
12962 // normal way
12963 var mc = new Roo.util.MixedCollection();
12964 mc.add(someEl.dom.id, someEl);
12965 mc.add(otherEl.dom.id, otherEl);
12966 //and so on
12967
12968 // using getKey
12969 var mc = new Roo.util.MixedCollection();
12970 mc.getKey = function(el){
12971    return el.dom.id;
12972 };
12973 mc.add(someEl);
12974 mc.add(otherEl);
12975
12976 // or via the constructor
12977 var mc = new Roo.util.MixedCollection(false, function(el){
12978    return el.dom.id;
12979 });
12980 mc.add(someEl);
12981 mc.add(otherEl);
12982 </code></pre>
12983  * @param o {Object} The item for which to find the key.
12984  * @return {Object} The key for the passed item.
12985  */
12986     getKey : function(o){
12987          return o.id; 
12988     },
12989    
12990 /**
12991  * Replaces an item in the collection.
12992  * @param {String} key The key associated with the item to replace, or the item to replace.
12993  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12994  * @return {Object}  The new item.
12995  */
12996     replace : function(key, o){
12997         if(arguments.length == 1){
12998             o = arguments[0];
12999             key = this.getKey(o);
13000         }
13001         var old = this.item(key);
13002         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13003              return this.add(key, o);
13004         }
13005         var index = this.indexOfKey(key);
13006         this.items[index] = o;
13007         this.map[key] = o;
13008         this.fireEvent("replace", key, old, o);
13009         return o;
13010     },
13011    
13012 /**
13013  * Adds all elements of an Array or an Object to the collection.
13014  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13015  * an Array of values, each of which are added to the collection.
13016  */
13017     addAll : function(objs){
13018         if(arguments.length > 1 || objs instanceof Array){
13019             var args = arguments.length > 1 ? arguments : objs;
13020             for(var i = 0, len = args.length; i < len; i++){
13021                 this.add(args[i]);
13022             }
13023         }else{
13024             for(var key in objs){
13025                 if(this.allowFunctions || typeof objs[key] != "function"){
13026                     this.add(key, objs[key]);
13027                 }
13028             }
13029         }
13030     },
13031    
13032 /**
13033  * Executes the specified function once for every item in the collection, passing each
13034  * item as the first and only parameter. returning false from the function will stop the iteration.
13035  * @param {Function} fn The function to execute for each item.
13036  * @param {Object} scope (optional) The scope in which to execute the function.
13037  */
13038     each : function(fn, scope){
13039         var items = [].concat(this.items); // each safe for removal
13040         for(var i = 0, len = items.length; i < len; i++){
13041             if(fn.call(scope || items[i], items[i], i, len) === false){
13042                 break;
13043             }
13044         }
13045     },
13046    
13047 /**
13048  * Executes the specified function once for every key in the collection, passing each
13049  * key, and its associated item as the first two parameters.
13050  * @param {Function} fn The function to execute for each item.
13051  * @param {Object} scope (optional) The scope in which to execute the function.
13052  */
13053     eachKey : function(fn, scope){
13054         for(var i = 0, len = this.keys.length; i < len; i++){
13055             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13056         }
13057     },
13058    
13059 /**
13060  * Returns the first item in the collection which elicits a true return value from the
13061  * passed selection function.
13062  * @param {Function} fn The selection function to execute for each item.
13063  * @param {Object} scope (optional) The scope in which to execute the function.
13064  * @return {Object} The first item in the collection which returned true from the selection function.
13065  */
13066     find : function(fn, scope){
13067         for(var i = 0, len = this.items.length; i < len; i++){
13068             if(fn.call(scope || window, this.items[i], this.keys[i])){
13069                 return this.items[i];
13070             }
13071         }
13072         return null;
13073     },
13074    
13075 /**
13076  * Inserts an item at the specified index in the collection.
13077  * @param {Number} index The index to insert the item at.
13078  * @param {String} key The key to associate with the new item, or the item itself.
13079  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13080  * @return {Object} The item inserted.
13081  */
13082     insert : function(index, key, o){
13083         if(arguments.length == 2){
13084             o = arguments[1];
13085             key = this.getKey(o);
13086         }
13087         if(index >= this.length){
13088             return this.add(key, o);
13089         }
13090         this.length++;
13091         this.items.splice(index, 0, o);
13092         if(typeof key != "undefined" && key != null){
13093             this.map[key] = o;
13094         }
13095         this.keys.splice(index, 0, key);
13096         this.fireEvent("add", index, o, key);
13097         return o;
13098     },
13099    
13100 /**
13101  * Removed an item from the collection.
13102  * @param {Object} o The item to remove.
13103  * @return {Object} The item removed.
13104  */
13105     remove : function(o){
13106         return this.removeAt(this.indexOf(o));
13107     },
13108    
13109 /**
13110  * Remove an item from a specified index in the collection.
13111  * @param {Number} index The index within the collection of the item to remove.
13112  */
13113     removeAt : function(index){
13114         if(index < this.length && index >= 0){
13115             this.length--;
13116             var o = this.items[index];
13117             this.items.splice(index, 1);
13118             var key = this.keys[index];
13119             if(typeof key != "undefined"){
13120                 delete this.map[key];
13121             }
13122             this.keys.splice(index, 1);
13123             this.fireEvent("remove", o, key);
13124         }
13125     },
13126    
13127 /**
13128  * Removed an item associated with the passed key fom the collection.
13129  * @param {String} key The key of the item to remove.
13130  */
13131     removeKey : function(key){
13132         return this.removeAt(this.indexOfKey(key));
13133     },
13134    
13135 /**
13136  * Returns the number of items in the collection.
13137  * @return {Number} the number of items in the collection.
13138  */
13139     getCount : function(){
13140         return this.length; 
13141     },
13142    
13143 /**
13144  * Returns index within the collection of the passed Object.
13145  * @param {Object} o The item to find the index of.
13146  * @return {Number} index of the item.
13147  */
13148     indexOf : function(o){
13149         if(!this.items.indexOf){
13150             for(var i = 0, len = this.items.length; i < len; i++){
13151                 if(this.items[i] == o) {
13152                     return i;
13153                 }
13154             }
13155             return -1;
13156         }else{
13157             return this.items.indexOf(o);
13158         }
13159     },
13160    
13161 /**
13162  * Returns index within the collection of the passed key.
13163  * @param {String} key The key to find the index of.
13164  * @return {Number} index of the key.
13165  */
13166     indexOfKey : function(key){
13167         if(!this.keys.indexOf){
13168             for(var i = 0, len = this.keys.length; i < len; i++){
13169                 if(this.keys[i] == key) {
13170                     return i;
13171                 }
13172             }
13173             return -1;
13174         }else{
13175             return this.keys.indexOf(key);
13176         }
13177     },
13178    
13179 /**
13180  * Returns the item associated with the passed key OR index. Key has priority over index.
13181  * @param {String/Number} key The key or index of the item.
13182  * @return {Object} The item associated with the passed key.
13183  */
13184     item : function(key){
13185         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13186         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13187     },
13188     
13189 /**
13190  * Returns the item at the specified index.
13191  * @param {Number} index The index of the item.
13192  * @return {Object}
13193  */
13194     itemAt : function(index){
13195         return this.items[index];
13196     },
13197     
13198 /**
13199  * Returns the item associated with the passed key.
13200  * @param {String/Number} key The key of the item.
13201  * @return {Object} The item associated with the passed key.
13202  */
13203     key : function(key){
13204         return this.map[key];
13205     },
13206    
13207 /**
13208  * Returns true if the collection contains the passed Object as an item.
13209  * @param {Object} o  The Object to look for in the collection.
13210  * @return {Boolean} True if the collection contains the Object as an item.
13211  */
13212     contains : function(o){
13213         return this.indexOf(o) != -1;
13214     },
13215    
13216 /**
13217  * Returns true if the collection contains the passed Object as a key.
13218  * @param {String} key The key to look for in the collection.
13219  * @return {Boolean} True if the collection contains the Object as a key.
13220  */
13221     containsKey : function(key){
13222         return typeof this.map[key] != "undefined";
13223     },
13224    
13225 /**
13226  * Removes all items from the collection.
13227  */
13228     clear : function(){
13229         this.length = 0;
13230         this.items = [];
13231         this.keys = [];
13232         this.map = {};
13233         this.fireEvent("clear");
13234     },
13235    
13236 /**
13237  * Returns the first item in the collection.
13238  * @return {Object} the first item in the collection..
13239  */
13240     first : function(){
13241         return this.items[0]; 
13242     },
13243    
13244 /**
13245  * Returns the last item in the collection.
13246  * @return {Object} the last item in the collection..
13247  */
13248     last : function(){
13249         return this.items[this.length-1];   
13250     },
13251     
13252     _sort : function(property, dir, fn){
13253         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13254         fn = fn || function(a, b){
13255             return a-b;
13256         };
13257         var c = [], k = this.keys, items = this.items;
13258         for(var i = 0, len = items.length; i < len; i++){
13259             c[c.length] = {key: k[i], value: items[i], index: i};
13260         }
13261         c.sort(function(a, b){
13262             var v = fn(a[property], b[property]) * dsc;
13263             if(v == 0){
13264                 v = (a.index < b.index ? -1 : 1);
13265             }
13266             return v;
13267         });
13268         for(var i = 0, len = c.length; i < len; i++){
13269             items[i] = c[i].value;
13270             k[i] = c[i].key;
13271         }
13272         this.fireEvent("sort", this);
13273     },
13274     
13275     /**
13276      * Sorts this collection with the passed comparison function
13277      * @param {String} direction (optional) "ASC" or "DESC"
13278      * @param {Function} fn (optional) comparison function
13279      */
13280     sort : function(dir, fn){
13281         this._sort("value", dir, fn);
13282     },
13283     
13284     /**
13285      * Sorts this collection by keys
13286      * @param {String} direction (optional) "ASC" or "DESC"
13287      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13288      */
13289     keySort : function(dir, fn){
13290         this._sort("key", dir, fn || function(a, b){
13291             return String(a).toUpperCase()-String(b).toUpperCase();
13292         });
13293     },
13294     
13295     /**
13296      * Returns a range of items in this collection
13297      * @param {Number} startIndex (optional) defaults to 0
13298      * @param {Number} endIndex (optional) default to the last item
13299      * @return {Array} An array of items
13300      */
13301     getRange : function(start, end){
13302         var items = this.items;
13303         if(items.length < 1){
13304             return [];
13305         }
13306         start = start || 0;
13307         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13308         var r = [];
13309         if(start <= end){
13310             for(var i = start; i <= end; i++) {
13311                     r[r.length] = items[i];
13312             }
13313         }else{
13314             for(var i = start; i >= end; i--) {
13315                     r[r.length] = items[i];
13316             }
13317         }
13318         return r;
13319     },
13320         
13321     /**
13322      * Filter the <i>objects</i> in this collection by a specific property. 
13323      * Returns a new collection that has been filtered.
13324      * @param {String} property A property on your objects
13325      * @param {String/RegExp} value Either string that the property values 
13326      * should start with or a RegExp to test against the property
13327      * @return {MixedCollection} The new filtered collection
13328      */
13329     filter : function(property, value){
13330         if(!value.exec){ // not a regex
13331             value = String(value);
13332             if(value.length == 0){
13333                 return this.clone();
13334             }
13335             value = new RegExp("^" + Roo.escapeRe(value), "i");
13336         }
13337         return this.filterBy(function(o){
13338             return o && value.test(o[property]);
13339         });
13340         },
13341     
13342     /**
13343      * Filter by a function. * Returns a new collection that has been filtered.
13344      * The passed function will be called with each 
13345      * object in the collection. If the function returns true, the value is included 
13346      * otherwise it is filtered.
13347      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13348      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13349      * @return {MixedCollection} The new filtered collection
13350      */
13351     filterBy : function(fn, scope){
13352         var r = new Roo.util.MixedCollection();
13353         r.getKey = this.getKey;
13354         var k = this.keys, it = this.items;
13355         for(var i = 0, len = it.length; i < len; i++){
13356             if(fn.call(scope||this, it[i], k[i])){
13357                                 r.add(k[i], it[i]);
13358                         }
13359         }
13360         return r;
13361     },
13362     
13363     /**
13364      * Creates a duplicate of this collection
13365      * @return {MixedCollection}
13366      */
13367     clone : function(){
13368         var r = new Roo.util.MixedCollection();
13369         var k = this.keys, it = this.items;
13370         for(var i = 0, len = it.length; i < len; i++){
13371             r.add(k[i], it[i]);
13372         }
13373         r.getKey = this.getKey;
13374         return r;
13375     }
13376 });
13377 /**
13378  * Returns the item associated with the passed key or index.
13379  * @method
13380  * @param {String/Number} key The key or index of the item.
13381  * @return {Object} The item associated with the passed key.
13382  */
13383 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13384  * Based on:
13385  * Ext JS Library 1.1.1
13386  * Copyright(c) 2006-2007, Ext JS, LLC.
13387  *
13388  * Originally Released Under LGPL - original licence link has changed is not relivant.
13389  *
13390  * Fork - LGPL
13391  * <script type="text/javascript">
13392  */
13393 /**
13394  * @class Roo.util.JSON
13395  * Modified version of Douglas Crockford"s json.js that doesn"t
13396  * mess with the Object prototype 
13397  * http://www.json.org/js.html
13398  * @singleton
13399  */
13400 Roo.util.JSON = new (function(){
13401     var useHasOwn = {}.hasOwnProperty ? true : false;
13402     
13403     // crashes Safari in some instances
13404     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13405     
13406     var pad = function(n) {
13407         return n < 10 ? "0" + n : n;
13408     };
13409     
13410     var m = {
13411         "\b": '\\b',
13412         "\t": '\\t',
13413         "\n": '\\n',
13414         "\f": '\\f',
13415         "\r": '\\r',
13416         '"' : '\\"',
13417         "\\": '\\\\'
13418     };
13419
13420     var encodeString = function(s){
13421         if (/["\\\x00-\x1f]/.test(s)) {
13422             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13423                 var c = m[b];
13424                 if(c){
13425                     return c;
13426                 }
13427                 c = b.charCodeAt();
13428                 return "\\u00" +
13429                     Math.floor(c / 16).toString(16) +
13430                     (c % 16).toString(16);
13431             }) + '"';
13432         }
13433         return '"' + s + '"';
13434     };
13435     
13436     var encodeArray = function(o){
13437         var a = ["["], b, i, l = o.length, v;
13438             for (i = 0; i < l; i += 1) {
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(v === null ? "null" : Roo.util.JSON.encode(v));
13450                         b = true;
13451                 }
13452             }
13453             a.push("]");
13454             return a.join("");
13455     };
13456     
13457     var encodeDate = function(o){
13458         return '"' + o.getFullYear() + "-" +
13459                 pad(o.getMonth() + 1) + "-" +
13460                 pad(o.getDate()) + "T" +
13461                 pad(o.getHours()) + ":" +
13462                 pad(o.getMinutes()) + ":" +
13463                 pad(o.getSeconds()) + '"';
13464     };
13465     
13466     /**
13467      * Encodes an Object, Array or other value
13468      * @param {Mixed} o The variable to encode
13469      * @return {String} The JSON string
13470      */
13471     this.encode = function(o)
13472     {
13473         // should this be extended to fully wrap stringify..
13474         
13475         if(typeof o == "undefined" || o === null){
13476             return "null";
13477         }else if(o instanceof Array){
13478             return encodeArray(o);
13479         }else if(o instanceof Date){
13480             return encodeDate(o);
13481         }else if(typeof o == "string"){
13482             return encodeString(o);
13483         }else if(typeof o == "number"){
13484             return isFinite(o) ? String(o) : "null";
13485         }else if(typeof o == "boolean"){
13486             return String(o);
13487         }else {
13488             var a = ["{"], b, i, v;
13489             for (i in o) {
13490                 if(!useHasOwn || o.hasOwnProperty(i)) {
13491                     v = o[i];
13492                     switch (typeof v) {
13493                     case "undefined":
13494                     case "function":
13495                     case "unknown":
13496                         break;
13497                     default:
13498                         if(b){
13499                             a.push(',');
13500                         }
13501                         a.push(this.encode(i), ":",
13502                                 v === null ? "null" : this.encode(v));
13503                         b = true;
13504                     }
13505                 }
13506             }
13507             a.push("}");
13508             return a.join("");
13509         }
13510     };
13511     
13512     /**
13513      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13514      * @param {String} json The JSON string
13515      * @return {Object} The resulting object
13516      */
13517     this.decode = function(json){
13518         
13519         return  /** eval:var:json */ eval("(" + json + ')');
13520     };
13521 })();
13522 /** 
13523  * Shorthand for {@link Roo.util.JSON#encode}
13524  * @member Roo encode 
13525  * @method */
13526 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13527 /** 
13528  * Shorthand for {@link Roo.util.JSON#decode}
13529  * @member Roo decode 
13530  * @method */
13531 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13532 /*
13533  * Based on:
13534  * Ext JS Library 1.1.1
13535  * Copyright(c) 2006-2007, Ext JS, LLC.
13536  *
13537  * Originally Released Under LGPL - original licence link has changed is not relivant.
13538  *
13539  * Fork - LGPL
13540  * <script type="text/javascript">
13541  */
13542  
13543 /**
13544  * @class Roo.util.Format
13545  * Reusable data formatting functions
13546  * @singleton
13547  */
13548 Roo.util.Format = function(){
13549     var trimRe = /^\s+|\s+$/g;
13550     return {
13551         /**
13552          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13553          * @param {String} value The string to truncate
13554          * @param {Number} length The maximum length to allow before truncating
13555          * @return {String} The converted text
13556          */
13557         ellipsis : function(value, len){
13558             if(value && value.length > len){
13559                 return value.substr(0, len-3)+"...";
13560             }
13561             return value;
13562         },
13563
13564         /**
13565          * Checks a reference and converts it to empty string if it is undefined
13566          * @param {Mixed} value Reference to check
13567          * @return {Mixed} Empty string if converted, otherwise the original value
13568          */
13569         undef : function(value){
13570             return typeof value != "undefined" ? value : "";
13571         },
13572
13573         /**
13574          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13575          * @param {String} value The string to encode
13576          * @return {String} The encoded text
13577          */
13578         htmlEncode : function(value){
13579             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13580         },
13581
13582         /**
13583          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13584          * @param {String} value The string to decode
13585          * @return {String} The decoded text
13586          */
13587         htmlDecode : function(value){
13588             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13589         },
13590
13591         /**
13592          * Trims any whitespace from either side of a string
13593          * @param {String} value The text to trim
13594          * @return {String} The trimmed text
13595          */
13596         trim : function(value){
13597             return String(value).replace(trimRe, "");
13598         },
13599
13600         /**
13601          * Returns a substring from within an original string
13602          * @param {String} value The original text
13603          * @param {Number} start The start index of the substring
13604          * @param {Number} length The length of the substring
13605          * @return {String} The substring
13606          */
13607         substr : function(value, start, length){
13608             return String(value).substr(start, length);
13609         },
13610
13611         /**
13612          * Converts a string to all lower case letters
13613          * @param {String} value The text to convert
13614          * @return {String} The converted text
13615          */
13616         lowercase : function(value){
13617             return String(value).toLowerCase();
13618         },
13619
13620         /**
13621          * Converts a string to all upper case letters
13622          * @param {String} value The text to convert
13623          * @return {String} The converted text
13624          */
13625         uppercase : function(value){
13626             return String(value).toUpperCase();
13627         },
13628
13629         /**
13630          * Converts the first character only of a string to upper case
13631          * @param {String} value The text to convert
13632          * @return {String} The converted text
13633          */
13634         capitalize : function(value){
13635             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13636         },
13637
13638         // private
13639         call : function(value, fn){
13640             if(arguments.length > 2){
13641                 var args = Array.prototype.slice.call(arguments, 2);
13642                 args.unshift(value);
13643                  
13644                 return /** eval:var:value */  eval(fn).apply(window, args);
13645             }else{
13646                 /** eval:var:value */
13647                 return /** eval:var:value */ eval(fn).call(window, value);
13648             }
13649         },
13650
13651        
13652         /**
13653          * safer version of Math.toFixed..??/
13654          * @param {Number/String} value The numeric value to format
13655          * @param {Number/String} value Decimal places 
13656          * @return {String} The formatted currency string
13657          */
13658         toFixed : function(v, n)
13659         {
13660             // why not use to fixed - precision is buggered???
13661             if (!n) {
13662                 return Math.round(v-0);
13663             }
13664             var fact = Math.pow(10,n+1);
13665             v = (Math.round((v-0)*fact))/fact;
13666             var z = (''+fact).substring(2);
13667             if (v == Math.floor(v)) {
13668                 return Math.floor(v) + '.' + z;
13669             }
13670             
13671             // now just padd decimals..
13672             var ps = String(v).split('.');
13673             var fd = (ps[1] + z);
13674             var r = fd.substring(0,n); 
13675             var rm = fd.substring(n); 
13676             if (rm < 5) {
13677                 return ps[0] + '.' + r;
13678             }
13679             r*=1; // turn it into a number;
13680             r++;
13681             if (String(r).length != n) {
13682                 ps[0]*=1;
13683                 ps[0]++;
13684                 r = String(r).substring(1); // chop the end off.
13685             }
13686             
13687             return ps[0] + '.' + r;
13688              
13689         },
13690         
13691         /**
13692          * Format a number as US currency
13693          * @param {Number/String} value The numeric value to format
13694          * @return {String} The formatted currency string
13695          */
13696         usMoney : function(v){
13697             return '$' + Roo.util.Format.number(v);
13698         },
13699         
13700         /**
13701          * Format a number
13702          * eventually this should probably emulate php's number_format
13703          * @param {Number/String} value The numeric value to format
13704          * @param {Number} decimals number of decimal places
13705          * @return {String} The formatted currency string
13706          */
13707         number : function(v,decimals)
13708         {
13709             // multiply and round.
13710             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13711             var mul = Math.pow(10, decimals);
13712             var zero = String(mul).substring(1);
13713             v = (Math.round((v-0)*mul))/mul;
13714             
13715             // if it's '0' number.. then
13716             
13717             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13718             v = String(v);
13719             var ps = v.split('.');
13720             var whole = ps[0];
13721             
13722             
13723             var r = /(\d+)(\d{3})/;
13724             // add comma's
13725             while (r.test(whole)) {
13726                 whole = whole.replace(r, '$1' + ',' + '$2');
13727             }
13728             
13729             
13730             var sub = ps[1] ?
13731                     // has decimals..
13732                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13733                     // does not have decimals
13734                     (decimals ? ('.' + zero) : '');
13735             
13736             
13737             return whole + sub ;
13738         },
13739         
13740         /**
13741          * Parse a value into a formatted date using the specified format pattern.
13742          * @param {Mixed} value The value to format
13743          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13744          * @return {String} The formatted date string
13745          */
13746         date : function(v, format){
13747             if(!v){
13748                 return "";
13749             }
13750             if(!(v instanceof Date)){
13751                 v = new Date(Date.parse(v));
13752             }
13753             return v.dateFormat(format || Roo.util.Format.defaults.date);
13754         },
13755
13756         /**
13757          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13758          * @param {String} format Any valid date format string
13759          * @return {Function} The date formatting function
13760          */
13761         dateRenderer : function(format){
13762             return function(v){
13763                 return Roo.util.Format.date(v, format);  
13764             };
13765         },
13766
13767         // private
13768         stripTagsRE : /<\/?[^>]+>/gi,
13769         
13770         /**
13771          * Strips all HTML tags
13772          * @param {Mixed} value The text from which to strip tags
13773          * @return {String} The stripped text
13774          */
13775         stripTags : function(v){
13776             return !v ? v : String(v).replace(this.stripTagsRE, "");
13777         }
13778     };
13779 }();
13780 Roo.util.Format.defaults = {
13781     date : 'd/M/Y'
13782 };/*
13783  * Based on:
13784  * Ext JS Library 1.1.1
13785  * Copyright(c) 2006-2007, Ext JS, LLC.
13786  *
13787  * Originally Released Under LGPL - original licence link has changed is not relivant.
13788  *
13789  * Fork - LGPL
13790  * <script type="text/javascript">
13791  */
13792
13793
13794  
13795
13796 /**
13797  * @class Roo.MasterTemplate
13798  * @extends Roo.Template
13799  * Provides a template that can have child templates. The syntax is:
13800 <pre><code>
13801 var t = new Roo.MasterTemplate(
13802         '&lt;select name="{name}"&gt;',
13803                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13804         '&lt;/select&gt;'
13805 );
13806 t.add('options', {value: 'foo', text: 'bar'});
13807 // or you can add multiple child elements in one shot
13808 t.addAll('options', [
13809     {value: 'foo', text: 'bar'},
13810     {value: 'foo2', text: 'bar2'},
13811     {value: 'foo3', text: 'bar3'}
13812 ]);
13813 // then append, applying the master template values
13814 t.append('my-form', {name: 'my-select'});
13815 </code></pre>
13816 * A name attribute for the child template is not required if you have only one child
13817 * template or you want to refer to them by index.
13818  */
13819 Roo.MasterTemplate = function(){
13820     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13821     this.originalHtml = this.html;
13822     var st = {};
13823     var m, re = this.subTemplateRe;
13824     re.lastIndex = 0;
13825     var subIndex = 0;
13826     while(m = re.exec(this.html)){
13827         var name = m[1], content = m[2];
13828         st[subIndex] = {
13829             name: name,
13830             index: subIndex,
13831             buffer: [],
13832             tpl : new Roo.Template(content)
13833         };
13834         if(name){
13835             st[name] = st[subIndex];
13836         }
13837         st[subIndex].tpl.compile();
13838         st[subIndex].tpl.call = this.call.createDelegate(this);
13839         subIndex++;
13840     }
13841     this.subCount = subIndex;
13842     this.subs = st;
13843 };
13844 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13845     /**
13846     * The regular expression used to match sub templates
13847     * @type RegExp
13848     * @property
13849     */
13850     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13851
13852     /**
13853      * Applies the passed values to a child template.
13854      * @param {String/Number} name (optional) The name or index of the child template
13855      * @param {Array/Object} values The values to be applied to the template
13856      * @return {MasterTemplate} this
13857      */
13858      add : function(name, values){
13859         if(arguments.length == 1){
13860             values = arguments[0];
13861             name = 0;
13862         }
13863         var s = this.subs[name];
13864         s.buffer[s.buffer.length] = s.tpl.apply(values);
13865         return this;
13866     },
13867
13868     /**
13869      * Applies all the passed values to a child template.
13870      * @param {String/Number} name (optional) The name or index of the child template
13871      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13872      * @param {Boolean} reset (optional) True to reset the template first
13873      * @return {MasterTemplate} this
13874      */
13875     fill : function(name, values, reset){
13876         var a = arguments;
13877         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13878             values = a[0];
13879             name = 0;
13880             reset = a[1];
13881         }
13882         if(reset){
13883             this.reset();
13884         }
13885         for(var i = 0, len = values.length; i < len; i++){
13886             this.add(name, values[i]);
13887         }
13888         return this;
13889     },
13890
13891     /**
13892      * Resets the template for reuse
13893      * @return {MasterTemplate} this
13894      */
13895      reset : function(){
13896         var s = this.subs;
13897         for(var i = 0; i < this.subCount; i++){
13898             s[i].buffer = [];
13899         }
13900         return this;
13901     },
13902
13903     applyTemplate : function(values){
13904         var s = this.subs;
13905         var replaceIndex = -1;
13906         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13907             return s[++replaceIndex].buffer.join("");
13908         });
13909         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13910     },
13911
13912     apply : function(){
13913         return this.applyTemplate.apply(this, arguments);
13914     },
13915
13916     compile : function(){return this;}
13917 });
13918
13919 /**
13920  * Alias for fill().
13921  * @method
13922  */
13923 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13924  /**
13925  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13926  * var tpl = Roo.MasterTemplate.from('element-id');
13927  * @param {String/HTMLElement} el
13928  * @param {Object} config
13929  * @static
13930  */
13931 Roo.MasterTemplate.from = function(el, config){
13932     el = Roo.getDom(el);
13933     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13934 };/*
13935  * Based on:
13936  * Ext JS Library 1.1.1
13937  * Copyright(c) 2006-2007, Ext JS, LLC.
13938  *
13939  * Originally Released Under LGPL - original licence link has changed is not relivant.
13940  *
13941  * Fork - LGPL
13942  * <script type="text/javascript">
13943  */
13944
13945  
13946 /**
13947  * @class Roo.util.CSS
13948  * Utility class for manipulating CSS rules
13949  * @singleton
13950  */
13951 Roo.util.CSS = function(){
13952         var rules = null;
13953         var doc = document;
13954
13955     var camelRe = /(-[a-z])/gi;
13956     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13957
13958    return {
13959    /**
13960     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13961     * tag and appended to the HEAD of the document.
13962     * @param {String|Object} cssText The text containing the css rules
13963     * @param {String} id An id to add to the stylesheet for later removal
13964     * @return {StyleSheet}
13965     */
13966     createStyleSheet : function(cssText, id){
13967         var ss;
13968         var head = doc.getElementsByTagName("head")[0];
13969         var nrules = doc.createElement("style");
13970         nrules.setAttribute("type", "text/css");
13971         if(id){
13972             nrules.setAttribute("id", id);
13973         }
13974         if (typeof(cssText) != 'string') {
13975             // support object maps..
13976             // not sure if this a good idea.. 
13977             // perhaps it should be merged with the general css handling
13978             // and handle js style props.
13979             var cssTextNew = [];
13980             for(var n in cssText) {
13981                 var citems = [];
13982                 for(var k in cssText[n]) {
13983                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13984                 }
13985                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13986                 
13987             }
13988             cssText = cssTextNew.join("\n");
13989             
13990         }
13991        
13992        
13993        if(Roo.isIE){
13994            head.appendChild(nrules);
13995            ss = nrules.styleSheet;
13996            ss.cssText = cssText;
13997        }else{
13998            try{
13999                 nrules.appendChild(doc.createTextNode(cssText));
14000            }catch(e){
14001                nrules.cssText = cssText; 
14002            }
14003            head.appendChild(nrules);
14004            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14005        }
14006        this.cacheStyleSheet(ss);
14007        return ss;
14008    },
14009
14010    /**
14011     * Removes a style or link tag by id
14012     * @param {String} id The id of the tag
14013     */
14014    removeStyleSheet : function(id){
14015        var existing = doc.getElementById(id);
14016        if(existing){
14017            existing.parentNode.removeChild(existing);
14018        }
14019    },
14020
14021    /**
14022     * Dynamically swaps an existing stylesheet reference for a new one
14023     * @param {String} id The id of an existing link tag to remove
14024     * @param {String} url The href of the new stylesheet to include
14025     */
14026    swapStyleSheet : function(id, url){
14027        this.removeStyleSheet(id);
14028        var ss = doc.createElement("link");
14029        ss.setAttribute("rel", "stylesheet");
14030        ss.setAttribute("type", "text/css");
14031        ss.setAttribute("id", id);
14032        ss.setAttribute("href", url);
14033        doc.getElementsByTagName("head")[0].appendChild(ss);
14034    },
14035    
14036    /**
14037     * Refresh the rule cache if you have dynamically added stylesheets
14038     * @return {Object} An object (hash) of rules indexed by selector
14039     */
14040    refreshCache : function(){
14041        return this.getRules(true);
14042    },
14043
14044    // private
14045    cacheStyleSheet : function(stylesheet){
14046        if(!rules){
14047            rules = {};
14048        }
14049        try{// try catch for cross domain access issue
14050            var ssRules = stylesheet.cssRules || stylesheet.rules;
14051            for(var j = ssRules.length-1; j >= 0; --j){
14052                rules[ssRules[j].selectorText] = ssRules[j];
14053            }
14054        }catch(e){}
14055    },
14056    
14057    /**
14058     * Gets all css rules for the document
14059     * @param {Boolean} refreshCache true to refresh the internal cache
14060     * @return {Object} An object (hash) of rules indexed by selector
14061     */
14062    getRules : function(refreshCache){
14063                 if(rules == null || refreshCache){
14064                         rules = {};
14065                         var ds = doc.styleSheets;
14066                         for(var i =0, len = ds.length; i < len; i++){
14067                             try{
14068                         this.cacheStyleSheet(ds[i]);
14069                     }catch(e){} 
14070                 }
14071                 }
14072                 return rules;
14073         },
14074         
14075         /**
14076     * Gets an an individual CSS rule by selector(s)
14077     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14078     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14079     * @return {CSSRule} The CSS rule or null if one is not found
14080     */
14081    getRule : function(selector, refreshCache){
14082                 var rs = this.getRules(refreshCache);
14083                 if(!(selector instanceof Array)){
14084                     return rs[selector];
14085                 }
14086                 for(var i = 0; i < selector.length; i++){
14087                         if(rs[selector[i]]){
14088                                 return rs[selector[i]];
14089                         }
14090                 }
14091                 return null;
14092         },
14093         
14094         
14095         /**
14096     * Updates a rule property
14097     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14098     * @param {String} property The css property
14099     * @param {String} value The new value for the property
14100     * @return {Boolean} true If a rule was found and updated
14101     */
14102    updateRule : function(selector, property, value){
14103                 if(!(selector instanceof Array)){
14104                         var rule = this.getRule(selector);
14105                         if(rule){
14106                                 rule.style[property.replace(camelRe, camelFn)] = value;
14107                                 return true;
14108                         }
14109                 }else{
14110                         for(var i = 0; i < selector.length; i++){
14111                                 if(this.updateRule(selector[i], property, value)){
14112                                         return true;
14113                                 }
14114                         }
14115                 }
14116                 return false;
14117         }
14118    };   
14119 }();/*
14120  * Based on:
14121  * Ext JS Library 1.1.1
14122  * Copyright(c) 2006-2007, Ext JS, LLC.
14123  *
14124  * Originally Released Under LGPL - original licence link has changed is not relivant.
14125  *
14126  * Fork - LGPL
14127  * <script type="text/javascript">
14128  */
14129
14130  
14131
14132 /**
14133  * @class Roo.util.ClickRepeater
14134  * @extends Roo.util.Observable
14135  * 
14136  * A wrapper class which can be applied to any element. Fires a "click" event while the
14137  * mouse is pressed. The interval between firings may be specified in the config but
14138  * defaults to 10 milliseconds.
14139  * 
14140  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14141  * 
14142  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14143  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14144  * Similar to an autorepeat key delay.
14145  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14146  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14147  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14148  *           "interval" and "delay" are ignored. "immediate" is honored.
14149  * @cfg {Boolean} preventDefault True to prevent the default click event
14150  * @cfg {Boolean} stopDefault True to stop the default click event
14151  * 
14152  * @history
14153  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14154  *     2007-02-02 jvs Renamed to ClickRepeater
14155  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14156  *
14157  *  @constructor
14158  * @param {String/HTMLElement/Element} el The element to listen on
14159  * @param {Object} config
14160  **/
14161 Roo.util.ClickRepeater = function(el, config)
14162 {
14163     this.el = Roo.get(el);
14164     this.el.unselectable();
14165
14166     Roo.apply(this, config);
14167
14168     this.addEvents({
14169     /**
14170      * @event mousedown
14171      * Fires when the mouse button is depressed.
14172      * @param {Roo.util.ClickRepeater} this
14173      */
14174         "mousedown" : true,
14175     /**
14176      * @event click
14177      * Fires on a specified interval during the time the element is pressed.
14178      * @param {Roo.util.ClickRepeater} this
14179      */
14180         "click" : true,
14181     /**
14182      * @event mouseup
14183      * Fires when the mouse key is released.
14184      * @param {Roo.util.ClickRepeater} this
14185      */
14186         "mouseup" : true
14187     });
14188
14189     this.el.on("mousedown", this.handleMouseDown, this);
14190     if(this.preventDefault || this.stopDefault){
14191         this.el.on("click", function(e){
14192             if(this.preventDefault){
14193                 e.preventDefault();
14194             }
14195             if(this.stopDefault){
14196                 e.stopEvent();
14197             }
14198         }, this);
14199     }
14200
14201     // allow inline handler
14202     if(this.handler){
14203         this.on("click", this.handler,  this.scope || this);
14204     }
14205
14206     Roo.util.ClickRepeater.superclass.constructor.call(this);
14207 };
14208
14209 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14210     interval : 20,
14211     delay: 250,
14212     preventDefault : true,
14213     stopDefault : false,
14214     timer : 0,
14215
14216     // private
14217     handleMouseDown : function(){
14218         clearTimeout(this.timer);
14219         this.el.blur();
14220         if(this.pressClass){
14221             this.el.addClass(this.pressClass);
14222         }
14223         this.mousedownTime = new Date();
14224
14225         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14226         this.el.on("mouseout", this.handleMouseOut, this);
14227
14228         this.fireEvent("mousedown", this);
14229         this.fireEvent("click", this);
14230         
14231         this.timer = this.click.defer(this.delay || this.interval, this);
14232     },
14233
14234     // private
14235     click : function(){
14236         this.fireEvent("click", this);
14237         this.timer = this.click.defer(this.getInterval(), this);
14238     },
14239
14240     // private
14241     getInterval: function(){
14242         if(!this.accelerate){
14243             return this.interval;
14244         }
14245         var pressTime = this.mousedownTime.getElapsed();
14246         if(pressTime < 500){
14247             return 400;
14248         }else if(pressTime < 1700){
14249             return 320;
14250         }else if(pressTime < 2600){
14251             return 250;
14252         }else if(pressTime < 3500){
14253             return 180;
14254         }else if(pressTime < 4400){
14255             return 140;
14256         }else if(pressTime < 5300){
14257             return 80;
14258         }else if(pressTime < 6200){
14259             return 50;
14260         }else{
14261             return 10;
14262         }
14263     },
14264
14265     // private
14266     handleMouseOut : function(){
14267         clearTimeout(this.timer);
14268         if(this.pressClass){
14269             this.el.removeClass(this.pressClass);
14270         }
14271         this.el.on("mouseover", this.handleMouseReturn, this);
14272     },
14273
14274     // private
14275     handleMouseReturn : function(){
14276         this.el.un("mouseover", this.handleMouseReturn);
14277         if(this.pressClass){
14278             this.el.addClass(this.pressClass);
14279         }
14280         this.click();
14281     },
14282
14283     // private
14284     handleMouseUp : function(){
14285         clearTimeout(this.timer);
14286         this.el.un("mouseover", this.handleMouseReturn);
14287         this.el.un("mouseout", this.handleMouseOut);
14288         Roo.get(document).un("mouseup", this.handleMouseUp);
14289         this.el.removeClass(this.pressClass);
14290         this.fireEvent("mouseup", this);
14291     }
14292 });/*
14293  * Based on:
14294  * Ext JS Library 1.1.1
14295  * Copyright(c) 2006-2007, Ext JS, LLC.
14296  *
14297  * Originally Released Under LGPL - original licence link has changed is not relivant.
14298  *
14299  * Fork - LGPL
14300  * <script type="text/javascript">
14301  */
14302
14303  
14304 /**
14305  * @class Roo.KeyNav
14306  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14307  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14308  * way to implement custom navigation schemes for any UI component.</p>
14309  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14310  * pageUp, pageDown, del, home, end.  Usage:</p>
14311  <pre><code>
14312 var nav = new Roo.KeyNav("my-element", {
14313     "left" : function(e){
14314         this.moveLeft(e.ctrlKey);
14315     },
14316     "right" : function(e){
14317         this.moveRight(e.ctrlKey);
14318     },
14319     "enter" : function(e){
14320         this.save();
14321     },
14322     scope : this
14323 });
14324 </code></pre>
14325  * @constructor
14326  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14327  * @param {Object} config The config
14328  */
14329 Roo.KeyNav = function(el, config){
14330     this.el = Roo.get(el);
14331     Roo.apply(this, config);
14332     if(!this.disabled){
14333         this.disabled = true;
14334         this.enable();
14335     }
14336 };
14337
14338 Roo.KeyNav.prototype = {
14339     /**
14340      * @cfg {Boolean} disabled
14341      * True to disable this KeyNav instance (defaults to false)
14342      */
14343     disabled : false,
14344     /**
14345      * @cfg {String} defaultEventAction
14346      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14347      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14348      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14349      */
14350     defaultEventAction: "stopEvent",
14351     /**
14352      * @cfg {Boolean} forceKeyDown
14353      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14354      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14355      * handle keydown instead of keypress.
14356      */
14357     forceKeyDown : false,
14358
14359     // private
14360     prepareEvent : function(e){
14361         var k = e.getKey();
14362         var h = this.keyToHandler[k];
14363         //if(h && this[h]){
14364         //    e.stopPropagation();
14365         //}
14366         if(Roo.isSafari && h && k >= 37 && k <= 40){
14367             e.stopEvent();
14368         }
14369     },
14370
14371     // private
14372     relay : function(e){
14373         var k = e.getKey();
14374         var h = this.keyToHandler[k];
14375         if(h && this[h]){
14376             if(this.doRelay(e, this[h], h) !== true){
14377                 e[this.defaultEventAction]();
14378             }
14379         }
14380     },
14381
14382     // private
14383     doRelay : function(e, h, hname){
14384         return h.call(this.scope || this, e);
14385     },
14386
14387     // possible handlers
14388     enter : false,
14389     left : false,
14390     right : false,
14391     up : false,
14392     down : false,
14393     tab : false,
14394     esc : false,
14395     pageUp : false,
14396     pageDown : false,
14397     del : false,
14398     home : false,
14399     end : false,
14400
14401     // quick lookup hash
14402     keyToHandler : {
14403         37 : "left",
14404         39 : "right",
14405         38 : "up",
14406         40 : "down",
14407         33 : "pageUp",
14408         34 : "pageDown",
14409         46 : "del",
14410         36 : "home",
14411         35 : "end",
14412         13 : "enter",
14413         27 : "esc",
14414         9  : "tab"
14415     },
14416
14417         /**
14418          * Enable this KeyNav
14419          */
14420         enable: function(){
14421                 if(this.disabled){
14422             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14423             // the EventObject will normalize Safari automatically
14424             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14425                 this.el.on("keydown", this.relay,  this);
14426             }else{
14427                 this.el.on("keydown", this.prepareEvent,  this);
14428                 this.el.on("keypress", this.relay,  this);
14429             }
14430                     this.disabled = false;
14431                 }
14432         },
14433
14434         /**
14435          * Disable this KeyNav
14436          */
14437         disable: function(){
14438                 if(!this.disabled){
14439                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14440                 this.el.un("keydown", this.relay);
14441             }else{
14442                 this.el.un("keydown", this.prepareEvent);
14443                 this.el.un("keypress", this.relay);
14444             }
14445                     this.disabled = true;
14446                 }
14447         }
14448 };/*
14449  * Based on:
14450  * Ext JS Library 1.1.1
14451  * Copyright(c) 2006-2007, Ext JS, LLC.
14452  *
14453  * Originally Released Under LGPL - original licence link has changed is not relivant.
14454  *
14455  * Fork - LGPL
14456  * <script type="text/javascript">
14457  */
14458
14459  
14460 /**
14461  * @class Roo.KeyMap
14462  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14463  * The constructor accepts the same config object as defined by {@link #addBinding}.
14464  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14465  * combination it will call the function with this signature (if the match is a multi-key
14466  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14467  * A KeyMap can also handle a string representation of keys.<br />
14468  * Usage:
14469  <pre><code>
14470 // map one key by key code
14471 var map = new Roo.KeyMap("my-element", {
14472     key: 13, // or Roo.EventObject.ENTER
14473     fn: myHandler,
14474     scope: myObject
14475 });
14476
14477 // map multiple keys to one action by string
14478 var map = new Roo.KeyMap("my-element", {
14479     key: "a\r\n\t",
14480     fn: myHandler,
14481     scope: myObject
14482 });
14483
14484 // map multiple keys to multiple actions by strings and array of codes
14485 var map = new Roo.KeyMap("my-element", [
14486     {
14487         key: [10,13],
14488         fn: function(){ alert("Return was pressed"); }
14489     }, {
14490         key: "abc",
14491         fn: function(){ alert('a, b or c was pressed'); }
14492     }, {
14493         key: "\t",
14494         ctrl:true,
14495         shift:true,
14496         fn: function(){ alert('Control + shift + tab was pressed.'); }
14497     }
14498 ]);
14499 </code></pre>
14500  * <b>Note: A KeyMap starts enabled</b>
14501  * @constructor
14502  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14503  * @param {Object} config The config (see {@link #addBinding})
14504  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14505  */
14506 Roo.KeyMap = function(el, config, eventName){
14507     this.el  = Roo.get(el);
14508     this.eventName = eventName || "keydown";
14509     this.bindings = [];
14510     if(config){
14511         this.addBinding(config);
14512     }
14513     this.enable();
14514 };
14515
14516 Roo.KeyMap.prototype = {
14517     /**
14518      * True to stop the event from bubbling and prevent the default browser action if the
14519      * key was handled by the KeyMap (defaults to false)
14520      * @type Boolean
14521      */
14522     stopEvent : false,
14523
14524     /**
14525      * Add a new binding to this KeyMap. The following config object properties are supported:
14526      * <pre>
14527 Property    Type             Description
14528 ----------  ---------------  ----------------------------------------------------------------------
14529 key         String/Array     A single keycode or an array of keycodes to handle
14530 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14531 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14532 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14533 fn          Function         The function to call when KeyMap finds the expected key combination
14534 scope       Object           The scope of the callback function
14535 </pre>
14536      *
14537      * Usage:
14538      * <pre><code>
14539 // Create a KeyMap
14540 var map = new Roo.KeyMap(document, {
14541     key: Roo.EventObject.ENTER,
14542     fn: handleKey,
14543     scope: this
14544 });
14545
14546 //Add a new binding to the existing KeyMap later
14547 map.addBinding({
14548     key: 'abc',
14549     shift: true,
14550     fn: handleKey,
14551     scope: this
14552 });
14553 </code></pre>
14554      * @param {Object/Array} config A single KeyMap config or an array of configs
14555      */
14556         addBinding : function(config){
14557         if(config instanceof Array){
14558             for(var i = 0, len = config.length; i < len; i++){
14559                 this.addBinding(config[i]);
14560             }
14561             return;
14562         }
14563         var keyCode = config.key,
14564             shift = config.shift, 
14565             ctrl = config.ctrl, 
14566             alt = config.alt,
14567             fn = config.fn,
14568             scope = config.scope;
14569         if(typeof keyCode == "string"){
14570             var ks = [];
14571             var keyString = keyCode.toUpperCase();
14572             for(var j = 0, len = keyString.length; j < len; j++){
14573                 ks.push(keyString.charCodeAt(j));
14574             }
14575             keyCode = ks;
14576         }
14577         var keyArray = keyCode instanceof Array;
14578         var handler = function(e){
14579             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14580                 var k = e.getKey();
14581                 if(keyArray){
14582                     for(var i = 0, len = keyCode.length; i < len; i++){
14583                         if(keyCode[i] == k){
14584                           if(this.stopEvent){
14585                               e.stopEvent();
14586                           }
14587                           fn.call(scope || window, k, e);
14588                           return;
14589                         }
14590                     }
14591                 }else{
14592                     if(k == keyCode){
14593                         if(this.stopEvent){
14594                            e.stopEvent();
14595                         }
14596                         fn.call(scope || window, k, e);
14597                     }
14598                 }
14599             }
14600         };
14601         this.bindings.push(handler);  
14602         },
14603
14604     /**
14605      * Shorthand for adding a single key listener
14606      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14607      * following options:
14608      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14609      * @param {Function} fn The function to call
14610      * @param {Object} scope (optional) The scope of the function
14611      */
14612     on : function(key, fn, scope){
14613         var keyCode, shift, ctrl, alt;
14614         if(typeof key == "object" && !(key instanceof Array)){
14615             keyCode = key.key;
14616             shift = key.shift;
14617             ctrl = key.ctrl;
14618             alt = key.alt;
14619         }else{
14620             keyCode = key;
14621         }
14622         this.addBinding({
14623             key: keyCode,
14624             shift: shift,
14625             ctrl: ctrl,
14626             alt: alt,
14627             fn: fn,
14628             scope: scope
14629         })
14630     },
14631
14632     // private
14633     handleKeyDown : function(e){
14634             if(this.enabled){ //just in case
14635             var b = this.bindings;
14636             for(var i = 0, len = b.length; i < len; i++){
14637                 b[i].call(this, e);
14638             }
14639             }
14640         },
14641         
14642         /**
14643          * Returns true if this KeyMap is enabled
14644          * @return {Boolean} 
14645          */
14646         isEnabled : function(){
14647             return this.enabled;  
14648         },
14649         
14650         /**
14651          * Enables this KeyMap
14652          */
14653         enable: function(){
14654                 if(!this.enabled){
14655                     this.el.on(this.eventName, this.handleKeyDown, this);
14656                     this.enabled = true;
14657                 }
14658         },
14659
14660         /**
14661          * Disable this KeyMap
14662          */
14663         disable: function(){
14664                 if(this.enabled){
14665                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14666                     this.enabled = false;
14667                 }
14668         }
14669 };/*
14670  * Based on:
14671  * Ext JS Library 1.1.1
14672  * Copyright(c) 2006-2007, Ext JS, LLC.
14673  *
14674  * Originally Released Under LGPL - original licence link has changed is not relivant.
14675  *
14676  * Fork - LGPL
14677  * <script type="text/javascript">
14678  */
14679
14680  
14681 /**
14682  * @class Roo.util.TextMetrics
14683  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14684  * wide, in pixels, a given block of text will be.
14685  * @singleton
14686  */
14687 Roo.util.TextMetrics = function(){
14688     var shared;
14689     return {
14690         /**
14691          * Measures the size of the specified text
14692          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14693          * that can affect the size of the rendered text
14694          * @param {String} text The text to measure
14695          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14696          * in order to accurately measure the text height
14697          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14698          */
14699         measure : function(el, text, fixedWidth){
14700             if(!shared){
14701                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14702             }
14703             shared.bind(el);
14704             shared.setFixedWidth(fixedWidth || 'auto');
14705             return shared.getSize(text);
14706         },
14707
14708         /**
14709          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14710          * the overhead of multiple calls to initialize the style properties on each measurement.
14711          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14712          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14713          * in order to accurately measure the text height
14714          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14715          */
14716         createInstance : function(el, fixedWidth){
14717             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14718         }
14719     };
14720 }();
14721
14722  
14723
14724 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14725     var ml = new Roo.Element(document.createElement('div'));
14726     document.body.appendChild(ml.dom);
14727     ml.position('absolute');
14728     ml.setLeftTop(-1000, -1000);
14729     ml.hide();
14730
14731     if(fixedWidth){
14732         ml.setWidth(fixedWidth);
14733     }
14734      
14735     var instance = {
14736         /**
14737          * Returns the size of the specified text based on the internal element's style and width properties
14738          * @memberOf Roo.util.TextMetrics.Instance#
14739          * @param {String} text The text to measure
14740          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14741          */
14742         getSize : function(text){
14743             ml.update(text);
14744             var s = ml.getSize();
14745             ml.update('');
14746             return s;
14747         },
14748
14749         /**
14750          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14751          * that can affect the size of the rendered text
14752          * @memberOf Roo.util.TextMetrics.Instance#
14753          * @param {String/HTMLElement} el The element, dom node or id
14754          */
14755         bind : function(el){
14756             ml.setStyle(
14757                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14758             );
14759         },
14760
14761         /**
14762          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14763          * to set a fixed width in order to accurately measure the text height.
14764          * @memberOf Roo.util.TextMetrics.Instance#
14765          * @param {Number} width The width to set on the element
14766          */
14767         setFixedWidth : function(width){
14768             ml.setWidth(width);
14769         },
14770
14771         /**
14772          * Returns the measured width of the specified text
14773          * @memberOf Roo.util.TextMetrics.Instance#
14774          * @param {String} text The text to measure
14775          * @return {Number} width The width in pixels
14776          */
14777         getWidth : function(text){
14778             ml.dom.style.width = 'auto';
14779             return this.getSize(text).width;
14780         },
14781
14782         /**
14783          * Returns the measured height of the specified text.  For multiline text, be sure to call
14784          * {@link #setFixedWidth} if necessary.
14785          * @memberOf Roo.util.TextMetrics.Instance#
14786          * @param {String} text The text to measure
14787          * @return {Number} height The height in pixels
14788          */
14789         getHeight : function(text){
14790             return this.getSize(text).height;
14791         }
14792     };
14793
14794     instance.bind(bindTo);
14795
14796     return instance;
14797 };
14798
14799 // backwards compat
14800 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14801  * Based on:
14802  * Ext JS Library 1.1.1
14803  * Copyright(c) 2006-2007, Ext JS, LLC.
14804  *
14805  * Originally Released Under LGPL - original licence link has changed is not relivant.
14806  *
14807  * Fork - LGPL
14808  * <script type="text/javascript">
14809  */
14810
14811 /**
14812  * @class Roo.state.Provider
14813  * Abstract base class for state provider implementations. This class provides methods
14814  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14815  * Provider interface.
14816  */
14817 Roo.state.Provider = function(){
14818     /**
14819      * @event statechange
14820      * Fires when a state change occurs.
14821      * @param {Provider} this This state provider
14822      * @param {String} key The state key which was changed
14823      * @param {String} value The encoded value for the state
14824      */
14825     this.addEvents({
14826         "statechange": true
14827     });
14828     this.state = {};
14829     Roo.state.Provider.superclass.constructor.call(this);
14830 };
14831 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14832     /**
14833      * Returns the current value for a key
14834      * @param {String} name The key name
14835      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14836      * @return {Mixed} The state data
14837      */
14838     get : function(name, defaultValue){
14839         return typeof this.state[name] == "undefined" ?
14840             defaultValue : this.state[name];
14841     },
14842     
14843     /**
14844      * Clears a value from the state
14845      * @param {String} name The key name
14846      */
14847     clear : function(name){
14848         delete this.state[name];
14849         this.fireEvent("statechange", this, name, null);
14850     },
14851     
14852     /**
14853      * Sets the value for a key
14854      * @param {String} name The key name
14855      * @param {Mixed} value The value to set
14856      */
14857     set : function(name, value){
14858         this.state[name] = value;
14859         this.fireEvent("statechange", this, name, value);
14860     },
14861     
14862     /**
14863      * Decodes a string previously encoded with {@link #encodeValue}.
14864      * @param {String} value The value to decode
14865      * @return {Mixed} The decoded value
14866      */
14867     decodeValue : function(cookie){
14868         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14869         var matches = re.exec(unescape(cookie));
14870         if(!matches || !matches[1]) {
14871             return; // non state cookie
14872         }
14873         var type = matches[1];
14874         var v = matches[2];
14875         switch(type){
14876             case "n":
14877                 return parseFloat(v);
14878             case "d":
14879                 return new Date(Date.parse(v));
14880             case "b":
14881                 return (v == "1");
14882             case "a":
14883                 var all = [];
14884                 var values = v.split("^");
14885                 for(var i = 0, len = values.length; i < len; i++){
14886                     all.push(this.decodeValue(values[i]));
14887                 }
14888                 return all;
14889            case "o":
14890                 var all = {};
14891                 var values = v.split("^");
14892                 for(var i = 0, len = values.length; i < len; i++){
14893                     var kv = values[i].split("=");
14894                     all[kv[0]] = this.decodeValue(kv[1]);
14895                 }
14896                 return all;
14897            default:
14898                 return v;
14899         }
14900     },
14901     
14902     /**
14903      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14904      * @param {Mixed} value The value to encode
14905      * @return {String} The encoded value
14906      */
14907     encodeValue : function(v){
14908         var enc;
14909         if(typeof v == "number"){
14910             enc = "n:" + v;
14911         }else if(typeof v == "boolean"){
14912             enc = "b:" + (v ? "1" : "0");
14913         }else if(v instanceof Date){
14914             enc = "d:" + v.toGMTString();
14915         }else if(v instanceof Array){
14916             var flat = "";
14917             for(var i = 0, len = v.length; i < len; i++){
14918                 flat += this.encodeValue(v[i]);
14919                 if(i != len-1) {
14920                     flat += "^";
14921                 }
14922             }
14923             enc = "a:" + flat;
14924         }else if(typeof v == "object"){
14925             var flat = "";
14926             for(var key in v){
14927                 if(typeof v[key] != "function"){
14928                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14929                 }
14930             }
14931             enc = "o:" + flat.substring(0, flat.length-1);
14932         }else{
14933             enc = "s:" + v;
14934         }
14935         return escape(enc);        
14936     }
14937 });
14938
14939 /*
14940  * Based on:
14941  * Ext JS Library 1.1.1
14942  * Copyright(c) 2006-2007, Ext JS, LLC.
14943  *
14944  * Originally Released Under LGPL - original licence link has changed is not relivant.
14945  *
14946  * Fork - LGPL
14947  * <script type="text/javascript">
14948  */
14949 /**
14950  * @class Roo.state.Manager
14951  * This is the global state manager. By default all components that are "state aware" check this class
14952  * for state information if you don't pass them a custom state provider. In order for this class
14953  * to be useful, it must be initialized with a provider when your application initializes.
14954  <pre><code>
14955 // in your initialization function
14956 init : function(){
14957    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14958    ...
14959    // supposed you have a {@link Roo.BorderLayout}
14960    var layout = new Roo.BorderLayout(...);
14961    layout.restoreState();
14962    // or a {Roo.BasicDialog}
14963    var dialog = new Roo.BasicDialog(...);
14964    dialog.restoreState();
14965  </code></pre>
14966  * @singleton
14967  */
14968 Roo.state.Manager = function(){
14969     var provider = new Roo.state.Provider();
14970     
14971     return {
14972         /**
14973          * Configures the default state provider for your application
14974          * @param {Provider} stateProvider The state provider to set
14975          */
14976         setProvider : function(stateProvider){
14977             provider = stateProvider;
14978         },
14979         
14980         /**
14981          * Returns the current value for a key
14982          * @param {String} name The key name
14983          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14984          * @return {Mixed} The state data
14985          */
14986         get : function(key, defaultValue){
14987             return provider.get(key, defaultValue);
14988         },
14989         
14990         /**
14991          * Sets the value for a key
14992          * @param {String} name The key name
14993          * @param {Mixed} value The state data
14994          */
14995          set : function(key, value){
14996             provider.set(key, value);
14997         },
14998         
14999         /**
15000          * Clears a value from the state
15001          * @param {String} name The key name
15002          */
15003         clear : function(key){
15004             provider.clear(key);
15005         },
15006         
15007         /**
15008          * Gets the currently configured state provider
15009          * @return {Provider} The state provider
15010          */
15011         getProvider : function(){
15012             return provider;
15013         }
15014     };
15015 }();
15016 /*
15017  * Based on:
15018  * Ext JS Library 1.1.1
15019  * Copyright(c) 2006-2007, Ext JS, LLC.
15020  *
15021  * Originally Released Under LGPL - original licence link has changed is not relivant.
15022  *
15023  * Fork - LGPL
15024  * <script type="text/javascript">
15025  */
15026 /**
15027  * @class Roo.state.CookieProvider
15028  * @extends Roo.state.Provider
15029  * The default Provider implementation which saves state via cookies.
15030  * <br />Usage:
15031  <pre><code>
15032    var cp = new Roo.state.CookieProvider({
15033        path: "/cgi-bin/",
15034        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15035        domain: "roojs.com"
15036    })
15037    Roo.state.Manager.setProvider(cp);
15038  </code></pre>
15039  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15040  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15041  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15042  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15043  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15044  * domain the page is running on including the 'www' like 'www.roojs.com')
15045  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15046  * @constructor
15047  * Create a new CookieProvider
15048  * @param {Object} config The configuration object
15049  */
15050 Roo.state.CookieProvider = function(config){
15051     Roo.state.CookieProvider.superclass.constructor.call(this);
15052     this.path = "/";
15053     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15054     this.domain = null;
15055     this.secure = false;
15056     Roo.apply(this, config);
15057     this.state = this.readCookies();
15058 };
15059
15060 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15061     // private
15062     set : function(name, value){
15063         if(typeof value == "undefined" || value === null){
15064             this.clear(name);
15065             return;
15066         }
15067         this.setCookie(name, value);
15068         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15069     },
15070
15071     // private
15072     clear : function(name){
15073         this.clearCookie(name);
15074         Roo.state.CookieProvider.superclass.clear.call(this, name);
15075     },
15076
15077     // private
15078     readCookies : function(){
15079         var cookies = {};
15080         var c = document.cookie + ";";
15081         var re = /\s?(.*?)=(.*?);/g;
15082         var matches;
15083         while((matches = re.exec(c)) != null){
15084             var name = matches[1];
15085             var value = matches[2];
15086             if(name && name.substring(0,3) == "ys-"){
15087                 cookies[name.substr(3)] = this.decodeValue(value);
15088             }
15089         }
15090         return cookies;
15091     },
15092
15093     // private
15094     setCookie : function(name, value){
15095         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15096            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15097            ((this.path == null) ? "" : ("; path=" + this.path)) +
15098            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15099            ((this.secure == true) ? "; secure" : "");
15100     },
15101
15102     // private
15103     clearCookie : function(name){
15104         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15105            ((this.path == null) ? "" : ("; path=" + this.path)) +
15106            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15107            ((this.secure == true) ? "; secure" : "");
15108     }
15109 });/*
15110  * Based on:
15111  * Ext JS Library 1.1.1
15112  * Copyright(c) 2006-2007, Ext JS, LLC.
15113  *
15114  * Originally Released Under LGPL - original licence link has changed is not relivant.
15115  *
15116  * Fork - LGPL
15117  * <script type="text/javascript">
15118  */
15119  
15120
15121 /**
15122  * @class Roo.ComponentMgr
15123  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15124  * @singleton
15125  */
15126 Roo.ComponentMgr = function(){
15127     var all = new Roo.util.MixedCollection();
15128
15129     return {
15130         /**
15131          * Registers a component.
15132          * @param {Roo.Component} c The component
15133          */
15134         register : function(c){
15135             all.add(c);
15136         },
15137
15138         /**
15139          * Unregisters a component.
15140          * @param {Roo.Component} c The component
15141          */
15142         unregister : function(c){
15143             all.remove(c);
15144         },
15145
15146         /**
15147          * Returns a component by id
15148          * @param {String} id The component id
15149          */
15150         get : function(id){
15151             return all.get(id);
15152         },
15153
15154         /**
15155          * Registers a function that will be called when a specified component is added to ComponentMgr
15156          * @param {String} id The component id
15157          * @param {Funtction} fn The callback function
15158          * @param {Object} scope The scope of the callback
15159          */
15160         onAvailable : function(id, fn, scope){
15161             all.on("add", function(index, o){
15162                 if(o.id == id){
15163                     fn.call(scope || o, o);
15164                     all.un("add", fn, scope);
15165                 }
15166             });
15167         }
15168     };
15169 }();/*
15170  * Based on:
15171  * Ext JS Library 1.1.1
15172  * Copyright(c) 2006-2007, Ext JS, LLC.
15173  *
15174  * Originally Released Under LGPL - original licence link has changed is not relivant.
15175  *
15176  * Fork - LGPL
15177  * <script type="text/javascript">
15178  */
15179  
15180 /**
15181  * @class Roo.Component
15182  * @extends Roo.util.Observable
15183  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15184  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15185  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15186  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15187  * All visual components (widgets) that require rendering into a layout should subclass Component.
15188  * @constructor
15189  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15190  * 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
15191  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15192  */
15193 Roo.Component = function(config){
15194     config = config || {};
15195     if(config.tagName || config.dom || typeof config == "string"){ // element object
15196         config = {el: config, id: config.id || config};
15197     }
15198     this.initialConfig = config;
15199
15200     Roo.apply(this, config);
15201     this.addEvents({
15202         /**
15203          * @event disable
15204          * Fires after the component is disabled.
15205              * @param {Roo.Component} this
15206              */
15207         disable : true,
15208         /**
15209          * @event enable
15210          * Fires after the component is enabled.
15211              * @param {Roo.Component} this
15212              */
15213         enable : true,
15214         /**
15215          * @event beforeshow
15216          * Fires before the component is shown.  Return false to stop the show.
15217              * @param {Roo.Component} this
15218              */
15219         beforeshow : true,
15220         /**
15221          * @event show
15222          * Fires after the component is shown.
15223              * @param {Roo.Component} this
15224              */
15225         show : true,
15226         /**
15227          * @event beforehide
15228          * Fires before the component is hidden. Return false to stop the hide.
15229              * @param {Roo.Component} this
15230              */
15231         beforehide : true,
15232         /**
15233          * @event hide
15234          * Fires after the component is hidden.
15235              * @param {Roo.Component} this
15236              */
15237         hide : true,
15238         /**
15239          * @event beforerender
15240          * Fires before the component is rendered. Return false to stop the render.
15241              * @param {Roo.Component} this
15242              */
15243         beforerender : true,
15244         /**
15245          * @event render
15246          * Fires after the component is rendered.
15247              * @param {Roo.Component} this
15248              */
15249         render : true,
15250         /**
15251          * @event beforedestroy
15252          * Fires before the component is destroyed. Return false to stop the destroy.
15253              * @param {Roo.Component} this
15254              */
15255         beforedestroy : true,
15256         /**
15257          * @event destroy
15258          * Fires after the component is destroyed.
15259              * @param {Roo.Component} this
15260              */
15261         destroy : true
15262     });
15263     if(!this.id){
15264         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15265     }
15266     Roo.ComponentMgr.register(this);
15267     Roo.Component.superclass.constructor.call(this);
15268     this.initComponent();
15269     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15270         this.render(this.renderTo);
15271         delete this.renderTo;
15272     }
15273 };
15274
15275 /** @private */
15276 Roo.Component.AUTO_ID = 1000;
15277
15278 Roo.extend(Roo.Component, Roo.util.Observable, {
15279     /**
15280      * @scope Roo.Component.prototype
15281      * @type {Boolean}
15282      * true if this component is hidden. Read-only.
15283      */
15284     hidden : false,
15285     /**
15286      * @type {Boolean}
15287      * true if this component is disabled. Read-only.
15288      */
15289     disabled : false,
15290     /**
15291      * @type {Boolean}
15292      * true if this component has been rendered. Read-only.
15293      */
15294     rendered : false,
15295     
15296     /** @cfg {String} disableClass
15297      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15298      */
15299     disabledClass : "x-item-disabled",
15300         /** @cfg {Boolean} allowDomMove
15301          * Whether the component can move the Dom node when rendering (defaults to true).
15302          */
15303     allowDomMove : true,
15304     /** @cfg {String} hideMode (display|visibility)
15305      * How this component should hidden. Supported values are
15306      * "visibility" (css visibility), "offsets" (negative offset position) and
15307      * "display" (css display) - defaults to "display".
15308      */
15309     hideMode: 'display',
15310
15311     /** @private */
15312     ctype : "Roo.Component",
15313
15314     /**
15315      * @cfg {String} actionMode 
15316      * which property holds the element that used for  hide() / show() / disable() / enable()
15317      * default is 'el' 
15318      */
15319     actionMode : "el",
15320
15321     /** @private */
15322     getActionEl : function(){
15323         return this[this.actionMode];
15324     },
15325
15326     initComponent : Roo.emptyFn,
15327     /**
15328      * If this is a lazy rendering component, render it to its container element.
15329      * @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.
15330      */
15331     render : function(container, position){
15332         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15333             if(!container && this.el){
15334                 this.el = Roo.get(this.el);
15335                 container = this.el.dom.parentNode;
15336                 this.allowDomMove = false;
15337             }
15338             this.container = Roo.get(container);
15339             this.rendered = true;
15340             if(position !== undefined){
15341                 if(typeof position == 'number'){
15342                     position = this.container.dom.childNodes[position];
15343                 }else{
15344                     position = Roo.getDom(position);
15345                 }
15346             }
15347             this.onRender(this.container, position || null);
15348             if(this.cls){
15349                 this.el.addClass(this.cls);
15350                 delete this.cls;
15351             }
15352             if(this.style){
15353                 this.el.applyStyles(this.style);
15354                 delete this.style;
15355             }
15356             this.fireEvent("render", this);
15357             this.afterRender(this.container);
15358             if(this.hidden){
15359                 this.hide();
15360             }
15361             if(this.disabled){
15362                 this.disable();
15363             }
15364         }
15365         return this;
15366     },
15367
15368     /** @private */
15369     // default function is not really useful
15370     onRender : function(ct, position){
15371         if(this.el){
15372             this.el = Roo.get(this.el);
15373             if(this.allowDomMove !== false){
15374                 ct.dom.insertBefore(this.el.dom, position);
15375             }
15376         }
15377     },
15378
15379     /** @private */
15380     getAutoCreate : function(){
15381         var cfg = typeof this.autoCreate == "object" ?
15382                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15383         if(this.id && !cfg.id){
15384             cfg.id = this.id;
15385         }
15386         return cfg;
15387     },
15388
15389     /** @private */
15390     afterRender : Roo.emptyFn,
15391
15392     /**
15393      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15394      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15395      */
15396     destroy : function(){
15397         if(this.fireEvent("beforedestroy", this) !== false){
15398             this.purgeListeners();
15399             this.beforeDestroy();
15400             if(this.rendered){
15401                 this.el.removeAllListeners();
15402                 this.el.remove();
15403                 if(this.actionMode == "container"){
15404                     this.container.remove();
15405                 }
15406             }
15407             this.onDestroy();
15408             Roo.ComponentMgr.unregister(this);
15409             this.fireEvent("destroy", this);
15410         }
15411     },
15412
15413         /** @private */
15414     beforeDestroy : function(){
15415
15416     },
15417
15418         /** @private */
15419         onDestroy : function(){
15420
15421     },
15422
15423     /**
15424      * Returns the underlying {@link Roo.Element}.
15425      * @return {Roo.Element} The element
15426      */
15427     getEl : function(){
15428         return this.el;
15429     },
15430
15431     /**
15432      * Returns the id of this component.
15433      * @return {String}
15434      */
15435     getId : function(){
15436         return this.id;
15437     },
15438
15439     /**
15440      * Try to focus this component.
15441      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15442      * @return {Roo.Component} this
15443      */
15444     focus : function(selectText){
15445         if(this.rendered){
15446             this.el.focus();
15447             if(selectText === true){
15448                 this.el.dom.select();
15449             }
15450         }
15451         return this;
15452     },
15453
15454     /** @private */
15455     blur : function(){
15456         if(this.rendered){
15457             this.el.blur();
15458         }
15459         return this;
15460     },
15461
15462     /**
15463      * Disable this component.
15464      * @return {Roo.Component} this
15465      */
15466     disable : function(){
15467         if(this.rendered){
15468             this.onDisable();
15469         }
15470         this.disabled = true;
15471         this.fireEvent("disable", this);
15472         return this;
15473     },
15474
15475         // private
15476     onDisable : function(){
15477         this.getActionEl().addClass(this.disabledClass);
15478         this.el.dom.disabled = true;
15479     },
15480
15481     /**
15482      * Enable this component.
15483      * @return {Roo.Component} this
15484      */
15485     enable : function(){
15486         if(this.rendered){
15487             this.onEnable();
15488         }
15489         this.disabled = false;
15490         this.fireEvent("enable", this);
15491         return this;
15492     },
15493
15494         // private
15495     onEnable : function(){
15496         this.getActionEl().removeClass(this.disabledClass);
15497         this.el.dom.disabled = false;
15498     },
15499
15500     /**
15501      * Convenience function for setting disabled/enabled by boolean.
15502      * @param {Boolean} disabled
15503      */
15504     setDisabled : function(disabled){
15505         this[disabled ? "disable" : "enable"]();
15506     },
15507
15508     /**
15509      * Show this component.
15510      * @return {Roo.Component} this
15511      */
15512     show: function(){
15513         if(this.fireEvent("beforeshow", this) !== false){
15514             this.hidden = false;
15515             if(this.rendered){
15516                 this.onShow();
15517             }
15518             this.fireEvent("show", this);
15519         }
15520         return this;
15521     },
15522
15523     // private
15524     onShow : function(){
15525         var ae = this.getActionEl();
15526         if(this.hideMode == 'visibility'){
15527             ae.dom.style.visibility = "visible";
15528         }else if(this.hideMode == 'offsets'){
15529             ae.removeClass('x-hidden');
15530         }else{
15531             ae.dom.style.display = "";
15532         }
15533     },
15534
15535     /**
15536      * Hide this component.
15537      * @return {Roo.Component} this
15538      */
15539     hide: function(){
15540         if(this.fireEvent("beforehide", this) !== false){
15541             this.hidden = true;
15542             if(this.rendered){
15543                 this.onHide();
15544             }
15545             this.fireEvent("hide", this);
15546         }
15547         return this;
15548     },
15549
15550     // private
15551     onHide : function(){
15552         var ae = this.getActionEl();
15553         if(this.hideMode == 'visibility'){
15554             ae.dom.style.visibility = "hidden";
15555         }else if(this.hideMode == 'offsets'){
15556             ae.addClass('x-hidden');
15557         }else{
15558             ae.dom.style.display = "none";
15559         }
15560     },
15561
15562     /**
15563      * Convenience function to hide or show this component by boolean.
15564      * @param {Boolean} visible True to show, false to hide
15565      * @return {Roo.Component} this
15566      */
15567     setVisible: function(visible){
15568         if(visible) {
15569             this.show();
15570         }else{
15571             this.hide();
15572         }
15573         return this;
15574     },
15575
15576     /**
15577      * Returns true if this component is visible.
15578      */
15579     isVisible : function(){
15580         return this.getActionEl().isVisible();
15581     },
15582
15583     cloneConfig : function(overrides){
15584         overrides = overrides || {};
15585         var id = overrides.id || Roo.id();
15586         var cfg = Roo.applyIf(overrides, this.initialConfig);
15587         cfg.id = id; // prevent dup id
15588         return new this.constructor(cfg);
15589     }
15590 });/*
15591  * Based on:
15592  * Ext JS Library 1.1.1
15593  * Copyright(c) 2006-2007, Ext JS, LLC.
15594  *
15595  * Originally Released Under LGPL - original licence link has changed is not relivant.
15596  *
15597  * Fork - LGPL
15598  * <script type="text/javascript">
15599  */
15600
15601 /**
15602  * @class Roo.BoxComponent
15603  * @extends Roo.Component
15604  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15605  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15606  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15607  * layout containers.
15608  * @constructor
15609  * @param {Roo.Element/String/Object} config The configuration options.
15610  */
15611 Roo.BoxComponent = function(config){
15612     Roo.Component.call(this, config);
15613     this.addEvents({
15614         /**
15615          * @event resize
15616          * Fires after the component is resized.
15617              * @param {Roo.Component} this
15618              * @param {Number} adjWidth The box-adjusted width that was set
15619              * @param {Number} adjHeight The box-adjusted height that was set
15620              * @param {Number} rawWidth The width that was originally specified
15621              * @param {Number} rawHeight The height that was originally specified
15622              */
15623         resize : true,
15624         /**
15625          * @event move
15626          * Fires after the component is moved.
15627              * @param {Roo.Component} this
15628              * @param {Number} x The new x position
15629              * @param {Number} y The new y position
15630              */
15631         move : true
15632     });
15633 };
15634
15635 Roo.extend(Roo.BoxComponent, Roo.Component, {
15636     // private, set in afterRender to signify that the component has been rendered
15637     boxReady : false,
15638     // private, used to defer height settings to subclasses
15639     deferHeight: false,
15640     /** @cfg {Number} width
15641      * width (optional) size of component
15642      */
15643      /** @cfg {Number} height
15644      * height (optional) size of component
15645      */
15646      
15647     /**
15648      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15649      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15650      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15651      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15652      * @return {Roo.BoxComponent} this
15653      */
15654     setSize : function(w, h){
15655         // support for standard size objects
15656         if(typeof w == 'object'){
15657             h = w.height;
15658             w = w.width;
15659         }
15660         // not rendered
15661         if(!this.boxReady){
15662             this.width = w;
15663             this.height = h;
15664             return this;
15665         }
15666
15667         // prevent recalcs when not needed
15668         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15669             return this;
15670         }
15671         this.lastSize = {width: w, height: h};
15672
15673         var adj = this.adjustSize(w, h);
15674         var aw = adj.width, ah = adj.height;
15675         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15676             var rz = this.getResizeEl();
15677             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15678                 rz.setSize(aw, ah);
15679             }else if(!this.deferHeight && ah !== undefined){
15680                 rz.setHeight(ah);
15681             }else if(aw !== undefined){
15682                 rz.setWidth(aw);
15683             }
15684             this.onResize(aw, ah, w, h);
15685             this.fireEvent('resize', this, aw, ah, w, h);
15686         }
15687         return this;
15688     },
15689
15690     /**
15691      * Gets the current size of the component's underlying element.
15692      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15693      */
15694     getSize : function(){
15695         return this.el.getSize();
15696     },
15697
15698     /**
15699      * Gets the current XY position of the component's underlying element.
15700      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15701      * @return {Array} The XY position of the element (e.g., [100, 200])
15702      */
15703     getPosition : function(local){
15704         if(local === true){
15705             return [this.el.getLeft(true), this.el.getTop(true)];
15706         }
15707         return this.xy || this.el.getXY();
15708     },
15709
15710     /**
15711      * Gets the current box measurements of the component's underlying element.
15712      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15713      * @returns {Object} box An object in the format {x, y, width, height}
15714      */
15715     getBox : function(local){
15716         var s = this.el.getSize();
15717         if(local){
15718             s.x = this.el.getLeft(true);
15719             s.y = this.el.getTop(true);
15720         }else{
15721             var xy = this.xy || this.el.getXY();
15722             s.x = xy[0];
15723             s.y = xy[1];
15724         }
15725         return s;
15726     },
15727
15728     /**
15729      * Sets the current box measurements of the component's underlying element.
15730      * @param {Object} box An object in the format {x, y, width, height}
15731      * @returns {Roo.BoxComponent} this
15732      */
15733     updateBox : function(box){
15734         this.setSize(box.width, box.height);
15735         this.setPagePosition(box.x, box.y);
15736         return this;
15737     },
15738
15739     // protected
15740     getResizeEl : function(){
15741         return this.resizeEl || this.el;
15742     },
15743
15744     // protected
15745     getPositionEl : function(){
15746         return this.positionEl || this.el;
15747     },
15748
15749     /**
15750      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15751      * This method fires the move event.
15752      * @param {Number} left The new left
15753      * @param {Number} top The new top
15754      * @returns {Roo.BoxComponent} this
15755      */
15756     setPosition : function(x, y){
15757         this.x = x;
15758         this.y = y;
15759         if(!this.boxReady){
15760             return this;
15761         }
15762         var adj = this.adjustPosition(x, y);
15763         var ax = adj.x, ay = adj.y;
15764
15765         var el = this.getPositionEl();
15766         if(ax !== undefined || ay !== undefined){
15767             if(ax !== undefined && ay !== undefined){
15768                 el.setLeftTop(ax, ay);
15769             }else if(ax !== undefined){
15770                 el.setLeft(ax);
15771             }else if(ay !== undefined){
15772                 el.setTop(ay);
15773             }
15774             this.onPosition(ax, ay);
15775             this.fireEvent('move', this, ax, ay);
15776         }
15777         return this;
15778     },
15779
15780     /**
15781      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15782      * This method fires the move event.
15783      * @param {Number} x The new x position
15784      * @param {Number} y The new y position
15785      * @returns {Roo.BoxComponent} this
15786      */
15787     setPagePosition : function(x, y){
15788         this.pageX = x;
15789         this.pageY = y;
15790         if(!this.boxReady){
15791             return;
15792         }
15793         if(x === undefined || y === undefined){ // cannot translate undefined points
15794             return;
15795         }
15796         var p = this.el.translatePoints(x, y);
15797         this.setPosition(p.left, p.top);
15798         return this;
15799     },
15800
15801     // private
15802     onRender : function(ct, position){
15803         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15804         if(this.resizeEl){
15805             this.resizeEl = Roo.get(this.resizeEl);
15806         }
15807         if(this.positionEl){
15808             this.positionEl = Roo.get(this.positionEl);
15809         }
15810     },
15811
15812     // private
15813     afterRender : function(){
15814         Roo.BoxComponent.superclass.afterRender.call(this);
15815         this.boxReady = true;
15816         this.setSize(this.width, this.height);
15817         if(this.x || this.y){
15818             this.setPosition(this.x, this.y);
15819         }
15820         if(this.pageX || this.pageY){
15821             this.setPagePosition(this.pageX, this.pageY);
15822         }
15823     },
15824
15825     /**
15826      * Force the component's size to recalculate based on the underlying element's current height and width.
15827      * @returns {Roo.BoxComponent} this
15828      */
15829     syncSize : function(){
15830         delete this.lastSize;
15831         this.setSize(this.el.getWidth(), this.el.getHeight());
15832         return this;
15833     },
15834
15835     /**
15836      * Called after the component is resized, this method is empty by default but can be implemented by any
15837      * subclass that needs to perform custom logic after a resize occurs.
15838      * @param {Number} adjWidth The box-adjusted width that was set
15839      * @param {Number} adjHeight The box-adjusted height that was set
15840      * @param {Number} rawWidth The width that was originally specified
15841      * @param {Number} rawHeight The height that was originally specified
15842      */
15843     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15844
15845     },
15846
15847     /**
15848      * Called after the component is moved, this method is empty by default but can be implemented by any
15849      * subclass that needs to perform custom logic after a move occurs.
15850      * @param {Number} x The new x position
15851      * @param {Number} y The new y position
15852      */
15853     onPosition : function(x, y){
15854
15855     },
15856
15857     // private
15858     adjustSize : function(w, h){
15859         if(this.autoWidth){
15860             w = 'auto';
15861         }
15862         if(this.autoHeight){
15863             h = 'auto';
15864         }
15865         return {width : w, height: h};
15866     },
15867
15868     // private
15869     adjustPosition : function(x, y){
15870         return {x : x, y: y};
15871     }
15872 });/*
15873  * Original code for Roojs - LGPL
15874  * <script type="text/javascript">
15875  */
15876  
15877 /**
15878  * @class Roo.XComponent
15879  * A delayed Element creator...
15880  * Or a way to group chunks of interface together.
15881  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15882  *  used in conjunction with XComponent.build() it will create an instance of each element,
15883  *  then call addxtype() to build the User interface.
15884  * 
15885  * Mypart.xyx = new Roo.XComponent({
15886
15887     parent : 'Mypart.xyz', // empty == document.element.!!
15888     order : '001',
15889     name : 'xxxx'
15890     region : 'xxxx'
15891     disabled : function() {} 
15892      
15893     tree : function() { // return an tree of xtype declared components
15894         var MODULE = this;
15895         return 
15896         {
15897             xtype : 'NestedLayoutPanel',
15898             // technicall
15899         }
15900      ]
15901  *})
15902  *
15903  *
15904  * It can be used to build a big heiracy, with parent etc.
15905  * or you can just use this to render a single compoent to a dom element
15906  * MYPART.render(Roo.Element | String(id) | dom_element )
15907  *
15908  *
15909  * Usage patterns.
15910  *
15911  * Classic Roo
15912  *
15913  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15914  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15915  *
15916  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15917  *
15918  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15919  * - if mulitple topModules exist, the last one is defined as the top module.
15920  *
15921  * Embeded Roo
15922  * 
15923  * When the top level or multiple modules are to embedded into a existing HTML page,
15924  * the parent element can container '#id' of the element where the module will be drawn.
15925  *
15926  * Bootstrap Roo
15927  *
15928  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15929  * it relies more on a include mechanism, where sub modules are included into an outer page.
15930  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15931  * 
15932  * Bootstrap Roo Included elements
15933  *
15934  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15935  * hence confusing the component builder as it thinks there are multiple top level elements. 
15936  *
15937  * 
15938  * 
15939  * @extends Roo.util.Observable
15940  * @constructor
15941  * @param cfg {Object} configuration of component
15942  * 
15943  */
15944 Roo.XComponent = function(cfg) {
15945     Roo.apply(this, cfg);
15946     this.addEvents({ 
15947         /**
15948              * @event built
15949              * Fires when this the componnt is built
15950              * @param {Roo.XComponent} c the component
15951              */
15952         'built' : true
15953         
15954     });
15955     this.region = this.region || 'center'; // default..
15956     Roo.XComponent.register(this);
15957     this.modules = false;
15958     this.el = false; // where the layout goes..
15959     
15960     
15961 }
15962 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15963     /**
15964      * @property el
15965      * The created element (with Roo.factory())
15966      * @type {Roo.Layout}
15967      */
15968     el  : false,
15969     
15970     /**
15971      * @property el
15972      * for BC  - use el in new code
15973      * @type {Roo.Layout}
15974      */
15975     panel : false,
15976     
15977     /**
15978      * @property layout
15979      * for BC  - use el in new code
15980      * @type {Roo.Layout}
15981      */
15982     layout : false,
15983     
15984      /**
15985      * @cfg {Function|boolean} disabled
15986      * If this module is disabled by some rule, return true from the funtion
15987      */
15988     disabled : false,
15989     
15990     /**
15991      * @cfg {String} parent 
15992      * Name of parent element which it get xtype added to..
15993      */
15994     parent: false,
15995     
15996     /**
15997      * @cfg {String} order
15998      * Used to set the order in which elements are created (usefull for multiple tabs)
15999      */
16000     
16001     order : false,
16002     /**
16003      * @cfg {String} name
16004      * String to display while loading.
16005      */
16006     name : false,
16007     /**
16008      * @cfg {String} region
16009      * Region to render component to (defaults to center)
16010      */
16011     region : 'center',
16012     
16013     /**
16014      * @cfg {Array} items
16015      * A single item array - the first element is the root of the tree..
16016      * It's done this way to stay compatible with the Xtype system...
16017      */
16018     items : false,
16019     
16020     /**
16021      * @property _tree
16022      * The method that retuns the tree of parts that make up this compoennt 
16023      * @type {function}
16024      */
16025     _tree  : false,
16026     
16027      /**
16028      * render
16029      * render element to dom or tree
16030      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16031      */
16032     
16033     render : function(el)
16034     {
16035         
16036         el = el || false;
16037         var hp = this.parent ? 1 : 0;
16038         Roo.debug &&  Roo.log(this);
16039         
16040         var tree = this._tree ? this._tree() : this.tree();
16041
16042         
16043         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16044             // if parent is a '#.....' string, then let's use that..
16045             var ename = this.parent.substr(1);
16046             this.parent = false;
16047             Roo.debug && Roo.log(ename);
16048             switch (ename) {
16049                 case 'bootstrap-body':
16050                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16051                         // this is the BorderLayout standard?
16052                        this.parent = { el : true };
16053                        break;
16054                     }
16055                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16056                         // need to insert stuff...
16057                         this.parent =  {
16058                              el : new Roo.bootstrap.layout.Border({
16059                                  el : document.body, 
16060                      
16061                                  center: {
16062                                     titlebar: false,
16063                                     autoScroll:false,
16064                                     closeOnTab: true,
16065                                     tabPosition: 'top',
16066                                       //resizeTabs: true,
16067                                     alwaysShowTabs: true,
16068                                     hideTabs: false
16069                                      //minTabWidth: 140
16070                                  }
16071                              })
16072                         
16073                          };
16074                          break;
16075                     }
16076                          
16077                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16078                         this.parent = { el :  new  Roo.bootstrap.Body() };
16079                         Roo.debug && Roo.log("setting el to doc body");
16080                          
16081                     } else {
16082                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16083                     }
16084                     break;
16085                 case 'bootstrap':
16086                     this.parent = { el : true};
16087                     // fall through
16088                 default:
16089                     el = Roo.get(ename);
16090                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16091                         this.parent = { el : true};
16092                     }
16093                     
16094                     break;
16095             }
16096                 
16097             
16098             if (!el && !this.parent) {
16099                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16100                 return;
16101             }
16102         }
16103         
16104         Roo.debug && Roo.log("EL:");
16105         Roo.debug && Roo.log(el);
16106         Roo.debug && Roo.log("this.parent.el:");
16107         Roo.debug && Roo.log(this.parent.el);
16108         
16109
16110         // altertive root elements ??? - we need a better way to indicate these.
16111         var is_alt = Roo.XComponent.is_alt ||
16112                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16113                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16114                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16115         
16116         
16117         
16118         if (!this.parent && is_alt) {
16119             //el = Roo.get(document.body);
16120             this.parent = { el : true };
16121         }
16122             
16123             
16124         
16125         if (!this.parent) {
16126             
16127             Roo.debug && Roo.log("no parent - creating one");
16128             
16129             el = el ? Roo.get(el) : false;      
16130             
16131             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16132                 
16133                 this.parent =  {
16134                     el : new Roo.bootstrap.layout.Border({
16135                         el: el || document.body,
16136                     
16137                         center: {
16138                             titlebar: false,
16139                             autoScroll:false,
16140                             closeOnTab: true,
16141                             tabPosition: 'top',
16142                              //resizeTabs: true,
16143                             alwaysShowTabs: false,
16144                             hideTabs: true,
16145                             minTabWidth: 140,
16146                             overflow: 'visible'
16147                          }
16148                      })
16149                 };
16150             } else {
16151             
16152                 // it's a top level one..
16153                 this.parent =  {
16154                     el : new Roo.BorderLayout(el || document.body, {
16155                         center: {
16156                             titlebar: false,
16157                             autoScroll:false,
16158                             closeOnTab: true,
16159                             tabPosition: 'top',
16160                              //resizeTabs: true,
16161                             alwaysShowTabs: el && hp? false :  true,
16162                             hideTabs: el || !hp ? true :  false,
16163                             minTabWidth: 140
16164                          }
16165                     })
16166                 };
16167             }
16168         }
16169         
16170         if (!this.parent.el) {
16171                 // probably an old style ctor, which has been disabled.
16172                 return;
16173
16174         }
16175                 // The 'tree' method is  '_tree now' 
16176             
16177         tree.region = tree.region || this.region;
16178         var is_body = false;
16179         if (this.parent.el === true) {
16180             // bootstrap... - body..
16181             if (el) {
16182                 tree.el = el;
16183             }
16184             this.parent.el = Roo.factory(tree);
16185             is_body = true;
16186         }
16187         
16188         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16189         this.fireEvent('built', this);
16190         
16191         this.panel = this.el;
16192         this.layout = this.panel.layout;
16193         this.parentLayout = this.parent.layout  || false;  
16194          
16195     }
16196     
16197 });
16198
16199 Roo.apply(Roo.XComponent, {
16200     /**
16201      * @property  hideProgress
16202      * true to disable the building progress bar.. usefull on single page renders.
16203      * @type Boolean
16204      */
16205     hideProgress : false,
16206     /**
16207      * @property  buildCompleted
16208      * True when the builder has completed building the interface.
16209      * @type Boolean
16210      */
16211     buildCompleted : false,
16212      
16213     /**
16214      * @property  topModule
16215      * the upper most module - uses document.element as it's constructor.
16216      * @type Object
16217      */
16218      
16219     topModule  : false,
16220       
16221     /**
16222      * @property  modules
16223      * array of modules to be created by registration system.
16224      * @type {Array} of Roo.XComponent
16225      */
16226     
16227     modules : [],
16228     /**
16229      * @property  elmodules
16230      * array of modules to be created by which use #ID 
16231      * @type {Array} of Roo.XComponent
16232      */
16233      
16234     elmodules : [],
16235
16236      /**
16237      * @property  is_alt
16238      * Is an alternative Root - normally used by bootstrap or other systems,
16239      *    where the top element in the tree can wrap 'body' 
16240      * @type {boolean}  (default false)
16241      */
16242      
16243     is_alt : false,
16244     /**
16245      * @property  build_from_html
16246      * Build elements from html - used by bootstrap HTML stuff 
16247      *    - this is cleared after build is completed
16248      * @type {boolean}    (default false)
16249      */
16250      
16251     build_from_html : false,
16252     /**
16253      * Register components to be built later.
16254      *
16255      * This solves the following issues
16256      * - Building is not done on page load, but after an authentication process has occured.
16257      * - Interface elements are registered on page load
16258      * - Parent Interface elements may not be loaded before child, so this handles that..
16259      * 
16260      *
16261      * example:
16262      * 
16263      * MyApp.register({
16264           order : '000001',
16265           module : 'Pman.Tab.projectMgr',
16266           region : 'center',
16267           parent : 'Pman.layout',
16268           disabled : false,  // or use a function..
16269         })
16270      
16271      * * @param {Object} details about module
16272      */
16273     register : function(obj) {
16274                 
16275         Roo.XComponent.event.fireEvent('register', obj);
16276         switch(typeof(obj.disabled) ) {
16277                 
16278             case 'undefined':
16279                 break;
16280             
16281             case 'function':
16282                 if ( obj.disabled() ) {
16283                         return;
16284                 }
16285                 break;
16286             
16287             default:
16288                 if (obj.disabled) {
16289                         return;
16290                 }
16291                 break;
16292         }
16293                 
16294         this.modules.push(obj);
16295          
16296     },
16297     /**
16298      * convert a string to an object..
16299      * eg. 'AAA.BBB' -> finds AAA.BBB
16300
16301      */
16302     
16303     toObject : function(str)
16304     {
16305         if (!str || typeof(str) == 'object') {
16306             return str;
16307         }
16308         if (str.substring(0,1) == '#') {
16309             return str;
16310         }
16311
16312         var ar = str.split('.');
16313         var rt, o;
16314         rt = ar.shift();
16315             /** eval:var:o */
16316         try {
16317             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16318         } catch (e) {
16319             throw "Module not found : " + str;
16320         }
16321         
16322         if (o === false) {
16323             throw "Module not found : " + str;
16324         }
16325         Roo.each(ar, function(e) {
16326             if (typeof(o[e]) == 'undefined') {
16327                 throw "Module not found : " + str;
16328             }
16329             o = o[e];
16330         });
16331         
16332         return o;
16333         
16334     },
16335     
16336     
16337     /**
16338      * move modules into their correct place in the tree..
16339      * 
16340      */
16341     preBuild : function ()
16342     {
16343         var _t = this;
16344         Roo.each(this.modules , function (obj)
16345         {
16346             Roo.XComponent.event.fireEvent('beforebuild', obj);
16347             
16348             var opar = obj.parent;
16349             try { 
16350                 obj.parent = this.toObject(opar);
16351             } catch(e) {
16352                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16353                 return;
16354             }
16355             
16356             if (!obj.parent) {
16357                 Roo.debug && Roo.log("GOT top level module");
16358                 Roo.debug && Roo.log(obj);
16359                 obj.modules = new Roo.util.MixedCollection(false, 
16360                     function(o) { return o.order + '' }
16361                 );
16362                 this.topModule = obj;
16363                 return;
16364             }
16365                         // parent is a string (usually a dom element name..)
16366             if (typeof(obj.parent) == 'string') {
16367                 this.elmodules.push(obj);
16368                 return;
16369             }
16370             if (obj.parent.constructor != Roo.XComponent) {
16371                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16372             }
16373             if (!obj.parent.modules) {
16374                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16375                     function(o) { return o.order + '' }
16376                 );
16377             }
16378             if (obj.parent.disabled) {
16379                 obj.disabled = true;
16380             }
16381             obj.parent.modules.add(obj);
16382         }, this);
16383     },
16384     
16385      /**
16386      * make a list of modules to build.
16387      * @return {Array} list of modules. 
16388      */ 
16389     
16390     buildOrder : function()
16391     {
16392         var _this = this;
16393         var cmp = function(a,b) {   
16394             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16395         };
16396         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16397             throw "No top level modules to build";
16398         }
16399         
16400         // make a flat list in order of modules to build.
16401         var mods = this.topModule ? [ this.topModule ] : [];
16402                 
16403         
16404         // elmodules (is a list of DOM based modules )
16405         Roo.each(this.elmodules, function(e) {
16406             mods.push(e);
16407             if (!this.topModule &&
16408                 typeof(e.parent) == 'string' &&
16409                 e.parent.substring(0,1) == '#' &&
16410                 Roo.get(e.parent.substr(1))
16411                ) {
16412                 
16413                 _this.topModule = e;
16414             }
16415             
16416         });
16417
16418         
16419         // add modules to their parents..
16420         var addMod = function(m) {
16421             Roo.debug && Roo.log("build Order: add: " + m.name);
16422                 
16423             mods.push(m);
16424             if (m.modules && !m.disabled) {
16425                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16426                 m.modules.keySort('ASC',  cmp );
16427                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16428     
16429                 m.modules.each(addMod);
16430             } else {
16431                 Roo.debug && Roo.log("build Order: no child modules");
16432             }
16433             // not sure if this is used any more..
16434             if (m.finalize) {
16435                 m.finalize.name = m.name + " (clean up) ";
16436                 mods.push(m.finalize);
16437             }
16438             
16439         }
16440         if (this.topModule && this.topModule.modules) { 
16441             this.topModule.modules.keySort('ASC',  cmp );
16442             this.topModule.modules.each(addMod);
16443         } 
16444         return mods;
16445     },
16446     
16447      /**
16448      * Build the registered modules.
16449      * @param {Object} parent element.
16450      * @param {Function} optional method to call after module has been added.
16451      * 
16452      */ 
16453    
16454     build : function(opts) 
16455     {
16456         
16457         if (typeof(opts) != 'undefined') {
16458             Roo.apply(this,opts);
16459         }
16460         
16461         this.preBuild();
16462         var mods = this.buildOrder();
16463       
16464         //this.allmods = mods;
16465         //Roo.debug && Roo.log(mods);
16466         //return;
16467         if (!mods.length) { // should not happen
16468             throw "NO modules!!!";
16469         }
16470         
16471         
16472         var msg = "Building Interface...";
16473         // flash it up as modal - so we store the mask!?
16474         if (!this.hideProgress && Roo.MessageBox) {
16475             Roo.MessageBox.show({ title: 'loading' });
16476             Roo.MessageBox.show({
16477                title: "Please wait...",
16478                msg: msg,
16479                width:450,
16480                progress:true,
16481                closable:false,
16482                modal: false
16483               
16484             });
16485         }
16486         var total = mods.length;
16487         
16488         var _this = this;
16489         var progressRun = function() {
16490             if (!mods.length) {
16491                 Roo.debug && Roo.log('hide?');
16492                 if (!this.hideProgress && Roo.MessageBox) {
16493                     Roo.MessageBox.hide();
16494                 }
16495                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16496                 
16497                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16498                 
16499                 // THE END...
16500                 return false;   
16501             }
16502             
16503             var m = mods.shift();
16504             
16505             
16506             Roo.debug && Roo.log(m);
16507             // not sure if this is supported any more.. - modules that are are just function
16508             if (typeof(m) == 'function') { 
16509                 m.call(this);
16510                 return progressRun.defer(10, _this);
16511             } 
16512             
16513             
16514             msg = "Building Interface " + (total  - mods.length) + 
16515                     " of " + total + 
16516                     (m.name ? (' - ' + m.name) : '');
16517                         Roo.debug && Roo.log(msg);
16518             if (!this.hideProgress &&  Roo.MessageBox) { 
16519                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16520             }
16521             
16522          
16523             // is the module disabled?
16524             var disabled = (typeof(m.disabled) == 'function') ?
16525                 m.disabled.call(m.module.disabled) : m.disabled;    
16526             
16527             
16528             if (disabled) {
16529                 return progressRun(); // we do not update the display!
16530             }
16531             
16532             // now build 
16533             
16534                         
16535                         
16536             m.render();
16537             // it's 10 on top level, and 1 on others??? why...
16538             return progressRun.defer(10, _this);
16539              
16540         }
16541         progressRun.defer(1, _this);
16542      
16543         
16544         
16545     },
16546         
16547         
16548         /**
16549          * Event Object.
16550          *
16551          *
16552          */
16553         event: false, 
16554     /**
16555          * wrapper for event.on - aliased later..  
16556          * Typically use to register a event handler for register:
16557          *
16558          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16559          *
16560          */
16561     on : false
16562    
16563     
16564     
16565 });
16566
16567 Roo.XComponent.event = new Roo.util.Observable({
16568                 events : { 
16569                         /**
16570                          * @event register
16571                          * Fires when an Component is registered,
16572                          * set the disable property on the Component to stop registration.
16573                          * @param {Roo.XComponent} c the component being registerd.
16574                          * 
16575                          */
16576                         'register' : true,
16577             /**
16578                          * @event beforebuild
16579                          * Fires before each Component is built
16580                          * can be used to apply permissions.
16581                          * @param {Roo.XComponent} c the component being registerd.
16582                          * 
16583                          */
16584                         'beforebuild' : true,
16585                         /**
16586                          * @event buildcomplete
16587                          * Fires on the top level element when all elements have been built
16588                          * @param {Roo.XComponent} the top level component.
16589                          */
16590                         'buildcomplete' : true
16591                         
16592                 }
16593 });
16594
16595 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16596  //
16597  /**
16598  * marked - a markdown parser
16599  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16600  * https://github.com/chjj/marked
16601  */
16602
16603
16604 /**
16605  *
16606  * Roo.Markdown - is a very crude wrapper around marked..
16607  *
16608  * usage:
16609  * 
16610  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16611  * 
16612  * Note: move the sample code to the bottom of this
16613  * file before uncommenting it.
16614  *
16615  */
16616
16617 Roo.Markdown = {};
16618 Roo.Markdown.toHtml = function(text) {
16619     
16620     var c = new Roo.Markdown.marked.setOptions({
16621             renderer: new Roo.Markdown.marked.Renderer(),
16622             gfm: true,
16623             tables: true,
16624             breaks: false,
16625             pedantic: false,
16626             sanitize: false,
16627             smartLists: true,
16628             smartypants: false
16629           });
16630     // A FEW HACKS!!?
16631     
16632     text = text.replace(/\\\n/g,' ');
16633     return Roo.Markdown.marked(text);
16634 };
16635 //
16636 // converter
16637 //
16638 // Wraps all "globals" so that the only thing
16639 // exposed is makeHtml().
16640 //
16641 (function() {
16642     
16643     /**
16644      * Block-Level Grammar
16645      */
16646     
16647     var block = {
16648       newline: /^\n+/,
16649       code: /^( {4}[^\n]+\n*)+/,
16650       fences: noop,
16651       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16652       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16653       nptable: noop,
16654       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16655       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16656       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16657       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16658       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16659       table: noop,
16660       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16661       text: /^[^\n]+/
16662     };
16663     
16664     block.bullet = /(?:[*+-]|\d+\.)/;
16665     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16666     block.item = replace(block.item, 'gm')
16667       (/bull/g, block.bullet)
16668       ();
16669     
16670     block.list = replace(block.list)
16671       (/bull/g, block.bullet)
16672       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16673       ('def', '\\n+(?=' + block.def.source + ')')
16674       ();
16675     
16676     block.blockquote = replace(block.blockquote)
16677       ('def', block.def)
16678       ();
16679     
16680     block._tag = '(?!(?:'
16681       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16682       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16683       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16684     
16685     block.html = replace(block.html)
16686       ('comment', /<!--[\s\S]*?-->/)
16687       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16688       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16689       (/tag/g, block._tag)
16690       ();
16691     
16692     block.paragraph = replace(block.paragraph)
16693       ('hr', block.hr)
16694       ('heading', block.heading)
16695       ('lheading', block.lheading)
16696       ('blockquote', block.blockquote)
16697       ('tag', '<' + block._tag)
16698       ('def', block.def)
16699       ();
16700     
16701     /**
16702      * Normal Block Grammar
16703      */
16704     
16705     block.normal = merge({}, block);
16706     
16707     /**
16708      * GFM Block Grammar
16709      */
16710     
16711     block.gfm = merge({}, block.normal, {
16712       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16713       paragraph: /^/,
16714       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16715     });
16716     
16717     block.gfm.paragraph = replace(block.paragraph)
16718       ('(?!', '(?!'
16719         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16720         + block.list.source.replace('\\1', '\\3') + '|')
16721       ();
16722     
16723     /**
16724      * GFM + Tables Block Grammar
16725      */
16726     
16727     block.tables = merge({}, block.gfm, {
16728       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16729       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16730     });
16731     
16732     /**
16733      * Block Lexer
16734      */
16735     
16736     function Lexer(options) {
16737       this.tokens = [];
16738       this.tokens.links = {};
16739       this.options = options || marked.defaults;
16740       this.rules = block.normal;
16741     
16742       if (this.options.gfm) {
16743         if (this.options.tables) {
16744           this.rules = block.tables;
16745         } else {
16746           this.rules = block.gfm;
16747         }
16748       }
16749     }
16750     
16751     /**
16752      * Expose Block Rules
16753      */
16754     
16755     Lexer.rules = block;
16756     
16757     /**
16758      * Static Lex Method
16759      */
16760     
16761     Lexer.lex = function(src, options) {
16762       var lexer = new Lexer(options);
16763       return lexer.lex(src);
16764     };
16765     
16766     /**
16767      * Preprocessing
16768      */
16769     
16770     Lexer.prototype.lex = function(src) {
16771       src = src
16772         .replace(/\r\n|\r/g, '\n')
16773         .replace(/\t/g, '    ')
16774         .replace(/\u00a0/g, ' ')
16775         .replace(/\u2424/g, '\n');
16776     
16777       return this.token(src, true);
16778     };
16779     
16780     /**
16781      * Lexing
16782      */
16783     
16784     Lexer.prototype.token = function(src, top, bq) {
16785       var src = src.replace(/^ +$/gm, '')
16786         , next
16787         , loose
16788         , cap
16789         , bull
16790         , b
16791         , item
16792         , space
16793         , i
16794         , l;
16795     
16796       while (src) {
16797         // newline
16798         if (cap = this.rules.newline.exec(src)) {
16799           src = src.substring(cap[0].length);
16800           if (cap[0].length > 1) {
16801             this.tokens.push({
16802               type: 'space'
16803             });
16804           }
16805         }
16806     
16807         // code
16808         if (cap = this.rules.code.exec(src)) {
16809           src = src.substring(cap[0].length);
16810           cap = cap[0].replace(/^ {4}/gm, '');
16811           this.tokens.push({
16812             type: 'code',
16813             text: !this.options.pedantic
16814               ? cap.replace(/\n+$/, '')
16815               : cap
16816           });
16817           continue;
16818         }
16819     
16820         // fences (gfm)
16821         if (cap = this.rules.fences.exec(src)) {
16822           src = src.substring(cap[0].length);
16823           this.tokens.push({
16824             type: 'code',
16825             lang: cap[2],
16826             text: cap[3] || ''
16827           });
16828           continue;
16829         }
16830     
16831         // heading
16832         if (cap = this.rules.heading.exec(src)) {
16833           src = src.substring(cap[0].length);
16834           this.tokens.push({
16835             type: 'heading',
16836             depth: cap[1].length,
16837             text: cap[2]
16838           });
16839           continue;
16840         }
16841     
16842         // table no leading pipe (gfm)
16843         if (top && (cap = this.rules.nptable.exec(src))) {
16844           src = src.substring(cap[0].length);
16845     
16846           item = {
16847             type: 'table',
16848             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16849             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16850             cells: cap[3].replace(/\n$/, '').split('\n')
16851           };
16852     
16853           for (i = 0; i < item.align.length; i++) {
16854             if (/^ *-+: *$/.test(item.align[i])) {
16855               item.align[i] = 'right';
16856             } else if (/^ *:-+: *$/.test(item.align[i])) {
16857               item.align[i] = 'center';
16858             } else if (/^ *:-+ *$/.test(item.align[i])) {
16859               item.align[i] = 'left';
16860             } else {
16861               item.align[i] = null;
16862             }
16863           }
16864     
16865           for (i = 0; i < item.cells.length; i++) {
16866             item.cells[i] = item.cells[i].split(/ *\| */);
16867           }
16868     
16869           this.tokens.push(item);
16870     
16871           continue;
16872         }
16873     
16874         // lheading
16875         if (cap = this.rules.lheading.exec(src)) {
16876           src = src.substring(cap[0].length);
16877           this.tokens.push({
16878             type: 'heading',
16879             depth: cap[2] === '=' ? 1 : 2,
16880             text: cap[1]
16881           });
16882           continue;
16883         }
16884     
16885         // hr
16886         if (cap = this.rules.hr.exec(src)) {
16887           src = src.substring(cap[0].length);
16888           this.tokens.push({
16889             type: 'hr'
16890           });
16891           continue;
16892         }
16893     
16894         // blockquote
16895         if (cap = this.rules.blockquote.exec(src)) {
16896           src = src.substring(cap[0].length);
16897     
16898           this.tokens.push({
16899             type: 'blockquote_start'
16900           });
16901     
16902           cap = cap[0].replace(/^ *> ?/gm, '');
16903     
16904           // Pass `top` to keep the current
16905           // "toplevel" state. This is exactly
16906           // how markdown.pl works.
16907           this.token(cap, top, true);
16908     
16909           this.tokens.push({
16910             type: 'blockquote_end'
16911           });
16912     
16913           continue;
16914         }
16915     
16916         // list
16917         if (cap = this.rules.list.exec(src)) {
16918           src = src.substring(cap[0].length);
16919           bull = cap[2];
16920     
16921           this.tokens.push({
16922             type: 'list_start',
16923             ordered: bull.length > 1
16924           });
16925     
16926           // Get each top-level item.
16927           cap = cap[0].match(this.rules.item);
16928     
16929           next = false;
16930           l = cap.length;
16931           i = 0;
16932     
16933           for (; i < l; i++) {
16934             item = cap[i];
16935     
16936             // Remove the list item's bullet
16937             // so it is seen as the next token.
16938             space = item.length;
16939             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
16940     
16941             // Outdent whatever the
16942             // list item contains. Hacky.
16943             if (~item.indexOf('\n ')) {
16944               space -= item.length;
16945               item = !this.options.pedantic
16946                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
16947                 : item.replace(/^ {1,4}/gm, '');
16948             }
16949     
16950             // Determine whether the next list item belongs here.
16951             // Backpedal if it does not belong in this list.
16952             if (this.options.smartLists && i !== l - 1) {
16953               b = block.bullet.exec(cap[i + 1])[0];
16954               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
16955                 src = cap.slice(i + 1).join('\n') + src;
16956                 i = l - 1;
16957               }
16958             }
16959     
16960             // Determine whether item is loose or not.
16961             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
16962             // for discount behavior.
16963             loose = next || /\n\n(?!\s*$)/.test(item);
16964             if (i !== l - 1) {
16965               next = item.charAt(item.length - 1) === '\n';
16966               if (!loose) { loose = next; }
16967             }
16968     
16969             this.tokens.push({
16970               type: loose
16971                 ? 'loose_item_start'
16972                 : 'list_item_start'
16973             });
16974     
16975             // Recurse.
16976             this.token(item, false, bq);
16977     
16978             this.tokens.push({
16979               type: 'list_item_end'
16980             });
16981           }
16982     
16983           this.tokens.push({
16984             type: 'list_end'
16985           });
16986     
16987           continue;
16988         }
16989     
16990         // html
16991         if (cap = this.rules.html.exec(src)) {
16992           src = src.substring(cap[0].length);
16993           this.tokens.push({
16994             type: this.options.sanitize
16995               ? 'paragraph'
16996               : 'html',
16997             pre: !this.options.sanitizer
16998               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
16999             text: cap[0]
17000           });
17001           continue;
17002         }
17003     
17004         // def
17005         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17006           src = src.substring(cap[0].length);
17007           this.tokens.links[cap[1].toLowerCase()] = {
17008             href: cap[2],
17009             title: cap[3]
17010           };
17011           continue;
17012         }
17013     
17014         // table (gfm)
17015         if (top && (cap = this.rules.table.exec(src))) {
17016           src = src.substring(cap[0].length);
17017     
17018           item = {
17019             type: 'table',
17020             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17021             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17022             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17023           };
17024     
17025           for (i = 0; i < item.align.length; i++) {
17026             if (/^ *-+: *$/.test(item.align[i])) {
17027               item.align[i] = 'right';
17028             } else if (/^ *:-+: *$/.test(item.align[i])) {
17029               item.align[i] = 'center';
17030             } else if (/^ *:-+ *$/.test(item.align[i])) {
17031               item.align[i] = 'left';
17032             } else {
17033               item.align[i] = null;
17034             }
17035           }
17036     
17037           for (i = 0; i < item.cells.length; i++) {
17038             item.cells[i] = item.cells[i]
17039               .replace(/^ *\| *| *\| *$/g, '')
17040               .split(/ *\| */);
17041           }
17042     
17043           this.tokens.push(item);
17044     
17045           continue;
17046         }
17047     
17048         // top-level paragraph
17049         if (top && (cap = this.rules.paragraph.exec(src))) {
17050           src = src.substring(cap[0].length);
17051           this.tokens.push({
17052             type: 'paragraph',
17053             text: cap[1].charAt(cap[1].length - 1) === '\n'
17054               ? cap[1].slice(0, -1)
17055               : cap[1]
17056           });
17057           continue;
17058         }
17059     
17060         // text
17061         if (cap = this.rules.text.exec(src)) {
17062           // Top-level should never reach here.
17063           src = src.substring(cap[0].length);
17064           this.tokens.push({
17065             type: 'text',
17066             text: cap[0]
17067           });
17068           continue;
17069         }
17070     
17071         if (src) {
17072           throw new
17073             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17074         }
17075       }
17076     
17077       return this.tokens;
17078     };
17079     
17080     /**
17081      * Inline-Level Grammar
17082      */
17083     
17084     var inline = {
17085       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17086       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17087       url: noop,
17088       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17089       link: /^!?\[(inside)\]\(href\)/,
17090       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17091       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17092       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17093       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17094       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17095       br: /^ {2,}\n(?!\s*$)/,
17096       del: noop,
17097       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17098     };
17099     
17100     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17101     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17102     
17103     inline.link = replace(inline.link)
17104       ('inside', inline._inside)
17105       ('href', inline._href)
17106       ();
17107     
17108     inline.reflink = replace(inline.reflink)
17109       ('inside', inline._inside)
17110       ();
17111     
17112     /**
17113      * Normal Inline Grammar
17114      */
17115     
17116     inline.normal = merge({}, inline);
17117     
17118     /**
17119      * Pedantic Inline Grammar
17120      */
17121     
17122     inline.pedantic = merge({}, inline.normal, {
17123       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17124       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17125     });
17126     
17127     /**
17128      * GFM Inline Grammar
17129      */
17130     
17131     inline.gfm = merge({}, inline.normal, {
17132       escape: replace(inline.escape)('])', '~|])')(),
17133       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17134       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17135       text: replace(inline.text)
17136         (']|', '~]|')
17137         ('|', '|https?://|')
17138         ()
17139     });
17140     
17141     /**
17142      * GFM + Line Breaks Inline Grammar
17143      */
17144     
17145     inline.breaks = merge({}, inline.gfm, {
17146       br: replace(inline.br)('{2,}', '*')(),
17147       text: replace(inline.gfm.text)('{2,}', '*')()
17148     });
17149     
17150     /**
17151      * Inline Lexer & Compiler
17152      */
17153     
17154     function InlineLexer(links, options) {
17155       this.options = options || marked.defaults;
17156       this.links = links;
17157       this.rules = inline.normal;
17158       this.renderer = this.options.renderer || new Renderer;
17159       this.renderer.options = this.options;
17160     
17161       if (!this.links) {
17162         throw new
17163           Error('Tokens array requires a `links` property.');
17164       }
17165     
17166       if (this.options.gfm) {
17167         if (this.options.breaks) {
17168           this.rules = inline.breaks;
17169         } else {
17170           this.rules = inline.gfm;
17171         }
17172       } else if (this.options.pedantic) {
17173         this.rules = inline.pedantic;
17174       }
17175     }
17176     
17177     /**
17178      * Expose Inline Rules
17179      */
17180     
17181     InlineLexer.rules = inline;
17182     
17183     /**
17184      * Static Lexing/Compiling Method
17185      */
17186     
17187     InlineLexer.output = function(src, links, options) {
17188       var inline = new InlineLexer(links, options);
17189       return inline.output(src);
17190     };
17191     
17192     /**
17193      * Lexing/Compiling
17194      */
17195     
17196     InlineLexer.prototype.output = function(src) {
17197       var out = ''
17198         , link
17199         , text
17200         , href
17201         , cap;
17202     
17203       while (src) {
17204         // escape
17205         if (cap = this.rules.escape.exec(src)) {
17206           src = src.substring(cap[0].length);
17207           out += cap[1];
17208           continue;
17209         }
17210     
17211         // autolink
17212         if (cap = this.rules.autolink.exec(src)) {
17213           src = src.substring(cap[0].length);
17214           if (cap[2] === '@') {
17215             text = cap[1].charAt(6) === ':'
17216               ? this.mangle(cap[1].substring(7))
17217               : this.mangle(cap[1]);
17218             href = this.mangle('mailto:') + text;
17219           } else {
17220             text = escape(cap[1]);
17221             href = text;
17222           }
17223           out += this.renderer.link(href, null, text);
17224           continue;
17225         }
17226     
17227         // url (gfm)
17228         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17229           src = src.substring(cap[0].length);
17230           text = escape(cap[1]);
17231           href = text;
17232           out += this.renderer.link(href, null, text);
17233           continue;
17234         }
17235     
17236         // tag
17237         if (cap = this.rules.tag.exec(src)) {
17238           if (!this.inLink && /^<a /i.test(cap[0])) {
17239             this.inLink = true;
17240           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17241             this.inLink = false;
17242           }
17243           src = src.substring(cap[0].length);
17244           out += this.options.sanitize
17245             ? this.options.sanitizer
17246               ? this.options.sanitizer(cap[0])
17247               : escape(cap[0])
17248             : cap[0];
17249           continue;
17250         }
17251     
17252         // link
17253         if (cap = this.rules.link.exec(src)) {
17254           src = src.substring(cap[0].length);
17255           this.inLink = true;
17256           out += this.outputLink(cap, {
17257             href: cap[2],
17258             title: cap[3]
17259           });
17260           this.inLink = false;
17261           continue;
17262         }
17263     
17264         // reflink, nolink
17265         if ((cap = this.rules.reflink.exec(src))
17266             || (cap = this.rules.nolink.exec(src))) {
17267           src = src.substring(cap[0].length);
17268           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17269           link = this.links[link.toLowerCase()];
17270           if (!link || !link.href) {
17271             out += cap[0].charAt(0);
17272             src = cap[0].substring(1) + src;
17273             continue;
17274           }
17275           this.inLink = true;
17276           out += this.outputLink(cap, link);
17277           this.inLink = false;
17278           continue;
17279         }
17280     
17281         // strong
17282         if (cap = this.rules.strong.exec(src)) {
17283           src = src.substring(cap[0].length);
17284           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17285           continue;
17286         }
17287     
17288         // em
17289         if (cap = this.rules.em.exec(src)) {
17290           src = src.substring(cap[0].length);
17291           out += this.renderer.em(this.output(cap[2] || cap[1]));
17292           continue;
17293         }
17294     
17295         // code
17296         if (cap = this.rules.code.exec(src)) {
17297           src = src.substring(cap[0].length);
17298           out += this.renderer.codespan(escape(cap[2], true));
17299           continue;
17300         }
17301     
17302         // br
17303         if (cap = this.rules.br.exec(src)) {
17304           src = src.substring(cap[0].length);
17305           out += this.renderer.br();
17306           continue;
17307         }
17308     
17309         // del (gfm)
17310         if (cap = this.rules.del.exec(src)) {
17311           src = src.substring(cap[0].length);
17312           out += this.renderer.del(this.output(cap[1]));
17313           continue;
17314         }
17315     
17316         // text
17317         if (cap = this.rules.text.exec(src)) {
17318           src = src.substring(cap[0].length);
17319           out += this.renderer.text(escape(this.smartypants(cap[0])));
17320           continue;
17321         }
17322     
17323         if (src) {
17324           throw new
17325             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17326         }
17327       }
17328     
17329       return out;
17330     };
17331     
17332     /**
17333      * Compile Link
17334      */
17335     
17336     InlineLexer.prototype.outputLink = function(cap, link) {
17337       var href = escape(link.href)
17338         , title = link.title ? escape(link.title) : null;
17339     
17340       return cap[0].charAt(0) !== '!'
17341         ? this.renderer.link(href, title, this.output(cap[1]))
17342         : this.renderer.image(href, title, escape(cap[1]));
17343     };
17344     
17345     /**
17346      * Smartypants Transformations
17347      */
17348     
17349     InlineLexer.prototype.smartypants = function(text) {
17350       if (!this.options.smartypants)  { return text; }
17351       return text
17352         // em-dashes
17353         .replace(/---/g, '\u2014')
17354         // en-dashes
17355         .replace(/--/g, '\u2013')
17356         // opening singles
17357         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17358         // closing singles & apostrophes
17359         .replace(/'/g, '\u2019')
17360         // opening doubles
17361         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17362         // closing doubles
17363         .replace(/"/g, '\u201d')
17364         // ellipses
17365         .replace(/\.{3}/g, '\u2026');
17366     };
17367     
17368     /**
17369      * Mangle Links
17370      */
17371     
17372     InlineLexer.prototype.mangle = function(text) {
17373       if (!this.options.mangle) { return text; }
17374       var out = ''
17375         , l = text.length
17376         , i = 0
17377         , ch;
17378     
17379       for (; i < l; i++) {
17380         ch = text.charCodeAt(i);
17381         if (Math.random() > 0.5) {
17382           ch = 'x' + ch.toString(16);
17383         }
17384         out += '&#' + ch + ';';
17385       }
17386     
17387       return out;
17388     };
17389     
17390     /**
17391      * Renderer
17392      */
17393     
17394     function Renderer(options) {
17395       this.options = options || {};
17396     }
17397     
17398     Renderer.prototype.code = function(code, lang, escaped) {
17399       if (this.options.highlight) {
17400         var out = this.options.highlight(code, lang);
17401         if (out != null && out !== code) {
17402           escaped = true;
17403           code = out;
17404         }
17405       } else {
17406             // hack!!! - it's already escapeD?
17407             escaped = true;
17408       }
17409     
17410       if (!lang) {
17411         return '<pre><code>'
17412           + (escaped ? code : escape(code, true))
17413           + '\n</code></pre>';
17414       }
17415     
17416       return '<pre><code class="'
17417         + this.options.langPrefix
17418         + escape(lang, true)
17419         + '">'
17420         + (escaped ? code : escape(code, true))
17421         + '\n</code></pre>\n';
17422     };
17423     
17424     Renderer.prototype.blockquote = function(quote) {
17425       return '<blockquote>\n' + quote + '</blockquote>\n';
17426     };
17427     
17428     Renderer.prototype.html = function(html) {
17429       return html;
17430     };
17431     
17432     Renderer.prototype.heading = function(text, level, raw) {
17433       return '<h'
17434         + level
17435         + ' id="'
17436         + this.options.headerPrefix
17437         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17438         + '">'
17439         + text
17440         + '</h'
17441         + level
17442         + '>\n';
17443     };
17444     
17445     Renderer.prototype.hr = function() {
17446       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17447     };
17448     
17449     Renderer.prototype.list = function(body, ordered) {
17450       var type = ordered ? 'ol' : 'ul';
17451       return '<' + type + '>\n' + body + '</' + type + '>\n';
17452     };
17453     
17454     Renderer.prototype.listitem = function(text) {
17455       return '<li>' + text + '</li>\n';
17456     };
17457     
17458     Renderer.prototype.paragraph = function(text) {
17459       return '<p>' + text + '</p>\n';
17460     };
17461     
17462     Renderer.prototype.table = function(header, body) {
17463       return '<table class="table table-striped">\n'
17464         + '<thead>\n'
17465         + header
17466         + '</thead>\n'
17467         + '<tbody>\n'
17468         + body
17469         + '</tbody>\n'
17470         + '</table>\n';
17471     };
17472     
17473     Renderer.prototype.tablerow = function(content) {
17474       return '<tr>\n' + content + '</tr>\n';
17475     };
17476     
17477     Renderer.prototype.tablecell = function(content, flags) {
17478       var type = flags.header ? 'th' : 'td';
17479       var tag = flags.align
17480         ? '<' + type + ' style="text-align:' + flags.align + '">'
17481         : '<' + type + '>';
17482       return tag + content + '</' + type + '>\n';
17483     };
17484     
17485     // span level renderer
17486     Renderer.prototype.strong = function(text) {
17487       return '<strong>' + text + '</strong>';
17488     };
17489     
17490     Renderer.prototype.em = function(text) {
17491       return '<em>' + text + '</em>';
17492     };
17493     
17494     Renderer.prototype.codespan = function(text) {
17495       return '<code>' + text + '</code>';
17496     };
17497     
17498     Renderer.prototype.br = function() {
17499       return this.options.xhtml ? '<br/>' : '<br>';
17500     };
17501     
17502     Renderer.prototype.del = function(text) {
17503       return '<del>' + text + '</del>';
17504     };
17505     
17506     Renderer.prototype.link = function(href, title, text) {
17507       if (this.options.sanitize) {
17508         try {
17509           var prot = decodeURIComponent(unescape(href))
17510             .replace(/[^\w:]/g, '')
17511             .toLowerCase();
17512         } catch (e) {
17513           return '';
17514         }
17515         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17516           return '';
17517         }
17518       }
17519       var out = '<a href="' + href + '"';
17520       if (title) {
17521         out += ' title="' + title + '"';
17522       }
17523       out += '>' + text + '</a>';
17524       return out;
17525     };
17526     
17527     Renderer.prototype.image = function(href, title, text) {
17528       var out = '<img src="' + href + '" alt="' + text + '"';
17529       if (title) {
17530         out += ' title="' + title + '"';
17531       }
17532       out += this.options.xhtml ? '/>' : '>';
17533       return out;
17534     };
17535     
17536     Renderer.prototype.text = function(text) {
17537       return text;
17538     };
17539     
17540     /**
17541      * Parsing & Compiling
17542      */
17543     
17544     function Parser(options) {
17545       this.tokens = [];
17546       this.token = null;
17547       this.options = options || marked.defaults;
17548       this.options.renderer = this.options.renderer || new Renderer;
17549       this.renderer = this.options.renderer;
17550       this.renderer.options = this.options;
17551     }
17552     
17553     /**
17554      * Static Parse Method
17555      */
17556     
17557     Parser.parse = function(src, options, renderer) {
17558       var parser = new Parser(options, renderer);
17559       return parser.parse(src);
17560     };
17561     
17562     /**
17563      * Parse Loop
17564      */
17565     
17566     Parser.prototype.parse = function(src) {
17567       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17568       this.tokens = src.reverse();
17569     
17570       var out = '';
17571       while (this.next()) {
17572         out += this.tok();
17573       }
17574     
17575       return out;
17576     };
17577     
17578     /**
17579      * Next Token
17580      */
17581     
17582     Parser.prototype.next = function() {
17583       return this.token = this.tokens.pop();
17584     };
17585     
17586     /**
17587      * Preview Next Token
17588      */
17589     
17590     Parser.prototype.peek = function() {
17591       return this.tokens[this.tokens.length - 1] || 0;
17592     };
17593     
17594     /**
17595      * Parse Text Tokens
17596      */
17597     
17598     Parser.prototype.parseText = function() {
17599       var body = this.token.text;
17600     
17601       while (this.peek().type === 'text') {
17602         body += '\n' + this.next().text;
17603       }
17604     
17605       return this.inline.output(body);
17606     };
17607     
17608     /**
17609      * Parse Current Token
17610      */
17611     
17612     Parser.prototype.tok = function() {
17613       switch (this.token.type) {
17614         case 'space': {
17615           return '';
17616         }
17617         case 'hr': {
17618           return this.renderer.hr();
17619         }
17620         case 'heading': {
17621           return this.renderer.heading(
17622             this.inline.output(this.token.text),
17623             this.token.depth,
17624             this.token.text);
17625         }
17626         case 'code': {
17627           return this.renderer.code(this.token.text,
17628             this.token.lang,
17629             this.token.escaped);
17630         }
17631         case 'table': {
17632           var header = ''
17633             , body = ''
17634             , i
17635             , row
17636             , cell
17637             , flags
17638             , j;
17639     
17640           // header
17641           cell = '';
17642           for (i = 0; i < this.token.header.length; i++) {
17643             flags = { header: true, align: this.token.align[i] };
17644             cell += this.renderer.tablecell(
17645               this.inline.output(this.token.header[i]),
17646               { header: true, align: this.token.align[i] }
17647             );
17648           }
17649           header += this.renderer.tablerow(cell);
17650     
17651           for (i = 0; i < this.token.cells.length; i++) {
17652             row = this.token.cells[i];
17653     
17654             cell = '';
17655             for (j = 0; j < row.length; j++) {
17656               cell += this.renderer.tablecell(
17657                 this.inline.output(row[j]),
17658                 { header: false, align: this.token.align[j] }
17659               );
17660             }
17661     
17662             body += this.renderer.tablerow(cell);
17663           }
17664           return this.renderer.table(header, body);
17665         }
17666         case 'blockquote_start': {
17667           var body = '';
17668     
17669           while (this.next().type !== 'blockquote_end') {
17670             body += this.tok();
17671           }
17672     
17673           return this.renderer.blockquote(body);
17674         }
17675         case 'list_start': {
17676           var body = ''
17677             , ordered = this.token.ordered;
17678     
17679           while (this.next().type !== 'list_end') {
17680             body += this.tok();
17681           }
17682     
17683           return this.renderer.list(body, ordered);
17684         }
17685         case 'list_item_start': {
17686           var body = '';
17687     
17688           while (this.next().type !== 'list_item_end') {
17689             body += this.token.type === 'text'
17690               ? this.parseText()
17691               : this.tok();
17692           }
17693     
17694           return this.renderer.listitem(body);
17695         }
17696         case 'loose_item_start': {
17697           var body = '';
17698     
17699           while (this.next().type !== 'list_item_end') {
17700             body += this.tok();
17701           }
17702     
17703           return this.renderer.listitem(body);
17704         }
17705         case 'html': {
17706           var html = !this.token.pre && !this.options.pedantic
17707             ? this.inline.output(this.token.text)
17708             : this.token.text;
17709           return this.renderer.html(html);
17710         }
17711         case 'paragraph': {
17712           return this.renderer.paragraph(this.inline.output(this.token.text));
17713         }
17714         case 'text': {
17715           return this.renderer.paragraph(this.parseText());
17716         }
17717       }
17718     };
17719     
17720     /**
17721      * Helpers
17722      */
17723     
17724     function escape(html, encode) {
17725       return html
17726         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17727         .replace(/</g, '&lt;')
17728         .replace(/>/g, '&gt;')
17729         .replace(/"/g, '&quot;')
17730         .replace(/'/g, '&#39;');
17731     }
17732     
17733     function unescape(html) {
17734         // explicitly match decimal, hex, and named HTML entities 
17735       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17736         n = n.toLowerCase();
17737         if (n === 'colon') { return ':'; }
17738         if (n.charAt(0) === '#') {
17739           return n.charAt(1) === 'x'
17740             ? String.fromCharCode(parseInt(n.substring(2), 16))
17741             : String.fromCharCode(+n.substring(1));
17742         }
17743         return '';
17744       });
17745     }
17746     
17747     function replace(regex, opt) {
17748       regex = regex.source;
17749       opt = opt || '';
17750       return function self(name, val) {
17751         if (!name) { return new RegExp(regex, opt); }
17752         val = val.source || val;
17753         val = val.replace(/(^|[^\[])\^/g, '$1');
17754         regex = regex.replace(name, val);
17755         return self;
17756       };
17757     }
17758     
17759     function noop() {}
17760     noop.exec = noop;
17761     
17762     function merge(obj) {
17763       var i = 1
17764         , target
17765         , key;
17766     
17767       for (; i < arguments.length; i++) {
17768         target = arguments[i];
17769         for (key in target) {
17770           if (Object.prototype.hasOwnProperty.call(target, key)) {
17771             obj[key] = target[key];
17772           }
17773         }
17774       }
17775     
17776       return obj;
17777     }
17778     
17779     
17780     /**
17781      * Marked
17782      */
17783     
17784     function marked(src, opt, callback) {
17785       if (callback || typeof opt === 'function') {
17786         if (!callback) {
17787           callback = opt;
17788           opt = null;
17789         }
17790     
17791         opt = merge({}, marked.defaults, opt || {});
17792     
17793         var highlight = opt.highlight
17794           , tokens
17795           , pending
17796           , i = 0;
17797     
17798         try {
17799           tokens = Lexer.lex(src, opt)
17800         } catch (e) {
17801           return callback(e);
17802         }
17803     
17804         pending = tokens.length;
17805     
17806         var done = function(err) {
17807           if (err) {
17808             opt.highlight = highlight;
17809             return callback(err);
17810           }
17811     
17812           var out;
17813     
17814           try {
17815             out = Parser.parse(tokens, opt);
17816           } catch (e) {
17817             err = e;
17818           }
17819     
17820           opt.highlight = highlight;
17821     
17822           return err
17823             ? callback(err)
17824             : callback(null, out);
17825         };
17826     
17827         if (!highlight || highlight.length < 3) {
17828           return done();
17829         }
17830     
17831         delete opt.highlight;
17832     
17833         if (!pending) { return done(); }
17834     
17835         for (; i < tokens.length; i++) {
17836           (function(token) {
17837             if (token.type !== 'code') {
17838               return --pending || done();
17839             }
17840             return highlight(token.text, token.lang, function(err, code) {
17841               if (err) { return done(err); }
17842               if (code == null || code === token.text) {
17843                 return --pending || done();
17844               }
17845               token.text = code;
17846               token.escaped = true;
17847               --pending || done();
17848             });
17849           })(tokens[i]);
17850         }
17851     
17852         return;
17853       }
17854       try {
17855         if (opt) { opt = merge({}, marked.defaults, opt); }
17856         return Parser.parse(Lexer.lex(src, opt), opt);
17857       } catch (e) {
17858         e.message += '\nPlease report this to https://github.com/chjj/marked.';
17859         if ((opt || marked.defaults).silent) {
17860           return '<p>An error occured:</p><pre>'
17861             + escape(e.message + '', true)
17862             + '</pre>';
17863         }
17864         throw e;
17865       }
17866     }
17867     
17868     /**
17869      * Options
17870      */
17871     
17872     marked.options =
17873     marked.setOptions = function(opt) {
17874       merge(marked.defaults, opt);
17875       return marked;
17876     };
17877     
17878     marked.defaults = {
17879       gfm: true,
17880       tables: true,
17881       breaks: false,
17882       pedantic: false,
17883       sanitize: false,
17884       sanitizer: null,
17885       mangle: true,
17886       smartLists: false,
17887       silent: false,
17888       highlight: null,
17889       langPrefix: 'lang-',
17890       smartypants: false,
17891       headerPrefix: '',
17892       renderer: new Renderer,
17893       xhtml: false
17894     };
17895     
17896     /**
17897      * Expose
17898      */
17899     
17900     marked.Parser = Parser;
17901     marked.parser = Parser.parse;
17902     
17903     marked.Renderer = Renderer;
17904     
17905     marked.Lexer = Lexer;
17906     marked.lexer = Lexer.lex;
17907     
17908     marked.InlineLexer = InlineLexer;
17909     marked.inlineLexer = InlineLexer.output;
17910     
17911     marked.parse = marked;
17912     
17913     Roo.Markdown.marked = marked;
17914
17915 })();/*
17916  * Based on:
17917  * Ext JS Library 1.1.1
17918  * Copyright(c) 2006-2007, Ext JS, LLC.
17919  *
17920  * Originally Released Under LGPL - original licence link has changed is not relivant.
17921  *
17922  * Fork - LGPL
17923  * <script type="text/javascript">
17924  */
17925
17926
17927
17928 /*
17929  * These classes are derivatives of the similarly named classes in the YUI Library.
17930  * The original license:
17931  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
17932  * Code licensed under the BSD License:
17933  * http://developer.yahoo.net/yui/license.txt
17934  */
17935
17936 (function() {
17937
17938 var Event=Roo.EventManager;
17939 var Dom=Roo.lib.Dom;
17940
17941 /**
17942  * @class Roo.dd.DragDrop
17943  * @extends Roo.util.Observable
17944  * Defines the interface and base operation of items that that can be
17945  * dragged or can be drop targets.  It was designed to be extended, overriding
17946  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
17947  * Up to three html elements can be associated with a DragDrop instance:
17948  * <ul>
17949  * <li>linked element: the element that is passed into the constructor.
17950  * This is the element which defines the boundaries for interaction with
17951  * other DragDrop objects.</li>
17952  * <li>handle element(s): The drag operation only occurs if the element that
17953  * was clicked matches a handle element.  By default this is the linked
17954  * element, but there are times that you will want only a portion of the
17955  * linked element to initiate the drag operation, and the setHandleElId()
17956  * method provides a way to define this.</li>
17957  * <li>drag element: this represents the element that would be moved along
17958  * with the cursor during a drag operation.  By default, this is the linked
17959  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
17960  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
17961  * </li>
17962  * </ul>
17963  * This class should not be instantiated until the onload event to ensure that
17964  * the associated elements are available.
17965  * The following would define a DragDrop obj that would interact with any
17966  * other DragDrop obj in the "group1" group:
17967  * <pre>
17968  *  dd = new Roo.dd.DragDrop("div1", "group1");
17969  * </pre>
17970  * Since none of the event handlers have been implemented, nothing would
17971  * actually happen if you were to run the code above.  Normally you would
17972  * override this class or one of the default implementations, but you can
17973  * also override the methods you want on an instance of the class...
17974  * <pre>
17975  *  dd.onDragDrop = function(e, id) {
17976  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
17977  *  }
17978  * </pre>
17979  * @constructor
17980  * @param {String} id of the element that is linked to this instance
17981  * @param {String} sGroup the group of related DragDrop objects
17982  * @param {object} config an object containing configurable attributes
17983  *                Valid properties for DragDrop:
17984  *                    padding, isTarget, maintainOffset, primaryButtonOnly
17985  */
17986 Roo.dd.DragDrop = function(id, sGroup, config) {
17987     if (id) {
17988         this.init(id, sGroup, config);
17989     }
17990     
17991 };
17992
17993 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
17994
17995     /**
17996      * The id of the element associated with this object.  This is what we
17997      * refer to as the "linked element" because the size and position of
17998      * this element is used to determine when the drag and drop objects have
17999      * interacted.
18000      * @property id
18001      * @type String
18002      */
18003     id: null,
18004
18005     /**
18006      * Configuration attributes passed into the constructor
18007      * @property config
18008      * @type object
18009      */
18010     config: null,
18011
18012     /**
18013      * The id of the element that will be dragged.  By default this is same
18014      * as the linked element , but could be changed to another element. Ex:
18015      * Roo.dd.DDProxy
18016      * @property dragElId
18017      * @type String
18018      * @private
18019      */
18020     dragElId: null,
18021
18022     /**
18023      * the id of the element that initiates the drag operation.  By default
18024      * this is the linked element, but could be changed to be a child of this
18025      * element.  This lets us do things like only starting the drag when the
18026      * header element within the linked html element is clicked.
18027      * @property handleElId
18028      * @type String
18029      * @private
18030      */
18031     handleElId: null,
18032
18033     /**
18034      * An associative array of HTML tags that will be ignored if clicked.
18035      * @property invalidHandleTypes
18036      * @type {string: string}
18037      */
18038     invalidHandleTypes: null,
18039
18040     /**
18041      * An associative array of ids for elements that will be ignored if clicked
18042      * @property invalidHandleIds
18043      * @type {string: string}
18044      */
18045     invalidHandleIds: null,
18046
18047     /**
18048      * An indexted array of css class names for elements that will be ignored
18049      * if clicked.
18050      * @property invalidHandleClasses
18051      * @type string[]
18052      */
18053     invalidHandleClasses: null,
18054
18055     /**
18056      * The linked element's absolute X position at the time the drag was
18057      * started
18058      * @property startPageX
18059      * @type int
18060      * @private
18061      */
18062     startPageX: 0,
18063
18064     /**
18065      * The linked element's absolute X position at the time the drag was
18066      * started
18067      * @property startPageY
18068      * @type int
18069      * @private
18070      */
18071     startPageY: 0,
18072
18073     /**
18074      * The group defines a logical collection of DragDrop objects that are
18075      * related.  Instances only get events when interacting with other
18076      * DragDrop object in the same group.  This lets us define multiple
18077      * groups using a single DragDrop subclass if we want.
18078      * @property groups
18079      * @type {string: string}
18080      */
18081     groups: null,
18082
18083     /**
18084      * Individual drag/drop instances can be locked.  This will prevent
18085      * onmousedown start drag.
18086      * @property locked
18087      * @type boolean
18088      * @private
18089      */
18090     locked: false,
18091
18092     /**
18093      * Lock this instance
18094      * @method lock
18095      */
18096     lock: function() { this.locked = true; },
18097
18098     /**
18099      * Unlock this instace
18100      * @method unlock
18101      */
18102     unlock: function() { this.locked = false; },
18103
18104     /**
18105      * By default, all insances can be a drop target.  This can be disabled by
18106      * setting isTarget to false.
18107      * @method isTarget
18108      * @type boolean
18109      */
18110     isTarget: true,
18111
18112     /**
18113      * The padding configured for this drag and drop object for calculating
18114      * the drop zone intersection with this object.
18115      * @method padding
18116      * @type int[]
18117      */
18118     padding: null,
18119
18120     /**
18121      * Cached reference to the linked element
18122      * @property _domRef
18123      * @private
18124      */
18125     _domRef: null,
18126
18127     /**
18128      * Internal typeof flag
18129      * @property __ygDragDrop
18130      * @private
18131      */
18132     __ygDragDrop: true,
18133
18134     /**
18135      * Set to true when horizontal contraints are applied
18136      * @property constrainX
18137      * @type boolean
18138      * @private
18139      */
18140     constrainX: false,
18141
18142     /**
18143      * Set to true when vertical contraints are applied
18144      * @property constrainY
18145      * @type boolean
18146      * @private
18147      */
18148     constrainY: false,
18149
18150     /**
18151      * The left constraint
18152      * @property minX
18153      * @type int
18154      * @private
18155      */
18156     minX: 0,
18157
18158     /**
18159      * The right constraint
18160      * @property maxX
18161      * @type int
18162      * @private
18163      */
18164     maxX: 0,
18165
18166     /**
18167      * The up constraint
18168      * @property minY
18169      * @type int
18170      * @type int
18171      * @private
18172      */
18173     minY: 0,
18174
18175     /**
18176      * The down constraint
18177      * @property maxY
18178      * @type int
18179      * @private
18180      */
18181     maxY: 0,
18182
18183     /**
18184      * Maintain offsets when we resetconstraints.  Set to true when you want
18185      * the position of the element relative to its parent to stay the same
18186      * when the page changes
18187      *
18188      * @property maintainOffset
18189      * @type boolean
18190      */
18191     maintainOffset: false,
18192
18193     /**
18194      * Array of pixel locations the element will snap to if we specified a
18195      * horizontal graduation/interval.  This array is generated automatically
18196      * when you define a tick interval.
18197      * @property xTicks
18198      * @type int[]
18199      */
18200     xTicks: null,
18201
18202     /**
18203      * Array of pixel locations the element will snap to if we specified a
18204      * vertical graduation/interval.  This array is generated automatically
18205      * when you define a tick interval.
18206      * @property yTicks
18207      * @type int[]
18208      */
18209     yTicks: null,
18210
18211     /**
18212      * By default the drag and drop instance will only respond to the primary
18213      * button click (left button for a right-handed mouse).  Set to true to
18214      * allow drag and drop to start with any mouse click that is propogated
18215      * by the browser
18216      * @property primaryButtonOnly
18217      * @type boolean
18218      */
18219     primaryButtonOnly: true,
18220
18221     /**
18222      * The availabe property is false until the linked dom element is accessible.
18223      * @property available
18224      * @type boolean
18225      */
18226     available: false,
18227
18228     /**
18229      * By default, drags can only be initiated if the mousedown occurs in the
18230      * region the linked element is.  This is done in part to work around a
18231      * bug in some browsers that mis-report the mousedown if the previous
18232      * mouseup happened outside of the window.  This property is set to true
18233      * if outer handles are defined.
18234      *
18235      * @property hasOuterHandles
18236      * @type boolean
18237      * @default false
18238      */
18239     hasOuterHandles: false,
18240
18241     /**
18242      * Code that executes immediately before the startDrag event
18243      * @method b4StartDrag
18244      * @private
18245      */
18246     b4StartDrag: function(x, y) { },
18247
18248     /**
18249      * Abstract method called after a drag/drop object is clicked
18250      * and the drag or mousedown time thresholds have beeen met.
18251      * @method startDrag
18252      * @param {int} X click location
18253      * @param {int} Y click location
18254      */
18255     startDrag: function(x, y) { /* override this */ },
18256
18257     /**
18258      * Code that executes immediately before the onDrag event
18259      * @method b4Drag
18260      * @private
18261      */
18262     b4Drag: function(e) { },
18263
18264     /**
18265      * Abstract method called during the onMouseMove event while dragging an
18266      * object.
18267      * @method onDrag
18268      * @param {Event} e the mousemove event
18269      */
18270     onDrag: function(e) { /* override this */ },
18271
18272     /**
18273      * Abstract method called when this element fist begins hovering over
18274      * another DragDrop obj
18275      * @method onDragEnter
18276      * @param {Event} e the mousemove event
18277      * @param {String|DragDrop[]} id In POINT mode, the element
18278      * id this is hovering over.  In INTERSECT mode, an array of one or more
18279      * dragdrop items being hovered over.
18280      */
18281     onDragEnter: function(e, id) { /* override this */ },
18282
18283     /**
18284      * Code that executes immediately before the onDragOver event
18285      * @method b4DragOver
18286      * @private
18287      */
18288     b4DragOver: function(e) { },
18289
18290     /**
18291      * Abstract method called when this element is hovering over another
18292      * DragDrop obj
18293      * @method onDragOver
18294      * @param {Event} e the mousemove event
18295      * @param {String|DragDrop[]} id In POINT mode, the element
18296      * id this is hovering over.  In INTERSECT mode, an array of dd items
18297      * being hovered over.
18298      */
18299     onDragOver: function(e, id) { /* override this */ },
18300
18301     /**
18302      * Code that executes immediately before the onDragOut event
18303      * @method b4DragOut
18304      * @private
18305      */
18306     b4DragOut: function(e) { },
18307
18308     /**
18309      * Abstract method called when we are no longer hovering over an element
18310      * @method onDragOut
18311      * @param {Event} e the mousemove event
18312      * @param {String|DragDrop[]} id In POINT mode, the element
18313      * id this was hovering over.  In INTERSECT mode, an array of dd items
18314      * that the mouse is no longer over.
18315      */
18316     onDragOut: function(e, id) { /* override this */ },
18317
18318     /**
18319      * Code that executes immediately before the onDragDrop event
18320      * @method b4DragDrop
18321      * @private
18322      */
18323     b4DragDrop: function(e) { },
18324
18325     /**
18326      * Abstract method called when this item is dropped on another DragDrop
18327      * obj
18328      * @method onDragDrop
18329      * @param {Event} e the mouseup event
18330      * @param {String|DragDrop[]} id In POINT mode, the element
18331      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18332      * was dropped on.
18333      */
18334     onDragDrop: function(e, id) { /* override this */ },
18335
18336     /**
18337      * Abstract method called when this item is dropped on an area with no
18338      * drop target
18339      * @method onInvalidDrop
18340      * @param {Event} e the mouseup event
18341      */
18342     onInvalidDrop: function(e) { /* override this */ },
18343
18344     /**
18345      * Code that executes immediately before the endDrag event
18346      * @method b4EndDrag
18347      * @private
18348      */
18349     b4EndDrag: function(e) { },
18350
18351     /**
18352      * Fired when we are done dragging the object
18353      * @method endDrag
18354      * @param {Event} e the mouseup event
18355      */
18356     endDrag: function(e) { /* override this */ },
18357
18358     /**
18359      * Code executed immediately before the onMouseDown event
18360      * @method b4MouseDown
18361      * @param {Event} e the mousedown event
18362      * @private
18363      */
18364     b4MouseDown: function(e) {  },
18365
18366     /**
18367      * Event handler that fires when a drag/drop obj gets a mousedown
18368      * @method onMouseDown
18369      * @param {Event} e the mousedown event
18370      */
18371     onMouseDown: function(e) { /* override this */ },
18372
18373     /**
18374      * Event handler that fires when a drag/drop obj gets a mouseup
18375      * @method onMouseUp
18376      * @param {Event} e the mouseup event
18377      */
18378     onMouseUp: function(e) { /* override this */ },
18379
18380     /**
18381      * Override the onAvailable method to do what is needed after the initial
18382      * position was determined.
18383      * @method onAvailable
18384      */
18385     onAvailable: function () {
18386     },
18387
18388     /*
18389      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18390      * @type Object
18391      */
18392     defaultPadding : {left:0, right:0, top:0, bottom:0},
18393
18394     /*
18395      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18396  *
18397  * Usage:
18398  <pre><code>
18399  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18400                 { dragElId: "existingProxyDiv" });
18401  dd.startDrag = function(){
18402      this.constrainTo("parent-id");
18403  };
18404  </code></pre>
18405  * Or you can initalize it using the {@link Roo.Element} object:
18406  <pre><code>
18407  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18408      startDrag : function(){
18409          this.constrainTo("parent-id");
18410      }
18411  });
18412  </code></pre>
18413      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18414      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18415      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18416      * an object containing the sides to pad. For example: {right:10, bottom:10}
18417      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18418      */
18419     constrainTo : function(constrainTo, pad, inContent){
18420         if(typeof pad == "number"){
18421             pad = {left: pad, right:pad, top:pad, bottom:pad};
18422         }
18423         pad = pad || this.defaultPadding;
18424         var b = Roo.get(this.getEl()).getBox();
18425         var ce = Roo.get(constrainTo);
18426         var s = ce.getScroll();
18427         var c, cd = ce.dom;
18428         if(cd == document.body){
18429             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18430         }else{
18431             xy = ce.getXY();
18432             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18433         }
18434
18435
18436         var topSpace = b.y - c.y;
18437         var leftSpace = b.x - c.x;
18438
18439         this.resetConstraints();
18440         this.setXConstraint(leftSpace - (pad.left||0), // left
18441                 c.width - leftSpace - b.width - (pad.right||0) //right
18442         );
18443         this.setYConstraint(topSpace - (pad.top||0), //top
18444                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18445         );
18446     },
18447
18448     /**
18449      * Returns a reference to the linked element
18450      * @method getEl
18451      * @return {HTMLElement} the html element
18452      */
18453     getEl: function() {
18454         if (!this._domRef) {
18455             this._domRef = Roo.getDom(this.id);
18456         }
18457
18458         return this._domRef;
18459     },
18460
18461     /**
18462      * Returns a reference to the actual element to drag.  By default this is
18463      * the same as the html element, but it can be assigned to another
18464      * element. An example of this can be found in Roo.dd.DDProxy
18465      * @method getDragEl
18466      * @return {HTMLElement} the html element
18467      */
18468     getDragEl: function() {
18469         return Roo.getDom(this.dragElId);
18470     },
18471
18472     /**
18473      * Sets up the DragDrop object.  Must be called in the constructor of any
18474      * Roo.dd.DragDrop subclass
18475      * @method init
18476      * @param id the id of the linked element
18477      * @param {String} sGroup the group of related items
18478      * @param {object} config configuration attributes
18479      */
18480     init: function(id, sGroup, config) {
18481         this.initTarget(id, sGroup, config);
18482         if (!Roo.isTouch) {
18483             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18484         }
18485         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18486         // Event.on(this.id, "selectstart", Event.preventDefault);
18487     },
18488
18489     /**
18490      * Initializes Targeting functionality only... the object does not
18491      * get a mousedown handler.
18492      * @method initTarget
18493      * @param id the id of the linked element
18494      * @param {String} sGroup the group of related items
18495      * @param {object} config configuration attributes
18496      */
18497     initTarget: function(id, sGroup, config) {
18498
18499         // configuration attributes
18500         this.config = config || {};
18501
18502         // create a local reference to the drag and drop manager
18503         this.DDM = Roo.dd.DDM;
18504         // initialize the groups array
18505         this.groups = {};
18506
18507         // assume that we have an element reference instead of an id if the
18508         // parameter is not a string
18509         if (typeof id !== "string") {
18510             id = Roo.id(id);
18511         }
18512
18513         // set the id
18514         this.id = id;
18515
18516         // add to an interaction group
18517         this.addToGroup((sGroup) ? sGroup : "default");
18518
18519         // We don't want to register this as the handle with the manager
18520         // so we just set the id rather than calling the setter.
18521         this.handleElId = id;
18522
18523         // the linked element is the element that gets dragged by default
18524         this.setDragElId(id);
18525
18526         // by default, clicked anchors will not start drag operations.
18527         this.invalidHandleTypes = { A: "A" };
18528         this.invalidHandleIds = {};
18529         this.invalidHandleClasses = [];
18530
18531         this.applyConfig();
18532
18533         this.handleOnAvailable();
18534     },
18535
18536     /**
18537      * Applies the configuration parameters that were passed into the constructor.
18538      * This is supposed to happen at each level through the inheritance chain.  So
18539      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18540      * DragDrop in order to get all of the parameters that are available in
18541      * each object.
18542      * @method applyConfig
18543      */
18544     applyConfig: function() {
18545
18546         // configurable properties:
18547         //    padding, isTarget, maintainOffset, primaryButtonOnly
18548         this.padding           = this.config.padding || [0, 0, 0, 0];
18549         this.isTarget          = (this.config.isTarget !== false);
18550         this.maintainOffset    = (this.config.maintainOffset);
18551         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18552
18553     },
18554
18555     /**
18556      * Executed when the linked element is available
18557      * @method handleOnAvailable
18558      * @private
18559      */
18560     handleOnAvailable: function() {
18561         this.available = true;
18562         this.resetConstraints();
18563         this.onAvailable();
18564     },
18565
18566      /**
18567      * Configures the padding for the target zone in px.  Effectively expands
18568      * (or reduces) the virtual object size for targeting calculations.
18569      * Supports css-style shorthand; if only one parameter is passed, all sides
18570      * will have that padding, and if only two are passed, the top and bottom
18571      * will have the first param, the left and right the second.
18572      * @method setPadding
18573      * @param {int} iTop    Top pad
18574      * @param {int} iRight  Right pad
18575      * @param {int} iBot    Bot pad
18576      * @param {int} iLeft   Left pad
18577      */
18578     setPadding: function(iTop, iRight, iBot, iLeft) {
18579         // this.padding = [iLeft, iRight, iTop, iBot];
18580         if (!iRight && 0 !== iRight) {
18581             this.padding = [iTop, iTop, iTop, iTop];
18582         } else if (!iBot && 0 !== iBot) {
18583             this.padding = [iTop, iRight, iTop, iRight];
18584         } else {
18585             this.padding = [iTop, iRight, iBot, iLeft];
18586         }
18587     },
18588
18589     /**
18590      * Stores the initial placement of the linked element.
18591      * @method setInitialPosition
18592      * @param {int} diffX   the X offset, default 0
18593      * @param {int} diffY   the Y offset, default 0
18594      */
18595     setInitPosition: function(diffX, diffY) {
18596         var el = this.getEl();
18597
18598         if (!this.DDM.verifyEl(el)) {
18599             return;
18600         }
18601
18602         var dx = diffX || 0;
18603         var dy = diffY || 0;
18604
18605         var p = Dom.getXY( el );
18606
18607         this.initPageX = p[0] - dx;
18608         this.initPageY = p[1] - dy;
18609
18610         this.lastPageX = p[0];
18611         this.lastPageY = p[1];
18612
18613
18614         this.setStartPosition(p);
18615     },
18616
18617     /**
18618      * Sets the start position of the element.  This is set when the obj
18619      * is initialized, the reset when a drag is started.
18620      * @method setStartPosition
18621      * @param pos current position (from previous lookup)
18622      * @private
18623      */
18624     setStartPosition: function(pos) {
18625         var p = pos || Dom.getXY( this.getEl() );
18626         this.deltaSetXY = null;
18627
18628         this.startPageX = p[0];
18629         this.startPageY = p[1];
18630     },
18631
18632     /**
18633      * Add this instance to a group of related drag/drop objects.  All
18634      * instances belong to at least one group, and can belong to as many
18635      * groups as needed.
18636      * @method addToGroup
18637      * @param sGroup {string} the name of the group
18638      */
18639     addToGroup: function(sGroup) {
18640         this.groups[sGroup] = true;
18641         this.DDM.regDragDrop(this, sGroup);
18642     },
18643
18644     /**
18645      * Remove's this instance from the supplied interaction group
18646      * @method removeFromGroup
18647      * @param {string}  sGroup  The group to drop
18648      */
18649     removeFromGroup: function(sGroup) {
18650         if (this.groups[sGroup]) {
18651             delete this.groups[sGroup];
18652         }
18653
18654         this.DDM.removeDDFromGroup(this, sGroup);
18655     },
18656
18657     /**
18658      * Allows you to specify that an element other than the linked element
18659      * will be moved with the cursor during a drag
18660      * @method setDragElId
18661      * @param id {string} the id of the element that will be used to initiate the drag
18662      */
18663     setDragElId: function(id) {
18664         this.dragElId = id;
18665     },
18666
18667     /**
18668      * Allows you to specify a child of the linked element that should be
18669      * used to initiate the drag operation.  An example of this would be if
18670      * you have a content div with text and links.  Clicking anywhere in the
18671      * content area would normally start the drag operation.  Use this method
18672      * to specify that an element inside of the content div is the element
18673      * that starts the drag operation.
18674      * @method setHandleElId
18675      * @param id {string} the id of the element that will be used to
18676      * initiate the drag.
18677      */
18678     setHandleElId: function(id) {
18679         if (typeof id !== "string") {
18680             id = Roo.id(id);
18681         }
18682         this.handleElId = id;
18683         this.DDM.regHandle(this.id, id);
18684     },
18685
18686     /**
18687      * Allows you to set an element outside of the linked element as a drag
18688      * handle
18689      * @method setOuterHandleElId
18690      * @param id the id of the element that will be used to initiate the drag
18691      */
18692     setOuterHandleElId: function(id) {
18693         if (typeof id !== "string") {
18694             id = Roo.id(id);
18695         }
18696         Event.on(id, "mousedown",
18697                 this.handleMouseDown, this);
18698         this.setHandleElId(id);
18699
18700         this.hasOuterHandles = true;
18701     },
18702
18703     /**
18704      * Remove all drag and drop hooks for this element
18705      * @method unreg
18706      */
18707     unreg: function() {
18708         Event.un(this.id, "mousedown",
18709                 this.handleMouseDown);
18710         Event.un(this.id, "touchstart",
18711                 this.handleMouseDown);
18712         this._domRef = null;
18713         this.DDM._remove(this);
18714     },
18715
18716     destroy : function(){
18717         this.unreg();
18718     },
18719
18720     /**
18721      * Returns true if this instance is locked, or the drag drop mgr is locked
18722      * (meaning that all drag/drop is disabled on the page.)
18723      * @method isLocked
18724      * @return {boolean} true if this obj or all drag/drop is locked, else
18725      * false
18726      */
18727     isLocked: function() {
18728         return (this.DDM.isLocked() || this.locked);
18729     },
18730
18731     /**
18732      * Fired when this object is clicked
18733      * @method handleMouseDown
18734      * @param {Event} e
18735      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18736      * @private
18737      */
18738     handleMouseDown: function(e, oDD){
18739      
18740         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18741             //Roo.log('not touch/ button !=0');
18742             return;
18743         }
18744         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18745             return; // double touch..
18746         }
18747         
18748
18749         if (this.isLocked()) {
18750             //Roo.log('locked');
18751             return;
18752         }
18753
18754         this.DDM.refreshCache(this.groups);
18755 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18756         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18757         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18758             //Roo.log('no outer handes or not over target');
18759                 // do nothing.
18760         } else {
18761 //            Roo.log('check validator');
18762             if (this.clickValidator(e)) {
18763 //                Roo.log('validate success');
18764                 // set the initial element position
18765                 this.setStartPosition();
18766
18767
18768                 this.b4MouseDown(e);
18769                 this.onMouseDown(e);
18770
18771                 this.DDM.handleMouseDown(e, this);
18772
18773                 this.DDM.stopEvent(e);
18774             } else {
18775
18776
18777             }
18778         }
18779     },
18780
18781     clickValidator: function(e) {
18782         var target = e.getTarget();
18783         return ( this.isValidHandleChild(target) &&
18784                     (this.id == this.handleElId ||
18785                         this.DDM.handleWasClicked(target, this.id)) );
18786     },
18787
18788     /**
18789      * Allows you to specify a tag name that should not start a drag operation
18790      * when clicked.  This is designed to facilitate embedding links within a
18791      * drag handle that do something other than start the drag.
18792      * @method addInvalidHandleType
18793      * @param {string} tagName the type of element to exclude
18794      */
18795     addInvalidHandleType: function(tagName) {
18796         var type = tagName.toUpperCase();
18797         this.invalidHandleTypes[type] = type;
18798     },
18799
18800     /**
18801      * Lets you to specify an element id for a child of a drag handle
18802      * that should not initiate a drag
18803      * @method addInvalidHandleId
18804      * @param {string} id the element id of the element you wish to ignore
18805      */
18806     addInvalidHandleId: function(id) {
18807         if (typeof id !== "string") {
18808             id = Roo.id(id);
18809         }
18810         this.invalidHandleIds[id] = id;
18811     },
18812
18813     /**
18814      * Lets you specify a css class of elements that will not initiate a drag
18815      * @method addInvalidHandleClass
18816      * @param {string} cssClass the class of the elements you wish to ignore
18817      */
18818     addInvalidHandleClass: function(cssClass) {
18819         this.invalidHandleClasses.push(cssClass);
18820     },
18821
18822     /**
18823      * Unsets an excluded tag name set by addInvalidHandleType
18824      * @method removeInvalidHandleType
18825      * @param {string} tagName the type of element to unexclude
18826      */
18827     removeInvalidHandleType: function(tagName) {
18828         var type = tagName.toUpperCase();
18829         // this.invalidHandleTypes[type] = null;
18830         delete this.invalidHandleTypes[type];
18831     },
18832
18833     /**
18834      * Unsets an invalid handle id
18835      * @method removeInvalidHandleId
18836      * @param {string} id the id of the element to re-enable
18837      */
18838     removeInvalidHandleId: function(id) {
18839         if (typeof id !== "string") {
18840             id = Roo.id(id);
18841         }
18842         delete this.invalidHandleIds[id];
18843     },
18844
18845     /**
18846      * Unsets an invalid css class
18847      * @method removeInvalidHandleClass
18848      * @param {string} cssClass the class of the element(s) you wish to
18849      * re-enable
18850      */
18851     removeInvalidHandleClass: function(cssClass) {
18852         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
18853             if (this.invalidHandleClasses[i] == cssClass) {
18854                 delete this.invalidHandleClasses[i];
18855             }
18856         }
18857     },
18858
18859     /**
18860      * Checks the tag exclusion list to see if this click should be ignored
18861      * @method isValidHandleChild
18862      * @param {HTMLElement} node the HTMLElement to evaluate
18863      * @return {boolean} true if this is a valid tag type, false if not
18864      */
18865     isValidHandleChild: function(node) {
18866
18867         var valid = true;
18868         // var n = (node.nodeName == "#text") ? node.parentNode : node;
18869         var nodeName;
18870         try {
18871             nodeName = node.nodeName.toUpperCase();
18872         } catch(e) {
18873             nodeName = node.nodeName;
18874         }
18875         valid = valid && !this.invalidHandleTypes[nodeName];
18876         valid = valid && !this.invalidHandleIds[node.id];
18877
18878         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
18879             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
18880         }
18881
18882
18883         return valid;
18884
18885     },
18886
18887     /**
18888      * Create the array of horizontal tick marks if an interval was specified
18889      * in setXConstraint().
18890      * @method setXTicks
18891      * @private
18892      */
18893     setXTicks: function(iStartX, iTickSize) {
18894         this.xTicks = [];
18895         this.xTickSize = iTickSize;
18896
18897         var tickMap = {};
18898
18899         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
18900             if (!tickMap[i]) {
18901                 this.xTicks[this.xTicks.length] = i;
18902                 tickMap[i] = true;
18903             }
18904         }
18905
18906         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
18907             if (!tickMap[i]) {
18908                 this.xTicks[this.xTicks.length] = i;
18909                 tickMap[i] = true;
18910             }
18911         }
18912
18913         this.xTicks.sort(this.DDM.numericSort) ;
18914     },
18915
18916     /**
18917      * Create the array of vertical tick marks if an interval was specified in
18918      * setYConstraint().
18919      * @method setYTicks
18920      * @private
18921      */
18922     setYTicks: function(iStartY, iTickSize) {
18923         this.yTicks = [];
18924         this.yTickSize = iTickSize;
18925
18926         var tickMap = {};
18927
18928         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
18929             if (!tickMap[i]) {
18930                 this.yTicks[this.yTicks.length] = i;
18931                 tickMap[i] = true;
18932             }
18933         }
18934
18935         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
18936             if (!tickMap[i]) {
18937                 this.yTicks[this.yTicks.length] = i;
18938                 tickMap[i] = true;
18939             }
18940         }
18941
18942         this.yTicks.sort(this.DDM.numericSort) ;
18943     },
18944
18945     /**
18946      * By default, the element can be dragged any place on the screen.  Use
18947      * this method to limit the horizontal travel of the element.  Pass in
18948      * 0,0 for the parameters if you want to lock the drag to the y axis.
18949      * @method setXConstraint
18950      * @param {int} iLeft the number of pixels the element can move to the left
18951      * @param {int} iRight the number of pixels the element can move to the
18952      * right
18953      * @param {int} iTickSize optional parameter for specifying that the
18954      * element
18955      * should move iTickSize pixels at a time.
18956      */
18957     setXConstraint: function(iLeft, iRight, iTickSize) {
18958         this.leftConstraint = iLeft;
18959         this.rightConstraint = iRight;
18960
18961         this.minX = this.initPageX - iLeft;
18962         this.maxX = this.initPageX + iRight;
18963         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
18964
18965         this.constrainX = true;
18966     },
18967
18968     /**
18969      * Clears any constraints applied to this instance.  Also clears ticks
18970      * since they can't exist independent of a constraint at this time.
18971      * @method clearConstraints
18972      */
18973     clearConstraints: function() {
18974         this.constrainX = false;
18975         this.constrainY = false;
18976         this.clearTicks();
18977     },
18978
18979     /**
18980      * Clears any tick interval defined for this instance
18981      * @method clearTicks
18982      */
18983     clearTicks: function() {
18984         this.xTicks = null;
18985         this.yTicks = null;
18986         this.xTickSize = 0;
18987         this.yTickSize = 0;
18988     },
18989
18990     /**
18991      * By default, the element can be dragged any place on the screen.  Set
18992      * this to limit the vertical travel of the element.  Pass in 0,0 for the
18993      * parameters if you want to lock the drag to the x axis.
18994      * @method setYConstraint
18995      * @param {int} iUp the number of pixels the element can move up
18996      * @param {int} iDown the number of pixels the element can move down
18997      * @param {int} iTickSize optional parameter for specifying that the
18998      * element should move iTickSize pixels at a time.
18999      */
19000     setYConstraint: function(iUp, iDown, iTickSize) {
19001         this.topConstraint = iUp;
19002         this.bottomConstraint = iDown;
19003
19004         this.minY = this.initPageY - iUp;
19005         this.maxY = this.initPageY + iDown;
19006         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19007
19008         this.constrainY = true;
19009
19010     },
19011
19012     /**
19013      * resetConstraints must be called if you manually reposition a dd element.
19014      * @method resetConstraints
19015      * @param {boolean} maintainOffset
19016      */
19017     resetConstraints: function() {
19018
19019
19020         // Maintain offsets if necessary
19021         if (this.initPageX || this.initPageX === 0) {
19022             // figure out how much this thing has moved
19023             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19024             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19025
19026             this.setInitPosition(dx, dy);
19027
19028         // This is the first time we have detected the element's position
19029         } else {
19030             this.setInitPosition();
19031         }
19032
19033         if (this.constrainX) {
19034             this.setXConstraint( this.leftConstraint,
19035                                  this.rightConstraint,
19036                                  this.xTickSize        );
19037         }
19038
19039         if (this.constrainY) {
19040             this.setYConstraint( this.topConstraint,
19041                                  this.bottomConstraint,
19042                                  this.yTickSize         );
19043         }
19044     },
19045
19046     /**
19047      * Normally the drag element is moved pixel by pixel, but we can specify
19048      * that it move a number of pixels at a time.  This method resolves the
19049      * location when we have it set up like this.
19050      * @method getTick
19051      * @param {int} val where we want to place the object
19052      * @param {int[]} tickArray sorted array of valid points
19053      * @return {int} the closest tick
19054      * @private
19055      */
19056     getTick: function(val, tickArray) {
19057
19058         if (!tickArray) {
19059             // If tick interval is not defined, it is effectively 1 pixel,
19060             // so we return the value passed to us.
19061             return val;
19062         } else if (tickArray[0] >= val) {
19063             // The value is lower than the first tick, so we return the first
19064             // tick.
19065             return tickArray[0];
19066         } else {
19067             for (var i=0, len=tickArray.length; i<len; ++i) {
19068                 var next = i + 1;
19069                 if (tickArray[next] && tickArray[next] >= val) {
19070                     var diff1 = val - tickArray[i];
19071                     var diff2 = tickArray[next] - val;
19072                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19073                 }
19074             }
19075
19076             // The value is larger than the last tick, so we return the last
19077             // tick.
19078             return tickArray[tickArray.length - 1];
19079         }
19080     },
19081
19082     /**
19083      * toString method
19084      * @method toString
19085      * @return {string} string representation of the dd obj
19086      */
19087     toString: function() {
19088         return ("DragDrop " + this.id);
19089     }
19090
19091 });
19092
19093 })();
19094 /*
19095  * Based on:
19096  * Ext JS Library 1.1.1
19097  * Copyright(c) 2006-2007, Ext JS, LLC.
19098  *
19099  * Originally Released Under LGPL - original licence link has changed is not relivant.
19100  *
19101  * Fork - LGPL
19102  * <script type="text/javascript">
19103  */
19104
19105
19106 /**
19107  * The drag and drop utility provides a framework for building drag and drop
19108  * applications.  In addition to enabling drag and drop for specific elements,
19109  * the drag and drop elements are tracked by the manager class, and the
19110  * interactions between the various elements are tracked during the drag and
19111  * the implementing code is notified about these important moments.
19112  */
19113
19114 // Only load the library once.  Rewriting the manager class would orphan
19115 // existing drag and drop instances.
19116 if (!Roo.dd.DragDropMgr) {
19117
19118 /**
19119  * @class Roo.dd.DragDropMgr
19120  * DragDropMgr is a singleton that tracks the element interaction for
19121  * all DragDrop items in the window.  Generally, you will not call
19122  * this class directly, but it does have helper methods that could
19123  * be useful in your DragDrop implementations.
19124  * @singleton
19125  */
19126 Roo.dd.DragDropMgr = function() {
19127
19128     var Event = Roo.EventManager;
19129
19130     return {
19131
19132         /**
19133          * Two dimensional Array of registered DragDrop objects.  The first
19134          * dimension is the DragDrop item group, the second the DragDrop
19135          * object.
19136          * @property ids
19137          * @type {string: string}
19138          * @private
19139          * @static
19140          */
19141         ids: {},
19142
19143         /**
19144          * Array of element ids defined as drag handles.  Used to determine
19145          * if the element that generated the mousedown event is actually the
19146          * handle and not the html element itself.
19147          * @property handleIds
19148          * @type {string: string}
19149          * @private
19150          * @static
19151          */
19152         handleIds: {},
19153
19154         /**
19155          * the DragDrop object that is currently being dragged
19156          * @property dragCurrent
19157          * @type DragDrop
19158          * @private
19159          * @static
19160          **/
19161         dragCurrent: null,
19162
19163         /**
19164          * the DragDrop object(s) that are being hovered over
19165          * @property dragOvers
19166          * @type Array
19167          * @private
19168          * @static
19169          */
19170         dragOvers: {},
19171
19172         /**
19173          * the X distance between the cursor and the object being dragged
19174          * @property deltaX
19175          * @type int
19176          * @private
19177          * @static
19178          */
19179         deltaX: 0,
19180
19181         /**
19182          * the Y distance between the cursor and the object being dragged
19183          * @property deltaY
19184          * @type int
19185          * @private
19186          * @static
19187          */
19188         deltaY: 0,
19189
19190         /**
19191          * Flag to determine if we should prevent the default behavior of the
19192          * events we define. By default this is true, but this can be set to
19193          * false if you need the default behavior (not recommended)
19194          * @property preventDefault
19195          * @type boolean
19196          * @static
19197          */
19198         preventDefault: true,
19199
19200         /**
19201          * Flag to determine if we should stop the propagation of the events
19202          * we generate. This is true by default but you may want to set it to
19203          * false if the html element contains other features that require the
19204          * mouse click.
19205          * @property stopPropagation
19206          * @type boolean
19207          * @static
19208          */
19209         stopPropagation: true,
19210
19211         /**
19212          * Internal flag that is set to true when drag and drop has been
19213          * intialized
19214          * @property initialized
19215          * @private
19216          * @static
19217          */
19218         initalized: false,
19219
19220         /**
19221          * All drag and drop can be disabled.
19222          * @property locked
19223          * @private
19224          * @static
19225          */
19226         locked: false,
19227
19228         /**
19229          * Called the first time an element is registered.
19230          * @method init
19231          * @private
19232          * @static
19233          */
19234         init: function() {
19235             this.initialized = true;
19236         },
19237
19238         /**
19239          * In point mode, drag and drop interaction is defined by the
19240          * location of the cursor during the drag/drop
19241          * @property POINT
19242          * @type int
19243          * @static
19244          */
19245         POINT: 0,
19246
19247         /**
19248          * In intersect mode, drag and drop interactio nis defined by the
19249          * overlap of two or more drag and drop objects.
19250          * @property INTERSECT
19251          * @type int
19252          * @static
19253          */
19254         INTERSECT: 1,
19255
19256         /**
19257          * The current drag and drop mode.  Default: POINT
19258          * @property mode
19259          * @type int
19260          * @static
19261          */
19262         mode: 0,
19263
19264         /**
19265          * Runs method on all drag and drop objects
19266          * @method _execOnAll
19267          * @private
19268          * @static
19269          */
19270         _execOnAll: function(sMethod, args) {
19271             for (var i in this.ids) {
19272                 for (var j in this.ids[i]) {
19273                     var oDD = this.ids[i][j];
19274                     if (! this.isTypeOfDD(oDD)) {
19275                         continue;
19276                     }
19277                     oDD[sMethod].apply(oDD, args);
19278                 }
19279             }
19280         },
19281
19282         /**
19283          * Drag and drop initialization.  Sets up the global event handlers
19284          * @method _onLoad
19285          * @private
19286          * @static
19287          */
19288         _onLoad: function() {
19289
19290             this.init();
19291
19292             if (!Roo.isTouch) {
19293                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19294                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19295             }
19296             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19297             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19298             
19299             Event.on(window,   "unload",    this._onUnload, this, true);
19300             Event.on(window,   "resize",    this._onResize, this, true);
19301             // Event.on(window,   "mouseout",    this._test);
19302
19303         },
19304
19305         /**
19306          * Reset constraints on all drag and drop objs
19307          * @method _onResize
19308          * @private
19309          * @static
19310          */
19311         _onResize: function(e) {
19312             this._execOnAll("resetConstraints", []);
19313         },
19314
19315         /**
19316          * Lock all drag and drop functionality
19317          * @method lock
19318          * @static
19319          */
19320         lock: function() { this.locked = true; },
19321
19322         /**
19323          * Unlock all drag and drop functionality
19324          * @method unlock
19325          * @static
19326          */
19327         unlock: function() { this.locked = false; },
19328
19329         /**
19330          * Is drag and drop locked?
19331          * @method isLocked
19332          * @return {boolean} True if drag and drop is locked, false otherwise.
19333          * @static
19334          */
19335         isLocked: function() { return this.locked; },
19336
19337         /**
19338          * Location cache that is set for all drag drop objects when a drag is
19339          * initiated, cleared when the drag is finished.
19340          * @property locationCache
19341          * @private
19342          * @static
19343          */
19344         locationCache: {},
19345
19346         /**
19347          * Set useCache to false if you want to force object the lookup of each
19348          * drag and drop linked element constantly during a drag.
19349          * @property useCache
19350          * @type boolean
19351          * @static
19352          */
19353         useCache: true,
19354
19355         /**
19356          * The number of pixels that the mouse needs to move after the
19357          * mousedown before the drag is initiated.  Default=3;
19358          * @property clickPixelThresh
19359          * @type int
19360          * @static
19361          */
19362         clickPixelThresh: 3,
19363
19364         /**
19365          * The number of milliseconds after the mousedown event to initiate the
19366          * drag if we don't get a mouseup event. Default=1000
19367          * @property clickTimeThresh
19368          * @type int
19369          * @static
19370          */
19371         clickTimeThresh: 350,
19372
19373         /**
19374          * Flag that indicates that either the drag pixel threshold or the
19375          * mousdown time threshold has been met
19376          * @property dragThreshMet
19377          * @type boolean
19378          * @private
19379          * @static
19380          */
19381         dragThreshMet: false,
19382
19383         /**
19384          * Timeout used for the click time threshold
19385          * @property clickTimeout
19386          * @type Object
19387          * @private
19388          * @static
19389          */
19390         clickTimeout: null,
19391
19392         /**
19393          * The X position of the mousedown event stored for later use when a
19394          * drag threshold is met.
19395          * @property startX
19396          * @type int
19397          * @private
19398          * @static
19399          */
19400         startX: 0,
19401
19402         /**
19403          * The Y position of the mousedown event stored for later use when a
19404          * drag threshold is met.
19405          * @property startY
19406          * @type int
19407          * @private
19408          * @static
19409          */
19410         startY: 0,
19411
19412         /**
19413          * Each DragDrop instance must be registered with the DragDropMgr.
19414          * This is executed in DragDrop.init()
19415          * @method regDragDrop
19416          * @param {DragDrop} oDD the DragDrop object to register
19417          * @param {String} sGroup the name of the group this element belongs to
19418          * @static
19419          */
19420         regDragDrop: function(oDD, sGroup) {
19421             if (!this.initialized) { this.init(); }
19422
19423             if (!this.ids[sGroup]) {
19424                 this.ids[sGroup] = {};
19425             }
19426             this.ids[sGroup][oDD.id] = oDD;
19427         },
19428
19429         /**
19430          * Removes the supplied dd instance from the supplied group. Executed
19431          * by DragDrop.removeFromGroup, so don't call this function directly.
19432          * @method removeDDFromGroup
19433          * @private
19434          * @static
19435          */
19436         removeDDFromGroup: function(oDD, sGroup) {
19437             if (!this.ids[sGroup]) {
19438                 this.ids[sGroup] = {};
19439             }
19440
19441             var obj = this.ids[sGroup];
19442             if (obj && obj[oDD.id]) {
19443                 delete obj[oDD.id];
19444             }
19445         },
19446
19447         /**
19448          * Unregisters a drag and drop item.  This is executed in
19449          * DragDrop.unreg, use that method instead of calling this directly.
19450          * @method _remove
19451          * @private
19452          * @static
19453          */
19454         _remove: function(oDD) {
19455             for (var g in oDD.groups) {
19456                 if (g && this.ids[g][oDD.id]) {
19457                     delete this.ids[g][oDD.id];
19458                 }
19459             }
19460             delete this.handleIds[oDD.id];
19461         },
19462
19463         /**
19464          * Each DragDrop handle element must be registered.  This is done
19465          * automatically when executing DragDrop.setHandleElId()
19466          * @method regHandle
19467          * @param {String} sDDId the DragDrop id this element is a handle for
19468          * @param {String} sHandleId the id of the element that is the drag
19469          * handle
19470          * @static
19471          */
19472         regHandle: function(sDDId, sHandleId) {
19473             if (!this.handleIds[sDDId]) {
19474                 this.handleIds[sDDId] = {};
19475             }
19476             this.handleIds[sDDId][sHandleId] = sHandleId;
19477         },
19478
19479         /**
19480          * Utility function to determine if a given element has been
19481          * registered as a drag drop item.
19482          * @method isDragDrop
19483          * @param {String} id the element id to check
19484          * @return {boolean} true if this element is a DragDrop item,
19485          * false otherwise
19486          * @static
19487          */
19488         isDragDrop: function(id) {
19489             return ( this.getDDById(id) ) ? true : false;
19490         },
19491
19492         /**
19493          * Returns the drag and drop instances that are in all groups the
19494          * passed in instance belongs to.
19495          * @method getRelated
19496          * @param {DragDrop} p_oDD the obj to get related data for
19497          * @param {boolean} bTargetsOnly if true, only return targetable objs
19498          * @return {DragDrop[]} the related instances
19499          * @static
19500          */
19501         getRelated: function(p_oDD, bTargetsOnly) {
19502             var oDDs = [];
19503             for (var i in p_oDD.groups) {
19504                 for (j in this.ids[i]) {
19505                     var dd = this.ids[i][j];
19506                     if (! this.isTypeOfDD(dd)) {
19507                         continue;
19508                     }
19509                     if (!bTargetsOnly || dd.isTarget) {
19510                         oDDs[oDDs.length] = dd;
19511                     }
19512                 }
19513             }
19514
19515             return oDDs;
19516         },
19517
19518         /**
19519          * Returns true if the specified dd target is a legal target for
19520          * the specifice drag obj
19521          * @method isLegalTarget
19522          * @param {DragDrop} the drag obj
19523          * @param {DragDrop} the target
19524          * @return {boolean} true if the target is a legal target for the
19525          * dd obj
19526          * @static
19527          */
19528         isLegalTarget: function (oDD, oTargetDD) {
19529             var targets = this.getRelated(oDD, true);
19530             for (var i=0, len=targets.length;i<len;++i) {
19531                 if (targets[i].id == oTargetDD.id) {
19532                     return true;
19533                 }
19534             }
19535
19536             return false;
19537         },
19538
19539         /**
19540          * My goal is to be able to transparently determine if an object is
19541          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19542          * returns "object", oDD.constructor.toString() always returns
19543          * "DragDrop" and not the name of the subclass.  So for now it just
19544          * evaluates a well-known variable in DragDrop.
19545          * @method isTypeOfDD
19546          * @param {Object} the object to evaluate
19547          * @return {boolean} true if typeof oDD = DragDrop
19548          * @static
19549          */
19550         isTypeOfDD: function (oDD) {
19551             return (oDD && oDD.__ygDragDrop);
19552         },
19553
19554         /**
19555          * Utility function to determine if a given element has been
19556          * registered as a drag drop handle for the given Drag Drop object.
19557          * @method isHandle
19558          * @param {String} id the element id to check
19559          * @return {boolean} true if this element is a DragDrop handle, false
19560          * otherwise
19561          * @static
19562          */
19563         isHandle: function(sDDId, sHandleId) {
19564             return ( this.handleIds[sDDId] &&
19565                             this.handleIds[sDDId][sHandleId] );
19566         },
19567
19568         /**
19569          * Returns the DragDrop instance for a given id
19570          * @method getDDById
19571          * @param {String} id the id of the DragDrop object
19572          * @return {DragDrop} the drag drop object, null if it is not found
19573          * @static
19574          */
19575         getDDById: function(id) {
19576             for (var i in this.ids) {
19577                 if (this.ids[i][id]) {
19578                     return this.ids[i][id];
19579                 }
19580             }
19581             return null;
19582         },
19583
19584         /**
19585          * Fired after a registered DragDrop object gets the mousedown event.
19586          * Sets up the events required to track the object being dragged
19587          * @method handleMouseDown
19588          * @param {Event} e the event
19589          * @param oDD the DragDrop object being dragged
19590          * @private
19591          * @static
19592          */
19593         handleMouseDown: function(e, oDD) {
19594             if(Roo.QuickTips){
19595                 Roo.QuickTips.disable();
19596             }
19597             this.currentTarget = e.getTarget();
19598
19599             this.dragCurrent = oDD;
19600
19601             var el = oDD.getEl();
19602
19603             // track start position
19604             this.startX = e.getPageX();
19605             this.startY = e.getPageY();
19606
19607             this.deltaX = this.startX - el.offsetLeft;
19608             this.deltaY = this.startY - el.offsetTop;
19609
19610             this.dragThreshMet = false;
19611
19612             this.clickTimeout = setTimeout(
19613                     function() {
19614                         var DDM = Roo.dd.DDM;
19615                         DDM.startDrag(DDM.startX, DDM.startY);
19616                     },
19617                     this.clickTimeThresh );
19618         },
19619
19620         /**
19621          * Fired when either the drag pixel threshol or the mousedown hold
19622          * time threshold has been met.
19623          * @method startDrag
19624          * @param x {int} the X position of the original mousedown
19625          * @param y {int} the Y position of the original mousedown
19626          * @static
19627          */
19628         startDrag: function(x, y) {
19629             clearTimeout(this.clickTimeout);
19630             if (this.dragCurrent) {
19631                 this.dragCurrent.b4StartDrag(x, y);
19632                 this.dragCurrent.startDrag(x, y);
19633             }
19634             this.dragThreshMet = true;
19635         },
19636
19637         /**
19638          * Internal function to handle the mouseup event.  Will be invoked
19639          * from the context of the document.
19640          * @method handleMouseUp
19641          * @param {Event} e the event
19642          * @private
19643          * @static
19644          */
19645         handleMouseUp: function(e) {
19646
19647             if(Roo.QuickTips){
19648                 Roo.QuickTips.enable();
19649             }
19650             if (! this.dragCurrent) {
19651                 return;
19652             }
19653
19654             clearTimeout(this.clickTimeout);
19655
19656             if (this.dragThreshMet) {
19657                 this.fireEvents(e, true);
19658             } else {
19659             }
19660
19661             this.stopDrag(e);
19662
19663             this.stopEvent(e);
19664         },
19665
19666         /**
19667          * Utility to stop event propagation and event default, if these
19668          * features are turned on.
19669          * @method stopEvent
19670          * @param {Event} e the event as returned by this.getEvent()
19671          * @static
19672          */
19673         stopEvent: function(e){
19674             if(this.stopPropagation) {
19675                 e.stopPropagation();
19676             }
19677
19678             if (this.preventDefault) {
19679                 e.preventDefault();
19680             }
19681         },
19682
19683         /**
19684          * Internal function to clean up event handlers after the drag
19685          * operation is complete
19686          * @method stopDrag
19687          * @param {Event} e the event
19688          * @private
19689          * @static
19690          */
19691         stopDrag: function(e) {
19692             // Fire the drag end event for the item that was dragged
19693             if (this.dragCurrent) {
19694                 if (this.dragThreshMet) {
19695                     this.dragCurrent.b4EndDrag(e);
19696                     this.dragCurrent.endDrag(e);
19697                 }
19698
19699                 this.dragCurrent.onMouseUp(e);
19700             }
19701
19702             this.dragCurrent = null;
19703             this.dragOvers = {};
19704         },
19705
19706         /**
19707          * Internal function to handle the mousemove event.  Will be invoked
19708          * from the context of the html element.
19709          *
19710          * @TODO figure out what we can do about mouse events lost when the
19711          * user drags objects beyond the window boundary.  Currently we can
19712          * detect this in internet explorer by verifying that the mouse is
19713          * down during the mousemove event.  Firefox doesn't give us the
19714          * button state on the mousemove event.
19715          * @method handleMouseMove
19716          * @param {Event} e the event
19717          * @private
19718          * @static
19719          */
19720         handleMouseMove: function(e) {
19721             if (! this.dragCurrent) {
19722                 return true;
19723             }
19724
19725             // var button = e.which || e.button;
19726
19727             // check for IE mouseup outside of page boundary
19728             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19729                 this.stopEvent(e);
19730                 return this.handleMouseUp(e);
19731             }
19732
19733             if (!this.dragThreshMet) {
19734                 var diffX = Math.abs(this.startX - e.getPageX());
19735                 var diffY = Math.abs(this.startY - e.getPageY());
19736                 if (diffX > this.clickPixelThresh ||
19737                             diffY > this.clickPixelThresh) {
19738                     this.startDrag(this.startX, this.startY);
19739                 }
19740             }
19741
19742             if (this.dragThreshMet) {
19743                 this.dragCurrent.b4Drag(e);
19744                 this.dragCurrent.onDrag(e);
19745                 if(!this.dragCurrent.moveOnly){
19746                     this.fireEvents(e, false);
19747                 }
19748             }
19749
19750             this.stopEvent(e);
19751
19752             return true;
19753         },
19754
19755         /**
19756          * Iterates over all of the DragDrop elements to find ones we are
19757          * hovering over or dropping on
19758          * @method fireEvents
19759          * @param {Event} e the event
19760          * @param {boolean} isDrop is this a drop op or a mouseover op?
19761          * @private
19762          * @static
19763          */
19764         fireEvents: function(e, isDrop) {
19765             var dc = this.dragCurrent;
19766
19767             // If the user did the mouse up outside of the window, we could
19768             // get here even though we have ended the drag.
19769             if (!dc || dc.isLocked()) {
19770                 return;
19771             }
19772
19773             var pt = e.getPoint();
19774
19775             // cache the previous dragOver array
19776             var oldOvers = [];
19777
19778             var outEvts   = [];
19779             var overEvts  = [];
19780             var dropEvts  = [];
19781             var enterEvts = [];
19782
19783             // Check to see if the object(s) we were hovering over is no longer
19784             // being hovered over so we can fire the onDragOut event
19785             for (var i in this.dragOvers) {
19786
19787                 var ddo = this.dragOvers[i];
19788
19789                 if (! this.isTypeOfDD(ddo)) {
19790                     continue;
19791                 }
19792
19793                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19794                     outEvts.push( ddo );
19795                 }
19796
19797                 oldOvers[i] = true;
19798                 delete this.dragOvers[i];
19799             }
19800
19801             for (var sGroup in dc.groups) {
19802
19803                 if ("string" != typeof sGroup) {
19804                     continue;
19805                 }
19806
19807                 for (i in this.ids[sGroup]) {
19808                     var oDD = this.ids[sGroup][i];
19809                     if (! this.isTypeOfDD(oDD)) {
19810                         continue;
19811                     }
19812
19813                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19814                         if (this.isOverTarget(pt, oDD, this.mode)) {
19815                             // look for drop interactions
19816                             if (isDrop) {
19817                                 dropEvts.push( oDD );
19818                             // look for drag enter and drag over interactions
19819                             } else {
19820
19821                                 // initial drag over: dragEnter fires
19822                                 if (!oldOvers[oDD.id]) {
19823                                     enterEvts.push( oDD );
19824                                 // subsequent drag overs: dragOver fires
19825                                 } else {
19826                                     overEvts.push( oDD );
19827                                 }
19828
19829                                 this.dragOvers[oDD.id] = oDD;
19830                             }
19831                         }
19832                     }
19833                 }
19834             }
19835
19836             if (this.mode) {
19837                 if (outEvts.length) {
19838                     dc.b4DragOut(e, outEvts);
19839                     dc.onDragOut(e, outEvts);
19840                 }
19841
19842                 if (enterEvts.length) {
19843                     dc.onDragEnter(e, enterEvts);
19844                 }
19845
19846                 if (overEvts.length) {
19847                     dc.b4DragOver(e, overEvts);
19848                     dc.onDragOver(e, overEvts);
19849                 }
19850
19851                 if (dropEvts.length) {
19852                     dc.b4DragDrop(e, dropEvts);
19853                     dc.onDragDrop(e, dropEvts);
19854                 }
19855
19856             } else {
19857                 // fire dragout events
19858                 var len = 0;
19859                 for (i=0, len=outEvts.length; i<len; ++i) {
19860                     dc.b4DragOut(e, outEvts[i].id);
19861                     dc.onDragOut(e, outEvts[i].id);
19862                 }
19863
19864                 // fire enter events
19865                 for (i=0,len=enterEvts.length; i<len; ++i) {
19866                     // dc.b4DragEnter(e, oDD.id);
19867                     dc.onDragEnter(e, enterEvts[i].id);
19868                 }
19869
19870                 // fire over events
19871                 for (i=0,len=overEvts.length; i<len; ++i) {
19872                     dc.b4DragOver(e, overEvts[i].id);
19873                     dc.onDragOver(e, overEvts[i].id);
19874                 }
19875
19876                 // fire drop events
19877                 for (i=0, len=dropEvts.length; i<len; ++i) {
19878                     dc.b4DragDrop(e, dropEvts[i].id);
19879                     dc.onDragDrop(e, dropEvts[i].id);
19880                 }
19881
19882             }
19883
19884             // notify about a drop that did not find a target
19885             if (isDrop && !dropEvts.length) {
19886                 dc.onInvalidDrop(e);
19887             }
19888
19889         },
19890
19891         /**
19892          * Helper function for getting the best match from the list of drag
19893          * and drop objects returned by the drag and drop events when we are
19894          * in INTERSECT mode.  It returns either the first object that the
19895          * cursor is over, or the object that has the greatest overlap with
19896          * the dragged element.
19897          * @method getBestMatch
19898          * @param  {DragDrop[]} dds The array of drag and drop objects
19899          * targeted
19900          * @return {DragDrop}       The best single match
19901          * @static
19902          */
19903         getBestMatch: function(dds) {
19904             var winner = null;
19905             // Return null if the input is not what we expect
19906             //if (!dds || !dds.length || dds.length == 0) {
19907                // winner = null;
19908             // If there is only one item, it wins
19909             //} else if (dds.length == 1) {
19910
19911             var len = dds.length;
19912
19913             if (len == 1) {
19914                 winner = dds[0];
19915             } else {
19916                 // Loop through the targeted items
19917                 for (var i=0; i<len; ++i) {
19918                     var dd = dds[i];
19919                     // If the cursor is over the object, it wins.  If the
19920                     // cursor is over multiple matches, the first one we come
19921                     // to wins.
19922                     if (dd.cursorIsOver) {
19923                         winner = dd;
19924                         break;
19925                     // Otherwise the object with the most overlap wins
19926                     } else {
19927                         if (!winner ||
19928                             winner.overlap.getArea() < dd.overlap.getArea()) {
19929                             winner = dd;
19930                         }
19931                     }
19932                 }
19933             }
19934
19935             return winner;
19936         },
19937
19938         /**
19939          * Refreshes the cache of the top-left and bottom-right points of the
19940          * drag and drop objects in the specified group(s).  This is in the
19941          * format that is stored in the drag and drop instance, so typical
19942          * usage is:
19943          * <code>
19944          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
19945          * </code>
19946          * Alternatively:
19947          * <code>
19948          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
19949          * </code>
19950          * @TODO this really should be an indexed array.  Alternatively this
19951          * method could accept both.
19952          * @method refreshCache
19953          * @param {Object} groups an associative array of groups to refresh
19954          * @static
19955          */
19956         refreshCache: function(groups) {
19957             for (var sGroup in groups) {
19958                 if ("string" != typeof sGroup) {
19959                     continue;
19960                 }
19961                 for (var i in this.ids[sGroup]) {
19962                     var oDD = this.ids[sGroup][i];
19963
19964                     if (this.isTypeOfDD(oDD)) {
19965                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
19966                         var loc = this.getLocation(oDD);
19967                         if (loc) {
19968                             this.locationCache[oDD.id] = loc;
19969                         } else {
19970                             delete this.locationCache[oDD.id];
19971                             // this will unregister the drag and drop object if
19972                             // the element is not in a usable state
19973                             // oDD.unreg();
19974                         }
19975                     }
19976                 }
19977             }
19978         },
19979
19980         /**
19981          * This checks to make sure an element exists and is in the DOM.  The
19982          * main purpose is to handle cases where innerHTML is used to remove
19983          * drag and drop objects from the DOM.  IE provides an 'unspecified
19984          * error' when trying to access the offsetParent of such an element
19985          * @method verifyEl
19986          * @param {HTMLElement} el the element to check
19987          * @return {boolean} true if the element looks usable
19988          * @static
19989          */
19990         verifyEl: function(el) {
19991             if (el) {
19992                 var parent;
19993                 if(Roo.isIE){
19994                     try{
19995                         parent = el.offsetParent;
19996                     }catch(e){}
19997                 }else{
19998                     parent = el.offsetParent;
19999                 }
20000                 if (parent) {
20001                     return true;
20002                 }
20003             }
20004
20005             return false;
20006         },
20007
20008         /**
20009          * Returns a Region object containing the drag and drop element's position
20010          * and size, including the padding configured for it
20011          * @method getLocation
20012          * @param {DragDrop} oDD the drag and drop object to get the
20013          *                       location for
20014          * @return {Roo.lib.Region} a Region object representing the total area
20015          *                             the element occupies, including any padding
20016          *                             the instance is configured for.
20017          * @static
20018          */
20019         getLocation: function(oDD) {
20020             if (! this.isTypeOfDD(oDD)) {
20021                 return null;
20022             }
20023
20024             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20025
20026             try {
20027                 pos= Roo.lib.Dom.getXY(el);
20028             } catch (e) { }
20029
20030             if (!pos) {
20031                 return null;
20032             }
20033
20034             x1 = pos[0];
20035             x2 = x1 + el.offsetWidth;
20036             y1 = pos[1];
20037             y2 = y1 + el.offsetHeight;
20038
20039             t = y1 - oDD.padding[0];
20040             r = x2 + oDD.padding[1];
20041             b = y2 + oDD.padding[2];
20042             l = x1 - oDD.padding[3];
20043
20044             return new Roo.lib.Region( t, r, b, l );
20045         },
20046
20047         /**
20048          * Checks the cursor location to see if it over the target
20049          * @method isOverTarget
20050          * @param {Roo.lib.Point} pt The point to evaluate
20051          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20052          * @return {boolean} true if the mouse is over the target
20053          * @private
20054          * @static
20055          */
20056         isOverTarget: function(pt, oTarget, intersect) {
20057             // use cache if available
20058             var loc = this.locationCache[oTarget.id];
20059             if (!loc || !this.useCache) {
20060                 loc = this.getLocation(oTarget);
20061                 this.locationCache[oTarget.id] = loc;
20062
20063             }
20064
20065             if (!loc) {
20066                 return false;
20067             }
20068
20069             oTarget.cursorIsOver = loc.contains( pt );
20070
20071             // DragDrop is using this as a sanity check for the initial mousedown
20072             // in this case we are done.  In POINT mode, if the drag obj has no
20073             // contraints, we are also done. Otherwise we need to evaluate the
20074             // location of the target as related to the actual location of the
20075             // dragged element.
20076             var dc = this.dragCurrent;
20077             if (!dc || !dc.getTargetCoord ||
20078                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20079                 return oTarget.cursorIsOver;
20080             }
20081
20082             oTarget.overlap = null;
20083
20084             // Get the current location of the drag element, this is the
20085             // location of the mouse event less the delta that represents
20086             // where the original mousedown happened on the element.  We
20087             // need to consider constraints and ticks as well.
20088             var pos = dc.getTargetCoord(pt.x, pt.y);
20089
20090             var el = dc.getDragEl();
20091             var curRegion = new Roo.lib.Region( pos.y,
20092                                                    pos.x + el.offsetWidth,
20093                                                    pos.y + el.offsetHeight,
20094                                                    pos.x );
20095
20096             var overlap = curRegion.intersect(loc);
20097
20098             if (overlap) {
20099                 oTarget.overlap = overlap;
20100                 return (intersect) ? true : oTarget.cursorIsOver;
20101             } else {
20102                 return false;
20103             }
20104         },
20105
20106         /**
20107          * unload event handler
20108          * @method _onUnload
20109          * @private
20110          * @static
20111          */
20112         _onUnload: function(e, me) {
20113             Roo.dd.DragDropMgr.unregAll();
20114         },
20115
20116         /**
20117          * Cleans up the drag and drop events and objects.
20118          * @method unregAll
20119          * @private
20120          * @static
20121          */
20122         unregAll: function() {
20123
20124             if (this.dragCurrent) {
20125                 this.stopDrag();
20126                 this.dragCurrent = null;
20127             }
20128
20129             this._execOnAll("unreg", []);
20130
20131             for (i in this.elementCache) {
20132                 delete this.elementCache[i];
20133             }
20134
20135             this.elementCache = {};
20136             this.ids = {};
20137         },
20138
20139         /**
20140          * A cache of DOM elements
20141          * @property elementCache
20142          * @private
20143          * @static
20144          */
20145         elementCache: {},
20146
20147         /**
20148          * Get the wrapper for the DOM element specified
20149          * @method getElWrapper
20150          * @param {String} id the id of the element to get
20151          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20152          * @private
20153          * @deprecated This wrapper isn't that useful
20154          * @static
20155          */
20156         getElWrapper: function(id) {
20157             var oWrapper = this.elementCache[id];
20158             if (!oWrapper || !oWrapper.el) {
20159                 oWrapper = this.elementCache[id] =
20160                     new this.ElementWrapper(Roo.getDom(id));
20161             }
20162             return oWrapper;
20163         },
20164
20165         /**
20166          * Returns the actual DOM element
20167          * @method getElement
20168          * @param {String} id the id of the elment to get
20169          * @return {Object} The element
20170          * @deprecated use Roo.getDom instead
20171          * @static
20172          */
20173         getElement: function(id) {
20174             return Roo.getDom(id);
20175         },
20176
20177         /**
20178          * Returns the style property for the DOM element (i.e.,
20179          * document.getElById(id).style)
20180          * @method getCss
20181          * @param {String} id the id of the elment to get
20182          * @return {Object} The style property of the element
20183          * @deprecated use Roo.getDom instead
20184          * @static
20185          */
20186         getCss: function(id) {
20187             var el = Roo.getDom(id);
20188             return (el) ? el.style : null;
20189         },
20190
20191         /**
20192          * Inner class for cached elements
20193          * @class DragDropMgr.ElementWrapper
20194          * @for DragDropMgr
20195          * @private
20196          * @deprecated
20197          */
20198         ElementWrapper: function(el) {
20199                 /**
20200                  * The element
20201                  * @property el
20202                  */
20203                 this.el = el || null;
20204                 /**
20205                  * The element id
20206                  * @property id
20207                  */
20208                 this.id = this.el && el.id;
20209                 /**
20210                  * A reference to the style property
20211                  * @property css
20212                  */
20213                 this.css = this.el && el.style;
20214             },
20215
20216         /**
20217          * Returns the X position of an html element
20218          * @method getPosX
20219          * @param el the element for which to get the position
20220          * @return {int} the X coordinate
20221          * @for DragDropMgr
20222          * @deprecated use Roo.lib.Dom.getX instead
20223          * @static
20224          */
20225         getPosX: function(el) {
20226             return Roo.lib.Dom.getX(el);
20227         },
20228
20229         /**
20230          * Returns the Y position of an html element
20231          * @method getPosY
20232          * @param el the element for which to get the position
20233          * @return {int} the Y coordinate
20234          * @deprecated use Roo.lib.Dom.getY instead
20235          * @static
20236          */
20237         getPosY: function(el) {
20238             return Roo.lib.Dom.getY(el);
20239         },
20240
20241         /**
20242          * Swap two nodes.  In IE, we use the native method, for others we
20243          * emulate the IE behavior
20244          * @method swapNode
20245          * @param n1 the first node to swap
20246          * @param n2 the other node to swap
20247          * @static
20248          */
20249         swapNode: function(n1, n2) {
20250             if (n1.swapNode) {
20251                 n1.swapNode(n2);
20252             } else {
20253                 var p = n2.parentNode;
20254                 var s = n2.nextSibling;
20255
20256                 if (s == n1) {
20257                     p.insertBefore(n1, n2);
20258                 } else if (n2 == n1.nextSibling) {
20259                     p.insertBefore(n2, n1);
20260                 } else {
20261                     n1.parentNode.replaceChild(n2, n1);
20262                     p.insertBefore(n1, s);
20263                 }
20264             }
20265         },
20266
20267         /**
20268          * Returns the current scroll position
20269          * @method getScroll
20270          * @private
20271          * @static
20272          */
20273         getScroll: function () {
20274             var t, l, dde=document.documentElement, db=document.body;
20275             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20276                 t = dde.scrollTop;
20277                 l = dde.scrollLeft;
20278             } else if (db) {
20279                 t = db.scrollTop;
20280                 l = db.scrollLeft;
20281             } else {
20282
20283             }
20284             return { top: t, left: l };
20285         },
20286
20287         /**
20288          * Returns the specified element style property
20289          * @method getStyle
20290          * @param {HTMLElement} el          the element
20291          * @param {string}      styleProp   the style property
20292          * @return {string} The value of the style property
20293          * @deprecated use Roo.lib.Dom.getStyle
20294          * @static
20295          */
20296         getStyle: function(el, styleProp) {
20297             return Roo.fly(el).getStyle(styleProp);
20298         },
20299
20300         /**
20301          * Gets the scrollTop
20302          * @method getScrollTop
20303          * @return {int} the document's scrollTop
20304          * @static
20305          */
20306         getScrollTop: function () { return this.getScroll().top; },
20307
20308         /**
20309          * Gets the scrollLeft
20310          * @method getScrollLeft
20311          * @return {int} the document's scrollTop
20312          * @static
20313          */
20314         getScrollLeft: function () { return this.getScroll().left; },
20315
20316         /**
20317          * Sets the x/y position of an element to the location of the
20318          * target element.
20319          * @method moveToEl
20320          * @param {HTMLElement} moveEl      The element to move
20321          * @param {HTMLElement} targetEl    The position reference element
20322          * @static
20323          */
20324         moveToEl: function (moveEl, targetEl) {
20325             var aCoord = Roo.lib.Dom.getXY(targetEl);
20326             Roo.lib.Dom.setXY(moveEl, aCoord);
20327         },
20328
20329         /**
20330          * Numeric array sort function
20331          * @method numericSort
20332          * @static
20333          */
20334         numericSort: function(a, b) { return (a - b); },
20335
20336         /**
20337          * Internal counter
20338          * @property _timeoutCount
20339          * @private
20340          * @static
20341          */
20342         _timeoutCount: 0,
20343
20344         /**
20345          * Trying to make the load order less important.  Without this we get
20346          * an error if this file is loaded before the Event Utility.
20347          * @method _addListeners
20348          * @private
20349          * @static
20350          */
20351         _addListeners: function() {
20352             var DDM = Roo.dd.DDM;
20353             if ( Roo.lib.Event && document ) {
20354                 DDM._onLoad();
20355             } else {
20356                 if (DDM._timeoutCount > 2000) {
20357                 } else {
20358                     setTimeout(DDM._addListeners, 10);
20359                     if (document && document.body) {
20360                         DDM._timeoutCount += 1;
20361                     }
20362                 }
20363             }
20364         },
20365
20366         /**
20367          * Recursively searches the immediate parent and all child nodes for
20368          * the handle element in order to determine wheter or not it was
20369          * clicked.
20370          * @method handleWasClicked
20371          * @param node the html element to inspect
20372          * @static
20373          */
20374         handleWasClicked: function(node, id) {
20375             if (this.isHandle(id, node.id)) {
20376                 return true;
20377             } else {
20378                 // check to see if this is a text node child of the one we want
20379                 var p = node.parentNode;
20380
20381                 while (p) {
20382                     if (this.isHandle(id, p.id)) {
20383                         return true;
20384                     } else {
20385                         p = p.parentNode;
20386                     }
20387                 }
20388             }
20389
20390             return false;
20391         }
20392
20393     };
20394
20395 }();
20396
20397 // shorter alias, save a few bytes
20398 Roo.dd.DDM = Roo.dd.DragDropMgr;
20399 Roo.dd.DDM._addListeners();
20400
20401 }/*
20402  * Based on:
20403  * Ext JS Library 1.1.1
20404  * Copyright(c) 2006-2007, Ext JS, LLC.
20405  *
20406  * Originally Released Under LGPL - original licence link has changed is not relivant.
20407  *
20408  * Fork - LGPL
20409  * <script type="text/javascript">
20410  */
20411
20412 /**
20413  * @class Roo.dd.DD
20414  * A DragDrop implementation where the linked element follows the
20415  * mouse cursor during a drag.
20416  * @extends Roo.dd.DragDrop
20417  * @constructor
20418  * @param {String} id the id of the linked element
20419  * @param {String} sGroup the group of related DragDrop items
20420  * @param {object} config an object containing configurable attributes
20421  *                Valid properties for DD:
20422  *                    scroll
20423  */
20424 Roo.dd.DD = function(id, sGroup, config) {
20425     if (id) {
20426         this.init(id, sGroup, config);
20427     }
20428 };
20429
20430 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20431
20432     /**
20433      * When set to true, the utility automatically tries to scroll the browser
20434      * window wehn a drag and drop element is dragged near the viewport boundary.
20435      * Defaults to true.
20436      * @property scroll
20437      * @type boolean
20438      */
20439     scroll: true,
20440
20441     /**
20442      * Sets the pointer offset to the distance between the linked element's top
20443      * left corner and the location the element was clicked
20444      * @method autoOffset
20445      * @param {int} iPageX the X coordinate of the click
20446      * @param {int} iPageY the Y coordinate of the click
20447      */
20448     autoOffset: function(iPageX, iPageY) {
20449         var x = iPageX - this.startPageX;
20450         var y = iPageY - this.startPageY;
20451         this.setDelta(x, y);
20452     },
20453
20454     /**
20455      * Sets the pointer offset.  You can call this directly to force the
20456      * offset to be in a particular location (e.g., pass in 0,0 to set it
20457      * to the center of the object)
20458      * @method setDelta
20459      * @param {int} iDeltaX the distance from the left
20460      * @param {int} iDeltaY the distance from the top
20461      */
20462     setDelta: function(iDeltaX, iDeltaY) {
20463         this.deltaX = iDeltaX;
20464         this.deltaY = iDeltaY;
20465     },
20466
20467     /**
20468      * Sets the drag element to the location of the mousedown or click event,
20469      * maintaining the cursor location relative to the location on the element
20470      * that was clicked.  Override this if you want to place the element in a
20471      * location other than where the cursor is.
20472      * @method setDragElPos
20473      * @param {int} iPageX the X coordinate of the mousedown or drag event
20474      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20475      */
20476     setDragElPos: function(iPageX, iPageY) {
20477         // the first time we do this, we are going to check to make sure
20478         // the element has css positioning
20479
20480         var el = this.getDragEl();
20481         this.alignElWithMouse(el, iPageX, iPageY);
20482     },
20483
20484     /**
20485      * Sets the element to the location of the mousedown or click event,
20486      * maintaining the cursor location relative to the location on the element
20487      * that was clicked.  Override this if you want to place the element in a
20488      * location other than where the cursor is.
20489      * @method alignElWithMouse
20490      * @param {HTMLElement} el the element to move
20491      * @param {int} iPageX the X coordinate of the mousedown or drag event
20492      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20493      */
20494     alignElWithMouse: function(el, iPageX, iPageY) {
20495         var oCoord = this.getTargetCoord(iPageX, iPageY);
20496         var fly = el.dom ? el : Roo.fly(el);
20497         if (!this.deltaSetXY) {
20498             var aCoord = [oCoord.x, oCoord.y];
20499             fly.setXY(aCoord);
20500             var newLeft = fly.getLeft(true);
20501             var newTop  = fly.getTop(true);
20502             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20503         } else {
20504             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20505         }
20506
20507         this.cachePosition(oCoord.x, oCoord.y);
20508         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20509         return oCoord;
20510     },
20511
20512     /**
20513      * Saves the most recent position so that we can reset the constraints and
20514      * tick marks on-demand.  We need to know this so that we can calculate the
20515      * number of pixels the element is offset from its original position.
20516      * @method cachePosition
20517      * @param iPageX the current x position (optional, this just makes it so we
20518      * don't have to look it up again)
20519      * @param iPageY the current y position (optional, this just makes it so we
20520      * don't have to look it up again)
20521      */
20522     cachePosition: function(iPageX, iPageY) {
20523         if (iPageX) {
20524             this.lastPageX = iPageX;
20525             this.lastPageY = iPageY;
20526         } else {
20527             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20528             this.lastPageX = aCoord[0];
20529             this.lastPageY = aCoord[1];
20530         }
20531     },
20532
20533     /**
20534      * Auto-scroll the window if the dragged object has been moved beyond the
20535      * visible window boundary.
20536      * @method autoScroll
20537      * @param {int} x the drag element's x position
20538      * @param {int} y the drag element's y position
20539      * @param {int} h the height of the drag element
20540      * @param {int} w the width of the drag element
20541      * @private
20542      */
20543     autoScroll: function(x, y, h, w) {
20544
20545         if (this.scroll) {
20546             // The client height
20547             var clientH = Roo.lib.Dom.getViewWidth();
20548
20549             // The client width
20550             var clientW = Roo.lib.Dom.getViewHeight();
20551
20552             // The amt scrolled down
20553             var st = this.DDM.getScrollTop();
20554
20555             // The amt scrolled right
20556             var sl = this.DDM.getScrollLeft();
20557
20558             // Location of the bottom of the element
20559             var bot = h + y;
20560
20561             // Location of the right of the element
20562             var right = w + x;
20563
20564             // The distance from the cursor to the bottom of the visible area,
20565             // adjusted so that we don't scroll if the cursor is beyond the
20566             // element drag constraints
20567             var toBot = (clientH + st - y - this.deltaY);
20568
20569             // The distance from the cursor to the right of the visible area
20570             var toRight = (clientW + sl - x - this.deltaX);
20571
20572
20573             // How close to the edge the cursor must be before we scroll
20574             // var thresh = (document.all) ? 100 : 40;
20575             var thresh = 40;
20576
20577             // How many pixels to scroll per autoscroll op.  This helps to reduce
20578             // clunky scrolling. IE is more sensitive about this ... it needs this
20579             // value to be higher.
20580             var scrAmt = (document.all) ? 80 : 30;
20581
20582             // Scroll down if we are near the bottom of the visible page and the
20583             // obj extends below the crease
20584             if ( bot > clientH && toBot < thresh ) {
20585                 window.scrollTo(sl, st + scrAmt);
20586             }
20587
20588             // Scroll up if the window is scrolled down and the top of the object
20589             // goes above the top border
20590             if ( y < st && st > 0 && y - st < thresh ) {
20591                 window.scrollTo(sl, st - scrAmt);
20592             }
20593
20594             // Scroll right if the obj is beyond the right border and the cursor is
20595             // near the border.
20596             if ( right > clientW && toRight < thresh ) {
20597                 window.scrollTo(sl + scrAmt, st);
20598             }
20599
20600             // Scroll left if the window has been scrolled to the right and the obj
20601             // extends past the left border
20602             if ( x < sl && sl > 0 && x - sl < thresh ) {
20603                 window.scrollTo(sl - scrAmt, st);
20604             }
20605         }
20606     },
20607
20608     /**
20609      * Finds the location the element should be placed if we want to move
20610      * it to where the mouse location less the click offset would place us.
20611      * @method getTargetCoord
20612      * @param {int} iPageX the X coordinate of the click
20613      * @param {int} iPageY the Y coordinate of the click
20614      * @return an object that contains the coordinates (Object.x and Object.y)
20615      * @private
20616      */
20617     getTargetCoord: function(iPageX, iPageY) {
20618
20619
20620         var x = iPageX - this.deltaX;
20621         var y = iPageY - this.deltaY;
20622
20623         if (this.constrainX) {
20624             if (x < this.minX) { x = this.minX; }
20625             if (x > this.maxX) { x = this.maxX; }
20626         }
20627
20628         if (this.constrainY) {
20629             if (y < this.minY) { y = this.minY; }
20630             if (y > this.maxY) { y = this.maxY; }
20631         }
20632
20633         x = this.getTick(x, this.xTicks);
20634         y = this.getTick(y, this.yTicks);
20635
20636
20637         return {x:x, y:y};
20638     },
20639
20640     /*
20641      * Sets up config options specific to this class. Overrides
20642      * Roo.dd.DragDrop, but all versions of this method through the
20643      * inheritance chain are called
20644      */
20645     applyConfig: function() {
20646         Roo.dd.DD.superclass.applyConfig.call(this);
20647         this.scroll = (this.config.scroll !== false);
20648     },
20649
20650     /*
20651      * Event that fires prior to the onMouseDown event.  Overrides
20652      * Roo.dd.DragDrop.
20653      */
20654     b4MouseDown: function(e) {
20655         // this.resetConstraints();
20656         this.autoOffset(e.getPageX(),
20657                             e.getPageY());
20658     },
20659
20660     /*
20661      * Event that fires prior to the onDrag event.  Overrides
20662      * Roo.dd.DragDrop.
20663      */
20664     b4Drag: function(e) {
20665         this.setDragElPos(e.getPageX(),
20666                             e.getPageY());
20667     },
20668
20669     toString: function() {
20670         return ("DD " + this.id);
20671     }
20672
20673     //////////////////////////////////////////////////////////////////////////
20674     // Debugging ygDragDrop events that can be overridden
20675     //////////////////////////////////////////////////////////////////////////
20676     /*
20677     startDrag: function(x, y) {
20678     },
20679
20680     onDrag: function(e) {
20681     },
20682
20683     onDragEnter: function(e, id) {
20684     },
20685
20686     onDragOver: function(e, id) {
20687     },
20688
20689     onDragOut: function(e, id) {
20690     },
20691
20692     onDragDrop: function(e, id) {
20693     },
20694
20695     endDrag: function(e) {
20696     }
20697
20698     */
20699
20700 });/*
20701  * Based on:
20702  * Ext JS Library 1.1.1
20703  * Copyright(c) 2006-2007, Ext JS, LLC.
20704  *
20705  * Originally Released Under LGPL - original licence link has changed is not relivant.
20706  *
20707  * Fork - LGPL
20708  * <script type="text/javascript">
20709  */
20710
20711 /**
20712  * @class Roo.dd.DDProxy
20713  * A DragDrop implementation that inserts an empty, bordered div into
20714  * the document that follows the cursor during drag operations.  At the time of
20715  * the click, the frame div is resized to the dimensions of the linked html
20716  * element, and moved to the exact location of the linked element.
20717  *
20718  * References to the "frame" element refer to the single proxy element that
20719  * was created to be dragged in place of all DDProxy elements on the
20720  * page.
20721  *
20722  * @extends Roo.dd.DD
20723  * @constructor
20724  * @param {String} id the id of the linked html element
20725  * @param {String} sGroup the group of related DragDrop objects
20726  * @param {object} config an object containing configurable attributes
20727  *                Valid properties for DDProxy in addition to those in DragDrop:
20728  *                   resizeFrame, centerFrame, dragElId
20729  */
20730 Roo.dd.DDProxy = function(id, sGroup, config) {
20731     if (id) {
20732         this.init(id, sGroup, config);
20733         this.initFrame();
20734     }
20735 };
20736
20737 /**
20738  * The default drag frame div id
20739  * @property Roo.dd.DDProxy.dragElId
20740  * @type String
20741  * @static
20742  */
20743 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20744
20745 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20746
20747     /**
20748      * By default we resize the drag frame to be the same size as the element
20749      * we want to drag (this is to get the frame effect).  We can turn it off
20750      * if we want a different behavior.
20751      * @property resizeFrame
20752      * @type boolean
20753      */
20754     resizeFrame: true,
20755
20756     /**
20757      * By default the frame is positioned exactly where the drag element is, so
20758      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20759      * you do not have constraints on the obj is to have the drag frame centered
20760      * around the cursor.  Set centerFrame to true for this effect.
20761      * @property centerFrame
20762      * @type boolean
20763      */
20764     centerFrame: false,
20765
20766     /**
20767      * Creates the proxy element if it does not yet exist
20768      * @method createFrame
20769      */
20770     createFrame: function() {
20771         var self = this;
20772         var body = document.body;
20773
20774         if (!body || !body.firstChild) {
20775             setTimeout( function() { self.createFrame(); }, 50 );
20776             return;
20777         }
20778
20779         var div = this.getDragEl();
20780
20781         if (!div) {
20782             div    = document.createElement("div");
20783             div.id = this.dragElId;
20784             var s  = div.style;
20785
20786             s.position   = "absolute";
20787             s.visibility = "hidden";
20788             s.cursor     = "move";
20789             s.border     = "2px solid #aaa";
20790             s.zIndex     = 999;
20791
20792             // appendChild can blow up IE if invoked prior to the window load event
20793             // while rendering a table.  It is possible there are other scenarios
20794             // that would cause this to happen as well.
20795             body.insertBefore(div, body.firstChild);
20796         }
20797     },
20798
20799     /**
20800      * Initialization for the drag frame element.  Must be called in the
20801      * constructor of all subclasses
20802      * @method initFrame
20803      */
20804     initFrame: function() {
20805         this.createFrame();
20806     },
20807
20808     applyConfig: function() {
20809         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20810
20811         this.resizeFrame = (this.config.resizeFrame !== false);
20812         this.centerFrame = (this.config.centerFrame);
20813         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20814     },
20815
20816     /**
20817      * Resizes the drag frame to the dimensions of the clicked object, positions
20818      * it over the object, and finally displays it
20819      * @method showFrame
20820      * @param {int} iPageX X click position
20821      * @param {int} iPageY Y click position
20822      * @private
20823      */
20824     showFrame: function(iPageX, iPageY) {
20825         var el = this.getEl();
20826         var dragEl = this.getDragEl();
20827         var s = dragEl.style;
20828
20829         this._resizeProxy();
20830
20831         if (this.centerFrame) {
20832             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20833                            Math.round(parseInt(s.height, 10)/2) );
20834         }
20835
20836         this.setDragElPos(iPageX, iPageY);
20837
20838         Roo.fly(dragEl).show();
20839     },
20840
20841     /**
20842      * The proxy is automatically resized to the dimensions of the linked
20843      * element when a drag is initiated, unless resizeFrame is set to false
20844      * @method _resizeProxy
20845      * @private
20846      */
20847     _resizeProxy: function() {
20848         if (this.resizeFrame) {
20849             var el = this.getEl();
20850             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
20851         }
20852     },
20853
20854     // overrides Roo.dd.DragDrop
20855     b4MouseDown: function(e) {
20856         var x = e.getPageX();
20857         var y = e.getPageY();
20858         this.autoOffset(x, y);
20859         this.setDragElPos(x, y);
20860     },
20861
20862     // overrides Roo.dd.DragDrop
20863     b4StartDrag: function(x, y) {
20864         // show the drag frame
20865         this.showFrame(x, y);
20866     },
20867
20868     // overrides Roo.dd.DragDrop
20869     b4EndDrag: function(e) {
20870         Roo.fly(this.getDragEl()).hide();
20871     },
20872
20873     // overrides Roo.dd.DragDrop
20874     // By default we try to move the element to the last location of the frame.
20875     // This is so that the default behavior mirrors that of Roo.dd.DD.
20876     endDrag: function(e) {
20877
20878         var lel = this.getEl();
20879         var del = this.getDragEl();
20880
20881         // Show the drag frame briefly so we can get its position
20882         del.style.visibility = "";
20883
20884         this.beforeMove();
20885         // Hide the linked element before the move to get around a Safari
20886         // rendering bug.
20887         lel.style.visibility = "hidden";
20888         Roo.dd.DDM.moveToEl(lel, del);
20889         del.style.visibility = "hidden";
20890         lel.style.visibility = "";
20891
20892         this.afterDrag();
20893     },
20894
20895     beforeMove : function(){
20896
20897     },
20898
20899     afterDrag : function(){
20900
20901     },
20902
20903     toString: function() {
20904         return ("DDProxy " + this.id);
20905     }
20906
20907 });
20908 /*
20909  * Based on:
20910  * Ext JS Library 1.1.1
20911  * Copyright(c) 2006-2007, Ext JS, LLC.
20912  *
20913  * Originally Released Under LGPL - original licence link has changed is not relivant.
20914  *
20915  * Fork - LGPL
20916  * <script type="text/javascript">
20917  */
20918
20919  /**
20920  * @class Roo.dd.DDTarget
20921  * A DragDrop implementation that does not move, but can be a drop
20922  * target.  You would get the same result by simply omitting implementation
20923  * for the event callbacks, but this way we reduce the processing cost of the
20924  * event listener and the callbacks.
20925  * @extends Roo.dd.DragDrop
20926  * @constructor
20927  * @param {String} id the id of the element that is a drop target
20928  * @param {String} sGroup the group of related DragDrop objects
20929  * @param {object} config an object containing configurable attributes
20930  *                 Valid properties for DDTarget in addition to those in
20931  *                 DragDrop:
20932  *                    none
20933  */
20934 Roo.dd.DDTarget = function(id, sGroup, config) {
20935     if (id) {
20936         this.initTarget(id, sGroup, config);
20937     }
20938     if (config.listeners || config.events) { 
20939        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
20940             listeners : config.listeners || {}, 
20941             events : config.events || {} 
20942         });    
20943     }
20944 };
20945
20946 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
20947 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
20948     toString: function() {
20949         return ("DDTarget " + this.id);
20950     }
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 /**
20965  * @class Roo.dd.ScrollManager
20966  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
20967  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
20968  * @singleton
20969  */
20970 Roo.dd.ScrollManager = function(){
20971     var ddm = Roo.dd.DragDropMgr;
20972     var els = {};
20973     var dragEl = null;
20974     var proc = {};
20975     
20976     
20977     
20978     var onStop = function(e){
20979         dragEl = null;
20980         clearProc();
20981     };
20982     
20983     var triggerRefresh = function(){
20984         if(ddm.dragCurrent){
20985              ddm.refreshCache(ddm.dragCurrent.groups);
20986         }
20987     };
20988     
20989     var doScroll = function(){
20990         if(ddm.dragCurrent){
20991             var dds = Roo.dd.ScrollManager;
20992             if(!dds.animate){
20993                 if(proc.el.scroll(proc.dir, dds.increment)){
20994                     triggerRefresh();
20995                 }
20996             }else{
20997                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
20998             }
20999         }
21000     };
21001     
21002     var clearProc = function(){
21003         if(proc.id){
21004             clearInterval(proc.id);
21005         }
21006         proc.id = 0;
21007         proc.el = null;
21008         proc.dir = "";
21009     };
21010     
21011     var startProc = function(el, dir){
21012          Roo.log('scroll startproc');
21013         clearProc();
21014         proc.el = el;
21015         proc.dir = dir;
21016         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21017     };
21018     
21019     var onFire = function(e, isDrop){
21020        
21021         if(isDrop || !ddm.dragCurrent){ return; }
21022         var dds = Roo.dd.ScrollManager;
21023         if(!dragEl || dragEl != ddm.dragCurrent){
21024             dragEl = ddm.dragCurrent;
21025             // refresh regions on drag start
21026             dds.refreshCache();
21027         }
21028         
21029         var xy = Roo.lib.Event.getXY(e);
21030         var pt = new Roo.lib.Point(xy[0], xy[1]);
21031         for(var id in els){
21032             var el = els[id], r = el._region;
21033             if(r && r.contains(pt) && el.isScrollable()){
21034                 if(r.bottom - pt.y <= dds.thresh){
21035                     if(proc.el != el){
21036                         startProc(el, "down");
21037                     }
21038                     return;
21039                 }else if(r.right - pt.x <= dds.thresh){
21040                     if(proc.el != el){
21041                         startProc(el, "left");
21042                     }
21043                     return;
21044                 }else if(pt.y - r.top <= dds.thresh){
21045                     if(proc.el != el){
21046                         startProc(el, "up");
21047                     }
21048                     return;
21049                 }else if(pt.x - r.left <= dds.thresh){
21050                     if(proc.el != el){
21051                         startProc(el, "right");
21052                     }
21053                     return;
21054                 }
21055             }
21056         }
21057         clearProc();
21058     };
21059     
21060     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21061     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21062     
21063     return {
21064         /**
21065          * Registers new overflow element(s) to auto scroll
21066          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21067          */
21068         register : function(el){
21069             if(el instanceof Array){
21070                 for(var i = 0, len = el.length; i < len; i++) {
21071                         this.register(el[i]);
21072                 }
21073             }else{
21074                 el = Roo.get(el);
21075                 els[el.id] = el;
21076             }
21077             Roo.dd.ScrollManager.els = els;
21078         },
21079         
21080         /**
21081          * Unregisters overflow element(s) so they are no longer scrolled
21082          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21083          */
21084         unregister : function(el){
21085             if(el instanceof Array){
21086                 for(var i = 0, len = el.length; i < len; i++) {
21087                         this.unregister(el[i]);
21088                 }
21089             }else{
21090                 el = Roo.get(el);
21091                 delete els[el.id];
21092             }
21093         },
21094         
21095         /**
21096          * The number of pixels from the edge of a container the pointer needs to be to 
21097          * trigger scrolling (defaults to 25)
21098          * @type Number
21099          */
21100         thresh : 25,
21101         
21102         /**
21103          * The number of pixels to scroll in each scroll increment (defaults to 50)
21104          * @type Number
21105          */
21106         increment : 100,
21107         
21108         /**
21109          * The frequency of scrolls in milliseconds (defaults to 500)
21110          * @type Number
21111          */
21112         frequency : 500,
21113         
21114         /**
21115          * True to animate the scroll (defaults to true)
21116          * @type Boolean
21117          */
21118         animate: true,
21119         
21120         /**
21121          * The animation duration in seconds - 
21122          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21123          * @type Number
21124          */
21125         animDuration: .4,
21126         
21127         /**
21128          * Manually trigger a cache refresh.
21129          */
21130         refreshCache : function(){
21131             for(var id in els){
21132                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21133                     els[id]._region = els[id].getRegion();
21134                 }
21135             }
21136         }
21137     };
21138 }();/*
21139  * Based on:
21140  * Ext JS Library 1.1.1
21141  * Copyright(c) 2006-2007, Ext JS, LLC.
21142  *
21143  * Originally Released Under LGPL - original licence link has changed is not relivant.
21144  *
21145  * Fork - LGPL
21146  * <script type="text/javascript">
21147  */
21148  
21149
21150 /**
21151  * @class Roo.dd.Registry
21152  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21153  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21154  * @singleton
21155  */
21156 Roo.dd.Registry = function(){
21157     var elements = {}; 
21158     var handles = {}; 
21159     var autoIdSeed = 0;
21160
21161     var getId = function(el, autogen){
21162         if(typeof el == "string"){
21163             return el;
21164         }
21165         var id = el.id;
21166         if(!id && autogen !== false){
21167             id = "roodd-" + (++autoIdSeed);
21168             el.id = id;
21169         }
21170         return id;
21171     };
21172     
21173     return {
21174     /**
21175      * Register a drag drop element
21176      * @param {String|HTMLElement} element The id or DOM node to register
21177      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21178      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21179      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21180      * populated in the data object (if applicable):
21181      * <pre>
21182 Value      Description<br />
21183 ---------  ------------------------------------------<br />
21184 handles    Array of DOM nodes that trigger dragging<br />
21185            for the element being registered<br />
21186 isHandle   True if the element passed in triggers<br />
21187            dragging itself, else false
21188 </pre>
21189      */
21190         register : function(el, data){
21191             data = data || {};
21192             if(typeof el == "string"){
21193                 el = document.getElementById(el);
21194             }
21195             data.ddel = el;
21196             elements[getId(el)] = data;
21197             if(data.isHandle !== false){
21198                 handles[data.ddel.id] = data;
21199             }
21200             if(data.handles){
21201                 var hs = data.handles;
21202                 for(var i = 0, len = hs.length; i < len; i++){
21203                         handles[getId(hs[i])] = data;
21204                 }
21205             }
21206         },
21207
21208     /**
21209      * Unregister a drag drop element
21210      * @param {String|HTMLElement}  element The id or DOM node to unregister
21211      */
21212         unregister : function(el){
21213             var id = getId(el, false);
21214             var data = elements[id];
21215             if(data){
21216                 delete elements[id];
21217                 if(data.handles){
21218                     var hs = data.handles;
21219                     for(var i = 0, len = hs.length; i < len; i++){
21220                         delete handles[getId(hs[i], false)];
21221                     }
21222                 }
21223             }
21224         },
21225
21226     /**
21227      * Returns the handle registered for a DOM Node by id
21228      * @param {String|HTMLElement} id The DOM node or id to look up
21229      * @return {Object} handle The custom handle data
21230      */
21231         getHandle : function(id){
21232             if(typeof id != "string"){ // must be element?
21233                 id = id.id;
21234             }
21235             return handles[id];
21236         },
21237
21238     /**
21239      * Returns the handle that is registered for the DOM node that is the target of the event
21240      * @param {Event} e The event
21241      * @return {Object} handle The custom handle data
21242      */
21243         getHandleFromEvent : function(e){
21244             var t = Roo.lib.Event.getTarget(e);
21245             return t ? handles[t.id] : null;
21246         },
21247
21248     /**
21249      * Returns a custom data object that is registered for a DOM node by id
21250      * @param {String|HTMLElement} id The DOM node or id to look up
21251      * @return {Object} data The custom data
21252      */
21253         getTarget : function(id){
21254             if(typeof id != "string"){ // must be element?
21255                 id = id.id;
21256             }
21257             return elements[id];
21258         },
21259
21260     /**
21261      * Returns a custom data object that is registered for the DOM node that is the target of the event
21262      * @param {Event} e The event
21263      * @return {Object} data The custom data
21264      */
21265         getTargetFromEvent : function(e){
21266             var t = Roo.lib.Event.getTarget(e);
21267             return t ? elements[t.id] || handles[t.id] : null;
21268         }
21269     };
21270 }();/*
21271  * Based on:
21272  * Ext JS Library 1.1.1
21273  * Copyright(c) 2006-2007, Ext JS, LLC.
21274  *
21275  * Originally Released Under LGPL - original licence link has changed is not relivant.
21276  *
21277  * Fork - LGPL
21278  * <script type="text/javascript">
21279  */
21280  
21281
21282 /**
21283  * @class Roo.dd.StatusProxy
21284  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21285  * default drag proxy used by all Roo.dd components.
21286  * @constructor
21287  * @param {Object} config
21288  */
21289 Roo.dd.StatusProxy = function(config){
21290     Roo.apply(this, config);
21291     this.id = this.id || Roo.id();
21292     this.el = new Roo.Layer({
21293         dh: {
21294             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21295                 {tag: "div", cls: "x-dd-drop-icon"},
21296                 {tag: "div", cls: "x-dd-drag-ghost"}
21297             ]
21298         }, 
21299         shadow: !config || config.shadow !== false
21300     });
21301     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21302     this.dropStatus = this.dropNotAllowed;
21303 };
21304
21305 Roo.dd.StatusProxy.prototype = {
21306     /**
21307      * @cfg {String} dropAllowed
21308      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21309      */
21310     dropAllowed : "x-dd-drop-ok",
21311     /**
21312      * @cfg {String} dropNotAllowed
21313      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21314      */
21315     dropNotAllowed : "x-dd-drop-nodrop",
21316
21317     /**
21318      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21319      * over the current target element.
21320      * @param {String} cssClass The css class for the new drop status indicator image
21321      */
21322     setStatus : function(cssClass){
21323         cssClass = cssClass || this.dropNotAllowed;
21324         if(this.dropStatus != cssClass){
21325             this.el.replaceClass(this.dropStatus, cssClass);
21326             this.dropStatus = cssClass;
21327         }
21328     },
21329
21330     /**
21331      * Resets the status indicator to the default dropNotAllowed value
21332      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21333      */
21334     reset : function(clearGhost){
21335         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21336         this.dropStatus = this.dropNotAllowed;
21337         if(clearGhost){
21338             this.ghost.update("");
21339         }
21340     },
21341
21342     /**
21343      * Updates the contents of the ghost element
21344      * @param {String} html The html that will replace the current innerHTML of the ghost element
21345      */
21346     update : function(html){
21347         if(typeof html == "string"){
21348             this.ghost.update(html);
21349         }else{
21350             this.ghost.update("");
21351             html.style.margin = "0";
21352             this.ghost.dom.appendChild(html);
21353         }
21354         // ensure float = none set?? cant remember why though.
21355         var el = this.ghost.dom.firstChild;
21356                 if(el){
21357                         Roo.fly(el).setStyle('float', 'none');
21358                 }
21359     },
21360     
21361     /**
21362      * Returns the underlying proxy {@link Roo.Layer}
21363      * @return {Roo.Layer} el
21364     */
21365     getEl : function(){
21366         return this.el;
21367     },
21368
21369     /**
21370      * Returns the ghost element
21371      * @return {Roo.Element} el
21372      */
21373     getGhost : function(){
21374         return this.ghost;
21375     },
21376
21377     /**
21378      * Hides the proxy
21379      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21380      */
21381     hide : function(clear){
21382         this.el.hide();
21383         if(clear){
21384             this.reset(true);
21385         }
21386     },
21387
21388     /**
21389      * Stops the repair animation if it's currently running
21390      */
21391     stop : function(){
21392         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21393             this.anim.stop();
21394         }
21395     },
21396
21397     /**
21398      * Displays this proxy
21399      */
21400     show : function(){
21401         this.el.show();
21402     },
21403
21404     /**
21405      * Force the Layer to sync its shadow and shim positions to the element
21406      */
21407     sync : function(){
21408         this.el.sync();
21409     },
21410
21411     /**
21412      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21413      * invalid drop operation by the item being dragged.
21414      * @param {Array} xy The XY position of the element ([x, y])
21415      * @param {Function} callback The function to call after the repair is complete
21416      * @param {Object} scope The scope in which to execute the callback
21417      */
21418     repair : function(xy, callback, scope){
21419         this.callback = callback;
21420         this.scope = scope;
21421         if(xy && this.animRepair !== false){
21422             this.el.addClass("x-dd-drag-repair");
21423             this.el.hideUnders(true);
21424             this.anim = this.el.shift({
21425                 duration: this.repairDuration || .5,
21426                 easing: 'easeOut',
21427                 xy: xy,
21428                 stopFx: true,
21429                 callback: this.afterRepair,
21430                 scope: this
21431             });
21432         }else{
21433             this.afterRepair();
21434         }
21435     },
21436
21437     // private
21438     afterRepair : function(){
21439         this.hide(true);
21440         if(typeof this.callback == "function"){
21441             this.callback.call(this.scope || this);
21442         }
21443         this.callback = null;
21444         this.scope = null;
21445     }
21446 };/*
21447  * Based on:
21448  * Ext JS Library 1.1.1
21449  * Copyright(c) 2006-2007, Ext JS, LLC.
21450  *
21451  * Originally Released Under LGPL - original licence link has changed is not relivant.
21452  *
21453  * Fork - LGPL
21454  * <script type="text/javascript">
21455  */
21456
21457 /**
21458  * @class Roo.dd.DragSource
21459  * @extends Roo.dd.DDProxy
21460  * A simple class that provides the basic implementation needed to make any element draggable.
21461  * @constructor
21462  * @param {String/HTMLElement/Element} el The container element
21463  * @param {Object} config
21464  */
21465 Roo.dd.DragSource = function(el, config){
21466     this.el = Roo.get(el);
21467     this.dragData = {};
21468     
21469     Roo.apply(this, config);
21470     
21471     if(!this.proxy){
21472         this.proxy = new Roo.dd.StatusProxy();
21473     }
21474
21475     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21476           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21477     
21478     this.dragging = false;
21479 };
21480
21481 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21482     /**
21483      * @cfg {String} dropAllowed
21484      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21485      */
21486     dropAllowed : "x-dd-drop-ok",
21487     /**
21488      * @cfg {String} dropNotAllowed
21489      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21490      */
21491     dropNotAllowed : "x-dd-drop-nodrop",
21492
21493     /**
21494      * Returns the data object associated with this drag source
21495      * @return {Object} data An object containing arbitrary data
21496      */
21497     getDragData : function(e){
21498         return this.dragData;
21499     },
21500
21501     // private
21502     onDragEnter : function(e, id){
21503         var target = Roo.dd.DragDropMgr.getDDById(id);
21504         this.cachedTarget = target;
21505         if(this.beforeDragEnter(target, e, id) !== false){
21506             if(target.isNotifyTarget){
21507                 var status = target.notifyEnter(this, e, this.dragData);
21508                 this.proxy.setStatus(status);
21509             }else{
21510                 this.proxy.setStatus(this.dropAllowed);
21511             }
21512             
21513             if(this.afterDragEnter){
21514                 /**
21515                  * An empty function by default, but provided so that you can perform a custom action
21516                  * when the dragged item enters the drop target by providing an implementation.
21517                  * @param {Roo.dd.DragDrop} target The drop target
21518                  * @param {Event} e The event object
21519                  * @param {String} id The id of the dragged element
21520                  * @method afterDragEnter
21521                  */
21522                 this.afterDragEnter(target, e, id);
21523             }
21524         }
21525     },
21526
21527     /**
21528      * An empty function by default, but provided so that you can perform a custom action
21529      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21530      * @param {Roo.dd.DragDrop} target The drop target
21531      * @param {Event} e The event object
21532      * @param {String} id The id of the dragged element
21533      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21534      */
21535     beforeDragEnter : function(target, e, id){
21536         return true;
21537     },
21538
21539     // private
21540     alignElWithMouse: function() {
21541         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21542         this.proxy.sync();
21543     },
21544
21545     // private
21546     onDragOver : function(e, id){
21547         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21548         if(this.beforeDragOver(target, e, id) !== false){
21549             if(target.isNotifyTarget){
21550                 var status = target.notifyOver(this, e, this.dragData);
21551                 this.proxy.setStatus(status);
21552             }
21553
21554             if(this.afterDragOver){
21555                 /**
21556                  * An empty function by default, but provided so that you can perform a custom action
21557                  * while the dragged item is over the drop target by providing an implementation.
21558                  * @param {Roo.dd.DragDrop} target The drop target
21559                  * @param {Event} e The event object
21560                  * @param {String} id The id of the dragged element
21561                  * @method afterDragOver
21562                  */
21563                 this.afterDragOver(target, e, id);
21564             }
21565         }
21566     },
21567
21568     /**
21569      * An empty function by default, but provided so that you can perform a custom action
21570      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21571      * @param {Roo.dd.DragDrop} target The drop target
21572      * @param {Event} e The event object
21573      * @param {String} id The id of the dragged element
21574      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21575      */
21576     beforeDragOver : function(target, e, id){
21577         return true;
21578     },
21579
21580     // private
21581     onDragOut : function(e, id){
21582         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21583         if(this.beforeDragOut(target, e, id) !== false){
21584             if(target.isNotifyTarget){
21585                 target.notifyOut(this, e, this.dragData);
21586             }
21587             this.proxy.reset();
21588             if(this.afterDragOut){
21589                 /**
21590                  * An empty function by default, but provided so that you can perform a custom action
21591                  * after the dragged item is dragged out of the target without dropping.
21592                  * @param {Roo.dd.DragDrop} target The drop target
21593                  * @param {Event} e The event object
21594                  * @param {String} id The id of the dragged element
21595                  * @method afterDragOut
21596                  */
21597                 this.afterDragOut(target, e, id);
21598             }
21599         }
21600         this.cachedTarget = null;
21601     },
21602
21603     /**
21604      * An empty function by default, but provided so that you can perform a custom action before the dragged
21605      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21606      * @param {Roo.dd.DragDrop} target The drop target
21607      * @param {Event} e The event object
21608      * @param {String} id The id of the dragged element
21609      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21610      */
21611     beforeDragOut : function(target, e, id){
21612         return true;
21613     },
21614     
21615     // private
21616     onDragDrop : function(e, id){
21617         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21618         if(this.beforeDragDrop(target, e, id) !== false){
21619             if(target.isNotifyTarget){
21620                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21621                     this.onValidDrop(target, e, id);
21622                 }else{
21623                     this.onInvalidDrop(target, e, id);
21624                 }
21625             }else{
21626                 this.onValidDrop(target, e, id);
21627             }
21628             
21629             if(this.afterDragDrop){
21630                 /**
21631                  * An empty function by default, but provided so that you can perform a custom action
21632                  * after a valid drag drop has occurred by providing an implementation.
21633                  * @param {Roo.dd.DragDrop} target The drop target
21634                  * @param {Event} e The event object
21635                  * @param {String} id The id of the dropped element
21636                  * @method afterDragDrop
21637                  */
21638                 this.afterDragDrop(target, e, id);
21639             }
21640         }
21641         delete this.cachedTarget;
21642     },
21643
21644     /**
21645      * An empty function by default, but provided so that you can perform a custom action before the dragged
21646      * item is dropped onto the target and optionally cancel the onDragDrop.
21647      * @param {Roo.dd.DragDrop} target The drop target
21648      * @param {Event} e The event object
21649      * @param {String} id The id of the dragged element
21650      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21651      */
21652     beforeDragDrop : function(target, e, id){
21653         return true;
21654     },
21655
21656     // private
21657     onValidDrop : function(target, e, id){
21658         this.hideProxy();
21659         if(this.afterValidDrop){
21660             /**
21661              * An empty function by default, but provided so that you can perform a custom action
21662              * after a valid drop has occurred by providing an implementation.
21663              * @param {Object} target The target DD 
21664              * @param {Event} e The event object
21665              * @param {String} id The id of the dropped element
21666              * @method afterInvalidDrop
21667              */
21668             this.afterValidDrop(target, e, id);
21669         }
21670     },
21671
21672     // private
21673     getRepairXY : function(e, data){
21674         return this.el.getXY();  
21675     },
21676
21677     // private
21678     onInvalidDrop : function(target, e, id){
21679         this.beforeInvalidDrop(target, e, id);
21680         if(this.cachedTarget){
21681             if(this.cachedTarget.isNotifyTarget){
21682                 this.cachedTarget.notifyOut(this, e, this.dragData);
21683             }
21684             this.cacheTarget = null;
21685         }
21686         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21687
21688         if(this.afterInvalidDrop){
21689             /**
21690              * An empty function by default, but provided so that you can perform a custom action
21691              * after an invalid drop has occurred by providing an implementation.
21692              * @param {Event} e The event object
21693              * @param {String} id The id of the dropped element
21694              * @method afterInvalidDrop
21695              */
21696             this.afterInvalidDrop(e, id);
21697         }
21698     },
21699
21700     // private
21701     afterRepair : function(){
21702         if(Roo.enableFx){
21703             this.el.highlight(this.hlColor || "c3daf9");
21704         }
21705         this.dragging = false;
21706     },
21707
21708     /**
21709      * An empty function by default, but provided so that you can perform a custom action after an invalid
21710      * drop has occurred.
21711      * @param {Roo.dd.DragDrop} target The drop target
21712      * @param {Event} e The event object
21713      * @param {String} id The id of the dragged element
21714      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21715      */
21716     beforeInvalidDrop : function(target, e, id){
21717         return true;
21718     },
21719
21720     // private
21721     handleMouseDown : function(e){
21722         if(this.dragging) {
21723             return;
21724         }
21725         var data = this.getDragData(e);
21726         if(data && this.onBeforeDrag(data, e) !== false){
21727             this.dragData = data;
21728             this.proxy.stop();
21729             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21730         } 
21731     },
21732
21733     /**
21734      * An empty function by default, but provided so that you can perform a custom action before the initial
21735      * drag event begins and optionally cancel it.
21736      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21737      * @param {Event} e The event object
21738      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21739      */
21740     onBeforeDrag : function(data, e){
21741         return true;
21742     },
21743
21744     /**
21745      * An empty function by default, but provided so that you can perform a custom action once the initial
21746      * drag event has begun.  The drag cannot be canceled from this function.
21747      * @param {Number} x The x position of the click on the dragged object
21748      * @param {Number} y The y position of the click on the dragged object
21749      */
21750     onStartDrag : Roo.emptyFn,
21751
21752     // private - YUI override
21753     startDrag : function(x, y){
21754         this.proxy.reset();
21755         this.dragging = true;
21756         this.proxy.update("");
21757         this.onInitDrag(x, y);
21758         this.proxy.show();
21759     },
21760
21761     // private
21762     onInitDrag : function(x, y){
21763         var clone = this.el.dom.cloneNode(true);
21764         clone.id = Roo.id(); // prevent duplicate ids
21765         this.proxy.update(clone);
21766         this.onStartDrag(x, y);
21767         return true;
21768     },
21769
21770     /**
21771      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21772      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21773      */
21774     getProxy : function(){
21775         return this.proxy;  
21776     },
21777
21778     /**
21779      * Hides the drag source's {@link Roo.dd.StatusProxy}
21780      */
21781     hideProxy : function(){
21782         this.proxy.hide();  
21783         this.proxy.reset(true);
21784         this.dragging = false;
21785     },
21786
21787     // private
21788     triggerCacheRefresh : function(){
21789         Roo.dd.DDM.refreshCache(this.groups);
21790     },
21791
21792     // private - override to prevent hiding
21793     b4EndDrag: function(e) {
21794     },
21795
21796     // private - override to prevent moving
21797     endDrag : function(e){
21798         this.onEndDrag(this.dragData, e);
21799     },
21800
21801     // private
21802     onEndDrag : function(data, e){
21803     },
21804     
21805     // private - pin to cursor
21806     autoOffset : function(x, y) {
21807         this.setDelta(-12, -20);
21808     }    
21809 });/*
21810  * Based on:
21811  * Ext JS Library 1.1.1
21812  * Copyright(c) 2006-2007, Ext JS, LLC.
21813  *
21814  * Originally Released Under LGPL - original licence link has changed is not relivant.
21815  *
21816  * Fork - LGPL
21817  * <script type="text/javascript">
21818  */
21819
21820
21821 /**
21822  * @class Roo.dd.DropTarget
21823  * @extends Roo.dd.DDTarget
21824  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21825  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21826  * @constructor
21827  * @param {String/HTMLElement/Element} el The container element
21828  * @param {Object} config
21829  */
21830 Roo.dd.DropTarget = function(el, config){
21831     this.el = Roo.get(el);
21832     
21833     var listeners = false; ;
21834     if (config && config.listeners) {
21835         listeners= config.listeners;
21836         delete config.listeners;
21837     }
21838     Roo.apply(this, config);
21839     
21840     if(this.containerScroll){
21841         Roo.dd.ScrollManager.register(this.el);
21842     }
21843     this.addEvents( {
21844          /**
21845          * @scope Roo.dd.DropTarget
21846          */
21847          
21848          /**
21849          * @event enter
21850          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
21851          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
21852          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
21853          * 
21854          * IMPORTANT : it should set this.overClass and this.dropAllowed
21855          * 
21856          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21857          * @param {Event} e The event
21858          * @param {Object} data An object containing arbitrary data supplied by the drag source
21859          */
21860         "enter" : true,
21861         
21862          /**
21863          * @event over
21864          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
21865          * This method will be called on every mouse movement while the drag source is over the drop target.
21866          * This default implementation simply returns the dropAllowed config value.
21867          * 
21868          * IMPORTANT : it should set this.dropAllowed
21869          * 
21870          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21871          * @param {Event} e The event
21872          * @param {Object} data An object containing arbitrary data supplied by the drag source
21873          
21874          */
21875         "over" : true,
21876         /**
21877          * @event out
21878          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
21879          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
21880          * overClass (if any) from the drop element.
21881          * 
21882          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21883          * @param {Event} e The event
21884          * @param {Object} data An object containing arbitrary data supplied by the drag source
21885          */
21886          "out" : true,
21887          
21888         /**
21889          * @event drop
21890          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
21891          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
21892          * implementation that does something to process the drop event and returns true so that the drag source's
21893          * repair action does not run.
21894          * 
21895          * IMPORTANT : it should set this.success
21896          * 
21897          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21898          * @param {Event} e The event
21899          * @param {Object} data An object containing arbitrary data supplied by the drag source
21900         */
21901          "drop" : true
21902     });
21903             
21904      
21905     Roo.dd.DropTarget.superclass.constructor.call(  this, 
21906         this.el.dom, 
21907         this.ddGroup || this.group,
21908         {
21909             isTarget: true,
21910             listeners : listeners || {} 
21911            
21912         
21913         }
21914     );
21915
21916 };
21917
21918 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
21919     /**
21920      * @cfg {String} overClass
21921      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
21922      */
21923      /**
21924      * @cfg {String} ddGroup
21925      * The drag drop group to handle drop events for
21926      */
21927      
21928     /**
21929      * @cfg {String} dropAllowed
21930      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21931      */
21932     dropAllowed : "x-dd-drop-ok",
21933     /**
21934      * @cfg {String} dropNotAllowed
21935      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21936      */
21937     dropNotAllowed : "x-dd-drop-nodrop",
21938     /**
21939      * @cfg {boolean} success
21940      * set this after drop listener.. 
21941      */
21942     success : false,
21943     /**
21944      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
21945      * if the drop point is valid for over/enter..
21946      */
21947     valid : false,
21948     // private
21949     isTarget : true,
21950
21951     // private
21952     isNotifyTarget : true,
21953     
21954     /**
21955      * @hide
21956      */
21957     notifyEnter : function(dd, e, data)
21958     {
21959         this.valid = true;
21960         this.fireEvent('enter', dd, e, data);
21961         if(this.overClass){
21962             this.el.addClass(this.overClass);
21963         }
21964         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
21965             this.valid ? this.dropAllowed : this.dropNotAllowed
21966         );
21967     },
21968
21969     /**
21970      * @hide
21971      */
21972     notifyOver : function(dd, e, data)
21973     {
21974         this.valid = true;
21975         this.fireEvent('over', dd, e, data);
21976         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
21977             this.valid ? this.dropAllowed : this.dropNotAllowed
21978         );
21979     },
21980
21981     /**
21982      * @hide
21983      */
21984     notifyOut : function(dd, e, data)
21985     {
21986         this.fireEvent('out', dd, e, data);
21987         if(this.overClass){
21988             this.el.removeClass(this.overClass);
21989         }
21990     },
21991
21992     /**
21993      * @hide
21994      */
21995     notifyDrop : function(dd, e, data)
21996     {
21997         this.success = false;
21998         this.fireEvent('drop', dd, e, data);
21999         return this.success;
22000     }
22001 });/*
22002  * Based on:
22003  * Ext JS Library 1.1.1
22004  * Copyright(c) 2006-2007, Ext JS, LLC.
22005  *
22006  * Originally Released Under LGPL - original licence link has changed is not relivant.
22007  *
22008  * Fork - LGPL
22009  * <script type="text/javascript">
22010  */
22011
22012
22013 /**
22014  * @class Roo.dd.DragZone
22015  * @extends Roo.dd.DragSource
22016  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22017  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22018  * @constructor
22019  * @param {String/HTMLElement/Element} el The container element
22020  * @param {Object} config
22021  */
22022 Roo.dd.DragZone = function(el, config){
22023     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22024     if(this.containerScroll){
22025         Roo.dd.ScrollManager.register(this.el);
22026     }
22027 };
22028
22029 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22030     /**
22031      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22032      * for auto scrolling during drag operations.
22033      */
22034     /**
22035      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22036      * method after a failed drop (defaults to "c3daf9" - light blue)
22037      */
22038
22039     /**
22040      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22041      * for a valid target to drag based on the mouse down. Override this method
22042      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22043      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22044      * @param {EventObject} e The mouse down event
22045      * @return {Object} The dragData
22046      */
22047     getDragData : function(e){
22048         return Roo.dd.Registry.getHandleFromEvent(e);
22049     },
22050     
22051     /**
22052      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22053      * this.dragData.ddel
22054      * @param {Number} x The x position of the click on the dragged object
22055      * @param {Number} y The y position of the click on the dragged object
22056      * @return {Boolean} true to continue the drag, false to cancel
22057      */
22058     onInitDrag : function(x, y){
22059         this.proxy.update(this.dragData.ddel.cloneNode(true));
22060         this.onStartDrag(x, y);
22061         return true;
22062     },
22063     
22064     /**
22065      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22066      */
22067     afterRepair : function(){
22068         if(Roo.enableFx){
22069             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22070         }
22071         this.dragging = false;
22072     },
22073
22074     /**
22075      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22076      * the XY of this.dragData.ddel
22077      * @param {EventObject} e The mouse up event
22078      * @return {Array} The xy location (e.g. [100, 200])
22079      */
22080     getRepairXY : function(e){
22081         return Roo.Element.fly(this.dragData.ddel).getXY();  
22082     }
22083 });/*
22084  * Based on:
22085  * Ext JS Library 1.1.1
22086  * Copyright(c) 2006-2007, Ext JS, LLC.
22087  *
22088  * Originally Released Under LGPL - original licence link has changed is not relivant.
22089  *
22090  * Fork - LGPL
22091  * <script type="text/javascript">
22092  */
22093 /**
22094  * @class Roo.dd.DropZone
22095  * @extends Roo.dd.DropTarget
22096  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22097  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22098  * @constructor
22099  * @param {String/HTMLElement/Element} el The container element
22100  * @param {Object} config
22101  */
22102 Roo.dd.DropZone = function(el, config){
22103     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22104 };
22105
22106 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22107     /**
22108      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22109      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22110      * provide your own custom lookup.
22111      * @param {Event} e The event
22112      * @return {Object} data The custom data
22113      */
22114     getTargetFromEvent : function(e){
22115         return Roo.dd.Registry.getTargetFromEvent(e);
22116     },
22117
22118     /**
22119      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22120      * that it has registered.  This method has no default implementation and should be overridden to provide
22121      * node-specific processing if necessary.
22122      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22123      * {@link #getTargetFromEvent} for this node)
22124      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22125      * @param {Event} e The event
22126      * @param {Object} data An object containing arbitrary data supplied by the drag source
22127      */
22128     onNodeEnter : function(n, dd, e, data){
22129         
22130     },
22131
22132     /**
22133      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22134      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22135      * overridden to provide the proper feedback.
22136      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22137      * {@link #getTargetFromEvent} for this node)
22138      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22139      * @param {Event} e The event
22140      * @param {Object} data An object containing arbitrary data supplied by the drag source
22141      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22142      * underlying {@link Roo.dd.StatusProxy} can be updated
22143      */
22144     onNodeOver : function(n, dd, e, data){
22145         return this.dropAllowed;
22146     },
22147
22148     /**
22149      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22150      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22151      * node-specific processing if necessary.
22152      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22153      * {@link #getTargetFromEvent} for this node)
22154      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22155      * @param {Event} e The event
22156      * @param {Object} data An object containing arbitrary data supplied by the drag source
22157      */
22158     onNodeOut : function(n, dd, e, data){
22159         
22160     },
22161
22162     /**
22163      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22164      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22165      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22166      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22167      * {@link #getTargetFromEvent} for this node)
22168      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22169      * @param {Event} e The event
22170      * @param {Object} data An object containing arbitrary data supplied by the drag source
22171      * @return {Boolean} True if the drop was valid, else false
22172      */
22173     onNodeDrop : function(n, dd, e, data){
22174         return false;
22175     },
22176
22177     /**
22178      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22179      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22180      * it should be overridden to provide the proper feedback if necessary.
22181      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22182      * @param {Event} e The event
22183      * @param {Object} data An object containing arbitrary data supplied by the drag source
22184      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22185      * underlying {@link Roo.dd.StatusProxy} can be updated
22186      */
22187     onContainerOver : function(dd, e, data){
22188         return this.dropNotAllowed;
22189     },
22190
22191     /**
22192      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22193      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22194      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22195      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22196      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22197      * @param {Event} e The event
22198      * @param {Object} data An object containing arbitrary data supplied by the drag source
22199      * @return {Boolean} True if the drop was valid, else false
22200      */
22201     onContainerDrop : function(dd, e, data){
22202         return false;
22203     },
22204
22205     /**
22206      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22207      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22208      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22209      * you should override this method and provide a custom implementation.
22210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22211      * @param {Event} e The event
22212      * @param {Object} data An object containing arbitrary data supplied by the drag source
22213      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22214      * underlying {@link Roo.dd.StatusProxy} can be updated
22215      */
22216     notifyEnter : function(dd, e, data){
22217         return this.dropNotAllowed;
22218     },
22219
22220     /**
22221      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22222      * This method will be called on every mouse movement while the drag source is over the drop zone.
22223      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22224      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22225      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22226      * registered node, it will call {@link #onContainerOver}.
22227      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22228      * @param {Event} e The event
22229      * @param {Object} data An object containing arbitrary data supplied by the drag source
22230      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22231      * underlying {@link Roo.dd.StatusProxy} can be updated
22232      */
22233     notifyOver : function(dd, e, data){
22234         var n = this.getTargetFromEvent(e);
22235         if(!n){ // not over valid drop target
22236             if(this.lastOverNode){
22237                 this.onNodeOut(this.lastOverNode, dd, e, data);
22238                 this.lastOverNode = null;
22239             }
22240             return this.onContainerOver(dd, e, data);
22241         }
22242         if(this.lastOverNode != n){
22243             if(this.lastOverNode){
22244                 this.onNodeOut(this.lastOverNode, dd, e, data);
22245             }
22246             this.onNodeEnter(n, dd, e, data);
22247             this.lastOverNode = n;
22248         }
22249         return this.onNodeOver(n, dd, e, data);
22250     },
22251
22252     /**
22253      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22254      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22255      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22256      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22257      * @param {Event} e The event
22258      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22259      */
22260     notifyOut : function(dd, e, data){
22261         if(this.lastOverNode){
22262             this.onNodeOut(this.lastOverNode, dd, e, data);
22263             this.lastOverNode = null;
22264         }
22265     },
22266
22267     /**
22268      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22269      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22270      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22271      * otherwise it will call {@link #onContainerDrop}.
22272      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22273      * @param {Event} e The event
22274      * @param {Object} data An object containing arbitrary data supplied by the drag source
22275      * @return {Boolean} True if the drop was valid, else false
22276      */
22277     notifyDrop : function(dd, e, data){
22278         if(this.lastOverNode){
22279             this.onNodeOut(this.lastOverNode, dd, e, data);
22280             this.lastOverNode = null;
22281         }
22282         var n = this.getTargetFromEvent(e);
22283         return n ?
22284             this.onNodeDrop(n, dd, e, data) :
22285             this.onContainerDrop(dd, e, data);
22286     },
22287
22288     // private
22289     triggerCacheRefresh : function(){
22290         Roo.dd.DDM.refreshCache(this.groups);
22291     }  
22292 });/*
22293  * Based on:
22294  * Ext JS Library 1.1.1
22295  * Copyright(c) 2006-2007, Ext JS, LLC.
22296  *
22297  * Originally Released Under LGPL - original licence link has changed is not relivant.
22298  *
22299  * Fork - LGPL
22300  * <script type="text/javascript">
22301  */
22302
22303
22304
22305 /*
22306  * These classes are derivatives of the similarly named classes in the YUI Library.
22307  * The original license:
22308  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
22309  * Code licensed under the BSD License:
22310  * http://developer.yahoo.net/yui/license.txt
22311  */
22312
22313 (function() {
22314
22315 var Event=Roo.EventManager;
22316 var Dom=Roo.lib.Dom;
22317
22318 /**
22319  * @class Roo.dd.DragDrop
22320  * @extends Roo.util.Observable
22321  * Defines the interface and base operation of items that that can be
22322  * dragged or can be drop targets.  It was designed to be extended, overriding
22323  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
22324  * Up to three html elements can be associated with a DragDrop instance:
22325  * <ul>
22326  * <li>linked element: the element that is passed into the constructor.
22327  * This is the element which defines the boundaries for interaction with
22328  * other DragDrop objects.</li>
22329  * <li>handle element(s): The drag operation only occurs if the element that
22330  * was clicked matches a handle element.  By default this is the linked
22331  * element, but there are times that you will want only a portion of the
22332  * linked element to initiate the drag operation, and the setHandleElId()
22333  * method provides a way to define this.</li>
22334  * <li>drag element: this represents the element that would be moved along
22335  * with the cursor during a drag operation.  By default, this is the linked
22336  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
22337  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
22338  * </li>
22339  * </ul>
22340  * This class should not be instantiated until the onload event to ensure that
22341  * the associated elements are available.
22342  * The following would define a DragDrop obj that would interact with any
22343  * other DragDrop obj in the "group1" group:
22344  * <pre>
22345  *  dd = new Roo.dd.DragDrop("div1", "group1");
22346  * </pre>
22347  * Since none of the event handlers have been implemented, nothing would
22348  * actually happen if you were to run the code above.  Normally you would
22349  * override this class or one of the default implementations, but you can
22350  * also override the methods you want on an instance of the class...
22351  * <pre>
22352  *  dd.onDragDrop = function(e, id) {
22353  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
22354  *  }
22355  * </pre>
22356  * @constructor
22357  * @param {String} id of the element that is linked to this instance
22358  * @param {String} sGroup the group of related DragDrop objects
22359  * @param {object} config an object containing configurable attributes
22360  *                Valid properties for DragDrop:
22361  *                    padding, isTarget, maintainOffset, primaryButtonOnly
22362  */
22363 Roo.dd.DragDrop = function(id, sGroup, config) {
22364     if (id) {
22365         this.init(id, sGroup, config);
22366     }
22367     
22368 };
22369
22370 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
22371
22372     /**
22373      * The id of the element associated with this object.  This is what we
22374      * refer to as the "linked element" because the size and position of
22375      * this element is used to determine when the drag and drop objects have
22376      * interacted.
22377      * @property id
22378      * @type String
22379      */
22380     id: null,
22381
22382     /**
22383      * Configuration attributes passed into the constructor
22384      * @property config
22385      * @type object
22386      */
22387     config: null,
22388
22389     /**
22390      * The id of the element that will be dragged.  By default this is same
22391      * as the linked element , but could be changed to another element. Ex:
22392      * Roo.dd.DDProxy
22393      * @property dragElId
22394      * @type String
22395      * @private
22396      */
22397     dragElId: null,
22398
22399     /**
22400      * the id of the element that initiates the drag operation.  By default
22401      * this is the linked element, but could be changed to be a child of this
22402      * element.  This lets us do things like only starting the drag when the
22403      * header element within the linked html element is clicked.
22404      * @property handleElId
22405      * @type String
22406      * @private
22407      */
22408     handleElId: null,
22409
22410     /**
22411      * An associative array of HTML tags that will be ignored if clicked.
22412      * @property invalidHandleTypes
22413      * @type {string: string}
22414      */
22415     invalidHandleTypes: null,
22416
22417     /**
22418      * An associative array of ids for elements that will be ignored if clicked
22419      * @property invalidHandleIds
22420      * @type {string: string}
22421      */
22422     invalidHandleIds: null,
22423
22424     /**
22425      * An indexted array of css class names for elements that will be ignored
22426      * if clicked.
22427      * @property invalidHandleClasses
22428      * @type string[]
22429      */
22430     invalidHandleClasses: null,
22431
22432     /**
22433      * The linked element's absolute X position at the time the drag was
22434      * started
22435      * @property startPageX
22436      * @type int
22437      * @private
22438      */
22439     startPageX: 0,
22440
22441     /**
22442      * The linked element's absolute X position at the time the drag was
22443      * started
22444      * @property startPageY
22445      * @type int
22446      * @private
22447      */
22448     startPageY: 0,
22449
22450     /**
22451      * The group defines a logical collection of DragDrop objects that are
22452      * related.  Instances only get events when interacting with other
22453      * DragDrop object in the same group.  This lets us define multiple
22454      * groups using a single DragDrop subclass if we want.
22455      * @property groups
22456      * @type {string: string}
22457      */
22458     groups: null,
22459
22460     /**
22461      * Individual drag/drop instances can be locked.  This will prevent
22462      * onmousedown start drag.
22463      * @property locked
22464      * @type boolean
22465      * @private
22466      */
22467     locked: false,
22468
22469     /**
22470      * Lock this instance
22471      * @method lock
22472      */
22473     lock: function() { this.locked = true; },
22474
22475     /**
22476      * Unlock this instace
22477      * @method unlock
22478      */
22479     unlock: function() { this.locked = false; },
22480
22481     /**
22482      * By default, all insances can be a drop target.  This can be disabled by
22483      * setting isTarget to false.
22484      * @method isTarget
22485      * @type boolean
22486      */
22487     isTarget: true,
22488
22489     /**
22490      * The padding configured for this drag and drop object for calculating
22491      * the drop zone intersection with this object.
22492      * @method padding
22493      * @type int[]
22494      */
22495     padding: null,
22496
22497     /**
22498      * Cached reference to the linked element
22499      * @property _domRef
22500      * @private
22501      */
22502     _domRef: null,
22503
22504     /**
22505      * Internal typeof flag
22506      * @property __ygDragDrop
22507      * @private
22508      */
22509     __ygDragDrop: true,
22510
22511     /**
22512      * Set to true when horizontal contraints are applied
22513      * @property constrainX
22514      * @type boolean
22515      * @private
22516      */
22517     constrainX: false,
22518
22519     /**
22520      * Set to true when vertical contraints are applied
22521      * @property constrainY
22522      * @type boolean
22523      * @private
22524      */
22525     constrainY: false,
22526
22527     /**
22528      * The left constraint
22529      * @property minX
22530      * @type int
22531      * @private
22532      */
22533     minX: 0,
22534
22535     /**
22536      * The right constraint
22537      * @property maxX
22538      * @type int
22539      * @private
22540      */
22541     maxX: 0,
22542
22543     /**
22544      * The up constraint
22545      * @property minY
22546      * @type int
22547      * @type int
22548      * @private
22549      */
22550     minY: 0,
22551
22552     /**
22553      * The down constraint
22554      * @property maxY
22555      * @type int
22556      * @private
22557      */
22558     maxY: 0,
22559
22560     /**
22561      * Maintain offsets when we resetconstraints.  Set to true when you want
22562      * the position of the element relative to its parent to stay the same
22563      * when the page changes
22564      *
22565      * @property maintainOffset
22566      * @type boolean
22567      */
22568     maintainOffset: false,
22569
22570     /**
22571      * Array of pixel locations the element will snap to if we specified a
22572      * horizontal graduation/interval.  This array is generated automatically
22573      * when you define a tick interval.
22574      * @property xTicks
22575      * @type int[]
22576      */
22577     xTicks: null,
22578
22579     /**
22580      * Array of pixel locations the element will snap to if we specified a
22581      * vertical graduation/interval.  This array is generated automatically
22582      * when you define a tick interval.
22583      * @property yTicks
22584      * @type int[]
22585      */
22586     yTicks: null,
22587
22588     /**
22589      * By default the drag and drop instance will only respond to the primary
22590      * button click (left button for a right-handed mouse).  Set to true to
22591      * allow drag and drop to start with any mouse click that is propogated
22592      * by the browser
22593      * @property primaryButtonOnly
22594      * @type boolean
22595      */
22596     primaryButtonOnly: true,
22597
22598     /**
22599      * The availabe property is false until the linked dom element is accessible.
22600      * @property available
22601      * @type boolean
22602      */
22603     available: false,
22604
22605     /**
22606      * By default, drags can only be initiated if the mousedown occurs in the
22607      * region the linked element is.  This is done in part to work around a
22608      * bug in some browsers that mis-report the mousedown if the previous
22609      * mouseup happened outside of the window.  This property is set to true
22610      * if outer handles are defined.
22611      *
22612      * @property hasOuterHandles
22613      * @type boolean
22614      * @default false
22615      */
22616     hasOuterHandles: false,
22617
22618     /**
22619      * Code that executes immediately before the startDrag event
22620      * @method b4StartDrag
22621      * @private
22622      */
22623     b4StartDrag: function(x, y) { },
22624
22625     /**
22626      * Abstract method called after a drag/drop object is clicked
22627      * and the drag or mousedown time thresholds have beeen met.
22628      * @method startDrag
22629      * @param {int} X click location
22630      * @param {int} Y click location
22631      */
22632     startDrag: function(x, y) { /* override this */ },
22633
22634     /**
22635      * Code that executes immediately before the onDrag event
22636      * @method b4Drag
22637      * @private
22638      */
22639     b4Drag: function(e) { },
22640
22641     /**
22642      * Abstract method called during the onMouseMove event while dragging an
22643      * object.
22644      * @method onDrag
22645      * @param {Event} e the mousemove event
22646      */
22647     onDrag: function(e) { /* override this */ },
22648
22649     /**
22650      * Abstract method called when this element fist begins hovering over
22651      * another DragDrop obj
22652      * @method onDragEnter
22653      * @param {Event} e the mousemove event
22654      * @param {String|DragDrop[]} id In POINT mode, the element
22655      * id this is hovering over.  In INTERSECT mode, an array of one or more
22656      * dragdrop items being hovered over.
22657      */
22658     onDragEnter: function(e, id) { /* override this */ },
22659
22660     /**
22661      * Code that executes immediately before the onDragOver event
22662      * @method b4DragOver
22663      * @private
22664      */
22665     b4DragOver: function(e) { },
22666
22667     /**
22668      * Abstract method called when this element is hovering over another
22669      * DragDrop obj
22670      * @method onDragOver
22671      * @param {Event} e the mousemove event
22672      * @param {String|DragDrop[]} id In POINT mode, the element
22673      * id this is hovering over.  In INTERSECT mode, an array of dd items
22674      * being hovered over.
22675      */
22676     onDragOver: function(e, id) { /* override this */ },
22677
22678     /**
22679      * Code that executes immediately before the onDragOut event
22680      * @method b4DragOut
22681      * @private
22682      */
22683     b4DragOut: function(e) { },
22684
22685     /**
22686      * Abstract method called when we are no longer hovering over an element
22687      * @method onDragOut
22688      * @param {Event} e the mousemove event
22689      * @param {String|DragDrop[]} id In POINT mode, the element
22690      * id this was hovering over.  In INTERSECT mode, an array of dd items
22691      * that the mouse is no longer over.
22692      */
22693     onDragOut: function(e, id) { /* override this */ },
22694
22695     /**
22696      * Code that executes immediately before the onDragDrop event
22697      * @method b4DragDrop
22698      * @private
22699      */
22700     b4DragDrop: function(e) { },
22701
22702     /**
22703      * Abstract method called when this item is dropped on another DragDrop
22704      * obj
22705      * @method onDragDrop
22706      * @param {Event} e the mouseup event
22707      * @param {String|DragDrop[]} id In POINT mode, the element
22708      * id this was dropped on.  In INTERSECT mode, an array of dd items this
22709      * was dropped on.
22710      */
22711     onDragDrop: function(e, id) { /* override this */ },
22712
22713     /**
22714      * Abstract method called when this item is dropped on an area with no
22715      * drop target
22716      * @method onInvalidDrop
22717      * @param {Event} e the mouseup event
22718      */
22719     onInvalidDrop: function(e) { /* override this */ },
22720
22721     /**
22722      * Code that executes immediately before the endDrag event
22723      * @method b4EndDrag
22724      * @private
22725      */
22726     b4EndDrag: function(e) { },
22727
22728     /**
22729      * Fired when we are done dragging the object
22730      * @method endDrag
22731      * @param {Event} e the mouseup event
22732      */
22733     endDrag: function(e) { /* override this */ },
22734
22735     /**
22736      * Code executed immediately before the onMouseDown event
22737      * @method b4MouseDown
22738      * @param {Event} e the mousedown event
22739      * @private
22740      */
22741     b4MouseDown: function(e) {  },
22742
22743     /**
22744      * Event handler that fires when a drag/drop obj gets a mousedown
22745      * @method onMouseDown
22746      * @param {Event} e the mousedown event
22747      */
22748     onMouseDown: function(e) { /* override this */ },
22749
22750     /**
22751      * Event handler that fires when a drag/drop obj gets a mouseup
22752      * @method onMouseUp
22753      * @param {Event} e the mouseup event
22754      */
22755     onMouseUp: function(e) { /* override this */ },
22756
22757     /**
22758      * Override the onAvailable method to do what is needed after the initial
22759      * position was determined.
22760      * @method onAvailable
22761      */
22762     onAvailable: function () {
22763     },
22764
22765     /*
22766      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
22767      * @type Object
22768      */
22769     defaultPadding : {left:0, right:0, top:0, bottom:0},
22770
22771     /*
22772      * Initializes the drag drop object's constraints to restrict movement to a certain element.
22773  *
22774  * Usage:
22775  <pre><code>
22776  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
22777                 { dragElId: "existingProxyDiv" });
22778  dd.startDrag = function(){
22779      this.constrainTo("parent-id");
22780  };
22781  </code></pre>
22782  * Or you can initalize it using the {@link Roo.Element} object:
22783  <pre><code>
22784  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
22785      startDrag : function(){
22786          this.constrainTo("parent-id");
22787      }
22788  });
22789  </code></pre>
22790      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
22791      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
22792      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
22793      * an object containing the sides to pad. For example: {right:10, bottom:10}
22794      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
22795      */
22796     constrainTo : function(constrainTo, pad, inContent){
22797         if(typeof pad == "number"){
22798             pad = {left: pad, right:pad, top:pad, bottom:pad};
22799         }
22800         pad = pad || this.defaultPadding;
22801         var b = Roo.get(this.getEl()).getBox();
22802         var ce = Roo.get(constrainTo);
22803         var s = ce.getScroll();
22804         var c, cd = ce.dom;
22805         if(cd == document.body){
22806             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
22807         }else{
22808             xy = ce.getXY();
22809             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
22810         }
22811
22812
22813         var topSpace = b.y - c.y;
22814         var leftSpace = b.x - c.x;
22815
22816         this.resetConstraints();
22817         this.setXConstraint(leftSpace - (pad.left||0), // left
22818                 c.width - leftSpace - b.width - (pad.right||0) //right
22819         );
22820         this.setYConstraint(topSpace - (pad.top||0), //top
22821                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
22822         );
22823     },
22824
22825     /**
22826      * Returns a reference to the linked element
22827      * @method getEl
22828      * @return {HTMLElement} the html element
22829      */
22830     getEl: function() {
22831         if (!this._domRef) {
22832             this._domRef = Roo.getDom(this.id);
22833         }
22834
22835         return this._domRef;
22836     },
22837
22838     /**
22839      * Returns a reference to the actual element to drag.  By default this is
22840      * the same as the html element, but it can be assigned to another
22841      * element. An example of this can be found in Roo.dd.DDProxy
22842      * @method getDragEl
22843      * @return {HTMLElement} the html element
22844      */
22845     getDragEl: function() {
22846         return Roo.getDom(this.dragElId);
22847     },
22848
22849     /**
22850      * Sets up the DragDrop object.  Must be called in the constructor of any
22851      * Roo.dd.DragDrop subclass
22852      * @method init
22853      * @param id the id of the linked element
22854      * @param {String} sGroup the group of related items
22855      * @param {object} config configuration attributes
22856      */
22857     init: function(id, sGroup, config) {
22858         this.initTarget(id, sGroup, config);
22859         if (!Roo.isTouch) {
22860             Event.on(this.id, "mousedown", this.handleMouseDown, this);
22861         }
22862         Event.on(this.id, "touchstart", this.handleMouseDown, this);
22863         // Event.on(this.id, "selectstart", Event.preventDefault);
22864     },
22865
22866     /**
22867      * Initializes Targeting functionality only... the object does not
22868      * get a mousedown handler.
22869      * @method initTarget
22870      * @param id the id of the linked element
22871      * @param {String} sGroup the group of related items
22872      * @param {object} config configuration attributes
22873      */
22874     initTarget: function(id, sGroup, config) {
22875
22876         // configuration attributes
22877         this.config = config || {};
22878
22879         // create a local reference to the drag and drop manager
22880         this.DDM = Roo.dd.DDM;
22881         // initialize the groups array
22882         this.groups = {};
22883
22884         // assume that we have an element reference instead of an id if the
22885         // parameter is not a string
22886         if (typeof id !== "string") {
22887             id = Roo.id(id);
22888         }
22889
22890         // set the id
22891         this.id = id;
22892
22893         // add to an interaction group
22894         this.addToGroup((sGroup) ? sGroup : "default");
22895
22896         // We don't want to register this as the handle with the manager
22897         // so we just set the id rather than calling the setter.
22898         this.handleElId = id;
22899
22900         // the linked element is the element that gets dragged by default
22901         this.setDragElId(id);
22902
22903         // by default, clicked anchors will not start drag operations.
22904         this.invalidHandleTypes = { A: "A" };
22905         this.invalidHandleIds = {};
22906         this.invalidHandleClasses = [];
22907
22908         this.applyConfig();
22909
22910         this.handleOnAvailable();
22911     },
22912
22913     /**
22914      * Applies the configuration parameters that were passed into the constructor.
22915      * This is supposed to happen at each level through the inheritance chain.  So
22916      * a DDProxy implentation will execute apply config on DDProxy, DD, and
22917      * DragDrop in order to get all of the parameters that are available in
22918      * each object.
22919      * @method applyConfig
22920      */
22921     applyConfig: function() {
22922
22923         // configurable properties:
22924         //    padding, isTarget, maintainOffset, primaryButtonOnly
22925         this.padding           = this.config.padding || [0, 0, 0, 0];
22926         this.isTarget          = (this.config.isTarget !== false);
22927         this.maintainOffset    = (this.config.maintainOffset);
22928         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
22929
22930     },
22931
22932     /**
22933      * Executed when the linked element is available
22934      * @method handleOnAvailable
22935      * @private
22936      */
22937     handleOnAvailable: function() {
22938         this.available = true;
22939         this.resetConstraints();
22940         this.onAvailable();
22941     },
22942
22943      /**
22944      * Configures the padding for the target zone in px.  Effectively expands
22945      * (or reduces) the virtual object size for targeting calculations.
22946      * Supports css-style shorthand; if only one parameter is passed, all sides
22947      * will have that padding, and if only two are passed, the top and bottom
22948      * will have the first param, the left and right the second.
22949      * @method setPadding
22950      * @param {int} iTop    Top pad
22951      * @param {int} iRight  Right pad
22952      * @param {int} iBot    Bot pad
22953      * @param {int} iLeft   Left pad
22954      */
22955     setPadding: function(iTop, iRight, iBot, iLeft) {
22956         // this.padding = [iLeft, iRight, iTop, iBot];
22957         if (!iRight && 0 !== iRight) {
22958             this.padding = [iTop, iTop, iTop, iTop];
22959         } else if (!iBot && 0 !== iBot) {
22960             this.padding = [iTop, iRight, iTop, iRight];
22961         } else {
22962             this.padding = [iTop, iRight, iBot, iLeft];
22963         }
22964     },
22965
22966     /**
22967      * Stores the initial placement of the linked element.
22968      * @method setInitialPosition
22969      * @param {int} diffX   the X offset, default 0
22970      * @param {int} diffY   the Y offset, default 0
22971      */
22972     setInitPosition: function(diffX, diffY) {
22973         var el = this.getEl();
22974
22975         if (!this.DDM.verifyEl(el)) {
22976             return;
22977         }
22978
22979         var dx = diffX || 0;
22980         var dy = diffY || 0;
22981
22982         var p = Dom.getXY( el );
22983
22984         this.initPageX = p[0] - dx;
22985         this.initPageY = p[1] - dy;
22986
22987         this.lastPageX = p[0];
22988         this.lastPageY = p[1];
22989
22990
22991         this.setStartPosition(p);
22992     },
22993
22994     /**
22995      * Sets the start position of the element.  This is set when the obj
22996      * is initialized, the reset when a drag is started.
22997      * @method setStartPosition
22998      * @param pos current position (from previous lookup)
22999      * @private
23000      */
23001     setStartPosition: function(pos) {
23002         var p = pos || Dom.getXY( this.getEl() );
23003         this.deltaSetXY = null;
23004
23005         this.startPageX = p[0];
23006         this.startPageY = p[1];
23007     },
23008
23009     /**
23010      * Add this instance to a group of related drag/drop objects.  All
23011      * instances belong to at least one group, and can belong to as many
23012      * groups as needed.
23013      * @method addToGroup
23014      * @param sGroup {string} the name of the group
23015      */
23016     addToGroup: function(sGroup) {
23017         this.groups[sGroup] = true;
23018         this.DDM.regDragDrop(this, sGroup);
23019     },
23020
23021     /**
23022      * Remove's this instance from the supplied interaction group
23023      * @method removeFromGroup
23024      * @param {string}  sGroup  The group to drop
23025      */
23026     removeFromGroup: function(sGroup) {
23027         if (this.groups[sGroup]) {
23028             delete this.groups[sGroup];
23029         }
23030
23031         this.DDM.removeDDFromGroup(this, sGroup);
23032     },
23033
23034     /**
23035      * Allows you to specify that an element other than the linked element
23036      * will be moved with the cursor during a drag
23037      * @method setDragElId
23038      * @param id {string} the id of the element that will be used to initiate the drag
23039      */
23040     setDragElId: function(id) {
23041         this.dragElId = id;
23042     },
23043
23044     /**
23045      * Allows you to specify a child of the linked element that should be
23046      * used to initiate the drag operation.  An example of this would be if
23047      * you have a content div with text and links.  Clicking anywhere in the
23048      * content area would normally start the drag operation.  Use this method
23049      * to specify that an element inside of the content div is the element
23050      * that starts the drag operation.
23051      * @method setHandleElId
23052      * @param id {string} the id of the element that will be used to
23053      * initiate the drag.
23054      */
23055     setHandleElId: function(id) {
23056         if (typeof id !== "string") {
23057             id = Roo.id(id);
23058         }
23059         this.handleElId = id;
23060         this.DDM.regHandle(this.id, id);
23061     },
23062
23063     /**
23064      * Allows you to set an element outside of the linked element as a drag
23065      * handle
23066      * @method setOuterHandleElId
23067      * @param id the id of the element that will be used to initiate the drag
23068      */
23069     setOuterHandleElId: function(id) {
23070         if (typeof id !== "string") {
23071             id = Roo.id(id);
23072         }
23073         Event.on(id, "mousedown",
23074                 this.handleMouseDown, this);
23075         this.setHandleElId(id);
23076
23077         this.hasOuterHandles = true;
23078     },
23079
23080     /**
23081      * Remove all drag and drop hooks for this element
23082      * @method unreg
23083      */
23084     unreg: function() {
23085         Event.un(this.id, "mousedown",
23086                 this.handleMouseDown);
23087         Event.un(this.id, "touchstart",
23088                 this.handleMouseDown);
23089         this._domRef = null;
23090         this.DDM._remove(this);
23091     },
23092
23093     destroy : function(){
23094         this.unreg();
23095     },
23096
23097     /**
23098      * Returns true if this instance is locked, or the drag drop mgr is locked
23099      * (meaning that all drag/drop is disabled on the page.)
23100      * @method isLocked
23101      * @return {boolean} true if this obj or all drag/drop is locked, else
23102      * false
23103      */
23104     isLocked: function() {
23105         return (this.DDM.isLocked() || this.locked);
23106     },
23107
23108     /**
23109      * Fired when this object is clicked
23110      * @method handleMouseDown
23111      * @param {Event} e
23112      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
23113      * @private
23114      */
23115     handleMouseDown: function(e, oDD){
23116      
23117         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
23118             //Roo.log('not touch/ button !=0');
23119             return;
23120         }
23121         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
23122             return; // double touch..
23123         }
23124         
23125
23126         if (this.isLocked()) {
23127             //Roo.log('locked');
23128             return;
23129         }
23130
23131         this.DDM.refreshCache(this.groups);
23132 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
23133         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
23134         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
23135             //Roo.log('no outer handes or not over target');
23136                 // do nothing.
23137         } else {
23138 //            Roo.log('check validator');
23139             if (this.clickValidator(e)) {
23140 //                Roo.log('validate success');
23141                 // set the initial element position
23142                 this.setStartPosition();
23143
23144
23145                 this.b4MouseDown(e);
23146                 this.onMouseDown(e);
23147
23148                 this.DDM.handleMouseDown(e, this);
23149
23150                 this.DDM.stopEvent(e);
23151             } else {
23152
23153
23154             }
23155         }
23156     },
23157
23158     clickValidator: function(e) {
23159         var target = e.getTarget();
23160         return ( this.isValidHandleChild(target) &&
23161                     (this.id == this.handleElId ||
23162                         this.DDM.handleWasClicked(target, this.id)) );
23163     },
23164
23165     /**
23166      * Allows you to specify a tag name that should not start a drag operation
23167      * when clicked.  This is designed to facilitate embedding links within a
23168      * drag handle that do something other than start the drag.
23169      * @method addInvalidHandleType
23170      * @param {string} tagName the type of element to exclude
23171      */
23172     addInvalidHandleType: function(tagName) {
23173         var type = tagName.toUpperCase();
23174         this.invalidHandleTypes[type] = type;
23175     },
23176
23177     /**
23178      * Lets you to specify an element id for a child of a drag handle
23179      * that should not initiate a drag
23180      * @method addInvalidHandleId
23181      * @param {string} id the element id of the element you wish to ignore
23182      */
23183     addInvalidHandleId: function(id) {
23184         if (typeof id !== "string") {
23185             id = Roo.id(id);
23186         }
23187         this.invalidHandleIds[id] = id;
23188     },
23189
23190     /**
23191      * Lets you specify a css class of elements that will not initiate a drag
23192      * @method addInvalidHandleClass
23193      * @param {string} cssClass the class of the elements you wish to ignore
23194      */
23195     addInvalidHandleClass: function(cssClass) {
23196         this.invalidHandleClasses.push(cssClass);
23197     },
23198
23199     /**
23200      * Unsets an excluded tag name set by addInvalidHandleType
23201      * @method removeInvalidHandleType
23202      * @param {string} tagName the type of element to unexclude
23203      */
23204     removeInvalidHandleType: function(tagName) {
23205         var type = tagName.toUpperCase();
23206         // this.invalidHandleTypes[type] = null;
23207         delete this.invalidHandleTypes[type];
23208     },
23209
23210     /**
23211      * Unsets an invalid handle id
23212      * @method removeInvalidHandleId
23213      * @param {string} id the id of the element to re-enable
23214      */
23215     removeInvalidHandleId: function(id) {
23216         if (typeof id !== "string") {
23217             id = Roo.id(id);
23218         }
23219         delete this.invalidHandleIds[id];
23220     },
23221
23222     /**
23223      * Unsets an invalid css class
23224      * @method removeInvalidHandleClass
23225      * @param {string} cssClass the class of the element(s) you wish to
23226      * re-enable
23227      */
23228     removeInvalidHandleClass: function(cssClass) {
23229         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
23230             if (this.invalidHandleClasses[i] == cssClass) {
23231                 delete this.invalidHandleClasses[i];
23232             }
23233         }
23234     },
23235
23236     /**
23237      * Checks the tag exclusion list to see if this click should be ignored
23238      * @method isValidHandleChild
23239      * @param {HTMLElement} node the HTMLElement to evaluate
23240      * @return {boolean} true if this is a valid tag type, false if not
23241      */
23242     isValidHandleChild: function(node) {
23243
23244         var valid = true;
23245         // var n = (node.nodeName == "#text") ? node.parentNode : node;
23246         var nodeName;
23247         try {
23248             nodeName = node.nodeName.toUpperCase();
23249         } catch(e) {
23250             nodeName = node.nodeName;
23251         }
23252         valid = valid && !this.invalidHandleTypes[nodeName];
23253         valid = valid && !this.invalidHandleIds[node.id];
23254
23255         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
23256             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
23257         }
23258
23259
23260         return valid;
23261
23262     },
23263
23264     /**
23265      * Create the array of horizontal tick marks if an interval was specified
23266      * in setXConstraint().
23267      * @method setXTicks
23268      * @private
23269      */
23270     setXTicks: function(iStartX, iTickSize) {
23271         this.xTicks = [];
23272         this.xTickSize = iTickSize;
23273
23274         var tickMap = {};
23275
23276         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
23277             if (!tickMap[i]) {
23278                 this.xTicks[this.xTicks.length] = i;
23279                 tickMap[i] = true;
23280             }
23281         }
23282
23283         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
23284             if (!tickMap[i]) {
23285                 this.xTicks[this.xTicks.length] = i;
23286                 tickMap[i] = true;
23287             }
23288         }
23289
23290         this.xTicks.sort(this.DDM.numericSort) ;
23291     },
23292
23293     /**
23294      * Create the array of vertical tick marks if an interval was specified in
23295      * setYConstraint().
23296      * @method setYTicks
23297      * @private
23298      */
23299     setYTicks: function(iStartY, iTickSize) {
23300         this.yTicks = [];
23301         this.yTickSize = iTickSize;
23302
23303         var tickMap = {};
23304
23305         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
23306             if (!tickMap[i]) {
23307                 this.yTicks[this.yTicks.length] = i;
23308                 tickMap[i] = true;
23309             }
23310         }
23311
23312         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
23313             if (!tickMap[i]) {
23314                 this.yTicks[this.yTicks.length] = i;
23315                 tickMap[i] = true;
23316             }
23317         }
23318
23319         this.yTicks.sort(this.DDM.numericSort) ;
23320     },
23321
23322     /**
23323      * By default, the element can be dragged any place on the screen.  Use
23324      * this method to limit the horizontal travel of the element.  Pass in
23325      * 0,0 for the parameters if you want to lock the drag to the y axis.
23326      * @method setXConstraint
23327      * @param {int} iLeft the number of pixels the element can move to the left
23328      * @param {int} iRight the number of pixels the element can move to the
23329      * right
23330      * @param {int} iTickSize optional parameter for specifying that the
23331      * element
23332      * should move iTickSize pixels at a time.
23333      */
23334     setXConstraint: function(iLeft, iRight, iTickSize) {
23335         this.leftConstraint = iLeft;
23336         this.rightConstraint = iRight;
23337
23338         this.minX = this.initPageX - iLeft;
23339         this.maxX = this.initPageX + iRight;
23340         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
23341
23342         this.constrainX = true;
23343     },
23344
23345     /**
23346      * Clears any constraints applied to this instance.  Also clears ticks
23347      * since they can't exist independent of a constraint at this time.
23348      * @method clearConstraints
23349      */
23350     clearConstraints: function() {
23351         this.constrainX = false;
23352         this.constrainY = false;
23353         this.clearTicks();
23354     },
23355
23356     /**
23357      * Clears any tick interval defined for this instance
23358      * @method clearTicks
23359      */
23360     clearTicks: function() {
23361         this.xTicks = null;
23362         this.yTicks = null;
23363         this.xTickSize = 0;
23364         this.yTickSize = 0;
23365     },
23366
23367     /**
23368      * By default, the element can be dragged any place on the screen.  Set
23369      * this to limit the vertical travel of the element.  Pass in 0,0 for the
23370      * parameters if you want to lock the drag to the x axis.
23371      * @method setYConstraint
23372      * @param {int} iUp the number of pixels the element can move up
23373      * @param {int} iDown the number of pixels the element can move down
23374      * @param {int} iTickSize optional parameter for specifying that the
23375      * element should move iTickSize pixels at a time.
23376      */
23377     setYConstraint: function(iUp, iDown, iTickSize) {
23378         this.topConstraint = iUp;
23379         this.bottomConstraint = iDown;
23380
23381         this.minY = this.initPageY - iUp;
23382         this.maxY = this.initPageY + iDown;
23383         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
23384
23385         this.constrainY = true;
23386
23387     },
23388
23389     /**
23390      * resetConstraints must be called if you manually reposition a dd element.
23391      * @method resetConstraints
23392      * @param {boolean} maintainOffset
23393      */
23394     resetConstraints: function() {
23395
23396
23397         // Maintain offsets if necessary
23398         if (this.initPageX || this.initPageX === 0) {
23399             // figure out how much this thing has moved
23400             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
23401             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
23402
23403             this.setInitPosition(dx, dy);
23404
23405         // This is the first time we have detected the element's position
23406         } else {
23407             this.setInitPosition();
23408         }
23409
23410         if (this.constrainX) {
23411             this.setXConstraint( this.leftConstraint,
23412                                  this.rightConstraint,
23413                                  this.xTickSize        );
23414         }
23415
23416         if (this.constrainY) {
23417             this.setYConstraint( this.topConstraint,
23418                                  this.bottomConstraint,
23419                                  this.yTickSize         );
23420         }
23421     },
23422
23423     /**
23424      * Normally the drag element is moved pixel by pixel, but we can specify
23425      * that it move a number of pixels at a time.  This method resolves the
23426      * location when we have it set up like this.
23427      * @method getTick
23428      * @param {int} val where we want to place the object
23429      * @param {int[]} tickArray sorted array of valid points
23430      * @return {int} the closest tick
23431      * @private
23432      */
23433     getTick: function(val, tickArray) {
23434
23435         if (!tickArray) {
23436             // If tick interval is not defined, it is effectively 1 pixel,
23437             // so we return the value passed to us.
23438             return val;
23439         } else if (tickArray[0] >= val) {
23440             // The value is lower than the first tick, so we return the first
23441             // tick.
23442             return tickArray[0];
23443         } else {
23444             for (var i=0, len=tickArray.length; i<len; ++i) {
23445                 var next = i + 1;
23446                 if (tickArray[next] && tickArray[next] >= val) {
23447                     var diff1 = val - tickArray[i];
23448                     var diff2 = tickArray[next] - val;
23449                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
23450                 }
23451             }
23452
23453             // The value is larger than the last tick, so we return the last
23454             // tick.
23455             return tickArray[tickArray.length - 1];
23456         }
23457     },
23458
23459     /**
23460      * toString method
23461      * @method toString
23462      * @return {string} string representation of the dd obj
23463      */
23464     toString: function() {
23465         return ("DragDrop " + this.id);
23466     }
23467
23468 });
23469
23470 })();
23471 /*
23472  * Based on:
23473  * Ext JS Library 1.1.1
23474  * Copyright(c) 2006-2007, Ext JS, LLC.
23475  *
23476  * Originally Released Under LGPL - original licence link has changed is not relivant.
23477  *
23478  * Fork - LGPL
23479  * <script type="text/javascript">
23480  */
23481
23482
23483 /**
23484  * The drag and drop utility provides a framework for building drag and drop
23485  * applications.  In addition to enabling drag and drop for specific elements,
23486  * the drag and drop elements are tracked by the manager class, and the
23487  * interactions between the various elements are tracked during the drag and
23488  * the implementing code is notified about these important moments.
23489  */
23490
23491 // Only load the library once.  Rewriting the manager class would orphan
23492 // existing drag and drop instances.
23493 if (!Roo.dd.DragDropMgr) {
23494
23495 /**
23496  * @class Roo.dd.DragDropMgr
23497  * DragDropMgr is a singleton that tracks the element interaction for
23498  * all DragDrop items in the window.  Generally, you will not call
23499  * this class directly, but it does have helper methods that could
23500  * be useful in your DragDrop implementations.
23501  * @singleton
23502  */
23503 Roo.dd.DragDropMgr = function() {
23504
23505     var Event = Roo.EventManager;
23506
23507     return {
23508
23509         /**
23510          * Two dimensional Array of registered DragDrop objects.  The first
23511          * dimension is the DragDrop item group, the second the DragDrop
23512          * object.
23513          * @property ids
23514          * @type {string: string}
23515          * @private
23516          * @static
23517          */
23518         ids: {},
23519
23520         /**
23521          * Array of element ids defined as drag handles.  Used to determine
23522          * if the element that generated the mousedown event is actually the
23523          * handle and not the html element itself.
23524          * @property handleIds
23525          * @type {string: string}
23526          * @private
23527          * @static
23528          */
23529         handleIds: {},
23530
23531         /**
23532          * the DragDrop object that is currently being dragged
23533          * @property dragCurrent
23534          * @type DragDrop
23535          * @private
23536          * @static
23537          **/
23538         dragCurrent: null,
23539
23540         /**
23541          * the DragDrop object(s) that are being hovered over
23542          * @property dragOvers
23543          * @type Array
23544          * @private
23545          * @static
23546          */
23547         dragOvers: {},
23548
23549         /**
23550          * the X distance between the cursor and the object being dragged
23551          * @property deltaX
23552          * @type int
23553          * @private
23554          * @static
23555          */
23556         deltaX: 0,
23557
23558         /**
23559          * the Y distance between the cursor and the object being dragged
23560          * @property deltaY
23561          * @type int
23562          * @private
23563          * @static
23564          */
23565         deltaY: 0,
23566
23567         /**
23568          * Flag to determine if we should prevent the default behavior of the
23569          * events we define. By default this is true, but this can be set to
23570          * false if you need the default behavior (not recommended)
23571          * @property preventDefault
23572          * @type boolean
23573          * @static
23574          */
23575         preventDefault: true,
23576
23577         /**
23578          * Flag to determine if we should stop the propagation of the events
23579          * we generate. This is true by default but you may want to set it to
23580          * false if the html element contains other features that require the
23581          * mouse click.
23582          * @property stopPropagation
23583          * @type boolean
23584          * @static
23585          */
23586         stopPropagation: true,
23587
23588         /**
23589          * Internal flag that is set to true when drag and drop has been
23590          * intialized
23591          * @property initialized
23592          * @private
23593          * @static
23594          */
23595         initalized: false,
23596
23597         /**
23598          * All drag and drop can be disabled.
23599          * @property locked
23600          * @private
23601          * @static
23602          */
23603         locked: false,
23604
23605         /**
23606          * Called the first time an element is registered.
23607          * @method init
23608          * @private
23609          * @static
23610          */
23611         init: function() {
23612             this.initialized = true;
23613         },
23614
23615         /**
23616          * In point mode, drag and drop interaction is defined by the
23617          * location of the cursor during the drag/drop
23618          * @property POINT
23619          * @type int
23620          * @static
23621          */
23622         POINT: 0,
23623
23624         /**
23625          * In intersect mode, drag and drop interactio nis defined by the
23626          * overlap of two or more drag and drop objects.
23627          * @property INTERSECT
23628          * @type int
23629          * @static
23630          */
23631         INTERSECT: 1,
23632
23633         /**
23634          * The current drag and drop mode.  Default: POINT
23635          * @property mode
23636          * @type int
23637          * @static
23638          */
23639         mode: 0,
23640
23641         /**
23642          * Runs method on all drag and drop objects
23643          * @method _execOnAll
23644          * @private
23645          * @static
23646          */
23647         _execOnAll: function(sMethod, args) {
23648             for (var i in this.ids) {
23649                 for (var j in this.ids[i]) {
23650                     var oDD = this.ids[i][j];
23651                     if (! this.isTypeOfDD(oDD)) {
23652                         continue;
23653                     }
23654                     oDD[sMethod].apply(oDD, args);
23655                 }
23656             }
23657         },
23658
23659         /**
23660          * Drag and drop initialization.  Sets up the global event handlers
23661          * @method _onLoad
23662          * @private
23663          * @static
23664          */
23665         _onLoad: function() {
23666
23667             this.init();
23668
23669             if (!Roo.isTouch) {
23670                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
23671                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
23672             }
23673             Event.on(document, "touchend",   this.handleMouseUp, this, true);
23674             Event.on(document, "touchmove", this.handleMouseMove, this, true);
23675             
23676             Event.on(window,   "unload",    this._onUnload, this, true);
23677             Event.on(window,   "resize",    this._onResize, this, true);
23678             // Event.on(window,   "mouseout",    this._test);
23679
23680         },
23681
23682         /**
23683          * Reset constraints on all drag and drop objs
23684          * @method _onResize
23685          * @private
23686          * @static
23687          */
23688         _onResize: function(e) {
23689             this._execOnAll("resetConstraints", []);
23690         },
23691
23692         /**
23693          * Lock all drag and drop functionality
23694          * @method lock
23695          * @static
23696          */
23697         lock: function() { this.locked = true; },
23698
23699         /**
23700          * Unlock all drag and drop functionality
23701          * @method unlock
23702          * @static
23703          */
23704         unlock: function() { this.locked = false; },
23705
23706         /**
23707          * Is drag and drop locked?
23708          * @method isLocked
23709          * @return {boolean} True if drag and drop is locked, false otherwise.
23710          * @static
23711          */
23712         isLocked: function() { return this.locked; },
23713
23714         /**
23715          * Location cache that is set for all drag drop objects when a drag is
23716          * initiated, cleared when the drag is finished.
23717          * @property locationCache
23718          * @private
23719          * @static
23720          */
23721         locationCache: {},
23722
23723         /**
23724          * Set useCache to false if you want to force object the lookup of each
23725          * drag and drop linked element constantly during a drag.
23726          * @property useCache
23727          * @type boolean
23728          * @static
23729          */
23730         useCache: true,
23731
23732         /**
23733          * The number of pixels that the mouse needs to move after the
23734          * mousedown before the drag is initiated.  Default=3;
23735          * @property clickPixelThresh
23736          * @type int
23737          * @static
23738          */
23739         clickPixelThresh: 3,
23740
23741         /**
23742          * The number of milliseconds after the mousedown event to initiate the
23743          * drag if we don't get a mouseup event. Default=1000
23744          * @property clickTimeThresh
23745          * @type int
23746          * @static
23747          */
23748         clickTimeThresh: 350,
23749
23750         /**
23751          * Flag that indicates that either the drag pixel threshold or the
23752          * mousdown time threshold has been met
23753          * @property dragThreshMet
23754          * @type boolean
23755          * @private
23756          * @static
23757          */
23758         dragThreshMet: false,
23759
23760         /**
23761          * Timeout used for the click time threshold
23762          * @property clickTimeout
23763          * @type Object
23764          * @private
23765          * @static
23766          */
23767         clickTimeout: null,
23768
23769         /**
23770          * The X position of the mousedown event stored for later use when a
23771          * drag threshold is met.
23772          * @property startX
23773          * @type int
23774          * @private
23775          * @static
23776          */
23777         startX: 0,
23778
23779         /**
23780          * The Y position of the mousedown event stored for later use when a
23781          * drag threshold is met.
23782          * @property startY
23783          * @type int
23784          * @private
23785          * @static
23786          */
23787         startY: 0,
23788
23789         /**
23790          * Each DragDrop instance must be registered with the DragDropMgr.
23791          * This is executed in DragDrop.init()
23792          * @method regDragDrop
23793          * @param {DragDrop} oDD the DragDrop object to register
23794          * @param {String} sGroup the name of the group this element belongs to
23795          * @static
23796          */
23797         regDragDrop: function(oDD, sGroup) {
23798             if (!this.initialized) { this.init(); }
23799
23800             if (!this.ids[sGroup]) {
23801                 this.ids[sGroup] = {};
23802             }
23803             this.ids[sGroup][oDD.id] = oDD;
23804         },
23805
23806         /**
23807          * Removes the supplied dd instance from the supplied group. Executed
23808          * by DragDrop.removeFromGroup, so don't call this function directly.
23809          * @method removeDDFromGroup
23810          * @private
23811          * @static
23812          */
23813         removeDDFromGroup: function(oDD, sGroup) {
23814             if (!this.ids[sGroup]) {
23815                 this.ids[sGroup] = {};
23816             }
23817
23818             var obj = this.ids[sGroup];
23819             if (obj && obj[oDD.id]) {
23820                 delete obj[oDD.id];
23821             }
23822         },
23823
23824         /**
23825          * Unregisters a drag and drop item.  This is executed in
23826          * DragDrop.unreg, use that method instead of calling this directly.
23827          * @method _remove
23828          * @private
23829          * @static
23830          */
23831         _remove: function(oDD) {
23832             for (var g in oDD.groups) {
23833                 if (g && this.ids[g][oDD.id]) {
23834                     delete this.ids[g][oDD.id];
23835                 }
23836             }
23837             delete this.handleIds[oDD.id];
23838         },
23839
23840         /**
23841          * Each DragDrop handle element must be registered.  This is done
23842          * automatically when executing DragDrop.setHandleElId()
23843          * @method regHandle
23844          * @param {String} sDDId the DragDrop id this element is a handle for
23845          * @param {String} sHandleId the id of the element that is the drag
23846          * handle
23847          * @static
23848          */
23849         regHandle: function(sDDId, sHandleId) {
23850             if (!this.handleIds[sDDId]) {
23851                 this.handleIds[sDDId] = {};
23852             }
23853             this.handleIds[sDDId][sHandleId] = sHandleId;
23854         },
23855
23856         /**
23857          * Utility function to determine if a given element has been
23858          * registered as a drag drop item.
23859          * @method isDragDrop
23860          * @param {String} id the element id to check
23861          * @return {boolean} true if this element is a DragDrop item,
23862          * false otherwise
23863          * @static
23864          */
23865         isDragDrop: function(id) {
23866             return ( this.getDDById(id) ) ? true : false;
23867         },
23868
23869         /**
23870          * Returns the drag and drop instances that are in all groups the
23871          * passed in instance belongs to.
23872          * @method getRelated
23873          * @param {DragDrop} p_oDD the obj to get related data for
23874          * @param {boolean} bTargetsOnly if true, only return targetable objs
23875          * @return {DragDrop[]} the related instances
23876          * @static
23877          */
23878         getRelated: function(p_oDD, bTargetsOnly) {
23879             var oDDs = [];
23880             for (var i in p_oDD.groups) {
23881                 for (j in this.ids[i]) {
23882                     var dd = this.ids[i][j];
23883                     if (! this.isTypeOfDD(dd)) {
23884                         continue;
23885                     }
23886                     if (!bTargetsOnly || dd.isTarget) {
23887                         oDDs[oDDs.length] = dd;
23888                     }
23889                 }
23890             }
23891
23892             return oDDs;
23893         },
23894
23895         /**
23896          * Returns true if the specified dd target is a legal target for
23897          * the specifice drag obj
23898          * @method isLegalTarget
23899          * @param {DragDrop} the drag obj
23900          * @param {DragDrop} the target
23901          * @return {boolean} true if the target is a legal target for the
23902          * dd obj
23903          * @static
23904          */
23905         isLegalTarget: function (oDD, oTargetDD) {
23906             var targets = this.getRelated(oDD, true);
23907             for (var i=0, len=targets.length;i<len;++i) {
23908                 if (targets[i].id == oTargetDD.id) {
23909                     return true;
23910                 }
23911             }
23912
23913             return false;
23914         },
23915
23916         /**
23917          * My goal is to be able to transparently determine if an object is
23918          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
23919          * returns "object", oDD.constructor.toString() always returns
23920          * "DragDrop" and not the name of the subclass.  So for now it just
23921          * evaluates a well-known variable in DragDrop.
23922          * @method isTypeOfDD
23923          * @param {Object} the object to evaluate
23924          * @return {boolean} true if typeof oDD = DragDrop
23925          * @static
23926          */
23927         isTypeOfDD: function (oDD) {
23928             return (oDD && oDD.__ygDragDrop);
23929         },
23930
23931         /**
23932          * Utility function to determine if a given element has been
23933          * registered as a drag drop handle for the given Drag Drop object.
23934          * @method isHandle
23935          * @param {String} id the element id to check
23936          * @return {boolean} true if this element is a DragDrop handle, false
23937          * otherwise
23938          * @static
23939          */
23940         isHandle: function(sDDId, sHandleId) {
23941             return ( this.handleIds[sDDId] &&
23942                             this.handleIds[sDDId][sHandleId] );
23943         },
23944
23945         /**
23946          * Returns the DragDrop instance for a given id
23947          * @method getDDById
23948          * @param {String} id the id of the DragDrop object
23949          * @return {DragDrop} the drag drop object, null if it is not found
23950          * @static
23951          */
23952         getDDById: function(id) {
23953             for (var i in this.ids) {
23954                 if (this.ids[i][id]) {
23955                     return this.ids[i][id];
23956                 }
23957             }
23958             return null;
23959         },
23960
23961         /**
23962          * Fired after a registered DragDrop object gets the mousedown event.
23963          * Sets up the events required to track the object being dragged
23964          * @method handleMouseDown
23965          * @param {Event} e the event
23966          * @param oDD the DragDrop object being dragged
23967          * @private
23968          * @static
23969          */
23970         handleMouseDown: function(e, oDD) {
23971             if(Roo.QuickTips){
23972                 Roo.QuickTips.disable();
23973             }
23974             this.currentTarget = e.getTarget();
23975
23976             this.dragCurrent = oDD;
23977
23978             var el = oDD.getEl();
23979
23980             // track start position
23981             this.startX = e.getPageX();
23982             this.startY = e.getPageY();
23983
23984             this.deltaX = this.startX - el.offsetLeft;
23985             this.deltaY = this.startY - el.offsetTop;
23986
23987             this.dragThreshMet = false;
23988
23989             this.clickTimeout = setTimeout(
23990                     function() {
23991                         var DDM = Roo.dd.DDM;
23992                         DDM.startDrag(DDM.startX, DDM.startY);
23993                     },
23994                     this.clickTimeThresh );
23995         },
23996
23997         /**
23998          * Fired when either the drag pixel threshol or the mousedown hold
23999          * time threshold has been met.
24000          * @method startDrag
24001          * @param x {int} the X position of the original mousedown
24002          * @param y {int} the Y position of the original mousedown
24003          * @static
24004          */
24005         startDrag: function(x, y) {
24006             clearTimeout(this.clickTimeout);
24007             if (this.dragCurrent) {
24008                 this.dragCurrent.b4StartDrag(x, y);
24009                 this.dragCurrent.startDrag(x, y);
24010             }
24011             this.dragThreshMet = true;
24012         },
24013
24014         /**
24015          * Internal function to handle the mouseup event.  Will be invoked
24016          * from the context of the document.
24017          * @method handleMouseUp
24018          * @param {Event} e the event
24019          * @private
24020          * @static
24021          */
24022         handleMouseUp: function(e) {
24023
24024             if(Roo.QuickTips){
24025                 Roo.QuickTips.enable();
24026             }
24027             if (! this.dragCurrent) {
24028                 return;
24029             }
24030
24031             clearTimeout(this.clickTimeout);
24032
24033             if (this.dragThreshMet) {
24034                 this.fireEvents(e, true);
24035             } else {
24036             }
24037
24038             this.stopDrag(e);
24039
24040             this.stopEvent(e);
24041         },
24042
24043         /**
24044          * Utility to stop event propagation and event default, if these
24045          * features are turned on.
24046          * @method stopEvent
24047          * @param {Event} e the event as returned by this.getEvent()
24048          * @static
24049          */
24050         stopEvent: function(e){
24051             if(this.stopPropagation) {
24052                 e.stopPropagation();
24053             }
24054
24055             if (this.preventDefault) {
24056                 e.preventDefault();
24057             }
24058         },
24059
24060         /**
24061          * Internal function to clean up event handlers after the drag
24062          * operation is complete
24063          * @method stopDrag
24064          * @param {Event} e the event
24065          * @private
24066          * @static
24067          */
24068         stopDrag: function(e) {
24069             // Fire the drag end event for the item that was dragged
24070             if (this.dragCurrent) {
24071                 if (this.dragThreshMet) {
24072                     this.dragCurrent.b4EndDrag(e);
24073                     this.dragCurrent.endDrag(e);
24074                 }
24075
24076                 this.dragCurrent.onMouseUp(e);
24077             }
24078
24079             this.dragCurrent = null;
24080             this.dragOvers = {};
24081         },
24082
24083         /**
24084          * Internal function to handle the mousemove event.  Will be invoked
24085          * from the context of the html element.
24086          *
24087          * @TODO figure out what we can do about mouse events lost when the
24088          * user drags objects beyond the window boundary.  Currently we can
24089          * detect this in internet explorer by verifying that the mouse is
24090          * down during the mousemove event.  Firefox doesn't give us the
24091          * button state on the mousemove event.
24092          * @method handleMouseMove
24093          * @param {Event} e the event
24094          * @private
24095          * @static
24096          */
24097         handleMouseMove: function(e) {
24098             if (! this.dragCurrent) {
24099                 return true;
24100             }
24101
24102             // var button = e.which || e.button;
24103
24104             // check for IE mouseup outside of page boundary
24105             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
24106                 this.stopEvent(e);
24107                 return this.handleMouseUp(e);
24108             }
24109
24110             if (!this.dragThreshMet) {
24111                 var diffX = Math.abs(this.startX - e.getPageX());
24112                 var diffY = Math.abs(this.startY - e.getPageY());
24113                 if (diffX > this.clickPixelThresh ||
24114                             diffY > this.clickPixelThresh) {
24115                     this.startDrag(this.startX, this.startY);
24116                 }
24117             }
24118
24119             if (this.dragThreshMet) {
24120                 this.dragCurrent.b4Drag(e);
24121                 this.dragCurrent.onDrag(e);
24122                 if(!this.dragCurrent.moveOnly){
24123                     this.fireEvents(e, false);
24124                 }
24125             }
24126
24127             this.stopEvent(e);
24128
24129             return true;
24130         },
24131
24132         /**
24133          * Iterates over all of the DragDrop elements to find ones we are
24134          * hovering over or dropping on
24135          * @method fireEvents
24136          * @param {Event} e the event
24137          * @param {boolean} isDrop is this a drop op or a mouseover op?
24138          * @private
24139          * @static
24140          */
24141         fireEvents: function(e, isDrop) {
24142             var dc = this.dragCurrent;
24143
24144             // If the user did the mouse up outside of the window, we could
24145             // get here even though we have ended the drag.
24146             if (!dc || dc.isLocked()) {
24147                 return;
24148             }
24149
24150             var pt = e.getPoint();
24151
24152             // cache the previous dragOver array
24153             var oldOvers = [];
24154
24155             var outEvts   = [];
24156             var overEvts  = [];
24157             var dropEvts  = [];
24158             var enterEvts = [];
24159
24160             // Check to see if the object(s) we were hovering over is no longer
24161             // being hovered over so we can fire the onDragOut event
24162             for (var i in this.dragOvers) {
24163
24164                 var ddo = this.dragOvers[i];
24165
24166                 if (! this.isTypeOfDD(ddo)) {
24167                     continue;
24168                 }
24169
24170                 if (! this.isOverTarget(pt, ddo, this.mode)) {
24171                     outEvts.push( ddo );
24172                 }
24173
24174                 oldOvers[i] = true;
24175                 delete this.dragOvers[i];
24176             }
24177
24178             for (var sGroup in dc.groups) {
24179
24180                 if ("string" != typeof sGroup) {
24181                     continue;
24182                 }
24183
24184                 for (i in this.ids[sGroup]) {
24185                     var oDD = this.ids[sGroup][i];
24186                     if (! this.isTypeOfDD(oDD)) {
24187                         continue;
24188                     }
24189
24190                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
24191                         if (this.isOverTarget(pt, oDD, this.mode)) {
24192                             // look for drop interactions
24193                             if (isDrop) {
24194                                 dropEvts.push( oDD );
24195                             // look for drag enter and drag over interactions
24196                             } else {
24197
24198                                 // initial drag over: dragEnter fires
24199                                 if (!oldOvers[oDD.id]) {
24200                                     enterEvts.push( oDD );
24201                                 // subsequent drag overs: dragOver fires
24202                                 } else {
24203                                     overEvts.push( oDD );
24204                                 }
24205
24206                                 this.dragOvers[oDD.id] = oDD;
24207                             }
24208                         }
24209                     }
24210                 }
24211             }
24212
24213             if (this.mode) {
24214                 if (outEvts.length) {
24215                     dc.b4DragOut(e, outEvts);
24216                     dc.onDragOut(e, outEvts);
24217                 }
24218
24219                 if (enterEvts.length) {
24220                     dc.onDragEnter(e, enterEvts);
24221                 }
24222
24223                 if (overEvts.length) {
24224                     dc.b4DragOver(e, overEvts);
24225                     dc.onDragOver(e, overEvts);
24226                 }
24227
24228                 if (dropEvts.length) {
24229                     dc.b4DragDrop(e, dropEvts);
24230                     dc.onDragDrop(e, dropEvts);
24231                 }
24232
24233             } else {
24234                 // fire dragout events
24235                 var len = 0;
24236                 for (i=0, len=outEvts.length; i<len; ++i) {
24237                     dc.b4DragOut(e, outEvts[i].id);
24238                     dc.onDragOut(e, outEvts[i].id);
24239                 }
24240
24241                 // fire enter events
24242                 for (i=0,len=enterEvts.length; i<len; ++i) {
24243                     // dc.b4DragEnter(e, oDD.id);
24244                     dc.onDragEnter(e, enterEvts[i].id);
24245                 }
24246
24247                 // fire over events
24248                 for (i=0,len=overEvts.length; i<len; ++i) {
24249                     dc.b4DragOver(e, overEvts[i].id);
24250                     dc.onDragOver(e, overEvts[i].id);
24251                 }
24252
24253                 // fire drop events
24254                 for (i=0, len=dropEvts.length; i<len; ++i) {
24255                     dc.b4DragDrop(e, dropEvts[i].id);
24256                     dc.onDragDrop(e, dropEvts[i].id);
24257                 }
24258
24259             }
24260
24261             // notify about a drop that did not find a target
24262             if (isDrop && !dropEvts.length) {
24263                 dc.onInvalidDrop(e);
24264             }
24265
24266         },
24267
24268         /**
24269          * Helper function for getting the best match from the list of drag
24270          * and drop objects returned by the drag and drop events when we are
24271          * in INTERSECT mode.  It returns either the first object that the
24272          * cursor is over, or the object that has the greatest overlap with
24273          * the dragged element.
24274          * @method getBestMatch
24275          * @param  {DragDrop[]} dds The array of drag and drop objects
24276          * targeted
24277          * @return {DragDrop}       The best single match
24278          * @static
24279          */
24280         getBestMatch: function(dds) {
24281             var winner = null;
24282             // Return null if the input is not what we expect
24283             //if (!dds || !dds.length || dds.length == 0) {
24284                // winner = null;
24285             // If there is only one item, it wins
24286             //} else if (dds.length == 1) {
24287
24288             var len = dds.length;
24289
24290             if (len == 1) {
24291                 winner = dds[0];
24292             } else {
24293                 // Loop through the targeted items
24294                 for (var i=0; i<len; ++i) {
24295                     var dd = dds[i];
24296                     // If the cursor is over the object, it wins.  If the
24297                     // cursor is over multiple matches, the first one we come
24298                     // to wins.
24299                     if (dd.cursorIsOver) {
24300                         winner = dd;
24301                         break;
24302                     // Otherwise the object with the most overlap wins
24303                     } else {
24304                         if (!winner ||
24305                             winner.overlap.getArea() < dd.overlap.getArea()) {
24306                             winner = dd;
24307                         }
24308                     }
24309                 }
24310             }
24311
24312             return winner;
24313         },
24314
24315         /**
24316          * Refreshes the cache of the top-left and bottom-right points of the
24317          * drag and drop objects in the specified group(s).  This is in the
24318          * format that is stored in the drag and drop instance, so typical
24319          * usage is:
24320          * <code>
24321          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
24322          * </code>
24323          * Alternatively:
24324          * <code>
24325          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
24326          * </code>
24327          * @TODO this really should be an indexed array.  Alternatively this
24328          * method could accept both.
24329          * @method refreshCache
24330          * @param {Object} groups an associative array of groups to refresh
24331          * @static
24332          */
24333         refreshCache: function(groups) {
24334             for (var sGroup in groups) {
24335                 if ("string" != typeof sGroup) {
24336                     continue;
24337                 }
24338                 for (var i in this.ids[sGroup]) {
24339                     var oDD = this.ids[sGroup][i];
24340
24341                     if (this.isTypeOfDD(oDD)) {
24342                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
24343                         var loc = this.getLocation(oDD);
24344                         if (loc) {
24345                             this.locationCache[oDD.id] = loc;
24346                         } else {
24347                             delete this.locationCache[oDD.id];
24348                             // this will unregister the drag and drop object if
24349                             // the element is not in a usable state
24350                             // oDD.unreg();
24351                         }
24352                     }
24353                 }
24354             }
24355         },
24356
24357         /**
24358          * This checks to make sure an element exists and is in the DOM.  The
24359          * main purpose is to handle cases where innerHTML is used to remove
24360          * drag and drop objects from the DOM.  IE provides an 'unspecified
24361          * error' when trying to access the offsetParent of such an element
24362          * @method verifyEl
24363          * @param {HTMLElement} el the element to check
24364          * @return {boolean} true if the element looks usable
24365          * @static
24366          */
24367         verifyEl: function(el) {
24368             if (el) {
24369                 var parent;
24370                 if(Roo.isIE){
24371                     try{
24372                         parent = el.offsetParent;
24373                     }catch(e){}
24374                 }else{
24375                     parent = el.offsetParent;
24376                 }
24377                 if (parent) {
24378                     return true;
24379                 }
24380             }
24381
24382             return false;
24383         },
24384
24385         /**
24386          * Returns a Region object containing the drag and drop element's position
24387          * and size, including the padding configured for it
24388          * @method getLocation
24389          * @param {DragDrop} oDD the drag and drop object to get the
24390          *                       location for
24391          * @return {Roo.lib.Region} a Region object representing the total area
24392          *                             the element occupies, including any padding
24393          *                             the instance is configured for.
24394          * @static
24395          */
24396         getLocation: function(oDD) {
24397             if (! this.isTypeOfDD(oDD)) {
24398                 return null;
24399             }
24400
24401             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
24402
24403             try {
24404                 pos= Roo.lib.Dom.getXY(el);
24405             } catch (e) { }
24406
24407             if (!pos) {
24408                 return null;
24409             }
24410
24411             x1 = pos[0];
24412             x2 = x1 + el.offsetWidth;
24413             y1 = pos[1];
24414             y2 = y1 + el.offsetHeight;
24415
24416             t = y1 - oDD.padding[0];
24417             r = x2 + oDD.padding[1];
24418             b = y2 + oDD.padding[2];
24419             l = x1 - oDD.padding[3];
24420
24421             return new Roo.lib.Region( t, r, b, l );
24422         },
24423
24424         /**
24425          * Checks the cursor location to see if it over the target
24426          * @method isOverTarget
24427          * @param {Roo.lib.Point} pt The point to evaluate
24428          * @param {DragDrop} oTarget the DragDrop object we are inspecting
24429          * @return {boolean} true if the mouse is over the target
24430          * @private
24431          * @static
24432          */
24433         isOverTarget: function(pt, oTarget, intersect) {
24434             // use cache if available
24435             var loc = this.locationCache[oTarget.id];
24436             if (!loc || !this.useCache) {
24437                 loc = this.getLocation(oTarget);
24438                 this.locationCache[oTarget.id] = loc;
24439
24440             }
24441
24442             if (!loc) {
24443                 return false;
24444             }
24445
24446             oTarget.cursorIsOver = loc.contains( pt );
24447
24448             // DragDrop is using this as a sanity check for the initial mousedown
24449             // in this case we are done.  In POINT mode, if the drag obj has no
24450             // contraints, we are also done. Otherwise we need to evaluate the
24451             // location of the target as related to the actual location of the
24452             // dragged element.
24453             var dc = this.dragCurrent;
24454             if (!dc || !dc.getTargetCoord ||
24455                     (!intersect && !dc.constrainX && !dc.constrainY)) {
24456                 return oTarget.cursorIsOver;
24457             }
24458
24459             oTarget.overlap = null;
24460
24461             // Get the current location of the drag element, this is the
24462             // location of the mouse event less the delta that represents
24463             // where the original mousedown happened on the element.  We
24464             // need to consider constraints and ticks as well.
24465             var pos = dc.getTargetCoord(pt.x, pt.y);
24466
24467             var el = dc.getDragEl();
24468             var curRegion = new Roo.lib.Region( pos.y,
24469                                                    pos.x + el.offsetWidth,
24470                                                    pos.y + el.offsetHeight,
24471                                                    pos.x );
24472
24473             var overlap = curRegion.intersect(loc);
24474
24475             if (overlap) {
24476                 oTarget.overlap = overlap;
24477                 return (intersect) ? true : oTarget.cursorIsOver;
24478             } else {
24479                 return false;
24480             }
24481         },
24482
24483         /**
24484          * unload event handler
24485          * @method _onUnload
24486          * @private
24487          * @static
24488          */
24489         _onUnload: function(e, me) {
24490             Roo.dd.DragDropMgr.unregAll();
24491         },
24492
24493         /**
24494          * Cleans up the drag and drop events and objects.
24495          * @method unregAll
24496          * @private
24497          * @static
24498          */
24499         unregAll: function() {
24500
24501             if (this.dragCurrent) {
24502                 this.stopDrag();
24503                 this.dragCurrent = null;
24504             }
24505
24506             this._execOnAll("unreg", []);
24507
24508             for (i in this.elementCache) {
24509                 delete this.elementCache[i];
24510             }
24511
24512             this.elementCache = {};
24513             this.ids = {};
24514         },
24515
24516         /**
24517          * A cache of DOM elements
24518          * @property elementCache
24519          * @private
24520          * @static
24521          */
24522         elementCache: {},
24523
24524         /**
24525          * Get the wrapper for the DOM element specified
24526          * @method getElWrapper
24527          * @param {String} id the id of the element to get
24528          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
24529          * @private
24530          * @deprecated This wrapper isn't that useful
24531          * @static
24532          */
24533         getElWrapper: function(id) {
24534             var oWrapper = this.elementCache[id];
24535             if (!oWrapper || !oWrapper.el) {
24536                 oWrapper = this.elementCache[id] =
24537                     new this.ElementWrapper(Roo.getDom(id));
24538             }
24539             return oWrapper;
24540         },
24541
24542         /**
24543          * Returns the actual DOM element
24544          * @method getElement
24545          * @param {String} id the id of the elment to get
24546          * @return {Object} The element
24547          * @deprecated use Roo.getDom instead
24548          * @static
24549          */
24550         getElement: function(id) {
24551             return Roo.getDom(id);
24552         },
24553
24554         /**
24555          * Returns the style property for the DOM element (i.e.,
24556          * document.getElById(id).style)
24557          * @method getCss
24558          * @param {String} id the id of the elment to get
24559          * @return {Object} The style property of the element
24560          * @deprecated use Roo.getDom instead
24561          * @static
24562          */
24563         getCss: function(id) {
24564             var el = Roo.getDom(id);
24565             return (el) ? el.style : null;
24566         },
24567
24568         /**
24569          * Inner class for cached elements
24570          * @class DragDropMgr.ElementWrapper
24571          * @for DragDropMgr
24572          * @private
24573          * @deprecated
24574          */
24575         ElementWrapper: function(el) {
24576                 /**
24577                  * The element
24578                  * @property el
24579                  */
24580                 this.el = el || null;
24581                 /**
24582                  * The element id
24583                  * @property id
24584                  */
24585                 this.id = this.el && el.id;
24586                 /**
24587                  * A reference to the style property
24588                  * @property css
24589                  */
24590                 this.css = this.el && el.style;
24591             },
24592
24593         /**
24594          * Returns the X position of an html element
24595          * @method getPosX
24596          * @param el the element for which to get the position
24597          * @return {int} the X coordinate
24598          * @for DragDropMgr
24599          * @deprecated use Roo.lib.Dom.getX instead
24600          * @static
24601          */
24602         getPosX: function(el) {
24603             return Roo.lib.Dom.getX(el);
24604         },
24605
24606         /**
24607          * Returns the Y position of an html element
24608          * @method getPosY
24609          * @param el the element for which to get the position
24610          * @return {int} the Y coordinate
24611          * @deprecated use Roo.lib.Dom.getY instead
24612          * @static
24613          */
24614         getPosY: function(el) {
24615             return Roo.lib.Dom.getY(el);
24616         },
24617
24618         /**
24619          * Swap two nodes.  In IE, we use the native method, for others we
24620          * emulate the IE behavior
24621          * @method swapNode
24622          * @param n1 the first node to swap
24623          * @param n2 the other node to swap
24624          * @static
24625          */
24626         swapNode: function(n1, n2) {
24627             if (n1.swapNode) {
24628                 n1.swapNode(n2);
24629             } else {
24630                 var p = n2.parentNode;
24631                 var s = n2.nextSibling;
24632
24633                 if (s == n1) {
24634                     p.insertBefore(n1, n2);
24635                 } else if (n2 == n1.nextSibling) {
24636                     p.insertBefore(n2, n1);
24637                 } else {
24638                     n1.parentNode.replaceChild(n2, n1);
24639                     p.insertBefore(n1, s);
24640                 }
24641             }
24642         },
24643
24644         /**
24645          * Returns the current scroll position
24646          * @method getScroll
24647          * @private
24648          * @static
24649          */
24650         getScroll: function () {
24651             var t, l, dde=document.documentElement, db=document.body;
24652             if (dde && (dde.scrollTop || dde.scrollLeft)) {
24653                 t = dde.scrollTop;
24654                 l = dde.scrollLeft;
24655             } else if (db) {
24656                 t = db.scrollTop;
24657                 l = db.scrollLeft;
24658             } else {
24659
24660             }
24661             return { top: t, left: l };
24662         },
24663
24664         /**
24665          * Returns the specified element style property
24666          * @method getStyle
24667          * @param {HTMLElement} el          the element
24668          * @param {string}      styleProp   the style property
24669          * @return {string} The value of the style property
24670          * @deprecated use Roo.lib.Dom.getStyle
24671          * @static
24672          */
24673         getStyle: function(el, styleProp) {
24674             return Roo.fly(el).getStyle(styleProp);
24675         },
24676
24677         /**
24678          * Gets the scrollTop
24679          * @method getScrollTop
24680          * @return {int} the document's scrollTop
24681          * @static
24682          */
24683         getScrollTop: function () { return this.getScroll().top; },
24684
24685         /**
24686          * Gets the scrollLeft
24687          * @method getScrollLeft
24688          * @return {int} the document's scrollTop
24689          * @static
24690          */
24691         getScrollLeft: function () { return this.getScroll().left; },
24692
24693         /**
24694          * Sets the x/y position of an element to the location of the
24695          * target element.
24696          * @method moveToEl
24697          * @param {HTMLElement} moveEl      The element to move
24698          * @param {HTMLElement} targetEl    The position reference element
24699          * @static
24700          */
24701         moveToEl: function (moveEl, targetEl) {
24702             var aCoord = Roo.lib.Dom.getXY(targetEl);
24703             Roo.lib.Dom.setXY(moveEl, aCoord);
24704         },
24705
24706         /**
24707          * Numeric array sort function
24708          * @method numericSort
24709          * @static
24710          */
24711         numericSort: function(a, b) { return (a - b); },
24712
24713         /**
24714          * Internal counter
24715          * @property _timeoutCount
24716          * @private
24717          * @static
24718          */
24719         _timeoutCount: 0,
24720
24721         /**
24722          * Trying to make the load order less important.  Without this we get
24723          * an error if this file is loaded before the Event Utility.
24724          * @method _addListeners
24725          * @private
24726          * @static
24727          */
24728         _addListeners: function() {
24729             var DDM = Roo.dd.DDM;
24730             if ( Roo.lib.Event && document ) {
24731                 DDM._onLoad();
24732             } else {
24733                 if (DDM._timeoutCount > 2000) {
24734                 } else {
24735                     setTimeout(DDM._addListeners, 10);
24736                     if (document && document.body) {
24737                         DDM._timeoutCount += 1;
24738                     }
24739                 }
24740             }
24741         },
24742
24743         /**
24744          * Recursively searches the immediate parent and all child nodes for
24745          * the handle element in order to determine wheter or not it was
24746          * clicked.
24747          * @method handleWasClicked
24748          * @param node the html element to inspect
24749          * @static
24750          */
24751         handleWasClicked: function(node, id) {
24752             if (this.isHandle(id, node.id)) {
24753                 return true;
24754             } else {
24755                 // check to see if this is a text node child of the one we want
24756                 var p = node.parentNode;
24757
24758                 while (p) {
24759                     if (this.isHandle(id, p.id)) {
24760                         return true;
24761                     } else {
24762                         p = p.parentNode;
24763                     }
24764                 }
24765             }
24766
24767             return false;
24768         }
24769
24770     };
24771
24772 }();
24773
24774 // shorter alias, save a few bytes
24775 Roo.dd.DDM = Roo.dd.DragDropMgr;
24776 Roo.dd.DDM._addListeners();
24777
24778 }/*
24779  * Based on:
24780  * Ext JS Library 1.1.1
24781  * Copyright(c) 2006-2007, Ext JS, LLC.
24782  *
24783  * Originally Released Under LGPL - original licence link has changed is not relivant.
24784  *
24785  * Fork - LGPL
24786  * <script type="text/javascript">
24787  */
24788
24789 /**
24790  * @class Roo.dd.DD
24791  * A DragDrop implementation where the linked element follows the
24792  * mouse cursor during a drag.
24793  * @extends Roo.dd.DragDrop
24794  * @constructor
24795  * @param {String} id the id of the linked element
24796  * @param {String} sGroup the group of related DragDrop items
24797  * @param {object} config an object containing configurable attributes
24798  *                Valid properties for DD:
24799  *                    scroll
24800  */
24801 Roo.dd.DD = function(id, sGroup, config) {
24802     if (id) {
24803         this.init(id, sGroup, config);
24804     }
24805 };
24806
24807 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
24808
24809     /**
24810      * When set to true, the utility automatically tries to scroll the browser
24811      * window wehn a drag and drop element is dragged near the viewport boundary.
24812      * Defaults to true.
24813      * @property scroll
24814      * @type boolean
24815      */
24816     scroll: true,
24817
24818     /**
24819      * Sets the pointer offset to the distance between the linked element's top
24820      * left corner and the location the element was clicked
24821      * @method autoOffset
24822      * @param {int} iPageX the X coordinate of the click
24823      * @param {int} iPageY the Y coordinate of the click
24824      */
24825     autoOffset: function(iPageX, iPageY) {
24826         var x = iPageX - this.startPageX;
24827         var y = iPageY - this.startPageY;
24828         this.setDelta(x, y);
24829     },
24830
24831     /**
24832      * Sets the pointer offset.  You can call this directly to force the
24833      * offset to be in a particular location (e.g., pass in 0,0 to set it
24834      * to the center of the object)
24835      * @method setDelta
24836      * @param {int} iDeltaX the distance from the left
24837      * @param {int} iDeltaY the distance from the top
24838      */
24839     setDelta: function(iDeltaX, iDeltaY) {
24840         this.deltaX = iDeltaX;
24841         this.deltaY = iDeltaY;
24842     },
24843
24844     /**
24845      * Sets the drag element to the location of the mousedown or click event,
24846      * maintaining the cursor location relative to the location on the element
24847      * that was clicked.  Override this if you want to place the element in a
24848      * location other than where the cursor is.
24849      * @method setDragElPos
24850      * @param {int} iPageX the X coordinate of the mousedown or drag event
24851      * @param {int} iPageY the Y coordinate of the mousedown or drag event
24852      */
24853     setDragElPos: function(iPageX, iPageY) {
24854         // the first time we do this, we are going to check to make sure
24855         // the element has css positioning
24856
24857         var el = this.getDragEl();
24858         this.alignElWithMouse(el, iPageX, iPageY);
24859     },
24860
24861     /**
24862      * Sets the element to the location of the mousedown or click event,
24863      * maintaining the cursor location relative to the location on the element
24864      * that was clicked.  Override this if you want to place the element in a
24865      * location other than where the cursor is.
24866      * @method alignElWithMouse
24867      * @param {HTMLElement} el the element to move
24868      * @param {int} iPageX the X coordinate of the mousedown or drag event
24869      * @param {int} iPageY the Y coordinate of the mousedown or drag event
24870      */
24871     alignElWithMouse: function(el, iPageX, iPageY) {
24872         var oCoord = this.getTargetCoord(iPageX, iPageY);
24873         var fly = el.dom ? el : Roo.fly(el);
24874         if (!this.deltaSetXY) {
24875             var aCoord = [oCoord.x, oCoord.y];
24876             fly.setXY(aCoord);
24877             var newLeft = fly.getLeft(true);
24878             var newTop  = fly.getTop(true);
24879             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
24880         } else {
24881             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
24882         }
24883
24884         this.cachePosition(oCoord.x, oCoord.y);
24885         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
24886         return oCoord;
24887     },
24888
24889     /**
24890      * Saves the most recent position so that we can reset the constraints and
24891      * tick marks on-demand.  We need to know this so that we can calculate the
24892      * number of pixels the element is offset from its original position.
24893      * @method cachePosition
24894      * @param iPageX the current x position (optional, this just makes it so we
24895      * don't have to look it up again)
24896      * @param iPageY the current y position (optional, this just makes it so we
24897      * don't have to look it up again)
24898      */
24899     cachePosition: function(iPageX, iPageY) {
24900         if (iPageX) {
24901             this.lastPageX = iPageX;
24902             this.lastPageY = iPageY;
24903         } else {
24904             var aCoord = Roo.lib.Dom.getXY(this.getEl());
24905             this.lastPageX = aCoord[0];
24906             this.lastPageY = aCoord[1];
24907         }
24908     },
24909
24910     /**
24911      * Auto-scroll the window if the dragged object has been moved beyond the
24912      * visible window boundary.
24913      * @method autoScroll
24914      * @param {int} x the drag element's x position
24915      * @param {int} y the drag element's y position
24916      * @param {int} h the height of the drag element
24917      * @param {int} w the width of the drag element
24918      * @private
24919      */
24920     autoScroll: function(x, y, h, w) {
24921
24922         if (this.scroll) {
24923             // The client height
24924             var clientH = Roo.lib.Dom.getViewWidth();
24925
24926             // The client width
24927             var clientW = Roo.lib.Dom.getViewHeight();
24928
24929             // The amt scrolled down
24930             var st = this.DDM.getScrollTop();
24931
24932             // The amt scrolled right
24933             var sl = this.DDM.getScrollLeft();
24934
24935             // Location of the bottom of the element
24936             var bot = h + y;
24937
24938             // Location of the right of the element
24939             var right = w + x;
24940
24941             // The distance from the cursor to the bottom of the visible area,
24942             // adjusted so that we don't scroll if the cursor is beyond the
24943             // element drag constraints
24944             var toBot = (clientH + st - y - this.deltaY);
24945
24946             // The distance from the cursor to the right of the visible area
24947             var toRight = (clientW + sl - x - this.deltaX);
24948
24949
24950             // How close to the edge the cursor must be before we scroll
24951             // var thresh = (document.all) ? 100 : 40;
24952             var thresh = 40;
24953
24954             // How many pixels to scroll per autoscroll op.  This helps to reduce
24955             // clunky scrolling. IE is more sensitive about this ... it needs this
24956             // value to be higher.
24957             var scrAmt = (document.all) ? 80 : 30;
24958
24959             // Scroll down if we are near the bottom of the visible page and the
24960             // obj extends below the crease
24961             if ( bot > clientH && toBot < thresh ) {
24962                 window.scrollTo(sl, st + scrAmt);
24963             }
24964
24965             // Scroll up if the window is scrolled down and the top of the object
24966             // goes above the top border
24967             if ( y < st && st > 0 && y - st < thresh ) {
24968                 window.scrollTo(sl, st - scrAmt);
24969             }
24970
24971             // Scroll right if the obj is beyond the right border and the cursor is
24972             // near the border.
24973             if ( right > clientW && toRight < thresh ) {
24974                 window.scrollTo(sl + scrAmt, st);
24975             }
24976
24977             // Scroll left if the window has been scrolled to the right and the obj
24978             // extends past the left border
24979             if ( x < sl && sl > 0 && x - sl < thresh ) {
24980                 window.scrollTo(sl - scrAmt, st);
24981             }
24982         }
24983     },
24984
24985     /**
24986      * Finds the location the element should be placed if we want to move
24987      * it to where the mouse location less the click offset would place us.
24988      * @method getTargetCoord
24989      * @param {int} iPageX the X coordinate of the click
24990      * @param {int} iPageY the Y coordinate of the click
24991      * @return an object that contains the coordinates (Object.x and Object.y)
24992      * @private
24993      */
24994     getTargetCoord: function(iPageX, iPageY) {
24995
24996
24997         var x = iPageX - this.deltaX;
24998         var y = iPageY - this.deltaY;
24999
25000         if (this.constrainX) {
25001             if (x < this.minX) { x = this.minX; }
25002             if (x > this.maxX) { x = this.maxX; }
25003         }
25004
25005         if (this.constrainY) {
25006             if (y < this.minY) { y = this.minY; }
25007             if (y > this.maxY) { y = this.maxY; }
25008         }
25009
25010         x = this.getTick(x, this.xTicks);
25011         y = this.getTick(y, this.yTicks);
25012
25013
25014         return {x:x, y:y};
25015     },
25016
25017     /*
25018      * Sets up config options specific to this class. Overrides
25019      * Roo.dd.DragDrop, but all versions of this method through the
25020      * inheritance chain are called
25021      */
25022     applyConfig: function() {
25023         Roo.dd.DD.superclass.applyConfig.call(this);
25024         this.scroll = (this.config.scroll !== false);
25025     },
25026
25027     /*
25028      * Event that fires prior to the onMouseDown event.  Overrides
25029      * Roo.dd.DragDrop.
25030      */
25031     b4MouseDown: function(e) {
25032         // this.resetConstraints();
25033         this.autoOffset(e.getPageX(),
25034                             e.getPageY());
25035     },
25036
25037     /*
25038      * Event that fires prior to the onDrag event.  Overrides
25039      * Roo.dd.DragDrop.
25040      */
25041     b4Drag: function(e) {
25042         this.setDragElPos(e.getPageX(),
25043                             e.getPageY());
25044     },
25045
25046     toString: function() {
25047         return ("DD " + this.id);
25048     }
25049
25050     //////////////////////////////////////////////////////////////////////////
25051     // Debugging ygDragDrop events that can be overridden
25052     //////////////////////////////////////////////////////////////////////////
25053     /*
25054     startDrag: function(x, y) {
25055     },
25056
25057     onDrag: function(e) {
25058     },
25059
25060     onDragEnter: function(e, id) {
25061     },
25062
25063     onDragOver: function(e, id) {
25064     },
25065
25066     onDragOut: function(e, id) {
25067     },
25068
25069     onDragDrop: function(e, id) {
25070     },
25071
25072     endDrag: function(e) {
25073     }
25074
25075     */
25076
25077 });/*
25078  * Based on:
25079  * Ext JS Library 1.1.1
25080  * Copyright(c) 2006-2007, Ext JS, LLC.
25081  *
25082  * Originally Released Under LGPL - original licence link has changed is not relivant.
25083  *
25084  * Fork - LGPL
25085  * <script type="text/javascript">
25086  */
25087
25088 /**
25089  * @class Roo.dd.DDProxy
25090  * A DragDrop implementation that inserts an empty, bordered div into
25091  * the document that follows the cursor during drag operations.  At the time of
25092  * the click, the frame div is resized to the dimensions of the linked html
25093  * element, and moved to the exact location of the linked element.
25094  *
25095  * References to the "frame" element refer to the single proxy element that
25096  * was created to be dragged in place of all DDProxy elements on the
25097  * page.
25098  *
25099  * @extends Roo.dd.DD
25100  * @constructor
25101  * @param {String} id the id of the linked html element
25102  * @param {String} sGroup the group of related DragDrop objects
25103  * @param {object} config an object containing configurable attributes
25104  *                Valid properties for DDProxy in addition to those in DragDrop:
25105  *                   resizeFrame, centerFrame, dragElId
25106  */
25107 Roo.dd.DDProxy = function(id, sGroup, config) {
25108     if (id) {
25109         this.init(id, sGroup, config);
25110         this.initFrame();
25111     }
25112 };
25113
25114 /**
25115  * The default drag frame div id
25116  * @property Roo.dd.DDProxy.dragElId
25117  * @type String
25118  * @static
25119  */
25120 Roo.dd.DDProxy.dragElId = "ygddfdiv";
25121
25122 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
25123
25124     /**
25125      * By default we resize the drag frame to be the same size as the element
25126      * we want to drag (this is to get the frame effect).  We can turn it off
25127      * if we want a different behavior.
25128      * @property resizeFrame
25129      * @type boolean
25130      */
25131     resizeFrame: true,
25132
25133     /**
25134      * By default the frame is positioned exactly where the drag element is, so
25135      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
25136      * you do not have constraints on the obj is to have the drag frame centered
25137      * around the cursor.  Set centerFrame to true for this effect.
25138      * @property centerFrame
25139      * @type boolean
25140      */
25141     centerFrame: false,
25142
25143     /**
25144      * Creates the proxy element if it does not yet exist
25145      * @method createFrame
25146      */
25147     createFrame: function() {
25148         var self = this;
25149         var body = document.body;
25150
25151         if (!body || !body.firstChild) {
25152             setTimeout( function() { self.createFrame(); }, 50 );
25153             return;
25154         }
25155
25156         var div = this.getDragEl();
25157
25158         if (!div) {
25159             div    = document.createElement("div");
25160             div.id = this.dragElId;
25161             var s  = div.style;
25162
25163             s.position   = "absolute";
25164             s.visibility = "hidden";
25165             s.cursor     = "move";
25166             s.border     = "2px solid #aaa";
25167             s.zIndex     = 999;
25168
25169             // appendChild can blow up IE if invoked prior to the window load event
25170             // while rendering a table.  It is possible there are other scenarios
25171             // that would cause this to happen as well.
25172             body.insertBefore(div, body.firstChild);
25173         }
25174     },
25175
25176     /**
25177      * Initialization for the drag frame element.  Must be called in the
25178      * constructor of all subclasses
25179      * @method initFrame
25180      */
25181     initFrame: function() {
25182         this.createFrame();
25183     },
25184
25185     applyConfig: function() {
25186         Roo.dd.DDProxy.superclass.applyConfig.call(this);
25187
25188         this.resizeFrame = (this.config.resizeFrame !== false);
25189         this.centerFrame = (this.config.centerFrame);
25190         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
25191     },
25192
25193     /**
25194      * Resizes the drag frame to the dimensions of the clicked object, positions
25195      * it over the object, and finally displays it
25196      * @method showFrame
25197      * @param {int} iPageX X click position
25198      * @param {int} iPageY Y click position
25199      * @private
25200      */
25201     showFrame: function(iPageX, iPageY) {
25202         var el = this.getEl();
25203         var dragEl = this.getDragEl();
25204         var s = dragEl.style;
25205
25206         this._resizeProxy();
25207
25208         if (this.centerFrame) {
25209             this.setDelta( Math.round(parseInt(s.width,  10)/2),
25210                            Math.round(parseInt(s.height, 10)/2) );
25211         }
25212
25213         this.setDragElPos(iPageX, iPageY);
25214
25215         Roo.fly(dragEl).show();
25216     },
25217
25218     /**
25219      * The proxy is automatically resized to the dimensions of the linked
25220      * element when a drag is initiated, unless resizeFrame is set to false
25221      * @method _resizeProxy
25222      * @private
25223      */
25224     _resizeProxy: function() {
25225         if (this.resizeFrame) {
25226             var el = this.getEl();
25227             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
25228         }
25229     },
25230
25231     // overrides Roo.dd.DragDrop
25232     b4MouseDown: function(e) {
25233         var x = e.getPageX();
25234         var y = e.getPageY();
25235         this.autoOffset(x, y);
25236         this.setDragElPos(x, y);
25237     },
25238
25239     // overrides Roo.dd.DragDrop
25240     b4StartDrag: function(x, y) {
25241         // show the drag frame
25242         this.showFrame(x, y);
25243     },
25244
25245     // overrides Roo.dd.DragDrop
25246     b4EndDrag: function(e) {
25247         Roo.fly(this.getDragEl()).hide();
25248     },
25249
25250     // overrides Roo.dd.DragDrop
25251     // By default we try to move the element to the last location of the frame.
25252     // This is so that the default behavior mirrors that of Roo.dd.DD.
25253     endDrag: function(e) {
25254
25255         var lel = this.getEl();
25256         var del = this.getDragEl();
25257
25258         // Show the drag frame briefly so we can get its position
25259         del.style.visibility = "";
25260
25261         this.beforeMove();
25262         // Hide the linked element before the move to get around a Safari
25263         // rendering bug.
25264         lel.style.visibility = "hidden";
25265         Roo.dd.DDM.moveToEl(lel, del);
25266         del.style.visibility = "hidden";
25267         lel.style.visibility = "";
25268
25269         this.afterDrag();
25270     },
25271
25272     beforeMove : function(){
25273
25274     },
25275
25276     afterDrag : function(){
25277
25278     },
25279
25280     toString: function() {
25281         return ("DDProxy " + this.id);
25282     }
25283
25284 });
25285 /*
25286  * Based on:
25287  * Ext JS Library 1.1.1
25288  * Copyright(c) 2006-2007, Ext JS, LLC.
25289  *
25290  * Originally Released Under LGPL - original licence link has changed is not relivant.
25291  *
25292  * Fork - LGPL
25293  * <script type="text/javascript">
25294  */
25295
25296  /**
25297  * @class Roo.dd.DDTarget
25298  * A DragDrop implementation that does not move, but can be a drop
25299  * target.  You would get the same result by simply omitting implementation
25300  * for the event callbacks, but this way we reduce the processing cost of the
25301  * event listener and the callbacks.
25302  * @extends Roo.dd.DragDrop
25303  * @constructor
25304  * @param {String} id the id of the element that is a drop target
25305  * @param {String} sGroup the group of related DragDrop objects
25306  * @param {object} config an object containing configurable attributes
25307  *                 Valid properties for DDTarget in addition to those in
25308  *                 DragDrop:
25309  *                    none
25310  */
25311 Roo.dd.DDTarget = function(id, sGroup, config) {
25312     if (id) {
25313         this.initTarget(id, sGroup, config);
25314     }
25315     if (config.listeners || config.events) { 
25316        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
25317             listeners : config.listeners || {}, 
25318             events : config.events || {} 
25319         });    
25320     }
25321 };
25322
25323 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
25324 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
25325     toString: function() {
25326         return ("DDTarget " + this.id);
25327     }
25328 });
25329 /*
25330  * Based on:
25331  * Ext JS Library 1.1.1
25332  * Copyright(c) 2006-2007, Ext JS, LLC.
25333  *
25334  * Originally Released Under LGPL - original licence link has changed is not relivant.
25335  *
25336  * Fork - LGPL
25337  * <script type="text/javascript">
25338  */
25339  
25340
25341 /**
25342  * @class Roo.dd.ScrollManager
25343  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
25344  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
25345  * @singleton
25346  */
25347 Roo.dd.ScrollManager = function(){
25348     var ddm = Roo.dd.DragDropMgr;
25349     var els = {};
25350     var dragEl = null;
25351     var proc = {};
25352     
25353     
25354     
25355     var onStop = function(e){
25356         dragEl = null;
25357         clearProc();
25358     };
25359     
25360     var triggerRefresh = function(){
25361         if(ddm.dragCurrent){
25362              ddm.refreshCache(ddm.dragCurrent.groups);
25363         }
25364     };
25365     
25366     var doScroll = function(){
25367         if(ddm.dragCurrent){
25368             var dds = Roo.dd.ScrollManager;
25369             if(!dds.animate){
25370                 if(proc.el.scroll(proc.dir, dds.increment)){
25371                     triggerRefresh();
25372                 }
25373             }else{
25374                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
25375             }
25376         }
25377     };
25378     
25379     var clearProc = function(){
25380         if(proc.id){
25381             clearInterval(proc.id);
25382         }
25383         proc.id = 0;
25384         proc.el = null;
25385         proc.dir = "";
25386     };
25387     
25388     var startProc = function(el, dir){
25389          Roo.log('scroll startproc');
25390         clearProc();
25391         proc.el = el;
25392         proc.dir = dir;
25393         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
25394     };
25395     
25396     var onFire = function(e, isDrop){
25397        
25398         if(isDrop || !ddm.dragCurrent){ return; }
25399         var dds = Roo.dd.ScrollManager;
25400         if(!dragEl || dragEl != ddm.dragCurrent){
25401             dragEl = ddm.dragCurrent;
25402             // refresh regions on drag start
25403             dds.refreshCache();
25404         }
25405         
25406         var xy = Roo.lib.Event.getXY(e);
25407         var pt = new Roo.lib.Point(xy[0], xy[1]);
25408         for(var id in els){
25409             var el = els[id], r = el._region;
25410             if(r && r.contains(pt) && el.isScrollable()){
25411                 if(r.bottom - pt.y <= dds.thresh){
25412                     if(proc.el != el){
25413                         startProc(el, "down");
25414                     }
25415                     return;
25416                 }else if(r.right - pt.x <= dds.thresh){
25417                     if(proc.el != el){
25418                         startProc(el, "left");
25419                     }
25420                     return;
25421                 }else if(pt.y - r.top <= dds.thresh){
25422                     if(proc.el != el){
25423                         startProc(el, "up");
25424                     }
25425                     return;
25426                 }else if(pt.x - r.left <= dds.thresh){
25427                     if(proc.el != el){
25428                         startProc(el, "right");
25429                     }
25430                     return;
25431                 }
25432             }
25433         }
25434         clearProc();
25435     };
25436     
25437     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
25438     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
25439     
25440     return {
25441         /**
25442          * Registers new overflow element(s) to auto scroll
25443          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
25444          */
25445         register : function(el){
25446             if(el instanceof Array){
25447                 for(var i = 0, len = el.length; i < len; i++) {
25448                         this.register(el[i]);
25449                 }
25450             }else{
25451                 el = Roo.get(el);
25452                 els[el.id] = el;
25453             }
25454             Roo.dd.ScrollManager.els = els;
25455         },
25456         
25457         /**
25458          * Unregisters overflow element(s) so they are no longer scrolled
25459          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
25460          */
25461         unregister : function(el){
25462             if(el instanceof Array){
25463                 for(var i = 0, len = el.length; i < len; i++) {
25464                         this.unregister(el[i]);
25465                 }
25466             }else{
25467                 el = Roo.get(el);
25468                 delete els[el.id];
25469             }
25470         },
25471         
25472         /**
25473          * The number of pixels from the edge of a container the pointer needs to be to 
25474          * trigger scrolling (defaults to 25)
25475          * @type Number
25476          */
25477         thresh : 25,
25478         
25479         /**
25480          * The number of pixels to scroll in each scroll increment (defaults to 50)
25481          * @type Number
25482          */
25483         increment : 100,
25484         
25485         /**
25486          * The frequency of scrolls in milliseconds (defaults to 500)
25487          * @type Number
25488          */
25489         frequency : 500,
25490         
25491         /**
25492          * True to animate the scroll (defaults to true)
25493          * @type Boolean
25494          */
25495         animate: true,
25496         
25497         /**
25498          * The animation duration in seconds - 
25499          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
25500          * @type Number
25501          */
25502         animDuration: .4,
25503         
25504         /**
25505          * Manually trigger a cache refresh.
25506          */
25507         refreshCache : function(){
25508             for(var id in els){
25509                 if(typeof els[id] == 'object'){ // for people extending the object prototype
25510                     els[id]._region = els[id].getRegion();
25511                 }
25512             }
25513         }
25514     };
25515 }();/*
25516  * Based on:
25517  * Ext JS Library 1.1.1
25518  * Copyright(c) 2006-2007, Ext JS, LLC.
25519  *
25520  * Originally Released Under LGPL - original licence link has changed is not relivant.
25521  *
25522  * Fork - LGPL
25523  * <script type="text/javascript">
25524  */
25525  
25526
25527 /**
25528  * @class Roo.dd.Registry
25529  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
25530  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
25531  * @singleton
25532  */
25533 Roo.dd.Registry = function(){
25534     var elements = {}; 
25535     var handles = {}; 
25536     var autoIdSeed = 0;
25537
25538     var getId = function(el, autogen){
25539         if(typeof el == "string"){
25540             return el;
25541         }
25542         var id = el.id;
25543         if(!id && autogen !== false){
25544             id = "roodd-" + (++autoIdSeed);
25545             el.id = id;
25546         }
25547         return id;
25548     };
25549     
25550     return {
25551     /**
25552      * Register a drag drop element
25553      * @param {String|HTMLElement} element The id or DOM node to register
25554      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
25555      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
25556      * knows how to interpret, plus there are some specific properties known to the Registry that should be
25557      * populated in the data object (if applicable):
25558      * <pre>
25559 Value      Description<br />
25560 ---------  ------------------------------------------<br />
25561 handles    Array of DOM nodes that trigger dragging<br />
25562            for the element being registered<br />
25563 isHandle   True if the element passed in triggers<br />
25564            dragging itself, else false
25565 </pre>
25566      */
25567         register : function(el, data){
25568             data = data || {};
25569             if(typeof el == "string"){
25570                 el = document.getElementById(el);
25571             }
25572             data.ddel = el;
25573             elements[getId(el)] = data;
25574             if(data.isHandle !== false){
25575                 handles[data.ddel.id] = data;
25576             }
25577             if(data.handles){
25578                 var hs = data.handles;
25579                 for(var i = 0, len = hs.length; i < len; i++){
25580                         handles[getId(hs[i])] = data;
25581                 }
25582             }
25583         },
25584
25585     /**
25586      * Unregister a drag drop element
25587      * @param {String|HTMLElement}  element The id or DOM node to unregister
25588      */
25589         unregister : function(el){
25590             var id = getId(el, false);
25591             var data = elements[id];
25592             if(data){
25593                 delete elements[id];
25594                 if(data.handles){
25595                     var hs = data.handles;
25596                     for(var i = 0, len = hs.length; i < len; i++){
25597                         delete handles[getId(hs[i], false)];
25598                     }
25599                 }
25600             }
25601         },
25602
25603     /**
25604      * Returns the handle registered for a DOM Node by id
25605      * @param {String|HTMLElement} id The DOM node or id to look up
25606      * @return {Object} handle The custom handle data
25607      */
25608         getHandle : function(id){
25609             if(typeof id != "string"){ // must be element?
25610                 id = id.id;
25611             }
25612             return handles[id];
25613         },
25614
25615     /**
25616      * Returns the handle that is registered for the DOM node that is the target of the event
25617      * @param {Event} e The event
25618      * @return {Object} handle The custom handle data
25619      */
25620         getHandleFromEvent : function(e){
25621             var t = Roo.lib.Event.getTarget(e);
25622             return t ? handles[t.id] : null;
25623         },
25624
25625     /**
25626      * Returns a custom data object that is registered for a DOM node by id
25627      * @param {String|HTMLElement} id The DOM node or id to look up
25628      * @return {Object} data The custom data
25629      */
25630         getTarget : function(id){
25631             if(typeof id != "string"){ // must be element?
25632                 id = id.id;
25633             }
25634             return elements[id];
25635         },
25636
25637     /**
25638      * Returns a custom data object that is registered for the DOM node that is the target of the event
25639      * @param {Event} e The event
25640      * @return {Object} data The custom data
25641      */
25642         getTargetFromEvent : function(e){
25643             var t = Roo.lib.Event.getTarget(e);
25644             return t ? elements[t.id] || handles[t.id] : null;
25645         }
25646     };
25647 }();/*
25648  * Based on:
25649  * Ext JS Library 1.1.1
25650  * Copyright(c) 2006-2007, Ext JS, LLC.
25651  *
25652  * Originally Released Under LGPL - original licence link has changed is not relivant.
25653  *
25654  * Fork - LGPL
25655  * <script type="text/javascript">
25656  */
25657  
25658
25659 /**
25660  * @class Roo.dd.StatusProxy
25661  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
25662  * default drag proxy used by all Roo.dd components.
25663  * @constructor
25664  * @param {Object} config
25665  */
25666 Roo.dd.StatusProxy = function(config){
25667     Roo.apply(this, config);
25668     this.id = this.id || Roo.id();
25669     this.el = new Roo.Layer({
25670         dh: {
25671             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
25672                 {tag: "div", cls: "x-dd-drop-icon"},
25673                 {tag: "div", cls: "x-dd-drag-ghost"}
25674             ]
25675         }, 
25676         shadow: !config || config.shadow !== false
25677     });
25678     this.ghost = Roo.get(this.el.dom.childNodes[1]);
25679     this.dropStatus = this.dropNotAllowed;
25680 };
25681
25682 Roo.dd.StatusProxy.prototype = {
25683     /**
25684      * @cfg {String} dropAllowed
25685      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
25686      */
25687     dropAllowed : "x-dd-drop-ok",
25688     /**
25689      * @cfg {String} dropNotAllowed
25690      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
25691      */
25692     dropNotAllowed : "x-dd-drop-nodrop",
25693
25694     /**
25695      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
25696      * over the current target element.
25697      * @param {String} cssClass The css class for the new drop status indicator image
25698      */
25699     setStatus : function(cssClass){
25700         cssClass = cssClass || this.dropNotAllowed;
25701         if(this.dropStatus != cssClass){
25702             this.el.replaceClass(this.dropStatus, cssClass);
25703             this.dropStatus = cssClass;
25704         }
25705     },
25706
25707     /**
25708      * Resets the status indicator to the default dropNotAllowed value
25709      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
25710      */
25711     reset : function(clearGhost){
25712         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
25713         this.dropStatus = this.dropNotAllowed;
25714         if(clearGhost){
25715             this.ghost.update("");
25716         }
25717     },
25718
25719     /**
25720      * Updates the contents of the ghost element
25721      * @param {String} html The html that will replace the current innerHTML of the ghost element
25722      */
25723     update : function(html){
25724         if(typeof html == "string"){
25725             this.ghost.update(html);
25726         }else{
25727             this.ghost.update("");
25728             html.style.margin = "0";
25729             this.ghost.dom.appendChild(html);
25730         }
25731         // ensure float = none set?? cant remember why though.
25732         var el = this.ghost.dom.firstChild;
25733                 if(el){
25734                         Roo.fly(el).setStyle('float', 'none');
25735                 }
25736     },
25737     
25738     /**
25739      * Returns the underlying proxy {@link Roo.Layer}
25740      * @return {Roo.Layer} el
25741     */
25742     getEl : function(){
25743         return this.el;
25744     },
25745
25746     /**
25747      * Returns the ghost element
25748      * @return {Roo.Element} el
25749      */
25750     getGhost : function(){
25751         return this.ghost;
25752     },
25753
25754     /**
25755      * Hides the proxy
25756      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
25757      */
25758     hide : function(clear){
25759         this.el.hide();
25760         if(clear){
25761             this.reset(true);
25762         }
25763     },
25764
25765     /**
25766      * Stops the repair animation if it's currently running
25767      */
25768     stop : function(){
25769         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
25770             this.anim.stop();
25771         }
25772     },
25773
25774     /**
25775      * Displays this proxy
25776      */
25777     show : function(){
25778         this.el.show();
25779     },
25780
25781     /**
25782      * Force the Layer to sync its shadow and shim positions to the element
25783      */
25784     sync : function(){
25785         this.el.sync();
25786     },
25787
25788     /**
25789      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
25790      * invalid drop operation by the item being dragged.
25791      * @param {Array} xy The XY position of the element ([x, y])
25792      * @param {Function} callback The function to call after the repair is complete
25793      * @param {Object} scope The scope in which to execute the callback
25794      */
25795     repair : function(xy, callback, scope){
25796         this.callback = callback;
25797         this.scope = scope;
25798         if(xy && this.animRepair !== false){
25799             this.el.addClass("x-dd-drag-repair");
25800             this.el.hideUnders(true);
25801             this.anim = this.el.shift({
25802                 duration: this.repairDuration || .5,
25803                 easing: 'easeOut',
25804                 xy: xy,
25805                 stopFx: true,
25806                 callback: this.afterRepair,
25807                 scope: this
25808             });
25809         }else{
25810             this.afterRepair();
25811         }
25812     },
25813
25814     // private
25815     afterRepair : function(){
25816         this.hide(true);
25817         if(typeof this.callback == "function"){
25818             this.callback.call(this.scope || this);
25819         }
25820         this.callback = null;
25821         this.scope = null;
25822     }
25823 };/*
25824  * Based on:
25825  * Ext JS Library 1.1.1
25826  * Copyright(c) 2006-2007, Ext JS, LLC.
25827  *
25828  * Originally Released Under LGPL - original licence link has changed is not relivant.
25829  *
25830  * Fork - LGPL
25831  * <script type="text/javascript">
25832  */
25833
25834 /**
25835  * @class Roo.dd.DragSource
25836  * @extends Roo.dd.DDProxy
25837  * A simple class that provides the basic implementation needed to make any element draggable.
25838  * @constructor
25839  * @param {String/HTMLElement/Element} el The container element
25840  * @param {Object} config
25841  */
25842 Roo.dd.DragSource = function(el, config){
25843     this.el = Roo.get(el);
25844     this.dragData = {};
25845     
25846     Roo.apply(this, config);
25847     
25848     if(!this.proxy){
25849         this.proxy = new Roo.dd.StatusProxy();
25850     }
25851
25852     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
25853           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
25854     
25855     this.dragging = false;
25856 };
25857
25858 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
25859     /**
25860      * @cfg {String} dropAllowed
25861      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
25862      */
25863     dropAllowed : "x-dd-drop-ok",
25864     /**
25865      * @cfg {String} dropNotAllowed
25866      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
25867      */
25868     dropNotAllowed : "x-dd-drop-nodrop",
25869
25870     /**
25871      * Returns the data object associated with this drag source
25872      * @return {Object} data An object containing arbitrary data
25873      */
25874     getDragData : function(e){
25875         return this.dragData;
25876     },
25877
25878     // private
25879     onDragEnter : function(e, id){
25880         var target = Roo.dd.DragDropMgr.getDDById(id);
25881         this.cachedTarget = target;
25882         if(this.beforeDragEnter(target, e, id) !== false){
25883             if(target.isNotifyTarget){
25884                 var status = target.notifyEnter(this, e, this.dragData);
25885                 this.proxy.setStatus(status);
25886             }else{
25887                 this.proxy.setStatus(this.dropAllowed);
25888             }
25889             
25890             if(this.afterDragEnter){
25891                 /**
25892                  * An empty function by default, but provided so that you can perform a custom action
25893                  * when the dragged item enters the drop target by providing an implementation.
25894                  * @param {Roo.dd.DragDrop} target The drop target
25895                  * @param {Event} e The event object
25896                  * @param {String} id The id of the dragged element
25897                  * @method afterDragEnter
25898                  */
25899                 this.afterDragEnter(target, e, id);
25900             }
25901         }
25902     },
25903
25904     /**
25905      * An empty function by default, but provided so that you can perform a custom action
25906      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
25907      * @param {Roo.dd.DragDrop} target The drop target
25908      * @param {Event} e The event object
25909      * @param {String} id The id of the dragged element
25910      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
25911      */
25912     beforeDragEnter : function(target, e, id){
25913         return true;
25914     },
25915
25916     // private
25917     alignElWithMouse: function() {
25918         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
25919         this.proxy.sync();
25920     },
25921
25922     // private
25923     onDragOver : function(e, id){
25924         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
25925         if(this.beforeDragOver(target, e, id) !== false){
25926             if(target.isNotifyTarget){
25927                 var status = target.notifyOver(this, e, this.dragData);
25928                 this.proxy.setStatus(status);
25929             }
25930
25931             if(this.afterDragOver){
25932                 /**
25933                  * An empty function by default, but provided so that you can perform a custom action
25934                  * while the dragged item is over the drop target by providing an implementation.
25935                  * @param {Roo.dd.DragDrop} target The drop target
25936                  * @param {Event} e The event object
25937                  * @param {String} id The id of the dragged element
25938                  * @method afterDragOver
25939                  */
25940                 this.afterDragOver(target, e, id);
25941             }
25942         }
25943     },
25944
25945     /**
25946      * An empty function by default, but provided so that you can perform a custom action
25947      * while the dragged item is over the drop target and optionally cancel the onDragOver.
25948      * @param {Roo.dd.DragDrop} target The drop target
25949      * @param {Event} e The event object
25950      * @param {String} id The id of the dragged element
25951      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
25952      */
25953     beforeDragOver : function(target, e, id){
25954         return true;
25955     },
25956
25957     // private
25958     onDragOut : function(e, id){
25959         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
25960         if(this.beforeDragOut(target, e, id) !== false){
25961             if(target.isNotifyTarget){
25962                 target.notifyOut(this, e, this.dragData);
25963             }
25964             this.proxy.reset();
25965             if(this.afterDragOut){
25966                 /**
25967                  * An empty function by default, but provided so that you can perform a custom action
25968                  * after the dragged item is dragged out of the target without dropping.
25969                  * @param {Roo.dd.DragDrop} target The drop target
25970                  * @param {Event} e The event object
25971                  * @param {String} id The id of the dragged element
25972                  * @method afterDragOut
25973                  */
25974                 this.afterDragOut(target, e, id);
25975             }
25976         }
25977         this.cachedTarget = null;
25978     },
25979
25980     /**
25981      * An empty function by default, but provided so that you can perform a custom action before the dragged
25982      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
25983      * @param {Roo.dd.DragDrop} target The drop target
25984      * @param {Event} e The event object
25985      * @param {String} id The id of the dragged element
25986      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
25987      */
25988     beforeDragOut : function(target, e, id){
25989         return true;
25990     },
25991     
25992     // private
25993     onDragDrop : function(e, id){
25994         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
25995         if(this.beforeDragDrop(target, e, id) !== false){
25996             if(target.isNotifyTarget){
25997                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
25998                     this.onValidDrop(target, e, id);
25999                 }else{
26000                     this.onInvalidDrop(target, e, id);
26001                 }
26002             }else{
26003                 this.onValidDrop(target, e, id);
26004             }
26005             
26006             if(this.afterDragDrop){
26007                 /**
26008                  * An empty function by default, but provided so that you can perform a custom action
26009                  * after a valid drag drop has occurred by providing an implementation.
26010                  * @param {Roo.dd.DragDrop} target The drop target
26011                  * @param {Event} e The event object
26012                  * @param {String} id The id of the dropped element
26013                  * @method afterDragDrop
26014                  */
26015                 this.afterDragDrop(target, e, id);
26016             }
26017         }
26018         delete this.cachedTarget;
26019     },
26020
26021     /**
26022      * An empty function by default, but provided so that you can perform a custom action before the dragged
26023      * item is dropped onto the target and optionally cancel the onDragDrop.
26024      * @param {Roo.dd.DragDrop} target The drop target
26025      * @param {Event} e The event object
26026      * @param {String} id The id of the dragged element
26027      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
26028      */
26029     beforeDragDrop : function(target, e, id){
26030         return true;
26031     },
26032
26033     // private
26034     onValidDrop : function(target, e, id){
26035         this.hideProxy();
26036         if(this.afterValidDrop){
26037             /**
26038              * An empty function by default, but provided so that you can perform a custom action
26039              * after a valid drop has occurred by providing an implementation.
26040              * @param {Object} target The target DD 
26041              * @param {Event} e The event object
26042              * @param {String} id The id of the dropped element
26043              * @method afterInvalidDrop
26044              */
26045             this.afterValidDrop(target, e, id);
26046         }
26047     },
26048
26049     // private
26050     getRepairXY : function(e, data){
26051         return this.el.getXY();  
26052     },
26053
26054     // private
26055     onInvalidDrop : function(target, e, id){
26056         this.beforeInvalidDrop(target, e, id);
26057         if(this.cachedTarget){
26058             if(this.cachedTarget.isNotifyTarget){
26059                 this.cachedTarget.notifyOut(this, e, this.dragData);
26060             }
26061             this.cacheTarget = null;
26062         }
26063         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
26064
26065         if(this.afterInvalidDrop){
26066             /**
26067              * An empty function by default, but provided so that you can perform a custom action
26068              * after an invalid drop has occurred by providing an implementation.
26069              * @param {Event} e The event object
26070              * @param {String} id The id of the dropped element
26071              * @method afterInvalidDrop
26072              */
26073             this.afterInvalidDrop(e, id);
26074         }
26075     },
26076
26077     // private
26078     afterRepair : function(){
26079         if(Roo.enableFx){
26080             this.el.highlight(this.hlColor || "c3daf9");
26081         }
26082         this.dragging = false;
26083     },
26084
26085     /**
26086      * An empty function by default, but provided so that you can perform a custom action after an invalid
26087      * drop has occurred.
26088      * @param {Roo.dd.DragDrop} target The drop target
26089      * @param {Event} e The event object
26090      * @param {String} id The id of the dragged element
26091      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
26092      */
26093     beforeInvalidDrop : function(target, e, id){
26094         return true;
26095     },
26096
26097     // private
26098     handleMouseDown : function(e){
26099         if(this.dragging) {
26100             return;
26101         }
26102         var data = this.getDragData(e);
26103         if(data && this.onBeforeDrag(data, e) !== false){
26104             this.dragData = data;
26105             this.proxy.stop();
26106             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
26107         } 
26108     },
26109
26110     /**
26111      * An empty function by default, but provided so that you can perform a custom action before the initial
26112      * drag event begins and optionally cancel it.
26113      * @param {Object} data An object containing arbitrary data to be shared with drop targets
26114      * @param {Event} e The event object
26115      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
26116      */
26117     onBeforeDrag : function(data, e){
26118         return true;
26119     },
26120
26121     /**
26122      * An empty function by default, but provided so that you can perform a custom action once the initial
26123      * drag event has begun.  The drag cannot be canceled from this function.
26124      * @param {Number} x The x position of the click on the dragged object
26125      * @param {Number} y The y position of the click on the dragged object
26126      */
26127     onStartDrag : Roo.emptyFn,
26128
26129     // private - YUI override
26130     startDrag : function(x, y){
26131         this.proxy.reset();
26132         this.dragging = true;
26133         this.proxy.update("");
26134         this.onInitDrag(x, y);
26135         this.proxy.show();
26136     },
26137
26138     // private
26139     onInitDrag : function(x, y){
26140         var clone = this.el.dom.cloneNode(true);
26141         clone.id = Roo.id(); // prevent duplicate ids
26142         this.proxy.update(clone);
26143         this.onStartDrag(x, y);
26144         return true;
26145     },
26146
26147     /**
26148      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
26149      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
26150      */
26151     getProxy : function(){
26152         return this.proxy;  
26153     },
26154
26155     /**
26156      * Hides the drag source's {@link Roo.dd.StatusProxy}
26157      */
26158     hideProxy : function(){
26159         this.proxy.hide();  
26160         this.proxy.reset(true);
26161         this.dragging = false;
26162     },
26163
26164     // private
26165     triggerCacheRefresh : function(){
26166         Roo.dd.DDM.refreshCache(this.groups);
26167     },
26168
26169     // private - override to prevent hiding
26170     b4EndDrag: function(e) {
26171     },
26172
26173     // private - override to prevent moving
26174     endDrag : function(e){
26175         this.onEndDrag(this.dragData, e);
26176     },
26177
26178     // private
26179     onEndDrag : function(data, e){
26180     },
26181     
26182     // private - pin to cursor
26183     autoOffset : function(x, y) {
26184         this.setDelta(-12, -20);
26185     }    
26186 });/*
26187  * Based on:
26188  * Ext JS Library 1.1.1
26189  * Copyright(c) 2006-2007, Ext JS, LLC.
26190  *
26191  * Originally Released Under LGPL - original licence link has changed is not relivant.
26192  *
26193  * Fork - LGPL
26194  * <script type="text/javascript">
26195  */
26196
26197
26198 /**
26199  * @class Roo.dd.DropTarget
26200  * @extends Roo.dd.DDTarget
26201  * A simple class that provides the basic implementation needed to make any element a drop target that can have
26202  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
26203  * @constructor
26204  * @param {String/HTMLElement/Element} el The container element
26205  * @param {Object} config
26206  */
26207 Roo.dd.DropTarget = function(el, config){
26208     this.el = Roo.get(el);
26209     
26210     var listeners = false; ;
26211     if (config && config.listeners) {
26212         listeners= config.listeners;
26213         delete config.listeners;
26214     }
26215     Roo.apply(this, config);
26216     
26217     if(this.containerScroll){
26218         Roo.dd.ScrollManager.register(this.el);
26219     }
26220     this.addEvents( {
26221          /**
26222          * @scope Roo.dd.DropTarget
26223          */
26224          
26225          /**
26226          * @event enter
26227          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
26228          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
26229          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
26230          * 
26231          * IMPORTANT : it should set this.overClass and this.dropAllowed
26232          * 
26233          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
26234          * @param {Event} e The event
26235          * @param {Object} data An object containing arbitrary data supplied by the drag source
26236          */
26237         "enter" : true,
26238         
26239          /**
26240          * @event over
26241          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
26242          * This method will be called on every mouse movement while the drag source is over the drop target.
26243          * This default implementation simply returns the dropAllowed config value.
26244          * 
26245          * IMPORTANT : it should set this.dropAllowed
26246          * 
26247          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
26248          * @param {Event} e The event
26249          * @param {Object} data An object containing arbitrary data supplied by the drag source
26250          
26251          */
26252         "over" : true,
26253         /**
26254          * @event out
26255          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
26256          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
26257          * overClass (if any) from the drop element.
26258          * 
26259          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
26260          * @param {Event} e The event
26261          * @param {Object} data An object containing arbitrary data supplied by the drag source
26262          */
26263          "out" : true,
26264          
26265         /**
26266          * @event drop
26267          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
26268          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
26269          * implementation that does something to process the drop event and returns true so that the drag source's
26270          * repair action does not run.
26271          * 
26272          * IMPORTANT : it should set this.success
26273          * 
26274          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
26275          * @param {Event} e The event
26276          * @param {Object} data An object containing arbitrary data supplied by the drag source
26277         */
26278          "drop" : true
26279     });
26280             
26281      
26282     Roo.dd.DropTarget.superclass.constructor.call(  this, 
26283         this.el.dom, 
26284         this.ddGroup || this.group,
26285         {
26286             isTarget: true,
26287             listeners : listeners || {} 
26288            
26289         
26290         }
26291     );
26292
26293 };
26294
26295 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
26296     /**
26297      * @cfg {String} overClass
26298      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
26299      */
26300      /**
26301      * @cfg {String} ddGroup
26302      * The drag drop group to handle drop events for
26303      */
26304      
26305     /**
26306      * @cfg {String} dropAllowed
26307      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
26308      */
26309     dropAllowed : "x-dd-drop-ok",
26310     /**
26311      * @cfg {String} dropNotAllowed
26312      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
26313      */
26314     dropNotAllowed : "x-dd-drop-nodrop",
26315     /**
26316      * @cfg {boolean} success
26317      * set this after drop listener.. 
26318      */
26319     success : false,
26320     /**
26321      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
26322      * if the drop point is valid for over/enter..
26323      */
26324     valid : false,
26325     // private
26326     isTarget : true,
26327
26328     // private
26329     isNotifyTarget : true,
26330     
26331     /**
26332      * @hide
26333      */
26334     notifyEnter : function(dd, e, data)
26335     {
26336         this.valid = true;
26337         this.fireEvent('enter', dd, e, data);
26338         if(this.overClass){
26339             this.el.addClass(this.overClass);
26340         }
26341         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
26342             this.valid ? this.dropAllowed : this.dropNotAllowed
26343         );
26344     },
26345
26346     /**
26347      * @hide
26348      */
26349     notifyOver : function(dd, e, data)
26350     {
26351         this.valid = true;
26352         this.fireEvent('over', dd, e, data);
26353         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
26354             this.valid ? this.dropAllowed : this.dropNotAllowed
26355         );
26356     },
26357
26358     /**
26359      * @hide
26360      */
26361     notifyOut : function(dd, e, data)
26362     {
26363         this.fireEvent('out', dd, e, data);
26364         if(this.overClass){
26365             this.el.removeClass(this.overClass);
26366         }
26367     },
26368
26369     /**
26370      * @hide
26371      */
26372     notifyDrop : function(dd, e, data)
26373     {
26374         this.success = false;
26375         this.fireEvent('drop', dd, e, data);
26376         return this.success;
26377     }
26378 });/*
26379  * Based on:
26380  * Ext JS Library 1.1.1
26381  * Copyright(c) 2006-2007, Ext JS, LLC.
26382  *
26383  * Originally Released Under LGPL - original licence link has changed is not relivant.
26384  *
26385  * Fork - LGPL
26386  * <script type="text/javascript">
26387  */
26388
26389
26390 /**
26391  * @class Roo.dd.DragZone
26392  * @extends Roo.dd.DragSource
26393  * This class provides a container DD instance that proxies for multiple child node sources.<br />
26394  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
26395  * @constructor
26396  * @param {String/HTMLElement/Element} el The container element
26397  * @param {Object} config
26398  */
26399 Roo.dd.DragZone = function(el, config){
26400     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
26401     if(this.containerScroll){
26402         Roo.dd.ScrollManager.register(this.el);
26403     }
26404 };
26405
26406 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
26407     /**
26408      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
26409      * for auto scrolling during drag operations.
26410      */
26411     /**
26412      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
26413      * method after a failed drop (defaults to "c3daf9" - light blue)
26414      */
26415
26416     /**
26417      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
26418      * for a valid target to drag based on the mouse down. Override this method
26419      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
26420      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
26421      * @param {EventObject} e The mouse down event
26422      * @return {Object} The dragData
26423      */
26424     getDragData : function(e){
26425         return Roo.dd.Registry.getHandleFromEvent(e);
26426     },
26427     
26428     /**
26429      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
26430      * this.dragData.ddel
26431      * @param {Number} x The x position of the click on the dragged object
26432      * @param {Number} y The y position of the click on the dragged object
26433      * @return {Boolean} true to continue the drag, false to cancel
26434      */
26435     onInitDrag : function(x, y){
26436         this.proxy.update(this.dragData.ddel.cloneNode(true));
26437         this.onStartDrag(x, y);
26438         return true;
26439     },
26440     
26441     /**
26442      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
26443      */
26444     afterRepair : function(){
26445         if(Roo.enableFx){
26446             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
26447         }
26448         this.dragging = false;
26449     },
26450
26451     /**
26452      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
26453      * the XY of this.dragData.ddel
26454      * @param {EventObject} e The mouse up event
26455      * @return {Array} The xy location (e.g. [100, 200])
26456      */
26457     getRepairXY : function(e){
26458         return Roo.Element.fly(this.dragData.ddel).getXY();  
26459     }
26460 });/*
26461  * Based on:
26462  * Ext JS Library 1.1.1
26463  * Copyright(c) 2006-2007, Ext JS, LLC.
26464  *
26465  * Originally Released Under LGPL - original licence link has changed is not relivant.
26466  *
26467  * Fork - LGPL
26468  * <script type="text/javascript">
26469  */
26470 /**
26471  * @class Roo.dd.DropZone
26472  * @extends Roo.dd.DropTarget
26473  * This class provides a container DD instance that proxies for multiple child node targets.<br />
26474  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
26475  * @constructor
26476  * @param {String/HTMLElement/Element} el The container element
26477  * @param {Object} config
26478  */
26479 Roo.dd.DropZone = function(el, config){
26480     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
26481 };
26482
26483 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
26484     /**
26485      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
26486      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
26487      * provide your own custom lookup.
26488      * @param {Event} e The event
26489      * @return {Object} data The custom data
26490      */
26491     getTargetFromEvent : function(e){
26492         return Roo.dd.Registry.getTargetFromEvent(e);
26493     },
26494
26495     /**
26496      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
26497      * that it has registered.  This method has no default implementation and should be overridden to provide
26498      * node-specific processing if necessary.
26499      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
26500      * {@link #getTargetFromEvent} for this node)
26501      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
26502      * @param {Event} e The event
26503      * @param {Object} data An object containing arbitrary data supplied by the drag source
26504      */
26505     onNodeEnter : function(n, dd, e, data){
26506         
26507     },
26508
26509     /**
26510      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
26511      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
26512      * overridden to provide the proper feedback.
26513      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
26514      * {@link #getTargetFromEvent} for this node)
26515      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
26516      * @param {Event} e The event
26517      * @param {Object} data An object containing arbitrary data supplied by the drag source
26518      * @return {String} status The CSS class that communicates the drop status back to the source so that the
26519      * underlying {@link Roo.dd.StatusProxy} can be updated
26520      */
26521     onNodeOver : function(n, dd, e, data){
26522         return this.dropAllowed;
26523     },
26524
26525     /**
26526      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
26527      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
26528      * node-specific processing if necessary.
26529      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
26530      * {@link #getTargetFromEvent} for this node)
26531      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
26532      * @param {Event} e The event
26533      * @param {Object} data An object containing arbitrary data supplied by the drag source
26534      */
26535     onNodeOut : function(n, dd, e, data){
26536         
26537     },
26538
26539     /**
26540      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
26541      * the drop node.  The default implementation returns false, so it should be overridden to provide the
26542      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
26543      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
26544      * {@link #getTargetFromEvent} for this node)
26545      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
26546      * @param {Event} e The event
26547      * @param {Object} data An object containing arbitrary data supplied by the drag source
26548      * @return {Boolean} True if the drop was valid, else false
26549      */
26550     onNodeDrop : function(n, dd, e, data){
26551         return false;
26552     },
26553
26554     /**
26555      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
26556      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
26557      * it should be overridden to provide the proper feedback if necessary.
26558      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
26559      * @param {Event} e The event
26560      * @param {Object} data An object containing arbitrary data supplied by the drag source
26561      * @return {String} status The CSS class that communicates the drop status back to the source so that the
26562      * underlying {@link Roo.dd.StatusProxy} can be updated
26563      */
26564     onContainerOver : function(dd, e, data){
26565         return this.dropNotAllowed;
26566     },
26567
26568     /**
26569      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
26570      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
26571      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
26572      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
26573      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
26574      * @param {Event} e The event
26575      * @param {Object} data An object containing arbitrary data supplied by the drag source
26576      * @return {Boolean} True if the drop was valid, else false
26577      */
26578     onContainerDrop : function(dd, e, data){
26579         return false;
26580     },
26581
26582     /**
26583      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
26584      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
26585      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
26586      * you should override this method and provide a custom implementation.
26587      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
26588      * @param {Event} e The event
26589      * @param {Object} data An object containing arbitrary data supplied by the drag source
26590      * @return {String} status The CSS class that communicates the drop status back to the source so that the
26591      * underlying {@link Roo.dd.StatusProxy} can be updated
26592      */
26593     notifyEnter : function(dd, e, data){
26594         return this.dropNotAllowed;
26595     },
26596
26597     /**
26598      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
26599      * This method will be called on every mouse movement while the drag source is over the drop zone.
26600      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
26601      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
26602      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
26603      * registered node, it will call {@link #onContainerOver}.
26604      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
26605      * @param {Event} e The event
26606      * @param {Object} data An object containing arbitrary data supplied by the drag source
26607      * @return {String} status The CSS class that communicates the drop status back to the source so that the
26608      * underlying {@link Roo.dd.StatusProxy} can be updated
26609      */
26610     notifyOver : function(dd, e, data){
26611         var n = this.getTargetFromEvent(e);
26612         if(!n){ // not over valid drop target
26613             if(this.lastOverNode){
26614                 this.onNodeOut(this.lastOverNode, dd, e, data);
26615                 this.lastOverNode = null;
26616             }
26617             return this.onContainerOver(dd, e, data);
26618         }
26619         if(this.lastOverNode != n){
26620             if(this.lastOverNode){
26621                 this.onNodeOut(this.lastOverNode, dd, e, data);
26622             }
26623             this.onNodeEnter(n, dd, e, data);
26624             this.lastOverNode = n;
26625         }
26626         return this.onNodeOver(n, dd, e, data);
26627     },
26628
26629     /**
26630      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
26631      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
26632      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
26633      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
26634      * @param {Event} e The event
26635      * @param {Object} data An object containing arbitrary data supplied by the drag zone
26636      */
26637     notifyOut : function(dd, e, data){
26638         if(this.lastOverNode){
26639             this.onNodeOut(this.lastOverNode, dd, e, data);
26640             this.lastOverNode = null;
26641         }
26642     },
26643
26644     /**
26645      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
26646      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
26647      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
26648      * otherwise it will call {@link #onContainerDrop}.
26649      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
26650      * @param {Event} e The event
26651      * @param {Object} data An object containing arbitrary data supplied by the drag source
26652      * @return {Boolean} True if the drop was valid, else false
26653      */
26654     notifyDrop : function(dd, e, data){
26655         if(this.lastOverNode){
26656             this.onNodeOut(this.lastOverNode, dd, e, data);
26657             this.lastOverNode = null;
26658         }
26659         var n = this.getTargetFromEvent(e);
26660         return n ?
26661             this.onNodeDrop(n, dd, e, data) :
26662             this.onContainerDrop(dd, e, data);
26663     },
26664
26665     // private
26666     triggerCacheRefresh : function(){
26667         Roo.dd.DDM.refreshCache(this.groups);
26668     }  
26669 });/*
26670  * Based on:
26671  * Ext JS Library 1.1.1
26672  * Copyright(c) 2006-2007, Ext JS, LLC.
26673  *
26674  * Originally Released Under LGPL - original licence link has changed is not relivant.
26675  *
26676  * Fork - LGPL
26677  * <script type="text/javascript">
26678  */
26679
26680
26681 /**
26682  * @class Roo.data.SortTypes
26683  * @singleton
26684  * Defines the default sorting (casting?) comparison functions used when sorting data.
26685  */
26686 Roo.data.SortTypes = {
26687     /**
26688      * Default sort that does nothing
26689      * @param {Mixed} s The value being converted
26690      * @return {Mixed} The comparison value
26691      */
26692     none : function(s){
26693         return s;
26694     },
26695     
26696     /**
26697      * The regular expression used to strip tags
26698      * @type {RegExp}
26699      * @property
26700      */
26701     stripTagsRE : /<\/?[^>]+>/gi,
26702     
26703     /**
26704      * Strips all HTML tags to sort on text only
26705      * @param {Mixed} s The value being converted
26706      * @return {String} The comparison value
26707      */
26708     asText : function(s){
26709         return String(s).replace(this.stripTagsRE, "");
26710     },
26711     
26712     /**
26713      * Strips all HTML tags to sort on text only - Case insensitive
26714      * @param {Mixed} s The value being converted
26715      * @return {String} The comparison value
26716      */
26717     asUCText : function(s){
26718         return String(s).toUpperCase().replace(this.stripTagsRE, "");
26719     },
26720     
26721     /**
26722      * Case insensitive string
26723      * @param {Mixed} s The value being converted
26724      * @return {String} The comparison value
26725      */
26726     asUCString : function(s) {
26727         return String(s).toUpperCase();
26728     },
26729     
26730     /**
26731      * Date sorting
26732      * @param {Mixed} s The value being converted
26733      * @return {Number} The comparison value
26734      */
26735     asDate : function(s) {
26736         if(!s){
26737             return 0;
26738         }
26739         if(s instanceof Date){
26740             return s.getTime();
26741         }
26742         return Date.parse(String(s));
26743     },
26744     
26745     /**
26746      * Float sorting
26747      * @param {Mixed} s The value being converted
26748      * @return {Float} The comparison value
26749      */
26750     asFloat : function(s) {
26751         var val = parseFloat(String(s).replace(/,/g, ""));
26752         if(isNaN(val)) {
26753             val = 0;
26754         }
26755         return val;
26756     },
26757     
26758     /**
26759      * Integer sorting
26760      * @param {Mixed} s The value being converted
26761      * @return {Number} The comparison value
26762      */
26763     asInt : function(s) {
26764         var val = parseInt(String(s).replace(/,/g, ""));
26765         if(isNaN(val)) {
26766             val = 0;
26767         }
26768         return val;
26769     }
26770 };/*
26771  * Based on:
26772  * Ext JS Library 1.1.1
26773  * Copyright(c) 2006-2007, Ext JS, LLC.
26774  *
26775  * Originally Released Under LGPL - original licence link has changed is not relivant.
26776  *
26777  * Fork - LGPL
26778  * <script type="text/javascript">
26779  */
26780
26781 /**
26782 * @class Roo.data.Record
26783  * Instances of this class encapsulate both record <em>definition</em> information, and record
26784  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
26785  * to access Records cached in an {@link Roo.data.Store} object.<br>
26786  * <p>
26787  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
26788  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
26789  * objects.<br>
26790  * <p>
26791  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
26792  * @constructor
26793  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
26794  * {@link #create}. The parameters are the same.
26795  * @param {Array} data An associative Array of data values keyed by the field name.
26796  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
26797  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
26798  * not specified an integer id is generated.
26799  */
26800 Roo.data.Record = function(data, id){
26801     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
26802     this.data = data;
26803 };
26804
26805 /**
26806  * Generate a constructor for a specific record layout.
26807  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
26808  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
26809  * Each field definition object may contain the following properties: <ul>
26810  * <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,
26811  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
26812  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
26813  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
26814  * is being used, then this is a string containing the javascript expression to reference the data relative to 
26815  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
26816  * to the data item relative to the record element. If the mapping expression is the same as the field name,
26817  * this may be omitted.</p></li>
26818  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
26819  * <ul><li>auto (Default, implies no conversion)</li>
26820  * <li>string</li>
26821  * <li>int</li>
26822  * <li>float</li>
26823  * <li>boolean</li>
26824  * <li>date</li></ul></p></li>
26825  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
26826  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
26827  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
26828  * by the Reader into an object that will be stored in the Record. It is passed the
26829  * following parameters:<ul>
26830  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
26831  * </ul></p></li>
26832  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
26833  * </ul>
26834  * <br>usage:<br><pre><code>
26835 var TopicRecord = Roo.data.Record.create(
26836     {name: 'title', mapping: 'topic_title'},
26837     {name: 'author', mapping: 'username'},
26838     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
26839     {name: 'lastPost', mapping: 'post_time', type: 'date'},
26840     {name: 'lastPoster', mapping: 'user2'},
26841     {name: 'excerpt', mapping: 'post_text'}
26842 );
26843
26844 var myNewRecord = new TopicRecord({
26845     title: 'Do my job please',
26846     author: 'noobie',
26847     totalPosts: 1,
26848     lastPost: new Date(),
26849     lastPoster: 'Animal',
26850     excerpt: 'No way dude!'
26851 });
26852 myStore.add(myNewRecord);
26853 </code></pre>
26854  * @method create
26855  * @static
26856  */
26857 Roo.data.Record.create = function(o){
26858     var f = function(){
26859         f.superclass.constructor.apply(this, arguments);
26860     };
26861     Roo.extend(f, Roo.data.Record);
26862     var p = f.prototype;
26863     p.fields = new Roo.util.MixedCollection(false, function(field){
26864         return field.name;
26865     });
26866     for(var i = 0, len = o.length; i < len; i++){
26867         p.fields.add(new Roo.data.Field(o[i]));
26868     }
26869     f.getField = function(name){
26870         return p.fields.get(name);  
26871     };
26872     return f;
26873 };
26874
26875 Roo.data.Record.AUTO_ID = 1000;
26876 Roo.data.Record.EDIT = 'edit';
26877 Roo.data.Record.REJECT = 'reject';
26878 Roo.data.Record.COMMIT = 'commit';
26879
26880 Roo.data.Record.prototype = {
26881     /**
26882      * Readonly flag - true if this record has been modified.
26883      * @type Boolean
26884      */
26885     dirty : false,
26886     editing : false,
26887     error: null,
26888     modified: null,
26889
26890     // private
26891     join : function(store){
26892         this.store = store;
26893     },
26894
26895     /**
26896      * Set the named field to the specified value.
26897      * @param {String} name The name of the field to set.
26898      * @param {Object} value The value to set the field to.
26899      */
26900     set : function(name, value){
26901         if(this.data[name] == value){
26902             return;
26903         }
26904         this.dirty = true;
26905         if(!this.modified){
26906             this.modified = {};
26907         }
26908         if(typeof this.modified[name] == 'undefined'){
26909             this.modified[name] = this.data[name];
26910         }
26911         this.data[name] = value;
26912         if(!this.editing && this.store){
26913             this.store.afterEdit(this);
26914         }       
26915     },
26916
26917     /**
26918      * Get the value of the named field.
26919      * @param {String} name The name of the field to get the value of.
26920      * @return {Object} The value of the field.
26921      */
26922     get : function(name){
26923         return this.data[name]; 
26924     },
26925
26926     // private
26927     beginEdit : function(){
26928         this.editing = true;
26929         this.modified = {}; 
26930     },
26931
26932     // private
26933     cancelEdit : function(){
26934         this.editing = false;
26935         delete this.modified;
26936     },
26937
26938     // private
26939     endEdit : function(){
26940         this.editing = false;
26941         if(this.dirty && this.store){
26942             this.store.afterEdit(this);
26943         }
26944     },
26945
26946     /**
26947      * Usually called by the {@link Roo.data.Store} which owns the Record.
26948      * Rejects all changes made to the Record since either creation, or the last commit operation.
26949      * Modified fields are reverted to their original values.
26950      * <p>
26951      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
26952      * of reject operations.
26953      */
26954     reject : function(){
26955         var m = this.modified;
26956         for(var n in m){
26957             if(typeof m[n] != "function"){
26958                 this.data[n] = m[n];
26959             }
26960         }
26961         this.dirty = false;
26962         delete this.modified;
26963         this.editing = false;
26964         if(this.store){
26965             this.store.afterReject(this);
26966         }
26967     },
26968
26969     /**
26970      * Usually called by the {@link Roo.data.Store} which owns the Record.
26971      * Commits all changes made to the Record since either creation, or the last commit operation.
26972      * <p>
26973      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
26974      * of commit operations.
26975      */
26976     commit : function(){
26977         this.dirty = false;
26978         delete this.modified;
26979         this.editing = false;
26980         if(this.store){
26981             this.store.afterCommit(this);
26982         }
26983     },
26984
26985     // private
26986     hasError : function(){
26987         return this.error != null;
26988     },
26989
26990     // private
26991     clearError : function(){
26992         this.error = null;
26993     },
26994
26995     /**
26996      * Creates a copy of this record.
26997      * @param {String} id (optional) A new record id if you don't want to use this record's id
26998      * @return {Record}
26999      */
27000     copy : function(newId) {
27001         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
27002     }
27003 };/*
27004  * Based on:
27005  * Ext JS Library 1.1.1
27006  * Copyright(c) 2006-2007, Ext JS, LLC.
27007  *
27008  * Originally Released Under LGPL - original licence link has changed is not relivant.
27009  *
27010  * Fork - LGPL
27011  * <script type="text/javascript">
27012  */
27013
27014
27015
27016 /**
27017  * @class Roo.data.Store
27018  * @extends Roo.util.Observable
27019  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
27020  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
27021  * <p>
27022  * 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
27023  * has no knowledge of the format of the data returned by the Proxy.<br>
27024  * <p>
27025  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
27026  * instances from the data object. These records are cached and made available through accessor functions.
27027  * @constructor
27028  * Creates a new Store.
27029  * @param {Object} config A config object containing the objects needed for the Store to access data,
27030  * and read the data into Records.
27031  */
27032 Roo.data.Store = function(config){
27033     this.data = new Roo.util.MixedCollection(false);
27034     this.data.getKey = function(o){
27035         return o.id;
27036     };
27037     this.baseParams = {};
27038     // private
27039     this.paramNames = {
27040         "start" : "start",
27041         "limit" : "limit",
27042         "sort" : "sort",
27043         "dir" : "dir",
27044         "multisort" : "_multisort"
27045     };
27046
27047     if(config && config.data){
27048         this.inlineData = config.data;
27049         delete config.data;
27050     }
27051
27052     Roo.apply(this, config);
27053     
27054     if(this.reader){ // reader passed
27055         this.reader = Roo.factory(this.reader, Roo.data);
27056         this.reader.xmodule = this.xmodule || false;
27057         if(!this.recordType){
27058             this.recordType = this.reader.recordType;
27059         }
27060         if(this.reader.onMetaChange){
27061             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
27062         }
27063     }
27064
27065     if(this.recordType){
27066         this.fields = this.recordType.prototype.fields;
27067     }
27068     this.modified = [];
27069
27070     this.addEvents({
27071         /**
27072          * @event datachanged
27073          * Fires when the data cache has changed, and a widget which is using this Store
27074          * as a Record cache should refresh its view.
27075          * @param {Store} this
27076          */
27077         datachanged : true,
27078         /**
27079          * @event metachange
27080          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
27081          * @param {Store} this
27082          * @param {Object} meta The JSON metadata
27083          */
27084         metachange : true,
27085         /**
27086          * @event add
27087          * Fires when Records have been added to the Store
27088          * @param {Store} this
27089          * @param {Roo.data.Record[]} records The array of Records added
27090          * @param {Number} index The index at which the record(s) were added
27091          */
27092         add : true,
27093         /**
27094          * @event remove
27095          * Fires when a Record has been removed from the Store
27096          * @param {Store} this
27097          * @param {Roo.data.Record} record The Record that was removed
27098          * @param {Number} index The index at which the record was removed
27099          */
27100         remove : true,
27101         /**
27102          * @event update
27103          * Fires when a Record has been updated
27104          * @param {Store} this
27105          * @param {Roo.data.Record} record The Record that was updated
27106          * @param {String} operation The update operation being performed.  Value may be one of:
27107          * <pre><code>
27108  Roo.data.Record.EDIT
27109  Roo.data.Record.REJECT
27110  Roo.data.Record.COMMIT
27111          * </code></pre>
27112          */
27113         update : true,
27114         /**
27115          * @event clear
27116          * Fires when the data cache has been cleared.
27117          * @param {Store} this
27118          */
27119         clear : true,
27120         /**
27121          * @event beforeload
27122          * Fires before a request is made for a new data object.  If the beforeload handler returns false
27123          * the load action will be canceled.
27124          * @param {Store} this
27125          * @param {Object} options The loading options that were specified (see {@link #load} for details)
27126          */
27127         beforeload : true,
27128         /**
27129          * @event beforeloadadd
27130          * Fires after a new set of Records has been loaded.
27131          * @param {Store} this
27132          * @param {Roo.data.Record[]} records The Records that were loaded
27133          * @param {Object} options The loading options that were specified (see {@link #load} for details)
27134          */
27135         beforeloadadd : true,
27136         /**
27137          * @event load
27138          * Fires after a new set of Records has been loaded, before they are added to the store.
27139          * @param {Store} this
27140          * @param {Roo.data.Record[]} records The Records that were loaded
27141          * @param {Object} options The loading options that were specified (see {@link #load} for details)
27142          * @params {Object} return from reader
27143          */
27144         load : true,
27145         /**
27146          * @event loadexception
27147          * Fires if an exception occurs in the Proxy during loading.
27148          * Called with the signature of the Proxy's "loadexception" event.
27149          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
27150          * 
27151          * @param {Proxy} 
27152          * @param {Object} return from JsonData.reader() - success, totalRecords, records
27153          * @param {Object} load options 
27154          * @param {Object} jsonData from your request (normally this contains the Exception)
27155          */
27156         loadexception : true
27157     });
27158     
27159     if(this.proxy){
27160         this.proxy = Roo.factory(this.proxy, Roo.data);
27161         this.proxy.xmodule = this.xmodule || false;
27162         this.relayEvents(this.proxy,  ["loadexception"]);
27163     }
27164     this.sortToggle = {};
27165     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
27166
27167     Roo.data.Store.superclass.constructor.call(this);
27168
27169     if(this.inlineData){
27170         this.loadData(this.inlineData);
27171         delete this.inlineData;
27172     }
27173 };
27174
27175 Roo.extend(Roo.data.Store, Roo.util.Observable, {
27176      /**
27177     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
27178     * without a remote query - used by combo/forms at present.
27179     */
27180     
27181     /**
27182     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
27183     */
27184     /**
27185     * @cfg {Array} data Inline data to be loaded when the store is initialized.
27186     */
27187     /**
27188     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
27189     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
27190     */
27191     /**
27192     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
27193     * on any HTTP request
27194     */
27195     /**
27196     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
27197     */
27198     /**
27199     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
27200     */
27201     multiSort: false,
27202     /**
27203     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
27204     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
27205     */
27206     remoteSort : false,
27207
27208     /**
27209     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
27210      * loaded or when a record is removed. (defaults to false).
27211     */
27212     pruneModifiedRecords : false,
27213
27214     // private
27215     lastOptions : null,
27216
27217     /**
27218      * Add Records to the Store and fires the add event.
27219      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
27220      */
27221     add : function(records){
27222         records = [].concat(records);
27223         for(var i = 0, len = records.length; i < len; i++){
27224             records[i].join(this);
27225         }
27226         var index = this.data.length;
27227         this.data.addAll(records);
27228         this.fireEvent("add", this, records, index);
27229     },
27230
27231     /**
27232      * Remove a Record from the Store and fires the remove event.
27233      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
27234      */
27235     remove : function(record){
27236         var index = this.data.indexOf(record);
27237         this.data.removeAt(index);
27238         if(this.pruneModifiedRecords){
27239             this.modified.remove(record);
27240         }
27241         this.fireEvent("remove", this, record, index);
27242     },
27243
27244     /**
27245      * Remove all Records from the Store and fires the clear event.
27246      */
27247     removeAll : function(){
27248         this.data.clear();
27249         if(this.pruneModifiedRecords){
27250             this.modified = [];
27251         }
27252         this.fireEvent("clear", this);
27253     },
27254
27255     /**
27256      * Inserts Records to the Store at the given index and fires the add event.
27257      * @param {Number} index The start index at which to insert the passed Records.
27258      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
27259      */
27260     insert : function(index, records){
27261         records = [].concat(records);
27262         for(var i = 0, len = records.length; i < len; i++){
27263             this.data.insert(index, records[i]);
27264             records[i].join(this);
27265         }
27266         this.fireEvent("add", this, records, index);
27267     },
27268
27269     /**
27270      * Get the index within the cache of the passed Record.
27271      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
27272      * @return {Number} The index of the passed Record. Returns -1 if not found.
27273      */
27274     indexOf : function(record){
27275         return this.data.indexOf(record);
27276     },
27277
27278     /**
27279      * Get the index within the cache of the Record with the passed id.
27280      * @param {String} id The id of the Record to find.
27281      * @return {Number} The index of the Record. Returns -1 if not found.
27282      */
27283     indexOfId : function(id){
27284         return this.data.indexOfKey(id);
27285     },
27286
27287     /**
27288      * Get the Record with the specified id.
27289      * @param {String} id The id of the Record to find.
27290      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
27291      */
27292     getById : function(id){
27293         return this.data.key(id);
27294     },
27295
27296     /**
27297      * Get the Record at the specified index.
27298      * @param {Number} index The index of the Record to find.
27299      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
27300      */
27301     getAt : function(index){
27302         return this.data.itemAt(index);
27303     },
27304
27305     /**
27306      * Returns a range of Records between specified indices.
27307      * @param {Number} startIndex (optional) The starting index (defaults to 0)
27308      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
27309      * @return {Roo.data.Record[]} An array of Records
27310      */
27311     getRange : function(start, end){
27312         return this.data.getRange(start, end);
27313     },
27314
27315     // private
27316     storeOptions : function(o){
27317         o = Roo.apply({}, o);
27318         delete o.callback;
27319         delete o.scope;
27320         this.lastOptions = o;
27321     },
27322
27323     /**
27324      * Loads the Record cache from the configured Proxy using the configured Reader.
27325      * <p>
27326      * If using remote paging, then the first load call must specify the <em>start</em>
27327      * and <em>limit</em> properties in the options.params property to establish the initial
27328      * position within the dataset, and the number of Records to cache on each read from the Proxy.
27329      * <p>
27330      * <strong>It is important to note that for remote data sources, loading is asynchronous,
27331      * and this call will return before the new data has been loaded. Perform any post-processing
27332      * in a callback function, or in a "load" event handler.</strong>
27333      * <p>
27334      * @param {Object} options An object containing properties which control loading options:<ul>
27335      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
27336      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
27337      * passed the following arguments:<ul>
27338      * <li>r : Roo.data.Record[]</li>
27339      * <li>options: Options object from the load call</li>
27340      * <li>success: Boolean success indicator</li></ul></li>
27341      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
27342      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
27343      * </ul>
27344      */
27345     load : function(options){
27346         options = options || {};
27347         if(this.fireEvent("beforeload", this, options) !== false){
27348             this.storeOptions(options);
27349             var p = Roo.apply(options.params || {}, this.baseParams);
27350             // if meta was not loaded from remote source.. try requesting it.
27351             if (!this.reader.metaFromRemote) {
27352                 p._requestMeta = 1;
27353             }
27354             if(this.sortInfo && this.remoteSort){
27355                 var pn = this.paramNames;
27356                 p[pn["sort"]] = this.sortInfo.field;
27357                 p[pn["dir"]] = this.sortInfo.direction;
27358             }
27359             if (this.multiSort) {
27360                 var pn = this.paramNames;
27361                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
27362             }
27363             
27364             this.proxy.load(p, this.reader, this.loadRecords, this, options);
27365         }
27366     },
27367
27368     /**
27369      * Reloads the Record cache from the configured Proxy using the configured Reader and
27370      * the options from the last load operation performed.
27371      * @param {Object} options (optional) An object containing properties which may override the options
27372      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
27373      * the most recently used options are reused).
27374      */
27375     reload : function(options){
27376         this.load(Roo.applyIf(options||{}, this.lastOptions));
27377     },
27378
27379     // private
27380     // Called as a callback by the Reader during a load operation.
27381     loadRecords : function(o, options, success){
27382         if(!o || success === false){
27383             if(success !== false){
27384                 this.fireEvent("load", this, [], options, o);
27385             }
27386             if(options.callback){
27387                 options.callback.call(options.scope || this, [], options, false);
27388             }
27389             return;
27390         }
27391         // if data returned failure - throw an exception.
27392         if (o.success === false) {
27393             // show a message if no listener is registered.
27394             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
27395                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
27396             }
27397             // loadmask wil be hooked into this..
27398             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
27399             return;
27400         }
27401         var r = o.records, t = o.totalRecords || r.length;
27402         
27403         this.fireEvent("beforeloadadd", this, r, options, o);
27404         
27405         if(!options || options.add !== true){
27406             if(this.pruneModifiedRecords){
27407                 this.modified = [];
27408             }
27409             for(var i = 0, len = r.length; i < len; i++){
27410                 r[i].join(this);
27411             }
27412             if(this.snapshot){
27413                 this.data = this.snapshot;
27414                 delete this.snapshot;
27415             }
27416             this.data.clear();
27417             this.data.addAll(r);
27418             this.totalLength = t;
27419             this.applySort();
27420             this.fireEvent("datachanged", this);
27421         }else{
27422             this.totalLength = Math.max(t, this.data.length+r.length);
27423             this.add(r);
27424         }
27425         this.fireEvent("load", this, r, options, o);
27426         if(options.callback){
27427             options.callback.call(options.scope || this, r, options, true);
27428         }
27429     },
27430
27431
27432     /**
27433      * Loads data from a passed data block. A Reader which understands the format of the data
27434      * must have been configured in the constructor.
27435      * @param {Object} data The data block from which to read the Records.  The format of the data expected
27436      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
27437      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
27438      */
27439     loadData : function(o, append){
27440         var r = this.reader.readRecords(o);
27441         this.loadRecords(r, {add: append}, true);
27442     },
27443
27444     /**
27445      * Gets the number of cached records.
27446      * <p>
27447      * <em>If using paging, this may not be the total size of the dataset. If the data object
27448      * used by the Reader contains the dataset size, then the getTotalCount() function returns
27449      * the data set size</em>
27450      */
27451     getCount : function(){
27452         return this.data.length || 0;
27453     },
27454
27455     /**
27456      * Gets the total number of records in the dataset as returned by the server.
27457      * <p>
27458      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
27459      * the dataset size</em>
27460      */
27461     getTotalCount : function(){
27462         return this.totalLength || 0;
27463     },
27464
27465     /**
27466      * Returns the sort state of the Store as an object with two properties:
27467      * <pre><code>
27468  field {String} The name of the field by which the Records are sorted
27469  direction {String} The sort order, "ASC" or "DESC"
27470      * </code></pre>
27471      */
27472     getSortState : function(){
27473         return this.sortInfo;
27474     },
27475
27476     // private
27477     applySort : function(){
27478         if(this.sortInfo && !this.remoteSort){
27479             var s = this.sortInfo, f = s.field;
27480             var st = this.fields.get(f).sortType;
27481             var fn = function(r1, r2){
27482                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
27483                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
27484             };
27485             this.data.sort(s.direction, fn);
27486             if(this.snapshot && this.snapshot != this.data){
27487                 this.snapshot.sort(s.direction, fn);
27488             }
27489         }
27490     },
27491
27492     /**
27493      * Sets the default sort column and order to be used by the next load operation.
27494      * @param {String} fieldName The name of the field to sort by.
27495      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
27496      */
27497     setDefaultSort : function(field, dir){
27498         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
27499     },
27500
27501     /**
27502      * Sort the Records.
27503      * If remote sorting is used, the sort is performed on the server, and the cache is
27504      * reloaded. If local sorting is used, the cache is sorted internally.
27505      * @param {String} fieldName The name of the field to sort by.
27506      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
27507      */
27508     sort : function(fieldName, dir){
27509         var f = this.fields.get(fieldName);
27510         if(!dir){
27511             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
27512             
27513             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
27514                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
27515             }else{
27516                 dir = f.sortDir;
27517             }
27518         }
27519         this.sortToggle[f.name] = dir;
27520         this.sortInfo = {field: f.name, direction: dir};
27521         if(!this.remoteSort){
27522             this.applySort();
27523             this.fireEvent("datachanged", this);
27524         }else{
27525             this.load(this.lastOptions);
27526         }
27527     },
27528
27529     /**
27530      * Calls the specified function for each of the Records in the cache.
27531      * @param {Function} fn The function to call. The Record is passed as the first parameter.
27532      * Returning <em>false</em> aborts and exits the iteration.
27533      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
27534      */
27535     each : function(fn, scope){
27536         this.data.each(fn, scope);
27537     },
27538
27539     /**
27540      * Gets all records modified since the last commit.  Modified records are persisted across load operations
27541      * (e.g., during paging).
27542      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
27543      */
27544     getModifiedRecords : function(){
27545         return this.modified;
27546     },
27547
27548     // private
27549     createFilterFn : function(property, value, anyMatch){
27550         if(!value.exec){ // not a regex
27551             value = String(value);
27552             if(value.length == 0){
27553                 return false;
27554             }
27555             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
27556         }
27557         return function(r){
27558             return value.test(r.data[property]);
27559         };
27560     },
27561
27562     /**
27563      * Sums the value of <i>property</i> for each record between start and end and returns the result.
27564      * @param {String} property A field on your records
27565      * @param {Number} start The record index to start at (defaults to 0)
27566      * @param {Number} end The last record index to include (defaults to length - 1)
27567      * @return {Number} The sum
27568      */
27569     sum : function(property, start, end){
27570         var rs = this.data.items, v = 0;
27571         start = start || 0;
27572         end = (end || end === 0) ? end : rs.length-1;
27573
27574         for(var i = start; i <= end; i++){
27575             v += (rs[i].data[property] || 0);
27576         }
27577         return v;
27578     },
27579
27580     /**
27581      * Filter the records by a specified property.
27582      * @param {String} field A field on your records
27583      * @param {String/RegExp} value Either a string that the field
27584      * should start with or a RegExp to test against the field
27585      * @param {Boolean} anyMatch True to match any part not just the beginning
27586      */
27587     filter : function(property, value, anyMatch){
27588         var fn = this.createFilterFn(property, value, anyMatch);
27589         return fn ? this.filterBy(fn) : this.clearFilter();
27590     },
27591
27592     /**
27593      * Filter by a function. The specified function will be called with each
27594      * record in this data source. If the function returns true the record is included,
27595      * otherwise it is filtered.
27596      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
27597      * @param {Object} scope (optional) The scope of the function (defaults to this)
27598      */
27599     filterBy : function(fn, scope){
27600         this.snapshot = this.snapshot || this.data;
27601         this.data = this.queryBy(fn, scope||this);
27602         this.fireEvent("datachanged", this);
27603     },
27604
27605     /**
27606      * Query the records by a specified property.
27607      * @param {String} field A field on your records
27608      * @param {String/RegExp} value Either a string that the field
27609      * should start with or a RegExp to test against the field
27610      * @param {Boolean} anyMatch True to match any part not just the beginning
27611      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
27612      */
27613     query : function(property, value, anyMatch){
27614         var fn = this.createFilterFn(property, value, anyMatch);
27615         return fn ? this.queryBy(fn) : this.data.clone();
27616     },
27617
27618     /**
27619      * Query by a function. The specified function will be called with each
27620      * record in this data source. If the function returns true the record is included
27621      * in the results.
27622      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
27623      * @param {Object} scope (optional) The scope of the function (defaults to this)
27624       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
27625      **/
27626     queryBy : function(fn, scope){
27627         var data = this.snapshot || this.data;
27628         return data.filterBy(fn, scope||this);
27629     },
27630
27631     /**
27632      * Collects unique values for a particular dataIndex from this store.
27633      * @param {String} dataIndex The property to collect
27634      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
27635      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
27636      * @return {Array} An array of the unique values
27637      **/
27638     collect : function(dataIndex, allowNull, bypassFilter){
27639         var d = (bypassFilter === true && this.snapshot) ?
27640                 this.snapshot.items : this.data.items;
27641         var v, sv, r = [], l = {};
27642         for(var i = 0, len = d.length; i < len; i++){
27643             v = d[i].data[dataIndex];
27644             sv = String(v);
27645             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
27646                 l[sv] = true;
27647                 r[r.length] = v;
27648             }
27649         }
27650         return r;
27651     },
27652
27653     /**
27654      * Revert to a view of the Record cache with no filtering applied.
27655      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
27656      */
27657     clearFilter : function(suppressEvent){
27658         if(this.snapshot && this.snapshot != this.data){
27659             this.data = this.snapshot;
27660             delete this.snapshot;
27661             if(suppressEvent !== true){
27662                 this.fireEvent("datachanged", this);
27663             }
27664         }
27665     },
27666
27667     // private
27668     afterEdit : function(record){
27669         if(this.modified.indexOf(record) == -1){
27670             this.modified.push(record);
27671         }
27672         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
27673     },
27674     
27675     // private
27676     afterReject : function(record){
27677         this.modified.remove(record);
27678         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
27679     },
27680
27681     // private
27682     afterCommit : function(record){
27683         this.modified.remove(record);
27684         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
27685     },
27686
27687     /**
27688      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
27689      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
27690      */
27691     commitChanges : function(){
27692         var m = this.modified.slice(0);
27693         this.modified = [];
27694         for(var i = 0, len = m.length; i < len; i++){
27695             m[i].commit();
27696         }
27697     },
27698
27699     /**
27700      * Cancel outstanding changes on all changed records.
27701      */
27702     rejectChanges : function(){
27703         var m = this.modified.slice(0);
27704         this.modified = [];
27705         for(var i = 0, len = m.length; i < len; i++){
27706             m[i].reject();
27707         }
27708     },
27709
27710     onMetaChange : function(meta, rtype, o){
27711         this.recordType = rtype;
27712         this.fields = rtype.prototype.fields;
27713         delete this.snapshot;
27714         this.sortInfo = meta.sortInfo || this.sortInfo;
27715         this.modified = [];
27716         this.fireEvent('metachange', this, this.reader.meta);
27717     },
27718     
27719     moveIndex : function(data, type)
27720     {
27721         var index = this.indexOf(data);
27722         
27723         var newIndex = index + type;
27724         
27725         this.remove(data);
27726         
27727         this.insert(newIndex, data);
27728         
27729     }
27730 });/*
27731  * Based on:
27732  * Ext JS Library 1.1.1
27733  * Copyright(c) 2006-2007, Ext JS, LLC.
27734  *
27735  * Originally Released Under LGPL - original licence link has changed is not relivant.
27736  *
27737  * Fork - LGPL
27738  * <script type="text/javascript">
27739  */
27740
27741 /**
27742  * @class Roo.data.SimpleStore
27743  * @extends Roo.data.Store
27744  * Small helper class to make creating Stores from Array data easier.
27745  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
27746  * @cfg {Array} fields An array of field definition objects, or field name strings.
27747  * @cfg {Array} data The multi-dimensional array of data
27748  * @constructor
27749  * @param {Object} config
27750  */
27751 Roo.data.SimpleStore = function(config){
27752     Roo.data.SimpleStore.superclass.constructor.call(this, {
27753         isLocal : true,
27754         reader: new Roo.data.ArrayReader({
27755                 id: config.id
27756             },
27757             Roo.data.Record.create(config.fields)
27758         ),
27759         proxy : new Roo.data.MemoryProxy(config.data)
27760     });
27761     this.load();
27762 };
27763 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
27764  * Based on:
27765  * Ext JS Library 1.1.1
27766  * Copyright(c) 2006-2007, Ext JS, LLC.
27767  *
27768  * Originally Released Under LGPL - original licence link has changed is not relivant.
27769  *
27770  * Fork - LGPL
27771  * <script type="text/javascript">
27772  */
27773
27774 /**
27775 /**
27776  * @extends Roo.data.Store
27777  * @class Roo.data.JsonStore
27778  * Small helper class to make creating Stores for JSON data easier. <br/>
27779 <pre><code>
27780 var store = new Roo.data.JsonStore({
27781     url: 'get-images.php',
27782     root: 'images',
27783     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
27784 });
27785 </code></pre>
27786  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
27787  * JsonReader and HttpProxy (unless inline data is provided).</b>
27788  * @cfg {Array} fields An array of field definition objects, or field name strings.
27789  * @constructor
27790  * @param {Object} config
27791  */
27792 Roo.data.JsonStore = function(c){
27793     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
27794         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
27795         reader: new Roo.data.JsonReader(c, c.fields)
27796     }));
27797 };
27798 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
27799  * Based on:
27800  * Ext JS Library 1.1.1
27801  * Copyright(c) 2006-2007, Ext JS, LLC.
27802  *
27803  * Originally Released Under LGPL - original licence link has changed is not relivant.
27804  *
27805  * Fork - LGPL
27806  * <script type="text/javascript">
27807  */
27808
27809  
27810 Roo.data.Field = function(config){
27811     if(typeof config == "string"){
27812         config = {name: config};
27813     }
27814     Roo.apply(this, config);
27815     
27816     if(!this.type){
27817         this.type = "auto";
27818     }
27819     
27820     var st = Roo.data.SortTypes;
27821     // named sortTypes are supported, here we look them up
27822     if(typeof this.sortType == "string"){
27823         this.sortType = st[this.sortType];
27824     }
27825     
27826     // set default sortType for strings and dates
27827     if(!this.sortType){
27828         switch(this.type){
27829             case "string":
27830                 this.sortType = st.asUCString;
27831                 break;
27832             case "date":
27833                 this.sortType = st.asDate;
27834                 break;
27835             default:
27836                 this.sortType = st.none;
27837         }
27838     }
27839
27840     // define once
27841     var stripRe = /[\$,%]/g;
27842
27843     // prebuilt conversion function for this field, instead of
27844     // switching every time we're reading a value
27845     if(!this.convert){
27846         var cv, dateFormat = this.dateFormat;
27847         switch(this.type){
27848             case "":
27849             case "auto":
27850             case undefined:
27851                 cv = function(v){ return v; };
27852                 break;
27853             case "string":
27854                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
27855                 break;
27856             case "int":
27857                 cv = function(v){
27858                     return v !== undefined && v !== null && v !== '' ?
27859                            parseInt(String(v).replace(stripRe, ""), 10) : '';
27860                     };
27861                 break;
27862             case "float":
27863                 cv = function(v){
27864                     return v !== undefined && v !== null && v !== '' ?
27865                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
27866                     };
27867                 break;
27868             case "bool":
27869             case "boolean":
27870                 cv = function(v){ return v === true || v === "true" || v == 1; };
27871                 break;
27872             case "date":
27873                 cv = function(v){
27874                     if(!v){
27875                         return '';
27876                     }
27877                     if(v instanceof Date){
27878                         return v;
27879                     }
27880                     if(dateFormat){
27881                         if(dateFormat == "timestamp"){
27882                             return new Date(v*1000);
27883                         }
27884                         return Date.parseDate(v, dateFormat);
27885                     }
27886                     var parsed = Date.parse(v);
27887                     return parsed ? new Date(parsed) : null;
27888                 };
27889              break;
27890             
27891         }
27892         this.convert = cv;
27893     }
27894 };
27895
27896 Roo.data.Field.prototype = {
27897     dateFormat: null,
27898     defaultValue: "",
27899     mapping: null,
27900     sortType : null,
27901     sortDir : "ASC"
27902 };/*
27903  * Based on:
27904  * Ext JS Library 1.1.1
27905  * Copyright(c) 2006-2007, Ext JS, LLC.
27906  *
27907  * Originally Released Under LGPL - original licence link has changed is not relivant.
27908  *
27909  * Fork - LGPL
27910  * <script type="text/javascript">
27911  */
27912  
27913 // Base class for reading structured data from a data source.  This class is intended to be
27914 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
27915
27916 /**
27917  * @class Roo.data.DataReader
27918  * Base class for reading structured data from a data source.  This class is intended to be
27919  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
27920  */
27921
27922 Roo.data.DataReader = function(meta, recordType){
27923     
27924     this.meta = meta;
27925     
27926     this.recordType = recordType instanceof Array ? 
27927         Roo.data.Record.create(recordType) : recordType;
27928 };
27929
27930 Roo.data.DataReader.prototype = {
27931      /**
27932      * Create an empty record
27933      * @param {Object} data (optional) - overlay some values
27934      * @return {Roo.data.Record} record created.
27935      */
27936     newRow :  function(d) {
27937         var da =  {};
27938         this.recordType.prototype.fields.each(function(c) {
27939             switch( c.type) {
27940                 case 'int' : da[c.name] = 0; break;
27941                 case 'date' : da[c.name] = new Date(); break;
27942                 case 'float' : da[c.name] = 0.0; break;
27943                 case 'boolean' : da[c.name] = false; break;
27944                 default : da[c.name] = ""; break;
27945             }
27946             
27947         });
27948         return new this.recordType(Roo.apply(da, d));
27949     }
27950     
27951 };/*
27952  * Based on:
27953  * Ext JS Library 1.1.1
27954  * Copyright(c) 2006-2007, Ext JS, LLC.
27955  *
27956  * Originally Released Under LGPL - original licence link has changed is not relivant.
27957  *
27958  * Fork - LGPL
27959  * <script type="text/javascript">
27960  */
27961
27962 /**
27963  * @class Roo.data.DataProxy
27964  * @extends Roo.data.Observable
27965  * This class is an abstract base class for implementations which provide retrieval of
27966  * unformatted data objects.<br>
27967  * <p>
27968  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
27969  * (of the appropriate type which knows how to parse the data object) to provide a block of
27970  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
27971  * <p>
27972  * Custom implementations must implement the load method as described in
27973  * {@link Roo.data.HttpProxy#load}.
27974  */
27975 Roo.data.DataProxy = function(){
27976     this.addEvents({
27977         /**
27978          * @event beforeload
27979          * Fires before a network request is made to retrieve a data object.
27980          * @param {Object} This DataProxy object.
27981          * @param {Object} params The params parameter to the load function.
27982          */
27983         beforeload : true,
27984         /**
27985          * @event load
27986          * Fires before the load method's callback is called.
27987          * @param {Object} This DataProxy object.
27988          * @param {Object} o The data object.
27989          * @param {Object} arg The callback argument object passed to the load function.
27990          */
27991         load : true,
27992         /**
27993          * @event loadexception
27994          * Fires if an Exception occurs during data retrieval.
27995          * @param {Object} This DataProxy object.
27996          * @param {Object} o The data object.
27997          * @param {Object} arg The callback argument object passed to the load function.
27998          * @param {Object} e The Exception.
27999          */
28000         loadexception : true
28001     });
28002     Roo.data.DataProxy.superclass.constructor.call(this);
28003 };
28004
28005 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
28006
28007     /**
28008      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
28009      */
28010 /*
28011  * Based on:
28012  * Ext JS Library 1.1.1
28013  * Copyright(c) 2006-2007, Ext JS, LLC.
28014  *
28015  * Originally Released Under LGPL - original licence link has changed is not relivant.
28016  *
28017  * Fork - LGPL
28018  * <script type="text/javascript">
28019  */
28020 /**
28021  * @class Roo.data.MemoryProxy
28022  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
28023  * to the Reader when its load method is called.
28024  * @constructor
28025  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
28026  */
28027 Roo.data.MemoryProxy = function(data){
28028     if (data.data) {
28029         data = data.data;
28030     }
28031     Roo.data.MemoryProxy.superclass.constructor.call(this);
28032     this.data = data;
28033 };
28034
28035 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
28036     /**
28037      * Load data from the requested source (in this case an in-memory
28038      * data object passed to the constructor), read the data object into
28039      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
28040      * process that block using the passed callback.
28041      * @param {Object} params This parameter is not used by the MemoryProxy class.
28042      * @param {Roo.data.DataReader} reader The Reader object which converts the data
28043      * object into a block of Roo.data.Records.
28044      * @param {Function} callback The function into which to pass the block of Roo.data.records.
28045      * The function must be passed <ul>
28046      * <li>The Record block object</li>
28047      * <li>The "arg" argument from the load function</li>
28048      * <li>A boolean success indicator</li>
28049      * </ul>
28050      * @param {Object} scope The scope in which to call the callback
28051      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
28052      */
28053     load : function(params, reader, callback, scope, arg){
28054         params = params || {};
28055         var result;
28056         try {
28057             result = reader.readRecords(this.data);
28058         }catch(e){
28059             this.fireEvent("loadexception", this, arg, null, e);
28060             callback.call(scope, null, arg, false);
28061             return;
28062         }
28063         callback.call(scope, result, arg, true);
28064     },
28065     
28066     // private
28067     update : function(params, records){
28068         
28069     }
28070 });/*
28071  * Based on:
28072  * Ext JS Library 1.1.1
28073  * Copyright(c) 2006-2007, Ext JS, LLC.
28074  *
28075  * Originally Released Under LGPL - original licence link has changed is not relivant.
28076  *
28077  * Fork - LGPL
28078  * <script type="text/javascript">
28079  */
28080 /**
28081  * @class Roo.data.HttpProxy
28082  * @extends Roo.data.DataProxy
28083  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
28084  * configured to reference a certain URL.<br><br>
28085  * <p>
28086  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
28087  * from which the running page was served.<br><br>
28088  * <p>
28089  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
28090  * <p>
28091  * Be aware that to enable the browser to parse an XML document, the server must set
28092  * the Content-Type header in the HTTP response to "text/xml".
28093  * @constructor
28094  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
28095  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
28096  * will be used to make the request.
28097  */
28098 Roo.data.HttpProxy = function(conn){
28099     Roo.data.HttpProxy.superclass.constructor.call(this);
28100     // is conn a conn config or a real conn?
28101     this.conn = conn;
28102     this.useAjax = !conn || !conn.events;
28103   
28104 };
28105
28106 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
28107     // thse are take from connection...
28108     
28109     /**
28110      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
28111      */
28112     /**
28113      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
28114      * extra parameters to each request made by this object. (defaults to undefined)
28115      */
28116     /**
28117      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
28118      *  to each request made by this object. (defaults to undefined)
28119      */
28120     /**
28121      * @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)
28122      */
28123     /**
28124      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
28125      */
28126      /**
28127      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
28128      * @type Boolean
28129      */
28130   
28131
28132     /**
28133      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
28134      * @type Boolean
28135      */
28136     /**
28137      * Return the {@link Roo.data.Connection} object being used by this Proxy.
28138      * @return {Connection} The Connection object. This object may be used to subscribe to events on
28139      * a finer-grained basis than the DataProxy events.
28140      */
28141     getConnection : function(){
28142         return this.useAjax ? Roo.Ajax : this.conn;
28143     },
28144
28145     /**
28146      * Load data from the configured {@link Roo.data.Connection}, read the data object into
28147      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
28148      * process that block using the passed callback.
28149      * @param {Object} params An object containing properties which are to be used as HTTP parameters
28150      * for the request to the remote server.
28151      * @param {Roo.data.DataReader} reader The Reader object which converts the data
28152      * object into a block of Roo.data.Records.
28153      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
28154      * The function must be passed <ul>
28155      * <li>The Record block object</li>
28156      * <li>The "arg" argument from the load function</li>
28157      * <li>A boolean success indicator</li>
28158      * </ul>
28159      * @param {Object} scope The scope in which to call the callback
28160      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
28161      */
28162     load : function(params, reader, callback, scope, arg){
28163         if(this.fireEvent("beforeload", this, params) !== false){
28164             var  o = {
28165                 params : params || {},
28166                 request: {
28167                     callback : callback,
28168                     scope : scope,
28169                     arg : arg
28170                 },
28171                 reader: reader,
28172                 callback : this.loadResponse,
28173                 scope: this
28174             };
28175             if(this.useAjax){
28176                 Roo.applyIf(o, this.conn);
28177                 if(this.activeRequest){
28178                     Roo.Ajax.abort(this.activeRequest);
28179                 }
28180                 this.activeRequest = Roo.Ajax.request(o);
28181             }else{
28182                 this.conn.request(o);
28183             }
28184         }else{
28185             callback.call(scope||this, null, arg, false);
28186         }
28187     },
28188
28189     // private
28190     loadResponse : function(o, success, response){
28191         delete this.activeRequest;
28192         if(!success){
28193             this.fireEvent("loadexception", this, o, response);
28194             o.request.callback.call(o.request.scope, null, o.request.arg, false);
28195             return;
28196         }
28197         var result;
28198         try {
28199             result = o.reader.read(response);
28200         }catch(e){
28201             this.fireEvent("loadexception", this, o, response, e);
28202             o.request.callback.call(o.request.scope, null, o.request.arg, false);
28203             return;
28204         }
28205         
28206         this.fireEvent("load", this, o, o.request.arg);
28207         o.request.callback.call(o.request.scope, result, o.request.arg, true);
28208     },
28209
28210     // private
28211     update : function(dataSet){
28212
28213     },
28214
28215     // private
28216     updateResponse : function(dataSet){
28217
28218     }
28219 });/*
28220  * Based on:
28221  * Ext JS Library 1.1.1
28222  * Copyright(c) 2006-2007, Ext JS, LLC.
28223  *
28224  * Originally Released Under LGPL - original licence link has changed is not relivant.
28225  *
28226  * Fork - LGPL
28227  * <script type="text/javascript">
28228  */
28229
28230 /**
28231  * @class Roo.data.ScriptTagProxy
28232  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
28233  * other than the originating domain of the running page.<br><br>
28234  * <p>
28235  * <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
28236  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
28237  * <p>
28238  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
28239  * source code that is used as the source inside a &lt;script> tag.<br><br>
28240  * <p>
28241  * In order for the browser to process the returned data, the server must wrap the data object
28242  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
28243  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
28244  * depending on whether the callback name was passed:
28245  * <p>
28246  * <pre><code>
28247 boolean scriptTag = false;
28248 String cb = request.getParameter("callback");
28249 if (cb != null) {
28250     scriptTag = true;
28251     response.setContentType("text/javascript");
28252 } else {
28253     response.setContentType("application/x-json");
28254 }
28255 Writer out = response.getWriter();
28256 if (scriptTag) {
28257     out.write(cb + "(");
28258 }
28259 out.print(dataBlock.toJsonString());
28260 if (scriptTag) {
28261     out.write(");");
28262 }
28263 </pre></code>
28264  *
28265  * @constructor
28266  * @param {Object} config A configuration object.
28267  */
28268 Roo.data.ScriptTagProxy = function(config){
28269     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
28270     Roo.apply(this, config);
28271     this.head = document.getElementsByTagName("head")[0];
28272 };
28273
28274 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
28275
28276 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
28277     /**
28278      * @cfg {String} url The URL from which to request the data object.
28279      */
28280     /**
28281      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
28282      */
28283     timeout : 30000,
28284     /**
28285      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
28286      * the server the name of the callback function set up by the load call to process the returned data object.
28287      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
28288      * javascript output which calls this named function passing the data object as its only parameter.
28289      */
28290     callbackParam : "callback",
28291     /**
28292      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
28293      * name to the request.
28294      */
28295     nocache : true,
28296
28297     /**
28298      * Load data from the configured URL, read the data object into
28299      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
28300      * process that block using the passed callback.
28301      * @param {Object} params An object containing properties which are to be used as HTTP parameters
28302      * for the request to the remote server.
28303      * @param {Roo.data.DataReader} reader The Reader object which converts the data
28304      * object into a block of Roo.data.Records.
28305      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
28306      * The function must be passed <ul>
28307      * <li>The Record block object</li>
28308      * <li>The "arg" argument from the load function</li>
28309      * <li>A boolean success indicator</li>
28310      * </ul>
28311      * @param {Object} scope The scope in which to call the callback
28312      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
28313      */
28314     load : function(params, reader, callback, scope, arg){
28315         if(this.fireEvent("beforeload", this, params) !== false){
28316
28317             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
28318
28319             var url = this.url;
28320             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
28321             if(this.nocache){
28322                 url += "&_dc=" + (new Date().getTime());
28323             }
28324             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
28325             var trans = {
28326                 id : transId,
28327                 cb : "stcCallback"+transId,
28328                 scriptId : "stcScript"+transId,
28329                 params : params,
28330                 arg : arg,
28331                 url : url,
28332                 callback : callback,
28333                 scope : scope,
28334                 reader : reader
28335             };
28336             var conn = this;
28337
28338             window[trans.cb] = function(o){
28339                 conn.handleResponse(o, trans);
28340             };
28341
28342             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
28343
28344             if(this.autoAbort !== false){
28345                 this.abort();
28346             }
28347
28348             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
28349
28350             var script = document.createElement("script");
28351             script.setAttribute("src", url);
28352             script.setAttribute("type", "text/javascript");
28353             script.setAttribute("id", trans.scriptId);
28354             this.head.appendChild(script);
28355
28356             this.trans = trans;
28357         }else{
28358             callback.call(scope||this, null, arg, false);
28359         }
28360     },
28361
28362     // private
28363     isLoading : function(){
28364         return this.trans ? true : false;
28365     },
28366
28367     /**
28368      * Abort the current server request.
28369      */
28370     abort : function(){
28371         if(this.isLoading()){
28372             this.destroyTrans(this.trans);
28373         }
28374     },
28375
28376     // private
28377     destroyTrans : function(trans, isLoaded){
28378         this.head.removeChild(document.getElementById(trans.scriptId));
28379         clearTimeout(trans.timeoutId);
28380         if(isLoaded){
28381             window[trans.cb] = undefined;
28382             try{
28383                 delete window[trans.cb];
28384             }catch(e){}
28385         }else{
28386             // if hasn't been loaded, wait for load to remove it to prevent script error
28387             window[trans.cb] = function(){
28388                 window[trans.cb] = undefined;
28389                 try{
28390                     delete window[trans.cb];
28391                 }catch(e){}
28392             };
28393         }
28394     },
28395
28396     // private
28397     handleResponse : function(o, trans){
28398         this.trans = false;
28399         this.destroyTrans(trans, true);
28400         var result;
28401         try {
28402             result = trans.reader.readRecords(o);
28403         }catch(e){
28404             this.fireEvent("loadexception", this, o, trans.arg, e);
28405             trans.callback.call(trans.scope||window, null, trans.arg, false);
28406             return;
28407         }
28408         this.fireEvent("load", this, o, trans.arg);
28409         trans.callback.call(trans.scope||window, result, trans.arg, true);
28410     },
28411
28412     // private
28413     handleFailure : function(trans){
28414         this.trans = false;
28415         this.destroyTrans(trans, false);
28416         this.fireEvent("loadexception", this, null, trans.arg);
28417         trans.callback.call(trans.scope||window, null, trans.arg, false);
28418     }
28419 });/*
28420  * Based on:
28421  * Ext JS Library 1.1.1
28422  * Copyright(c) 2006-2007, Ext JS, LLC.
28423  *
28424  * Originally Released Under LGPL - original licence link has changed is not relivant.
28425  *
28426  * Fork - LGPL
28427  * <script type="text/javascript">
28428  */
28429
28430 /**
28431  * @class Roo.data.JsonReader
28432  * @extends Roo.data.DataReader
28433  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
28434  * based on mappings in a provided Roo.data.Record constructor.
28435  * 
28436  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
28437  * in the reply previously. 
28438  * 
28439  * <p>
28440  * Example code:
28441  * <pre><code>
28442 var RecordDef = Roo.data.Record.create([
28443     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
28444     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
28445 ]);
28446 var myReader = new Roo.data.JsonReader({
28447     totalProperty: "results",    // The property which contains the total dataset size (optional)
28448     root: "rows",                // The property which contains an Array of row objects
28449     id: "id"                     // The property within each row object that provides an ID for the record (optional)
28450 }, RecordDef);
28451 </code></pre>
28452  * <p>
28453  * This would consume a JSON file like this:
28454  * <pre><code>
28455 { 'results': 2, 'rows': [
28456     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
28457     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
28458 }
28459 </code></pre>
28460  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
28461  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
28462  * paged from the remote server.
28463  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
28464  * @cfg {String} root name of the property which contains the Array of row objects.
28465  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
28466  * @cfg {Array} fields Array of field definition objects
28467  * @constructor
28468  * Create a new JsonReader
28469  * @param {Object} meta Metadata configuration options
28470  * @param {Object} recordType Either an Array of field definition objects,
28471  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
28472  */
28473 Roo.data.JsonReader = function(meta, recordType){
28474     
28475     meta = meta || {};
28476     // set some defaults:
28477     Roo.applyIf(meta, {
28478         totalProperty: 'total',
28479         successProperty : 'success',
28480         root : 'data',
28481         id : 'id'
28482     });
28483     
28484     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
28485 };
28486 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
28487     
28488     /**
28489      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
28490      * Used by Store query builder to append _requestMeta to params.
28491      * 
28492      */
28493     metaFromRemote : false,
28494     /**
28495      * This method is only used by a DataProxy which has retrieved data from a remote server.
28496      * @param {Object} response The XHR object which contains the JSON data in its responseText.
28497      * @return {Object} data A data block which is used by an Roo.data.Store object as
28498      * a cache of Roo.data.Records.
28499      */
28500     read : function(response){
28501         var json = response.responseText;
28502        
28503         var o = /* eval:var:o */ eval("("+json+")");
28504         if(!o) {
28505             throw {message: "JsonReader.read: Json object not found"};
28506         }
28507         
28508         if(o.metaData){
28509             
28510             delete this.ef;
28511             this.metaFromRemote = true;
28512             this.meta = o.metaData;
28513             this.recordType = Roo.data.Record.create(o.metaData.fields);
28514             this.onMetaChange(this.meta, this.recordType, o);
28515         }
28516         return this.readRecords(o);
28517     },
28518
28519     // private function a store will implement
28520     onMetaChange : function(meta, recordType, o){
28521
28522     },
28523
28524     /**
28525          * @ignore
28526          */
28527     simpleAccess: function(obj, subsc) {
28528         return obj[subsc];
28529     },
28530
28531         /**
28532          * @ignore
28533          */
28534     getJsonAccessor: function(){
28535         var re = /[\[\.]/;
28536         return function(expr) {
28537             try {
28538                 return(re.test(expr))
28539                     ? new Function("obj", "return obj." + expr)
28540                     : function(obj){
28541                         return obj[expr];
28542                     };
28543             } catch(e){}
28544             return Roo.emptyFn;
28545         };
28546     }(),
28547
28548     /**
28549      * Create a data block containing Roo.data.Records from an XML document.
28550      * @param {Object} o An object which contains an Array of row objects in the property specified
28551      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
28552      * which contains the total size of the dataset.
28553      * @return {Object} data A data block which is used by an Roo.data.Store object as
28554      * a cache of Roo.data.Records.
28555      */
28556     readRecords : function(o){
28557         /**
28558          * After any data loads, the raw JSON data is available for further custom processing.
28559          * @type Object
28560          */
28561         this.o = o;
28562         var s = this.meta, Record = this.recordType,
28563             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
28564
28565 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
28566         if (!this.ef) {
28567             if(s.totalProperty) {
28568                     this.getTotal = this.getJsonAccessor(s.totalProperty);
28569                 }
28570                 if(s.successProperty) {
28571                     this.getSuccess = this.getJsonAccessor(s.successProperty);
28572                 }
28573                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
28574                 if (s.id) {
28575                         var g = this.getJsonAccessor(s.id);
28576                         this.getId = function(rec) {
28577                                 var r = g(rec);  
28578                                 return (r === undefined || r === "") ? null : r;
28579                         };
28580                 } else {
28581                         this.getId = function(){return null;};
28582                 }
28583             this.ef = [];
28584             for(var jj = 0; jj < fl; jj++){
28585                 f = fi[jj];
28586                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
28587                 this.ef[jj] = this.getJsonAccessor(map);
28588             }
28589         }
28590
28591         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
28592         if(s.totalProperty){
28593             var vt = parseInt(this.getTotal(o), 10);
28594             if(!isNaN(vt)){
28595                 totalRecords = vt;
28596             }
28597         }
28598         if(s.successProperty){
28599             var vs = this.getSuccess(o);
28600             if(vs === false || vs === 'false'){
28601                 success = false;
28602             }
28603         }
28604         var records = [];
28605         for(var i = 0; i < c; i++){
28606                 var n = root[i];
28607             var values = {};
28608             var id = this.getId(n);
28609             for(var j = 0; j < fl; j++){
28610                 f = fi[j];
28611             var v = this.ef[j](n);
28612             if (!f.convert) {
28613                 Roo.log('missing convert for ' + f.name);
28614                 Roo.log(f);
28615                 continue;
28616             }
28617             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
28618             }
28619             var record = new Record(values, id);
28620             record.json = n;
28621             records[i] = record;
28622         }
28623         return {
28624             raw : o,
28625             success : success,
28626             records : records,
28627             totalRecords : totalRecords
28628         };
28629     }
28630 });/*
28631  * Based on:
28632  * Ext JS Library 1.1.1
28633  * Copyright(c) 2006-2007, Ext JS, LLC.
28634  *
28635  * Originally Released Under LGPL - original licence link has changed is not relivant.
28636  *
28637  * Fork - LGPL
28638  * <script type="text/javascript">
28639  */
28640
28641 /**
28642  * @class Roo.data.XmlReader
28643  * @extends Roo.data.DataReader
28644  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
28645  * based on mappings in a provided Roo.data.Record constructor.<br><br>
28646  * <p>
28647  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
28648  * header in the HTTP response must be set to "text/xml".</em>
28649  * <p>
28650  * Example code:
28651  * <pre><code>
28652 var RecordDef = Roo.data.Record.create([
28653    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
28654    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
28655 ]);
28656 var myReader = new Roo.data.XmlReader({
28657    totalRecords: "results", // The element which contains the total dataset size (optional)
28658    record: "row",           // The repeated element which contains row information
28659    id: "id"                 // The element within the row that provides an ID for the record (optional)
28660 }, RecordDef);
28661 </code></pre>
28662  * <p>
28663  * This would consume an XML file like this:
28664  * <pre><code>
28665 &lt;?xml?>
28666 &lt;dataset>
28667  &lt;results>2&lt;/results>
28668  &lt;row>
28669    &lt;id>1&lt;/id>
28670    &lt;name>Bill&lt;/name>
28671    &lt;occupation>Gardener&lt;/occupation>
28672  &lt;/row>
28673  &lt;row>
28674    &lt;id>2&lt;/id>
28675    &lt;name>Ben&lt;/name>
28676    &lt;occupation>Horticulturalist&lt;/occupation>
28677  &lt;/row>
28678 &lt;/dataset>
28679 </code></pre>
28680  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
28681  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
28682  * paged from the remote server.
28683  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
28684  * @cfg {String} success The DomQuery path to the success attribute used by forms.
28685  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
28686  * a record identifier value.
28687  * @constructor
28688  * Create a new XmlReader
28689  * @param {Object} meta Metadata configuration options
28690  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
28691  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
28692  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
28693  */
28694 Roo.data.XmlReader = function(meta, recordType){
28695     meta = meta || {};
28696     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
28697 };
28698 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
28699     /**
28700      * This method is only used by a DataProxy which has retrieved data from a remote server.
28701          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
28702          * to contain a method called 'responseXML' that returns an XML document object.
28703      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
28704      * a cache of Roo.data.Records.
28705      */
28706     read : function(response){
28707         var doc = response.responseXML;
28708         if(!doc) {
28709             throw {message: "XmlReader.read: XML Document not available"};
28710         }
28711         return this.readRecords(doc);
28712     },
28713
28714     /**
28715      * Create a data block containing Roo.data.Records from an XML document.
28716          * @param {Object} doc A parsed XML document.
28717      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
28718      * a cache of Roo.data.Records.
28719      */
28720     readRecords : function(doc){
28721         /**
28722          * After any data loads/reads, the raw XML Document is available for further custom processing.
28723          * @type XMLDocument
28724          */
28725         this.xmlData = doc;
28726         var root = doc.documentElement || doc;
28727         var q = Roo.DomQuery;
28728         var recordType = this.recordType, fields = recordType.prototype.fields;
28729         var sid = this.meta.id;
28730         var totalRecords = 0, success = true;
28731         if(this.meta.totalRecords){
28732             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
28733         }
28734         
28735         if(this.meta.success){
28736             var sv = q.selectValue(this.meta.success, root, true);
28737             success = sv !== false && sv !== 'false';
28738         }
28739         var records = [];
28740         var ns = q.select(this.meta.record, root);
28741         for(var i = 0, len = ns.length; i < len; i++) {
28742                 var n = ns[i];
28743                 var values = {};
28744                 var id = sid ? q.selectValue(sid, n) : undefined;
28745                 for(var j = 0, jlen = fields.length; j < jlen; j++){
28746                     var f = fields.items[j];
28747                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
28748                     v = f.convert(v);
28749                     values[f.name] = v;
28750                 }
28751                 var record = new recordType(values, id);
28752                 record.node = n;
28753                 records[records.length] = record;
28754             }
28755
28756             return {
28757                 success : success,
28758                 records : records,
28759                 totalRecords : totalRecords || records.length
28760             };
28761     }
28762 });/*
28763  * Based on:
28764  * Ext JS Library 1.1.1
28765  * Copyright(c) 2006-2007, Ext JS, LLC.
28766  *
28767  * Originally Released Under LGPL - original licence link has changed is not relivant.
28768  *
28769  * Fork - LGPL
28770  * <script type="text/javascript">
28771  */
28772
28773 /**
28774  * @class Roo.data.ArrayReader
28775  * @extends Roo.data.DataReader
28776  * Data reader class to create an Array of Roo.data.Record objects from an Array.
28777  * Each element of that Array represents a row of data fields. The
28778  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
28779  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
28780  * <p>
28781  * Example code:.
28782  * <pre><code>
28783 var RecordDef = Roo.data.Record.create([
28784     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
28785     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
28786 ]);
28787 var myReader = new Roo.data.ArrayReader({
28788     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
28789 }, RecordDef);
28790 </code></pre>
28791  * <p>
28792  * This would consume an Array like this:
28793  * <pre><code>
28794 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
28795   </code></pre>
28796  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
28797  * @constructor
28798  * Create a new JsonReader
28799  * @param {Object} meta Metadata configuration options.
28800  * @param {Object} recordType Either an Array of field definition objects
28801  * as specified to {@link Roo.data.Record#create},
28802  * or an {@link Roo.data.Record} object
28803  * created using {@link Roo.data.Record#create}.
28804  */
28805 Roo.data.ArrayReader = function(meta, recordType){
28806     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
28807 };
28808
28809 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
28810     /**
28811      * Create a data block containing Roo.data.Records from an XML document.
28812      * @param {Object} o An Array of row objects which represents the dataset.
28813      * @return {Object} data A data block which is used by an Roo.data.Store object as
28814      * a cache of Roo.data.Records.
28815      */
28816     readRecords : function(o){
28817         var sid = this.meta ? this.meta.id : null;
28818         var recordType = this.recordType, fields = recordType.prototype.fields;
28819         var records = [];
28820         var root = o;
28821             for(var i = 0; i < root.length; i++){
28822                     var n = root[i];
28823                 var values = {};
28824                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
28825                 for(var j = 0, jlen = fields.length; j < jlen; j++){
28826                 var f = fields.items[j];
28827                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
28828                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
28829                 v = f.convert(v);
28830                 values[f.name] = v;
28831             }
28832                 var record = new recordType(values, id);
28833                 record.json = n;
28834                 records[records.length] = record;
28835             }
28836             return {
28837                 records : records,
28838                 totalRecords : records.length
28839             };
28840     }
28841 });/*
28842  * Based on:
28843  * Ext JS Library 1.1.1
28844  * Copyright(c) 2006-2007, Ext JS, LLC.
28845  *
28846  * Originally Released Under LGPL - original licence link has changed is not relivant.
28847  *
28848  * Fork - LGPL
28849  * <script type="text/javascript">
28850  */
28851
28852
28853 /**
28854  * @class Roo.data.Tree
28855  * @extends Roo.util.Observable
28856  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
28857  * in the tree have most standard DOM functionality.
28858  * @constructor
28859  * @param {Node} root (optional) The root node
28860  */
28861 Roo.data.Tree = function(root){
28862    this.nodeHash = {};
28863    /**
28864     * The root node for this tree
28865     * @type Node
28866     */
28867    this.root = null;
28868    if(root){
28869        this.setRootNode(root);
28870    }
28871    this.addEvents({
28872        /**
28873         * @event append
28874         * Fires when a new child node is appended to a node in this tree.
28875         * @param {Tree} tree The owner tree
28876         * @param {Node} parent The parent node
28877         * @param {Node} node The newly appended node
28878         * @param {Number} index The index of the newly appended node
28879         */
28880        "append" : true,
28881        /**
28882         * @event remove
28883         * Fires when a child node is removed from a node in this tree.
28884         * @param {Tree} tree The owner tree
28885         * @param {Node} parent The parent node
28886         * @param {Node} node The child node removed
28887         */
28888        "remove" : true,
28889        /**
28890         * @event move
28891         * Fires when a node is moved to a new location in the tree
28892         * @param {Tree} tree The owner tree
28893         * @param {Node} node The node moved
28894         * @param {Node} oldParent The old parent of this node
28895         * @param {Node} newParent The new parent of this node
28896         * @param {Number} index The index it was moved to
28897         */
28898        "move" : true,
28899        /**
28900         * @event insert
28901         * Fires when a new child node is inserted in a node in this tree.
28902         * @param {Tree} tree The owner tree
28903         * @param {Node} parent The parent node
28904         * @param {Node} node The child node inserted
28905         * @param {Node} refNode The child node the node was inserted before
28906         */
28907        "insert" : true,
28908        /**
28909         * @event beforeappend
28910         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
28911         * @param {Tree} tree The owner tree
28912         * @param {Node} parent The parent node
28913         * @param {Node} node The child node to be appended
28914         */
28915        "beforeappend" : true,
28916        /**
28917         * @event beforeremove
28918         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
28919         * @param {Tree} tree The owner tree
28920         * @param {Node} parent The parent node
28921         * @param {Node} node The child node to be removed
28922         */
28923        "beforeremove" : true,
28924        /**
28925         * @event beforemove
28926         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
28927         * @param {Tree} tree The owner tree
28928         * @param {Node} node The node being moved
28929         * @param {Node} oldParent The parent of the node
28930         * @param {Node} newParent The new parent the node is moving to
28931         * @param {Number} index The index it is being moved to
28932         */
28933        "beforemove" : true,
28934        /**
28935         * @event beforeinsert
28936         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
28937         * @param {Tree} tree The owner tree
28938         * @param {Node} parent The parent node
28939         * @param {Node} node The child node to be inserted
28940         * @param {Node} refNode The child node the node is being inserted before
28941         */
28942        "beforeinsert" : true
28943    });
28944
28945     Roo.data.Tree.superclass.constructor.call(this);
28946 };
28947
28948 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
28949     pathSeparator: "/",
28950
28951     proxyNodeEvent : function(){
28952         return this.fireEvent.apply(this, arguments);
28953     },
28954
28955     /**
28956      * Returns the root node for this tree.
28957      * @return {Node}
28958      */
28959     getRootNode : function(){
28960         return this.root;
28961     },
28962
28963     /**
28964      * Sets the root node for this tree.
28965      * @param {Node} node
28966      * @return {Node}
28967      */
28968     setRootNode : function(node){
28969         this.root = node;
28970         node.ownerTree = this;
28971         node.isRoot = true;
28972         this.registerNode(node);
28973         return node;
28974     },
28975
28976     /**
28977      * Gets a node in this tree by its id.
28978      * @param {String} id
28979      * @return {Node}
28980      */
28981     getNodeById : function(id){
28982         return this.nodeHash[id];
28983     },
28984
28985     registerNode : function(node){
28986         this.nodeHash[node.id] = node;
28987     },
28988
28989     unregisterNode : function(node){
28990         delete this.nodeHash[node.id];
28991     },
28992
28993     toString : function(){
28994         return "[Tree"+(this.id?" "+this.id:"")+"]";
28995     }
28996 });
28997
28998 /**
28999  * @class Roo.data.Node
29000  * @extends Roo.util.Observable
29001  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
29002  * @cfg {String} id The id for this node. If one is not specified, one is generated.
29003  * @constructor
29004  * @param {Object} attributes The attributes/config for the node
29005  */
29006 Roo.data.Node = function(attributes){
29007     /**
29008      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
29009      * @type {Object}
29010      */
29011     this.attributes = attributes || {};
29012     this.leaf = this.attributes.leaf;
29013     /**
29014      * The node id. @type String
29015      */
29016     this.id = this.attributes.id;
29017     if(!this.id){
29018         this.id = Roo.id(null, "ynode-");
29019         this.attributes.id = this.id;
29020     }
29021      
29022     
29023     /**
29024      * All child nodes of this node. @type Array
29025      */
29026     this.childNodes = [];
29027     if(!this.childNodes.indexOf){ // indexOf is a must
29028         this.childNodes.indexOf = function(o){
29029             for(var i = 0, len = this.length; i < len; i++){
29030                 if(this[i] == o) {
29031                     return i;
29032                 }
29033             }
29034             return -1;
29035         };
29036     }
29037     /**
29038      * The parent node for this node. @type Node
29039      */
29040     this.parentNode = null;
29041     /**
29042      * The first direct child node of this node, or null if this node has no child nodes. @type Node
29043      */
29044     this.firstChild = null;
29045     /**
29046      * The last direct child node of this node, or null if this node has no child nodes. @type Node
29047      */
29048     this.lastChild = null;
29049     /**
29050      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
29051      */
29052     this.previousSibling = null;
29053     /**
29054      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
29055      */
29056     this.nextSibling = null;
29057
29058     this.addEvents({
29059        /**
29060         * @event append
29061         * Fires when a new child node is appended
29062         * @param {Tree} tree The owner tree
29063         * @param {Node} this This node
29064         * @param {Node} node The newly appended node
29065         * @param {Number} index The index of the newly appended node
29066         */
29067        "append" : true,
29068        /**
29069         * @event remove
29070         * Fires when a child node is removed
29071         * @param {Tree} tree The owner tree
29072         * @param {Node} this This node
29073         * @param {Node} node The removed node
29074         */
29075        "remove" : true,
29076        /**
29077         * @event move
29078         * Fires when this node is moved to a new location in the tree
29079         * @param {Tree} tree The owner tree
29080         * @param {Node} this This node
29081         * @param {Node} oldParent The old parent of this node
29082         * @param {Node} newParent The new parent of this node
29083         * @param {Number} index The index it was moved to
29084         */
29085        "move" : true,
29086        /**
29087         * @event insert
29088         * Fires when a new child node is inserted.
29089         * @param {Tree} tree The owner tree
29090         * @param {Node} this This node
29091         * @param {Node} node The child node inserted
29092         * @param {Node} refNode The child node the node was inserted before
29093         */
29094        "insert" : true,
29095        /**
29096         * @event beforeappend
29097         * Fires before a new child is appended, return false to cancel the append.
29098         * @param {Tree} tree The owner tree
29099         * @param {Node} this This node
29100         * @param {Node} node The child node to be appended
29101         */
29102        "beforeappend" : true,
29103        /**
29104         * @event beforeremove
29105         * Fires before a child is removed, return false to cancel the remove.
29106         * @param {Tree} tree The owner tree
29107         * @param {Node} this This node
29108         * @param {Node} node The child node to be removed
29109         */
29110        "beforeremove" : true,
29111        /**
29112         * @event beforemove
29113         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
29114         * @param {Tree} tree The owner tree
29115         * @param {Node} this This node
29116         * @param {Node} oldParent The parent of this node
29117         * @param {Node} newParent The new parent this node is moving to
29118         * @param {Number} index The index it is being moved to
29119         */
29120        "beforemove" : true,
29121        /**
29122         * @event beforeinsert
29123         * Fires before a new child is inserted, return false to cancel the insert.
29124         * @param {Tree} tree The owner tree
29125         * @param {Node} this This node
29126         * @param {Node} node The child node to be inserted
29127         * @param {Node} refNode The child node the node is being inserted before
29128         */
29129        "beforeinsert" : true
29130    });
29131     this.listeners = this.attributes.listeners;
29132     Roo.data.Node.superclass.constructor.call(this);
29133 };
29134
29135 Roo.extend(Roo.data.Node, Roo.util.Observable, {
29136     fireEvent : function(evtName){
29137         // first do standard event for this node
29138         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
29139             return false;
29140         }
29141         // then bubble it up to the tree if the event wasn't cancelled
29142         var ot = this.getOwnerTree();
29143         if(ot){
29144             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
29145                 return false;
29146             }
29147         }
29148         return true;
29149     },
29150
29151     /**
29152      * Returns true if this node is a leaf
29153      * @return {Boolean}
29154      */
29155     isLeaf : function(){
29156         return this.leaf === true;
29157     },
29158
29159     // private
29160     setFirstChild : function(node){
29161         this.firstChild = node;
29162     },
29163
29164     //private
29165     setLastChild : function(node){
29166         this.lastChild = node;
29167     },
29168
29169
29170     /**
29171      * Returns true if this node is the last child of its parent
29172      * @return {Boolean}
29173      */
29174     isLast : function(){
29175        return (!this.parentNode ? true : this.parentNode.lastChild == this);
29176     },
29177
29178     /**
29179      * Returns true if this node is the first child of its parent
29180      * @return {Boolean}
29181      */
29182     isFirst : function(){
29183        return (!this.parentNode ? true : this.parentNode.firstChild == this);
29184     },
29185
29186     hasChildNodes : function(){
29187         return !this.isLeaf() && this.childNodes.length > 0;
29188     },
29189
29190     /**
29191      * Insert node(s) as the last child node of this node.
29192      * @param {Node/Array} node The node or Array of nodes to append
29193      * @return {Node} The appended node if single append, or null if an array was passed
29194      */
29195     appendChild : function(node){
29196         var multi = false;
29197         if(node instanceof Array){
29198             multi = node;
29199         }else if(arguments.length > 1){
29200             multi = arguments;
29201         }
29202         // if passed an array or multiple args do them one by one
29203         if(multi){
29204             for(var i = 0, len = multi.length; i < len; i++) {
29205                 this.appendChild(multi[i]);
29206             }
29207         }else{
29208             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
29209                 return false;
29210             }
29211             var index = this.childNodes.length;
29212             var oldParent = node.parentNode;
29213             // it's a move, make sure we move it cleanly
29214             if(oldParent){
29215                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
29216                     return false;
29217                 }
29218                 oldParent.removeChild(node);
29219             }
29220             index = this.childNodes.length;
29221             if(index == 0){
29222                 this.setFirstChild(node);
29223             }
29224             this.childNodes.push(node);
29225             node.parentNode = this;
29226             var ps = this.childNodes[index-1];
29227             if(ps){
29228                 node.previousSibling = ps;
29229                 ps.nextSibling = node;
29230             }else{
29231                 node.previousSibling = null;
29232             }
29233             node.nextSibling = null;
29234             this.setLastChild(node);
29235             node.setOwnerTree(this.getOwnerTree());
29236             this.fireEvent("append", this.ownerTree, this, node, index);
29237             if(oldParent){
29238                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
29239             }
29240             return node;
29241         }
29242     },
29243
29244     /**
29245      * Removes a child node from this node.
29246      * @param {Node} node The node to remove
29247      * @return {Node} The removed node
29248      */
29249     removeChild : function(node){
29250         var index = this.childNodes.indexOf(node);
29251         if(index == -1){
29252             return false;
29253         }
29254         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
29255             return false;
29256         }
29257
29258         // remove it from childNodes collection
29259         this.childNodes.splice(index, 1);
29260
29261         // update siblings
29262         if(node.previousSibling){
29263             node.previousSibling.nextSibling = node.nextSibling;
29264         }
29265         if(node.nextSibling){
29266             node.nextSibling.previousSibling = node.previousSibling;
29267         }
29268
29269         // update child refs
29270         if(this.firstChild == node){
29271             this.setFirstChild(node.nextSibling);
29272         }
29273         if(this.lastChild == node){
29274             this.setLastChild(node.previousSibling);
29275         }
29276
29277         node.setOwnerTree(null);
29278         // clear any references from the node
29279         node.parentNode = null;
29280         node.previousSibling = null;
29281         node.nextSibling = null;
29282         this.fireEvent("remove", this.ownerTree, this, node);
29283         return node;
29284     },
29285
29286     /**
29287      * Inserts the first node before the second node in this nodes childNodes collection.
29288      * @param {Node} node The node to insert
29289      * @param {Node} refNode The node to insert before (if null the node is appended)
29290      * @return {Node} The inserted node
29291      */
29292     insertBefore : function(node, refNode){
29293         if(!refNode){ // like standard Dom, refNode can be null for append
29294             return this.appendChild(node);
29295         }
29296         // nothing to do
29297         if(node == refNode){
29298             return false;
29299         }
29300
29301         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
29302             return false;
29303         }
29304         var index = this.childNodes.indexOf(refNode);
29305         var oldParent = node.parentNode;
29306         var refIndex = index;
29307
29308         // when moving internally, indexes will change after remove
29309         if(oldParent == this && this.childNodes.indexOf(node) < index){
29310             refIndex--;
29311         }
29312
29313         // it's a move, make sure we move it cleanly
29314         if(oldParent){
29315             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
29316                 return false;
29317             }
29318             oldParent.removeChild(node);
29319         }
29320         if(refIndex == 0){
29321             this.setFirstChild(node);
29322         }
29323         this.childNodes.splice(refIndex, 0, node);
29324         node.parentNode = this;
29325         var ps = this.childNodes[refIndex-1];
29326         if(ps){
29327             node.previousSibling = ps;
29328             ps.nextSibling = node;
29329         }else{
29330             node.previousSibling = null;
29331         }
29332         node.nextSibling = refNode;
29333         refNode.previousSibling = node;
29334         node.setOwnerTree(this.getOwnerTree());
29335         this.fireEvent("insert", this.ownerTree, this, node, refNode);
29336         if(oldParent){
29337             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
29338         }
29339         return node;
29340     },
29341
29342     /**
29343      * Returns the child node at the specified index.
29344      * @param {Number} index
29345      * @return {Node}
29346      */
29347     item : function(index){
29348         return this.childNodes[index];
29349     },
29350
29351     /**
29352      * Replaces one child node in this node with another.
29353      * @param {Node} newChild The replacement node
29354      * @param {Node} oldChild The node to replace
29355      * @return {Node} The replaced node
29356      */
29357     replaceChild : function(newChild, oldChild){
29358         this.insertBefore(newChild, oldChild);
29359         this.removeChild(oldChild);
29360         return oldChild;
29361     },
29362
29363     /**
29364      * Returns the index of a child node
29365      * @param {Node} node
29366      * @return {Number} The index of the node or -1 if it was not found
29367      */
29368     indexOf : function(child){
29369         return this.childNodes.indexOf(child);
29370     },
29371
29372     /**
29373      * Returns the tree this node is in.
29374      * @return {Tree}
29375      */
29376     getOwnerTree : function(){
29377         // if it doesn't have one, look for one
29378         if(!this.ownerTree){
29379             var p = this;
29380             while(p){
29381                 if(p.ownerTree){
29382                     this.ownerTree = p.ownerTree;
29383                     break;
29384                 }
29385                 p = p.parentNode;
29386             }
29387         }
29388         return this.ownerTree;
29389     },
29390
29391     /**
29392      * Returns depth of this node (the root node has a depth of 0)
29393      * @return {Number}
29394      */
29395     getDepth : function(){
29396         var depth = 0;
29397         var p = this;
29398         while(p.parentNode){
29399             ++depth;
29400             p = p.parentNode;
29401         }
29402         return depth;
29403     },
29404
29405     // private
29406     setOwnerTree : function(tree){
29407         // if it's move, we need to update everyone
29408         if(tree != this.ownerTree){
29409             if(this.ownerTree){
29410                 this.ownerTree.unregisterNode(this);
29411             }
29412             this.ownerTree = tree;
29413             var cs = this.childNodes;
29414             for(var i = 0, len = cs.length; i < len; i++) {
29415                 cs[i].setOwnerTree(tree);
29416             }
29417             if(tree){
29418                 tree.registerNode(this);
29419             }
29420         }
29421     },
29422
29423     /**
29424      * Returns the path for this node. The path can be used to expand or select this node programmatically.
29425      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
29426      * @return {String} The path
29427      */
29428     getPath : function(attr){
29429         attr = attr || "id";
29430         var p = this.parentNode;
29431         var b = [this.attributes[attr]];
29432         while(p){
29433             b.unshift(p.attributes[attr]);
29434             p = p.parentNode;
29435         }
29436         var sep = this.getOwnerTree().pathSeparator;
29437         return sep + b.join(sep);
29438     },
29439
29440     /**
29441      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
29442      * function call will be the scope provided or the current node. The arguments to the function
29443      * will be the args provided or the current node. If the function returns false at any point,
29444      * the bubble is stopped.
29445      * @param {Function} fn The function to call
29446      * @param {Object} scope (optional) The scope of the function (defaults to current node)
29447      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
29448      */
29449     bubble : function(fn, scope, args){
29450         var p = this;
29451         while(p){
29452             if(fn.call(scope || p, args || p) === false){
29453                 break;
29454             }
29455             p = p.parentNode;
29456         }
29457     },
29458
29459     /**
29460      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
29461      * function call will be the scope provided or the current node. The arguments to the function
29462      * will be the args provided or the current node. If the function returns false at any point,
29463      * the cascade is stopped on that branch.
29464      * @param {Function} fn The function to call
29465      * @param {Object} scope (optional) The scope of the function (defaults to current node)
29466      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
29467      */
29468     cascade : function(fn, scope, args){
29469         if(fn.call(scope || this, args || this) !== false){
29470             var cs = this.childNodes;
29471             for(var i = 0, len = cs.length; i < len; i++) {
29472                 cs[i].cascade(fn, scope, args);
29473             }
29474         }
29475     },
29476
29477     /**
29478      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
29479      * function call will be the scope provided or the current node. The arguments to the function
29480      * will be the args provided or the current node. If the function returns false at any point,
29481      * the iteration stops.
29482      * @param {Function} fn The function to call
29483      * @param {Object} scope (optional) The scope of the function (defaults to current node)
29484      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
29485      */
29486     eachChild : function(fn, scope, args){
29487         var cs = this.childNodes;
29488         for(var i = 0, len = cs.length; i < len; i++) {
29489                 if(fn.call(scope || this, args || cs[i]) === false){
29490                     break;
29491                 }
29492         }
29493     },
29494
29495     /**
29496      * Finds the first child that has the attribute with the specified value.
29497      * @param {String} attribute The attribute name
29498      * @param {Mixed} value The value to search for
29499      * @return {Node} The found child or null if none was found
29500      */
29501     findChild : function(attribute, value){
29502         var cs = this.childNodes;
29503         for(var i = 0, len = cs.length; i < len; i++) {
29504                 if(cs[i].attributes[attribute] == value){
29505                     return cs[i];
29506                 }
29507         }
29508         return null;
29509     },
29510
29511     /**
29512      * Finds the first child by a custom function. The child matches if the function passed
29513      * returns true.
29514      * @param {Function} fn
29515      * @param {Object} scope (optional)
29516      * @return {Node} The found child or null if none was found
29517      */
29518     findChildBy : function(fn, scope){
29519         var cs = this.childNodes;
29520         for(var i = 0, len = cs.length; i < len; i++) {
29521                 if(fn.call(scope||cs[i], cs[i]) === true){
29522                     return cs[i];
29523                 }
29524         }
29525         return null;
29526     },
29527
29528     /**
29529      * Sorts this nodes children using the supplied sort function
29530      * @param {Function} fn
29531      * @param {Object} scope (optional)
29532      */
29533     sort : function(fn, scope){
29534         var cs = this.childNodes;
29535         var len = cs.length;
29536         if(len > 0){
29537             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
29538             cs.sort(sortFn);
29539             for(var i = 0; i < len; i++){
29540                 var n = cs[i];
29541                 n.previousSibling = cs[i-1];
29542                 n.nextSibling = cs[i+1];
29543                 if(i == 0){
29544                     this.setFirstChild(n);
29545                 }
29546                 if(i == len-1){
29547                     this.setLastChild(n);
29548                 }
29549             }
29550         }
29551     },
29552
29553     /**
29554      * Returns true if this node is an ancestor (at any point) of the passed node.
29555      * @param {Node} node
29556      * @return {Boolean}
29557      */
29558     contains : function(node){
29559         return node.isAncestor(this);
29560     },
29561
29562     /**
29563      * Returns true if the passed node is an ancestor (at any point) of this node.
29564      * @param {Node} node
29565      * @return {Boolean}
29566      */
29567     isAncestor : function(node){
29568         var p = this.parentNode;
29569         while(p){
29570             if(p == node){
29571                 return true;
29572             }
29573             p = p.parentNode;
29574         }
29575         return false;
29576     },
29577
29578     toString : function(){
29579         return "[Node"+(this.id?" "+this.id:"")+"]";
29580     }
29581 });/*
29582  * Based on:
29583  * Ext JS Library 1.1.1
29584  * Copyright(c) 2006-2007, Ext JS, LLC.
29585  *
29586  * Originally Released Under LGPL - original licence link has changed is not relivant.
29587  *
29588  * Fork - LGPL
29589  * <script type="text/javascript">
29590  */
29591  (function(){ 
29592 /**
29593  * @class Roo.Layer
29594  * @extends Roo.Element
29595  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
29596  * automatic maintaining of shadow/shim positions.
29597  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
29598  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
29599  * you can pass a string with a CSS class name. False turns off the shadow.
29600  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
29601  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
29602  * @cfg {String} cls CSS class to add to the element
29603  * @cfg {Number} zindex Starting z-index (defaults to 11000)
29604  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
29605  * @constructor
29606  * @param {Object} config An object with config options.
29607  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
29608  */
29609
29610 Roo.Layer = function(config, existingEl){
29611     config = config || {};
29612     var dh = Roo.DomHelper;
29613     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
29614     if(existingEl){
29615         this.dom = Roo.getDom(existingEl);
29616     }
29617     if(!this.dom){
29618         var o = config.dh || {tag: "div", cls: "x-layer"};
29619         this.dom = dh.append(pel, o);
29620     }
29621     if(config.cls){
29622         this.addClass(config.cls);
29623     }
29624     this.constrain = config.constrain !== false;
29625     this.visibilityMode = Roo.Element.VISIBILITY;
29626     if(config.id){
29627         this.id = this.dom.id = config.id;
29628     }else{
29629         this.id = Roo.id(this.dom);
29630     }
29631     this.zindex = config.zindex || this.getZIndex();
29632     this.position("absolute", this.zindex);
29633     if(config.shadow){
29634         this.shadowOffset = config.shadowOffset || 4;
29635         this.shadow = new Roo.Shadow({
29636             offset : this.shadowOffset,
29637             mode : config.shadow
29638         });
29639     }else{
29640         this.shadowOffset = 0;
29641     }
29642     this.useShim = config.shim !== false && Roo.useShims;
29643     this.useDisplay = config.useDisplay;
29644     this.hide();
29645 };
29646
29647 var supr = Roo.Element.prototype;
29648
29649 // shims are shared among layer to keep from having 100 iframes
29650 var shims = [];
29651
29652 Roo.extend(Roo.Layer, Roo.Element, {
29653
29654     getZIndex : function(){
29655         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
29656     },
29657
29658     getShim : function(){
29659         if(!this.useShim){
29660             return null;
29661         }
29662         if(this.shim){
29663             return this.shim;
29664         }
29665         var shim = shims.shift();
29666         if(!shim){
29667             shim = this.createShim();
29668             shim.enableDisplayMode('block');
29669             shim.dom.style.display = 'none';
29670             shim.dom.style.visibility = 'visible';
29671         }
29672         var pn = this.dom.parentNode;
29673         if(shim.dom.parentNode != pn){
29674             pn.insertBefore(shim.dom, this.dom);
29675         }
29676         shim.setStyle('z-index', this.getZIndex()-2);
29677         this.shim = shim;
29678         return shim;
29679     },
29680
29681     hideShim : function(){
29682         if(this.shim){
29683             this.shim.setDisplayed(false);
29684             shims.push(this.shim);
29685             delete this.shim;
29686         }
29687     },
29688
29689     disableShadow : function(){
29690         if(this.shadow){
29691             this.shadowDisabled = true;
29692             this.shadow.hide();
29693             this.lastShadowOffset = this.shadowOffset;
29694             this.shadowOffset = 0;
29695         }
29696     },
29697
29698     enableShadow : function(show){
29699         if(this.shadow){
29700             this.shadowDisabled = false;
29701             this.shadowOffset = this.lastShadowOffset;
29702             delete this.lastShadowOffset;
29703             if(show){
29704                 this.sync(true);
29705             }
29706         }
29707     },
29708
29709     // private
29710     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
29711     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
29712     sync : function(doShow){
29713         var sw = this.shadow;
29714         if(!this.updating && this.isVisible() && (sw || this.useShim)){
29715             var sh = this.getShim();
29716
29717             var w = this.getWidth(),
29718                 h = this.getHeight();
29719
29720             var l = this.getLeft(true),
29721                 t = this.getTop(true);
29722
29723             if(sw && !this.shadowDisabled){
29724                 if(doShow && !sw.isVisible()){
29725                     sw.show(this);
29726                 }else{
29727                     sw.realign(l, t, w, h);
29728                 }
29729                 if(sh){
29730                     if(doShow){
29731                        sh.show();
29732                     }
29733                     // fit the shim behind the shadow, so it is shimmed too
29734                     var a = sw.adjusts, s = sh.dom.style;
29735                     s.left = (Math.min(l, l+a.l))+"px";
29736                     s.top = (Math.min(t, t+a.t))+"px";
29737                     s.width = (w+a.w)+"px";
29738                     s.height = (h+a.h)+"px";
29739                 }
29740             }else if(sh){
29741                 if(doShow){
29742                    sh.show();
29743                 }
29744                 sh.setSize(w, h);
29745                 sh.setLeftTop(l, t);
29746             }
29747             
29748         }
29749     },
29750
29751     // private
29752     destroy : function(){
29753         this.hideShim();
29754         if(this.shadow){
29755             this.shadow.hide();
29756         }
29757         this.removeAllListeners();
29758         var pn = this.dom.parentNode;
29759         if(pn){
29760             pn.removeChild(this.dom);
29761         }
29762         Roo.Element.uncache(this.id);
29763     },
29764
29765     remove : function(){
29766         this.destroy();
29767     },
29768
29769     // private
29770     beginUpdate : function(){
29771         this.updating = true;
29772     },
29773
29774     // private
29775     endUpdate : function(){
29776         this.updating = false;
29777         this.sync(true);
29778     },
29779
29780     // private
29781     hideUnders : function(negOffset){
29782         if(this.shadow){
29783             this.shadow.hide();
29784         }
29785         this.hideShim();
29786     },
29787
29788     // private
29789     constrainXY : function(){
29790         if(this.constrain){
29791             var vw = Roo.lib.Dom.getViewWidth(),
29792                 vh = Roo.lib.Dom.getViewHeight();
29793             var s = Roo.get(document).getScroll();
29794
29795             var xy = this.getXY();
29796             var x = xy[0], y = xy[1];   
29797             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
29798             // only move it if it needs it
29799             var moved = false;
29800             // first validate right/bottom
29801             if((x + w) > vw+s.left){
29802                 x = vw - w - this.shadowOffset;
29803                 moved = true;
29804             }
29805             if((y + h) > vh+s.top){
29806                 y = vh - h - this.shadowOffset;
29807                 moved = true;
29808             }
29809             // then make sure top/left isn't negative
29810             if(x < s.left){
29811                 x = s.left;
29812                 moved = true;
29813             }
29814             if(y < s.top){
29815                 y = s.top;
29816                 moved = true;
29817             }
29818             if(moved){
29819                 if(this.avoidY){
29820                     var ay = this.avoidY;
29821                     if(y <= ay && (y+h) >= ay){
29822                         y = ay-h-5;   
29823                     }
29824                 }
29825                 xy = [x, y];
29826                 this.storeXY(xy);
29827                 supr.setXY.call(this, xy);
29828                 this.sync();
29829             }
29830         }
29831     },
29832
29833     isVisible : function(){
29834         return this.visible;    
29835     },
29836
29837     // private
29838     showAction : function(){
29839         this.visible = true; // track visibility to prevent getStyle calls
29840         if(this.useDisplay === true){
29841             this.setDisplayed("");
29842         }else if(this.lastXY){
29843             supr.setXY.call(this, this.lastXY);
29844         }else if(this.lastLT){
29845             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
29846         }
29847     },
29848
29849     // private
29850     hideAction : function(){
29851         this.visible = false;
29852         if(this.useDisplay === true){
29853             this.setDisplayed(false);
29854         }else{
29855             this.setLeftTop(-10000,-10000);
29856         }
29857     },
29858
29859     // overridden Element method
29860     setVisible : function(v, a, d, c, e){
29861         if(v){
29862             this.showAction();
29863         }
29864         if(a && v){
29865             var cb = function(){
29866                 this.sync(true);
29867                 if(c){
29868                     c();
29869                 }
29870             }.createDelegate(this);
29871             supr.setVisible.call(this, true, true, d, cb, e);
29872         }else{
29873             if(!v){
29874                 this.hideUnders(true);
29875             }
29876             var cb = c;
29877             if(a){
29878                 cb = function(){
29879                     this.hideAction();
29880                     if(c){
29881                         c();
29882                     }
29883                 }.createDelegate(this);
29884             }
29885             supr.setVisible.call(this, v, a, d, cb, e);
29886             if(v){
29887                 this.sync(true);
29888             }else if(!a){
29889                 this.hideAction();
29890             }
29891         }
29892     },
29893
29894     storeXY : function(xy){
29895         delete this.lastLT;
29896         this.lastXY = xy;
29897     },
29898
29899     storeLeftTop : function(left, top){
29900         delete this.lastXY;
29901         this.lastLT = [left, top];
29902     },
29903
29904     // private
29905     beforeFx : function(){
29906         this.beforeAction();
29907         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
29908     },
29909
29910     // private
29911     afterFx : function(){
29912         Roo.Layer.superclass.afterFx.apply(this, arguments);
29913         this.sync(this.isVisible());
29914     },
29915
29916     // private
29917     beforeAction : function(){
29918         if(!this.updating && this.shadow){
29919             this.shadow.hide();
29920         }
29921     },
29922
29923     // overridden Element method
29924     setLeft : function(left){
29925         this.storeLeftTop(left, this.getTop(true));
29926         supr.setLeft.apply(this, arguments);
29927         this.sync();
29928     },
29929
29930     setTop : function(top){
29931         this.storeLeftTop(this.getLeft(true), top);
29932         supr.setTop.apply(this, arguments);
29933         this.sync();
29934     },
29935
29936     setLeftTop : function(left, top){
29937         this.storeLeftTop(left, top);
29938         supr.setLeftTop.apply(this, arguments);
29939         this.sync();
29940     },
29941
29942     setXY : function(xy, a, d, c, e){
29943         this.fixDisplay();
29944         this.beforeAction();
29945         this.storeXY(xy);
29946         var cb = this.createCB(c);
29947         supr.setXY.call(this, xy, a, d, cb, e);
29948         if(!a){
29949             cb();
29950         }
29951     },
29952
29953     // private
29954     createCB : function(c){
29955         var el = this;
29956         return function(){
29957             el.constrainXY();
29958             el.sync(true);
29959             if(c){
29960                 c();
29961             }
29962         };
29963     },
29964
29965     // overridden Element method
29966     setX : function(x, a, d, c, e){
29967         this.setXY([x, this.getY()], a, d, c, e);
29968     },
29969
29970     // overridden Element method
29971     setY : function(y, a, d, c, e){
29972         this.setXY([this.getX(), y], a, d, c, e);
29973     },
29974
29975     // overridden Element method
29976     setSize : function(w, h, a, d, c, e){
29977         this.beforeAction();
29978         var cb = this.createCB(c);
29979         supr.setSize.call(this, w, h, a, d, cb, e);
29980         if(!a){
29981             cb();
29982         }
29983     },
29984
29985     // overridden Element method
29986     setWidth : function(w, a, d, c, e){
29987         this.beforeAction();
29988         var cb = this.createCB(c);
29989         supr.setWidth.call(this, w, a, d, cb, e);
29990         if(!a){
29991             cb();
29992         }
29993     },
29994
29995     // overridden Element method
29996     setHeight : function(h, a, d, c, e){
29997         this.beforeAction();
29998         var cb = this.createCB(c);
29999         supr.setHeight.call(this, h, a, d, cb, e);
30000         if(!a){
30001             cb();
30002         }
30003     },
30004
30005     // overridden Element method
30006     setBounds : function(x, y, w, h, a, d, c, e){
30007         this.beforeAction();
30008         var cb = this.createCB(c);
30009         if(!a){
30010             this.storeXY([x, y]);
30011             supr.setXY.call(this, [x, y]);
30012             supr.setSize.call(this, w, h, a, d, cb, e);
30013             cb();
30014         }else{
30015             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
30016         }
30017         return this;
30018     },
30019     
30020     /**
30021      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
30022      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
30023      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
30024      * @param {Number} zindex The new z-index to set
30025      * @return {this} The Layer
30026      */
30027     setZIndex : function(zindex){
30028         this.zindex = zindex;
30029         this.setStyle("z-index", zindex + 2);
30030         if(this.shadow){
30031             this.shadow.setZIndex(zindex + 1);
30032         }
30033         if(this.shim){
30034             this.shim.setStyle("z-index", zindex);
30035         }
30036     }
30037 });
30038 })();/*
30039  * Based on:
30040  * Ext JS Library 1.1.1
30041  * Copyright(c) 2006-2007, Ext JS, LLC.
30042  *
30043  * Originally Released Under LGPL - original licence link has changed is not relivant.
30044  *
30045  * Fork - LGPL
30046  * <script type="text/javascript">
30047  */
30048
30049
30050 /**
30051  * @class Roo.Shadow
30052  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
30053  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
30054  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
30055  * @constructor
30056  * Create a new Shadow
30057  * @param {Object} config The config object
30058  */
30059 Roo.Shadow = function(config){
30060     Roo.apply(this, config);
30061     if(typeof this.mode != "string"){
30062         this.mode = this.defaultMode;
30063     }
30064     var o = this.offset, a = {h: 0};
30065     var rad = Math.floor(this.offset/2);
30066     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
30067         case "drop":
30068             a.w = 0;
30069             a.l = a.t = o;
30070             a.t -= 1;
30071             if(Roo.isIE){
30072                 a.l -= this.offset + rad;
30073                 a.t -= this.offset + rad;
30074                 a.w -= rad;
30075                 a.h -= rad;
30076                 a.t += 1;
30077             }
30078         break;
30079         case "sides":
30080             a.w = (o*2);
30081             a.l = -o;
30082             a.t = o-1;
30083             if(Roo.isIE){
30084                 a.l -= (this.offset - rad);
30085                 a.t -= this.offset + rad;
30086                 a.l += 1;
30087                 a.w -= (this.offset - rad)*2;
30088                 a.w -= rad + 1;
30089                 a.h -= 1;
30090             }
30091         break;
30092         case "frame":
30093             a.w = a.h = (o*2);
30094             a.l = a.t = -o;
30095             a.t += 1;
30096             a.h -= 2;
30097             if(Roo.isIE){
30098                 a.l -= (this.offset - rad);
30099                 a.t -= (this.offset - rad);
30100                 a.l += 1;
30101                 a.w -= (this.offset + rad + 1);
30102                 a.h -= (this.offset + rad);
30103                 a.h += 1;
30104             }
30105         break;
30106     };
30107
30108     this.adjusts = a;
30109 };
30110
30111 Roo.Shadow.prototype = {
30112     /**
30113      * @cfg {String} mode
30114      * The shadow display mode.  Supports the following options:<br />
30115      * sides: Shadow displays on both sides and bottom only<br />
30116      * frame: Shadow displays equally on all four sides<br />
30117      * drop: Traditional bottom-right drop shadow (default)
30118      */
30119     /**
30120      * @cfg {String} offset
30121      * The number of pixels to offset the shadow from the element (defaults to 4)
30122      */
30123     offset: 4,
30124
30125     // private
30126     defaultMode: "drop",
30127
30128     /**
30129      * Displays the shadow under the target element
30130      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
30131      */
30132     show : function(target){
30133         target = Roo.get(target);
30134         if(!this.el){
30135             this.el = Roo.Shadow.Pool.pull();
30136             if(this.el.dom.nextSibling != target.dom){
30137                 this.el.insertBefore(target);
30138             }
30139         }
30140         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
30141         if(Roo.isIE){
30142             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
30143         }
30144         this.realign(
30145             target.getLeft(true),
30146             target.getTop(true),
30147             target.getWidth(),
30148             target.getHeight()
30149         );
30150         this.el.dom.style.display = "block";
30151     },
30152
30153     /**
30154      * Returns true if the shadow is visible, else false
30155      */
30156     isVisible : function(){
30157         return this.el ? true : false;  
30158     },
30159
30160     /**
30161      * Direct alignment when values are already available. Show must be called at least once before
30162      * calling this method to ensure it is initialized.
30163      * @param {Number} left The target element left position
30164      * @param {Number} top The target element top position
30165      * @param {Number} width The target element width
30166      * @param {Number} height The target element height
30167      */
30168     realign : function(l, t, w, h){
30169         if(!this.el){
30170             return;
30171         }
30172         var a = this.adjusts, d = this.el.dom, s = d.style;
30173         var iea = 0;
30174         s.left = (l+a.l)+"px";
30175         s.top = (t+a.t)+"px";
30176         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
30177  
30178         if(s.width != sws || s.height != shs){
30179             s.width = sws;
30180             s.height = shs;
30181             if(!Roo.isIE){
30182                 var cn = d.childNodes;
30183                 var sww = Math.max(0, (sw-12))+"px";
30184                 cn[0].childNodes[1].style.width = sww;
30185                 cn[1].childNodes[1].style.width = sww;
30186                 cn[2].childNodes[1].style.width = sww;
30187                 cn[1].style.height = Math.max(0, (sh-12))+"px";
30188             }
30189         }
30190     },
30191
30192     /**
30193      * Hides this shadow
30194      */
30195     hide : function(){
30196         if(this.el){
30197             this.el.dom.style.display = "none";
30198             Roo.Shadow.Pool.push(this.el);
30199             delete this.el;
30200         }
30201     },
30202
30203     /**
30204      * Adjust the z-index of this shadow
30205      * @param {Number} zindex The new z-index
30206      */
30207     setZIndex : function(z){
30208         this.zIndex = z;
30209         if(this.el){
30210             this.el.setStyle("z-index", z);
30211         }
30212     }
30213 };
30214
30215 // Private utility class that manages the internal Shadow cache
30216 Roo.Shadow.Pool = function(){
30217     var p = [];
30218     var markup = Roo.isIE ?
30219                  '<div class="x-ie-shadow"></div>' :
30220                  '<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>';
30221     return {
30222         pull : function(){
30223             var sh = p.shift();
30224             if(!sh){
30225                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
30226                 sh.autoBoxAdjust = false;
30227             }
30228             return sh;
30229         },
30230
30231         push : function(sh){
30232             p.push(sh);
30233         }
30234     };
30235 }();/*
30236  * Based on:
30237  * Ext JS Library 1.1.1
30238  * Copyright(c) 2006-2007, Ext JS, LLC.
30239  *
30240  * Originally Released Under LGPL - original licence link has changed is not relivant.
30241  *
30242  * Fork - LGPL
30243  * <script type="text/javascript">
30244  */
30245
30246
30247 /**
30248  * @class Roo.SplitBar
30249  * @extends Roo.util.Observable
30250  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30251  * <br><br>
30252  * Usage:
30253  * <pre><code>
30254 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
30255                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
30256 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
30257 split.minSize = 100;
30258 split.maxSize = 600;
30259 split.animate = true;
30260 split.on('moved', splitterMoved);
30261 </code></pre>
30262  * @constructor
30263  * Create a new SplitBar
30264  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
30265  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
30266  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30267  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
30268                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30269                         position of the SplitBar).
30270  */
30271 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
30272     
30273     /** @private */
30274     this.el = Roo.get(dragElement, true);
30275     this.el.dom.unselectable = "on";
30276     /** @private */
30277     this.resizingEl = Roo.get(resizingElement, true);
30278
30279     /**
30280      * @private
30281      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30282      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30283      * @type Number
30284      */
30285     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
30286     
30287     /**
30288      * The minimum size of the resizing element. (Defaults to 0)
30289      * @type Number
30290      */
30291     this.minSize = 0;
30292     
30293     /**
30294      * The maximum size of the resizing element. (Defaults to 2000)
30295      * @type Number
30296      */
30297     this.maxSize = 2000;
30298     
30299     /**
30300      * Whether to animate the transition to the new size
30301      * @type Boolean
30302      */
30303     this.animate = false;
30304     
30305     /**
30306      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30307      * @type Boolean
30308      */
30309     this.useShim = false;
30310     
30311     /** @private */
30312     this.shim = null;
30313     
30314     if(!existingProxy){
30315         /** @private */
30316         this.proxy = Roo.SplitBar.createProxy(this.orientation);
30317     }else{
30318         this.proxy = Roo.get(existingProxy).dom;
30319     }
30320     /** @private */
30321     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30322     
30323     /** @private */
30324     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30325     
30326     /** @private */
30327     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30328     
30329     /** @private */
30330     this.dragSpecs = {};
30331     
30332     /**
30333      * @private The adapter to use to positon and resize elements
30334      */
30335     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
30336     this.adapter.init(this);
30337     
30338     if(this.orientation == Roo.SplitBar.HORIZONTAL){
30339         /** @private */
30340         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
30341         this.el.addClass("x-splitbar-h");
30342     }else{
30343         /** @private */
30344         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
30345         this.el.addClass("x-splitbar-v");
30346     }
30347     
30348     this.addEvents({
30349         /**
30350          * @event resize
30351          * Fires when the splitter is moved (alias for {@link #event-moved})
30352          * @param {Roo.SplitBar} this
30353          * @param {Number} newSize the new width or height
30354          */
30355         "resize" : true,
30356         /**
30357          * @event moved
30358          * Fires when the splitter is moved
30359          * @param {Roo.SplitBar} this
30360          * @param {Number} newSize the new width or height
30361          */
30362         "moved" : true,
30363         /**
30364          * @event beforeresize
30365          * Fires before the splitter is dragged
30366          * @param {Roo.SplitBar} this
30367          */
30368         "beforeresize" : true,
30369
30370         "beforeapply" : true
30371     });
30372
30373     Roo.util.Observable.call(this);
30374 };
30375
30376 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
30377     onStartProxyDrag : function(x, y){
30378         this.fireEvent("beforeresize", this);
30379         if(!this.overlay){
30380             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
30381             o.unselectable();
30382             o.enableDisplayMode("block");
30383             // all splitbars share the same overlay
30384             Roo.SplitBar.prototype.overlay = o;
30385         }
30386         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30387         this.overlay.show();
30388         Roo.get(this.proxy).setDisplayed("block");
30389         var size = this.adapter.getElementSize(this);
30390         this.activeMinSize = this.getMinimumSize();;
30391         this.activeMaxSize = this.getMaximumSize();;
30392         var c1 = size - this.activeMinSize;
30393         var c2 = Math.max(this.activeMaxSize - size, 0);
30394         if(this.orientation == Roo.SplitBar.HORIZONTAL){
30395             this.dd.resetConstraints();
30396             this.dd.setXConstraint(
30397                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
30398                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
30399             );
30400             this.dd.setYConstraint(0, 0);
30401         }else{
30402             this.dd.resetConstraints();
30403             this.dd.setXConstraint(0, 0);
30404             this.dd.setYConstraint(
30405                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
30406                 this.placement == Roo.SplitBar.TOP ? c2 : c1
30407             );
30408          }
30409         this.dragSpecs.startSize = size;
30410         this.dragSpecs.startPoint = [x, y];
30411         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30412     },
30413     
30414     /** 
30415      * @private Called after the drag operation by the DDProxy
30416      */
30417     onEndProxyDrag : function(e){
30418         Roo.get(this.proxy).setDisplayed(false);
30419         var endPoint = Roo.lib.Event.getXY(e);
30420         if(this.overlay){
30421             this.overlay.hide();
30422         }
30423         var newSize;
30424         if(this.orientation == Roo.SplitBar.HORIZONTAL){
30425             newSize = this.dragSpecs.startSize + 
30426                 (this.placement == Roo.SplitBar.LEFT ?
30427                     endPoint[0] - this.dragSpecs.startPoint[0] :
30428                     this.dragSpecs.startPoint[0] - endPoint[0]
30429                 );
30430         }else{
30431             newSize = this.dragSpecs.startSize + 
30432                 (this.placement == Roo.SplitBar.TOP ?
30433                     endPoint[1] - this.dragSpecs.startPoint[1] :
30434                     this.dragSpecs.startPoint[1] - endPoint[1]
30435                 );
30436         }
30437         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30438         if(newSize != this.dragSpecs.startSize){
30439             if(this.fireEvent('beforeapply', this, newSize) !== false){
30440                 this.adapter.setElementSize(this, newSize);
30441                 this.fireEvent("moved", this, newSize);
30442                 this.fireEvent("resize", this, newSize);
30443             }
30444         }
30445     },
30446     
30447     /**
30448      * Get the adapter this SplitBar uses
30449      * @return The adapter object
30450      */
30451     getAdapter : function(){
30452         return this.adapter;
30453     },
30454     
30455     /**
30456      * Set the adapter this SplitBar uses
30457      * @param {Object} adapter A SplitBar adapter object
30458      */
30459     setAdapter : function(adapter){
30460         this.adapter = adapter;
30461         this.adapter.init(this);
30462     },
30463     
30464     /**
30465      * Gets the minimum size for the resizing element
30466      * @return {Number} The minimum size
30467      */
30468     getMinimumSize : function(){
30469         return this.minSize;
30470     },
30471     
30472     /**
30473      * Sets the minimum size for the resizing element
30474      * @param {Number} minSize The minimum size
30475      */
30476     setMinimumSize : function(minSize){
30477         this.minSize = minSize;
30478     },
30479     
30480     /**
30481      * Gets the maximum size for the resizing element
30482      * @return {Number} The maximum size
30483      */
30484     getMaximumSize : function(){
30485         return this.maxSize;
30486     },
30487     
30488     /**
30489      * Sets the maximum size for the resizing element
30490      * @param {Number} maxSize The maximum size
30491      */
30492     setMaximumSize : function(maxSize){
30493         this.maxSize = maxSize;
30494     },
30495     
30496     /**
30497      * Sets the initialize size for the resizing element
30498      * @param {Number} size The initial size
30499      */
30500     setCurrentSize : function(size){
30501         var oldAnimate = this.animate;
30502         this.animate = false;
30503         this.adapter.setElementSize(this, size);
30504         this.animate = oldAnimate;
30505     },
30506     
30507     /**
30508      * Destroy this splitbar. 
30509      * @param {Boolean} removeEl True to remove the element
30510      */
30511     destroy : function(removeEl){
30512         if(this.shim){
30513             this.shim.remove();
30514         }
30515         this.dd.unreg();
30516         this.proxy.parentNode.removeChild(this.proxy);
30517         if(removeEl){
30518             this.el.remove();
30519         }
30520     }
30521 });
30522
30523 /**
30524  * @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.
30525  */
30526 Roo.SplitBar.createProxy = function(dir){
30527     var proxy = new Roo.Element(document.createElement("div"));
30528     proxy.unselectable();
30529     var cls = 'x-splitbar-proxy';
30530     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
30531     document.body.appendChild(proxy.dom);
30532     return proxy.dom;
30533 };
30534
30535 /** 
30536  * @class Roo.SplitBar.BasicLayoutAdapter
30537  * Default Adapter. It assumes the splitter and resizing element are not positioned
30538  * elements and only gets/sets the width of the element. Generally used for table based layouts.
30539  */
30540 Roo.SplitBar.BasicLayoutAdapter = function(){
30541 };
30542
30543 Roo.SplitBar.BasicLayoutAdapter.prototype = {
30544     // do nothing for now
30545     init : function(s){
30546     
30547     },
30548     /**
30549      * Called before drag operations to get the current size of the resizing element. 
30550      * @param {Roo.SplitBar} s The SplitBar using this adapter
30551      */
30552      getElementSize : function(s){
30553         if(s.orientation == Roo.SplitBar.HORIZONTAL){
30554             return s.resizingEl.getWidth();
30555         }else{
30556             return s.resizingEl.getHeight();
30557         }
30558     },
30559     
30560     /**
30561      * Called after drag operations to set the size of the resizing element.
30562      * @param {Roo.SplitBar} s The SplitBar using this adapter
30563      * @param {Number} newSize The new size to set
30564      * @param {Function} onComplete A function to be invoked when resizing is complete
30565      */
30566     setElementSize : function(s, newSize, onComplete){
30567         if(s.orientation == Roo.SplitBar.HORIZONTAL){
30568             if(!s.animate){
30569                 s.resizingEl.setWidth(newSize);
30570                 if(onComplete){
30571                     onComplete(s, newSize);
30572                 }
30573             }else{
30574                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
30575             }
30576         }else{
30577             
30578             if(!s.animate){
30579                 s.resizingEl.setHeight(newSize);
30580                 if(onComplete){
30581                     onComplete(s, newSize);
30582                 }
30583             }else{
30584                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
30585             }
30586         }
30587     }
30588 };
30589
30590 /** 
30591  *@class Roo.SplitBar.AbsoluteLayoutAdapter
30592  * @extends Roo.SplitBar.BasicLayoutAdapter
30593  * Adapter that  moves the splitter element to align with the resized sizing element. 
30594  * Used with an absolute positioned SplitBar.
30595  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
30596  * document.body, make sure you assign an id to the body element.
30597  */
30598 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
30599     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
30600     this.container = Roo.get(container);
30601 };
30602
30603 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
30604     init : function(s){
30605         this.basic.init(s);
30606     },
30607     
30608     getElementSize : function(s){
30609         return this.basic.getElementSize(s);
30610     },
30611     
30612     setElementSize : function(s, newSize, onComplete){
30613         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
30614     },
30615     
30616     moveSplitter : function(s){
30617         var yes = Roo.SplitBar;
30618         switch(s.placement){
30619             case yes.LEFT:
30620                 s.el.setX(s.resizingEl.getRight());
30621                 break;
30622             case yes.RIGHT:
30623                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
30624                 break;
30625             case yes.TOP:
30626                 s.el.setY(s.resizingEl.getBottom());
30627                 break;
30628             case yes.BOTTOM:
30629                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
30630                 break;
30631         }
30632     }
30633 };
30634
30635 /**
30636  * Orientation constant - Create a vertical SplitBar
30637  * @static
30638  * @type Number
30639  */
30640 Roo.SplitBar.VERTICAL = 1;
30641
30642 /**
30643  * Orientation constant - Create a horizontal SplitBar
30644  * @static
30645  * @type Number
30646  */
30647 Roo.SplitBar.HORIZONTAL = 2;
30648
30649 /**
30650  * Placement constant - The resizing element is to the left of the splitter element
30651  * @static
30652  * @type Number
30653  */
30654 Roo.SplitBar.LEFT = 1;
30655
30656 /**
30657  * Placement constant - The resizing element is to the right of the splitter element
30658  * @static
30659  * @type Number
30660  */
30661 Roo.SplitBar.RIGHT = 2;
30662
30663 /**
30664  * Placement constant - The resizing element is positioned above the splitter element
30665  * @static
30666  * @type Number
30667  */
30668 Roo.SplitBar.TOP = 3;
30669
30670 /**
30671  * Placement constant - The resizing element is positioned under splitter element
30672  * @static
30673  * @type Number
30674  */
30675 Roo.SplitBar.BOTTOM = 4;
30676 /*
30677  * Based on:
30678  * Ext JS Library 1.1.1
30679  * Copyright(c) 2006-2007, Ext JS, LLC.
30680  *
30681  * Originally Released Under LGPL - original licence link has changed is not relivant.
30682  *
30683  * Fork - LGPL
30684  * <script type="text/javascript">
30685  */
30686
30687 /**
30688  * @class Roo.View
30689  * @extends Roo.util.Observable
30690  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
30691  * This class also supports single and multi selection modes. <br>
30692  * Create a data model bound view:
30693  <pre><code>
30694  var store = new Roo.data.Store(...);
30695
30696  var view = new Roo.View({
30697     el : "my-element",
30698     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
30699  
30700     singleSelect: true,
30701     selectedClass: "ydataview-selected",
30702     store: store
30703  });
30704
30705  // listen for node click?
30706  view.on("click", function(vw, index, node, e){
30707  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
30708  });
30709
30710  // load XML data
30711  dataModel.load("foobar.xml");
30712  </code></pre>
30713  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
30714  * <br><br>
30715  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
30716  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
30717  * 
30718  * Note: old style constructor is still suported (container, template, config)
30719  * 
30720  * @constructor
30721  * Create a new View
30722  * @param {Object} config The config object
30723  * 
30724  */
30725 Roo.View = function(config, depreciated_tpl, depreciated_config){
30726     
30727     this.parent = false;
30728     
30729     if (typeof(depreciated_tpl) == 'undefined') {
30730         // new way.. - universal constructor.
30731         Roo.apply(this, config);
30732         this.el  = Roo.get(this.el);
30733     } else {
30734         // old format..
30735         this.el  = Roo.get(config);
30736         this.tpl = depreciated_tpl;
30737         Roo.apply(this, depreciated_config);
30738     }
30739     this.wrapEl  = this.el.wrap().wrap();
30740     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
30741     
30742     
30743     if(typeof(this.tpl) == "string"){
30744         this.tpl = new Roo.Template(this.tpl);
30745     } else {
30746         // support xtype ctors..
30747         this.tpl = new Roo.factory(this.tpl, Roo);
30748     }
30749     
30750     
30751     this.tpl.compile();
30752     
30753     /** @private */
30754     this.addEvents({
30755         /**
30756          * @event beforeclick
30757          * Fires before a click is processed. Returns false to cancel the default action.
30758          * @param {Roo.View} this
30759          * @param {Number} index The index of the target node
30760          * @param {HTMLElement} node The target node
30761          * @param {Roo.EventObject} e The raw event object
30762          */
30763             "beforeclick" : true,
30764         /**
30765          * @event click
30766          * Fires when a template node is clicked.
30767          * @param {Roo.View} this
30768          * @param {Number} index The index of the target node
30769          * @param {HTMLElement} node The target node
30770          * @param {Roo.EventObject} e The raw event object
30771          */
30772             "click" : true,
30773         /**
30774          * @event dblclick
30775          * Fires when a template node is double clicked.
30776          * @param {Roo.View} this
30777          * @param {Number} index The index of the target node
30778          * @param {HTMLElement} node The target node
30779          * @param {Roo.EventObject} e The raw event object
30780          */
30781             "dblclick" : true,
30782         /**
30783          * @event contextmenu
30784          * Fires when a template node is right clicked.
30785          * @param {Roo.View} this
30786          * @param {Number} index The index of the target node
30787          * @param {HTMLElement} node The target node
30788          * @param {Roo.EventObject} e The raw event object
30789          */
30790             "contextmenu" : true,
30791         /**
30792          * @event selectionchange
30793          * Fires when the selected nodes change.
30794          * @param {Roo.View} this
30795          * @param {Array} selections Array of the selected nodes
30796          */
30797             "selectionchange" : true,
30798     
30799         /**
30800          * @event beforeselect
30801          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
30802          * @param {Roo.View} this
30803          * @param {HTMLElement} node The node to be selected
30804          * @param {Array} selections Array of currently selected nodes
30805          */
30806             "beforeselect" : true,
30807         /**
30808          * @event preparedata
30809          * Fires on every row to render, to allow you to change the data.
30810          * @param {Roo.View} this
30811          * @param {Object} data to be rendered (change this)
30812          */
30813           "preparedata" : true
30814           
30815           
30816         });
30817
30818
30819
30820     this.el.on({
30821         "click": this.onClick,
30822         "dblclick": this.onDblClick,
30823         "contextmenu": this.onContextMenu,
30824         scope:this
30825     });
30826
30827     this.selections = [];
30828     this.nodes = [];
30829     this.cmp = new Roo.CompositeElementLite([]);
30830     if(this.store){
30831         this.store = Roo.factory(this.store, Roo.data);
30832         this.setStore(this.store, true);
30833     }
30834     
30835     if ( this.footer && this.footer.xtype) {
30836            
30837          var fctr = this.wrapEl.appendChild(document.createElement("div"));
30838         
30839         this.footer.dataSource = this.store;
30840         this.footer.container = fctr;
30841         this.footer = Roo.factory(this.footer, Roo);
30842         fctr.insertFirst(this.el);
30843         
30844         // this is a bit insane - as the paging toolbar seems to detach the el..
30845 //        dom.parentNode.parentNode.parentNode
30846          // they get detached?
30847     }
30848     
30849     
30850     Roo.View.superclass.constructor.call(this);
30851     
30852     
30853 };
30854
30855 Roo.extend(Roo.View, Roo.util.Observable, {
30856     
30857      /**
30858      * @cfg {Roo.data.Store} store Data store to load data from.
30859      */
30860     store : false,
30861     
30862     /**
30863      * @cfg {String|Roo.Element} el The container element.
30864      */
30865     el : '',
30866     
30867     /**
30868      * @cfg {String|Roo.Template} tpl The template used by this View 
30869      */
30870     tpl : false,
30871     /**
30872      * @cfg {String} dataName the named area of the template to use as the data area
30873      *                          Works with domtemplates roo-name="name"
30874      */
30875     dataName: false,
30876     /**
30877      * @cfg {String} selectedClass The css class to add to selected nodes
30878      */
30879     selectedClass : "x-view-selected",
30880      /**
30881      * @cfg {String} emptyText The empty text to show when nothing is loaded.
30882      */
30883     emptyText : "",
30884     
30885     /**
30886      * @cfg {String} text to display on mask (default Loading)
30887      */
30888     mask : false,
30889     /**
30890      * @cfg {Boolean} multiSelect Allow multiple selection
30891      */
30892     multiSelect : false,
30893     /**
30894      * @cfg {Boolean} singleSelect Allow single selection
30895      */
30896     singleSelect:  false,
30897     
30898     /**
30899      * @cfg {Boolean} toggleSelect - selecting 
30900      */
30901     toggleSelect : false,
30902     
30903     /**
30904      * @cfg {Boolean} tickable - selecting 
30905      */
30906     tickable : false,
30907     
30908     /**
30909      * Returns the element this view is bound to.
30910      * @return {Roo.Element}
30911      */
30912     getEl : function(){
30913         return this.wrapEl;
30914     },
30915     
30916     
30917
30918     /**
30919      * Refreshes the view. - called by datachanged on the store. - do not call directly.
30920      */
30921     refresh : function(){
30922         //Roo.log('refresh');
30923         var t = this.tpl;
30924         
30925         // if we are using something like 'domtemplate', then
30926         // the what gets used is:
30927         // t.applySubtemplate(NAME, data, wrapping data..)
30928         // the outer template then get' applied with
30929         //     the store 'extra data'
30930         // and the body get's added to the
30931         //      roo-name="data" node?
30932         //      <span class='roo-tpl-{name}'></span> ?????
30933         
30934         
30935         
30936         this.clearSelections();
30937         this.el.update("");
30938         var html = [];
30939         var records = this.store.getRange();
30940         if(records.length < 1) {
30941             
30942             // is this valid??  = should it render a template??
30943             
30944             this.el.update(this.emptyText);
30945             return;
30946         }
30947         var el = this.el;
30948         if (this.dataName) {
30949             this.el.update(t.apply(this.store.meta)); //????
30950             el = this.el.child('.roo-tpl-' + this.dataName);
30951         }
30952         
30953         for(var i = 0, len = records.length; i < len; i++){
30954             var data = this.prepareData(records[i].data, i, records[i]);
30955             this.fireEvent("preparedata", this, data, i, records[i]);
30956             
30957             var d = Roo.apply({}, data);
30958             
30959             if(this.tickable){
30960                 Roo.apply(d, {'roo-id' : Roo.id()});
30961                 
30962                 var _this = this;
30963             
30964                 Roo.each(this.parent.item, function(item){
30965                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
30966                         return;
30967                     }
30968                     Roo.apply(d, {'roo-data-checked' : 'checked'});
30969                 });
30970             }
30971             
30972             html[html.length] = Roo.util.Format.trim(
30973                 this.dataName ?
30974                     t.applySubtemplate(this.dataName, d, this.store.meta) :
30975                     t.apply(d)
30976             );
30977         }
30978         
30979         
30980         
30981         el.update(html.join(""));
30982         this.nodes = el.dom.childNodes;
30983         this.updateIndexes(0);
30984     },
30985     
30986
30987     /**
30988      * Function to override to reformat the data that is sent to
30989      * the template for each node.
30990      * DEPRICATED - use the preparedata event handler.
30991      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
30992      * a JSON object for an UpdateManager bound view).
30993      */
30994     prepareData : function(data, index, record)
30995     {
30996         this.fireEvent("preparedata", this, data, index, record);
30997         return data;
30998     },
30999
31000     onUpdate : function(ds, record){
31001         // Roo.log('on update');   
31002         this.clearSelections();
31003         var index = this.store.indexOf(record);
31004         var n = this.nodes[index];
31005         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
31006         n.parentNode.removeChild(n);
31007         this.updateIndexes(index, index);
31008     },
31009
31010     
31011     
31012 // --------- FIXME     
31013     onAdd : function(ds, records, index)
31014     {
31015         //Roo.log(['on Add', ds, records, index] );        
31016         this.clearSelections();
31017         if(this.nodes.length == 0){
31018             this.refresh();
31019             return;
31020         }
31021         var n = this.nodes[index];
31022         for(var i = 0, len = records.length; i < len; i++){
31023             var d = this.prepareData(records[i].data, i, records[i]);
31024             if(n){
31025                 this.tpl.insertBefore(n, d);
31026             }else{
31027                 
31028                 this.tpl.append(this.el, d);
31029             }
31030         }
31031         this.updateIndexes(index);
31032     },
31033
31034     onRemove : function(ds, record, index){
31035        // Roo.log('onRemove');
31036         this.clearSelections();
31037         var el = this.dataName  ?
31038             this.el.child('.roo-tpl-' + this.dataName) :
31039             this.el; 
31040         
31041         el.dom.removeChild(this.nodes[index]);
31042         this.updateIndexes(index);
31043     },
31044
31045     /**
31046      * Refresh an individual node.
31047      * @param {Number} index
31048      */
31049     refreshNode : function(index){
31050         this.onUpdate(this.store, this.store.getAt(index));
31051     },
31052
31053     updateIndexes : function(startIndex, endIndex){
31054         var ns = this.nodes;
31055         startIndex = startIndex || 0;
31056         endIndex = endIndex || ns.length - 1;
31057         for(var i = startIndex; i <= endIndex; i++){
31058             ns[i].nodeIndex = i;
31059         }
31060     },
31061
31062     /**
31063      * Changes the data store this view uses and refresh the view.
31064      * @param {Store} store
31065      */
31066     setStore : function(store, initial){
31067         if(!initial && this.store){
31068             this.store.un("datachanged", this.refresh);
31069             this.store.un("add", this.onAdd);
31070             this.store.un("remove", this.onRemove);
31071             this.store.un("update", this.onUpdate);
31072             this.store.un("clear", this.refresh);
31073             this.store.un("beforeload", this.onBeforeLoad);
31074             this.store.un("load", this.onLoad);
31075             this.store.un("loadexception", this.onLoad);
31076         }
31077         if(store){
31078           
31079             store.on("datachanged", this.refresh, this);
31080             store.on("add", this.onAdd, this);
31081             store.on("remove", this.onRemove, this);
31082             store.on("update", this.onUpdate, this);
31083             store.on("clear", this.refresh, this);
31084             store.on("beforeload", this.onBeforeLoad, this);
31085             store.on("load", this.onLoad, this);
31086             store.on("loadexception", this.onLoad, this);
31087         }
31088         
31089         if(store){
31090             this.refresh();
31091         }
31092     },
31093     /**
31094      * onbeforeLoad - masks the loading area.
31095      *
31096      */
31097     onBeforeLoad : function(store,opts)
31098     {
31099          //Roo.log('onBeforeLoad');   
31100         if (!opts.add) {
31101             this.el.update("");
31102         }
31103         this.el.mask(this.mask ? this.mask : "Loading" ); 
31104     },
31105     onLoad : function ()
31106     {
31107         this.el.unmask();
31108     },
31109     
31110
31111     /**
31112      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
31113      * @param {HTMLElement} node
31114      * @return {HTMLElement} The template node
31115      */
31116     findItemFromChild : function(node){
31117         var el = this.dataName  ?
31118             this.el.child('.roo-tpl-' + this.dataName,true) :
31119             this.el.dom; 
31120         
31121         if(!node || node.parentNode == el){
31122                     return node;
31123             }
31124             var p = node.parentNode;
31125             while(p && p != el){
31126             if(p.parentNode == el){
31127                 return p;
31128             }
31129             p = p.parentNode;
31130         }
31131             return null;
31132     },
31133
31134     /** @ignore */
31135     onClick : function(e){
31136         var item = this.findItemFromChild(e.getTarget());
31137         if(item){
31138             var index = this.indexOf(item);
31139             if(this.onItemClick(item, index, e) !== false){
31140                 this.fireEvent("click", this, index, item, e);
31141             }
31142         }else{
31143             this.clearSelections();
31144         }
31145     },
31146
31147     /** @ignore */
31148     onContextMenu : function(e){
31149         var item = this.findItemFromChild(e.getTarget());
31150         if(item){
31151             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
31152         }
31153     },
31154
31155     /** @ignore */
31156     onDblClick : function(e){
31157         var item = this.findItemFromChild(e.getTarget());
31158         if(item){
31159             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
31160         }
31161     },
31162
31163     onItemClick : function(item, index, e)
31164     {
31165         if(this.fireEvent("beforeclick", this, index, item, e) === false){
31166             return false;
31167         }
31168         if (this.toggleSelect) {
31169             var m = this.isSelected(item) ? 'unselect' : 'select';
31170             //Roo.log(m);
31171             var _t = this;
31172             _t[m](item, true, false);
31173             return true;
31174         }
31175         if(this.multiSelect || this.singleSelect){
31176             if(this.multiSelect && e.shiftKey && this.lastSelection){
31177                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
31178             }else{
31179                 this.select(item, this.multiSelect && e.ctrlKey);
31180                 this.lastSelection = item;
31181             }
31182             
31183             if(!this.tickable){
31184                 e.preventDefault();
31185             }
31186             
31187         }
31188         return true;
31189     },
31190
31191     /**
31192      * Get the number of selected nodes.
31193      * @return {Number}
31194      */
31195     getSelectionCount : function(){
31196         return this.selections.length;
31197     },
31198
31199     /**
31200      * Get the currently selected nodes.
31201      * @return {Array} An array of HTMLElements
31202      */
31203     getSelectedNodes : function(){
31204         return this.selections;
31205     },
31206
31207     /**
31208      * Get the indexes of the selected nodes.
31209      * @return {Array}
31210      */
31211     getSelectedIndexes : function(){
31212         var indexes = [], s = this.selections;
31213         for(var i = 0, len = s.length; i < len; i++){
31214             indexes.push(s[i].nodeIndex);
31215         }
31216         return indexes;
31217     },
31218
31219     /**
31220      * Clear all selections
31221      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
31222      */
31223     clearSelections : function(suppressEvent){
31224         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
31225             this.cmp.elements = this.selections;
31226             this.cmp.removeClass(this.selectedClass);
31227             this.selections = [];
31228             if(!suppressEvent){
31229                 this.fireEvent("selectionchange", this, this.selections);
31230             }
31231         }
31232     },
31233
31234     /**
31235      * Returns true if the passed node is selected
31236      * @param {HTMLElement/Number} node The node or node index
31237      * @return {Boolean}
31238      */
31239     isSelected : function(node){
31240         var s = this.selections;
31241         if(s.length < 1){
31242             return false;
31243         }
31244         node = this.getNode(node);
31245         return s.indexOf(node) !== -1;
31246     },
31247
31248     /**
31249      * Selects nodes.
31250      * @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
31251      * @param {Boolean} keepExisting (optional) true to keep existing selections
31252      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
31253      */
31254     select : function(nodeInfo, keepExisting, suppressEvent){
31255         if(nodeInfo instanceof Array){
31256             if(!keepExisting){
31257                 this.clearSelections(true);
31258             }
31259             for(var i = 0, len = nodeInfo.length; i < len; i++){
31260                 this.select(nodeInfo[i], true, true);
31261             }
31262             return;
31263         } 
31264         var node = this.getNode(nodeInfo);
31265         if(!node || this.isSelected(node)){
31266             return; // already selected.
31267         }
31268         if(!keepExisting){
31269             this.clearSelections(true);
31270         }
31271         
31272         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
31273             Roo.fly(node).addClass(this.selectedClass);
31274             this.selections.push(node);
31275             if(!suppressEvent){
31276                 this.fireEvent("selectionchange", this, this.selections);
31277             }
31278         }
31279         
31280         
31281     },
31282       /**
31283      * Unselects nodes.
31284      * @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
31285      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
31286      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
31287      */
31288     unselect : function(nodeInfo, keepExisting, suppressEvent)
31289     {
31290         if(nodeInfo instanceof Array){
31291             Roo.each(this.selections, function(s) {
31292                 this.unselect(s, nodeInfo);
31293             }, this);
31294             return;
31295         }
31296         var node = this.getNode(nodeInfo);
31297         if(!node || !this.isSelected(node)){
31298             //Roo.log("not selected");
31299             return; // not selected.
31300         }
31301         // fireevent???
31302         var ns = [];
31303         Roo.each(this.selections, function(s) {
31304             if (s == node ) {
31305                 Roo.fly(node).removeClass(this.selectedClass);
31306
31307                 return;
31308             }
31309             ns.push(s);
31310         },this);
31311         
31312         this.selections= ns;
31313         this.fireEvent("selectionchange", this, this.selections);
31314     },
31315
31316     /**
31317      * Gets a template node.
31318      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
31319      * @return {HTMLElement} The node or null if it wasn't found
31320      */
31321     getNode : function(nodeInfo){
31322         if(typeof nodeInfo == "string"){
31323             return document.getElementById(nodeInfo);
31324         }else if(typeof nodeInfo == "number"){
31325             return this.nodes[nodeInfo];
31326         }
31327         return nodeInfo;
31328     },
31329
31330     /**
31331      * Gets a range template nodes.
31332      * @param {Number} startIndex
31333      * @param {Number} endIndex
31334      * @return {Array} An array of nodes
31335      */
31336     getNodes : function(start, end){
31337         var ns = this.nodes;
31338         start = start || 0;
31339         end = typeof end == "undefined" ? ns.length - 1 : end;
31340         var nodes = [];
31341         if(start <= end){
31342             for(var i = start; i <= end; i++){
31343                 nodes.push(ns[i]);
31344             }
31345         } else{
31346             for(var i = start; i >= end; i--){
31347                 nodes.push(ns[i]);
31348             }
31349         }
31350         return nodes;
31351     },
31352
31353     /**
31354      * Finds the index of the passed node
31355      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
31356      * @return {Number} The index of the node or -1
31357      */
31358     indexOf : function(node){
31359         node = this.getNode(node);
31360         if(typeof node.nodeIndex == "number"){
31361             return node.nodeIndex;
31362         }
31363         var ns = this.nodes;
31364         for(var i = 0, len = ns.length; i < len; i++){
31365             if(ns[i] == node){
31366                 return i;
31367             }
31368         }
31369         return -1;
31370     }
31371 });
31372 /*
31373  * Based on:
31374  * Ext JS Library 1.1.1
31375  * Copyright(c) 2006-2007, Ext JS, LLC.
31376  *
31377  * Originally Released Under LGPL - original licence link has changed is not relivant.
31378  *
31379  * Fork - LGPL
31380  * <script type="text/javascript">
31381  */
31382
31383 /**
31384  * @class Roo.JsonView
31385  * @extends Roo.View
31386  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
31387 <pre><code>
31388 var view = new Roo.JsonView({
31389     container: "my-element",
31390     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
31391     multiSelect: true, 
31392     jsonRoot: "data" 
31393 });
31394
31395 // listen for node click?
31396 view.on("click", function(vw, index, node, e){
31397     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
31398 });
31399
31400 // direct load of JSON data
31401 view.load("foobar.php");
31402
31403 // Example from my blog list
31404 var tpl = new Roo.Template(
31405     '&lt;div class="entry"&gt;' +
31406     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
31407     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
31408     "&lt;/div&gt;&lt;hr /&gt;"
31409 );
31410
31411 var moreView = new Roo.JsonView({
31412     container :  "entry-list", 
31413     template : tpl,
31414     jsonRoot: "posts"
31415 });
31416 moreView.on("beforerender", this.sortEntries, this);
31417 moreView.load({
31418     url: "/blog/get-posts.php",
31419     params: "allposts=true",
31420     text: "Loading Blog Entries..."
31421 });
31422 </code></pre>
31423
31424 * Note: old code is supported with arguments : (container, template, config)
31425
31426
31427  * @constructor
31428  * Create a new JsonView
31429  * 
31430  * @param {Object} config The config object
31431  * 
31432  */
31433 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
31434     
31435     
31436     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
31437
31438     var um = this.el.getUpdateManager();
31439     um.setRenderer(this);
31440     um.on("update", this.onLoad, this);
31441     um.on("failure", this.onLoadException, this);
31442
31443     /**
31444      * @event beforerender
31445      * Fires before rendering of the downloaded JSON data.
31446      * @param {Roo.JsonView} this
31447      * @param {Object} data The JSON data loaded
31448      */
31449     /**
31450      * @event load
31451      * Fires when data is loaded.
31452      * @param {Roo.JsonView} this
31453      * @param {Object} data The JSON data loaded
31454      * @param {Object} response The raw Connect response object
31455      */
31456     /**
31457      * @event loadexception
31458      * Fires when loading fails.
31459      * @param {Roo.JsonView} this
31460      * @param {Object} response The raw Connect response object
31461      */
31462     this.addEvents({
31463         'beforerender' : true,
31464         'load' : true,
31465         'loadexception' : true
31466     });
31467 };
31468 Roo.extend(Roo.JsonView, Roo.View, {
31469     /**
31470      * @type {String} The root property in the loaded JSON object that contains the data
31471      */
31472     jsonRoot : "",
31473
31474     /**
31475      * Refreshes the view.
31476      */
31477     refresh : function(){
31478         this.clearSelections();
31479         this.el.update("");
31480         var html = [];
31481         var o = this.jsonData;
31482         if(o && o.length > 0){
31483             for(var i = 0, len = o.length; i < len; i++){
31484                 var data = this.prepareData(o[i], i, o);
31485                 html[html.length] = this.tpl.apply(data);
31486             }
31487         }else{
31488             html.push(this.emptyText);
31489         }
31490         this.el.update(html.join(""));
31491         this.nodes = this.el.dom.childNodes;
31492         this.updateIndexes(0);
31493     },
31494
31495     /**
31496      * 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.
31497      * @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:
31498      <pre><code>
31499      view.load({
31500          url: "your-url.php",
31501          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31502          callback: yourFunction,
31503          scope: yourObject, //(optional scope)
31504          discardUrl: false,
31505          nocache: false,
31506          text: "Loading...",
31507          timeout: 30,
31508          scripts: false
31509      });
31510      </code></pre>
31511      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31512      * 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.
31513      * @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}
31514      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
31515      * @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.
31516      */
31517     load : function(){
31518         var um = this.el.getUpdateManager();
31519         um.update.apply(um, arguments);
31520     },
31521
31522     // note - render is a standard framework call...
31523     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
31524     render : function(el, response){
31525         
31526         this.clearSelections();
31527         this.el.update("");
31528         var o;
31529         try{
31530             if (response != '') {
31531                 o = Roo.util.JSON.decode(response.responseText);
31532                 if(this.jsonRoot){
31533                     
31534                     o = o[this.jsonRoot];
31535                 }
31536             }
31537         } catch(e){
31538         }
31539         /**
31540          * The current JSON data or null
31541          */
31542         this.jsonData = o;
31543         this.beforeRender();
31544         this.refresh();
31545     },
31546
31547 /**
31548  * Get the number of records in the current JSON dataset
31549  * @return {Number}
31550  */
31551     getCount : function(){
31552         return this.jsonData ? this.jsonData.length : 0;
31553     },
31554
31555 /**
31556  * Returns the JSON object for the specified node(s)
31557  * @param {HTMLElement/Array} node The node or an array of nodes
31558  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
31559  * you get the JSON object for the node
31560  */
31561     getNodeData : function(node){
31562         if(node instanceof Array){
31563             var data = [];
31564             for(var i = 0, len = node.length; i < len; i++){
31565                 data.push(this.getNodeData(node[i]));
31566             }
31567             return data;
31568         }
31569         return this.jsonData[this.indexOf(node)] || null;
31570     },
31571
31572     beforeRender : function(){
31573         this.snapshot = this.jsonData;
31574         if(this.sortInfo){
31575             this.sort.apply(this, this.sortInfo);
31576         }
31577         this.fireEvent("beforerender", this, this.jsonData);
31578     },
31579
31580     onLoad : function(el, o){
31581         this.fireEvent("load", this, this.jsonData, o);
31582     },
31583
31584     onLoadException : function(el, o){
31585         this.fireEvent("loadexception", this, o);
31586     },
31587
31588 /**
31589  * Filter the data by a specific property.
31590  * @param {String} property A property on your JSON objects
31591  * @param {String/RegExp} value Either string that the property values
31592  * should start with, or a RegExp to test against the property
31593  */
31594     filter : function(property, value){
31595         if(this.jsonData){
31596             var data = [];
31597             var ss = this.snapshot;
31598             if(typeof value == "string"){
31599                 var vlen = value.length;
31600                 if(vlen == 0){
31601                     this.clearFilter();
31602                     return;
31603                 }
31604                 value = value.toLowerCase();
31605                 for(var i = 0, len = ss.length; i < len; i++){
31606                     var o = ss[i];
31607                     if(o[property].substr(0, vlen).toLowerCase() == value){
31608                         data.push(o);
31609                     }
31610                 }
31611             } else if(value.exec){ // regex?
31612                 for(var i = 0, len = ss.length; i < len; i++){
31613                     var o = ss[i];
31614                     if(value.test(o[property])){
31615                         data.push(o);
31616                     }
31617                 }
31618             } else{
31619                 return;
31620             }
31621             this.jsonData = data;
31622             this.refresh();
31623         }
31624     },
31625
31626 /**
31627  * Filter by a function. The passed function will be called with each
31628  * object in the current dataset. If the function returns true the value is kept,
31629  * otherwise it is filtered.
31630  * @param {Function} fn
31631  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
31632  */
31633     filterBy : function(fn, scope){
31634         if(this.jsonData){
31635             var data = [];
31636             var ss = this.snapshot;
31637             for(var i = 0, len = ss.length; i < len; i++){
31638                 var o = ss[i];
31639                 if(fn.call(scope || this, o)){
31640                     data.push(o);
31641                 }
31642             }
31643             this.jsonData = data;
31644             this.refresh();
31645         }
31646     },
31647
31648 /**
31649  * Clears the current filter.
31650  */
31651     clearFilter : function(){
31652         if(this.snapshot && this.jsonData != this.snapshot){
31653             this.jsonData = this.snapshot;
31654             this.refresh();
31655         }
31656     },
31657
31658
31659 /**
31660  * Sorts the data for this view and refreshes it.
31661  * @param {String} property A property on your JSON objects to sort on
31662  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
31663  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
31664  */
31665     sort : function(property, dir, sortType){
31666         this.sortInfo = Array.prototype.slice.call(arguments, 0);
31667         if(this.jsonData){
31668             var p = property;
31669             var dsc = dir && dir.toLowerCase() == "desc";
31670             var f = function(o1, o2){
31671                 var v1 = sortType ? sortType(o1[p]) : o1[p];
31672                 var v2 = sortType ? sortType(o2[p]) : o2[p];
31673                 ;
31674                 if(v1 < v2){
31675                     return dsc ? +1 : -1;
31676                 } else if(v1 > v2){
31677                     return dsc ? -1 : +1;
31678                 } else{
31679                     return 0;
31680                 }
31681             };
31682             this.jsonData.sort(f);
31683             this.refresh();
31684             if(this.jsonData != this.snapshot){
31685                 this.snapshot.sort(f);
31686             }
31687         }
31688     }
31689 });/*
31690  * Based on:
31691  * Ext JS Library 1.1.1
31692  * Copyright(c) 2006-2007, Ext JS, LLC.
31693  *
31694  * Originally Released Under LGPL - original licence link has changed is not relivant.
31695  *
31696  * Fork - LGPL
31697  * <script type="text/javascript">
31698  */
31699  
31700
31701 /**
31702  * @class Roo.ColorPalette
31703  * @extends Roo.Component
31704  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
31705  * Here's an example of typical usage:
31706  * <pre><code>
31707 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
31708 cp.render('my-div');
31709
31710 cp.on('select', function(palette, selColor){
31711     // do something with selColor
31712 });
31713 </code></pre>
31714  * @constructor
31715  * Create a new ColorPalette
31716  * @param {Object} config The config object
31717  */
31718 Roo.ColorPalette = function(config){
31719     Roo.ColorPalette.superclass.constructor.call(this, config);
31720     this.addEvents({
31721         /**
31722              * @event select
31723              * Fires when a color is selected
31724              * @param {ColorPalette} this
31725              * @param {String} color The 6-digit color hex code (without the # symbol)
31726              */
31727         select: true
31728     });
31729
31730     if(this.handler){
31731         this.on("select", this.handler, this.scope, true);
31732     }
31733 };
31734 Roo.extend(Roo.ColorPalette, Roo.Component, {
31735     /**
31736      * @cfg {String} itemCls
31737      * The CSS class to apply to the containing element (defaults to "x-color-palette")
31738      */
31739     itemCls : "x-color-palette",
31740     /**
31741      * @cfg {String} value
31742      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
31743      * the hex codes are case-sensitive.
31744      */
31745     value : null,
31746     clickEvent:'click',
31747     // private
31748     ctype: "Roo.ColorPalette",
31749
31750     /**
31751      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
31752      */
31753     allowReselect : false,
31754
31755     /**
31756      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
31757      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
31758      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
31759      * of colors with the width setting until the box is symmetrical.</p>
31760      * <p>You can override individual colors if needed:</p>
31761      * <pre><code>
31762 var cp = new Roo.ColorPalette();
31763 cp.colors[0] = "FF0000";  // change the first box to red
31764 </code></pre>
31765
31766 Or you can provide a custom array of your own for complete control:
31767 <pre><code>
31768 var cp = new Roo.ColorPalette();
31769 cp.colors = ["000000", "993300", "333300"];
31770 </code></pre>
31771      * @type Array
31772      */
31773     colors : [
31774         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
31775         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
31776         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
31777         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
31778         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
31779     ],
31780
31781     // private
31782     onRender : function(container, position){
31783         var t = new Roo.MasterTemplate(
31784             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
31785         );
31786         var c = this.colors;
31787         for(var i = 0, len = c.length; i < len; i++){
31788             t.add([c[i]]);
31789         }
31790         var el = document.createElement("div");
31791         el.className = this.itemCls;
31792         t.overwrite(el);
31793         container.dom.insertBefore(el, position);
31794         this.el = Roo.get(el);
31795         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
31796         if(this.clickEvent != 'click'){
31797             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
31798         }
31799     },
31800
31801     // private
31802     afterRender : function(){
31803         Roo.ColorPalette.superclass.afterRender.call(this);
31804         if(this.value){
31805             var s = this.value;
31806             this.value = null;
31807             this.select(s);
31808         }
31809     },
31810
31811     // private
31812     handleClick : function(e, t){
31813         e.preventDefault();
31814         if(!this.disabled){
31815             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
31816             this.select(c.toUpperCase());
31817         }
31818     },
31819
31820     /**
31821      * Selects the specified color in the palette (fires the select event)
31822      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
31823      */
31824     select : function(color){
31825         color = color.replace("#", "");
31826         if(color != this.value || this.allowReselect){
31827             var el = this.el;
31828             if(this.value){
31829                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
31830             }
31831             el.child("a.color-"+color).addClass("x-color-palette-sel");
31832             this.value = color;
31833             this.fireEvent("select", this, color);
31834         }
31835     }
31836 });/*
31837  * Based on:
31838  * Ext JS Library 1.1.1
31839  * Copyright(c) 2006-2007, Ext JS, LLC.
31840  *
31841  * Originally Released Under LGPL - original licence link has changed is not relivant.
31842  *
31843  * Fork - LGPL
31844  * <script type="text/javascript">
31845  */
31846  
31847 /**
31848  * @class Roo.DatePicker
31849  * @extends Roo.Component
31850  * Simple date picker class.
31851  * @constructor
31852  * Create a new DatePicker
31853  * @param {Object} config The config object
31854  */
31855 Roo.DatePicker = function(config){
31856     Roo.DatePicker.superclass.constructor.call(this, config);
31857
31858     this.value = config && config.value ?
31859                  config.value.clearTime() : new Date().clearTime();
31860
31861     this.addEvents({
31862         /**
31863              * @event select
31864              * Fires when a date is selected
31865              * @param {DatePicker} this
31866              * @param {Date} date The selected date
31867              */
31868         'select': true,
31869         /**
31870              * @event monthchange
31871              * Fires when the displayed month changes 
31872              * @param {DatePicker} this
31873              * @param {Date} date The selected month
31874              */
31875         'monthchange': true
31876     });
31877
31878     if(this.handler){
31879         this.on("select", this.handler,  this.scope || this);
31880     }
31881     // build the disabledDatesRE
31882     if(!this.disabledDatesRE && this.disabledDates){
31883         var dd = this.disabledDates;
31884         var re = "(?:";
31885         for(var i = 0; i < dd.length; i++){
31886             re += dd[i];
31887             if(i != dd.length-1) {
31888                 re += "|";
31889             }
31890         }
31891         this.disabledDatesRE = new RegExp(re + ")");
31892     }
31893 };
31894
31895 Roo.extend(Roo.DatePicker, Roo.Component, {
31896     /**
31897      * @cfg {String} todayText
31898      * The text to display on the button that selects the current date (defaults to "Today")
31899      */
31900     todayText : "Today",
31901     /**
31902      * @cfg {String} okText
31903      * The text to display on the ok button
31904      */
31905     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
31906     /**
31907      * @cfg {String} cancelText
31908      * The text to display on the cancel button
31909      */
31910     cancelText : "Cancel",
31911     /**
31912      * @cfg {String} todayTip
31913      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
31914      */
31915     todayTip : "{0} (Spacebar)",
31916     /**
31917      * @cfg {Date} minDate
31918      * Minimum allowable date (JavaScript date object, defaults to null)
31919      */
31920     minDate : null,
31921     /**
31922      * @cfg {Date} maxDate
31923      * Maximum allowable date (JavaScript date object, defaults to null)
31924      */
31925     maxDate : null,
31926     /**
31927      * @cfg {String} minText
31928      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
31929      */
31930     minText : "This date is before the minimum date",
31931     /**
31932      * @cfg {String} maxText
31933      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
31934      */
31935     maxText : "This date is after the maximum date",
31936     /**
31937      * @cfg {String} format
31938      * The default date format string which can be overriden for localization support.  The format must be
31939      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
31940      */
31941     format : "m/d/y",
31942     /**
31943      * @cfg {Array} disabledDays
31944      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
31945      */
31946     disabledDays : null,
31947     /**
31948      * @cfg {String} disabledDaysText
31949      * The tooltip to display when the date falls on a disabled day (defaults to "")
31950      */
31951     disabledDaysText : "",
31952     /**
31953      * @cfg {RegExp} disabledDatesRE
31954      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
31955      */
31956     disabledDatesRE : null,
31957     /**
31958      * @cfg {String} disabledDatesText
31959      * The tooltip text to display when the date falls on a disabled date (defaults to "")
31960      */
31961     disabledDatesText : "",
31962     /**
31963      * @cfg {Boolean} constrainToViewport
31964      * True to constrain the date picker to the viewport (defaults to true)
31965      */
31966     constrainToViewport : true,
31967     /**
31968      * @cfg {Array} monthNames
31969      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
31970      */
31971     monthNames : Date.monthNames,
31972     /**
31973      * @cfg {Array} dayNames
31974      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
31975      */
31976     dayNames : Date.dayNames,
31977     /**
31978      * @cfg {String} nextText
31979      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
31980      */
31981     nextText: 'Next Month (Control+Right)',
31982     /**
31983      * @cfg {String} prevText
31984      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
31985      */
31986     prevText: 'Previous Month (Control+Left)',
31987     /**
31988      * @cfg {String} monthYearText
31989      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
31990      */
31991     monthYearText: 'Choose a month (Control+Up/Down to move years)',
31992     /**
31993      * @cfg {Number} startDay
31994      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
31995      */
31996     startDay : 0,
31997     /**
31998      * @cfg {Bool} showClear
31999      * Show a clear button (usefull for date form elements that can be blank.)
32000      */
32001     
32002     showClear: false,
32003     
32004     /**
32005      * Sets the value of the date field
32006      * @param {Date} value The date to set
32007      */
32008     setValue : function(value){
32009         var old = this.value;
32010         
32011         if (typeof(value) == 'string') {
32012          
32013             value = Date.parseDate(value, this.format);
32014         }
32015         if (!value) {
32016             value = new Date();
32017         }
32018         
32019         this.value = value.clearTime(true);
32020         if(this.el){
32021             this.update(this.value);
32022         }
32023     },
32024
32025     /**
32026      * Gets the current selected value of the date field
32027      * @return {Date} The selected date
32028      */
32029     getValue : function(){
32030         return this.value;
32031     },
32032
32033     // private
32034     focus : function(){
32035         if(this.el){
32036             this.update(this.activeDate);
32037         }
32038     },
32039
32040     // privateval
32041     onRender : function(container, position){
32042         
32043         var m = [
32044              '<table cellspacing="0">',
32045                 '<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>',
32046                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
32047         var dn = this.dayNames;
32048         for(var i = 0; i < 7; i++){
32049             var d = this.startDay+i;
32050             if(d > 6){
32051                 d = d-7;
32052             }
32053             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
32054         }
32055         m[m.length] = "</tr></thead><tbody><tr>";
32056         for(var i = 0; i < 42; i++) {
32057             if(i % 7 == 0 && i != 0){
32058                 m[m.length] = "</tr><tr>";
32059             }
32060             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
32061         }
32062         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
32063             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
32064
32065         var el = document.createElement("div");
32066         el.className = "x-date-picker";
32067         el.innerHTML = m.join("");
32068
32069         container.dom.insertBefore(el, position);
32070
32071         this.el = Roo.get(el);
32072         this.eventEl = Roo.get(el.firstChild);
32073
32074         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
32075             handler: this.showPrevMonth,
32076             scope: this,
32077             preventDefault:true,
32078             stopDefault:true
32079         });
32080
32081         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
32082             handler: this.showNextMonth,
32083             scope: this,
32084             preventDefault:true,
32085             stopDefault:true
32086         });
32087
32088         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
32089
32090         this.monthPicker = this.el.down('div.x-date-mp');
32091         this.monthPicker.enableDisplayMode('block');
32092         
32093         var kn = new Roo.KeyNav(this.eventEl, {
32094             "left" : function(e){
32095                 e.ctrlKey ?
32096                     this.showPrevMonth() :
32097                     this.update(this.activeDate.add("d", -1));
32098             },
32099
32100             "right" : function(e){
32101                 e.ctrlKey ?
32102                     this.showNextMonth() :
32103                     this.update(this.activeDate.add("d", 1));
32104             },
32105
32106             "up" : function(e){
32107                 e.ctrlKey ?
32108                     this.showNextYear() :
32109                     this.update(this.activeDate.add("d", -7));
32110             },
32111
32112             "down" : function(e){
32113                 e.ctrlKey ?
32114                     this.showPrevYear() :
32115                     this.update(this.activeDate.add("d", 7));
32116             },
32117
32118             "pageUp" : function(e){
32119                 this.showNextMonth();
32120             },
32121
32122             "pageDown" : function(e){
32123                 this.showPrevMonth();
32124             },
32125
32126             "enter" : function(e){
32127                 e.stopPropagation();
32128                 return true;
32129             },
32130
32131             scope : this
32132         });
32133
32134         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
32135
32136         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
32137
32138         this.el.unselectable();
32139         
32140         this.cells = this.el.select("table.x-date-inner tbody td");
32141         this.textNodes = this.el.query("table.x-date-inner tbody span");
32142
32143         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
32144             text: "&#160;",
32145             tooltip: this.monthYearText
32146         });
32147
32148         this.mbtn.on('click', this.showMonthPicker, this);
32149         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
32150
32151
32152         var today = (new Date()).dateFormat(this.format);
32153         
32154         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
32155         if (this.showClear) {
32156             baseTb.add( new Roo.Toolbar.Fill());
32157         }
32158         baseTb.add({
32159             text: String.format(this.todayText, today),
32160             tooltip: String.format(this.todayTip, today),
32161             handler: this.selectToday,
32162             scope: this
32163         });
32164         
32165         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
32166             
32167         //});
32168         if (this.showClear) {
32169             
32170             baseTb.add( new Roo.Toolbar.Fill());
32171             baseTb.add({
32172                 text: '&#160;',
32173                 cls: 'x-btn-icon x-btn-clear',
32174                 handler: function() {
32175                     //this.value = '';
32176                     this.fireEvent("select", this, '');
32177                 },
32178                 scope: this
32179             });
32180         }
32181         
32182         
32183         if(Roo.isIE){
32184             this.el.repaint();
32185         }
32186         this.update(this.value);
32187     },
32188
32189     createMonthPicker : function(){
32190         if(!this.monthPicker.dom.firstChild){
32191             var buf = ['<table border="0" cellspacing="0">'];
32192             for(var i = 0; i < 6; i++){
32193                 buf.push(
32194                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
32195                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
32196                     i == 0 ?
32197                     '<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>' :
32198                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
32199                 );
32200             }
32201             buf.push(
32202                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
32203                     this.okText,
32204                     '</button><button type="button" class="x-date-mp-cancel">',
32205                     this.cancelText,
32206                     '</button></td></tr>',
32207                 '</table>'
32208             );
32209             this.monthPicker.update(buf.join(''));
32210             this.monthPicker.on('click', this.onMonthClick, this);
32211             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
32212
32213             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
32214             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
32215
32216             this.mpMonths.each(function(m, a, i){
32217                 i += 1;
32218                 if((i%2) == 0){
32219                     m.dom.xmonth = 5 + Math.round(i * .5);
32220                 }else{
32221                     m.dom.xmonth = Math.round((i-1) * .5);
32222                 }
32223             });
32224         }
32225     },
32226
32227     showMonthPicker : function(){
32228         this.createMonthPicker();
32229         var size = this.el.getSize();
32230         this.monthPicker.setSize(size);
32231         this.monthPicker.child('table').setSize(size);
32232
32233         this.mpSelMonth = (this.activeDate || this.value).getMonth();
32234         this.updateMPMonth(this.mpSelMonth);
32235         this.mpSelYear = (this.activeDate || this.value).getFullYear();
32236         this.updateMPYear(this.mpSelYear);
32237
32238         this.monthPicker.slideIn('t', {duration:.2});
32239     },
32240
32241     updateMPYear : function(y){
32242         this.mpyear = y;
32243         var ys = this.mpYears.elements;
32244         for(var i = 1; i <= 10; i++){
32245             var td = ys[i-1], y2;
32246             if((i%2) == 0){
32247                 y2 = y + Math.round(i * .5);
32248                 td.firstChild.innerHTML = y2;
32249                 td.xyear = y2;
32250             }else{
32251                 y2 = y - (5-Math.round(i * .5));
32252                 td.firstChild.innerHTML = y2;
32253                 td.xyear = y2;
32254             }
32255             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
32256         }
32257     },
32258
32259     updateMPMonth : function(sm){
32260         this.mpMonths.each(function(m, a, i){
32261             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
32262         });
32263     },
32264
32265     selectMPMonth: function(m){
32266         
32267     },
32268
32269     onMonthClick : function(e, t){
32270         e.stopEvent();
32271         var el = new Roo.Element(t), pn;
32272         if(el.is('button.x-date-mp-cancel')){
32273             this.hideMonthPicker();
32274         }
32275         else if(el.is('button.x-date-mp-ok')){
32276             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
32277             this.hideMonthPicker();
32278         }
32279         else if(pn = el.up('td.x-date-mp-month', 2)){
32280             this.mpMonths.removeClass('x-date-mp-sel');
32281             pn.addClass('x-date-mp-sel');
32282             this.mpSelMonth = pn.dom.xmonth;
32283         }
32284         else if(pn = el.up('td.x-date-mp-year', 2)){
32285             this.mpYears.removeClass('x-date-mp-sel');
32286             pn.addClass('x-date-mp-sel');
32287             this.mpSelYear = pn.dom.xyear;
32288         }
32289         else if(el.is('a.x-date-mp-prev')){
32290             this.updateMPYear(this.mpyear-10);
32291         }
32292         else if(el.is('a.x-date-mp-next')){
32293             this.updateMPYear(this.mpyear+10);
32294         }
32295     },
32296
32297     onMonthDblClick : function(e, t){
32298         e.stopEvent();
32299         var el = new Roo.Element(t), pn;
32300         if(pn = el.up('td.x-date-mp-month', 2)){
32301             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
32302             this.hideMonthPicker();
32303         }
32304         else if(pn = el.up('td.x-date-mp-year', 2)){
32305             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
32306             this.hideMonthPicker();
32307         }
32308     },
32309
32310     hideMonthPicker : function(disableAnim){
32311         if(this.monthPicker){
32312             if(disableAnim === true){
32313                 this.monthPicker.hide();
32314             }else{
32315                 this.monthPicker.slideOut('t', {duration:.2});
32316             }
32317         }
32318     },
32319
32320     // private
32321     showPrevMonth : function(e){
32322         this.update(this.activeDate.add("mo", -1));
32323     },
32324
32325     // private
32326     showNextMonth : function(e){
32327         this.update(this.activeDate.add("mo", 1));
32328     },
32329
32330     // private
32331     showPrevYear : function(){
32332         this.update(this.activeDate.add("y", -1));
32333     },
32334
32335     // private
32336     showNextYear : function(){
32337         this.update(this.activeDate.add("y", 1));
32338     },
32339
32340     // private
32341     handleMouseWheel : function(e){
32342         var delta = e.getWheelDelta();
32343         if(delta > 0){
32344             this.showPrevMonth();
32345             e.stopEvent();
32346         } else if(delta < 0){
32347             this.showNextMonth();
32348             e.stopEvent();
32349         }
32350     },
32351
32352     // private
32353     handleDateClick : function(e, t){
32354         e.stopEvent();
32355         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
32356             this.setValue(new Date(t.dateValue));
32357             this.fireEvent("select", this, this.value);
32358         }
32359     },
32360
32361     // private
32362     selectToday : function(){
32363         this.setValue(new Date().clearTime());
32364         this.fireEvent("select", this, this.value);
32365     },
32366
32367     // private
32368     update : function(date)
32369     {
32370         var vd = this.activeDate;
32371         this.activeDate = date;
32372         if(vd && this.el){
32373             var t = date.getTime();
32374             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
32375                 this.cells.removeClass("x-date-selected");
32376                 this.cells.each(function(c){
32377                    if(c.dom.firstChild.dateValue == t){
32378                        c.addClass("x-date-selected");
32379                        setTimeout(function(){
32380                             try{c.dom.firstChild.focus();}catch(e){}
32381                        }, 50);
32382                        return false;
32383                    }
32384                 });
32385                 return;
32386             }
32387         }
32388         
32389         var days = date.getDaysInMonth();
32390         var firstOfMonth = date.getFirstDateOfMonth();
32391         var startingPos = firstOfMonth.getDay()-this.startDay;
32392
32393         if(startingPos <= this.startDay){
32394             startingPos += 7;
32395         }
32396
32397         var pm = date.add("mo", -1);
32398         var prevStart = pm.getDaysInMonth()-startingPos;
32399
32400         var cells = this.cells.elements;
32401         var textEls = this.textNodes;
32402         days += startingPos;
32403
32404         // convert everything to numbers so it's fast
32405         var day = 86400000;
32406         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
32407         var today = new Date().clearTime().getTime();
32408         var sel = date.clearTime().getTime();
32409         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
32410         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
32411         var ddMatch = this.disabledDatesRE;
32412         var ddText = this.disabledDatesText;
32413         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
32414         var ddaysText = this.disabledDaysText;
32415         var format = this.format;
32416
32417         var setCellClass = function(cal, cell){
32418             cell.title = "";
32419             var t = d.getTime();
32420             cell.firstChild.dateValue = t;
32421             if(t == today){
32422                 cell.className += " x-date-today";
32423                 cell.title = cal.todayText;
32424             }
32425             if(t == sel){
32426                 cell.className += " x-date-selected";
32427                 setTimeout(function(){
32428                     try{cell.firstChild.focus();}catch(e){}
32429                 }, 50);
32430             }
32431             // disabling
32432             if(t < min) {
32433                 cell.className = " x-date-disabled";
32434                 cell.title = cal.minText;
32435                 return;
32436             }
32437             if(t > max) {
32438                 cell.className = " x-date-disabled";
32439                 cell.title = cal.maxText;
32440                 return;
32441             }
32442             if(ddays){
32443                 if(ddays.indexOf(d.getDay()) != -1){
32444                     cell.title = ddaysText;
32445                     cell.className = " x-date-disabled";
32446                 }
32447             }
32448             if(ddMatch && format){
32449                 var fvalue = d.dateFormat(format);
32450                 if(ddMatch.test(fvalue)){
32451                     cell.title = ddText.replace("%0", fvalue);
32452                     cell.className = " x-date-disabled";
32453                 }
32454             }
32455         };
32456
32457         var i = 0;
32458         for(; i < startingPos; i++) {
32459             textEls[i].innerHTML = (++prevStart);
32460             d.setDate(d.getDate()+1);
32461             cells[i].className = "x-date-prevday";
32462             setCellClass(this, cells[i]);
32463         }
32464         for(; i < days; i++){
32465             intDay = i - startingPos + 1;
32466             textEls[i].innerHTML = (intDay);
32467             d.setDate(d.getDate()+1);
32468             cells[i].className = "x-date-active";
32469             setCellClass(this, cells[i]);
32470         }
32471         var extraDays = 0;
32472         for(; i < 42; i++) {
32473              textEls[i].innerHTML = (++extraDays);
32474              d.setDate(d.getDate()+1);
32475              cells[i].className = "x-date-nextday";
32476              setCellClass(this, cells[i]);
32477         }
32478
32479         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
32480         this.fireEvent('monthchange', this, date);
32481         
32482         if(!this.internalRender){
32483             var main = this.el.dom.firstChild;
32484             var w = main.offsetWidth;
32485             this.el.setWidth(w + this.el.getBorderWidth("lr"));
32486             Roo.fly(main).setWidth(w);
32487             this.internalRender = true;
32488             // opera does not respect the auto grow header center column
32489             // then, after it gets a width opera refuses to recalculate
32490             // without a second pass
32491             if(Roo.isOpera && !this.secondPass){
32492                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
32493                 this.secondPass = true;
32494                 this.update.defer(10, this, [date]);
32495             }
32496         }
32497         
32498         
32499     }
32500 });        /*
32501  * Based on:
32502  * Ext JS Library 1.1.1
32503  * Copyright(c) 2006-2007, Ext JS, LLC.
32504  *
32505  * Originally Released Under LGPL - original licence link has changed is not relivant.
32506  *
32507  * Fork - LGPL
32508  * <script type="text/javascript">
32509  */
32510 /**
32511  * @class Roo.TabPanel
32512  * @extends Roo.util.Observable
32513  * A lightweight tab container.
32514  * <br><br>
32515  * Usage:
32516  * <pre><code>
32517 // basic tabs 1, built from existing content
32518 var tabs = new Roo.TabPanel("tabs1");
32519 tabs.addTab("script", "View Script");
32520 tabs.addTab("markup", "View Markup");
32521 tabs.activate("script");
32522
32523 // more advanced tabs, built from javascript
32524 var jtabs = new Roo.TabPanel("jtabs");
32525 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
32526
32527 // set up the UpdateManager
32528 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
32529 var updater = tab2.getUpdateManager();
32530 updater.setDefaultUrl("ajax1.htm");
32531 tab2.on('activate', updater.refresh, updater, true);
32532
32533 // Use setUrl for Ajax loading
32534 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
32535 tab3.setUrl("ajax2.htm", null, true);
32536
32537 // Disabled tab
32538 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
32539 tab4.disable();
32540
32541 jtabs.activate("jtabs-1");
32542  * </code></pre>
32543  * @constructor
32544  * Create a new TabPanel.
32545  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
32546  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
32547  */
32548 Roo.TabPanel = function(container, config){
32549     /**
32550     * The container element for this TabPanel.
32551     * @type Roo.Element
32552     */
32553     this.el = Roo.get(container, true);
32554     if(config){
32555         if(typeof config == "boolean"){
32556             this.tabPosition = config ? "bottom" : "top";
32557         }else{
32558             Roo.apply(this, config);
32559         }
32560     }
32561     if(this.tabPosition == "bottom"){
32562         this.bodyEl = Roo.get(this.createBody(this.el.dom));
32563         this.el.addClass("x-tabs-bottom");
32564     }
32565     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
32566     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
32567     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
32568     if(Roo.isIE){
32569         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
32570     }
32571     if(this.tabPosition != "bottom"){
32572         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
32573          * @type Roo.Element
32574          */
32575         this.bodyEl = Roo.get(this.createBody(this.el.dom));
32576         this.el.addClass("x-tabs-top");
32577     }
32578     this.items = [];
32579
32580     this.bodyEl.setStyle("position", "relative");
32581
32582     this.active = null;
32583     this.activateDelegate = this.activate.createDelegate(this);
32584
32585     this.addEvents({
32586         /**
32587          * @event tabchange
32588          * Fires when the active tab changes
32589          * @param {Roo.TabPanel} this
32590          * @param {Roo.TabPanelItem} activePanel The new active tab
32591          */
32592         "tabchange": true,
32593         /**
32594          * @event beforetabchange
32595          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
32596          * @param {Roo.TabPanel} this
32597          * @param {Object} e Set cancel to true on this object to cancel the tab change
32598          * @param {Roo.TabPanelItem} tab The tab being changed to
32599          */
32600         "beforetabchange" : true
32601     });
32602
32603     Roo.EventManager.onWindowResize(this.onResize, this);
32604     this.cpad = this.el.getPadding("lr");
32605     this.hiddenCount = 0;
32606
32607
32608     // toolbar on the tabbar support...
32609     if (this.toolbar) {
32610         var tcfg = this.toolbar;
32611         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
32612         this.toolbar = new Roo.Toolbar(tcfg);
32613         if (Roo.isSafari) {
32614             var tbl = tcfg.container.child('table', true);
32615             tbl.setAttribute('width', '100%');
32616         }
32617         
32618     }
32619    
32620
32621
32622     Roo.TabPanel.superclass.constructor.call(this);
32623 };
32624
32625 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
32626     /*
32627      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
32628      */
32629     tabPosition : "top",
32630     /*
32631      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
32632      */
32633     currentTabWidth : 0,
32634     /*
32635      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
32636      */
32637     minTabWidth : 40,
32638     /*
32639      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
32640      */
32641     maxTabWidth : 250,
32642     /*
32643      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
32644      */
32645     preferredTabWidth : 175,
32646     /*
32647      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
32648      */
32649     resizeTabs : false,
32650     /*
32651      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
32652      */
32653     monitorResize : true,
32654     /*
32655      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
32656      */
32657     toolbar : false,
32658
32659     /**
32660      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
32661      * @param {String} id The id of the div to use <b>or create</b>
32662      * @param {String} text The text for the tab
32663      * @param {String} content (optional) Content to put in the TabPanelItem body
32664      * @param {Boolean} closable (optional) True to create a close icon on the tab
32665      * @return {Roo.TabPanelItem} The created TabPanelItem
32666      */
32667     addTab : function(id, text, content, closable){
32668         var item = new Roo.TabPanelItem(this, id, text, closable);
32669         this.addTabItem(item);
32670         if(content){
32671             item.setContent(content);
32672         }
32673         return item;
32674     },
32675
32676     /**
32677      * Returns the {@link Roo.TabPanelItem} with the specified id/index
32678      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
32679      * @return {Roo.TabPanelItem}
32680      */
32681     getTab : function(id){
32682         return this.items[id];
32683     },
32684
32685     /**
32686      * Hides the {@link Roo.TabPanelItem} with the specified id/index
32687      * @param {String/Number} id The id or index of the TabPanelItem to hide.
32688      */
32689     hideTab : function(id){
32690         var t = this.items[id];
32691         if(!t.isHidden()){
32692            t.setHidden(true);
32693            this.hiddenCount++;
32694            this.autoSizeTabs();
32695         }
32696     },
32697
32698     /**
32699      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
32700      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
32701      */
32702     unhideTab : function(id){
32703         var t = this.items[id];
32704         if(t.isHidden()){
32705            t.setHidden(false);
32706            this.hiddenCount--;
32707            this.autoSizeTabs();
32708         }
32709     },
32710
32711     /**
32712      * Adds an existing {@link Roo.TabPanelItem}.
32713      * @param {Roo.TabPanelItem} item The TabPanelItem to add
32714      */
32715     addTabItem : function(item){
32716         this.items[item.id] = item;
32717         this.items.push(item);
32718         if(this.resizeTabs){
32719            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
32720            this.autoSizeTabs();
32721         }else{
32722             item.autoSize();
32723         }
32724     },
32725
32726     /**
32727      * Removes a {@link Roo.TabPanelItem}.
32728      * @param {String/Number} id The id or index of the TabPanelItem to remove.
32729      */
32730     removeTab : function(id){
32731         var items = this.items;
32732         var tab = items[id];
32733         if(!tab) { return; }
32734         var index = items.indexOf(tab);
32735         if(this.active == tab && items.length > 1){
32736             var newTab = this.getNextAvailable(index);
32737             if(newTab) {
32738                 newTab.activate();
32739             }
32740         }
32741         this.stripEl.dom.removeChild(tab.pnode.dom);
32742         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
32743             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
32744         }
32745         items.splice(index, 1);
32746         delete this.items[tab.id];
32747         tab.fireEvent("close", tab);
32748         tab.purgeListeners();
32749         this.autoSizeTabs();
32750     },
32751
32752     getNextAvailable : function(start){
32753         var items = this.items;
32754         var index = start;
32755         // look for a next tab that will slide over to
32756         // replace the one being removed
32757         while(index < items.length){
32758             var item = items[++index];
32759             if(item && !item.isHidden()){
32760                 return item;
32761             }
32762         }
32763         // if one isn't found select the previous tab (on the left)
32764         index = start;
32765         while(index >= 0){
32766             var item = items[--index];
32767             if(item && !item.isHidden()){
32768                 return item;
32769             }
32770         }
32771         return null;
32772     },
32773
32774     /**
32775      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
32776      * @param {String/Number} id The id or index of the TabPanelItem to disable.
32777      */
32778     disableTab : function(id){
32779         var tab = this.items[id];
32780         if(tab && this.active != tab){
32781             tab.disable();
32782         }
32783     },
32784
32785     /**
32786      * Enables a {@link Roo.TabPanelItem} that is disabled.
32787      * @param {String/Number} id The id or index of the TabPanelItem to enable.
32788      */
32789     enableTab : function(id){
32790         var tab = this.items[id];
32791         tab.enable();
32792     },
32793
32794     /**
32795      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
32796      * @param {String/Number} id The id or index of the TabPanelItem to activate.
32797      * @return {Roo.TabPanelItem} The TabPanelItem.
32798      */
32799     activate : function(id){
32800         var tab = this.items[id];
32801         if(!tab){
32802             return null;
32803         }
32804         if(tab == this.active || tab.disabled){
32805             return tab;
32806         }
32807         var e = {};
32808         this.fireEvent("beforetabchange", this, e, tab);
32809         if(e.cancel !== true && !tab.disabled){
32810             if(this.active){
32811                 this.active.hide();
32812             }
32813             this.active = this.items[id];
32814             this.active.show();
32815             this.fireEvent("tabchange", this, this.active);
32816         }
32817         return tab;
32818     },
32819
32820     /**
32821      * Gets the active {@link Roo.TabPanelItem}.
32822      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
32823      */
32824     getActiveTab : function(){
32825         return this.active;
32826     },
32827
32828     /**
32829      * Updates the tab body element to fit the height of the container element
32830      * for overflow scrolling
32831      * @param {Number} targetHeight (optional) Override the starting height from the elements height
32832      */
32833     syncHeight : function(targetHeight){
32834         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32835         var bm = this.bodyEl.getMargins();
32836         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
32837         this.bodyEl.setHeight(newHeight);
32838         return newHeight;
32839     },
32840
32841     onResize : function(){
32842         if(this.monitorResize){
32843             this.autoSizeTabs();
32844         }
32845     },
32846
32847     /**
32848      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
32849      */
32850     beginUpdate : function(){
32851         this.updating = true;
32852     },
32853
32854     /**
32855      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
32856      */
32857     endUpdate : function(){
32858         this.updating = false;
32859         this.autoSizeTabs();
32860     },
32861
32862     /**
32863      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
32864      */
32865     autoSizeTabs : function(){
32866         var count = this.items.length;
32867         var vcount = count - this.hiddenCount;
32868         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
32869             return;
32870         }
32871         var w = Math.max(this.el.getWidth() - this.cpad, 10);
32872         var availWidth = Math.floor(w / vcount);
32873         var b = this.stripBody;
32874         if(b.getWidth() > w){
32875             var tabs = this.items;
32876             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
32877             if(availWidth < this.minTabWidth){
32878                 /*if(!this.sleft){    // incomplete scrolling code
32879                     this.createScrollButtons();
32880                 }
32881                 this.showScroll();
32882                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
32883             }
32884         }else{
32885             if(this.currentTabWidth < this.preferredTabWidth){
32886                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
32887             }
32888         }
32889     },
32890
32891     /**
32892      * Returns the number of tabs in this TabPanel.
32893      * @return {Number}
32894      */
32895      getCount : function(){
32896          return this.items.length;
32897      },
32898
32899     /**
32900      * Resizes all the tabs to the passed width
32901      * @param {Number} The new width
32902      */
32903     setTabWidth : function(width){
32904         this.currentTabWidth = width;
32905         for(var i = 0, len = this.items.length; i < len; i++) {
32906                 if(!this.items[i].isHidden()) {
32907                 this.items[i].setWidth(width);
32908             }
32909         }
32910     },
32911
32912     /**
32913      * Destroys this TabPanel
32914      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
32915      */
32916     destroy : function(removeEl){
32917         Roo.EventManager.removeResizeListener(this.onResize, this);
32918         for(var i = 0, len = this.items.length; i < len; i++){
32919             this.items[i].purgeListeners();
32920         }
32921         if(removeEl === true){
32922             this.el.update("");
32923             this.el.remove();
32924         }
32925     }
32926 });
32927
32928 /**
32929  * @class Roo.TabPanelItem
32930  * @extends Roo.util.Observable
32931  * Represents an individual item (tab plus body) in a TabPanel.
32932  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
32933  * @param {String} id The id of this TabPanelItem
32934  * @param {String} text The text for the tab of this TabPanelItem
32935  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
32936  */
32937 Roo.TabPanelItem = function(tabPanel, id, text, closable){
32938     /**
32939      * The {@link Roo.TabPanel} this TabPanelItem belongs to
32940      * @type Roo.TabPanel
32941      */
32942     this.tabPanel = tabPanel;
32943     /**
32944      * The id for this TabPanelItem
32945      * @type String
32946      */
32947     this.id = id;
32948     /** @private */
32949     this.disabled = false;
32950     /** @private */
32951     this.text = text;
32952     /** @private */
32953     this.loaded = false;
32954     this.closable = closable;
32955
32956     /**
32957      * The body element for this TabPanelItem.
32958      * @type Roo.Element
32959      */
32960     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
32961     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
32962     this.bodyEl.setStyle("display", "block");
32963     this.bodyEl.setStyle("zoom", "1");
32964     this.hideAction();
32965
32966     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
32967     /** @private */
32968     this.el = Roo.get(els.el, true);
32969     this.inner = Roo.get(els.inner, true);
32970     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
32971     this.pnode = Roo.get(els.el.parentNode, true);
32972     this.el.on("mousedown", this.onTabMouseDown, this);
32973     this.el.on("click", this.onTabClick, this);
32974     /** @private */
32975     if(closable){
32976         var c = Roo.get(els.close, true);
32977         c.dom.title = this.closeText;
32978         c.addClassOnOver("close-over");
32979         c.on("click", this.closeClick, this);
32980      }
32981
32982     this.addEvents({
32983          /**
32984          * @event activate
32985          * Fires when this tab becomes the active tab.
32986          * @param {Roo.TabPanel} tabPanel The parent TabPanel
32987          * @param {Roo.TabPanelItem} this
32988          */
32989         "activate": true,
32990         /**
32991          * @event beforeclose
32992          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
32993          * @param {Roo.TabPanelItem} this
32994          * @param {Object} e Set cancel to true on this object to cancel the close.
32995          */
32996         "beforeclose": true,
32997         /**
32998          * @event close
32999          * Fires when this tab is closed.
33000          * @param {Roo.TabPanelItem} this
33001          */
33002          "close": true,
33003         /**
33004          * @event deactivate
33005          * Fires when this tab is no longer the active tab.
33006          * @param {Roo.TabPanel} tabPanel The parent TabPanel
33007          * @param {Roo.TabPanelItem} this
33008          */
33009          "deactivate" : true
33010     });
33011     this.hidden = false;
33012
33013     Roo.TabPanelItem.superclass.constructor.call(this);
33014 };
33015
33016 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
33017     purgeListeners : function(){
33018        Roo.util.Observable.prototype.purgeListeners.call(this);
33019        this.el.removeAllListeners();
33020     },
33021     /**
33022      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
33023      */
33024     show : function(){
33025         this.pnode.addClass("on");
33026         this.showAction();
33027         if(Roo.isOpera){
33028             this.tabPanel.stripWrap.repaint();
33029         }
33030         this.fireEvent("activate", this.tabPanel, this);
33031     },
33032
33033     /**
33034      * Returns true if this tab is the active tab.
33035      * @return {Boolean}
33036      */
33037     isActive : function(){
33038         return this.tabPanel.getActiveTab() == this;
33039     },
33040
33041     /**
33042      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
33043      */
33044     hide : function(){
33045         this.pnode.removeClass("on");
33046         this.hideAction();
33047         this.fireEvent("deactivate", this.tabPanel, this);
33048     },
33049
33050     hideAction : function(){
33051         this.bodyEl.hide();
33052         this.bodyEl.setStyle("position", "absolute");
33053         this.bodyEl.setLeft("-20000px");
33054         this.bodyEl.setTop("-20000px");
33055     },
33056
33057     showAction : function(){
33058         this.bodyEl.setStyle("position", "relative");
33059         this.bodyEl.setTop("");
33060         this.bodyEl.setLeft("");
33061         this.bodyEl.show();
33062     },
33063
33064     /**
33065      * Set the tooltip for the tab.
33066      * @param {String} tooltip The tab's tooltip
33067      */
33068     setTooltip : function(text){
33069         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
33070             this.textEl.dom.qtip = text;
33071             this.textEl.dom.removeAttribute('title');
33072         }else{
33073             this.textEl.dom.title = text;
33074         }
33075     },
33076
33077     onTabClick : function(e){
33078         e.preventDefault();
33079         this.tabPanel.activate(this.id);
33080     },
33081
33082     onTabMouseDown : function(e){
33083         e.preventDefault();
33084         this.tabPanel.activate(this.id);
33085     },
33086
33087     getWidth : function(){
33088         return this.inner.getWidth();
33089     },
33090
33091     setWidth : function(width){
33092         var iwidth = width - this.pnode.getPadding("lr");
33093         this.inner.setWidth(iwidth);
33094         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
33095         this.pnode.setWidth(width);
33096     },
33097
33098     /**
33099      * Show or hide the tab
33100      * @param {Boolean} hidden True to hide or false to show.
33101      */
33102     setHidden : function(hidden){
33103         this.hidden = hidden;
33104         this.pnode.setStyle("display", hidden ? "none" : "");
33105     },
33106
33107     /**
33108      * Returns true if this tab is "hidden"
33109      * @return {Boolean}
33110      */
33111     isHidden : function(){
33112         return this.hidden;
33113     },
33114
33115     /**
33116      * Returns the text for this tab
33117      * @return {String}
33118      */
33119     getText : function(){
33120         return this.text;
33121     },
33122
33123     autoSize : function(){
33124         //this.el.beginMeasure();
33125         this.textEl.setWidth(1);
33126         /*
33127          *  #2804 [new] Tabs in Roojs
33128          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
33129          */
33130         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
33131         //this.el.endMeasure();
33132     },
33133
33134     /**
33135      * Sets the text for the tab (Note: this also sets the tooltip text)
33136      * @param {String} text The tab's text and tooltip
33137      */
33138     setText : function(text){
33139         this.text = text;
33140         this.textEl.update(text);
33141         this.setTooltip(text);
33142         if(!this.tabPanel.resizeTabs){
33143             this.autoSize();
33144         }
33145     },
33146     /**
33147      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
33148      */
33149     activate : function(){
33150         this.tabPanel.activate(this.id);
33151     },
33152
33153     /**
33154      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
33155      */
33156     disable : function(){
33157         if(this.tabPanel.active != this){
33158             this.disabled = true;
33159             this.pnode.addClass("disabled");
33160         }
33161     },
33162
33163     /**
33164      * Enables this TabPanelItem if it was previously disabled.
33165      */
33166     enable : function(){
33167         this.disabled = false;
33168         this.pnode.removeClass("disabled");
33169     },
33170
33171     /**
33172      * Sets the content for this TabPanelItem.
33173      * @param {String} content The content
33174      * @param {Boolean} loadScripts true to look for and load scripts
33175      */
33176     setContent : function(content, loadScripts){
33177         this.bodyEl.update(content, loadScripts);
33178     },
33179
33180     /**
33181      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
33182      * @return {Roo.UpdateManager} The UpdateManager
33183      */
33184     getUpdateManager : function(){
33185         return this.bodyEl.getUpdateManager();
33186     },
33187
33188     /**
33189      * Set a URL to be used to load the content for this TabPanelItem.
33190      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
33191      * @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)
33192      * @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)
33193      * @return {Roo.UpdateManager} The UpdateManager
33194      */
33195     setUrl : function(url, params, loadOnce){
33196         if(this.refreshDelegate){
33197             this.un('activate', this.refreshDelegate);
33198         }
33199         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33200         this.on("activate", this.refreshDelegate);
33201         return this.bodyEl.getUpdateManager();
33202     },
33203
33204     /** @private */
33205     _handleRefresh : function(url, params, loadOnce){
33206         if(!loadOnce || !this.loaded){
33207             var updater = this.bodyEl.getUpdateManager();
33208             updater.update(url, params, this._setLoaded.createDelegate(this));
33209         }
33210     },
33211
33212     /**
33213      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
33214      *   Will fail silently if the setUrl method has not been called.
33215      *   This does not activate the panel, just updates its content.
33216      */
33217     refresh : function(){
33218         if(this.refreshDelegate){
33219            this.loaded = false;
33220            this.refreshDelegate();
33221         }
33222     },
33223
33224     /** @private */
33225     _setLoaded : function(){
33226         this.loaded = true;
33227     },
33228
33229     /** @private */
33230     closeClick : function(e){
33231         var o = {};
33232         e.stopEvent();
33233         this.fireEvent("beforeclose", this, o);
33234         if(o.cancel !== true){
33235             this.tabPanel.removeTab(this.id);
33236         }
33237     },
33238     /**
33239      * The text displayed in the tooltip for the close icon.
33240      * @type String
33241      */
33242     closeText : "Close this tab"
33243 });
33244
33245 /** @private */
33246 Roo.TabPanel.prototype.createStrip = function(container){
33247     var strip = document.createElement("div");
33248     strip.className = "x-tabs-wrap";
33249     container.appendChild(strip);
33250     return strip;
33251 };
33252 /** @private */
33253 Roo.TabPanel.prototype.createStripList = function(strip){
33254     // div wrapper for retard IE
33255     // returns the "tr" element.
33256     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
33257         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
33258         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
33259     return strip.firstChild.firstChild.firstChild.firstChild;
33260 };
33261 /** @private */
33262 Roo.TabPanel.prototype.createBody = function(container){
33263     var body = document.createElement("div");
33264     Roo.id(body, "tab-body");
33265     Roo.fly(body).addClass("x-tabs-body");
33266     container.appendChild(body);
33267     return body;
33268 };
33269 /** @private */
33270 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
33271     var body = Roo.getDom(id);
33272     if(!body){
33273         body = document.createElement("div");
33274         body.id = id;
33275     }
33276     Roo.fly(body).addClass("x-tabs-item-body");
33277     bodyEl.insertBefore(body, bodyEl.firstChild);
33278     return body;
33279 };
33280 /** @private */
33281 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
33282     var td = document.createElement("td");
33283     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
33284     //stripEl.appendChild(td);
33285     if(closable){
33286         td.className = "x-tabs-closable";
33287         if(!this.closeTpl){
33288             this.closeTpl = new Roo.Template(
33289                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
33290                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
33291                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
33292             );
33293         }
33294         var el = this.closeTpl.overwrite(td, {"text": text});
33295         var close = el.getElementsByTagName("div")[0];
33296         var inner = el.getElementsByTagName("em")[0];
33297         return {"el": el, "close": close, "inner": inner};
33298     } else {
33299         if(!this.tabTpl){
33300             this.tabTpl = new Roo.Template(
33301                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
33302                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
33303             );
33304         }
33305         var el = this.tabTpl.overwrite(td, {"text": text});
33306         var inner = el.getElementsByTagName("em")[0];
33307         return {"el": el, "inner": inner};
33308     }
33309 };/*
33310  * Based on:
33311  * Ext JS Library 1.1.1
33312  * Copyright(c) 2006-2007, Ext JS, LLC.
33313  *
33314  * Originally Released Under LGPL - original licence link has changed is not relivant.
33315  *
33316  * Fork - LGPL
33317  * <script type="text/javascript">
33318  */
33319
33320 /**
33321  * @class Roo.Button
33322  * @extends Roo.util.Observable
33323  * Simple Button class
33324  * @cfg {String} text The button text
33325  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
33326  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
33327  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
33328  * @cfg {Object} scope The scope of the handler
33329  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
33330  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
33331  * @cfg {Boolean} hidden True to start hidden (defaults to false)
33332  * @cfg {Boolean} disabled True to start disabled (defaults to false)
33333  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
33334  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
33335    applies if enableToggle = true)
33336  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
33337  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
33338   an {@link Roo.util.ClickRepeater} config object (defaults to false).
33339  * @constructor
33340  * Create a new button
33341  * @param {Object} config The config object
33342  */
33343 Roo.Button = function(renderTo, config)
33344 {
33345     if (!config) {
33346         config = renderTo;
33347         renderTo = config.renderTo || false;
33348     }
33349     
33350     Roo.apply(this, config);
33351     this.addEvents({
33352         /**
33353              * @event click
33354              * Fires when this button is clicked
33355              * @param {Button} this
33356              * @param {EventObject} e The click event
33357              */
33358             "click" : true,
33359         /**
33360              * @event toggle
33361              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
33362              * @param {Button} this
33363              * @param {Boolean} pressed
33364              */
33365             "toggle" : true,
33366         /**
33367              * @event mouseover
33368              * Fires when the mouse hovers over the button
33369              * @param {Button} this
33370              * @param {Event} e The event object
33371              */
33372         'mouseover' : true,
33373         /**
33374              * @event mouseout
33375              * Fires when the mouse exits the button
33376              * @param {Button} this
33377              * @param {Event} e The event object
33378              */
33379         'mouseout': true,
33380          /**
33381              * @event render
33382              * Fires when the button is rendered
33383              * @param {Button} this
33384              */
33385         'render': true
33386     });
33387     if(this.menu){
33388         this.menu = Roo.menu.MenuMgr.get(this.menu);
33389     }
33390     // register listeners first!!  - so render can be captured..
33391     Roo.util.Observable.call(this);
33392     if(renderTo){
33393         this.render(renderTo);
33394     }
33395     
33396   
33397 };
33398
33399 Roo.extend(Roo.Button, Roo.util.Observable, {
33400     /**
33401      * 
33402      */
33403     
33404     /**
33405      * Read-only. True if this button is hidden
33406      * @type Boolean
33407      */
33408     hidden : false,
33409     /**
33410      * Read-only. True if this button is disabled
33411      * @type Boolean
33412      */
33413     disabled : false,
33414     /**
33415      * Read-only. True if this button is pressed (only if enableToggle = true)
33416      * @type Boolean
33417      */
33418     pressed : false,
33419
33420     /**
33421      * @cfg {Number} tabIndex 
33422      * The DOM tabIndex for this button (defaults to undefined)
33423      */
33424     tabIndex : undefined,
33425
33426     /**
33427      * @cfg {Boolean} enableToggle
33428      * True to enable pressed/not pressed toggling (defaults to false)
33429      */
33430     enableToggle: false,
33431     /**
33432      * @cfg {Mixed} menu
33433      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
33434      */
33435     menu : undefined,
33436     /**
33437      * @cfg {String} menuAlign
33438      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
33439      */
33440     menuAlign : "tl-bl?",
33441
33442     /**
33443      * @cfg {String} iconCls
33444      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
33445      */
33446     iconCls : undefined,
33447     /**
33448      * @cfg {String} type
33449      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
33450      */
33451     type : 'button',
33452
33453     // private
33454     menuClassTarget: 'tr',
33455
33456     /**
33457      * @cfg {String} clickEvent
33458      * The type of event to map to the button's event handler (defaults to 'click')
33459      */
33460     clickEvent : 'click',
33461
33462     /**
33463      * @cfg {Boolean} handleMouseEvents
33464      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
33465      */
33466     handleMouseEvents : true,
33467
33468     /**
33469      * @cfg {String} tooltipType
33470      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
33471      */
33472     tooltipType : 'qtip',
33473
33474     /**
33475      * @cfg {String} cls
33476      * A CSS class to apply to the button's main element.
33477      */
33478     
33479     /**
33480      * @cfg {Roo.Template} template (Optional)
33481      * An {@link Roo.Template} with which to create the Button's main element. This Template must
33482      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
33483      * require code modifications if required elements (e.g. a button) aren't present.
33484      */
33485
33486     // private
33487     render : function(renderTo){
33488         var btn;
33489         if(this.hideParent){
33490             this.parentEl = Roo.get(renderTo);
33491         }
33492         if(!this.dhconfig){
33493             if(!this.template){
33494                 if(!Roo.Button.buttonTemplate){
33495                     // hideous table template
33496                     Roo.Button.buttonTemplate = new Roo.Template(
33497                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
33498                         '<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>',
33499                         "</tr></tbody></table>");
33500                 }
33501                 this.template = Roo.Button.buttonTemplate;
33502             }
33503             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
33504             var btnEl = btn.child("button:first");
33505             btnEl.on('focus', this.onFocus, this);
33506             btnEl.on('blur', this.onBlur, this);
33507             if(this.cls){
33508                 btn.addClass(this.cls);
33509             }
33510             if(this.icon){
33511                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
33512             }
33513             if(this.iconCls){
33514                 btnEl.addClass(this.iconCls);
33515                 if(!this.cls){
33516                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
33517                 }
33518             }
33519             if(this.tabIndex !== undefined){
33520                 btnEl.dom.tabIndex = this.tabIndex;
33521             }
33522             if(this.tooltip){
33523                 if(typeof this.tooltip == 'object'){
33524                     Roo.QuickTips.tips(Roo.apply({
33525                           target: btnEl.id
33526                     }, this.tooltip));
33527                 } else {
33528                     btnEl.dom[this.tooltipType] = this.tooltip;
33529                 }
33530             }
33531         }else{
33532             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
33533         }
33534         this.el = btn;
33535         if(this.id){
33536             this.el.dom.id = this.el.id = this.id;
33537         }
33538         if(this.menu){
33539             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
33540             this.menu.on("show", this.onMenuShow, this);
33541             this.menu.on("hide", this.onMenuHide, this);
33542         }
33543         btn.addClass("x-btn");
33544         if(Roo.isIE && !Roo.isIE7){
33545             this.autoWidth.defer(1, this);
33546         }else{
33547             this.autoWidth();
33548         }
33549         if(this.handleMouseEvents){
33550             btn.on("mouseover", this.onMouseOver, this);
33551             btn.on("mouseout", this.onMouseOut, this);
33552             btn.on("mousedown", this.onMouseDown, this);
33553         }
33554         btn.on(this.clickEvent, this.onClick, this);
33555         //btn.on("mouseup", this.onMouseUp, this);
33556         if(this.hidden){
33557             this.hide();
33558         }
33559         if(this.disabled){
33560             this.disable();
33561         }
33562         Roo.ButtonToggleMgr.register(this);
33563         if(this.pressed){
33564             this.el.addClass("x-btn-pressed");
33565         }
33566         if(this.repeat){
33567             var repeater = new Roo.util.ClickRepeater(btn,
33568                 typeof this.repeat == "object" ? this.repeat : {}
33569             );
33570             repeater.on("click", this.onClick,  this);
33571         }
33572         
33573         this.fireEvent('render', this);
33574         
33575     },
33576     /**
33577      * Returns the button's underlying element
33578      * @return {Roo.Element} The element
33579      */
33580     getEl : function(){
33581         return this.el;  
33582     },
33583     
33584     /**
33585      * Destroys this Button and removes any listeners.
33586      */
33587     destroy : function(){
33588         Roo.ButtonToggleMgr.unregister(this);
33589         this.el.removeAllListeners();
33590         this.purgeListeners();
33591         this.el.remove();
33592     },
33593
33594     // private
33595     autoWidth : function(){
33596         if(this.el){
33597             this.el.setWidth("auto");
33598             if(Roo.isIE7 && Roo.isStrict){
33599                 var ib = this.el.child('button');
33600                 if(ib && ib.getWidth() > 20){
33601                     ib.clip();
33602                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
33603                 }
33604             }
33605             if(this.minWidth){
33606                 if(this.hidden){
33607                     this.el.beginMeasure();
33608                 }
33609                 if(this.el.getWidth() < this.minWidth){
33610                     this.el.setWidth(this.minWidth);
33611                 }
33612                 if(this.hidden){
33613                     this.el.endMeasure();
33614                 }
33615             }
33616         }
33617     },
33618
33619     /**
33620      * Assigns this button's click handler
33621      * @param {Function} handler The function to call when the button is clicked
33622      * @param {Object} scope (optional) Scope for the function passed in
33623      */
33624     setHandler : function(handler, scope){
33625         this.handler = handler;
33626         this.scope = scope;  
33627     },
33628     
33629     /**
33630      * Sets this button's text
33631      * @param {String} text The button text
33632      */
33633     setText : function(text){
33634         this.text = text;
33635         if(this.el){
33636             this.el.child("td.x-btn-center button.x-btn-text").update(text);
33637         }
33638         this.autoWidth();
33639     },
33640     
33641     /**
33642      * Gets the text for this button
33643      * @return {String} The button text
33644      */
33645     getText : function(){
33646         return this.text;  
33647     },
33648     
33649     /**
33650      * Show this button
33651      */
33652     show: function(){
33653         this.hidden = false;
33654         if(this.el){
33655             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
33656         }
33657     },
33658     
33659     /**
33660      * Hide this button
33661      */
33662     hide: function(){
33663         this.hidden = true;
33664         if(this.el){
33665             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
33666         }
33667     },
33668     
33669     /**
33670      * Convenience function for boolean show/hide
33671      * @param {Boolean} visible True to show, false to hide
33672      */
33673     setVisible: function(visible){
33674         if(visible) {
33675             this.show();
33676         }else{
33677             this.hide();
33678         }
33679     },
33680     
33681     /**
33682      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
33683      * @param {Boolean} state (optional) Force a particular state
33684      */
33685     toggle : function(state){
33686         state = state === undefined ? !this.pressed : state;
33687         if(state != this.pressed){
33688             if(state){
33689                 this.el.addClass("x-btn-pressed");
33690                 this.pressed = true;
33691                 this.fireEvent("toggle", this, true);
33692             }else{
33693                 this.el.removeClass("x-btn-pressed");
33694                 this.pressed = false;
33695                 this.fireEvent("toggle", this, false);
33696             }
33697             if(this.toggleHandler){
33698                 this.toggleHandler.call(this.scope || this, this, state);
33699             }
33700         }
33701     },
33702     
33703     /**
33704      * Focus the button
33705      */
33706     focus : function(){
33707         this.el.child('button:first').focus();
33708     },
33709     
33710     /**
33711      * Disable this button
33712      */
33713     disable : function(){
33714         if(this.el){
33715             this.el.addClass("x-btn-disabled");
33716         }
33717         this.disabled = true;
33718     },
33719     
33720     /**
33721      * Enable this button
33722      */
33723     enable : function(){
33724         if(this.el){
33725             this.el.removeClass("x-btn-disabled");
33726         }
33727         this.disabled = false;
33728     },
33729
33730     /**
33731      * Convenience function for boolean enable/disable
33732      * @param {Boolean} enabled True to enable, false to disable
33733      */
33734     setDisabled : function(v){
33735         this[v !== true ? "enable" : "disable"]();
33736     },
33737
33738     // private
33739     onClick : function(e)
33740     {
33741         if(e){
33742             e.preventDefault();
33743         }
33744         if(e.button != 0){
33745             return;
33746         }
33747         if(!this.disabled){
33748             if(this.enableToggle){
33749                 this.toggle();
33750             }
33751             if(this.menu && !this.menu.isVisible()){
33752                 this.menu.show(this.el, this.menuAlign);
33753             }
33754             this.fireEvent("click", this, e);
33755             if(this.handler){
33756                 this.el.removeClass("x-btn-over");
33757                 this.handler.call(this.scope || this, this, e);
33758             }
33759         }
33760     },
33761     // private
33762     onMouseOver : function(e){
33763         if(!this.disabled){
33764             this.el.addClass("x-btn-over");
33765             this.fireEvent('mouseover', this, e);
33766         }
33767     },
33768     // private
33769     onMouseOut : function(e){
33770         if(!e.within(this.el,  true)){
33771             this.el.removeClass("x-btn-over");
33772             this.fireEvent('mouseout', this, e);
33773         }
33774     },
33775     // private
33776     onFocus : function(e){
33777         if(!this.disabled){
33778             this.el.addClass("x-btn-focus");
33779         }
33780     },
33781     // private
33782     onBlur : function(e){
33783         this.el.removeClass("x-btn-focus");
33784     },
33785     // private
33786     onMouseDown : function(e){
33787         if(!this.disabled && e.button == 0){
33788             this.el.addClass("x-btn-click");
33789             Roo.get(document).on('mouseup', this.onMouseUp, this);
33790         }
33791     },
33792     // private
33793     onMouseUp : function(e){
33794         if(e.button == 0){
33795             this.el.removeClass("x-btn-click");
33796             Roo.get(document).un('mouseup', this.onMouseUp, this);
33797         }
33798     },
33799     // private
33800     onMenuShow : function(e){
33801         this.el.addClass("x-btn-menu-active");
33802     },
33803     // private
33804     onMenuHide : function(e){
33805         this.el.removeClass("x-btn-menu-active");
33806     }   
33807 });
33808
33809 // Private utility class used by Button
33810 Roo.ButtonToggleMgr = function(){
33811    var groups = {};
33812    
33813    function toggleGroup(btn, state){
33814        if(state){
33815            var g = groups[btn.toggleGroup];
33816            for(var i = 0, l = g.length; i < l; i++){
33817                if(g[i] != btn){
33818                    g[i].toggle(false);
33819                }
33820            }
33821        }
33822    }
33823    
33824    return {
33825        register : function(btn){
33826            if(!btn.toggleGroup){
33827                return;
33828            }
33829            var g = groups[btn.toggleGroup];
33830            if(!g){
33831                g = groups[btn.toggleGroup] = [];
33832            }
33833            g.push(btn);
33834            btn.on("toggle", toggleGroup);
33835        },
33836        
33837        unregister : function(btn){
33838            if(!btn.toggleGroup){
33839                return;
33840            }
33841            var g = groups[btn.toggleGroup];
33842            if(g){
33843                g.remove(btn);
33844                btn.un("toggle", toggleGroup);
33845            }
33846        }
33847    };
33848 }();/*
33849  * Based on:
33850  * Ext JS Library 1.1.1
33851  * Copyright(c) 2006-2007, Ext JS, LLC.
33852  *
33853  * Originally Released Under LGPL - original licence link has changed is not relivant.
33854  *
33855  * Fork - LGPL
33856  * <script type="text/javascript">
33857  */
33858  
33859 /**
33860  * @class Roo.SplitButton
33861  * @extends Roo.Button
33862  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
33863  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
33864  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
33865  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
33866  * @cfg {String} arrowTooltip The title attribute of the arrow
33867  * @constructor
33868  * Create a new menu button
33869  * @param {String/HTMLElement/Element} renderTo The element to append the button to
33870  * @param {Object} config The config object
33871  */
33872 Roo.SplitButton = function(renderTo, config){
33873     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
33874     /**
33875      * @event arrowclick
33876      * Fires when this button's arrow is clicked
33877      * @param {SplitButton} this
33878      * @param {EventObject} e The click event
33879      */
33880     this.addEvents({"arrowclick":true});
33881 };
33882
33883 Roo.extend(Roo.SplitButton, Roo.Button, {
33884     render : function(renderTo){
33885         // this is one sweet looking template!
33886         var tpl = new Roo.Template(
33887             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
33888             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
33889             '<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>',
33890             "</tbody></table></td><td>",
33891             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
33892             '<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>',
33893             "</tbody></table></td></tr></table>"
33894         );
33895         var btn = tpl.append(renderTo, [this.text, this.type], true);
33896         var btnEl = btn.child("button");
33897         if(this.cls){
33898             btn.addClass(this.cls);
33899         }
33900         if(this.icon){
33901             btnEl.setStyle('background-image', 'url(' +this.icon +')');
33902         }
33903         if(this.iconCls){
33904             btnEl.addClass(this.iconCls);
33905             if(!this.cls){
33906                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
33907             }
33908         }
33909         this.el = btn;
33910         if(this.handleMouseEvents){
33911             btn.on("mouseover", this.onMouseOver, this);
33912             btn.on("mouseout", this.onMouseOut, this);
33913             btn.on("mousedown", this.onMouseDown, this);
33914             btn.on("mouseup", this.onMouseUp, this);
33915         }
33916         btn.on(this.clickEvent, this.onClick, this);
33917         if(this.tooltip){
33918             if(typeof this.tooltip == 'object'){
33919                 Roo.QuickTips.tips(Roo.apply({
33920                       target: btnEl.id
33921                 }, this.tooltip));
33922             } else {
33923                 btnEl.dom[this.tooltipType] = this.tooltip;
33924             }
33925         }
33926         if(this.arrowTooltip){
33927             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
33928         }
33929         if(this.hidden){
33930             this.hide();
33931         }
33932         if(this.disabled){
33933             this.disable();
33934         }
33935         if(this.pressed){
33936             this.el.addClass("x-btn-pressed");
33937         }
33938         if(Roo.isIE && !Roo.isIE7){
33939             this.autoWidth.defer(1, this);
33940         }else{
33941             this.autoWidth();
33942         }
33943         if(this.menu){
33944             this.menu.on("show", this.onMenuShow, this);
33945             this.menu.on("hide", this.onMenuHide, this);
33946         }
33947         this.fireEvent('render', this);
33948     },
33949
33950     // private
33951     autoWidth : function(){
33952         if(this.el){
33953             var tbl = this.el.child("table:first");
33954             var tbl2 = this.el.child("table:last");
33955             this.el.setWidth("auto");
33956             tbl.setWidth("auto");
33957             if(Roo.isIE7 && Roo.isStrict){
33958                 var ib = this.el.child('button:first');
33959                 if(ib && ib.getWidth() > 20){
33960                     ib.clip();
33961                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
33962                 }
33963             }
33964             if(this.minWidth){
33965                 if(this.hidden){
33966                     this.el.beginMeasure();
33967                 }
33968                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
33969                     tbl.setWidth(this.minWidth-tbl2.getWidth());
33970                 }
33971                 if(this.hidden){
33972                     this.el.endMeasure();
33973                 }
33974             }
33975             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
33976         } 
33977     },
33978     /**
33979      * Sets this button's click handler
33980      * @param {Function} handler The function to call when the button is clicked
33981      * @param {Object} scope (optional) Scope for the function passed above
33982      */
33983     setHandler : function(handler, scope){
33984         this.handler = handler;
33985         this.scope = scope;  
33986     },
33987     
33988     /**
33989      * Sets this button's arrow click handler
33990      * @param {Function} handler The function to call when the arrow is clicked
33991      * @param {Object} scope (optional) Scope for the function passed above
33992      */
33993     setArrowHandler : function(handler, scope){
33994         this.arrowHandler = handler;
33995         this.scope = scope;  
33996     },
33997     
33998     /**
33999      * Focus the button
34000      */
34001     focus : function(){
34002         if(this.el){
34003             this.el.child("button:first").focus();
34004         }
34005     },
34006
34007     // private
34008     onClick : function(e){
34009         e.preventDefault();
34010         if(!this.disabled){
34011             if(e.getTarget(".x-btn-menu-arrow-wrap")){
34012                 if(this.menu && !this.menu.isVisible()){
34013                     this.menu.show(this.el, this.menuAlign);
34014                 }
34015                 this.fireEvent("arrowclick", this, e);
34016                 if(this.arrowHandler){
34017                     this.arrowHandler.call(this.scope || this, this, e);
34018                 }
34019             }else{
34020                 this.fireEvent("click", this, e);
34021                 if(this.handler){
34022                     this.handler.call(this.scope || this, this, e);
34023                 }
34024             }
34025         }
34026     },
34027     // private
34028     onMouseDown : function(e){
34029         if(!this.disabled){
34030             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
34031         }
34032     },
34033     // private
34034     onMouseUp : function(e){
34035         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
34036     }   
34037 });
34038
34039
34040 // backwards compat
34041 Roo.MenuButton = Roo.SplitButton;/*
34042  * Based on:
34043  * Ext JS Library 1.1.1
34044  * Copyright(c) 2006-2007, Ext JS, LLC.
34045  *
34046  * Originally Released Under LGPL - original licence link has changed is not relivant.
34047  *
34048  * Fork - LGPL
34049  * <script type="text/javascript">
34050  */
34051
34052 /**
34053  * @class Roo.Toolbar
34054  * Basic Toolbar class.
34055  * @constructor
34056  * Creates a new Toolbar
34057  * @param {Object} container The config object
34058  */ 
34059 Roo.Toolbar = function(container, buttons, config)
34060 {
34061     /// old consturctor format still supported..
34062     if(container instanceof Array){ // omit the container for later rendering
34063         buttons = container;
34064         config = buttons;
34065         container = null;
34066     }
34067     if (typeof(container) == 'object' && container.xtype) {
34068         config = container;
34069         container = config.container;
34070         buttons = config.buttons || []; // not really - use items!!
34071     }
34072     var xitems = [];
34073     if (config && config.items) {
34074         xitems = config.items;
34075         delete config.items;
34076     }
34077     Roo.apply(this, config);
34078     this.buttons = buttons;
34079     
34080     if(container){
34081         this.render(container);
34082     }
34083     this.xitems = xitems;
34084     Roo.each(xitems, function(b) {
34085         this.add(b);
34086     }, this);
34087     
34088 };
34089
34090 Roo.Toolbar.prototype = {
34091     /**
34092      * @cfg {Array} items
34093      * array of button configs or elements to add (will be converted to a MixedCollection)
34094      */
34095     
34096     /**
34097      * @cfg {String/HTMLElement/Element} container
34098      * The id or element that will contain the toolbar
34099      */
34100     // private
34101     render : function(ct){
34102         this.el = Roo.get(ct);
34103         if(this.cls){
34104             this.el.addClass(this.cls);
34105         }
34106         // using a table allows for vertical alignment
34107         // 100% width is needed by Safari...
34108         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
34109         this.tr = this.el.child("tr", true);
34110         var autoId = 0;
34111         this.items = new Roo.util.MixedCollection(false, function(o){
34112             return o.id || ("item" + (++autoId));
34113         });
34114         if(this.buttons){
34115             this.add.apply(this, this.buttons);
34116             delete this.buttons;
34117         }
34118     },
34119
34120     /**
34121      * Adds element(s) to the toolbar -- this function takes a variable number of 
34122      * arguments of mixed type and adds them to the toolbar.
34123      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
34124      * <ul>
34125      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
34126      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
34127      * <li>Field: Any form field (equivalent to {@link #addField})</li>
34128      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
34129      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
34130      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
34131      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
34132      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
34133      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
34134      * </ul>
34135      * @param {Mixed} arg2
34136      * @param {Mixed} etc.
34137      */
34138     add : function(){
34139         var a = arguments, l = a.length;
34140         for(var i = 0; i < l; i++){
34141             this._add(a[i]);
34142         }
34143     },
34144     // private..
34145     _add : function(el) {
34146         
34147         if (el.xtype) {
34148             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
34149         }
34150         
34151         if (el.applyTo){ // some kind of form field
34152             return this.addField(el);
34153         } 
34154         if (el.render){ // some kind of Toolbar.Item
34155             return this.addItem(el);
34156         }
34157         if (typeof el == "string"){ // string
34158             if(el == "separator" || el == "-"){
34159                 return this.addSeparator();
34160             }
34161             if (el == " "){
34162                 return this.addSpacer();
34163             }
34164             if(el == "->"){
34165                 return this.addFill();
34166             }
34167             return this.addText(el);
34168             
34169         }
34170         if(el.tagName){ // element
34171             return this.addElement(el);
34172         }
34173         if(typeof el == "object"){ // must be button config?
34174             return this.addButton(el);
34175         }
34176         // and now what?!?!
34177         return false;
34178         
34179     },
34180     
34181     /**
34182      * Add an Xtype element
34183      * @param {Object} xtype Xtype Object
34184      * @return {Object} created Object
34185      */
34186     addxtype : function(e){
34187         return this.add(e);  
34188     },
34189     
34190     /**
34191      * Returns the Element for this toolbar.
34192      * @return {Roo.Element}
34193      */
34194     getEl : function(){
34195         return this.el;  
34196     },
34197     
34198     /**
34199      * Adds a separator
34200      * @return {Roo.Toolbar.Item} The separator item
34201      */
34202     addSeparator : function(){
34203         return this.addItem(new Roo.Toolbar.Separator());
34204     },
34205
34206     /**
34207      * Adds a spacer element
34208      * @return {Roo.Toolbar.Spacer} The spacer item
34209      */
34210     addSpacer : function(){
34211         return this.addItem(new Roo.Toolbar.Spacer());
34212     },
34213
34214     /**
34215      * Adds a fill element that forces subsequent additions to the right side of the toolbar
34216      * @return {Roo.Toolbar.Fill} The fill item
34217      */
34218     addFill : function(){
34219         return this.addItem(new Roo.Toolbar.Fill());
34220     },
34221
34222     /**
34223      * Adds any standard HTML element to the toolbar
34224      * @param {String/HTMLElement/Element} el The element or id of the element to add
34225      * @return {Roo.Toolbar.Item} The element's item
34226      */
34227     addElement : function(el){
34228         return this.addItem(new Roo.Toolbar.Item(el));
34229     },
34230     /**
34231      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
34232      * @type Roo.util.MixedCollection  
34233      */
34234     items : false,
34235      
34236     /**
34237      * Adds any Toolbar.Item or subclass
34238      * @param {Roo.Toolbar.Item} item
34239      * @return {Roo.Toolbar.Item} The item
34240      */
34241     addItem : function(item){
34242         var td = this.nextBlock();
34243         item.render(td);
34244         this.items.add(item);
34245         return item;
34246     },
34247     
34248     /**
34249      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
34250      * @param {Object/Array} config A button config or array of configs
34251      * @return {Roo.Toolbar.Button/Array}
34252      */
34253     addButton : function(config){
34254         if(config instanceof Array){
34255             var buttons = [];
34256             for(var i = 0, len = config.length; i < len; i++) {
34257                 buttons.push(this.addButton(config[i]));
34258             }
34259             return buttons;
34260         }
34261         var b = config;
34262         if(!(config instanceof Roo.Toolbar.Button)){
34263             b = config.split ?
34264                 new Roo.Toolbar.SplitButton(config) :
34265                 new Roo.Toolbar.Button(config);
34266         }
34267         var td = this.nextBlock();
34268         b.render(td);
34269         this.items.add(b);
34270         return b;
34271     },
34272     
34273     /**
34274      * Adds text to the toolbar
34275      * @param {String} text The text to add
34276      * @return {Roo.Toolbar.Item} The element's item
34277      */
34278     addText : function(text){
34279         return this.addItem(new Roo.Toolbar.TextItem(text));
34280     },
34281     
34282     /**
34283      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
34284      * @param {Number} index The index where the item is to be inserted
34285      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
34286      * @return {Roo.Toolbar.Button/Item}
34287      */
34288     insertButton : function(index, item){
34289         if(item instanceof Array){
34290             var buttons = [];
34291             for(var i = 0, len = item.length; i < len; i++) {
34292                buttons.push(this.insertButton(index + i, item[i]));
34293             }
34294             return buttons;
34295         }
34296         if (!(item instanceof Roo.Toolbar.Button)){
34297            item = new Roo.Toolbar.Button(item);
34298         }
34299         var td = document.createElement("td");
34300         this.tr.insertBefore(td, this.tr.childNodes[index]);
34301         item.render(td);
34302         this.items.insert(index, item);
34303         return item;
34304     },
34305     
34306     /**
34307      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
34308      * @param {Object} config
34309      * @return {Roo.Toolbar.Item} The element's item
34310      */
34311     addDom : function(config, returnEl){
34312         var td = this.nextBlock();
34313         Roo.DomHelper.overwrite(td, config);
34314         var ti = new Roo.Toolbar.Item(td.firstChild);
34315         ti.render(td);
34316         this.items.add(ti);
34317         return ti;
34318     },
34319
34320     /**
34321      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
34322      * @type Roo.util.MixedCollection  
34323      */
34324     fields : false,
34325     
34326     /**
34327      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
34328      * Note: the field should not have been rendered yet. For a field that has already been
34329      * rendered, use {@link #addElement}.
34330      * @param {Roo.form.Field} field
34331      * @return {Roo.ToolbarItem}
34332      */
34333      
34334       
34335     addField : function(field) {
34336         if (!this.fields) {
34337             var autoId = 0;
34338             this.fields = new Roo.util.MixedCollection(false, function(o){
34339                 return o.id || ("item" + (++autoId));
34340             });
34341
34342         }
34343         
34344         var td = this.nextBlock();
34345         field.render(td);
34346         var ti = new Roo.Toolbar.Item(td.firstChild);
34347         ti.render(td);
34348         this.items.add(ti);
34349         this.fields.add(field);
34350         return ti;
34351     },
34352     /**
34353      * Hide the toolbar
34354      * @method hide
34355      */
34356      
34357       
34358     hide : function()
34359     {
34360         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
34361         this.el.child('div').hide();
34362     },
34363     /**
34364      * Show the toolbar
34365      * @method show
34366      */
34367     show : function()
34368     {
34369         this.el.child('div').show();
34370     },
34371       
34372     // private
34373     nextBlock : function(){
34374         var td = document.createElement("td");
34375         this.tr.appendChild(td);
34376         return td;
34377     },
34378
34379     // private
34380     destroy : function(){
34381         if(this.items){ // rendered?
34382             Roo.destroy.apply(Roo, this.items.items);
34383         }
34384         if(this.fields){ // rendered?
34385             Roo.destroy.apply(Roo, this.fields.items);
34386         }
34387         Roo.Element.uncache(this.el, this.tr);
34388     }
34389 };
34390
34391 /**
34392  * @class Roo.Toolbar.Item
34393  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
34394  * @constructor
34395  * Creates a new Item
34396  * @param {HTMLElement} el 
34397  */
34398 Roo.Toolbar.Item = function(el){
34399     var cfg = {};
34400     if (typeof (el.xtype) != 'undefined') {
34401         cfg = el;
34402         el = cfg.el;
34403     }
34404     
34405     this.el = Roo.getDom(el);
34406     this.id = Roo.id(this.el);
34407     this.hidden = false;
34408     
34409     this.addEvents({
34410          /**
34411              * @event render
34412              * Fires when the button is rendered
34413              * @param {Button} this
34414              */
34415         'render': true
34416     });
34417     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
34418 };
34419 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
34420 //Roo.Toolbar.Item.prototype = {
34421     
34422     /**
34423      * Get this item's HTML Element
34424      * @return {HTMLElement}
34425      */
34426     getEl : function(){
34427        return this.el;  
34428     },
34429
34430     // private
34431     render : function(td){
34432         
34433          this.td = td;
34434         td.appendChild(this.el);
34435         
34436         this.fireEvent('render', this);
34437     },
34438     
34439     /**
34440      * Removes and destroys this item.
34441      */
34442     destroy : function(){
34443         this.td.parentNode.removeChild(this.td);
34444     },
34445     
34446     /**
34447      * Shows this item.
34448      */
34449     show: function(){
34450         this.hidden = false;
34451         this.td.style.display = "";
34452     },
34453     
34454     /**
34455      * Hides this item.
34456      */
34457     hide: function(){
34458         this.hidden = true;
34459         this.td.style.display = "none";
34460     },
34461     
34462     /**
34463      * Convenience function for boolean show/hide.
34464      * @param {Boolean} visible true to show/false to hide
34465      */
34466     setVisible: function(visible){
34467         if(visible) {
34468             this.show();
34469         }else{
34470             this.hide();
34471         }
34472     },
34473     
34474     /**
34475      * Try to focus this item.
34476      */
34477     focus : function(){
34478         Roo.fly(this.el).focus();
34479     },
34480     
34481     /**
34482      * Disables this item.
34483      */
34484     disable : function(){
34485         Roo.fly(this.td).addClass("x-item-disabled");
34486         this.disabled = true;
34487         this.el.disabled = true;
34488     },
34489     
34490     /**
34491      * Enables this item.
34492      */
34493     enable : function(){
34494         Roo.fly(this.td).removeClass("x-item-disabled");
34495         this.disabled = false;
34496         this.el.disabled = false;
34497     }
34498 });
34499
34500
34501 /**
34502  * @class Roo.Toolbar.Separator
34503  * @extends Roo.Toolbar.Item
34504  * A simple toolbar separator class
34505  * @constructor
34506  * Creates a new Separator
34507  */
34508 Roo.Toolbar.Separator = function(cfg){
34509     
34510     var s = document.createElement("span");
34511     s.className = "ytb-sep";
34512     if (cfg) {
34513         cfg.el = s;
34514     }
34515     
34516     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
34517 };
34518 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
34519     enable:Roo.emptyFn,
34520     disable:Roo.emptyFn,
34521     focus:Roo.emptyFn
34522 });
34523
34524 /**
34525  * @class Roo.Toolbar.Spacer
34526  * @extends Roo.Toolbar.Item
34527  * A simple element that adds extra horizontal space to a toolbar.
34528  * @constructor
34529  * Creates a new Spacer
34530  */
34531 Roo.Toolbar.Spacer = function(cfg){
34532     var s = document.createElement("div");
34533     s.className = "ytb-spacer";
34534     if (cfg) {
34535         cfg.el = s;
34536     }
34537     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
34538 };
34539 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
34540     enable:Roo.emptyFn,
34541     disable:Roo.emptyFn,
34542     focus:Roo.emptyFn
34543 });
34544
34545 /**
34546  * @class Roo.Toolbar.Fill
34547  * @extends Roo.Toolbar.Spacer
34548  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
34549  * @constructor
34550  * Creates a new Spacer
34551  */
34552 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
34553     // private
34554     render : function(td){
34555         td.style.width = '100%';
34556         Roo.Toolbar.Fill.superclass.render.call(this, td);
34557     }
34558 });
34559
34560 /**
34561  * @class Roo.Toolbar.TextItem
34562  * @extends Roo.Toolbar.Item
34563  * A simple class that renders text directly into a toolbar.
34564  * @constructor
34565  * Creates a new TextItem
34566  * @param {String} text
34567  */
34568 Roo.Toolbar.TextItem = function(cfg){
34569     var  text = cfg || "";
34570     if (typeof(cfg) == 'object') {
34571         text = cfg.text || "";
34572     }  else {
34573         cfg = null;
34574     }
34575     var s = document.createElement("span");
34576     s.className = "ytb-text";
34577     s.innerHTML = text;
34578     if (cfg) {
34579         cfg.el  = s;
34580     }
34581     
34582     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
34583 };
34584 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
34585     
34586      
34587     enable:Roo.emptyFn,
34588     disable:Roo.emptyFn,
34589     focus:Roo.emptyFn
34590 });
34591
34592 /**
34593  * @class Roo.Toolbar.Button
34594  * @extends Roo.Button
34595  * A button that renders into a toolbar.
34596  * @constructor
34597  * Creates a new Button
34598  * @param {Object} config A standard {@link Roo.Button} config object
34599  */
34600 Roo.Toolbar.Button = function(config){
34601     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
34602 };
34603 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
34604     render : function(td){
34605         this.td = td;
34606         Roo.Toolbar.Button.superclass.render.call(this, td);
34607     },
34608     
34609     /**
34610      * Removes and destroys this button
34611      */
34612     destroy : function(){
34613         Roo.Toolbar.Button.superclass.destroy.call(this);
34614         this.td.parentNode.removeChild(this.td);
34615     },
34616     
34617     /**
34618      * Shows this button
34619      */
34620     show: function(){
34621         this.hidden = false;
34622         this.td.style.display = "";
34623     },
34624     
34625     /**
34626      * Hides this button
34627      */
34628     hide: function(){
34629         this.hidden = true;
34630         this.td.style.display = "none";
34631     },
34632
34633     /**
34634      * Disables this item
34635      */
34636     disable : function(){
34637         Roo.fly(this.td).addClass("x-item-disabled");
34638         this.disabled = true;
34639     },
34640
34641     /**
34642      * Enables this item
34643      */
34644     enable : function(){
34645         Roo.fly(this.td).removeClass("x-item-disabled");
34646         this.disabled = false;
34647     }
34648 });
34649 // backwards compat
34650 Roo.ToolbarButton = Roo.Toolbar.Button;
34651
34652 /**
34653  * @class Roo.Toolbar.SplitButton
34654  * @extends Roo.SplitButton
34655  * A menu button that renders into a toolbar.
34656  * @constructor
34657  * Creates a new SplitButton
34658  * @param {Object} config A standard {@link Roo.SplitButton} config object
34659  */
34660 Roo.Toolbar.SplitButton = function(config){
34661     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
34662 };
34663 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
34664     render : function(td){
34665         this.td = td;
34666         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
34667     },
34668     
34669     /**
34670      * Removes and destroys this button
34671      */
34672     destroy : function(){
34673         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
34674         this.td.parentNode.removeChild(this.td);
34675     },
34676     
34677     /**
34678      * Shows this button
34679      */
34680     show: function(){
34681         this.hidden = false;
34682         this.td.style.display = "";
34683     },
34684     
34685     /**
34686      * Hides this button
34687      */
34688     hide: function(){
34689         this.hidden = true;
34690         this.td.style.display = "none";
34691     }
34692 });
34693
34694 // backwards compat
34695 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
34696  * Based on:
34697  * Ext JS Library 1.1.1
34698  * Copyright(c) 2006-2007, Ext JS, LLC.
34699  *
34700  * Originally Released Under LGPL - original licence link has changed is not relivant.
34701  *
34702  * Fork - LGPL
34703  * <script type="text/javascript">
34704  */
34705  
34706 /**
34707  * @class Roo.PagingToolbar
34708  * @extends Roo.Toolbar
34709  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
34710  * @constructor
34711  * Create a new PagingToolbar
34712  * @param {Object} config The config object
34713  */
34714 Roo.PagingToolbar = function(el, ds, config)
34715 {
34716     // old args format still supported... - xtype is prefered..
34717     if (typeof(el) == 'object' && el.xtype) {
34718         // created from xtype...
34719         config = el;
34720         ds = el.dataSource;
34721         el = config.container;
34722     }
34723     var items = [];
34724     if (config.items) {
34725         items = config.items;
34726         config.items = [];
34727     }
34728     
34729     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
34730     this.ds = ds;
34731     this.cursor = 0;
34732     this.renderButtons(this.el);
34733     this.bind(ds);
34734     
34735     // supprot items array.
34736    
34737     Roo.each(items, function(e) {
34738         this.add(Roo.factory(e));
34739     },this);
34740     
34741 };
34742
34743 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
34744     /**
34745      * @cfg {Roo.data.Store} dataSource
34746      * The underlying data store providing the paged data
34747      */
34748     /**
34749      * @cfg {String/HTMLElement/Element} container
34750      * container The id or element that will contain the toolbar
34751      */
34752     /**
34753      * @cfg {Boolean} displayInfo
34754      * True to display the displayMsg (defaults to false)
34755      */
34756     /**
34757      * @cfg {Number} pageSize
34758      * The number of records to display per page (defaults to 20)
34759      */
34760     pageSize: 20,
34761     /**
34762      * @cfg {String} displayMsg
34763      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
34764      */
34765     displayMsg : 'Displaying {0} - {1} of {2}',
34766     /**
34767      * @cfg {String} emptyMsg
34768      * The message to display when no records are found (defaults to "No data to display")
34769      */
34770     emptyMsg : 'No data to display',
34771     /**
34772      * Customizable piece of the default paging text (defaults to "Page")
34773      * @type String
34774      */
34775     beforePageText : "Page",
34776     /**
34777      * Customizable piece of the default paging text (defaults to "of %0")
34778      * @type String
34779      */
34780     afterPageText : "of {0}",
34781     /**
34782      * Customizable piece of the default paging text (defaults to "First Page")
34783      * @type String
34784      */
34785     firstText : "First Page",
34786     /**
34787      * Customizable piece of the default paging text (defaults to "Previous Page")
34788      * @type String
34789      */
34790     prevText : "Previous Page",
34791     /**
34792      * Customizable piece of the default paging text (defaults to "Next Page")
34793      * @type String
34794      */
34795     nextText : "Next Page",
34796     /**
34797      * Customizable piece of the default paging text (defaults to "Last Page")
34798      * @type String
34799      */
34800     lastText : "Last Page",
34801     /**
34802      * Customizable piece of the default paging text (defaults to "Refresh")
34803      * @type String
34804      */
34805     refreshText : "Refresh",
34806
34807     // private
34808     renderButtons : function(el){
34809         Roo.PagingToolbar.superclass.render.call(this, el);
34810         this.first = this.addButton({
34811             tooltip: this.firstText,
34812             cls: "x-btn-icon x-grid-page-first",
34813             disabled: true,
34814             handler: this.onClick.createDelegate(this, ["first"])
34815         });
34816         this.prev = this.addButton({
34817             tooltip: this.prevText,
34818             cls: "x-btn-icon x-grid-page-prev",
34819             disabled: true,
34820             handler: this.onClick.createDelegate(this, ["prev"])
34821         });
34822         //this.addSeparator();
34823         this.add(this.beforePageText);
34824         this.field = Roo.get(this.addDom({
34825            tag: "input",
34826            type: "text",
34827            size: "3",
34828            value: "1",
34829            cls: "x-grid-page-number"
34830         }).el);
34831         this.field.on("keydown", this.onPagingKeydown, this);
34832         this.field.on("focus", function(){this.dom.select();});
34833         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
34834         this.field.setHeight(18);
34835         //this.addSeparator();
34836         this.next = this.addButton({
34837             tooltip: this.nextText,
34838             cls: "x-btn-icon x-grid-page-next",
34839             disabled: true,
34840             handler: this.onClick.createDelegate(this, ["next"])
34841         });
34842         this.last = this.addButton({
34843             tooltip: this.lastText,
34844             cls: "x-btn-icon x-grid-page-last",
34845             disabled: true,
34846             handler: this.onClick.createDelegate(this, ["last"])
34847         });
34848         //this.addSeparator();
34849         this.loading = this.addButton({
34850             tooltip: this.refreshText,
34851             cls: "x-btn-icon x-grid-loading",
34852             handler: this.onClick.createDelegate(this, ["refresh"])
34853         });
34854
34855         if(this.displayInfo){
34856             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
34857         }
34858     },
34859
34860     // private
34861     updateInfo : function(){
34862         if(this.displayEl){
34863             var count = this.ds.getCount();
34864             var msg = count == 0 ?
34865                 this.emptyMsg :
34866                 String.format(
34867                     this.displayMsg,
34868                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
34869                 );
34870             this.displayEl.update(msg);
34871         }
34872     },
34873
34874     // private
34875     onLoad : function(ds, r, o){
34876        this.cursor = o.params ? o.params.start : 0;
34877        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
34878
34879        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
34880        this.field.dom.value = ap;
34881        this.first.setDisabled(ap == 1);
34882        this.prev.setDisabled(ap == 1);
34883        this.next.setDisabled(ap == ps);
34884        this.last.setDisabled(ap == ps);
34885        this.loading.enable();
34886        this.updateInfo();
34887     },
34888
34889     // private
34890     getPageData : function(){
34891         var total = this.ds.getTotalCount();
34892         return {
34893             total : total,
34894             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
34895             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
34896         };
34897     },
34898
34899     // private
34900     onLoadError : function(){
34901         this.loading.enable();
34902     },
34903
34904     // private
34905     onPagingKeydown : function(e){
34906         var k = e.getKey();
34907         var d = this.getPageData();
34908         if(k == e.RETURN){
34909             var v = this.field.dom.value, pageNum;
34910             if(!v || isNaN(pageNum = parseInt(v, 10))){
34911                 this.field.dom.value = d.activePage;
34912                 return;
34913             }
34914             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
34915             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
34916             e.stopEvent();
34917         }
34918         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))
34919         {
34920           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
34921           this.field.dom.value = pageNum;
34922           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
34923           e.stopEvent();
34924         }
34925         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
34926         {
34927           var v = this.field.dom.value, pageNum; 
34928           var increment = (e.shiftKey) ? 10 : 1;
34929           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
34930             increment *= -1;
34931           }
34932           if(!v || isNaN(pageNum = parseInt(v, 10))) {
34933             this.field.dom.value = d.activePage;
34934             return;
34935           }
34936           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
34937           {
34938             this.field.dom.value = parseInt(v, 10) + increment;
34939             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
34940             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
34941           }
34942           e.stopEvent();
34943         }
34944     },
34945
34946     // private
34947     beforeLoad : function(){
34948         if(this.loading){
34949             this.loading.disable();
34950         }
34951     },
34952
34953     // private
34954     onClick : function(which){
34955         var ds = this.ds;
34956         switch(which){
34957             case "first":
34958                 ds.load({params:{start: 0, limit: this.pageSize}});
34959             break;
34960             case "prev":
34961                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
34962             break;
34963             case "next":
34964                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
34965             break;
34966             case "last":
34967                 var total = ds.getTotalCount();
34968                 var extra = total % this.pageSize;
34969                 var lastStart = extra ? (total - extra) : total-this.pageSize;
34970                 ds.load({params:{start: lastStart, limit: this.pageSize}});
34971             break;
34972             case "refresh":
34973                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
34974             break;
34975         }
34976     },
34977
34978     /**
34979      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
34980      * @param {Roo.data.Store} store The data store to unbind
34981      */
34982     unbind : function(ds){
34983         ds.un("beforeload", this.beforeLoad, this);
34984         ds.un("load", this.onLoad, this);
34985         ds.un("loadexception", this.onLoadError, this);
34986         ds.un("remove", this.updateInfo, this);
34987         ds.un("add", this.updateInfo, this);
34988         this.ds = undefined;
34989     },
34990
34991     /**
34992      * Binds the paging toolbar to the specified {@link Roo.data.Store}
34993      * @param {Roo.data.Store} store The data store to bind
34994      */
34995     bind : function(ds){
34996         ds.on("beforeload", this.beforeLoad, this);
34997         ds.on("load", this.onLoad, this);
34998         ds.on("loadexception", this.onLoadError, this);
34999         ds.on("remove", this.updateInfo, this);
35000         ds.on("add", this.updateInfo, this);
35001         this.ds = ds;
35002     }
35003 });/*
35004  * Based on:
35005  * Ext JS Library 1.1.1
35006  * Copyright(c) 2006-2007, Ext JS, LLC.
35007  *
35008  * Originally Released Under LGPL - original licence link has changed is not relivant.
35009  *
35010  * Fork - LGPL
35011  * <script type="text/javascript">
35012  */
35013
35014 /**
35015  * @class Roo.Resizable
35016  * @extends Roo.util.Observable
35017  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
35018  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
35019  * 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
35020  * the element will be wrapped for you automatically.</p>
35021  * <p>Here is the list of valid resize handles:</p>
35022  * <pre>
35023 Value   Description
35024 ------  -------------------
35025  'n'     north
35026  's'     south
35027  'e'     east
35028  'w'     west
35029  'nw'    northwest
35030  'sw'    southwest
35031  'se'    southeast
35032  'ne'    northeast
35033  'hd'    horizontal drag
35034  'all'   all
35035 </pre>
35036  * <p>Here's an example showing the creation of a typical Resizable:</p>
35037  * <pre><code>
35038 var resizer = new Roo.Resizable("element-id", {
35039     handles: 'all',
35040     minWidth: 200,
35041     minHeight: 100,
35042     maxWidth: 500,
35043     maxHeight: 400,
35044     pinned: true
35045 });
35046 resizer.on("resize", myHandler);
35047 </code></pre>
35048  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
35049  * resizer.east.setDisplayed(false);</p>
35050  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
35051  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
35052  * resize operation's new size (defaults to [0, 0])
35053  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
35054  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
35055  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
35056  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
35057  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
35058  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
35059  * @cfg {Number} width The width of the element in pixels (defaults to null)
35060  * @cfg {Number} height The height of the element in pixels (defaults to null)
35061  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
35062  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
35063  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
35064  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
35065  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
35066  * in favor of the handles config option (defaults to false)
35067  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
35068  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
35069  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
35070  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
35071  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
35072  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
35073  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
35074  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
35075  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
35076  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
35077  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
35078  * @constructor
35079  * Create a new resizable component
35080  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
35081  * @param {Object} config configuration options
35082   */
35083 Roo.Resizable = function(el, config)
35084 {
35085     this.el = Roo.get(el);
35086
35087     if(config && config.wrap){
35088         config.resizeChild = this.el;
35089         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
35090         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
35091         this.el.setStyle("overflow", "hidden");
35092         this.el.setPositioning(config.resizeChild.getPositioning());
35093         config.resizeChild.clearPositioning();
35094         if(!config.width || !config.height){
35095             var csize = config.resizeChild.getSize();
35096             this.el.setSize(csize.width, csize.height);
35097         }
35098         if(config.pinned && !config.adjustments){
35099             config.adjustments = "auto";
35100         }
35101     }
35102
35103     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
35104     this.proxy.unselectable();
35105     this.proxy.enableDisplayMode('block');
35106
35107     Roo.apply(this, config);
35108
35109     if(this.pinned){
35110         this.disableTrackOver = true;
35111         this.el.addClass("x-resizable-pinned");
35112     }
35113     // if the element isn't positioned, make it relative
35114     var position = this.el.getStyle("position");
35115     if(position != "absolute" && position != "fixed"){
35116         this.el.setStyle("position", "relative");
35117     }
35118     if(!this.handles){ // no handles passed, must be legacy style
35119         this.handles = 's,e,se';
35120         if(this.multiDirectional){
35121             this.handles += ',n,w';
35122         }
35123     }
35124     if(this.handles == "all"){
35125         this.handles = "n s e w ne nw se sw";
35126     }
35127     var hs = this.handles.split(/\s*?[,;]\s*?| /);
35128     var ps = Roo.Resizable.positions;
35129     for(var i = 0, len = hs.length; i < len; i++){
35130         if(hs[i] && ps[hs[i]]){
35131             var pos = ps[hs[i]];
35132             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
35133         }
35134     }
35135     // legacy
35136     this.corner = this.southeast;
35137     
35138     // updateBox = the box can move..
35139     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
35140         this.updateBox = true;
35141     }
35142
35143     this.activeHandle = null;
35144
35145     if(this.resizeChild){
35146         if(typeof this.resizeChild == "boolean"){
35147             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
35148         }else{
35149             this.resizeChild = Roo.get(this.resizeChild, true);
35150         }
35151     }
35152     
35153     if(this.adjustments == "auto"){
35154         var rc = this.resizeChild;
35155         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
35156         if(rc && (hw || hn)){
35157             rc.position("relative");
35158             rc.setLeft(hw ? hw.el.getWidth() : 0);
35159             rc.setTop(hn ? hn.el.getHeight() : 0);
35160         }
35161         this.adjustments = [
35162             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
35163             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
35164         ];
35165     }
35166
35167     if(this.draggable){
35168         this.dd = this.dynamic ?
35169             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
35170         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
35171     }
35172
35173     // public events
35174     this.addEvents({
35175         /**
35176          * @event beforeresize
35177          * Fired before resize is allowed. Set enabled to false to cancel resize.
35178          * @param {Roo.Resizable} this
35179          * @param {Roo.EventObject} e The mousedown event
35180          */
35181         "beforeresize" : true,
35182         /**
35183          * @event resizing
35184          * Fired a resizing.
35185          * @param {Roo.Resizable} this
35186          * @param {Number} x The new x position
35187          * @param {Number} y The new y position
35188          * @param {Number} w The new w width
35189          * @param {Number} h The new h hight
35190          * @param {Roo.EventObject} e The mouseup event
35191          */
35192         "resizing" : true,
35193         /**
35194          * @event resize
35195          * Fired after a resize.
35196          * @param {Roo.Resizable} this
35197          * @param {Number} width The new width
35198          * @param {Number} height The new height
35199          * @param {Roo.EventObject} e The mouseup event
35200          */
35201         "resize" : true
35202     });
35203
35204     if(this.width !== null && this.height !== null){
35205         this.resizeTo(this.width, this.height);
35206     }else{
35207         this.updateChildSize();
35208     }
35209     if(Roo.isIE){
35210         this.el.dom.style.zoom = 1;
35211     }
35212     Roo.Resizable.superclass.constructor.call(this);
35213 };
35214
35215 Roo.extend(Roo.Resizable, Roo.util.Observable, {
35216         resizeChild : false,
35217         adjustments : [0, 0],
35218         minWidth : 5,
35219         minHeight : 5,
35220         maxWidth : 10000,
35221         maxHeight : 10000,
35222         enabled : true,
35223         animate : false,
35224         duration : .35,
35225         dynamic : false,
35226         handles : false,
35227         multiDirectional : false,
35228         disableTrackOver : false,
35229         easing : 'easeOutStrong',
35230         widthIncrement : 0,
35231         heightIncrement : 0,
35232         pinned : false,
35233         width : null,
35234         height : null,
35235         preserveRatio : false,
35236         transparent: false,
35237         minX: 0,
35238         minY: 0,
35239         draggable: false,
35240
35241         /**
35242          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
35243          */
35244         constrainTo: undefined,
35245         /**
35246          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
35247          */
35248         resizeRegion: undefined,
35249
35250
35251     /**
35252      * Perform a manual resize
35253      * @param {Number} width
35254      * @param {Number} height
35255      */
35256     resizeTo : function(width, height){
35257         this.el.setSize(width, height);
35258         this.updateChildSize();
35259         this.fireEvent("resize", this, width, height, null);
35260     },
35261
35262     // private
35263     startSizing : function(e, handle){
35264         this.fireEvent("beforeresize", this, e);
35265         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
35266
35267             if(!this.overlay){
35268                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
35269                 this.overlay.unselectable();
35270                 this.overlay.enableDisplayMode("block");
35271                 this.overlay.on("mousemove", this.onMouseMove, this);
35272                 this.overlay.on("mouseup", this.onMouseUp, this);
35273             }
35274             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
35275
35276             this.resizing = true;
35277             this.startBox = this.el.getBox();
35278             this.startPoint = e.getXY();
35279             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
35280                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
35281
35282             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35283             this.overlay.show();
35284
35285             if(this.constrainTo) {
35286                 var ct = Roo.get(this.constrainTo);
35287                 this.resizeRegion = ct.getRegion().adjust(
35288                     ct.getFrameWidth('t'),
35289                     ct.getFrameWidth('l'),
35290                     -ct.getFrameWidth('b'),
35291                     -ct.getFrameWidth('r')
35292                 );
35293             }
35294
35295             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
35296             this.proxy.show();
35297             this.proxy.setBox(this.startBox);
35298             if(!this.dynamic){
35299                 this.proxy.setStyle('visibility', 'visible');
35300             }
35301         }
35302     },
35303
35304     // private
35305     onMouseDown : function(handle, e){
35306         if(this.enabled){
35307             e.stopEvent();
35308             this.activeHandle = handle;
35309             this.startSizing(e, handle);
35310         }
35311     },
35312
35313     // private
35314     onMouseUp : function(e){
35315         var size = this.resizeElement();
35316         this.resizing = false;
35317         this.handleOut();
35318         this.overlay.hide();
35319         this.proxy.hide();
35320         this.fireEvent("resize", this, size.width, size.height, e);
35321     },
35322
35323     // private
35324     updateChildSize : function(){
35325         
35326         if(this.resizeChild){
35327             var el = this.el;
35328             var child = this.resizeChild;
35329             var adj = this.adjustments;
35330             if(el.dom.offsetWidth){
35331                 var b = el.getSize(true);
35332                 child.setSize(b.width+adj[0], b.height+adj[1]);
35333             }
35334             // Second call here for IE
35335             // The first call enables instant resizing and
35336             // the second call corrects scroll bars if they
35337             // exist
35338             if(Roo.isIE){
35339                 setTimeout(function(){
35340                     if(el.dom.offsetWidth){
35341                         var b = el.getSize(true);
35342                         child.setSize(b.width+adj[0], b.height+adj[1]);
35343                     }
35344                 }, 10);
35345             }
35346         }
35347     },
35348
35349     // private
35350     snap : function(value, inc, min){
35351         if(!inc || !value) {
35352             return value;
35353         }
35354         var newValue = value;
35355         var m = value % inc;
35356         if(m > 0){
35357             if(m > (inc/2)){
35358                 newValue = value + (inc-m);
35359             }else{
35360                 newValue = value - m;
35361             }
35362         }
35363         return Math.max(min, newValue);
35364     },
35365
35366     // private
35367     resizeElement : function(){
35368         var box = this.proxy.getBox();
35369         if(this.updateBox){
35370             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
35371         }else{
35372             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
35373         }
35374         this.updateChildSize();
35375         if(!this.dynamic){
35376             this.proxy.hide();
35377         }
35378         return box;
35379     },
35380
35381     // private
35382     constrain : function(v, diff, m, mx){
35383         if(v - diff < m){
35384             diff = v - m;
35385         }else if(v - diff > mx){
35386             diff = mx - v;
35387         }
35388         return diff;
35389     },
35390
35391     // private
35392     onMouseMove : function(e){
35393         
35394         if(this.enabled){
35395             try{// try catch so if something goes wrong the user doesn't get hung
35396
35397             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
35398                 return;
35399             }
35400
35401             //var curXY = this.startPoint;
35402             var curSize = this.curSize || this.startBox;
35403             var x = this.startBox.x, y = this.startBox.y;
35404             var ox = x, oy = y;
35405             var w = curSize.width, h = curSize.height;
35406             var ow = w, oh = h;
35407             var mw = this.minWidth, mh = this.minHeight;
35408             var mxw = this.maxWidth, mxh = this.maxHeight;
35409             var wi = this.widthIncrement;
35410             var hi = this.heightIncrement;
35411
35412             var eventXY = e.getXY();
35413             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
35414             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
35415
35416             var pos = this.activeHandle.position;
35417
35418             switch(pos){
35419                 case "east":
35420                     w += diffX;
35421                     w = Math.min(Math.max(mw, w), mxw);
35422                     break;
35423              
35424                 case "south":
35425                     h += diffY;
35426                     h = Math.min(Math.max(mh, h), mxh);
35427                     break;
35428                 case "southeast":
35429                     w += diffX;
35430                     h += diffY;
35431                     w = Math.min(Math.max(mw, w), mxw);
35432                     h = Math.min(Math.max(mh, h), mxh);
35433                     break;
35434                 case "north":
35435                     diffY = this.constrain(h, diffY, mh, mxh);
35436                     y += diffY;
35437                     h -= diffY;
35438                     break;
35439                 case "hdrag":
35440                     
35441                     if (wi) {
35442                         var adiffX = Math.abs(diffX);
35443                         var sub = (adiffX % wi); // how much 
35444                         if (sub > (wi/2)) { // far enough to snap
35445                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
35446                         } else {
35447                             // remove difference.. 
35448                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
35449                         }
35450                     }
35451                     x += diffX;
35452                     x = Math.max(this.minX, x);
35453                     break;
35454                 case "west":
35455                     diffX = this.constrain(w, diffX, mw, mxw);
35456                     x += diffX;
35457                     w -= diffX;
35458                     break;
35459                 case "northeast":
35460                     w += diffX;
35461                     w = Math.min(Math.max(mw, w), mxw);
35462                     diffY = this.constrain(h, diffY, mh, mxh);
35463                     y += diffY;
35464                     h -= diffY;
35465                     break;
35466                 case "northwest":
35467                     diffX = this.constrain(w, diffX, mw, mxw);
35468                     diffY = this.constrain(h, diffY, mh, mxh);
35469                     y += diffY;
35470                     h -= diffY;
35471                     x += diffX;
35472                     w -= diffX;
35473                     break;
35474                case "southwest":
35475                     diffX = this.constrain(w, diffX, mw, mxw);
35476                     h += diffY;
35477                     h = Math.min(Math.max(mh, h), mxh);
35478                     x += diffX;
35479                     w -= diffX;
35480                     break;
35481             }
35482
35483             var sw = this.snap(w, wi, mw);
35484             var sh = this.snap(h, hi, mh);
35485             if(sw != w || sh != h){
35486                 switch(pos){
35487                     case "northeast":
35488                         y -= sh - h;
35489                     break;
35490                     case "north":
35491                         y -= sh - h;
35492                         break;
35493                     case "southwest":
35494                         x -= sw - w;
35495                     break;
35496                     case "west":
35497                         x -= sw - w;
35498                         break;
35499                     case "northwest":
35500                         x -= sw - w;
35501                         y -= sh - h;
35502                     break;
35503                 }
35504                 w = sw;
35505                 h = sh;
35506             }
35507
35508             if(this.preserveRatio){
35509                 switch(pos){
35510                     case "southeast":
35511                     case "east":
35512                         h = oh * (w/ow);
35513                         h = Math.min(Math.max(mh, h), mxh);
35514                         w = ow * (h/oh);
35515                        break;
35516                     case "south":
35517                         w = ow * (h/oh);
35518                         w = Math.min(Math.max(mw, w), mxw);
35519                         h = oh * (w/ow);
35520                         break;
35521                     case "northeast":
35522                         w = ow * (h/oh);
35523                         w = Math.min(Math.max(mw, w), mxw);
35524                         h = oh * (w/ow);
35525                     break;
35526                     case "north":
35527                         var tw = w;
35528                         w = ow * (h/oh);
35529                         w = Math.min(Math.max(mw, w), mxw);
35530                         h = oh * (w/ow);
35531                         x += (tw - w) / 2;
35532                         break;
35533                     case "southwest":
35534                         h = oh * (w/ow);
35535                         h = Math.min(Math.max(mh, h), mxh);
35536                         var tw = w;
35537                         w = ow * (h/oh);
35538                         x += tw - w;
35539                         break;
35540                     case "west":
35541                         var th = h;
35542                         h = oh * (w/ow);
35543                         h = Math.min(Math.max(mh, h), mxh);
35544                         y += (th - h) / 2;
35545                         var tw = w;
35546                         w = ow * (h/oh);
35547                         x += tw - w;
35548                        break;
35549                     case "northwest":
35550                         var tw = w;
35551                         var th = h;
35552                         h = oh * (w/ow);
35553                         h = Math.min(Math.max(mh, h), mxh);
35554                         w = ow * (h/oh);
35555                         y += th - h;
35556                         x += tw - w;
35557                        break;
35558
35559                 }
35560             }
35561             if (pos == 'hdrag') {
35562                 w = ow;
35563             }
35564             this.proxy.setBounds(x, y, w, h);
35565             if(this.dynamic){
35566                 this.resizeElement();
35567             }
35568             }catch(e){}
35569         }
35570         this.fireEvent("resizing", this, x, y, w, h, e);
35571     },
35572
35573     // private
35574     handleOver : function(){
35575         if(this.enabled){
35576             this.el.addClass("x-resizable-over");
35577         }
35578     },
35579
35580     // private
35581     handleOut : function(){
35582         if(!this.resizing){
35583             this.el.removeClass("x-resizable-over");
35584         }
35585     },
35586
35587     /**
35588      * Returns the element this component is bound to.
35589      * @return {Roo.Element}
35590      */
35591     getEl : function(){
35592         return this.el;
35593     },
35594
35595     /**
35596      * Returns the resizeChild element (or null).
35597      * @return {Roo.Element}
35598      */
35599     getResizeChild : function(){
35600         return this.resizeChild;
35601     },
35602     groupHandler : function()
35603     {
35604         
35605     },
35606     /**
35607      * Destroys this resizable. If the element was wrapped and
35608      * removeEl is not true then the element remains.
35609      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
35610      */
35611     destroy : function(removeEl){
35612         this.proxy.remove();
35613         if(this.overlay){
35614             this.overlay.removeAllListeners();
35615             this.overlay.remove();
35616         }
35617         var ps = Roo.Resizable.positions;
35618         for(var k in ps){
35619             if(typeof ps[k] != "function" && this[ps[k]]){
35620                 var h = this[ps[k]];
35621                 h.el.removeAllListeners();
35622                 h.el.remove();
35623             }
35624         }
35625         if(removeEl){
35626             this.el.update("");
35627             this.el.remove();
35628         }
35629     }
35630 });
35631
35632 // private
35633 // hash to map config positions to true positions
35634 Roo.Resizable.positions = {
35635     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
35636     hd: "hdrag"
35637 };
35638
35639 // private
35640 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
35641     if(!this.tpl){
35642         // only initialize the template if resizable is used
35643         var tpl = Roo.DomHelper.createTemplate(
35644             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
35645         );
35646         tpl.compile();
35647         Roo.Resizable.Handle.prototype.tpl = tpl;
35648     }
35649     this.position = pos;
35650     this.rz = rz;
35651     // show north drag fro topdra
35652     var handlepos = pos == 'hdrag' ? 'north' : pos;
35653     
35654     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
35655     if (pos == 'hdrag') {
35656         this.el.setStyle('cursor', 'pointer');
35657     }
35658     this.el.unselectable();
35659     if(transparent){
35660         this.el.setOpacity(0);
35661     }
35662     this.el.on("mousedown", this.onMouseDown, this);
35663     if(!disableTrackOver){
35664         this.el.on("mouseover", this.onMouseOver, this);
35665         this.el.on("mouseout", this.onMouseOut, this);
35666     }
35667 };
35668
35669 // private
35670 Roo.Resizable.Handle.prototype = {
35671     afterResize : function(rz){
35672         Roo.log('after?');
35673         // do nothing
35674     },
35675     // private
35676     onMouseDown : function(e){
35677         this.rz.onMouseDown(this, e);
35678     },
35679     // private
35680     onMouseOver : function(e){
35681         this.rz.handleOver(this, e);
35682     },
35683     // private
35684     onMouseOut : function(e){
35685         this.rz.handleOut(this, e);
35686     }
35687 };/*
35688  * Based on:
35689  * Ext JS Library 1.1.1
35690  * Copyright(c) 2006-2007, Ext JS, LLC.
35691  *
35692  * Originally Released Under LGPL - original licence link has changed is not relivant.
35693  *
35694  * Fork - LGPL
35695  * <script type="text/javascript">
35696  */
35697
35698 /**
35699  * @class Roo.Editor
35700  * @extends Roo.Component
35701  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
35702  * @constructor
35703  * Create a new Editor
35704  * @param {Roo.form.Field} field The Field object (or descendant)
35705  * @param {Object} config The config object
35706  */
35707 Roo.Editor = function(field, config){
35708     Roo.Editor.superclass.constructor.call(this, config);
35709     this.field = field;
35710     this.addEvents({
35711         /**
35712              * @event beforestartedit
35713              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35714              * false from the handler of this event.
35715              * @param {Editor} this
35716              * @param {Roo.Element} boundEl The underlying element bound to this editor
35717              * @param {Mixed} value The field value being set
35718              */
35719         "beforestartedit" : true,
35720         /**
35721              * @event startedit
35722              * Fires when this editor is displayed
35723              * @param {Roo.Element} boundEl The underlying element bound to this editor
35724              * @param {Mixed} value The starting field value
35725              */
35726         "startedit" : true,
35727         /**
35728              * @event beforecomplete
35729              * Fires after a change has been made to the field, but before the change is reflected in the underlying
35730              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
35731              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
35732              * event will not fire since no edit actually occurred.
35733              * @param {Editor} this
35734              * @param {Mixed} value The current field value
35735              * @param {Mixed} startValue The original field value
35736              */
35737         "beforecomplete" : true,
35738         /**
35739              * @event complete
35740              * Fires after editing is complete and any changed value has been written to the underlying field.
35741              * @param {Editor} this
35742              * @param {Mixed} value The current field value
35743              * @param {Mixed} startValue The original field value
35744              */
35745         "complete" : true,
35746         /**
35747          * @event specialkey
35748          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35749          * {@link Roo.EventObject#getKey} to determine which key was pressed.
35750          * @param {Roo.form.Field} this
35751          * @param {Roo.EventObject} e The event object
35752          */
35753         "specialkey" : true
35754     });
35755 };
35756
35757 Roo.extend(Roo.Editor, Roo.Component, {
35758     /**
35759      * @cfg {Boolean/String} autosize
35760      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
35761      * or "height" to adopt the height only (defaults to false)
35762      */
35763     /**
35764      * @cfg {Boolean} revertInvalid
35765      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
35766      * validation fails (defaults to true)
35767      */
35768     /**
35769      * @cfg {Boolean} ignoreNoChange
35770      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
35771      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
35772      * will never be ignored.
35773      */
35774     /**
35775      * @cfg {Boolean} hideEl
35776      * False to keep the bound element visible while the editor is displayed (defaults to true)
35777      */
35778     /**
35779      * @cfg {Mixed} value
35780      * The data value of the underlying field (defaults to "")
35781      */
35782     value : "",
35783     /**
35784      * @cfg {String} alignment
35785      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
35786      */
35787     alignment: "c-c?",
35788     /**
35789      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
35790      * for bottom-right shadow (defaults to "frame")
35791      */
35792     shadow : "frame",
35793     /**
35794      * @cfg {Boolean} constrain True to constrain the editor to the viewport
35795      */
35796     constrain : false,
35797     /**
35798      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
35799      */
35800     completeOnEnter : false,
35801     /**
35802      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
35803      */
35804     cancelOnEsc : false,
35805     /**
35806      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
35807      */
35808     updateEl : false,
35809
35810     // private
35811     onRender : function(ct, position){
35812         this.el = new Roo.Layer({
35813             shadow: this.shadow,
35814             cls: "x-editor",
35815             parentEl : ct,
35816             shim : this.shim,
35817             shadowOffset:4,
35818             id: this.id,
35819             constrain: this.constrain
35820         });
35821         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
35822         if(this.field.msgTarget != 'title'){
35823             this.field.msgTarget = 'qtip';
35824         }
35825         this.field.render(this.el);
35826         if(Roo.isGecko){
35827             this.field.el.dom.setAttribute('autocomplete', 'off');
35828         }
35829         this.field.on("specialkey", this.onSpecialKey, this);
35830         if(this.swallowKeys){
35831             this.field.el.swallowEvent(['keydown','keypress']);
35832         }
35833         this.field.show();
35834         this.field.on("blur", this.onBlur, this);
35835         if(this.field.grow){
35836             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
35837         }
35838     },
35839
35840     onSpecialKey : function(field, e)
35841     {
35842         //Roo.log('editor onSpecialKey');
35843         if(this.completeOnEnter && e.getKey() == e.ENTER){
35844             e.stopEvent();
35845             this.completeEdit();
35846             return;
35847         }
35848         // do not fire special key otherwise it might hide close the editor...
35849         if(e.getKey() == e.ENTER){    
35850             return;
35851         }
35852         if(this.cancelOnEsc && e.getKey() == e.ESC){
35853             this.cancelEdit();
35854             return;
35855         } 
35856         this.fireEvent('specialkey', field, e);
35857     
35858     },
35859
35860     /**
35861      * Starts the editing process and shows the editor.
35862      * @param {String/HTMLElement/Element} el The element to edit
35863      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
35864       * to the innerHTML of el.
35865      */
35866     startEdit : function(el, value){
35867         if(this.editing){
35868             this.completeEdit();
35869         }
35870         this.boundEl = Roo.get(el);
35871         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
35872         if(!this.rendered){
35873             this.render(this.parentEl || document.body);
35874         }
35875         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
35876             return;
35877         }
35878         this.startValue = v;
35879         this.field.setValue(v);
35880         if(this.autoSize){
35881             var sz = this.boundEl.getSize();
35882             switch(this.autoSize){
35883                 case "width":
35884                 this.setSize(sz.width,  "");
35885                 break;
35886                 case "height":
35887                 this.setSize("",  sz.height);
35888                 break;
35889                 default:
35890                 this.setSize(sz.width,  sz.height);
35891             }
35892         }
35893         this.el.alignTo(this.boundEl, this.alignment);
35894         this.editing = true;
35895         if(Roo.QuickTips){
35896             Roo.QuickTips.disable();
35897         }
35898         this.show();
35899     },
35900
35901     /**
35902      * Sets the height and width of this editor.
35903      * @param {Number} width The new width
35904      * @param {Number} height The new height
35905      */
35906     setSize : function(w, h){
35907         this.field.setSize(w, h);
35908         if(this.el){
35909             this.el.sync();
35910         }
35911     },
35912
35913     /**
35914      * Realigns the editor to the bound field based on the current alignment config value.
35915      */
35916     realign : function(){
35917         this.el.alignTo(this.boundEl, this.alignment);
35918     },
35919
35920     /**
35921      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
35922      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
35923      */
35924     completeEdit : function(remainVisible){
35925         if(!this.editing){
35926             return;
35927         }
35928         var v = this.getValue();
35929         if(this.revertInvalid !== false && !this.field.isValid()){
35930             v = this.startValue;
35931             this.cancelEdit(true);
35932         }
35933         if(String(v) === String(this.startValue) && this.ignoreNoChange){
35934             this.editing = false;
35935             this.hide();
35936             return;
35937         }
35938         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
35939             this.editing = false;
35940             if(this.updateEl && this.boundEl){
35941                 this.boundEl.update(v);
35942             }
35943             if(remainVisible !== true){
35944                 this.hide();
35945             }
35946             this.fireEvent("complete", this, v, this.startValue);
35947         }
35948     },
35949
35950     // private
35951     onShow : function(){
35952         this.el.show();
35953         if(this.hideEl !== false){
35954             this.boundEl.hide();
35955         }
35956         this.field.show();
35957         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
35958             this.fixIEFocus = true;
35959             this.deferredFocus.defer(50, this);
35960         }else{
35961             this.field.focus();
35962         }
35963         this.fireEvent("startedit", this.boundEl, this.startValue);
35964     },
35965
35966     deferredFocus : function(){
35967         if(this.editing){
35968             this.field.focus();
35969         }
35970     },
35971
35972     /**
35973      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
35974      * reverted to the original starting value.
35975      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
35976      * cancel (defaults to false)
35977      */
35978     cancelEdit : function(remainVisible){
35979         if(this.editing){
35980             this.setValue(this.startValue);
35981             if(remainVisible !== true){
35982                 this.hide();
35983             }
35984         }
35985     },
35986
35987     // private
35988     onBlur : function(){
35989         if(this.allowBlur !== true && this.editing){
35990             this.completeEdit();
35991         }
35992     },
35993
35994     // private
35995     onHide : function(){
35996         if(this.editing){
35997             this.completeEdit();
35998             return;
35999         }
36000         this.field.blur();
36001         if(this.field.collapse){
36002             this.field.collapse();
36003         }
36004         this.el.hide();
36005         if(this.hideEl !== false){
36006             this.boundEl.show();
36007         }
36008         if(Roo.QuickTips){
36009             Roo.QuickTips.enable();
36010         }
36011     },
36012
36013     /**
36014      * Sets the data value of the editor
36015      * @param {Mixed} value Any valid value supported by the underlying field
36016      */
36017     setValue : function(v){
36018         this.field.setValue(v);
36019     },
36020
36021     /**
36022      * Gets the data value of the editor
36023      * @return {Mixed} The data value
36024      */
36025     getValue : function(){
36026         return this.field.getValue();
36027     }
36028 });/*
36029  * Based on:
36030  * Ext JS Library 1.1.1
36031  * Copyright(c) 2006-2007, Ext JS, LLC.
36032  *
36033  * Originally Released Under LGPL - original licence link has changed is not relivant.
36034  *
36035  * Fork - LGPL
36036  * <script type="text/javascript">
36037  */
36038  
36039 /**
36040  * @class Roo.BasicDialog
36041  * @extends Roo.util.Observable
36042  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
36043  * <pre><code>
36044 var dlg = new Roo.BasicDialog("my-dlg", {
36045     height: 200,
36046     width: 300,
36047     minHeight: 100,
36048     minWidth: 150,
36049     modal: true,
36050     proxyDrag: true,
36051     shadow: true
36052 });
36053 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
36054 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
36055 dlg.addButton('Cancel', dlg.hide, dlg);
36056 dlg.show();
36057 </code></pre>
36058   <b>A Dialog should always be a direct child of the body element.</b>
36059  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
36060  * @cfg {String} title Default text to display in the title bar (defaults to null)
36061  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
36062  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
36063  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
36064  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
36065  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
36066  * (defaults to null with no animation)
36067  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
36068  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
36069  * property for valid values (defaults to 'all')
36070  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
36071  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
36072  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
36073  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
36074  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
36075  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
36076  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
36077  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
36078  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
36079  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
36080  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
36081  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
36082  * draggable = true (defaults to false)
36083  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
36084  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
36085  * shadow (defaults to false)
36086  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
36087  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
36088  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
36089  * @cfg {Array} buttons Array of buttons
36090  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
36091  * @constructor
36092  * Create a new BasicDialog.
36093  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
36094  * @param {Object} config Configuration options
36095  */
36096 Roo.BasicDialog = function(el, config){
36097     this.el = Roo.get(el);
36098     var dh = Roo.DomHelper;
36099     if(!this.el && config && config.autoCreate){
36100         if(typeof config.autoCreate == "object"){
36101             if(!config.autoCreate.id){
36102                 config.autoCreate.id = el;
36103             }
36104             this.el = dh.append(document.body,
36105                         config.autoCreate, true);
36106         }else{
36107             this.el = dh.append(document.body,
36108                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
36109         }
36110     }
36111     el = this.el;
36112     el.setDisplayed(true);
36113     el.hide = this.hideAction;
36114     this.id = el.id;
36115     el.addClass("x-dlg");
36116
36117     Roo.apply(this, config);
36118
36119     this.proxy = el.createProxy("x-dlg-proxy");
36120     this.proxy.hide = this.hideAction;
36121     this.proxy.setOpacity(.5);
36122     this.proxy.hide();
36123
36124     if(config.width){
36125         el.setWidth(config.width);
36126     }
36127     if(config.height){
36128         el.setHeight(config.height);
36129     }
36130     this.size = el.getSize();
36131     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
36132         this.xy = [config.x,config.y];
36133     }else{
36134         this.xy = el.getCenterXY(true);
36135     }
36136     /** The header element @type Roo.Element */
36137     this.header = el.child("> .x-dlg-hd");
36138     /** The body element @type Roo.Element */
36139     this.body = el.child("> .x-dlg-bd");
36140     /** The footer element @type Roo.Element */
36141     this.footer = el.child("> .x-dlg-ft");
36142
36143     if(!this.header){
36144         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
36145     }
36146     if(!this.body){
36147         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
36148     }
36149
36150     this.header.unselectable();
36151     if(this.title){
36152         this.header.update(this.title);
36153     }
36154     // this element allows the dialog to be focused for keyboard event
36155     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
36156     this.focusEl.swallowEvent("click", true);
36157
36158     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
36159
36160     // wrap the body and footer for special rendering
36161     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
36162     if(this.footer){
36163         this.bwrap.dom.appendChild(this.footer.dom);
36164     }
36165
36166     this.bg = this.el.createChild({
36167         tag: "div", cls:"x-dlg-bg",
36168         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
36169     });
36170     this.centerBg = this.bg.child("div.x-dlg-bg-center");
36171
36172
36173     if(this.autoScroll !== false && !this.autoTabs){
36174         this.body.setStyle("overflow", "auto");
36175     }
36176
36177     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
36178
36179     if(this.closable !== false){
36180         this.el.addClass("x-dlg-closable");
36181         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
36182         this.close.on("click", this.closeClick, this);
36183         this.close.addClassOnOver("x-dlg-close-over");
36184     }
36185     if(this.collapsible !== false){
36186         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
36187         this.collapseBtn.on("click", this.collapseClick, this);
36188         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
36189         this.header.on("dblclick", this.collapseClick, this);
36190     }
36191     if(this.resizable !== false){
36192         this.el.addClass("x-dlg-resizable");
36193         this.resizer = new Roo.Resizable(el, {
36194             minWidth: this.minWidth || 80,
36195             minHeight:this.minHeight || 80,
36196             handles: this.resizeHandles || "all",
36197             pinned: true
36198         });
36199         this.resizer.on("beforeresize", this.beforeResize, this);
36200         this.resizer.on("resize", this.onResize, this);
36201     }
36202     if(this.draggable !== false){
36203         el.addClass("x-dlg-draggable");
36204         if (!this.proxyDrag) {
36205             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
36206         }
36207         else {
36208             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
36209         }
36210         dd.setHandleElId(this.header.id);
36211         dd.endDrag = this.endMove.createDelegate(this);
36212         dd.startDrag = this.startMove.createDelegate(this);
36213         dd.onDrag = this.onDrag.createDelegate(this);
36214         dd.scroll = false;
36215         this.dd = dd;
36216     }
36217     if(this.modal){
36218         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
36219         this.mask.enableDisplayMode("block");
36220         this.mask.hide();
36221         this.el.addClass("x-dlg-modal");
36222     }
36223     if(this.shadow){
36224         this.shadow = new Roo.Shadow({
36225             mode : typeof this.shadow == "string" ? this.shadow : "sides",
36226             offset : this.shadowOffset
36227         });
36228     }else{
36229         this.shadowOffset = 0;
36230     }
36231     if(Roo.useShims && this.shim !== false){
36232         this.shim = this.el.createShim();
36233         this.shim.hide = this.hideAction;
36234         this.shim.hide();
36235     }else{
36236         this.shim = false;
36237     }
36238     if(this.autoTabs){
36239         this.initTabs();
36240     }
36241     if (this.buttons) { 
36242         var bts= this.buttons;
36243         this.buttons = [];
36244         Roo.each(bts, function(b) {
36245             this.addButton(b);
36246         }, this);
36247     }
36248     
36249     
36250     this.addEvents({
36251         /**
36252          * @event keydown
36253          * Fires when a key is pressed
36254          * @param {Roo.BasicDialog} this
36255          * @param {Roo.EventObject} e
36256          */
36257         "keydown" : true,
36258         /**
36259          * @event move
36260          * Fires when this dialog is moved by the user.
36261          * @param {Roo.BasicDialog} this
36262          * @param {Number} x The new page X
36263          * @param {Number} y The new page Y
36264          */
36265         "move" : true,
36266         /**
36267          * @event resize
36268          * Fires when this dialog is resized by the user.
36269          * @param {Roo.BasicDialog} this
36270          * @param {Number} width The new width
36271          * @param {Number} height The new height
36272          */
36273         "resize" : true,
36274         /**
36275          * @event beforehide
36276          * Fires before this dialog is hidden.
36277          * @param {Roo.BasicDialog} this
36278          */
36279         "beforehide" : true,
36280         /**
36281          * @event hide
36282          * Fires when this dialog is hidden.
36283          * @param {Roo.BasicDialog} this
36284          */
36285         "hide" : true,
36286         /**
36287          * @event beforeshow
36288          * Fires before this dialog is shown.
36289          * @param {Roo.BasicDialog} this
36290          */
36291         "beforeshow" : true,
36292         /**
36293          * @event show
36294          * Fires when this dialog is shown.
36295          * @param {Roo.BasicDialog} this
36296          */
36297         "show" : true
36298     });
36299     el.on("keydown", this.onKeyDown, this);
36300     el.on("mousedown", this.toFront, this);
36301     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
36302     this.el.hide();
36303     Roo.DialogManager.register(this);
36304     Roo.BasicDialog.superclass.constructor.call(this);
36305 };
36306
36307 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
36308     shadowOffset: Roo.isIE ? 6 : 5,
36309     minHeight: 80,
36310     minWidth: 200,
36311     minButtonWidth: 75,
36312     defaultButton: null,
36313     buttonAlign: "right",
36314     tabTag: 'div',
36315     firstShow: true,
36316
36317     /**
36318      * Sets the dialog title text
36319      * @param {String} text The title text to display
36320      * @return {Roo.BasicDialog} this
36321      */
36322     setTitle : function(text){
36323         this.header.update(text);
36324         return this;
36325     },
36326
36327     // private
36328     closeClick : function(){
36329         this.hide();
36330     },
36331
36332     // private
36333     collapseClick : function(){
36334         this[this.collapsed ? "expand" : "collapse"]();
36335     },
36336
36337     /**
36338      * Collapses the dialog to its minimized state (only the title bar is visible).
36339      * Equivalent to the user clicking the collapse dialog button.
36340      */
36341     collapse : function(){
36342         if(!this.collapsed){
36343             this.collapsed = true;
36344             this.el.addClass("x-dlg-collapsed");
36345             this.restoreHeight = this.el.getHeight();
36346             this.resizeTo(this.el.getWidth(), this.header.getHeight());
36347         }
36348     },
36349
36350     /**
36351      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
36352      * clicking the expand dialog button.
36353      */
36354     expand : function(){
36355         if(this.collapsed){
36356             this.collapsed = false;
36357             this.el.removeClass("x-dlg-collapsed");
36358             this.resizeTo(this.el.getWidth(), this.restoreHeight);
36359         }
36360     },
36361
36362     /**
36363      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
36364      * @return {Roo.TabPanel} The tabs component
36365      */
36366     initTabs : function(){
36367         var tabs = this.getTabs();
36368         while(tabs.getTab(0)){
36369             tabs.removeTab(0);
36370         }
36371         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
36372             var dom = el.dom;
36373             tabs.addTab(Roo.id(dom), dom.title);
36374             dom.title = "";
36375         });
36376         tabs.activate(0);
36377         return tabs;
36378     },
36379
36380     // private
36381     beforeResize : function(){
36382         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
36383     },
36384
36385     // private
36386     onResize : function(){
36387         this.refreshSize();
36388         this.syncBodyHeight();
36389         this.adjustAssets();
36390         this.focus();
36391         this.fireEvent("resize", this, this.size.width, this.size.height);
36392     },
36393
36394     // private
36395     onKeyDown : function(e){
36396         if(this.isVisible()){
36397             this.fireEvent("keydown", this, e);
36398         }
36399     },
36400
36401     /**
36402      * Resizes the dialog.
36403      * @param {Number} width
36404      * @param {Number} height
36405      * @return {Roo.BasicDialog} this
36406      */
36407     resizeTo : function(width, height){
36408         this.el.setSize(width, height);
36409         this.size = {width: width, height: height};
36410         this.syncBodyHeight();
36411         if(this.fixedcenter){
36412             this.center();
36413         }
36414         if(this.isVisible()){
36415             this.constrainXY();
36416             this.adjustAssets();
36417         }
36418         this.fireEvent("resize", this, width, height);
36419         return this;
36420     },
36421
36422
36423     /**
36424      * Resizes the dialog to fit the specified content size.
36425      * @param {Number} width
36426      * @param {Number} height
36427      * @return {Roo.BasicDialog} this
36428      */
36429     setContentSize : function(w, h){
36430         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
36431         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
36432         //if(!this.el.isBorderBox()){
36433             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
36434             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
36435         //}
36436         if(this.tabs){
36437             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
36438             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
36439         }
36440         this.resizeTo(w, h);
36441         return this;
36442     },
36443
36444     /**
36445      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
36446      * executed in response to a particular key being pressed while the dialog is active.
36447      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
36448      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
36449      * @param {Function} fn The function to call
36450      * @param {Object} scope (optional) The scope of the function
36451      * @return {Roo.BasicDialog} this
36452      */
36453     addKeyListener : function(key, fn, scope){
36454         var keyCode, shift, ctrl, alt;
36455         if(typeof key == "object" && !(key instanceof Array)){
36456             keyCode = key["key"];
36457             shift = key["shift"];
36458             ctrl = key["ctrl"];
36459             alt = key["alt"];
36460         }else{
36461             keyCode = key;
36462         }
36463         var handler = function(dlg, e){
36464             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
36465                 var k = e.getKey();
36466                 if(keyCode instanceof Array){
36467                     for(var i = 0, len = keyCode.length; i < len; i++){
36468                         if(keyCode[i] == k){
36469                           fn.call(scope || window, dlg, k, e);
36470                           return;
36471                         }
36472                     }
36473                 }else{
36474                     if(k == keyCode){
36475                         fn.call(scope || window, dlg, k, e);
36476                     }
36477                 }
36478             }
36479         };
36480         this.on("keydown", handler);
36481         return this;
36482     },
36483
36484     /**
36485      * Returns the TabPanel component (creates it if it doesn't exist).
36486      * Note: If you wish to simply check for the existence of tabs without creating them,
36487      * check for a null 'tabs' property.
36488      * @return {Roo.TabPanel} The tabs component
36489      */
36490     getTabs : function(){
36491         if(!this.tabs){
36492             this.el.addClass("x-dlg-auto-tabs");
36493             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
36494             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
36495         }
36496         return this.tabs;
36497     },
36498
36499     /**
36500      * Adds a button to the footer section of the dialog.
36501      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
36502      * object or a valid Roo.DomHelper element config
36503      * @param {Function} handler The function called when the button is clicked
36504      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
36505      * @return {Roo.Button} The new button
36506      */
36507     addButton : function(config, handler, scope){
36508         var dh = Roo.DomHelper;
36509         if(!this.footer){
36510             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
36511         }
36512         if(!this.btnContainer){
36513             var tb = this.footer.createChild({
36514
36515                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
36516                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
36517             }, null, true);
36518             this.btnContainer = tb.firstChild.firstChild.firstChild;
36519         }
36520         var bconfig = {
36521             handler: handler,
36522             scope: scope,
36523             minWidth: this.minButtonWidth,
36524             hideParent:true
36525         };
36526         if(typeof config == "string"){
36527             bconfig.text = config;
36528         }else{
36529             if(config.tag){
36530                 bconfig.dhconfig = config;
36531             }else{
36532                 Roo.apply(bconfig, config);
36533             }
36534         }
36535         var fc = false;
36536         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
36537             bconfig.position = Math.max(0, bconfig.position);
36538             fc = this.btnContainer.childNodes[bconfig.position];
36539         }
36540          
36541         var btn = new Roo.Button(
36542             fc ? 
36543                 this.btnContainer.insertBefore(document.createElement("td"),fc)
36544                 : this.btnContainer.appendChild(document.createElement("td")),
36545             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
36546             bconfig
36547         );
36548         this.syncBodyHeight();
36549         if(!this.buttons){
36550             /**
36551              * Array of all the buttons that have been added to this dialog via addButton
36552              * @type Array
36553              */
36554             this.buttons = [];
36555         }
36556         this.buttons.push(btn);
36557         return btn;
36558     },
36559
36560     /**
36561      * Sets the default button to be focused when the dialog is displayed.
36562      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
36563      * @return {Roo.BasicDialog} this
36564      */
36565     setDefaultButton : function(btn){
36566         this.defaultButton = btn;
36567         return this;
36568     },
36569
36570     // private
36571     getHeaderFooterHeight : function(safe){
36572         var height = 0;
36573         if(this.header){
36574            height += this.header.getHeight();
36575         }
36576         if(this.footer){
36577            var fm = this.footer.getMargins();
36578             height += (this.footer.getHeight()+fm.top+fm.bottom);
36579         }
36580         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
36581         height += this.centerBg.getPadding("tb");
36582         return height;
36583     },
36584
36585     // private
36586     syncBodyHeight : function()
36587     {
36588         var bd = this.body, // the text
36589             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
36590             bw = this.bwrap;
36591         var height = this.size.height - this.getHeaderFooterHeight(false);
36592         bd.setHeight(height-bd.getMargins("tb"));
36593         var hh = this.header.getHeight();
36594         var h = this.size.height-hh;
36595         cb.setHeight(h);
36596         
36597         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
36598         bw.setHeight(h-cb.getPadding("tb"));
36599         
36600         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
36601         bd.setWidth(bw.getWidth(true));
36602         if(this.tabs){
36603             this.tabs.syncHeight();
36604             if(Roo.isIE){
36605                 this.tabs.el.repaint();
36606             }
36607         }
36608     },
36609
36610     /**
36611      * Restores the previous state of the dialog if Roo.state is configured.
36612      * @return {Roo.BasicDialog} this
36613      */
36614     restoreState : function(){
36615         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
36616         if(box && box.width){
36617             this.xy = [box.x, box.y];
36618             this.resizeTo(box.width, box.height);
36619         }
36620         return this;
36621     },
36622
36623     // private
36624     beforeShow : function(){
36625         this.expand();
36626         if(this.fixedcenter){
36627             this.xy = this.el.getCenterXY(true);
36628         }
36629         if(this.modal){
36630             Roo.get(document.body).addClass("x-body-masked");
36631             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36632             this.mask.show();
36633         }
36634         this.constrainXY();
36635     },
36636
36637     // private
36638     animShow : function(){
36639         var b = Roo.get(this.animateTarget).getBox();
36640         this.proxy.setSize(b.width, b.height);
36641         this.proxy.setLocation(b.x, b.y);
36642         this.proxy.show();
36643         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
36644                     true, .35, this.showEl.createDelegate(this));
36645     },
36646
36647     /**
36648      * Shows the dialog.
36649      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
36650      * @return {Roo.BasicDialog} this
36651      */
36652     show : function(animateTarget){
36653         if (this.fireEvent("beforeshow", this) === false){
36654             return;
36655         }
36656         if(this.syncHeightBeforeShow){
36657             this.syncBodyHeight();
36658         }else if(this.firstShow){
36659             this.firstShow = false;
36660             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
36661         }
36662         this.animateTarget = animateTarget || this.animateTarget;
36663         if(!this.el.isVisible()){
36664             this.beforeShow();
36665             if(this.animateTarget && Roo.get(this.animateTarget)){
36666                 this.animShow();
36667             }else{
36668                 this.showEl();
36669             }
36670         }
36671         return this;
36672     },
36673
36674     // private
36675     showEl : function(){
36676         this.proxy.hide();
36677         this.el.setXY(this.xy);
36678         this.el.show();
36679         this.adjustAssets(true);
36680         this.toFront();
36681         this.focus();
36682         // IE peekaboo bug - fix found by Dave Fenwick
36683         if(Roo.isIE){
36684             this.el.repaint();
36685         }
36686         this.fireEvent("show", this);
36687     },
36688
36689     /**
36690      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
36691      * dialog itself will receive focus.
36692      */
36693     focus : function(){
36694         if(this.defaultButton){
36695             this.defaultButton.focus();
36696         }else{
36697             this.focusEl.focus();
36698         }
36699     },
36700
36701     // private
36702     constrainXY : function(){
36703         if(this.constraintoviewport !== false){
36704             if(!this.viewSize){
36705                 if(this.container){
36706                     var s = this.container.getSize();
36707                     this.viewSize = [s.width, s.height];
36708                 }else{
36709                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
36710                 }
36711             }
36712             var s = Roo.get(this.container||document).getScroll();
36713
36714             var x = this.xy[0], y = this.xy[1];
36715             var w = this.size.width, h = this.size.height;
36716             var vw = this.viewSize[0], vh = this.viewSize[1];
36717             // only move it if it needs it
36718             var moved = false;
36719             // first validate right/bottom
36720             if(x + w > vw+s.left){
36721                 x = vw - w;
36722                 moved = true;
36723             }
36724             if(y + h > vh+s.top){
36725                 y = vh - h;
36726                 moved = true;
36727             }
36728             // then make sure top/left isn't negative
36729             if(x < s.left){
36730                 x = s.left;
36731                 moved = true;
36732             }
36733             if(y < s.top){
36734                 y = s.top;
36735                 moved = true;
36736             }
36737             if(moved){
36738                 // cache xy
36739                 this.xy = [x, y];
36740                 if(this.isVisible()){
36741                     this.el.setLocation(x, y);
36742                     this.adjustAssets();
36743                 }
36744             }
36745         }
36746     },
36747
36748     // private
36749     onDrag : function(){
36750         if(!this.proxyDrag){
36751             this.xy = this.el.getXY();
36752             this.adjustAssets();
36753         }
36754     },
36755
36756     // private
36757     adjustAssets : function(doShow){
36758         var x = this.xy[0], y = this.xy[1];
36759         var w = this.size.width, h = this.size.height;
36760         if(doShow === true){
36761             if(this.shadow){
36762                 this.shadow.show(this.el);
36763             }
36764             if(this.shim){
36765                 this.shim.show();
36766             }
36767         }
36768         if(this.shadow && this.shadow.isVisible()){
36769             this.shadow.show(this.el);
36770         }
36771         if(this.shim && this.shim.isVisible()){
36772             this.shim.setBounds(x, y, w, h);
36773         }
36774     },
36775
36776     // private
36777     adjustViewport : function(w, h){
36778         if(!w || !h){
36779             w = Roo.lib.Dom.getViewWidth();
36780             h = Roo.lib.Dom.getViewHeight();
36781         }
36782         // cache the size
36783         this.viewSize = [w, h];
36784         if(this.modal && this.mask.isVisible()){
36785             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
36786             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36787         }
36788         if(this.isVisible()){
36789             this.constrainXY();
36790         }
36791     },
36792
36793     /**
36794      * Destroys this dialog and all its supporting elements (including any tabs, shim,
36795      * shadow, proxy, mask, etc.)  Also removes all event listeners.
36796      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
36797      */
36798     destroy : function(removeEl){
36799         if(this.isVisible()){
36800             this.animateTarget = null;
36801             this.hide();
36802         }
36803         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
36804         if(this.tabs){
36805             this.tabs.destroy(removeEl);
36806         }
36807         Roo.destroy(
36808              this.shim,
36809              this.proxy,
36810              this.resizer,
36811              this.close,
36812              this.mask
36813         );
36814         if(this.dd){
36815             this.dd.unreg();
36816         }
36817         if(this.buttons){
36818            for(var i = 0, len = this.buttons.length; i < len; i++){
36819                this.buttons[i].destroy();
36820            }
36821         }
36822         this.el.removeAllListeners();
36823         if(removeEl === true){
36824             this.el.update("");
36825             this.el.remove();
36826         }
36827         Roo.DialogManager.unregister(this);
36828     },
36829
36830     // private
36831     startMove : function(){
36832         if(this.proxyDrag){
36833             this.proxy.show();
36834         }
36835         if(this.constraintoviewport !== false){
36836             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
36837         }
36838     },
36839
36840     // private
36841     endMove : function(){
36842         if(!this.proxyDrag){
36843             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
36844         }else{
36845             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
36846             this.proxy.hide();
36847         }
36848         this.refreshSize();
36849         this.adjustAssets();
36850         this.focus();
36851         this.fireEvent("move", this, this.xy[0], this.xy[1]);
36852     },
36853
36854     /**
36855      * Brings this dialog to the front of any other visible dialogs
36856      * @return {Roo.BasicDialog} this
36857      */
36858     toFront : function(){
36859         Roo.DialogManager.bringToFront(this);
36860         return this;
36861     },
36862
36863     /**
36864      * Sends this dialog to the back (under) of any other visible dialogs
36865      * @return {Roo.BasicDialog} this
36866      */
36867     toBack : function(){
36868         Roo.DialogManager.sendToBack(this);
36869         return this;
36870     },
36871
36872     /**
36873      * Centers this dialog in the viewport
36874      * @return {Roo.BasicDialog} this
36875      */
36876     center : function(){
36877         var xy = this.el.getCenterXY(true);
36878         this.moveTo(xy[0], xy[1]);
36879         return this;
36880     },
36881
36882     /**
36883      * Moves the dialog's top-left corner to the specified point
36884      * @param {Number} x
36885      * @param {Number} y
36886      * @return {Roo.BasicDialog} this
36887      */
36888     moveTo : function(x, y){
36889         this.xy = [x,y];
36890         if(this.isVisible()){
36891             this.el.setXY(this.xy);
36892             this.adjustAssets();
36893         }
36894         return this;
36895     },
36896
36897     /**
36898      * Aligns the dialog to the specified element
36899      * @param {String/HTMLElement/Roo.Element} element The element to align to.
36900      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
36901      * @param {Array} offsets (optional) Offset the positioning by [x, y]
36902      * @return {Roo.BasicDialog} this
36903      */
36904     alignTo : function(element, position, offsets){
36905         this.xy = this.el.getAlignToXY(element, position, offsets);
36906         if(this.isVisible()){
36907             this.el.setXY(this.xy);
36908             this.adjustAssets();
36909         }
36910         return this;
36911     },
36912
36913     /**
36914      * Anchors an element to another element and realigns it when the window is resized.
36915      * @param {String/HTMLElement/Roo.Element} element The element to align to.
36916      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
36917      * @param {Array} offsets (optional) Offset the positioning by [x, y]
36918      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
36919      * is a number, it is used as the buffer delay (defaults to 50ms).
36920      * @return {Roo.BasicDialog} this
36921      */
36922     anchorTo : function(el, alignment, offsets, monitorScroll){
36923         var action = function(){
36924             this.alignTo(el, alignment, offsets);
36925         };
36926         Roo.EventManager.onWindowResize(action, this);
36927         var tm = typeof monitorScroll;
36928         if(tm != 'undefined'){
36929             Roo.EventManager.on(window, 'scroll', action, this,
36930                 {buffer: tm == 'number' ? monitorScroll : 50});
36931         }
36932         action.call(this);
36933         return this;
36934     },
36935
36936     /**
36937      * Returns true if the dialog is visible
36938      * @return {Boolean}
36939      */
36940     isVisible : function(){
36941         return this.el.isVisible();
36942     },
36943
36944     // private
36945     animHide : function(callback){
36946         var b = Roo.get(this.animateTarget).getBox();
36947         this.proxy.show();
36948         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
36949         this.el.hide();
36950         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
36951                     this.hideEl.createDelegate(this, [callback]));
36952     },
36953
36954     /**
36955      * Hides the dialog.
36956      * @param {Function} callback (optional) Function to call when the dialog is hidden
36957      * @return {Roo.BasicDialog} this
36958      */
36959     hide : function(callback){
36960         if (this.fireEvent("beforehide", this) === false){
36961             return;
36962         }
36963         if(this.shadow){
36964             this.shadow.hide();
36965         }
36966         if(this.shim) {
36967           this.shim.hide();
36968         }
36969         // sometimes animateTarget seems to get set.. causing problems...
36970         // this just double checks..
36971         if(this.animateTarget && Roo.get(this.animateTarget)) {
36972            this.animHide(callback);
36973         }else{
36974             this.el.hide();
36975             this.hideEl(callback);
36976         }
36977         return this;
36978     },
36979
36980     // private
36981     hideEl : function(callback){
36982         this.proxy.hide();
36983         if(this.modal){
36984             this.mask.hide();
36985             Roo.get(document.body).removeClass("x-body-masked");
36986         }
36987         this.fireEvent("hide", this);
36988         if(typeof callback == "function"){
36989             callback();
36990         }
36991     },
36992
36993     // private
36994     hideAction : function(){
36995         this.setLeft("-10000px");
36996         this.setTop("-10000px");
36997         this.setStyle("visibility", "hidden");
36998     },
36999
37000     // private
37001     refreshSize : function(){
37002         this.size = this.el.getSize();
37003         this.xy = this.el.getXY();
37004         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
37005     },
37006
37007     // private
37008     // z-index is managed by the DialogManager and may be overwritten at any time
37009     setZIndex : function(index){
37010         if(this.modal){
37011             this.mask.setStyle("z-index", index);
37012         }
37013         if(this.shim){
37014             this.shim.setStyle("z-index", ++index);
37015         }
37016         if(this.shadow){
37017             this.shadow.setZIndex(++index);
37018         }
37019         this.el.setStyle("z-index", ++index);
37020         if(this.proxy){
37021             this.proxy.setStyle("z-index", ++index);
37022         }
37023         if(this.resizer){
37024             this.resizer.proxy.setStyle("z-index", ++index);
37025         }
37026
37027         this.lastZIndex = index;
37028     },
37029
37030     /**
37031      * Returns the element for this dialog
37032      * @return {Roo.Element} The underlying dialog Element
37033      */
37034     getEl : function(){
37035         return this.el;
37036     }
37037 });
37038
37039 /**
37040  * @class Roo.DialogManager
37041  * Provides global access to BasicDialogs that have been created and
37042  * support for z-indexing (layering) multiple open dialogs.
37043  */
37044 Roo.DialogManager = function(){
37045     var list = {};
37046     var accessList = [];
37047     var front = null;
37048
37049     // private
37050     var sortDialogs = function(d1, d2){
37051         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
37052     };
37053
37054     // private
37055     var orderDialogs = function(){
37056         accessList.sort(sortDialogs);
37057         var seed = Roo.DialogManager.zseed;
37058         for(var i = 0, len = accessList.length; i < len; i++){
37059             var dlg = accessList[i];
37060             if(dlg){
37061                 dlg.setZIndex(seed + (i*10));
37062             }
37063         }
37064     };
37065
37066     return {
37067         /**
37068          * The starting z-index for BasicDialogs (defaults to 9000)
37069          * @type Number The z-index value
37070          */
37071         zseed : 9000,
37072
37073         // private
37074         register : function(dlg){
37075             list[dlg.id] = dlg;
37076             accessList.push(dlg);
37077         },
37078
37079         // private
37080         unregister : function(dlg){
37081             delete list[dlg.id];
37082             var i=0;
37083             var len=0;
37084             if(!accessList.indexOf){
37085                 for(  i = 0, len = accessList.length; i < len; i++){
37086                     if(accessList[i] == dlg){
37087                         accessList.splice(i, 1);
37088                         return;
37089                     }
37090                 }
37091             }else{
37092                  i = accessList.indexOf(dlg);
37093                 if(i != -1){
37094                     accessList.splice(i, 1);
37095                 }
37096             }
37097         },
37098
37099         /**
37100          * Gets a registered dialog by id
37101          * @param {String/Object} id The id of the dialog or a dialog
37102          * @return {Roo.BasicDialog} this
37103          */
37104         get : function(id){
37105             return typeof id == "object" ? id : list[id];
37106         },
37107
37108         /**
37109          * Brings the specified dialog to the front
37110          * @param {String/Object} dlg The id of the dialog or a dialog
37111          * @return {Roo.BasicDialog} this
37112          */
37113         bringToFront : function(dlg){
37114             dlg = this.get(dlg);
37115             if(dlg != front){
37116                 front = dlg;
37117                 dlg._lastAccess = new Date().getTime();
37118                 orderDialogs();
37119             }
37120             return dlg;
37121         },
37122
37123         /**
37124          * Sends the specified dialog to the back
37125          * @param {String/Object} dlg The id of the dialog or a dialog
37126          * @return {Roo.BasicDialog} this
37127          */
37128         sendToBack : function(dlg){
37129             dlg = this.get(dlg);
37130             dlg._lastAccess = -(new Date().getTime());
37131             orderDialogs();
37132             return dlg;
37133         },
37134
37135         /**
37136          * Hides all dialogs
37137          */
37138         hideAll : function(){
37139             for(var id in list){
37140                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
37141                     list[id].hide();
37142                 }
37143             }
37144         }
37145     };
37146 }();
37147
37148 /**
37149  * @class Roo.LayoutDialog
37150  * @extends Roo.BasicDialog
37151  * Dialog which provides adjustments for working with a layout in a Dialog.
37152  * Add your necessary layout config options to the dialog's config.<br>
37153  * Example usage (including a nested layout):
37154  * <pre><code>
37155 if(!dialog){
37156     dialog = new Roo.LayoutDialog("download-dlg", {
37157         modal: true,
37158         width:600,
37159         height:450,
37160         shadow:true,
37161         minWidth:500,
37162         minHeight:350,
37163         autoTabs:true,
37164         proxyDrag:true,
37165         // layout config merges with the dialog config
37166         center:{
37167             tabPosition: "top",
37168             alwaysShowTabs: true
37169         }
37170     });
37171     dialog.addKeyListener(27, dialog.hide, dialog);
37172     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
37173     dialog.addButton("Build It!", this.getDownload, this);
37174
37175     // we can even add nested layouts
37176     var innerLayout = new Roo.BorderLayout("dl-inner", {
37177         east: {
37178             initialSize: 200,
37179             autoScroll:true,
37180             split:true
37181         },
37182         center: {
37183             autoScroll:true
37184         }
37185     });
37186     innerLayout.beginUpdate();
37187     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
37188     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
37189     innerLayout.endUpdate(true);
37190
37191     var layout = dialog.getLayout();
37192     layout.beginUpdate();
37193     layout.add("center", new Roo.ContentPanel("standard-panel",
37194                         {title: "Download the Source", fitToFrame:true}));
37195     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
37196                {title: "Build your own roo.js"}));
37197     layout.getRegion("center").showPanel(sp);
37198     layout.endUpdate();
37199 }
37200 </code></pre>
37201     * @constructor
37202     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
37203     * @param {Object} config configuration options
37204   */
37205 Roo.LayoutDialog = function(el, cfg){
37206     
37207     var config=  cfg;
37208     if (typeof(cfg) == 'undefined') {
37209         config = Roo.apply({}, el);
37210         // not sure why we use documentElement here.. - it should always be body.
37211         // IE7 borks horribly if we use documentElement.
37212         // webkit also does not like documentElement - it creates a body element...
37213         el = Roo.get( document.body || document.documentElement ).createChild();
37214         //config.autoCreate = true;
37215     }
37216     
37217     
37218     config.autoTabs = false;
37219     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
37220     this.body.setStyle({overflow:"hidden", position:"relative"});
37221     this.layout = new Roo.BorderLayout(this.body.dom, config);
37222     this.layout.monitorWindowResize = false;
37223     this.el.addClass("x-dlg-auto-layout");
37224     // fix case when center region overwrites center function
37225     this.center = Roo.BasicDialog.prototype.center;
37226     this.on("show", this.layout.layout, this.layout, true);
37227     if (config.items) {
37228         var xitems = config.items;
37229         delete config.items;
37230         Roo.each(xitems, this.addxtype, this);
37231     }
37232     
37233     
37234 };
37235 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
37236     /**
37237      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
37238      * @deprecated
37239      */
37240     endUpdate : function(){
37241         this.layout.endUpdate();
37242     },
37243
37244     /**
37245      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
37246      *  @deprecated
37247      */
37248     beginUpdate : function(){
37249         this.layout.beginUpdate();
37250     },
37251
37252     /**
37253      * Get the BorderLayout for this dialog
37254      * @return {Roo.BorderLayout}
37255      */
37256     getLayout : function(){
37257         return this.layout;
37258     },
37259
37260     showEl : function(){
37261         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
37262         if(Roo.isIE7){
37263             this.layout.layout();
37264         }
37265     },
37266
37267     // private
37268     // Use the syncHeightBeforeShow config option to control this automatically
37269     syncBodyHeight : function(){
37270         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
37271         if(this.layout){this.layout.layout();}
37272     },
37273     
37274       /**
37275      * Add an xtype element (actually adds to the layout.)
37276      * @return {Object} xdata xtype object data.
37277      */
37278     
37279     addxtype : function(c) {
37280         return this.layout.addxtype(c);
37281     }
37282 });/*
37283  * Based on:
37284  * Ext JS Library 1.1.1
37285  * Copyright(c) 2006-2007, Ext JS, LLC.
37286  *
37287  * Originally Released Under LGPL - original licence link has changed is not relivant.
37288  *
37289  * Fork - LGPL
37290  * <script type="text/javascript">
37291  */
37292  
37293 /**
37294  * @class Roo.MessageBox
37295  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
37296  * Example usage:
37297  *<pre><code>
37298 // Basic alert:
37299 Roo.Msg.alert('Status', 'Changes saved successfully.');
37300
37301 // Prompt for user data:
37302 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
37303     if (btn == 'ok'){
37304         // process text value...
37305     }
37306 });
37307
37308 // Show a dialog using config options:
37309 Roo.Msg.show({
37310    title:'Save Changes?',
37311    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
37312    buttons: Roo.Msg.YESNOCANCEL,
37313    fn: processResult,
37314    animEl: 'elId'
37315 });
37316 </code></pre>
37317  * @singleton
37318  */
37319 Roo.MessageBox = function(){
37320     var dlg, opt, mask, waitTimer;
37321     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
37322     var buttons, activeTextEl, bwidth;
37323
37324     // private
37325     var handleButton = function(button){
37326         dlg.hide();
37327         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
37328     };
37329
37330     // private
37331     var handleHide = function(){
37332         if(opt && opt.cls){
37333             dlg.el.removeClass(opt.cls);
37334         }
37335         if(waitTimer){
37336             Roo.TaskMgr.stop(waitTimer);
37337             waitTimer = null;
37338         }
37339     };
37340
37341     // private
37342     var updateButtons = function(b){
37343         var width = 0;
37344         if(!b){
37345             buttons["ok"].hide();
37346             buttons["cancel"].hide();
37347             buttons["yes"].hide();
37348             buttons["no"].hide();
37349             dlg.footer.dom.style.display = 'none';
37350             return width;
37351         }
37352         dlg.footer.dom.style.display = '';
37353         for(var k in buttons){
37354             if(typeof buttons[k] != "function"){
37355                 if(b[k]){
37356                     buttons[k].show();
37357                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
37358                     width += buttons[k].el.getWidth()+15;
37359                 }else{
37360                     buttons[k].hide();
37361                 }
37362             }
37363         }
37364         return width;
37365     };
37366
37367     // private
37368     var handleEsc = function(d, k, e){
37369         if(opt && opt.closable !== false){
37370             dlg.hide();
37371         }
37372         if(e){
37373             e.stopEvent();
37374         }
37375     };
37376
37377     return {
37378         /**
37379          * Returns a reference to the underlying {@link Roo.BasicDialog} element
37380          * @return {Roo.BasicDialog} The BasicDialog element
37381          */
37382         getDialog : function(){
37383            if(!dlg){
37384                 dlg = new Roo.BasicDialog("x-msg-box", {
37385                     autoCreate : true,
37386                     shadow: true,
37387                     draggable: true,
37388                     resizable:false,
37389                     constraintoviewport:false,
37390                     fixedcenter:true,
37391                     collapsible : false,
37392                     shim:true,
37393                     modal: true,
37394                     width:400, height:100,
37395                     buttonAlign:"center",
37396                     closeClick : function(){
37397                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
37398                             handleButton("no");
37399                         }else{
37400                             handleButton("cancel");
37401                         }
37402                     }
37403                 });
37404                 dlg.on("hide", handleHide);
37405                 mask = dlg.mask;
37406                 dlg.addKeyListener(27, handleEsc);
37407                 buttons = {};
37408                 var bt = this.buttonText;
37409                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
37410                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
37411                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
37412                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
37413                 bodyEl = dlg.body.createChild({
37414
37415                     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>'
37416                 });
37417                 msgEl = bodyEl.dom.firstChild;
37418                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
37419                 textboxEl.enableDisplayMode();
37420                 textboxEl.addKeyListener([10,13], function(){
37421                     if(dlg.isVisible() && opt && opt.buttons){
37422                         if(opt.buttons.ok){
37423                             handleButton("ok");
37424                         }else if(opt.buttons.yes){
37425                             handleButton("yes");
37426                         }
37427                     }
37428                 });
37429                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
37430                 textareaEl.enableDisplayMode();
37431                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
37432                 progressEl.enableDisplayMode();
37433                 var pf = progressEl.dom.firstChild;
37434                 if (pf) {
37435                     pp = Roo.get(pf.firstChild);
37436                     pp.setHeight(pf.offsetHeight);
37437                 }
37438                 
37439             }
37440             return dlg;
37441         },
37442
37443         /**
37444          * Updates the message box body text
37445          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
37446          * the XHTML-compliant non-breaking space character '&amp;#160;')
37447          * @return {Roo.MessageBox} This message box
37448          */
37449         updateText : function(text){
37450             if(!dlg.isVisible() && !opt.width){
37451                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
37452             }
37453             msgEl.innerHTML = text || '&#160;';
37454       
37455             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
37456             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
37457             var w = Math.max(
37458                     Math.min(opt.width || cw , this.maxWidth), 
37459                     Math.max(opt.minWidth || this.minWidth, bwidth)
37460             );
37461             if(opt.prompt){
37462                 activeTextEl.setWidth(w);
37463             }
37464             if(dlg.isVisible()){
37465                 dlg.fixedcenter = false;
37466             }
37467             // to big, make it scroll. = But as usual stupid IE does not support
37468             // !important..
37469             
37470             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
37471                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
37472                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
37473             } else {
37474                 bodyEl.dom.style.height = '';
37475                 bodyEl.dom.style.overflowY = '';
37476             }
37477             if (cw > w) {
37478                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
37479             } else {
37480                 bodyEl.dom.style.overflowX = '';
37481             }
37482             
37483             dlg.setContentSize(w, bodyEl.getHeight());
37484             if(dlg.isVisible()){
37485                 dlg.fixedcenter = true;
37486             }
37487             return this;
37488         },
37489
37490         /**
37491          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
37492          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
37493          * @param {Number} value Any number between 0 and 1 (e.g., .5)
37494          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
37495          * @return {Roo.MessageBox} This message box
37496          */
37497         updateProgress : function(value, text){
37498             if(text){
37499                 this.updateText(text);
37500             }
37501             if (pp) { // weird bug on my firefox - for some reason this is not defined
37502                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
37503             }
37504             return this;
37505         },        
37506
37507         /**
37508          * Returns true if the message box is currently displayed
37509          * @return {Boolean} True if the message box is visible, else false
37510          */
37511         isVisible : function(){
37512             return dlg && dlg.isVisible();  
37513         },
37514
37515         /**
37516          * Hides the message box if it is displayed
37517          */
37518         hide : function(){
37519             if(this.isVisible()){
37520                 dlg.hide();
37521             }  
37522         },
37523
37524         /**
37525          * Displays a new message box, or reinitializes an existing message box, based on the config options
37526          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
37527          * The following config object properties are supported:
37528          * <pre>
37529 Property    Type             Description
37530 ----------  ---------------  ------------------------------------------------------------------------------------
37531 animEl            String/Element   An id or Element from which the message box should animate as it opens and
37532                                    closes (defaults to undefined)
37533 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
37534                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
37535 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
37536                                    progress and wait dialogs will ignore this property and always hide the
37537                                    close button as they can only be closed programmatically.
37538 cls               String           A custom CSS class to apply to the message box element
37539 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
37540                                    displayed (defaults to 75)
37541 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
37542                                    function will be btn (the name of the button that was clicked, if applicable,
37543                                    e.g. "ok"), and text (the value of the active text field, if applicable).
37544                                    Progress and wait dialogs will ignore this option since they do not respond to
37545                                    user actions and can only be closed programmatically, so any required function
37546                                    should be called by the same code after it closes the dialog.
37547 icon              String           A CSS class that provides a background image to be used as an icon for
37548                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
37549 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
37550 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
37551 modal             Boolean          False to allow user interaction with the page while the message box is
37552                                    displayed (defaults to true)
37553 msg               String           A string that will replace the existing message box body text (defaults
37554                                    to the XHTML-compliant non-breaking space character '&#160;')
37555 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
37556 progress          Boolean          True to display a progress bar (defaults to false)
37557 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
37558 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
37559 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
37560 title             String           The title text
37561 value             String           The string value to set into the active textbox element if displayed
37562 wait              Boolean          True to display a progress bar (defaults to false)
37563 width             Number           The width of the dialog in pixels
37564 </pre>
37565          *
37566          * Example usage:
37567          * <pre><code>
37568 Roo.Msg.show({
37569    title: 'Address',
37570    msg: 'Please enter your address:',
37571    width: 300,
37572    buttons: Roo.MessageBox.OKCANCEL,
37573    multiline: true,
37574    fn: saveAddress,
37575    animEl: 'addAddressBtn'
37576 });
37577 </code></pre>
37578          * @param {Object} config Configuration options
37579          * @return {Roo.MessageBox} This message box
37580          */
37581         show : function(options)
37582         {
37583             
37584             // this causes nightmares if you show one dialog after another
37585             // especially on callbacks..
37586              
37587             if(this.isVisible()){
37588                 
37589                 this.hide();
37590                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
37591                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
37592                 Roo.log("New Dialog Message:" +  options.msg )
37593                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
37594                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
37595                 
37596             }
37597             var d = this.getDialog();
37598             opt = options;
37599             d.setTitle(opt.title || "&#160;");
37600             d.close.setDisplayed(opt.closable !== false);
37601             activeTextEl = textboxEl;
37602             opt.prompt = opt.prompt || (opt.multiline ? true : false);
37603             if(opt.prompt){
37604                 if(opt.multiline){
37605                     textboxEl.hide();
37606                     textareaEl.show();
37607                     textareaEl.setHeight(typeof opt.multiline == "number" ?
37608                         opt.multiline : this.defaultTextHeight);
37609                     activeTextEl = textareaEl;
37610                 }else{
37611                     textboxEl.show();
37612                     textareaEl.hide();
37613                 }
37614             }else{
37615                 textboxEl.hide();
37616                 textareaEl.hide();
37617             }
37618             progressEl.setDisplayed(opt.progress === true);
37619             this.updateProgress(0);
37620             activeTextEl.dom.value = opt.value || "";
37621             if(opt.prompt){
37622                 dlg.setDefaultButton(activeTextEl);
37623             }else{
37624                 var bs = opt.buttons;
37625                 var db = null;
37626                 if(bs && bs.ok){
37627                     db = buttons["ok"];
37628                 }else if(bs && bs.yes){
37629                     db = buttons["yes"];
37630                 }
37631                 dlg.setDefaultButton(db);
37632             }
37633             bwidth = updateButtons(opt.buttons);
37634             this.updateText(opt.msg);
37635             if(opt.cls){
37636                 d.el.addClass(opt.cls);
37637             }
37638             d.proxyDrag = opt.proxyDrag === true;
37639             d.modal = opt.modal !== false;
37640             d.mask = opt.modal !== false ? mask : false;
37641             if(!d.isVisible()){
37642                 // force it to the end of the z-index stack so it gets a cursor in FF
37643                 document.body.appendChild(dlg.el.dom);
37644                 d.animateTarget = null;
37645                 d.show(options.animEl);
37646             }
37647             return this;
37648         },
37649
37650         /**
37651          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
37652          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
37653          * and closing the message box when the process is complete.
37654          * @param {String} title The title bar text
37655          * @param {String} msg The message box body text
37656          * @return {Roo.MessageBox} This message box
37657          */
37658         progress : function(title, msg){
37659             this.show({
37660                 title : title,
37661                 msg : msg,
37662                 buttons: false,
37663                 progress:true,
37664                 closable:false,
37665                 minWidth: this.minProgressWidth,
37666                 modal : true
37667             });
37668             return this;
37669         },
37670
37671         /**
37672          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
37673          * If a callback function is passed it will be called after the user clicks the button, and the
37674          * id of the button that was clicked will be passed as the only parameter to the callback
37675          * (could also be the top-right close button).
37676          * @param {String} title The title bar text
37677          * @param {String} msg The message box body text
37678          * @param {Function} fn (optional) The callback function invoked after the message box is closed
37679          * @param {Object} scope (optional) The scope of the callback function
37680          * @return {Roo.MessageBox} This message box
37681          */
37682         alert : function(title, msg, fn, scope){
37683             this.show({
37684                 title : title,
37685                 msg : msg,
37686                 buttons: this.OK,
37687                 fn: fn,
37688                 scope : scope,
37689                 modal : true
37690             });
37691             return this;
37692         },
37693
37694         /**
37695          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
37696          * interaction while waiting for a long-running process to complete that does not have defined intervals.
37697          * You are responsible for closing the message box when the process is complete.
37698          * @param {String} msg The message box body text
37699          * @param {String} title (optional) The title bar text
37700          * @return {Roo.MessageBox} This message box
37701          */
37702         wait : function(msg, title){
37703             this.show({
37704                 title : title,
37705                 msg : msg,
37706                 buttons: false,
37707                 closable:false,
37708                 progress:true,
37709                 modal:true,
37710                 width:300,
37711                 wait:true
37712             });
37713             waitTimer = Roo.TaskMgr.start({
37714                 run: function(i){
37715                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
37716                 },
37717                 interval: 1000
37718             });
37719             return this;
37720         },
37721
37722         /**
37723          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
37724          * If a callback function is passed it will be called after the user clicks either button, and the id of the
37725          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
37726          * @param {String} title The title bar text
37727          * @param {String} msg The message box body text
37728          * @param {Function} fn (optional) The callback function invoked after the message box is closed
37729          * @param {Object} scope (optional) The scope of the callback function
37730          * @return {Roo.MessageBox} This message box
37731          */
37732         confirm : function(title, msg, fn, scope){
37733             this.show({
37734                 title : title,
37735                 msg : msg,
37736                 buttons: this.YESNO,
37737                 fn: fn,
37738                 scope : scope,
37739                 modal : true
37740             });
37741             return this;
37742         },
37743
37744         /**
37745          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
37746          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
37747          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
37748          * (could also be the top-right close button) and the text that was entered will be passed as the two
37749          * parameters to the callback.
37750          * @param {String} title The title bar text
37751          * @param {String} msg The message box body text
37752          * @param {Function} fn (optional) The callback function invoked after the message box is closed
37753          * @param {Object} scope (optional) The scope of the callback function
37754          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
37755          * property, or the height in pixels to create the textbox (defaults to false / single-line)
37756          * @return {Roo.MessageBox} This message box
37757          */
37758         prompt : function(title, msg, fn, scope, multiline){
37759             this.show({
37760                 title : title,
37761                 msg : msg,
37762                 buttons: this.OKCANCEL,
37763                 fn: fn,
37764                 minWidth:250,
37765                 scope : scope,
37766                 prompt:true,
37767                 multiline: multiline,
37768                 modal : true
37769             });
37770             return this;
37771         },
37772
37773         /**
37774          * Button config that displays a single OK button
37775          * @type Object
37776          */
37777         OK : {ok:true},
37778         /**
37779          * Button config that displays Yes and No buttons
37780          * @type Object
37781          */
37782         YESNO : {yes:true, no:true},
37783         /**
37784          * Button config that displays OK and Cancel buttons
37785          * @type Object
37786          */
37787         OKCANCEL : {ok:true, cancel:true},
37788         /**
37789          * Button config that displays Yes, No and Cancel buttons
37790          * @type Object
37791          */
37792         YESNOCANCEL : {yes:true, no:true, cancel:true},
37793
37794         /**
37795          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
37796          * @type Number
37797          */
37798         defaultTextHeight : 75,
37799         /**
37800          * The maximum width in pixels of the message box (defaults to 600)
37801          * @type Number
37802          */
37803         maxWidth : 600,
37804         /**
37805          * The minimum width in pixels of the message box (defaults to 100)
37806          * @type Number
37807          */
37808         minWidth : 100,
37809         /**
37810          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
37811          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
37812          * @type Number
37813          */
37814         minProgressWidth : 250,
37815         /**
37816          * An object containing the default button text strings that can be overriden for localized language support.
37817          * Supported properties are: ok, cancel, yes and no.
37818          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
37819          * @type Object
37820          */
37821         buttonText : {
37822             ok : "OK",
37823             cancel : "Cancel",
37824             yes : "Yes",
37825             no : "No"
37826         }
37827     };
37828 }();
37829
37830 /**
37831  * Shorthand for {@link Roo.MessageBox}
37832  */
37833 Roo.Msg = Roo.MessageBox;/*
37834  * Based on:
37835  * Ext JS Library 1.1.1
37836  * Copyright(c) 2006-2007, Ext JS, LLC.
37837  *
37838  * Originally Released Under LGPL - original licence link has changed is not relivant.
37839  *
37840  * Fork - LGPL
37841  * <script type="text/javascript">
37842  */
37843 /**
37844  * @class Roo.QuickTips
37845  * Provides attractive and customizable tooltips for any element.
37846  * @singleton
37847  */
37848 Roo.QuickTips = function(){
37849     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
37850     var ce, bd, xy, dd;
37851     var visible = false, disabled = true, inited = false;
37852     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
37853     
37854     var onOver = function(e){
37855         if(disabled){
37856             return;
37857         }
37858         var t = e.getTarget();
37859         if(!t || t.nodeType !== 1 || t == document || t == document.body){
37860             return;
37861         }
37862         if(ce && t == ce.el){
37863             clearTimeout(hideProc);
37864             return;
37865         }
37866         if(t && tagEls[t.id]){
37867             tagEls[t.id].el = t;
37868             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
37869             return;
37870         }
37871         var ttp, et = Roo.fly(t);
37872         var ns = cfg.namespace;
37873         if(tm.interceptTitles && t.title){
37874             ttp = t.title;
37875             t.qtip = ttp;
37876             t.removeAttribute("title");
37877             e.preventDefault();
37878         }else{
37879             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
37880         }
37881         if(ttp){
37882             showProc = show.defer(tm.showDelay, tm, [{
37883                 el: t, 
37884                 text: ttp, 
37885                 width: et.getAttributeNS(ns, cfg.width),
37886                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
37887                 title: et.getAttributeNS(ns, cfg.title),
37888                     cls: et.getAttributeNS(ns, cfg.cls)
37889             }]);
37890         }
37891     };
37892     
37893     var onOut = function(e){
37894         clearTimeout(showProc);
37895         var t = e.getTarget();
37896         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
37897             hideProc = setTimeout(hide, tm.hideDelay);
37898         }
37899     };
37900     
37901     var onMove = function(e){
37902         if(disabled){
37903             return;
37904         }
37905         xy = e.getXY();
37906         xy[1] += 18;
37907         if(tm.trackMouse && ce){
37908             el.setXY(xy);
37909         }
37910     };
37911     
37912     var onDown = function(e){
37913         clearTimeout(showProc);
37914         clearTimeout(hideProc);
37915         if(!e.within(el)){
37916             if(tm.hideOnClick){
37917                 hide();
37918                 tm.disable();
37919                 tm.enable.defer(100, tm);
37920             }
37921         }
37922     };
37923     
37924     var getPad = function(){
37925         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
37926     };
37927
37928     var show = function(o){
37929         if(disabled){
37930             return;
37931         }
37932         clearTimeout(dismissProc);
37933         ce = o;
37934         if(removeCls){ // in case manually hidden
37935             el.removeClass(removeCls);
37936             removeCls = null;
37937         }
37938         if(ce.cls){
37939             el.addClass(ce.cls);
37940             removeCls = ce.cls;
37941         }
37942         if(ce.title){
37943             tipTitle.update(ce.title);
37944             tipTitle.show();
37945         }else{
37946             tipTitle.update('');
37947             tipTitle.hide();
37948         }
37949         el.dom.style.width  = tm.maxWidth+'px';
37950         //tipBody.dom.style.width = '';
37951         tipBodyText.update(o.text);
37952         var p = getPad(), w = ce.width;
37953         if(!w){
37954             var td = tipBodyText.dom;
37955             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
37956             if(aw > tm.maxWidth){
37957                 w = tm.maxWidth;
37958             }else if(aw < tm.minWidth){
37959                 w = tm.minWidth;
37960             }else{
37961                 w = aw;
37962             }
37963         }
37964         //tipBody.setWidth(w);
37965         el.setWidth(parseInt(w, 10) + p);
37966         if(ce.autoHide === false){
37967             close.setDisplayed(true);
37968             if(dd){
37969                 dd.unlock();
37970             }
37971         }else{
37972             close.setDisplayed(false);
37973             if(dd){
37974                 dd.lock();
37975             }
37976         }
37977         if(xy){
37978             el.avoidY = xy[1]-18;
37979             el.setXY(xy);
37980         }
37981         if(tm.animate){
37982             el.setOpacity(.1);
37983             el.setStyle("visibility", "visible");
37984             el.fadeIn({callback: afterShow});
37985         }else{
37986             afterShow();
37987         }
37988     };
37989     
37990     var afterShow = function(){
37991         if(ce){
37992             el.show();
37993             esc.enable();
37994             if(tm.autoDismiss && ce.autoHide !== false){
37995                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
37996             }
37997         }
37998     };
37999     
38000     var hide = function(noanim){
38001         clearTimeout(dismissProc);
38002         clearTimeout(hideProc);
38003         ce = null;
38004         if(el.isVisible()){
38005             esc.disable();
38006             if(noanim !== true && tm.animate){
38007                 el.fadeOut({callback: afterHide});
38008             }else{
38009                 afterHide();
38010             } 
38011         }
38012     };
38013     
38014     var afterHide = function(){
38015         el.hide();
38016         if(removeCls){
38017             el.removeClass(removeCls);
38018             removeCls = null;
38019         }
38020     };
38021     
38022     return {
38023         /**
38024         * @cfg {Number} minWidth
38025         * The minimum width of the quick tip (defaults to 40)
38026         */
38027        minWidth : 40,
38028         /**
38029         * @cfg {Number} maxWidth
38030         * The maximum width of the quick tip (defaults to 300)
38031         */
38032        maxWidth : 300,
38033         /**
38034         * @cfg {Boolean} interceptTitles
38035         * True to automatically use the element's DOM title value if available (defaults to false)
38036         */
38037        interceptTitles : false,
38038         /**
38039         * @cfg {Boolean} trackMouse
38040         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
38041         */
38042        trackMouse : false,
38043         /**
38044         * @cfg {Boolean} hideOnClick
38045         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
38046         */
38047        hideOnClick : true,
38048         /**
38049         * @cfg {Number} showDelay
38050         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
38051         */
38052        showDelay : 500,
38053         /**
38054         * @cfg {Number} hideDelay
38055         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
38056         */
38057        hideDelay : 200,
38058         /**
38059         * @cfg {Boolean} autoHide
38060         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
38061         * Used in conjunction with hideDelay.
38062         */
38063        autoHide : true,
38064         /**
38065         * @cfg {Boolean}
38066         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
38067         * (defaults to true).  Used in conjunction with autoDismissDelay.
38068         */
38069        autoDismiss : true,
38070         /**
38071         * @cfg {Number}
38072         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
38073         */
38074        autoDismissDelay : 5000,
38075        /**
38076         * @cfg {Boolean} animate
38077         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
38078         */
38079        animate : false,
38080
38081        /**
38082         * @cfg {String} title
38083         * Title text to display (defaults to '').  This can be any valid HTML markup.
38084         */
38085         title: '',
38086        /**
38087         * @cfg {String} text
38088         * Body text to display (defaults to '').  This can be any valid HTML markup.
38089         */
38090         text : '',
38091        /**
38092         * @cfg {String} cls
38093         * A CSS class to apply to the base quick tip element (defaults to '').
38094         */
38095         cls : '',
38096        /**
38097         * @cfg {Number} width
38098         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
38099         * minWidth or maxWidth.
38100         */
38101         width : null,
38102
38103     /**
38104      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
38105      * or display QuickTips in a page.
38106      */
38107        init : function(){
38108           tm = Roo.QuickTips;
38109           cfg = tm.tagConfig;
38110           if(!inited){
38111               if(!Roo.isReady){ // allow calling of init() before onReady
38112                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
38113                   return;
38114               }
38115               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
38116               el.fxDefaults = {stopFx: true};
38117               // maximum custom styling
38118               //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>');
38119               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>');              
38120               tipTitle = el.child('h3');
38121               tipTitle.enableDisplayMode("block");
38122               tipBody = el.child('div.x-tip-bd');
38123               tipBodyText = el.child('div.x-tip-bd-inner');
38124               //bdLeft = el.child('div.x-tip-bd-left');
38125               //bdRight = el.child('div.x-tip-bd-right');
38126               close = el.child('div.x-tip-close');
38127               close.enableDisplayMode("block");
38128               close.on("click", hide);
38129               var d = Roo.get(document);
38130               d.on("mousedown", onDown);
38131               d.on("mouseover", onOver);
38132               d.on("mouseout", onOut);
38133               d.on("mousemove", onMove);
38134               esc = d.addKeyListener(27, hide);
38135               esc.disable();
38136               if(Roo.dd.DD){
38137                   dd = el.initDD("default", null, {
38138                       onDrag : function(){
38139                           el.sync();  
38140                       }
38141                   });
38142                   dd.setHandleElId(tipTitle.id);
38143                   dd.lock();
38144               }
38145               inited = true;
38146           }
38147           this.enable(); 
38148        },
38149
38150     /**
38151      * Configures a new quick tip instance and assigns it to a target element.  The following config options
38152      * are supported:
38153      * <pre>
38154 Property    Type                   Description
38155 ----------  ---------------------  ------------------------------------------------------------------------
38156 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
38157      * </ul>
38158      * @param {Object} config The config object
38159      */
38160        register : function(config){
38161            var cs = config instanceof Array ? config : arguments;
38162            for(var i = 0, len = cs.length; i < len; i++) {
38163                var c = cs[i];
38164                var target = c.target;
38165                if(target){
38166                    if(target instanceof Array){
38167                        for(var j = 0, jlen = target.length; j < jlen; j++){
38168                            tagEls[target[j]] = c;
38169                        }
38170                    }else{
38171                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
38172                    }
38173                }
38174            }
38175        },
38176
38177     /**
38178      * Removes this quick tip from its element and destroys it.
38179      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
38180      */
38181        unregister : function(el){
38182            delete tagEls[Roo.id(el)];
38183        },
38184
38185     /**
38186      * Enable this quick tip.
38187      */
38188        enable : function(){
38189            if(inited && disabled){
38190                locks.pop();
38191                if(locks.length < 1){
38192                    disabled = false;
38193                }
38194            }
38195        },
38196
38197     /**
38198      * Disable this quick tip.
38199      */
38200        disable : function(){
38201           disabled = true;
38202           clearTimeout(showProc);
38203           clearTimeout(hideProc);
38204           clearTimeout(dismissProc);
38205           if(ce){
38206               hide(true);
38207           }
38208           locks.push(1);
38209        },
38210
38211     /**
38212      * Returns true if the quick tip is enabled, else false.
38213      */
38214        isEnabled : function(){
38215             return !disabled;
38216        },
38217
38218         // private
38219        tagConfig : {
38220            namespace : "roo", // was ext?? this may break..
38221            alt_namespace : "ext",
38222            attribute : "qtip",
38223            width : "width",
38224            target : "target",
38225            title : "qtitle",
38226            hide : "hide",
38227            cls : "qclass"
38228        }
38229    };
38230 }();
38231
38232 // backwards compat
38233 Roo.QuickTips.tips = Roo.QuickTips.register;/*
38234  * Based on:
38235  * Ext JS Library 1.1.1
38236  * Copyright(c) 2006-2007, Ext JS, LLC.
38237  *
38238  * Originally Released Under LGPL - original licence link has changed is not relivant.
38239  *
38240  * Fork - LGPL
38241  * <script type="text/javascript">
38242  */
38243  
38244
38245 /**
38246  * @class Roo.tree.TreePanel
38247  * @extends Roo.data.Tree
38248
38249  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
38250  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
38251  * @cfg {Boolean} enableDD true to enable drag and drop
38252  * @cfg {Boolean} enableDrag true to enable just drag
38253  * @cfg {Boolean} enableDrop true to enable just drop
38254  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
38255  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
38256  * @cfg {String} ddGroup The DD group this TreePanel belongs to
38257  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
38258  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
38259  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
38260  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
38261  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
38262  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
38263  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
38264  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
38265  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
38266  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
38267  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
38268  * @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>
38269  * @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>
38270  * 
38271  * @constructor
38272  * @param {String/HTMLElement/Element} el The container element
38273  * @param {Object} config
38274  */
38275 Roo.tree.TreePanel = function(el, config){
38276     var root = false;
38277     var loader = false;
38278     if (config.root) {
38279         root = config.root;
38280         delete config.root;
38281     }
38282     if (config.loader) {
38283         loader = config.loader;
38284         delete config.loader;
38285     }
38286     
38287     Roo.apply(this, config);
38288     Roo.tree.TreePanel.superclass.constructor.call(this);
38289     this.el = Roo.get(el);
38290     this.el.addClass('x-tree');
38291     //console.log(root);
38292     if (root) {
38293         this.setRootNode( Roo.factory(root, Roo.tree));
38294     }
38295     if (loader) {
38296         this.loader = Roo.factory(loader, Roo.tree);
38297     }
38298    /**
38299     * Read-only. The id of the container element becomes this TreePanel's id.
38300     */
38301     this.id = this.el.id;
38302     this.addEvents({
38303         /**
38304         * @event beforeload
38305         * Fires before a node is loaded, return false to cancel
38306         * @param {Node} node The node being loaded
38307         */
38308         "beforeload" : true,
38309         /**
38310         * @event load
38311         * Fires when a node is loaded
38312         * @param {Node} node The node that was loaded
38313         */
38314         "load" : true,
38315         /**
38316         * @event textchange
38317         * Fires when the text for a node is changed
38318         * @param {Node} node The node
38319         * @param {String} text The new text
38320         * @param {String} oldText The old text
38321         */
38322         "textchange" : true,
38323         /**
38324         * @event beforeexpand
38325         * Fires before a node is expanded, return false to cancel.
38326         * @param {Node} node The node
38327         * @param {Boolean} deep
38328         * @param {Boolean} anim
38329         */
38330         "beforeexpand" : true,
38331         /**
38332         * @event beforecollapse
38333         * Fires before a node is collapsed, return false to cancel.
38334         * @param {Node} node The node
38335         * @param {Boolean} deep
38336         * @param {Boolean} anim
38337         */
38338         "beforecollapse" : true,
38339         /**
38340         * @event expand
38341         * Fires when a node is expanded
38342         * @param {Node} node The node
38343         */
38344         "expand" : true,
38345         /**
38346         * @event disabledchange
38347         * Fires when the disabled status of a node changes
38348         * @param {Node} node The node
38349         * @param {Boolean} disabled
38350         */
38351         "disabledchange" : true,
38352         /**
38353         * @event collapse
38354         * Fires when a node is collapsed
38355         * @param {Node} node The node
38356         */
38357         "collapse" : true,
38358         /**
38359         * @event beforeclick
38360         * Fires before click processing on a node. Return false to cancel the default action.
38361         * @param {Node} node The node
38362         * @param {Roo.EventObject} e The event object
38363         */
38364         "beforeclick":true,
38365         /**
38366         * @event checkchange
38367         * Fires when a node with a checkbox's checked property changes
38368         * @param {Node} this This node
38369         * @param {Boolean} checked
38370         */
38371         "checkchange":true,
38372         /**
38373         * @event click
38374         * Fires when a node is clicked
38375         * @param {Node} node The node
38376         * @param {Roo.EventObject} e The event object
38377         */
38378         "click":true,
38379         /**
38380         * @event dblclick
38381         * Fires when a node is double clicked
38382         * @param {Node} node The node
38383         * @param {Roo.EventObject} e The event object
38384         */
38385         "dblclick":true,
38386         /**
38387         * @event contextmenu
38388         * Fires when a node is right clicked
38389         * @param {Node} node The node
38390         * @param {Roo.EventObject} e The event object
38391         */
38392         "contextmenu":true,
38393         /**
38394         * @event beforechildrenrendered
38395         * Fires right before the child nodes for a node are rendered
38396         * @param {Node} node The node
38397         */
38398         "beforechildrenrendered":true,
38399         /**
38400         * @event startdrag
38401         * Fires when a node starts being dragged
38402         * @param {Roo.tree.TreePanel} this
38403         * @param {Roo.tree.TreeNode} node
38404         * @param {event} e The raw browser event
38405         */ 
38406        "startdrag" : true,
38407        /**
38408         * @event enddrag
38409         * Fires when a drag operation is complete
38410         * @param {Roo.tree.TreePanel} this
38411         * @param {Roo.tree.TreeNode} node
38412         * @param {event} e The raw browser event
38413         */
38414        "enddrag" : true,
38415        /**
38416         * @event dragdrop
38417         * Fires when a dragged node is dropped on a valid DD target
38418         * @param {Roo.tree.TreePanel} this
38419         * @param {Roo.tree.TreeNode} node
38420         * @param {DD} dd The dd it was dropped on
38421         * @param {event} e The raw browser event
38422         */
38423        "dragdrop" : true,
38424        /**
38425         * @event beforenodedrop
38426         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
38427         * passed to handlers has the following properties:<br />
38428         * <ul style="padding:5px;padding-left:16px;">
38429         * <li>tree - The TreePanel</li>
38430         * <li>target - The node being targeted for the drop</li>
38431         * <li>data - The drag data from the drag source</li>
38432         * <li>point - The point of the drop - append, above or below</li>
38433         * <li>source - The drag source</li>
38434         * <li>rawEvent - Raw mouse event</li>
38435         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
38436         * to be inserted by setting them on this object.</li>
38437         * <li>cancel - Set this to true to cancel the drop.</li>
38438         * </ul>
38439         * @param {Object} dropEvent
38440         */
38441        "beforenodedrop" : true,
38442        /**
38443         * @event nodedrop
38444         * Fires after a DD object is dropped on a node in this tree. The dropEvent
38445         * passed to handlers has the following properties:<br />
38446         * <ul style="padding:5px;padding-left:16px;">
38447         * <li>tree - The TreePanel</li>
38448         * <li>target - The node being targeted for the drop</li>
38449         * <li>data - The drag data from the drag source</li>
38450         * <li>point - The point of the drop - append, above or below</li>
38451         * <li>source - The drag source</li>
38452         * <li>rawEvent - Raw mouse event</li>
38453         * <li>dropNode - Dropped node(s).</li>
38454         * </ul>
38455         * @param {Object} dropEvent
38456         */
38457        "nodedrop" : true,
38458         /**
38459         * @event nodedragover
38460         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
38461         * passed to handlers has the following properties:<br />
38462         * <ul style="padding:5px;padding-left:16px;">
38463         * <li>tree - The TreePanel</li>
38464         * <li>target - The node being targeted for the drop</li>
38465         * <li>data - The drag data from the drag source</li>
38466         * <li>point - The point of the drop - append, above or below</li>
38467         * <li>source - The drag source</li>
38468         * <li>rawEvent - Raw mouse event</li>
38469         * <li>dropNode - Drop node(s) provided by the source.</li>
38470         * <li>cancel - Set this to true to signal drop not allowed.</li>
38471         * </ul>
38472         * @param {Object} dragOverEvent
38473         */
38474        "nodedragover" : true
38475         
38476     });
38477     if(this.singleExpand){
38478        this.on("beforeexpand", this.restrictExpand, this);
38479     }
38480     if (this.editor) {
38481         this.editor.tree = this;
38482         this.editor = Roo.factory(this.editor, Roo.tree);
38483     }
38484     
38485     if (this.selModel) {
38486         this.selModel = Roo.factory(this.selModel, Roo.tree);
38487     }
38488    
38489 };
38490 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
38491     rootVisible : true,
38492     animate: Roo.enableFx,
38493     lines : true,
38494     enableDD : false,
38495     hlDrop : Roo.enableFx,
38496   
38497     renderer: false,
38498     
38499     rendererTip: false,
38500     // private
38501     restrictExpand : function(node){
38502         var p = node.parentNode;
38503         if(p){
38504             if(p.expandedChild && p.expandedChild.parentNode == p){
38505                 p.expandedChild.collapse();
38506             }
38507             p.expandedChild = node;
38508         }
38509     },
38510
38511     // private override
38512     setRootNode : function(node){
38513         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
38514         if(!this.rootVisible){
38515             node.ui = new Roo.tree.RootTreeNodeUI(node);
38516         }
38517         return node;
38518     },
38519
38520     /**
38521      * Returns the container element for this TreePanel
38522      */
38523     getEl : function(){
38524         return this.el;
38525     },
38526
38527     /**
38528      * Returns the default TreeLoader for this TreePanel
38529      */
38530     getLoader : function(){
38531         return this.loader;
38532     },
38533
38534     /**
38535      * Expand all nodes
38536      */
38537     expandAll : function(){
38538         this.root.expand(true);
38539     },
38540
38541     /**
38542      * Collapse all nodes
38543      */
38544     collapseAll : function(){
38545         this.root.collapse(true);
38546     },
38547
38548     /**
38549      * Returns the selection model used by this TreePanel
38550      */
38551     getSelectionModel : function(){
38552         if(!this.selModel){
38553             this.selModel = new Roo.tree.DefaultSelectionModel();
38554         }
38555         return this.selModel;
38556     },
38557
38558     /**
38559      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
38560      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
38561      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
38562      * @return {Array}
38563      */
38564     getChecked : function(a, startNode){
38565         startNode = startNode || this.root;
38566         var r = [];
38567         var f = function(){
38568             if(this.attributes.checked){
38569                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
38570             }
38571         }
38572         startNode.cascade(f);
38573         return r;
38574     },
38575
38576     /**
38577      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
38578      * @param {String} path
38579      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
38580      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
38581      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
38582      */
38583     expandPath : function(path, attr, callback){
38584         attr = attr || "id";
38585         var keys = path.split(this.pathSeparator);
38586         var curNode = this.root;
38587         if(curNode.attributes[attr] != keys[1]){ // invalid root
38588             if(callback){
38589                 callback(false, null);
38590             }
38591             return;
38592         }
38593         var index = 1;
38594         var f = function(){
38595             if(++index == keys.length){
38596                 if(callback){
38597                     callback(true, curNode);
38598                 }
38599                 return;
38600             }
38601             var c = curNode.findChild(attr, keys[index]);
38602             if(!c){
38603                 if(callback){
38604                     callback(false, curNode);
38605                 }
38606                 return;
38607             }
38608             curNode = c;
38609             c.expand(false, false, f);
38610         };
38611         curNode.expand(false, false, f);
38612     },
38613
38614     /**
38615      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
38616      * @param {String} path
38617      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
38618      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
38619      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
38620      */
38621     selectPath : function(path, attr, callback){
38622         attr = attr || "id";
38623         var keys = path.split(this.pathSeparator);
38624         var v = keys.pop();
38625         if(keys.length > 0){
38626             var f = function(success, node){
38627                 if(success && node){
38628                     var n = node.findChild(attr, v);
38629                     if(n){
38630                         n.select();
38631                         if(callback){
38632                             callback(true, n);
38633                         }
38634                     }else if(callback){
38635                         callback(false, n);
38636                     }
38637                 }else{
38638                     if(callback){
38639                         callback(false, n);
38640                     }
38641                 }
38642             };
38643             this.expandPath(keys.join(this.pathSeparator), attr, f);
38644         }else{
38645             this.root.select();
38646             if(callback){
38647                 callback(true, this.root);
38648             }
38649         }
38650     },
38651
38652     getTreeEl : function(){
38653         return this.el;
38654     },
38655
38656     /**
38657      * Trigger rendering of this TreePanel
38658      */
38659     render : function(){
38660         if (this.innerCt) {
38661             return this; // stop it rendering more than once!!
38662         }
38663         
38664         this.innerCt = this.el.createChild({tag:"ul",
38665                cls:"x-tree-root-ct " +
38666                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
38667
38668         if(this.containerScroll){
38669             Roo.dd.ScrollManager.register(this.el);
38670         }
38671         if((this.enableDD || this.enableDrop) && !this.dropZone){
38672            /**
38673             * The dropZone used by this tree if drop is enabled
38674             * @type Roo.tree.TreeDropZone
38675             */
38676              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
38677                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
38678            });
38679         }
38680         if((this.enableDD || this.enableDrag) && !this.dragZone){
38681            /**
38682             * The dragZone used by this tree if drag is enabled
38683             * @type Roo.tree.TreeDragZone
38684             */
38685             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
38686                ddGroup: this.ddGroup || "TreeDD",
38687                scroll: this.ddScroll
38688            });
38689         }
38690         this.getSelectionModel().init(this);
38691         if (!this.root) {
38692             Roo.log("ROOT not set in tree");
38693             return this;
38694         }
38695         this.root.render();
38696         if(!this.rootVisible){
38697             this.root.renderChildren();
38698         }
38699         return this;
38700     }
38701 });/*
38702  * Based on:
38703  * Ext JS Library 1.1.1
38704  * Copyright(c) 2006-2007, Ext JS, LLC.
38705  *
38706  * Originally Released Under LGPL - original licence link has changed is not relivant.
38707  *
38708  * Fork - LGPL
38709  * <script type="text/javascript">
38710  */
38711  
38712
38713 /**
38714  * @class Roo.tree.DefaultSelectionModel
38715  * @extends Roo.util.Observable
38716  * The default single selection for a TreePanel.
38717  * @param {Object} cfg Configuration
38718  */
38719 Roo.tree.DefaultSelectionModel = function(cfg){
38720    this.selNode = null;
38721    
38722    
38723    
38724    this.addEvents({
38725        /**
38726         * @event selectionchange
38727         * Fires when the selected node changes
38728         * @param {DefaultSelectionModel} this
38729         * @param {TreeNode} node the new selection
38730         */
38731        "selectionchange" : true,
38732
38733        /**
38734         * @event beforeselect
38735         * Fires before the selected node changes, return false to cancel the change
38736         * @param {DefaultSelectionModel} this
38737         * @param {TreeNode} node the new selection
38738         * @param {TreeNode} node the old selection
38739         */
38740        "beforeselect" : true
38741    });
38742    
38743     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
38744 };
38745
38746 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
38747     init : function(tree){
38748         this.tree = tree;
38749         tree.getTreeEl().on("keydown", this.onKeyDown, this);
38750         tree.on("click", this.onNodeClick, this);
38751     },
38752     
38753     onNodeClick : function(node, e){
38754         if (e.ctrlKey && this.selNode == node)  {
38755             this.unselect(node);
38756             return;
38757         }
38758         this.select(node);
38759     },
38760     
38761     /**
38762      * Select a node.
38763      * @param {TreeNode} node The node to select
38764      * @return {TreeNode} The selected node
38765      */
38766     select : function(node){
38767         var last = this.selNode;
38768         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
38769             if(last){
38770                 last.ui.onSelectedChange(false);
38771             }
38772             this.selNode = node;
38773             node.ui.onSelectedChange(true);
38774             this.fireEvent("selectionchange", this, node, last);
38775         }
38776         return node;
38777     },
38778     
38779     /**
38780      * Deselect a node.
38781      * @param {TreeNode} node The node to unselect
38782      */
38783     unselect : function(node){
38784         if(this.selNode == node){
38785             this.clearSelections();
38786         }    
38787     },
38788     
38789     /**
38790      * Clear all selections
38791      */
38792     clearSelections : function(){
38793         var n = this.selNode;
38794         if(n){
38795             n.ui.onSelectedChange(false);
38796             this.selNode = null;
38797             this.fireEvent("selectionchange", this, null);
38798         }
38799         return n;
38800     },
38801     
38802     /**
38803      * Get the selected node
38804      * @return {TreeNode} The selected node
38805      */
38806     getSelectedNode : function(){
38807         return this.selNode;    
38808     },
38809     
38810     /**
38811      * Returns true if the node is selected
38812      * @param {TreeNode} node The node to check
38813      * @return {Boolean}
38814      */
38815     isSelected : function(node){
38816         return this.selNode == node;  
38817     },
38818
38819     /**
38820      * Selects the node above the selected node in the tree, intelligently walking the nodes
38821      * @return TreeNode The new selection
38822      */
38823     selectPrevious : function(){
38824         var s = this.selNode || this.lastSelNode;
38825         if(!s){
38826             return null;
38827         }
38828         var ps = s.previousSibling;
38829         if(ps){
38830             if(!ps.isExpanded() || ps.childNodes.length < 1){
38831                 return this.select(ps);
38832             } else{
38833                 var lc = ps.lastChild;
38834                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
38835                     lc = lc.lastChild;
38836                 }
38837                 return this.select(lc);
38838             }
38839         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
38840             return this.select(s.parentNode);
38841         }
38842         return null;
38843     },
38844
38845     /**
38846      * Selects the node above the selected node in the tree, intelligently walking the nodes
38847      * @return TreeNode The new selection
38848      */
38849     selectNext : function(){
38850         var s = this.selNode || this.lastSelNode;
38851         if(!s){
38852             return null;
38853         }
38854         if(s.firstChild && s.isExpanded()){
38855              return this.select(s.firstChild);
38856          }else if(s.nextSibling){
38857              return this.select(s.nextSibling);
38858          }else if(s.parentNode){
38859             var newS = null;
38860             s.parentNode.bubble(function(){
38861                 if(this.nextSibling){
38862                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
38863                     return false;
38864                 }
38865             });
38866             return newS;
38867          }
38868         return null;
38869     },
38870
38871     onKeyDown : function(e){
38872         var s = this.selNode || this.lastSelNode;
38873         // undesirable, but required
38874         var sm = this;
38875         if(!s){
38876             return;
38877         }
38878         var k = e.getKey();
38879         switch(k){
38880              case e.DOWN:
38881                  e.stopEvent();
38882                  this.selectNext();
38883              break;
38884              case e.UP:
38885                  e.stopEvent();
38886                  this.selectPrevious();
38887              break;
38888              case e.RIGHT:
38889                  e.preventDefault();
38890                  if(s.hasChildNodes()){
38891                      if(!s.isExpanded()){
38892                          s.expand();
38893                      }else if(s.firstChild){
38894                          this.select(s.firstChild, e);
38895                      }
38896                  }
38897              break;
38898              case e.LEFT:
38899                  e.preventDefault();
38900                  if(s.hasChildNodes() && s.isExpanded()){
38901                      s.collapse();
38902                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
38903                      this.select(s.parentNode, e);
38904                  }
38905              break;
38906         };
38907     }
38908 });
38909
38910 /**
38911  * @class Roo.tree.MultiSelectionModel
38912  * @extends Roo.util.Observable
38913  * Multi selection for a TreePanel.
38914  * @param {Object} cfg Configuration
38915  */
38916 Roo.tree.MultiSelectionModel = function(){
38917    this.selNodes = [];
38918    this.selMap = {};
38919    this.addEvents({
38920        /**
38921         * @event selectionchange
38922         * Fires when the selected nodes change
38923         * @param {MultiSelectionModel} this
38924         * @param {Array} nodes Array of the selected nodes
38925         */
38926        "selectionchange" : true
38927    });
38928    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
38929    
38930 };
38931
38932 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
38933     init : function(tree){
38934         this.tree = tree;
38935         tree.getTreeEl().on("keydown", this.onKeyDown, this);
38936         tree.on("click", this.onNodeClick, this);
38937     },
38938     
38939     onNodeClick : function(node, e){
38940         this.select(node, e, e.ctrlKey);
38941     },
38942     
38943     /**
38944      * Select a node.
38945      * @param {TreeNode} node The node to select
38946      * @param {EventObject} e (optional) An event associated with the selection
38947      * @param {Boolean} keepExisting True to retain existing selections
38948      * @return {TreeNode} The selected node
38949      */
38950     select : function(node, e, keepExisting){
38951         if(keepExisting !== true){
38952             this.clearSelections(true);
38953         }
38954         if(this.isSelected(node)){
38955             this.lastSelNode = node;
38956             return node;
38957         }
38958         this.selNodes.push(node);
38959         this.selMap[node.id] = node;
38960         this.lastSelNode = node;
38961         node.ui.onSelectedChange(true);
38962         this.fireEvent("selectionchange", this, this.selNodes);
38963         return node;
38964     },
38965     
38966     /**
38967      * Deselect a node.
38968      * @param {TreeNode} node The node to unselect
38969      */
38970     unselect : function(node){
38971         if(this.selMap[node.id]){
38972             node.ui.onSelectedChange(false);
38973             var sn = this.selNodes;
38974             var index = -1;
38975             if(sn.indexOf){
38976                 index = sn.indexOf(node);
38977             }else{
38978                 for(var i = 0, len = sn.length; i < len; i++){
38979                     if(sn[i] == node){
38980                         index = i;
38981                         break;
38982                     }
38983                 }
38984             }
38985             if(index != -1){
38986                 this.selNodes.splice(index, 1);
38987             }
38988             delete this.selMap[node.id];
38989             this.fireEvent("selectionchange", this, this.selNodes);
38990         }
38991     },
38992     
38993     /**
38994      * Clear all selections
38995      */
38996     clearSelections : function(suppressEvent){
38997         var sn = this.selNodes;
38998         if(sn.length > 0){
38999             for(var i = 0, len = sn.length; i < len; i++){
39000                 sn[i].ui.onSelectedChange(false);
39001             }
39002             this.selNodes = [];
39003             this.selMap = {};
39004             if(suppressEvent !== true){
39005                 this.fireEvent("selectionchange", this, this.selNodes);
39006             }
39007         }
39008     },
39009     
39010     /**
39011      * Returns true if the node is selected
39012      * @param {TreeNode} node The node to check
39013      * @return {Boolean}
39014      */
39015     isSelected : function(node){
39016         return this.selMap[node.id] ? true : false;  
39017     },
39018     
39019     /**
39020      * Returns an array of the selected nodes
39021      * @return {Array}
39022      */
39023     getSelectedNodes : function(){
39024         return this.selNodes;    
39025     },
39026
39027     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
39028
39029     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
39030
39031     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
39032 });/*
39033  * Based on:
39034  * Ext JS Library 1.1.1
39035  * Copyright(c) 2006-2007, Ext JS, LLC.
39036  *
39037  * Originally Released Under LGPL - original licence link has changed is not relivant.
39038  *
39039  * Fork - LGPL
39040  * <script type="text/javascript">
39041  */
39042  
39043 /**
39044  * @class Roo.tree.TreeNode
39045  * @extends Roo.data.Node
39046  * @cfg {String} text The text for this node
39047  * @cfg {Boolean} expanded true to start the node expanded
39048  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
39049  * @cfg {Boolean} allowDrop false if this node cannot be drop on
39050  * @cfg {Boolean} disabled true to start the node disabled
39051  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
39052  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
39053  * @cfg {String} cls A css class to be added to the node
39054  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
39055  * @cfg {String} href URL of the link used for the node (defaults to #)
39056  * @cfg {String} hrefTarget target frame for the link
39057  * @cfg {String} qtip An Ext QuickTip for the node
39058  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
39059  * @cfg {Boolean} singleClickExpand True for single click expand on this node
39060  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
39061  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
39062  * (defaults to undefined with no checkbox rendered)
39063  * @constructor
39064  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
39065  */
39066 Roo.tree.TreeNode = function(attributes){
39067     attributes = attributes || {};
39068     if(typeof attributes == "string"){
39069         attributes = {text: attributes};
39070     }
39071     this.childrenRendered = false;
39072     this.rendered = false;
39073     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
39074     this.expanded = attributes.expanded === true;
39075     this.isTarget = attributes.isTarget !== false;
39076     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
39077     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
39078
39079     /**
39080      * Read-only. The text for this node. To change it use setText().
39081      * @type String
39082      */
39083     this.text = attributes.text;
39084     /**
39085      * True if this node is disabled.
39086      * @type Boolean
39087      */
39088     this.disabled = attributes.disabled === true;
39089
39090     this.addEvents({
39091         /**
39092         * @event textchange
39093         * Fires when the text for this node is changed
39094         * @param {Node} this This node
39095         * @param {String} text The new text
39096         * @param {String} oldText The old text
39097         */
39098         "textchange" : true,
39099         /**
39100         * @event beforeexpand
39101         * Fires before this node is expanded, return false to cancel.
39102         * @param {Node} this This node
39103         * @param {Boolean} deep
39104         * @param {Boolean} anim
39105         */
39106         "beforeexpand" : true,
39107         /**
39108         * @event beforecollapse
39109         * Fires before this node is collapsed, return false to cancel.
39110         * @param {Node} this This node
39111         * @param {Boolean} deep
39112         * @param {Boolean} anim
39113         */
39114         "beforecollapse" : true,
39115         /**
39116         * @event expand
39117         * Fires when this node is expanded
39118         * @param {Node} this This node
39119         */
39120         "expand" : true,
39121         /**
39122         * @event disabledchange
39123         * Fires when the disabled status of this node changes
39124         * @param {Node} this This node
39125         * @param {Boolean} disabled
39126         */
39127         "disabledchange" : true,
39128         /**
39129         * @event collapse
39130         * Fires when this node is collapsed
39131         * @param {Node} this This node
39132         */
39133         "collapse" : true,
39134         /**
39135         * @event beforeclick
39136         * Fires before click processing. Return false to cancel the default action.
39137         * @param {Node} this This node
39138         * @param {Roo.EventObject} e The event object
39139         */
39140         "beforeclick":true,
39141         /**
39142         * @event checkchange
39143         * Fires when a node with a checkbox's checked property changes
39144         * @param {Node} this This node
39145         * @param {Boolean} checked
39146         */
39147         "checkchange":true,
39148         /**
39149         * @event click
39150         * Fires when this node is clicked
39151         * @param {Node} this This node
39152         * @param {Roo.EventObject} e The event object
39153         */
39154         "click":true,
39155         /**
39156         * @event dblclick
39157         * Fires when this node is double clicked
39158         * @param {Node} this This node
39159         * @param {Roo.EventObject} e The event object
39160         */
39161         "dblclick":true,
39162         /**
39163         * @event contextmenu
39164         * Fires when this node is right clicked
39165         * @param {Node} this This node
39166         * @param {Roo.EventObject} e The event object
39167         */
39168         "contextmenu":true,
39169         /**
39170         * @event beforechildrenrendered
39171         * Fires right before the child nodes for this node are rendered
39172         * @param {Node} this This node
39173         */
39174         "beforechildrenrendered":true
39175     });
39176
39177     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
39178
39179     /**
39180      * Read-only. The UI for this node
39181      * @type TreeNodeUI
39182      */
39183     this.ui = new uiClass(this);
39184     
39185     // finally support items[]
39186     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
39187         return;
39188     }
39189     
39190     
39191     Roo.each(this.attributes.items, function(c) {
39192         this.appendChild(Roo.factory(c,Roo.Tree));
39193     }, this);
39194     delete this.attributes.items;
39195     
39196     
39197     
39198 };
39199 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
39200     preventHScroll: true,
39201     /**
39202      * Returns true if this node is expanded
39203      * @return {Boolean}
39204      */
39205     isExpanded : function(){
39206         return this.expanded;
39207     },
39208
39209     /**
39210      * Returns the UI object for this node
39211      * @return {TreeNodeUI}
39212      */
39213     getUI : function(){
39214         return this.ui;
39215     },
39216
39217     // private override
39218     setFirstChild : function(node){
39219         var of = this.firstChild;
39220         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
39221         if(this.childrenRendered && of && node != of){
39222             of.renderIndent(true, true);
39223         }
39224         if(this.rendered){
39225             this.renderIndent(true, true);
39226         }
39227     },
39228
39229     // private override
39230     setLastChild : function(node){
39231         var ol = this.lastChild;
39232         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
39233         if(this.childrenRendered && ol && node != ol){
39234             ol.renderIndent(true, true);
39235         }
39236         if(this.rendered){
39237             this.renderIndent(true, true);
39238         }
39239     },
39240
39241     // these methods are overridden to provide lazy rendering support
39242     // private override
39243     appendChild : function()
39244     {
39245         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
39246         if(node && this.childrenRendered){
39247             node.render();
39248         }
39249         this.ui.updateExpandIcon();
39250         return node;
39251     },
39252
39253     // private override
39254     removeChild : function(node){
39255         this.ownerTree.getSelectionModel().unselect(node);
39256         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
39257         // if it's been rendered remove dom node
39258         if(this.childrenRendered){
39259             node.ui.remove();
39260         }
39261         if(this.childNodes.length < 1){
39262             this.collapse(false, false);
39263         }else{
39264             this.ui.updateExpandIcon();
39265         }
39266         if(!this.firstChild) {
39267             this.childrenRendered = false;
39268         }
39269         return node;
39270     },
39271
39272     // private override
39273     insertBefore : function(node, refNode){
39274         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
39275         if(newNode && refNode && this.childrenRendered){
39276             node.render();
39277         }
39278         this.ui.updateExpandIcon();
39279         return newNode;
39280     },
39281
39282     /**
39283      * Sets the text for this node
39284      * @param {String} text
39285      */
39286     setText : function(text){
39287         var oldText = this.text;
39288         this.text = text;
39289         this.attributes.text = text;
39290         if(this.rendered){ // event without subscribing
39291             this.ui.onTextChange(this, text, oldText);
39292         }
39293         this.fireEvent("textchange", this, text, oldText);
39294     },
39295
39296     /**
39297      * Triggers selection of this node
39298      */
39299     select : function(){
39300         this.getOwnerTree().getSelectionModel().select(this);
39301     },
39302
39303     /**
39304      * Triggers deselection of this node
39305      */
39306     unselect : function(){
39307         this.getOwnerTree().getSelectionModel().unselect(this);
39308     },
39309
39310     /**
39311      * Returns true if this node is selected
39312      * @return {Boolean}
39313      */
39314     isSelected : function(){
39315         return this.getOwnerTree().getSelectionModel().isSelected(this);
39316     },
39317
39318     /**
39319      * Expand this node.
39320      * @param {Boolean} deep (optional) True to expand all children as well
39321      * @param {Boolean} anim (optional) false to cancel the default animation
39322      * @param {Function} callback (optional) A callback to be called when
39323      * expanding this node completes (does not wait for deep expand to complete).
39324      * Called with 1 parameter, this node.
39325      */
39326     expand : function(deep, anim, callback){
39327         if(!this.expanded){
39328             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
39329                 return;
39330             }
39331             if(!this.childrenRendered){
39332                 this.renderChildren();
39333             }
39334             this.expanded = true;
39335             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
39336                 this.ui.animExpand(function(){
39337                     this.fireEvent("expand", this);
39338                     if(typeof callback == "function"){
39339                         callback(this);
39340                     }
39341                     if(deep === true){
39342                         this.expandChildNodes(true);
39343                     }
39344                 }.createDelegate(this));
39345                 return;
39346             }else{
39347                 this.ui.expand();
39348                 this.fireEvent("expand", this);
39349                 if(typeof callback == "function"){
39350                     callback(this);
39351                 }
39352             }
39353         }else{
39354            if(typeof callback == "function"){
39355                callback(this);
39356            }
39357         }
39358         if(deep === true){
39359             this.expandChildNodes(true);
39360         }
39361     },
39362
39363     isHiddenRoot : function(){
39364         return this.isRoot && !this.getOwnerTree().rootVisible;
39365     },
39366
39367     /**
39368      * Collapse this node.
39369      * @param {Boolean} deep (optional) True to collapse all children as well
39370      * @param {Boolean} anim (optional) false to cancel the default animation
39371      */
39372     collapse : function(deep, anim){
39373         if(this.expanded && !this.isHiddenRoot()){
39374             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
39375                 return;
39376             }
39377             this.expanded = false;
39378             if((this.getOwnerTree().animate && anim !== false) || anim){
39379                 this.ui.animCollapse(function(){
39380                     this.fireEvent("collapse", this);
39381                     if(deep === true){
39382                         this.collapseChildNodes(true);
39383                     }
39384                 }.createDelegate(this));
39385                 return;
39386             }else{
39387                 this.ui.collapse();
39388                 this.fireEvent("collapse", this);
39389             }
39390         }
39391         if(deep === true){
39392             var cs = this.childNodes;
39393             for(var i = 0, len = cs.length; i < len; i++) {
39394                 cs[i].collapse(true, false);
39395             }
39396         }
39397     },
39398
39399     // private
39400     delayedExpand : function(delay){
39401         if(!this.expandProcId){
39402             this.expandProcId = this.expand.defer(delay, this);
39403         }
39404     },
39405
39406     // private
39407     cancelExpand : function(){
39408         if(this.expandProcId){
39409             clearTimeout(this.expandProcId);
39410         }
39411         this.expandProcId = false;
39412     },
39413
39414     /**
39415      * Toggles expanded/collapsed state of the node
39416      */
39417     toggle : function(){
39418         if(this.expanded){
39419             this.collapse();
39420         }else{
39421             this.expand();
39422         }
39423     },
39424
39425     /**
39426      * Ensures all parent nodes are expanded
39427      */
39428     ensureVisible : function(callback){
39429         var tree = this.getOwnerTree();
39430         tree.expandPath(this.parentNode.getPath(), false, function(){
39431             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
39432             Roo.callback(callback);
39433         }.createDelegate(this));
39434     },
39435
39436     /**
39437      * Expand all child nodes
39438      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
39439      */
39440     expandChildNodes : function(deep){
39441         var cs = this.childNodes;
39442         for(var i = 0, len = cs.length; i < len; i++) {
39443                 cs[i].expand(deep);
39444         }
39445     },
39446
39447     /**
39448      * Collapse all child nodes
39449      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
39450      */
39451     collapseChildNodes : function(deep){
39452         var cs = this.childNodes;
39453         for(var i = 0, len = cs.length; i < len; i++) {
39454                 cs[i].collapse(deep);
39455         }
39456     },
39457
39458     /**
39459      * Disables this node
39460      */
39461     disable : function(){
39462         this.disabled = true;
39463         this.unselect();
39464         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
39465             this.ui.onDisableChange(this, true);
39466         }
39467         this.fireEvent("disabledchange", this, true);
39468     },
39469
39470     /**
39471      * Enables this node
39472      */
39473     enable : function(){
39474         this.disabled = false;
39475         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
39476             this.ui.onDisableChange(this, false);
39477         }
39478         this.fireEvent("disabledchange", this, false);
39479     },
39480
39481     // private
39482     renderChildren : function(suppressEvent){
39483         if(suppressEvent !== false){
39484             this.fireEvent("beforechildrenrendered", this);
39485         }
39486         var cs = this.childNodes;
39487         for(var i = 0, len = cs.length; i < len; i++){
39488             cs[i].render(true);
39489         }
39490         this.childrenRendered = true;
39491     },
39492
39493     // private
39494     sort : function(fn, scope){
39495         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
39496         if(this.childrenRendered){
39497             var cs = this.childNodes;
39498             for(var i = 0, len = cs.length; i < len; i++){
39499                 cs[i].render(true);
39500             }
39501         }
39502     },
39503
39504     // private
39505     render : function(bulkRender){
39506         this.ui.render(bulkRender);
39507         if(!this.rendered){
39508             this.rendered = true;
39509             if(this.expanded){
39510                 this.expanded = false;
39511                 this.expand(false, false);
39512             }
39513         }
39514     },
39515
39516     // private
39517     renderIndent : function(deep, refresh){
39518         if(refresh){
39519             this.ui.childIndent = null;
39520         }
39521         this.ui.renderIndent();
39522         if(deep === true && this.childrenRendered){
39523             var cs = this.childNodes;
39524             for(var i = 0, len = cs.length; i < len; i++){
39525                 cs[i].renderIndent(true, refresh);
39526             }
39527         }
39528     }
39529 });/*
39530  * Based on:
39531  * Ext JS Library 1.1.1
39532  * Copyright(c) 2006-2007, Ext JS, LLC.
39533  *
39534  * Originally Released Under LGPL - original licence link has changed is not relivant.
39535  *
39536  * Fork - LGPL
39537  * <script type="text/javascript">
39538  */
39539  
39540 /**
39541  * @class Roo.tree.AsyncTreeNode
39542  * @extends Roo.tree.TreeNode
39543  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
39544  * @constructor
39545  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
39546  */
39547  Roo.tree.AsyncTreeNode = function(config){
39548     this.loaded = false;
39549     this.loading = false;
39550     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
39551     /**
39552     * @event beforeload
39553     * Fires before this node is loaded, return false to cancel
39554     * @param {Node} this This node
39555     */
39556     this.addEvents({'beforeload':true, 'load': true});
39557     /**
39558     * @event load
39559     * Fires when this node is loaded
39560     * @param {Node} this This node
39561     */
39562     /**
39563      * The loader used by this node (defaults to using the tree's defined loader)
39564      * @type TreeLoader
39565      * @property loader
39566      */
39567 };
39568 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
39569     expand : function(deep, anim, callback){
39570         if(this.loading){ // if an async load is already running, waiting til it's done
39571             var timer;
39572             var f = function(){
39573                 if(!this.loading){ // done loading
39574                     clearInterval(timer);
39575                     this.expand(deep, anim, callback);
39576                 }
39577             }.createDelegate(this);
39578             timer = setInterval(f, 200);
39579             return;
39580         }
39581         if(!this.loaded){
39582             if(this.fireEvent("beforeload", this) === false){
39583                 return;
39584             }
39585             this.loading = true;
39586             this.ui.beforeLoad(this);
39587             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
39588             if(loader){
39589                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
39590                 return;
39591             }
39592         }
39593         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
39594     },
39595     
39596     /**
39597      * Returns true if this node is currently loading
39598      * @return {Boolean}
39599      */
39600     isLoading : function(){
39601         return this.loading;  
39602     },
39603     
39604     loadComplete : function(deep, anim, callback){
39605         this.loading = false;
39606         this.loaded = true;
39607         this.ui.afterLoad(this);
39608         this.fireEvent("load", this);
39609         this.expand(deep, anim, callback);
39610     },
39611     
39612     /**
39613      * Returns true if this node has been loaded
39614      * @return {Boolean}
39615      */
39616     isLoaded : function(){
39617         return this.loaded;
39618     },
39619     
39620     hasChildNodes : function(){
39621         if(!this.isLeaf() && !this.loaded){
39622             return true;
39623         }else{
39624             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
39625         }
39626     },
39627
39628     /**
39629      * Trigger a reload for this node
39630      * @param {Function} callback
39631      */
39632     reload : function(callback){
39633         this.collapse(false, false);
39634         while(this.firstChild){
39635             this.removeChild(this.firstChild);
39636         }
39637         this.childrenRendered = false;
39638         this.loaded = false;
39639         if(this.isHiddenRoot()){
39640             this.expanded = false;
39641         }
39642         this.expand(false, false, callback);
39643     }
39644 });/*
39645  * Based on:
39646  * Ext JS Library 1.1.1
39647  * Copyright(c) 2006-2007, Ext JS, LLC.
39648  *
39649  * Originally Released Under LGPL - original licence link has changed is not relivant.
39650  *
39651  * Fork - LGPL
39652  * <script type="text/javascript">
39653  */
39654  
39655 /**
39656  * @class Roo.tree.TreeNodeUI
39657  * @constructor
39658  * @param {Object} node The node to render
39659  * The TreeNode UI implementation is separate from the
39660  * tree implementation. Unless you are customizing the tree UI,
39661  * you should never have to use this directly.
39662  */
39663 Roo.tree.TreeNodeUI = function(node){
39664     this.node = node;
39665     this.rendered = false;
39666     this.animating = false;
39667     this.emptyIcon = Roo.BLANK_IMAGE_URL;
39668 };
39669
39670 Roo.tree.TreeNodeUI.prototype = {
39671     removeChild : function(node){
39672         if(this.rendered){
39673             this.ctNode.removeChild(node.ui.getEl());
39674         }
39675     },
39676
39677     beforeLoad : function(){
39678          this.addClass("x-tree-node-loading");
39679     },
39680
39681     afterLoad : function(){
39682          this.removeClass("x-tree-node-loading");
39683     },
39684
39685     onTextChange : function(node, text, oldText){
39686         if(this.rendered){
39687             this.textNode.innerHTML = text;
39688         }
39689     },
39690
39691     onDisableChange : function(node, state){
39692         this.disabled = state;
39693         if(state){
39694             this.addClass("x-tree-node-disabled");
39695         }else{
39696             this.removeClass("x-tree-node-disabled");
39697         }
39698     },
39699
39700     onSelectedChange : function(state){
39701         if(state){
39702             this.focus();
39703             this.addClass("x-tree-selected");
39704         }else{
39705             //this.blur();
39706             this.removeClass("x-tree-selected");
39707         }
39708     },
39709
39710     onMove : function(tree, node, oldParent, newParent, index, refNode){
39711         this.childIndent = null;
39712         if(this.rendered){
39713             var targetNode = newParent.ui.getContainer();
39714             if(!targetNode){//target not rendered
39715                 this.holder = document.createElement("div");
39716                 this.holder.appendChild(this.wrap);
39717                 return;
39718             }
39719             var insertBefore = refNode ? refNode.ui.getEl() : null;
39720             if(insertBefore){
39721                 targetNode.insertBefore(this.wrap, insertBefore);
39722             }else{
39723                 targetNode.appendChild(this.wrap);
39724             }
39725             this.node.renderIndent(true);
39726         }
39727     },
39728
39729     addClass : function(cls){
39730         if(this.elNode){
39731             Roo.fly(this.elNode).addClass(cls);
39732         }
39733     },
39734
39735     removeClass : function(cls){
39736         if(this.elNode){
39737             Roo.fly(this.elNode).removeClass(cls);
39738         }
39739     },
39740
39741     remove : function(){
39742         if(this.rendered){
39743             this.holder = document.createElement("div");
39744             this.holder.appendChild(this.wrap);
39745         }
39746     },
39747
39748     fireEvent : function(){
39749         return this.node.fireEvent.apply(this.node, arguments);
39750     },
39751
39752     initEvents : function(){
39753         this.node.on("move", this.onMove, this);
39754         var E = Roo.EventManager;
39755         var a = this.anchor;
39756
39757         var el = Roo.fly(a, '_treeui');
39758
39759         if(Roo.isOpera){ // opera render bug ignores the CSS
39760             el.setStyle("text-decoration", "none");
39761         }
39762
39763         el.on("click", this.onClick, this);
39764         el.on("dblclick", this.onDblClick, this);
39765
39766         if(this.checkbox){
39767             Roo.EventManager.on(this.checkbox,
39768                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
39769         }
39770
39771         el.on("contextmenu", this.onContextMenu, this);
39772
39773         var icon = Roo.fly(this.iconNode);
39774         icon.on("click", this.onClick, this);
39775         icon.on("dblclick", this.onDblClick, this);
39776         icon.on("contextmenu", this.onContextMenu, this);
39777         E.on(this.ecNode, "click", this.ecClick, this, true);
39778
39779         if(this.node.disabled){
39780             this.addClass("x-tree-node-disabled");
39781         }
39782         if(this.node.hidden){
39783             this.addClass("x-tree-node-disabled");
39784         }
39785         var ot = this.node.getOwnerTree();
39786         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
39787         if(dd && (!this.node.isRoot || ot.rootVisible)){
39788             Roo.dd.Registry.register(this.elNode, {
39789                 node: this.node,
39790                 handles: this.getDDHandles(),
39791                 isHandle: false
39792             });
39793         }
39794     },
39795
39796     getDDHandles : function(){
39797         return [this.iconNode, this.textNode];
39798     },
39799
39800     hide : function(){
39801         if(this.rendered){
39802             this.wrap.style.display = "none";
39803         }
39804     },
39805
39806     show : function(){
39807         if(this.rendered){
39808             this.wrap.style.display = "";
39809         }
39810     },
39811
39812     onContextMenu : function(e){
39813         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
39814             e.preventDefault();
39815             this.focus();
39816             this.fireEvent("contextmenu", this.node, e);
39817         }
39818     },
39819
39820     onClick : function(e){
39821         if(this.dropping){
39822             e.stopEvent();
39823             return;
39824         }
39825         if(this.fireEvent("beforeclick", this.node, e) !== false){
39826             if(!this.disabled && this.node.attributes.href){
39827                 this.fireEvent("click", this.node, e);
39828                 return;
39829             }
39830             e.preventDefault();
39831             if(this.disabled){
39832                 return;
39833             }
39834
39835             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
39836                 this.node.toggle();
39837             }
39838
39839             this.fireEvent("click", this.node, e);
39840         }else{
39841             e.stopEvent();
39842         }
39843     },
39844
39845     onDblClick : function(e){
39846         e.preventDefault();
39847         if(this.disabled){
39848             return;
39849         }
39850         if(this.checkbox){
39851             this.toggleCheck();
39852         }
39853         if(!this.animating && this.node.hasChildNodes()){
39854             this.node.toggle();
39855         }
39856         this.fireEvent("dblclick", this.node, e);
39857     },
39858
39859     onCheckChange : function(){
39860         var checked = this.checkbox.checked;
39861         this.node.attributes.checked = checked;
39862         this.fireEvent('checkchange', this.node, checked);
39863     },
39864
39865     ecClick : function(e){
39866         if(!this.animating && this.node.hasChildNodes()){
39867             this.node.toggle();
39868         }
39869     },
39870
39871     startDrop : function(){
39872         this.dropping = true;
39873     },
39874
39875     // delayed drop so the click event doesn't get fired on a drop
39876     endDrop : function(){
39877        setTimeout(function(){
39878            this.dropping = false;
39879        }.createDelegate(this), 50);
39880     },
39881
39882     expand : function(){
39883         this.updateExpandIcon();
39884         this.ctNode.style.display = "";
39885     },
39886
39887     focus : function(){
39888         if(!this.node.preventHScroll){
39889             try{this.anchor.focus();
39890             }catch(e){}
39891         }else if(!Roo.isIE){
39892             try{
39893                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
39894                 var l = noscroll.scrollLeft;
39895                 this.anchor.focus();
39896                 noscroll.scrollLeft = l;
39897             }catch(e){}
39898         }
39899     },
39900
39901     toggleCheck : function(value){
39902         var cb = this.checkbox;
39903         if(cb){
39904             cb.checked = (value === undefined ? !cb.checked : value);
39905         }
39906     },
39907
39908     blur : function(){
39909         try{
39910             this.anchor.blur();
39911         }catch(e){}
39912     },
39913
39914     animExpand : function(callback){
39915         var ct = Roo.get(this.ctNode);
39916         ct.stopFx();
39917         if(!this.node.hasChildNodes()){
39918             this.updateExpandIcon();
39919             this.ctNode.style.display = "";
39920             Roo.callback(callback);
39921             return;
39922         }
39923         this.animating = true;
39924         this.updateExpandIcon();
39925
39926         ct.slideIn('t', {
39927            callback : function(){
39928                this.animating = false;
39929                Roo.callback(callback);
39930             },
39931             scope: this,
39932             duration: this.node.ownerTree.duration || .25
39933         });
39934     },
39935
39936     highlight : function(){
39937         var tree = this.node.getOwnerTree();
39938         Roo.fly(this.wrap).highlight(
39939             tree.hlColor || "C3DAF9",
39940             {endColor: tree.hlBaseColor}
39941         );
39942     },
39943
39944     collapse : function(){
39945         this.updateExpandIcon();
39946         this.ctNode.style.display = "none";
39947     },
39948
39949     animCollapse : function(callback){
39950         var ct = Roo.get(this.ctNode);
39951         ct.enableDisplayMode('block');
39952         ct.stopFx();
39953
39954         this.animating = true;
39955         this.updateExpandIcon();
39956
39957         ct.slideOut('t', {
39958             callback : function(){
39959                this.animating = false;
39960                Roo.callback(callback);
39961             },
39962             scope: this,
39963             duration: this.node.ownerTree.duration || .25
39964         });
39965     },
39966
39967     getContainer : function(){
39968         return this.ctNode;
39969     },
39970
39971     getEl : function(){
39972         return this.wrap;
39973     },
39974
39975     appendDDGhost : function(ghostNode){
39976         ghostNode.appendChild(this.elNode.cloneNode(true));
39977     },
39978
39979     getDDRepairXY : function(){
39980         return Roo.lib.Dom.getXY(this.iconNode);
39981     },
39982
39983     onRender : function(){
39984         this.render();
39985     },
39986
39987     render : function(bulkRender){
39988         var n = this.node, a = n.attributes;
39989         var targetNode = n.parentNode ?
39990               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
39991
39992         if(!this.rendered){
39993             this.rendered = true;
39994
39995             this.renderElements(n, a, targetNode, bulkRender);
39996
39997             if(a.qtip){
39998                if(this.textNode.setAttributeNS){
39999                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
40000                    if(a.qtipTitle){
40001                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
40002                    }
40003                }else{
40004                    this.textNode.setAttribute("ext:qtip", a.qtip);
40005                    if(a.qtipTitle){
40006                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
40007                    }
40008                }
40009             }else if(a.qtipCfg){
40010                 a.qtipCfg.target = Roo.id(this.textNode);
40011                 Roo.QuickTips.register(a.qtipCfg);
40012             }
40013             this.initEvents();
40014             if(!this.node.expanded){
40015                 this.updateExpandIcon();
40016             }
40017         }else{
40018             if(bulkRender === true) {
40019                 targetNode.appendChild(this.wrap);
40020             }
40021         }
40022     },
40023
40024     renderElements : function(n, a, targetNode, bulkRender)
40025     {
40026         // add some indent caching, this helps performance when rendering a large tree
40027         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
40028         var t = n.getOwnerTree();
40029         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
40030         if (typeof(n.attributes.html) != 'undefined') {
40031             txt = n.attributes.html;
40032         }
40033         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
40034         var cb = typeof a.checked == 'boolean';
40035         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
40036         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
40037             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
40038             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
40039             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
40040             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
40041             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
40042              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
40043                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
40044             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
40045             "</li>"];
40046
40047         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
40048             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
40049                                 n.nextSibling.ui.getEl(), buf.join(""));
40050         }else{
40051             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
40052         }
40053
40054         this.elNode = this.wrap.childNodes[0];
40055         this.ctNode = this.wrap.childNodes[1];
40056         var cs = this.elNode.childNodes;
40057         this.indentNode = cs[0];
40058         this.ecNode = cs[1];
40059         this.iconNode = cs[2];
40060         var index = 3;
40061         if(cb){
40062             this.checkbox = cs[3];
40063             index++;
40064         }
40065         this.anchor = cs[index];
40066         this.textNode = cs[index].firstChild;
40067     },
40068
40069     getAnchor : function(){
40070         return this.anchor;
40071     },
40072
40073     getTextEl : function(){
40074         return this.textNode;
40075     },
40076
40077     getIconEl : function(){
40078         return this.iconNode;
40079     },
40080
40081     isChecked : function(){
40082         return this.checkbox ? this.checkbox.checked : false;
40083     },
40084
40085     updateExpandIcon : function(){
40086         if(this.rendered){
40087             var n = this.node, c1, c2;
40088             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
40089             var hasChild = n.hasChildNodes();
40090             if(hasChild){
40091                 if(n.expanded){
40092                     cls += "-minus";
40093                     c1 = "x-tree-node-collapsed";
40094                     c2 = "x-tree-node-expanded";
40095                 }else{
40096                     cls += "-plus";
40097                     c1 = "x-tree-node-expanded";
40098                     c2 = "x-tree-node-collapsed";
40099                 }
40100                 if(this.wasLeaf){
40101                     this.removeClass("x-tree-node-leaf");
40102                     this.wasLeaf = false;
40103                 }
40104                 if(this.c1 != c1 || this.c2 != c2){
40105                     Roo.fly(this.elNode).replaceClass(c1, c2);
40106                     this.c1 = c1; this.c2 = c2;
40107                 }
40108             }else{
40109                 // this changes non-leafs into leafs if they have no children.
40110                 // it's not very rational behaviour..
40111                 
40112                 if(!this.wasLeaf && this.node.leaf){
40113                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
40114                     delete this.c1;
40115                     delete this.c2;
40116                     this.wasLeaf = true;
40117                 }
40118             }
40119             var ecc = "x-tree-ec-icon "+cls;
40120             if(this.ecc != ecc){
40121                 this.ecNode.className = ecc;
40122                 this.ecc = ecc;
40123             }
40124         }
40125     },
40126
40127     getChildIndent : function(){
40128         if(!this.childIndent){
40129             var buf = [];
40130             var p = this.node;
40131             while(p){
40132                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
40133                     if(!p.isLast()) {
40134                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
40135                     } else {
40136                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
40137                     }
40138                 }
40139                 p = p.parentNode;
40140             }
40141             this.childIndent = buf.join("");
40142         }
40143         return this.childIndent;
40144     },
40145
40146     renderIndent : function(){
40147         if(this.rendered){
40148             var indent = "";
40149             var p = this.node.parentNode;
40150             if(p){
40151                 indent = p.ui.getChildIndent();
40152             }
40153             if(this.indentMarkup != indent){ // don't rerender if not required
40154                 this.indentNode.innerHTML = indent;
40155                 this.indentMarkup = indent;
40156             }
40157             this.updateExpandIcon();
40158         }
40159     }
40160 };
40161
40162 Roo.tree.RootTreeNodeUI = function(){
40163     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
40164 };
40165 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
40166     render : function(){
40167         if(!this.rendered){
40168             var targetNode = this.node.ownerTree.innerCt.dom;
40169             this.node.expanded = true;
40170             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
40171             this.wrap = this.ctNode = targetNode.firstChild;
40172         }
40173     },
40174     collapse : function(){
40175     },
40176     expand : function(){
40177     }
40178 });/*
40179  * Based on:
40180  * Ext JS Library 1.1.1
40181  * Copyright(c) 2006-2007, Ext JS, LLC.
40182  *
40183  * Originally Released Under LGPL - original licence link has changed is not relivant.
40184  *
40185  * Fork - LGPL
40186  * <script type="text/javascript">
40187  */
40188 /**
40189  * @class Roo.tree.TreeLoader
40190  * @extends Roo.util.Observable
40191  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
40192  * nodes from a specified URL. The response must be a javascript Array definition
40193  * who's elements are node definition objects. eg:
40194  * <pre><code>
40195 {  success : true,
40196    data :      [
40197    
40198     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
40199     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
40200     ]
40201 }
40202
40203
40204 </code></pre>
40205  * <br><br>
40206  * The old style respose with just an array is still supported, but not recommended.
40207  * <br><br>
40208  *
40209  * A server request is sent, and child nodes are loaded only when a node is expanded.
40210  * The loading node's id is passed to the server under the parameter name "node" to
40211  * enable the server to produce the correct child nodes.
40212  * <br><br>
40213  * To pass extra parameters, an event handler may be attached to the "beforeload"
40214  * event, and the parameters specified in the TreeLoader's baseParams property:
40215  * <pre><code>
40216     myTreeLoader.on("beforeload", function(treeLoader, node) {
40217         this.baseParams.category = node.attributes.category;
40218     }, this);
40219 </code></pre><
40220  * This would pass an HTTP parameter called "category" to the server containing
40221  * the value of the Node's "category" attribute.
40222  * @constructor
40223  * Creates a new Treeloader.
40224  * @param {Object} config A config object containing config properties.
40225  */
40226 Roo.tree.TreeLoader = function(config){
40227     this.baseParams = {};
40228     this.requestMethod = "POST";
40229     Roo.apply(this, config);
40230
40231     this.addEvents({
40232     
40233         /**
40234          * @event beforeload
40235          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
40236          * @param {Object} This TreeLoader object.
40237          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
40238          * @param {Object} callback The callback function specified in the {@link #load} call.
40239          */
40240         beforeload : true,
40241         /**
40242          * @event load
40243          * Fires when the node has been successfuly loaded.
40244          * @param {Object} This TreeLoader object.
40245          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
40246          * @param {Object} response The response object containing the data from the server.
40247          */
40248         load : true,
40249         /**
40250          * @event loadexception
40251          * Fires if the network request failed.
40252          * @param {Object} This TreeLoader object.
40253          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
40254          * @param {Object} response The response object containing the data from the server.
40255          */
40256         loadexception : true,
40257         /**
40258          * @event create
40259          * Fires before a node is created, enabling you to return custom Node types 
40260          * @param {Object} This TreeLoader object.
40261          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
40262          */
40263         create : true
40264     });
40265
40266     Roo.tree.TreeLoader.superclass.constructor.call(this);
40267 };
40268
40269 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
40270     /**
40271     * @cfg {String} dataUrl The URL from which to request a Json string which
40272     * specifies an array of node definition object representing the child nodes
40273     * to be loaded.
40274     */
40275     /**
40276     * @cfg {String} requestMethod either GET or POST
40277     * defaults to POST (due to BC)
40278     * to be loaded.
40279     */
40280     /**
40281     * @cfg {Object} baseParams (optional) An object containing properties which
40282     * specify HTTP parameters to be passed to each request for child nodes.
40283     */
40284     /**
40285     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
40286     * created by this loader. If the attributes sent by the server have an attribute in this object,
40287     * they take priority.
40288     */
40289     /**
40290     * @cfg {Object} uiProviders (optional) An object containing properties which
40291     * 
40292     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
40293     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
40294     * <i>uiProvider</i> attribute of a returned child node is a string rather
40295     * than a reference to a TreeNodeUI implementation, this that string value
40296     * is used as a property name in the uiProviders object. You can define the provider named
40297     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
40298     */
40299     uiProviders : {},
40300
40301     /**
40302     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
40303     * child nodes before loading.
40304     */
40305     clearOnLoad : true,
40306
40307     /**
40308     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
40309     * property on loading, rather than expecting an array. (eg. more compatible to a standard
40310     * Grid query { data : [ .....] }
40311     */
40312     
40313     root : false,
40314      /**
40315     * @cfg {String} queryParam (optional) 
40316     * Name of the query as it will be passed on the querystring (defaults to 'node')
40317     * eg. the request will be ?node=[id]
40318     */
40319     
40320     
40321     queryParam: false,
40322     
40323     /**
40324      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
40325      * This is called automatically when a node is expanded, but may be used to reload
40326      * a node (or append new children if the {@link #clearOnLoad} option is false.)
40327      * @param {Roo.tree.TreeNode} node
40328      * @param {Function} callback
40329      */
40330     load : function(node, callback){
40331         if(this.clearOnLoad){
40332             while(node.firstChild){
40333                 node.removeChild(node.firstChild);
40334             }
40335         }
40336         if(node.attributes.children){ // preloaded json children
40337             var cs = node.attributes.children;
40338             for(var i = 0, len = cs.length; i < len; i++){
40339                 node.appendChild(this.createNode(cs[i]));
40340             }
40341             if(typeof callback == "function"){
40342                 callback();
40343             }
40344         }else if(this.dataUrl){
40345             this.requestData(node, callback);
40346         }
40347     },
40348
40349     getParams: function(node){
40350         var buf = [], bp = this.baseParams;
40351         for(var key in bp){
40352             if(typeof bp[key] != "function"){
40353                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
40354             }
40355         }
40356         var n = this.queryParam === false ? 'node' : this.queryParam;
40357         buf.push(n + "=", encodeURIComponent(node.id));
40358         return buf.join("");
40359     },
40360
40361     requestData : function(node, callback){
40362         if(this.fireEvent("beforeload", this, node, callback) !== false){
40363             this.transId = Roo.Ajax.request({
40364                 method:this.requestMethod,
40365                 url: this.dataUrl||this.url,
40366                 success: this.handleResponse,
40367                 failure: this.handleFailure,
40368                 scope: this,
40369                 argument: {callback: callback, node: node},
40370                 params: this.getParams(node)
40371             });
40372         }else{
40373             // if the load is cancelled, make sure we notify
40374             // the node that we are done
40375             if(typeof callback == "function"){
40376                 callback();
40377             }
40378         }
40379     },
40380
40381     isLoading : function(){
40382         return this.transId ? true : false;
40383     },
40384
40385     abort : function(){
40386         if(this.isLoading()){
40387             Roo.Ajax.abort(this.transId);
40388         }
40389     },
40390
40391     // private
40392     createNode : function(attr)
40393     {
40394         // apply baseAttrs, nice idea Corey!
40395         if(this.baseAttrs){
40396             Roo.applyIf(attr, this.baseAttrs);
40397         }
40398         if(this.applyLoader !== false){
40399             attr.loader = this;
40400         }
40401         // uiProvider = depreciated..
40402         
40403         if(typeof(attr.uiProvider) == 'string'){
40404            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
40405                 /**  eval:var:attr */ eval(attr.uiProvider);
40406         }
40407         if(typeof(this.uiProviders['default']) != 'undefined') {
40408             attr.uiProvider = this.uiProviders['default'];
40409         }
40410         
40411         this.fireEvent('create', this, attr);
40412         
40413         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
40414         return(attr.leaf ?
40415                         new Roo.tree.TreeNode(attr) :
40416                         new Roo.tree.AsyncTreeNode(attr));
40417     },
40418
40419     processResponse : function(response, node, callback)
40420     {
40421         var json = response.responseText;
40422         try {
40423             
40424             var o = Roo.decode(json);
40425             
40426             if (this.root === false && typeof(o.success) != undefined) {
40427                 this.root = 'data'; // the default behaviour for list like data..
40428                 }
40429                 
40430             if (this.root !== false &&  !o.success) {
40431                 // it's a failure condition.
40432                 var a = response.argument;
40433                 this.fireEvent("loadexception", this, a.node, response);
40434                 Roo.log("Load failed - should have a handler really");
40435                 return;
40436             }
40437             
40438             
40439             
40440             if (this.root !== false) {
40441                  o = o[this.root];
40442             }
40443             
40444             for(var i = 0, len = o.length; i < len; i++){
40445                 var n = this.createNode(o[i]);
40446                 if(n){
40447                     node.appendChild(n);
40448                 }
40449             }
40450             if(typeof callback == "function"){
40451                 callback(this, node);
40452             }
40453         }catch(e){
40454             this.handleFailure(response);
40455         }
40456     },
40457
40458     handleResponse : function(response){
40459         this.transId = false;
40460         var a = response.argument;
40461         this.processResponse(response, a.node, a.callback);
40462         this.fireEvent("load", this, a.node, response);
40463     },
40464
40465     handleFailure : function(response)
40466     {
40467         // should handle failure better..
40468         this.transId = false;
40469         var a = response.argument;
40470         this.fireEvent("loadexception", this, a.node, response);
40471         if(typeof a.callback == "function"){
40472             a.callback(this, a.node);
40473         }
40474     }
40475 });/*
40476  * Based on:
40477  * Ext JS Library 1.1.1
40478  * Copyright(c) 2006-2007, Ext JS, LLC.
40479  *
40480  * Originally Released Under LGPL - original licence link has changed is not relivant.
40481  *
40482  * Fork - LGPL
40483  * <script type="text/javascript">
40484  */
40485
40486 /**
40487 * @class Roo.tree.TreeFilter
40488 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
40489 * @param {TreePanel} tree
40490 * @param {Object} config (optional)
40491  */
40492 Roo.tree.TreeFilter = function(tree, config){
40493     this.tree = tree;
40494     this.filtered = {};
40495     Roo.apply(this, config);
40496 };
40497
40498 Roo.tree.TreeFilter.prototype = {
40499     clearBlank:false,
40500     reverse:false,
40501     autoClear:false,
40502     remove:false,
40503
40504      /**
40505      * Filter the data by a specific attribute.
40506      * @param {String/RegExp} value Either string that the attribute value
40507      * should start with or a RegExp to test against the attribute
40508      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
40509      * @param {TreeNode} startNode (optional) The node to start the filter at.
40510      */
40511     filter : function(value, attr, startNode){
40512         attr = attr || "text";
40513         var f;
40514         if(typeof value == "string"){
40515             var vlen = value.length;
40516             // auto clear empty filter
40517             if(vlen == 0 && this.clearBlank){
40518                 this.clear();
40519                 return;
40520             }
40521             value = value.toLowerCase();
40522             f = function(n){
40523                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
40524             };
40525         }else if(value.exec){ // regex?
40526             f = function(n){
40527                 return value.test(n.attributes[attr]);
40528             };
40529         }else{
40530             throw 'Illegal filter type, must be string or regex';
40531         }
40532         this.filterBy(f, null, startNode);
40533         },
40534
40535     /**
40536      * Filter by a function. The passed function will be called with each
40537      * node in the tree (or from the startNode). If the function returns true, the node is kept
40538      * otherwise it is filtered. If a node is filtered, its children are also filtered.
40539      * @param {Function} fn The filter function
40540      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
40541      */
40542     filterBy : function(fn, scope, startNode){
40543         startNode = startNode || this.tree.root;
40544         if(this.autoClear){
40545             this.clear();
40546         }
40547         var af = this.filtered, rv = this.reverse;
40548         var f = function(n){
40549             if(n == startNode){
40550                 return true;
40551             }
40552             if(af[n.id]){
40553                 return false;
40554             }
40555             var m = fn.call(scope || n, n);
40556             if(!m || rv){
40557                 af[n.id] = n;
40558                 n.ui.hide();
40559                 return false;
40560             }
40561             return true;
40562         };
40563         startNode.cascade(f);
40564         if(this.remove){
40565            for(var id in af){
40566                if(typeof id != "function"){
40567                    var n = af[id];
40568                    if(n && n.parentNode){
40569                        n.parentNode.removeChild(n);
40570                    }
40571                }
40572            }
40573         }
40574     },
40575
40576     /**
40577      * Clears the current filter. Note: with the "remove" option
40578      * set a filter cannot be cleared.
40579      */
40580     clear : function(){
40581         var t = this.tree;
40582         var af = this.filtered;
40583         for(var id in af){
40584             if(typeof id != "function"){
40585                 var n = af[id];
40586                 if(n){
40587                     n.ui.show();
40588                 }
40589             }
40590         }
40591         this.filtered = {};
40592     }
40593 };
40594 /*
40595  * Based on:
40596  * Ext JS Library 1.1.1
40597  * Copyright(c) 2006-2007, Ext JS, LLC.
40598  *
40599  * Originally Released Under LGPL - original licence link has changed is not relivant.
40600  *
40601  * Fork - LGPL
40602  * <script type="text/javascript">
40603  */
40604  
40605
40606 /**
40607  * @class Roo.tree.TreeSorter
40608  * Provides sorting of nodes in a TreePanel
40609  * 
40610  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
40611  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
40612  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
40613  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
40614  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
40615  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
40616  * @constructor
40617  * @param {TreePanel} tree
40618  * @param {Object} config
40619  */
40620 Roo.tree.TreeSorter = function(tree, config){
40621     Roo.apply(this, config);
40622     tree.on("beforechildrenrendered", this.doSort, this);
40623     tree.on("append", this.updateSort, this);
40624     tree.on("insert", this.updateSort, this);
40625     
40626     var dsc = this.dir && this.dir.toLowerCase() == "desc";
40627     var p = this.property || "text";
40628     var sortType = this.sortType;
40629     var fs = this.folderSort;
40630     var cs = this.caseSensitive === true;
40631     var leafAttr = this.leafAttr || 'leaf';
40632
40633     this.sortFn = function(n1, n2){
40634         if(fs){
40635             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
40636                 return 1;
40637             }
40638             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
40639                 return -1;
40640             }
40641         }
40642         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
40643         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
40644         if(v1 < v2){
40645                         return dsc ? +1 : -1;
40646                 }else if(v1 > v2){
40647                         return dsc ? -1 : +1;
40648         }else{
40649                 return 0;
40650         }
40651     };
40652 };
40653
40654 Roo.tree.TreeSorter.prototype = {
40655     doSort : function(node){
40656         node.sort(this.sortFn);
40657     },
40658     
40659     compareNodes : function(n1, n2){
40660         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
40661     },
40662     
40663     updateSort : function(tree, node){
40664         if(node.childrenRendered){
40665             this.doSort.defer(1, this, [node]);
40666         }
40667     }
40668 };/*
40669  * Based on:
40670  * Ext JS Library 1.1.1
40671  * Copyright(c) 2006-2007, Ext JS, LLC.
40672  *
40673  * Originally Released Under LGPL - original licence link has changed is not relivant.
40674  *
40675  * Fork - LGPL
40676  * <script type="text/javascript">
40677  */
40678
40679 if(Roo.dd.DropZone){
40680     
40681 Roo.tree.TreeDropZone = function(tree, config){
40682     this.allowParentInsert = false;
40683     this.allowContainerDrop = false;
40684     this.appendOnly = false;
40685     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
40686     this.tree = tree;
40687     this.lastInsertClass = "x-tree-no-status";
40688     this.dragOverData = {};
40689 };
40690
40691 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
40692     ddGroup : "TreeDD",
40693     scroll:  true,
40694     
40695     expandDelay : 1000,
40696     
40697     expandNode : function(node){
40698         if(node.hasChildNodes() && !node.isExpanded()){
40699             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
40700         }
40701     },
40702     
40703     queueExpand : function(node){
40704         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
40705     },
40706     
40707     cancelExpand : function(){
40708         if(this.expandProcId){
40709             clearTimeout(this.expandProcId);
40710             this.expandProcId = false;
40711         }
40712     },
40713     
40714     isValidDropPoint : function(n, pt, dd, e, data){
40715         if(!n || !data){ return false; }
40716         var targetNode = n.node;
40717         var dropNode = data.node;
40718         // default drop rules
40719         if(!(targetNode && targetNode.isTarget && pt)){
40720             return false;
40721         }
40722         if(pt == "append" && targetNode.allowChildren === false){
40723             return false;
40724         }
40725         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
40726             return false;
40727         }
40728         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
40729             return false;
40730         }
40731         // reuse the object
40732         var overEvent = this.dragOverData;
40733         overEvent.tree = this.tree;
40734         overEvent.target = targetNode;
40735         overEvent.data = data;
40736         overEvent.point = pt;
40737         overEvent.source = dd;
40738         overEvent.rawEvent = e;
40739         overEvent.dropNode = dropNode;
40740         overEvent.cancel = false;  
40741         var result = this.tree.fireEvent("nodedragover", overEvent);
40742         return overEvent.cancel === false && result !== false;
40743     },
40744     
40745     getDropPoint : function(e, n, dd)
40746     {
40747         var tn = n.node;
40748         if(tn.isRoot){
40749             return tn.allowChildren !== false ? "append" : false; // always append for root
40750         }
40751         var dragEl = n.ddel;
40752         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
40753         var y = Roo.lib.Event.getPageY(e);
40754         //var noAppend = tn.allowChildren === false || tn.isLeaf();
40755         
40756         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
40757         var noAppend = tn.allowChildren === false;
40758         if(this.appendOnly || tn.parentNode.allowChildren === false){
40759             return noAppend ? false : "append";
40760         }
40761         var noBelow = false;
40762         if(!this.allowParentInsert){
40763             noBelow = tn.hasChildNodes() && tn.isExpanded();
40764         }
40765         var q = (b - t) / (noAppend ? 2 : 3);
40766         if(y >= t && y < (t + q)){
40767             return "above";
40768         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
40769             return "below";
40770         }else{
40771             return "append";
40772         }
40773     },
40774     
40775     onNodeEnter : function(n, dd, e, data)
40776     {
40777         this.cancelExpand();
40778     },
40779     
40780     onNodeOver : function(n, dd, e, data)
40781     {
40782        
40783         var pt = this.getDropPoint(e, n, dd);
40784         var node = n.node;
40785         
40786         // auto node expand check
40787         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
40788             this.queueExpand(node);
40789         }else if(pt != "append"){
40790             this.cancelExpand();
40791         }
40792         
40793         // set the insert point style on the target node
40794         var returnCls = this.dropNotAllowed;
40795         if(this.isValidDropPoint(n, pt, dd, e, data)){
40796            if(pt){
40797                var el = n.ddel;
40798                var cls;
40799                if(pt == "above"){
40800                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
40801                    cls = "x-tree-drag-insert-above";
40802                }else if(pt == "below"){
40803                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
40804                    cls = "x-tree-drag-insert-below";
40805                }else{
40806                    returnCls = "x-tree-drop-ok-append";
40807                    cls = "x-tree-drag-append";
40808                }
40809                if(this.lastInsertClass != cls){
40810                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
40811                    this.lastInsertClass = cls;
40812                }
40813            }
40814        }
40815        return returnCls;
40816     },
40817     
40818     onNodeOut : function(n, dd, e, data){
40819         
40820         this.cancelExpand();
40821         this.removeDropIndicators(n);
40822     },
40823     
40824     onNodeDrop : function(n, dd, e, data){
40825         var point = this.getDropPoint(e, n, dd);
40826         var targetNode = n.node;
40827         targetNode.ui.startDrop();
40828         if(!this.isValidDropPoint(n, point, dd, e, data)){
40829             targetNode.ui.endDrop();
40830             return false;
40831         }
40832         // first try to find the drop node
40833         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
40834         var dropEvent = {
40835             tree : this.tree,
40836             target: targetNode,
40837             data: data,
40838             point: point,
40839             source: dd,
40840             rawEvent: e,
40841             dropNode: dropNode,
40842             cancel: !dropNode   
40843         };
40844         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
40845         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
40846             targetNode.ui.endDrop();
40847             return false;
40848         }
40849         // allow target changing
40850         targetNode = dropEvent.target;
40851         if(point == "append" && !targetNode.isExpanded()){
40852             targetNode.expand(false, null, function(){
40853                 this.completeDrop(dropEvent);
40854             }.createDelegate(this));
40855         }else{
40856             this.completeDrop(dropEvent);
40857         }
40858         return true;
40859     },
40860     
40861     completeDrop : function(de){
40862         var ns = de.dropNode, p = de.point, t = de.target;
40863         if(!(ns instanceof Array)){
40864             ns = [ns];
40865         }
40866         var n;
40867         for(var i = 0, len = ns.length; i < len; i++){
40868             n = ns[i];
40869             if(p == "above"){
40870                 t.parentNode.insertBefore(n, t);
40871             }else if(p == "below"){
40872                 t.parentNode.insertBefore(n, t.nextSibling);
40873             }else{
40874                 t.appendChild(n);
40875             }
40876         }
40877         n.ui.focus();
40878         if(this.tree.hlDrop){
40879             n.ui.highlight();
40880         }
40881         t.ui.endDrop();
40882         this.tree.fireEvent("nodedrop", de);
40883     },
40884     
40885     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
40886         if(this.tree.hlDrop){
40887             dropNode.ui.focus();
40888             dropNode.ui.highlight();
40889         }
40890         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
40891     },
40892     
40893     getTree : function(){
40894         return this.tree;
40895     },
40896     
40897     removeDropIndicators : function(n){
40898         if(n && n.ddel){
40899             var el = n.ddel;
40900             Roo.fly(el).removeClass([
40901                     "x-tree-drag-insert-above",
40902                     "x-tree-drag-insert-below",
40903                     "x-tree-drag-append"]);
40904             this.lastInsertClass = "_noclass";
40905         }
40906     },
40907     
40908     beforeDragDrop : function(target, e, id){
40909         this.cancelExpand();
40910         return true;
40911     },
40912     
40913     afterRepair : function(data){
40914         if(data && Roo.enableFx){
40915             data.node.ui.highlight();
40916         }
40917         this.hideProxy();
40918     } 
40919     
40920 });
40921
40922 }
40923 /*
40924  * Based on:
40925  * Ext JS Library 1.1.1
40926  * Copyright(c) 2006-2007, Ext JS, LLC.
40927  *
40928  * Originally Released Under LGPL - original licence link has changed is not relivant.
40929  *
40930  * Fork - LGPL
40931  * <script type="text/javascript">
40932  */
40933  
40934
40935 if(Roo.dd.DragZone){
40936 Roo.tree.TreeDragZone = function(tree, config){
40937     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
40938     this.tree = tree;
40939 };
40940
40941 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
40942     ddGroup : "TreeDD",
40943    
40944     onBeforeDrag : function(data, e){
40945         var n = data.node;
40946         return n && n.draggable && !n.disabled;
40947     },
40948      
40949     
40950     onInitDrag : function(e){
40951         var data = this.dragData;
40952         this.tree.getSelectionModel().select(data.node);
40953         this.proxy.update("");
40954         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
40955         this.tree.fireEvent("startdrag", this.tree, data.node, e);
40956     },
40957     
40958     getRepairXY : function(e, data){
40959         return data.node.ui.getDDRepairXY();
40960     },
40961     
40962     onEndDrag : function(data, e){
40963         this.tree.fireEvent("enddrag", this.tree, data.node, e);
40964         
40965         
40966     },
40967     
40968     onValidDrop : function(dd, e, id){
40969         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
40970         this.hideProxy();
40971     },
40972     
40973     beforeInvalidDrop : function(e, id){
40974         // this scrolls the original position back into view
40975         var sm = this.tree.getSelectionModel();
40976         sm.clearSelections();
40977         sm.select(this.dragData.node);
40978     }
40979 });
40980 }/*
40981  * Based on:
40982  * Ext JS Library 1.1.1
40983  * Copyright(c) 2006-2007, Ext JS, LLC.
40984  *
40985  * Originally Released Under LGPL - original licence link has changed is not relivant.
40986  *
40987  * Fork - LGPL
40988  * <script type="text/javascript">
40989  */
40990 /**
40991  * @class Roo.tree.TreeEditor
40992  * @extends Roo.Editor
40993  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
40994  * as the editor field.
40995  * @constructor
40996  * @param {Object} config (used to be the tree panel.)
40997  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
40998  * 
40999  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
41000  * @cfg {Roo.form.TextField|Object} field The field configuration
41001  *
41002  * 
41003  */
41004 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
41005     var tree = config;
41006     var field;
41007     if (oldconfig) { // old style..
41008         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
41009     } else {
41010         // new style..
41011         tree = config.tree;
41012         config.field = config.field  || {};
41013         config.field.xtype = 'TextField';
41014         field = Roo.factory(config.field, Roo.form);
41015     }
41016     config = config || {};
41017     
41018     
41019     this.addEvents({
41020         /**
41021          * @event beforenodeedit
41022          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
41023          * false from the handler of this event.
41024          * @param {Editor} this
41025          * @param {Roo.tree.Node} node 
41026          */
41027         "beforenodeedit" : true
41028     });
41029     
41030     //Roo.log(config);
41031     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
41032
41033     this.tree = tree;
41034
41035     tree.on('beforeclick', this.beforeNodeClick, this);
41036     tree.getTreeEl().on('mousedown', this.hide, this);
41037     this.on('complete', this.updateNode, this);
41038     this.on('beforestartedit', this.fitToTree, this);
41039     this.on('startedit', this.bindScroll, this, {delay:10});
41040     this.on('specialkey', this.onSpecialKey, this);
41041 };
41042
41043 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
41044     /**
41045      * @cfg {String} alignment
41046      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
41047      */
41048     alignment: "l-l",
41049     // inherit
41050     autoSize: false,
41051     /**
41052      * @cfg {Boolean} hideEl
41053      * True to hide the bound element while the editor is displayed (defaults to false)
41054      */
41055     hideEl : false,
41056     /**
41057      * @cfg {String} cls
41058      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
41059      */
41060     cls: "x-small-editor x-tree-editor",
41061     /**
41062      * @cfg {Boolean} shim
41063      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
41064      */
41065     shim:false,
41066     // inherit
41067     shadow:"frame",
41068     /**
41069      * @cfg {Number} maxWidth
41070      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
41071      * the containing tree element's size, it will be automatically limited for you to the container width, taking
41072      * scroll and client offsets into account prior to each edit.
41073      */
41074     maxWidth: 250,
41075
41076     editDelay : 350,
41077
41078     // private
41079     fitToTree : function(ed, el){
41080         var td = this.tree.getTreeEl().dom, nd = el.dom;
41081         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
41082             td.scrollLeft = nd.offsetLeft;
41083         }
41084         var w = Math.min(
41085                 this.maxWidth,
41086                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
41087         this.setSize(w, '');
41088         
41089         return this.fireEvent('beforenodeedit', this, this.editNode);
41090         
41091     },
41092
41093     // private
41094     triggerEdit : function(node){
41095         this.completeEdit();
41096         this.editNode = node;
41097         this.startEdit(node.ui.textNode, node.text);
41098     },
41099
41100     // private
41101     bindScroll : function(){
41102         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
41103     },
41104
41105     // private
41106     beforeNodeClick : function(node, e){
41107         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
41108         this.lastClick = new Date();
41109         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
41110             e.stopEvent();
41111             this.triggerEdit(node);
41112             return false;
41113         }
41114         return true;
41115     },
41116
41117     // private
41118     updateNode : function(ed, value){
41119         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
41120         this.editNode.setText(value);
41121     },
41122
41123     // private
41124     onHide : function(){
41125         Roo.tree.TreeEditor.superclass.onHide.call(this);
41126         if(this.editNode){
41127             this.editNode.ui.focus();
41128         }
41129     },
41130
41131     // private
41132     onSpecialKey : function(field, e){
41133         var k = e.getKey();
41134         if(k == e.ESC){
41135             e.stopEvent();
41136             this.cancelEdit();
41137         }else if(k == e.ENTER && !e.hasModifier()){
41138             e.stopEvent();
41139             this.completeEdit();
41140         }
41141     }
41142 });//<Script type="text/javascript">
41143 /*
41144  * Based on:
41145  * Ext JS Library 1.1.1
41146  * Copyright(c) 2006-2007, Ext JS, LLC.
41147  *
41148  * Originally Released Under LGPL - original licence link has changed is not relivant.
41149  *
41150  * Fork - LGPL
41151  * <script type="text/javascript">
41152  */
41153  
41154 /**
41155  * Not documented??? - probably should be...
41156  */
41157
41158 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
41159     //focus: Roo.emptyFn, // prevent odd scrolling behavior
41160     
41161     renderElements : function(n, a, targetNode, bulkRender){
41162         //consel.log("renderElements?");
41163         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
41164
41165         var t = n.getOwnerTree();
41166         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
41167         
41168         var cols = t.columns;
41169         var bw = t.borderWidth;
41170         var c = cols[0];
41171         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
41172          var cb = typeof a.checked == "boolean";
41173         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
41174         var colcls = 'x-t-' + tid + '-c0';
41175         var buf = [
41176             '<li class="x-tree-node">',
41177             
41178                 
41179                 '<div class="x-tree-node-el ', a.cls,'">',
41180                     // extran...
41181                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
41182                 
41183                 
41184                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
41185                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
41186                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
41187                            (a.icon ? ' x-tree-node-inline-icon' : ''),
41188                            (a.iconCls ? ' '+a.iconCls : ''),
41189                            '" unselectable="on" />',
41190                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
41191                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
41192                              
41193                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
41194                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
41195                             '<span unselectable="on" qtip="' + tx + '">',
41196                              tx,
41197                              '</span></a>' ,
41198                     '</div>',
41199                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
41200                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
41201                  ];
41202         for(var i = 1, len = cols.length; i < len; i++){
41203             c = cols[i];
41204             colcls = 'x-t-' + tid + '-c' +i;
41205             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
41206             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
41207                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
41208                       "</div>");
41209          }
41210          
41211          buf.push(
41212             '</a>',
41213             '<div class="x-clear"></div></div>',
41214             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
41215             "</li>");
41216         
41217         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
41218             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
41219                                 n.nextSibling.ui.getEl(), buf.join(""));
41220         }else{
41221             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
41222         }
41223         var el = this.wrap.firstChild;
41224         this.elRow = el;
41225         this.elNode = el.firstChild;
41226         this.ranchor = el.childNodes[1];
41227         this.ctNode = this.wrap.childNodes[1];
41228         var cs = el.firstChild.childNodes;
41229         this.indentNode = cs[0];
41230         this.ecNode = cs[1];
41231         this.iconNode = cs[2];
41232         var index = 3;
41233         if(cb){
41234             this.checkbox = cs[3];
41235             index++;
41236         }
41237         this.anchor = cs[index];
41238         
41239         this.textNode = cs[index].firstChild;
41240         
41241         //el.on("click", this.onClick, this);
41242         //el.on("dblclick", this.onDblClick, this);
41243         
41244         
41245        // console.log(this);
41246     },
41247     initEvents : function(){
41248         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
41249         
41250             
41251         var a = this.ranchor;
41252
41253         var el = Roo.get(a);
41254
41255         if(Roo.isOpera){ // opera render bug ignores the CSS
41256             el.setStyle("text-decoration", "none");
41257         }
41258
41259         el.on("click", this.onClick, this);
41260         el.on("dblclick", this.onDblClick, this);
41261         el.on("contextmenu", this.onContextMenu, this);
41262         
41263     },
41264     
41265     /*onSelectedChange : function(state){
41266         if(state){
41267             this.focus();
41268             this.addClass("x-tree-selected");
41269         }else{
41270             //this.blur();
41271             this.removeClass("x-tree-selected");
41272         }
41273     },*/
41274     addClass : function(cls){
41275         if(this.elRow){
41276             Roo.fly(this.elRow).addClass(cls);
41277         }
41278         
41279     },
41280     
41281     
41282     removeClass : function(cls){
41283         if(this.elRow){
41284             Roo.fly(this.elRow).removeClass(cls);
41285         }
41286     }
41287
41288     
41289     
41290 });//<Script type="text/javascript">
41291
41292 /*
41293  * Based on:
41294  * Ext JS Library 1.1.1
41295  * Copyright(c) 2006-2007, Ext JS, LLC.
41296  *
41297  * Originally Released Under LGPL - original licence link has changed is not relivant.
41298  *
41299  * Fork - LGPL
41300  * <script type="text/javascript">
41301  */
41302  
41303
41304 /**
41305  * @class Roo.tree.ColumnTree
41306  * @extends Roo.data.TreePanel
41307  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
41308  * @cfg {int} borderWidth  compined right/left border allowance
41309  * @constructor
41310  * @param {String/HTMLElement/Element} el The container element
41311  * @param {Object} config
41312  */
41313 Roo.tree.ColumnTree =  function(el, config)
41314 {
41315    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
41316    this.addEvents({
41317         /**
41318         * @event resize
41319         * Fire this event on a container when it resizes
41320         * @param {int} w Width
41321         * @param {int} h Height
41322         */
41323        "resize" : true
41324     });
41325     this.on('resize', this.onResize, this);
41326 };
41327
41328 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
41329     //lines:false,
41330     
41331     
41332     borderWidth: Roo.isBorderBox ? 0 : 2, 
41333     headEls : false,
41334     
41335     render : function(){
41336         // add the header.....
41337        
41338         Roo.tree.ColumnTree.superclass.render.apply(this);
41339         
41340         this.el.addClass('x-column-tree');
41341         
41342         this.headers = this.el.createChild(
41343             {cls:'x-tree-headers'},this.innerCt.dom);
41344    
41345         var cols = this.columns, c;
41346         var totalWidth = 0;
41347         this.headEls = [];
41348         var  len = cols.length;
41349         for(var i = 0; i < len; i++){
41350              c = cols[i];
41351              totalWidth += c.width;
41352             this.headEls.push(this.headers.createChild({
41353                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
41354                  cn: {
41355                      cls:'x-tree-hd-text',
41356                      html: c.header
41357                  },
41358                  style:'width:'+(c.width-this.borderWidth)+'px;'
41359              }));
41360         }
41361         this.headers.createChild({cls:'x-clear'});
41362         // prevent floats from wrapping when clipped
41363         this.headers.setWidth(totalWidth);
41364         //this.innerCt.setWidth(totalWidth);
41365         this.innerCt.setStyle({ overflow: 'auto' });
41366         this.onResize(this.width, this.height);
41367              
41368         
41369     },
41370     onResize : function(w,h)
41371     {
41372         this.height = h;
41373         this.width = w;
41374         // resize cols..
41375         this.innerCt.setWidth(this.width);
41376         this.innerCt.setHeight(this.height-20);
41377         
41378         // headers...
41379         var cols = this.columns, c;
41380         var totalWidth = 0;
41381         var expEl = false;
41382         var len = cols.length;
41383         for(var i = 0; i < len; i++){
41384             c = cols[i];
41385             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
41386                 // it's the expander..
41387                 expEl  = this.headEls[i];
41388                 continue;
41389             }
41390             totalWidth += c.width;
41391             
41392         }
41393         if (expEl) {
41394             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
41395         }
41396         this.headers.setWidth(w-20);
41397
41398         
41399         
41400         
41401     }
41402 });
41403 /*
41404  * Based on:
41405  * Ext JS Library 1.1.1
41406  * Copyright(c) 2006-2007, Ext JS, LLC.
41407  *
41408  * Originally Released Under LGPL - original licence link has changed is not relivant.
41409  *
41410  * Fork - LGPL
41411  * <script type="text/javascript">
41412  */
41413  
41414 /**
41415  * @class Roo.menu.Menu
41416  * @extends Roo.util.Observable
41417  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
41418  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
41419  * @constructor
41420  * Creates a new Menu
41421  * @param {Object} config Configuration options
41422  */
41423 Roo.menu.Menu = function(config){
41424     Roo.apply(this, config);
41425     this.id = this.id || Roo.id();
41426     this.addEvents({
41427         /**
41428          * @event beforeshow
41429          * Fires before this menu is displayed
41430          * @param {Roo.menu.Menu} this
41431          */
41432         beforeshow : true,
41433         /**
41434          * @event beforehide
41435          * Fires before this menu is hidden
41436          * @param {Roo.menu.Menu} this
41437          */
41438         beforehide : true,
41439         /**
41440          * @event show
41441          * Fires after this menu is displayed
41442          * @param {Roo.menu.Menu} this
41443          */
41444         show : true,
41445         /**
41446          * @event hide
41447          * Fires after this menu is hidden
41448          * @param {Roo.menu.Menu} this
41449          */
41450         hide : true,
41451         /**
41452          * @event click
41453          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
41454          * @param {Roo.menu.Menu} this
41455          * @param {Roo.menu.Item} menuItem The menu item that was clicked
41456          * @param {Roo.EventObject} e
41457          */
41458         click : true,
41459         /**
41460          * @event mouseover
41461          * Fires when the mouse is hovering over this menu
41462          * @param {Roo.menu.Menu} this
41463          * @param {Roo.EventObject} e
41464          * @param {Roo.menu.Item} menuItem The menu item that was clicked
41465          */
41466         mouseover : true,
41467         /**
41468          * @event mouseout
41469          * Fires when the mouse exits this menu
41470          * @param {Roo.menu.Menu} this
41471          * @param {Roo.EventObject} e
41472          * @param {Roo.menu.Item} menuItem The menu item that was clicked
41473          */
41474         mouseout : true,
41475         /**
41476          * @event itemclick
41477          * Fires when a menu item contained in this menu is clicked
41478          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
41479          * @param {Roo.EventObject} e
41480          */
41481         itemclick: true
41482     });
41483     if (this.registerMenu) {
41484         Roo.menu.MenuMgr.register(this);
41485     }
41486     
41487     var mis = this.items;
41488     this.items = new Roo.util.MixedCollection();
41489     if(mis){
41490         this.add.apply(this, mis);
41491     }
41492 };
41493
41494 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
41495     /**
41496      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
41497      */
41498     minWidth : 120,
41499     /**
41500      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
41501      * for bottom-right shadow (defaults to "sides")
41502      */
41503     shadow : "sides",
41504     /**
41505      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
41506      * this menu (defaults to "tl-tr?")
41507      */
41508     subMenuAlign : "tl-tr?",
41509     /**
41510      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
41511      * relative to its element of origin (defaults to "tl-bl?")
41512      */
41513     defaultAlign : "tl-bl?",
41514     /**
41515      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
41516      */
41517     allowOtherMenus : false,
41518     /**
41519      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
41520      */
41521     registerMenu : true,
41522
41523     hidden:true,
41524
41525     // private
41526     render : function(){
41527         if(this.el){
41528             return;
41529         }
41530         var el = this.el = new Roo.Layer({
41531             cls: "x-menu",
41532             shadow:this.shadow,
41533             constrain: false,
41534             parentEl: this.parentEl || document.body,
41535             zindex:15000
41536         });
41537
41538         this.keyNav = new Roo.menu.MenuNav(this);
41539
41540         if(this.plain){
41541             el.addClass("x-menu-plain");
41542         }
41543         if(this.cls){
41544             el.addClass(this.cls);
41545         }
41546         // generic focus element
41547         this.focusEl = el.createChild({
41548             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
41549         });
41550         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
41551         //disabling touch- as it's causing issues ..
41552         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
41553         ul.on('click'   , this.onClick, this);
41554         
41555         
41556         ul.on("mouseover", this.onMouseOver, this);
41557         ul.on("mouseout", this.onMouseOut, this);
41558         this.items.each(function(item){
41559             if (item.hidden) {
41560                 return;
41561             }
41562             
41563             var li = document.createElement("li");
41564             li.className = "x-menu-list-item";
41565             ul.dom.appendChild(li);
41566             item.render(li, this);
41567         }, this);
41568         this.ul = ul;
41569         this.autoWidth();
41570     },
41571
41572     // private
41573     autoWidth : function(){
41574         var el = this.el, ul = this.ul;
41575         if(!el){
41576             return;
41577         }
41578         var w = this.width;
41579         if(w){
41580             el.setWidth(w);
41581         }else if(Roo.isIE){
41582             el.setWidth(this.minWidth);
41583             var t = el.dom.offsetWidth; // force recalc
41584             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
41585         }
41586     },
41587
41588     // private
41589     delayAutoWidth : function(){
41590         if(this.rendered){
41591             if(!this.awTask){
41592                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
41593             }
41594             this.awTask.delay(20);
41595         }
41596     },
41597
41598     // private
41599     findTargetItem : function(e){
41600         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
41601         if(t && t.menuItemId){
41602             return this.items.get(t.menuItemId);
41603         }
41604     },
41605
41606     // private
41607     onClick : function(e){
41608         Roo.log("menu.onClick");
41609         var t = this.findTargetItem(e);
41610         if(!t){
41611             return;
41612         }
41613         Roo.log(e);
41614         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
41615             if(t == this.activeItem && t.shouldDeactivate(e)){
41616                 this.activeItem.deactivate();
41617                 delete this.activeItem;
41618                 return;
41619             }
41620             if(t.canActivate){
41621                 this.setActiveItem(t, true);
41622             }
41623             return;
41624             
41625             
41626         }
41627         
41628         t.onClick(e);
41629         this.fireEvent("click", this, t, e);
41630     },
41631
41632     // private
41633     setActiveItem : function(item, autoExpand){
41634         if(item != this.activeItem){
41635             if(this.activeItem){
41636                 this.activeItem.deactivate();
41637             }
41638             this.activeItem = item;
41639             item.activate(autoExpand);
41640         }else if(autoExpand){
41641             item.expandMenu();
41642         }
41643     },
41644
41645     // private
41646     tryActivate : function(start, step){
41647         var items = this.items;
41648         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
41649             var item = items.get(i);
41650             if(!item.disabled && item.canActivate){
41651                 this.setActiveItem(item, false);
41652                 return item;
41653             }
41654         }
41655         return false;
41656     },
41657
41658     // private
41659     onMouseOver : function(e){
41660         var t;
41661         if(t = this.findTargetItem(e)){
41662             if(t.canActivate && !t.disabled){
41663                 this.setActiveItem(t, true);
41664             }
41665         }
41666         this.fireEvent("mouseover", this, e, t);
41667     },
41668
41669     // private
41670     onMouseOut : function(e){
41671         var t;
41672         if(t = this.findTargetItem(e)){
41673             if(t == this.activeItem && t.shouldDeactivate(e)){
41674                 this.activeItem.deactivate();
41675                 delete this.activeItem;
41676             }
41677         }
41678         this.fireEvent("mouseout", this, e, t);
41679     },
41680
41681     /**
41682      * Read-only.  Returns true if the menu is currently displayed, else false.
41683      * @type Boolean
41684      */
41685     isVisible : function(){
41686         return this.el && !this.hidden;
41687     },
41688
41689     /**
41690      * Displays this menu relative to another element
41691      * @param {String/HTMLElement/Roo.Element} element The element to align to
41692      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
41693      * the element (defaults to this.defaultAlign)
41694      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
41695      */
41696     show : function(el, pos, parentMenu){
41697         this.parentMenu = parentMenu;
41698         if(!this.el){
41699             this.render();
41700         }
41701         this.fireEvent("beforeshow", this);
41702         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
41703     },
41704
41705     /**
41706      * Displays this menu at a specific xy position
41707      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
41708      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
41709      */
41710     showAt : function(xy, parentMenu, /* private: */_e){
41711         this.parentMenu = parentMenu;
41712         if(!this.el){
41713             this.render();
41714         }
41715         if(_e !== false){
41716             this.fireEvent("beforeshow", this);
41717             xy = this.el.adjustForConstraints(xy);
41718         }
41719         this.el.setXY(xy);
41720         this.el.show();
41721         this.hidden = false;
41722         this.focus();
41723         this.fireEvent("show", this);
41724     },
41725
41726     focus : function(){
41727         if(!this.hidden){
41728             this.doFocus.defer(50, this);
41729         }
41730     },
41731
41732     doFocus : function(){
41733         if(!this.hidden){
41734             this.focusEl.focus();
41735         }
41736     },
41737
41738     /**
41739      * Hides this menu and optionally all parent menus
41740      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
41741      */
41742     hide : function(deep){
41743         if(this.el && this.isVisible()){
41744             this.fireEvent("beforehide", this);
41745             if(this.activeItem){
41746                 this.activeItem.deactivate();
41747                 this.activeItem = null;
41748             }
41749             this.el.hide();
41750             this.hidden = true;
41751             this.fireEvent("hide", this);
41752         }
41753         if(deep === true && this.parentMenu){
41754             this.parentMenu.hide(true);
41755         }
41756     },
41757
41758     /**
41759      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
41760      * Any of the following are valid:
41761      * <ul>
41762      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
41763      * <li>An HTMLElement object which will be converted to a menu item</li>
41764      * <li>A menu item config object that will be created as a new menu item</li>
41765      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
41766      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
41767      * </ul>
41768      * Usage:
41769      * <pre><code>
41770 // Create the menu
41771 var menu = new Roo.menu.Menu();
41772
41773 // Create a menu item to add by reference
41774 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
41775
41776 // Add a bunch of items at once using different methods.
41777 // Only the last item added will be returned.
41778 var item = menu.add(
41779     menuItem,                // add existing item by ref
41780     'Dynamic Item',          // new TextItem
41781     '-',                     // new separator
41782     { text: 'Config Item' }  // new item by config
41783 );
41784 </code></pre>
41785      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
41786      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
41787      */
41788     add : function(){
41789         var a = arguments, l = a.length, item;
41790         for(var i = 0; i < l; i++){
41791             var el = a[i];
41792             if ((typeof(el) == "object") && el.xtype && el.xns) {
41793                 el = Roo.factory(el, Roo.menu);
41794             }
41795             
41796             if(el.render){ // some kind of Item
41797                 item = this.addItem(el);
41798             }else if(typeof el == "string"){ // string
41799                 if(el == "separator" || el == "-"){
41800                     item = this.addSeparator();
41801                 }else{
41802                     item = this.addText(el);
41803                 }
41804             }else if(el.tagName || el.el){ // element
41805                 item = this.addElement(el);
41806             }else if(typeof el == "object"){ // must be menu item config?
41807                 item = this.addMenuItem(el);
41808             }
41809         }
41810         return item;
41811     },
41812
41813     /**
41814      * Returns this menu's underlying {@link Roo.Element} object
41815      * @return {Roo.Element} The element
41816      */
41817     getEl : function(){
41818         if(!this.el){
41819             this.render();
41820         }
41821         return this.el;
41822     },
41823
41824     /**
41825      * Adds a separator bar to the menu
41826      * @return {Roo.menu.Item} The menu item that was added
41827      */
41828     addSeparator : function(){
41829         return this.addItem(new Roo.menu.Separator());
41830     },
41831
41832     /**
41833      * Adds an {@link Roo.Element} object to the menu
41834      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
41835      * @return {Roo.menu.Item} The menu item that was added
41836      */
41837     addElement : function(el){
41838         return this.addItem(new Roo.menu.BaseItem(el));
41839     },
41840
41841     /**
41842      * Adds an existing object based on {@link Roo.menu.Item} to the menu
41843      * @param {Roo.menu.Item} item The menu item to add
41844      * @return {Roo.menu.Item} The menu item that was added
41845      */
41846     addItem : function(item){
41847         this.items.add(item);
41848         if(this.ul){
41849             var li = document.createElement("li");
41850             li.className = "x-menu-list-item";
41851             this.ul.dom.appendChild(li);
41852             item.render(li, this);
41853             this.delayAutoWidth();
41854         }
41855         return item;
41856     },
41857
41858     /**
41859      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
41860      * @param {Object} config A MenuItem config object
41861      * @return {Roo.menu.Item} The menu item that was added
41862      */
41863     addMenuItem : function(config){
41864         if(!(config instanceof Roo.menu.Item)){
41865             if(typeof config.checked == "boolean"){ // must be check menu item config?
41866                 config = new Roo.menu.CheckItem(config);
41867             }else{
41868                 config = new Roo.menu.Item(config);
41869             }
41870         }
41871         return this.addItem(config);
41872     },
41873
41874     /**
41875      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
41876      * @param {String} text The text to display in the menu item
41877      * @return {Roo.menu.Item} The menu item that was added
41878      */
41879     addText : function(text){
41880         return this.addItem(new Roo.menu.TextItem({ text : text }));
41881     },
41882
41883     /**
41884      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
41885      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
41886      * @param {Roo.menu.Item} item The menu item to add
41887      * @return {Roo.menu.Item} The menu item that was added
41888      */
41889     insert : function(index, item){
41890         this.items.insert(index, item);
41891         if(this.ul){
41892             var li = document.createElement("li");
41893             li.className = "x-menu-list-item";
41894             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
41895             item.render(li, this);
41896             this.delayAutoWidth();
41897         }
41898         return item;
41899     },
41900
41901     /**
41902      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
41903      * @param {Roo.menu.Item} item The menu item to remove
41904      */
41905     remove : function(item){
41906         this.items.removeKey(item.id);
41907         item.destroy();
41908     },
41909
41910     /**
41911      * Removes and destroys all items in the menu
41912      */
41913     removeAll : function(){
41914         var f;
41915         while(f = this.items.first()){
41916             this.remove(f);
41917         }
41918     }
41919 });
41920
41921 // MenuNav is a private utility class used internally by the Menu
41922 Roo.menu.MenuNav = function(menu){
41923     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
41924     this.scope = this.menu = menu;
41925 };
41926
41927 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
41928     doRelay : function(e, h){
41929         var k = e.getKey();
41930         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
41931             this.menu.tryActivate(0, 1);
41932             return false;
41933         }
41934         return h.call(this.scope || this, e, this.menu);
41935     },
41936
41937     up : function(e, m){
41938         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
41939             m.tryActivate(m.items.length-1, -1);
41940         }
41941     },
41942
41943     down : function(e, m){
41944         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
41945             m.tryActivate(0, 1);
41946         }
41947     },
41948
41949     right : function(e, m){
41950         if(m.activeItem){
41951             m.activeItem.expandMenu(true);
41952         }
41953     },
41954
41955     left : function(e, m){
41956         m.hide();
41957         if(m.parentMenu && m.parentMenu.activeItem){
41958             m.parentMenu.activeItem.activate();
41959         }
41960     },
41961
41962     enter : function(e, m){
41963         if(m.activeItem){
41964             e.stopPropagation();
41965             m.activeItem.onClick(e);
41966             m.fireEvent("click", this, m.activeItem);
41967             return true;
41968         }
41969     }
41970 });/*
41971  * Based on:
41972  * Ext JS Library 1.1.1
41973  * Copyright(c) 2006-2007, Ext JS, LLC.
41974  *
41975  * Originally Released Under LGPL - original licence link has changed is not relivant.
41976  *
41977  * Fork - LGPL
41978  * <script type="text/javascript">
41979  */
41980  
41981 /**
41982  * @class Roo.menu.MenuMgr
41983  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
41984  * @singleton
41985  */
41986 Roo.menu.MenuMgr = function(){
41987    var menus, active, groups = {}, attached = false, lastShow = new Date();
41988
41989    // private - called when first menu is created
41990    function init(){
41991        menus = {};
41992        active = new Roo.util.MixedCollection();
41993        Roo.get(document).addKeyListener(27, function(){
41994            if(active.length > 0){
41995                hideAll();
41996            }
41997        });
41998    }
41999
42000    // private
42001    function hideAll(){
42002        if(active && active.length > 0){
42003            var c = active.clone();
42004            c.each(function(m){
42005                m.hide();
42006            });
42007        }
42008    }
42009
42010    // private
42011    function onHide(m){
42012        active.remove(m);
42013        if(active.length < 1){
42014            Roo.get(document).un("mousedown", onMouseDown);
42015            attached = false;
42016        }
42017    }
42018
42019    // private
42020    function onShow(m){
42021        var last = active.last();
42022        lastShow = new Date();
42023        active.add(m);
42024        if(!attached){
42025            Roo.get(document).on("mousedown", onMouseDown);
42026            attached = true;
42027        }
42028        if(m.parentMenu){
42029           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
42030           m.parentMenu.activeChild = m;
42031        }else if(last && last.isVisible()){
42032           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
42033        }
42034    }
42035
42036    // private
42037    function onBeforeHide(m){
42038        if(m.activeChild){
42039            m.activeChild.hide();
42040        }
42041        if(m.autoHideTimer){
42042            clearTimeout(m.autoHideTimer);
42043            delete m.autoHideTimer;
42044        }
42045    }
42046
42047    // private
42048    function onBeforeShow(m){
42049        var pm = m.parentMenu;
42050        if(!pm && !m.allowOtherMenus){
42051            hideAll();
42052        }else if(pm && pm.activeChild && active != m){
42053            pm.activeChild.hide();
42054        }
42055    }
42056
42057    // private
42058    function onMouseDown(e){
42059        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
42060            hideAll();
42061        }
42062    }
42063
42064    // private
42065    function onBeforeCheck(mi, state){
42066        if(state){
42067            var g = groups[mi.group];
42068            for(var i = 0, l = g.length; i < l; i++){
42069                if(g[i] != mi){
42070                    g[i].setChecked(false);
42071                }
42072            }
42073        }
42074    }
42075
42076    return {
42077
42078        /**
42079         * Hides all menus that are currently visible
42080         */
42081        hideAll : function(){
42082             hideAll();  
42083        },
42084
42085        // private
42086        register : function(menu){
42087            if(!menus){
42088                init();
42089            }
42090            menus[menu.id] = menu;
42091            menu.on("beforehide", onBeforeHide);
42092            menu.on("hide", onHide);
42093            menu.on("beforeshow", onBeforeShow);
42094            menu.on("show", onShow);
42095            var g = menu.group;
42096            if(g && menu.events["checkchange"]){
42097                if(!groups[g]){
42098                    groups[g] = [];
42099                }
42100                groups[g].push(menu);
42101                menu.on("checkchange", onCheck);
42102            }
42103        },
42104
42105         /**
42106          * Returns a {@link Roo.menu.Menu} object
42107          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
42108          * be used to generate and return a new Menu instance.
42109          */
42110        get : function(menu){
42111            if(typeof menu == "string"){ // menu id
42112                return menus[menu];
42113            }else if(menu.events){  // menu instance
42114                return menu;
42115            }else if(typeof menu.length == 'number'){ // array of menu items?
42116                return new Roo.menu.Menu({items:menu});
42117            }else{ // otherwise, must be a config
42118                return new Roo.menu.Menu(menu);
42119            }
42120        },
42121
42122        // private
42123        unregister : function(menu){
42124            delete menus[menu.id];
42125            menu.un("beforehide", onBeforeHide);
42126            menu.un("hide", onHide);
42127            menu.un("beforeshow", onBeforeShow);
42128            menu.un("show", onShow);
42129            var g = menu.group;
42130            if(g && menu.events["checkchange"]){
42131                groups[g].remove(menu);
42132                menu.un("checkchange", onCheck);
42133            }
42134        },
42135
42136        // private
42137        registerCheckable : function(menuItem){
42138            var g = menuItem.group;
42139            if(g){
42140                if(!groups[g]){
42141                    groups[g] = [];
42142                }
42143                groups[g].push(menuItem);
42144                menuItem.on("beforecheckchange", onBeforeCheck);
42145            }
42146        },
42147
42148        // private
42149        unregisterCheckable : function(menuItem){
42150            var g = menuItem.group;
42151            if(g){
42152                groups[g].remove(menuItem);
42153                menuItem.un("beforecheckchange", onBeforeCheck);
42154            }
42155        }
42156    };
42157 }();/*
42158  * Based on:
42159  * Ext JS Library 1.1.1
42160  * Copyright(c) 2006-2007, Ext JS, LLC.
42161  *
42162  * Originally Released Under LGPL - original licence link has changed is not relivant.
42163  *
42164  * Fork - LGPL
42165  * <script type="text/javascript">
42166  */
42167  
42168
42169 /**
42170  * @class Roo.menu.BaseItem
42171  * @extends Roo.Component
42172  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
42173  * management and base configuration options shared by all menu components.
42174  * @constructor
42175  * Creates a new BaseItem
42176  * @param {Object} config Configuration options
42177  */
42178 Roo.menu.BaseItem = function(config){
42179     Roo.menu.BaseItem.superclass.constructor.call(this, config);
42180
42181     this.addEvents({
42182         /**
42183          * @event click
42184          * Fires when this item is clicked
42185          * @param {Roo.menu.BaseItem} this
42186          * @param {Roo.EventObject} e
42187          */
42188         click: true,
42189         /**
42190          * @event activate
42191          * Fires when this item is activated
42192          * @param {Roo.menu.BaseItem} this
42193          */
42194         activate : true,
42195         /**
42196          * @event deactivate
42197          * Fires when this item is deactivated
42198          * @param {Roo.menu.BaseItem} this
42199          */
42200         deactivate : true
42201     });
42202
42203     if(this.handler){
42204         this.on("click", this.handler, this.scope, true);
42205     }
42206 };
42207
42208 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
42209     /**
42210      * @cfg {Function} handler
42211      * A function that will handle the click event of this menu item (defaults to undefined)
42212      */
42213     /**
42214      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
42215      */
42216     canActivate : false,
42217     
42218      /**
42219      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
42220      */
42221     hidden: false,
42222     
42223     /**
42224      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
42225      */
42226     activeClass : "x-menu-item-active",
42227     /**
42228      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
42229      */
42230     hideOnClick : true,
42231     /**
42232      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
42233      */
42234     hideDelay : 100,
42235
42236     // private
42237     ctype: "Roo.menu.BaseItem",
42238
42239     // private
42240     actionMode : "container",
42241
42242     // private
42243     render : function(container, parentMenu){
42244         this.parentMenu = parentMenu;
42245         Roo.menu.BaseItem.superclass.render.call(this, container);
42246         this.container.menuItemId = this.id;
42247     },
42248
42249     // private
42250     onRender : function(container, position){
42251         this.el = Roo.get(this.el);
42252         container.dom.appendChild(this.el.dom);
42253     },
42254
42255     // private
42256     onClick : function(e){
42257         if(!this.disabled && this.fireEvent("click", this, e) !== false
42258                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
42259             this.handleClick(e);
42260         }else{
42261             e.stopEvent();
42262         }
42263     },
42264
42265     // private
42266     activate : function(){
42267         if(this.disabled){
42268             return false;
42269         }
42270         var li = this.container;
42271         li.addClass(this.activeClass);
42272         this.region = li.getRegion().adjust(2, 2, -2, -2);
42273         this.fireEvent("activate", this);
42274         return true;
42275     },
42276
42277     // private
42278     deactivate : function(){
42279         this.container.removeClass(this.activeClass);
42280         this.fireEvent("deactivate", this);
42281     },
42282
42283     // private
42284     shouldDeactivate : function(e){
42285         return !this.region || !this.region.contains(e.getPoint());
42286     },
42287
42288     // private
42289     handleClick : function(e){
42290         if(this.hideOnClick){
42291             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
42292         }
42293     },
42294
42295     // private
42296     expandMenu : function(autoActivate){
42297         // do nothing
42298     },
42299
42300     // private
42301     hideMenu : function(){
42302         // do nothing
42303     }
42304 });/*
42305  * Based on:
42306  * Ext JS Library 1.1.1
42307  * Copyright(c) 2006-2007, Ext JS, LLC.
42308  *
42309  * Originally Released Under LGPL - original licence link has changed is not relivant.
42310  *
42311  * Fork - LGPL
42312  * <script type="text/javascript">
42313  */
42314  
42315 /**
42316  * @class Roo.menu.Adapter
42317  * @extends Roo.menu.BaseItem
42318  * 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.
42319  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
42320  * @constructor
42321  * Creates a new Adapter
42322  * @param {Object} config Configuration options
42323  */
42324 Roo.menu.Adapter = function(component, config){
42325     Roo.menu.Adapter.superclass.constructor.call(this, config);
42326     this.component = component;
42327 };
42328 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
42329     // private
42330     canActivate : true,
42331
42332     // private
42333     onRender : function(container, position){
42334         this.component.render(container);
42335         this.el = this.component.getEl();
42336     },
42337
42338     // private
42339     activate : function(){
42340         if(this.disabled){
42341             return false;
42342         }
42343         this.component.focus();
42344         this.fireEvent("activate", this);
42345         return true;
42346     },
42347
42348     // private
42349     deactivate : function(){
42350         this.fireEvent("deactivate", this);
42351     },
42352
42353     // private
42354     disable : function(){
42355         this.component.disable();
42356         Roo.menu.Adapter.superclass.disable.call(this);
42357     },
42358
42359     // private
42360     enable : function(){
42361         this.component.enable();
42362         Roo.menu.Adapter.superclass.enable.call(this);
42363     }
42364 });/*
42365  * Based on:
42366  * Ext JS Library 1.1.1
42367  * Copyright(c) 2006-2007, Ext JS, LLC.
42368  *
42369  * Originally Released Under LGPL - original licence link has changed is not relivant.
42370  *
42371  * Fork - LGPL
42372  * <script type="text/javascript">
42373  */
42374
42375 /**
42376  * @class Roo.menu.TextItem
42377  * @extends Roo.menu.BaseItem
42378  * Adds a static text string to a menu, usually used as either a heading or group separator.
42379  * Note: old style constructor with text is still supported.
42380  * 
42381  * @constructor
42382  * Creates a new TextItem
42383  * @param {Object} cfg Configuration
42384  */
42385 Roo.menu.TextItem = function(cfg){
42386     if (typeof(cfg) == 'string') {
42387         this.text = cfg;
42388     } else {
42389         Roo.apply(this,cfg);
42390     }
42391     
42392     Roo.menu.TextItem.superclass.constructor.call(this);
42393 };
42394
42395 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
42396     /**
42397      * @cfg {Boolean} text Text to show on item.
42398      */
42399     text : '',
42400     
42401     /**
42402      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
42403      */
42404     hideOnClick : false,
42405     /**
42406      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
42407      */
42408     itemCls : "x-menu-text",
42409
42410     // private
42411     onRender : function(){
42412         var s = document.createElement("span");
42413         s.className = this.itemCls;
42414         s.innerHTML = this.text;
42415         this.el = s;
42416         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
42417     }
42418 });/*
42419  * Based on:
42420  * Ext JS Library 1.1.1
42421  * Copyright(c) 2006-2007, Ext JS, LLC.
42422  *
42423  * Originally Released Under LGPL - original licence link has changed is not relivant.
42424  *
42425  * Fork - LGPL
42426  * <script type="text/javascript">
42427  */
42428
42429 /**
42430  * @class Roo.menu.Separator
42431  * @extends Roo.menu.BaseItem
42432  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
42433  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
42434  * @constructor
42435  * @param {Object} config Configuration options
42436  */
42437 Roo.menu.Separator = function(config){
42438     Roo.menu.Separator.superclass.constructor.call(this, config);
42439 };
42440
42441 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
42442     /**
42443      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
42444      */
42445     itemCls : "x-menu-sep",
42446     /**
42447      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
42448      */
42449     hideOnClick : false,
42450
42451     // private
42452     onRender : function(li){
42453         var s = document.createElement("span");
42454         s.className = this.itemCls;
42455         s.innerHTML = "&#160;";
42456         this.el = s;
42457         li.addClass("x-menu-sep-li");
42458         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
42459     }
42460 });/*
42461  * Based on:
42462  * Ext JS Library 1.1.1
42463  * Copyright(c) 2006-2007, Ext JS, LLC.
42464  *
42465  * Originally Released Under LGPL - original licence link has changed is not relivant.
42466  *
42467  * Fork - LGPL
42468  * <script type="text/javascript">
42469  */
42470 /**
42471  * @class Roo.menu.Item
42472  * @extends Roo.menu.BaseItem
42473  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
42474  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
42475  * activation and click handling.
42476  * @constructor
42477  * Creates a new Item
42478  * @param {Object} config Configuration options
42479  */
42480 Roo.menu.Item = function(config){
42481     Roo.menu.Item.superclass.constructor.call(this, config);
42482     if(this.menu){
42483         this.menu = Roo.menu.MenuMgr.get(this.menu);
42484     }
42485 };
42486 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
42487     
42488     /**
42489      * @cfg {String} text
42490      * The text to show on the menu item.
42491      */
42492     text: '',
42493      /**
42494      * @cfg {String} HTML to render in menu
42495      * The text to show on the menu item (HTML version).
42496      */
42497     html: '',
42498     /**
42499      * @cfg {String} icon
42500      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
42501      */
42502     icon: undefined,
42503     /**
42504      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
42505      */
42506     itemCls : "x-menu-item",
42507     /**
42508      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
42509      */
42510     canActivate : true,
42511     /**
42512      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
42513      */
42514     showDelay: 200,
42515     // doc'd in BaseItem
42516     hideDelay: 200,
42517
42518     // private
42519     ctype: "Roo.menu.Item",
42520     
42521     // private
42522     onRender : function(container, position){
42523         var el = document.createElement("a");
42524         el.hideFocus = true;
42525         el.unselectable = "on";
42526         el.href = this.href || "#";
42527         if(this.hrefTarget){
42528             el.target = this.hrefTarget;
42529         }
42530         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
42531         
42532         var html = this.html.length ? this.html  : String.format('{0}',this.text);
42533         
42534         el.innerHTML = String.format(
42535                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
42536                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
42537         this.el = el;
42538         Roo.menu.Item.superclass.onRender.call(this, container, position);
42539     },
42540
42541     /**
42542      * Sets the text to display in this menu item
42543      * @param {String} text The text to display
42544      * @param {Boolean} isHTML true to indicate text is pure html.
42545      */
42546     setText : function(text, isHTML){
42547         if (isHTML) {
42548             this.html = text;
42549         } else {
42550             this.text = text;
42551             this.html = '';
42552         }
42553         if(this.rendered){
42554             var html = this.html.length ? this.html  : String.format('{0}',this.text);
42555      
42556             this.el.update(String.format(
42557                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
42558                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
42559             this.parentMenu.autoWidth();
42560         }
42561     },
42562
42563     // private
42564     handleClick : function(e){
42565         if(!this.href){ // if no link defined, stop the event automatically
42566             e.stopEvent();
42567         }
42568         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
42569     },
42570
42571     // private
42572     activate : function(autoExpand){
42573         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
42574             this.focus();
42575             if(autoExpand){
42576                 this.expandMenu();
42577             }
42578         }
42579         return true;
42580     },
42581
42582     // private
42583     shouldDeactivate : function(e){
42584         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
42585             if(this.menu && this.menu.isVisible()){
42586                 return !this.menu.getEl().getRegion().contains(e.getPoint());
42587             }
42588             return true;
42589         }
42590         return false;
42591     },
42592
42593     // private
42594     deactivate : function(){
42595         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
42596         this.hideMenu();
42597     },
42598
42599     // private
42600     expandMenu : function(autoActivate){
42601         if(!this.disabled && this.menu){
42602             clearTimeout(this.hideTimer);
42603             delete this.hideTimer;
42604             if(!this.menu.isVisible() && !this.showTimer){
42605                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
42606             }else if (this.menu.isVisible() && autoActivate){
42607                 this.menu.tryActivate(0, 1);
42608             }
42609         }
42610     },
42611
42612     // private
42613     deferExpand : function(autoActivate){
42614         delete this.showTimer;
42615         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
42616         if(autoActivate){
42617             this.menu.tryActivate(0, 1);
42618         }
42619     },
42620
42621     // private
42622     hideMenu : function(){
42623         clearTimeout(this.showTimer);
42624         delete this.showTimer;
42625         if(!this.hideTimer && this.menu && this.menu.isVisible()){
42626             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
42627         }
42628     },
42629
42630     // private
42631     deferHide : function(){
42632         delete this.hideTimer;
42633         this.menu.hide();
42634     }
42635 });/*
42636  * Based on:
42637  * Ext JS Library 1.1.1
42638  * Copyright(c) 2006-2007, Ext JS, LLC.
42639  *
42640  * Originally Released Under LGPL - original licence link has changed is not relivant.
42641  *
42642  * Fork - LGPL
42643  * <script type="text/javascript">
42644  */
42645  
42646 /**
42647  * @class Roo.menu.CheckItem
42648  * @extends Roo.menu.Item
42649  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
42650  * @constructor
42651  * Creates a new CheckItem
42652  * @param {Object} config Configuration options
42653  */
42654 Roo.menu.CheckItem = function(config){
42655     Roo.menu.CheckItem.superclass.constructor.call(this, config);
42656     this.addEvents({
42657         /**
42658          * @event beforecheckchange
42659          * Fires before the checked value is set, providing an opportunity to cancel if needed
42660          * @param {Roo.menu.CheckItem} this
42661          * @param {Boolean} checked The new checked value that will be set
42662          */
42663         "beforecheckchange" : true,
42664         /**
42665          * @event checkchange
42666          * Fires after the checked value has been set
42667          * @param {Roo.menu.CheckItem} this
42668          * @param {Boolean} checked The checked value that was set
42669          */
42670         "checkchange" : true
42671     });
42672     if(this.checkHandler){
42673         this.on('checkchange', this.checkHandler, this.scope);
42674     }
42675 };
42676 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
42677     /**
42678      * @cfg {String} group
42679      * All check items with the same group name will automatically be grouped into a single-select
42680      * radio button group (defaults to '')
42681      */
42682     /**
42683      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
42684      */
42685     itemCls : "x-menu-item x-menu-check-item",
42686     /**
42687      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
42688      */
42689     groupClass : "x-menu-group-item",
42690
42691     /**
42692      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
42693      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
42694      * initialized with checked = true will be rendered as checked.
42695      */
42696     checked: false,
42697
42698     // private
42699     ctype: "Roo.menu.CheckItem",
42700
42701     // private
42702     onRender : function(c){
42703         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
42704         if(this.group){
42705             this.el.addClass(this.groupClass);
42706         }
42707         Roo.menu.MenuMgr.registerCheckable(this);
42708         if(this.checked){
42709             this.checked = false;
42710             this.setChecked(true, true);
42711         }
42712     },
42713
42714     // private
42715     destroy : function(){
42716         if(this.rendered){
42717             Roo.menu.MenuMgr.unregisterCheckable(this);
42718         }
42719         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
42720     },
42721
42722     /**
42723      * Set the checked state of this item
42724      * @param {Boolean} checked The new checked value
42725      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
42726      */
42727     setChecked : function(state, suppressEvent){
42728         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
42729             if(this.container){
42730                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
42731             }
42732             this.checked = state;
42733             if(suppressEvent !== true){
42734                 this.fireEvent("checkchange", this, state);
42735             }
42736         }
42737     },
42738
42739     // private
42740     handleClick : function(e){
42741        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
42742            this.setChecked(!this.checked);
42743        }
42744        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
42745     }
42746 });/*
42747  * Based on:
42748  * Ext JS Library 1.1.1
42749  * Copyright(c) 2006-2007, Ext JS, LLC.
42750  *
42751  * Originally Released Under LGPL - original licence link has changed is not relivant.
42752  *
42753  * Fork - LGPL
42754  * <script type="text/javascript">
42755  */
42756  
42757 /**
42758  * @class Roo.menu.DateItem
42759  * @extends Roo.menu.Adapter
42760  * A menu item that wraps the {@link Roo.DatPicker} component.
42761  * @constructor
42762  * Creates a new DateItem
42763  * @param {Object} config Configuration options
42764  */
42765 Roo.menu.DateItem = function(config){
42766     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
42767     /** The Roo.DatePicker object @type Roo.DatePicker */
42768     this.picker = this.component;
42769     this.addEvents({select: true});
42770     
42771     this.picker.on("render", function(picker){
42772         picker.getEl().swallowEvent("click");
42773         picker.container.addClass("x-menu-date-item");
42774     });
42775
42776     this.picker.on("select", this.onSelect, this);
42777 };
42778
42779 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
42780     // private
42781     onSelect : function(picker, date){
42782         this.fireEvent("select", this, date, picker);
42783         Roo.menu.DateItem.superclass.handleClick.call(this);
42784     }
42785 });/*
42786  * Based on:
42787  * Ext JS Library 1.1.1
42788  * Copyright(c) 2006-2007, Ext JS, LLC.
42789  *
42790  * Originally Released Under LGPL - original licence link has changed is not relivant.
42791  *
42792  * Fork - LGPL
42793  * <script type="text/javascript">
42794  */
42795  
42796 /**
42797  * @class Roo.menu.ColorItem
42798  * @extends Roo.menu.Adapter
42799  * A menu item that wraps the {@link Roo.ColorPalette} component.
42800  * @constructor
42801  * Creates a new ColorItem
42802  * @param {Object} config Configuration options
42803  */
42804 Roo.menu.ColorItem = function(config){
42805     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
42806     /** The Roo.ColorPalette object @type Roo.ColorPalette */
42807     this.palette = this.component;
42808     this.relayEvents(this.palette, ["select"]);
42809     if(this.selectHandler){
42810         this.on('select', this.selectHandler, this.scope);
42811     }
42812 };
42813 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
42814  * Based on:
42815  * Ext JS Library 1.1.1
42816  * Copyright(c) 2006-2007, Ext JS, LLC.
42817  *
42818  * Originally Released Under LGPL - original licence link has changed is not relivant.
42819  *
42820  * Fork - LGPL
42821  * <script type="text/javascript">
42822  */
42823  
42824
42825 /**
42826  * @class Roo.menu.DateMenu
42827  * @extends Roo.menu.Menu
42828  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
42829  * @constructor
42830  * Creates a new DateMenu
42831  * @param {Object} config Configuration options
42832  */
42833 Roo.menu.DateMenu = function(config){
42834     Roo.menu.DateMenu.superclass.constructor.call(this, config);
42835     this.plain = true;
42836     var di = new Roo.menu.DateItem(config);
42837     this.add(di);
42838     /**
42839      * The {@link Roo.DatePicker} instance for this DateMenu
42840      * @type DatePicker
42841      */
42842     this.picker = di.picker;
42843     /**
42844      * @event select
42845      * @param {DatePicker} picker
42846      * @param {Date} date
42847      */
42848     this.relayEvents(di, ["select"]);
42849     this.on('beforeshow', function(){
42850         if(this.picker){
42851             this.picker.hideMonthPicker(false);
42852         }
42853     }, this);
42854 };
42855 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
42856     cls:'x-date-menu'
42857 });/*
42858  * Based on:
42859  * Ext JS Library 1.1.1
42860  * Copyright(c) 2006-2007, Ext JS, LLC.
42861  *
42862  * Originally Released Under LGPL - original licence link has changed is not relivant.
42863  *
42864  * Fork - LGPL
42865  * <script type="text/javascript">
42866  */
42867  
42868
42869 /**
42870  * @class Roo.menu.ColorMenu
42871  * @extends Roo.menu.Menu
42872  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
42873  * @constructor
42874  * Creates a new ColorMenu
42875  * @param {Object} config Configuration options
42876  */
42877 Roo.menu.ColorMenu = function(config){
42878     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
42879     this.plain = true;
42880     var ci = new Roo.menu.ColorItem(config);
42881     this.add(ci);
42882     /**
42883      * The {@link Roo.ColorPalette} instance for this ColorMenu
42884      * @type ColorPalette
42885      */
42886     this.palette = ci.palette;
42887     /**
42888      * @event select
42889      * @param {ColorPalette} palette
42890      * @param {String} color
42891      */
42892     this.relayEvents(ci, ["select"]);
42893 };
42894 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
42895  * Based on:
42896  * Ext JS Library 1.1.1
42897  * Copyright(c) 2006-2007, Ext JS, LLC.
42898  *
42899  * Originally Released Under LGPL - original licence link has changed is not relivant.
42900  *
42901  * Fork - LGPL
42902  * <script type="text/javascript">
42903  */
42904  
42905 /**
42906  * @class Roo.form.Field
42907  * @extends Roo.BoxComponent
42908  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
42909  * @constructor
42910  * Creates a new Field
42911  * @param {Object} config Configuration options
42912  */
42913 Roo.form.Field = function(config){
42914     Roo.form.Field.superclass.constructor.call(this, config);
42915 };
42916
42917 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
42918     /**
42919      * @cfg {String} fieldLabel Label to use when rendering a form.
42920      */
42921        /**
42922      * @cfg {String} qtip Mouse over tip
42923      */
42924      
42925     /**
42926      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
42927      */
42928     invalidClass : "x-form-invalid",
42929     /**
42930      * @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")
42931      */
42932     invalidText : "The value in this field is invalid",
42933     /**
42934      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
42935      */
42936     focusClass : "x-form-focus",
42937     /**
42938      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
42939       automatic validation (defaults to "keyup").
42940      */
42941     validationEvent : "keyup",
42942     /**
42943      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
42944      */
42945     validateOnBlur : true,
42946     /**
42947      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
42948      */
42949     validationDelay : 250,
42950     /**
42951      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42952      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
42953      */
42954     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
42955     /**
42956      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
42957      */
42958     fieldClass : "x-form-field",
42959     /**
42960      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
42961      *<pre>
42962 Value         Description
42963 -----------   ----------------------------------------------------------------------
42964 qtip          Display a quick tip when the user hovers over the field
42965 title         Display a default browser title attribute popup
42966 under         Add a block div beneath the field containing the error text
42967 side          Add an error icon to the right of the field with a popup on hover
42968 [element id]  Add the error text directly to the innerHTML of the specified element
42969 </pre>
42970      */
42971     msgTarget : 'qtip',
42972     /**
42973      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
42974      */
42975     msgFx : 'normal',
42976
42977     /**
42978      * @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.
42979      */
42980     readOnly : false,
42981
42982     /**
42983      * @cfg {Boolean} disabled True to disable the field (defaults to false).
42984      */
42985     disabled : false,
42986
42987     /**
42988      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
42989      */
42990     inputType : undefined,
42991     
42992     /**
42993      * @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).
42994          */
42995         tabIndex : undefined,
42996         
42997     // private
42998     isFormField : true,
42999
43000     // private
43001     hasFocus : false,
43002     /**
43003      * @property {Roo.Element} fieldEl
43004      * Element Containing the rendered Field (with label etc.)
43005      */
43006     /**
43007      * @cfg {Mixed} value A value to initialize this field with.
43008      */
43009     value : undefined,
43010
43011     /**
43012      * @cfg {String} name The field's HTML name attribute.
43013      */
43014     /**
43015      * @cfg {String} cls A CSS class to apply to the field's underlying element.
43016      */
43017     // private
43018     loadedValue : false,
43019      
43020      
43021         // private ??
43022         initComponent : function(){
43023         Roo.form.Field.superclass.initComponent.call(this);
43024         this.addEvents({
43025             /**
43026              * @event focus
43027              * Fires when this field receives input focus.
43028              * @param {Roo.form.Field} this
43029              */
43030             focus : true,
43031             /**
43032              * @event blur
43033              * Fires when this field loses input focus.
43034              * @param {Roo.form.Field} this
43035              */
43036             blur : true,
43037             /**
43038              * @event specialkey
43039              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
43040              * {@link Roo.EventObject#getKey} to determine which key was pressed.
43041              * @param {Roo.form.Field} this
43042              * @param {Roo.EventObject} e The event object
43043              */
43044             specialkey : true,
43045             /**
43046              * @event change
43047              * Fires just before the field blurs if the field value has changed.
43048              * @param {Roo.form.Field} this
43049              * @param {Mixed} newValue The new value
43050              * @param {Mixed} oldValue The original value
43051              */
43052             change : true,
43053             /**
43054              * @event invalid
43055              * Fires after the field has been marked as invalid.
43056              * @param {Roo.form.Field} this
43057              * @param {String} msg The validation message
43058              */
43059             invalid : true,
43060             /**
43061              * @event valid
43062              * Fires after the field has been validated with no errors.
43063              * @param {Roo.form.Field} this
43064              */
43065             valid : true,
43066              /**
43067              * @event keyup
43068              * Fires after the key up
43069              * @param {Roo.form.Field} this
43070              * @param {Roo.EventObject}  e The event Object
43071              */
43072             keyup : true
43073         });
43074     },
43075
43076     /**
43077      * Returns the name attribute of the field if available
43078      * @return {String} name The field name
43079      */
43080     getName: function(){
43081          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
43082     },
43083
43084     // private
43085     onRender : function(ct, position){
43086         Roo.form.Field.superclass.onRender.call(this, ct, position);
43087         if(!this.el){
43088             var cfg = this.getAutoCreate();
43089             if(!cfg.name){
43090                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
43091             }
43092             if (!cfg.name.length) {
43093                 delete cfg.name;
43094             }
43095             if(this.inputType){
43096                 cfg.type = this.inputType;
43097             }
43098             this.el = ct.createChild(cfg, position);
43099         }
43100         var type = this.el.dom.type;
43101         if(type){
43102             if(type == 'password'){
43103                 type = 'text';
43104             }
43105             this.el.addClass('x-form-'+type);
43106         }
43107         if(this.readOnly){
43108             this.el.dom.readOnly = true;
43109         }
43110         if(this.tabIndex !== undefined){
43111             this.el.dom.setAttribute('tabIndex', this.tabIndex);
43112         }
43113
43114         this.el.addClass([this.fieldClass, this.cls]);
43115         this.initValue();
43116     },
43117
43118     /**
43119      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
43120      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
43121      * @return {Roo.form.Field} this
43122      */
43123     applyTo : function(target){
43124         this.allowDomMove = false;
43125         this.el = Roo.get(target);
43126         this.render(this.el.dom.parentNode);
43127         return this;
43128     },
43129
43130     // private
43131     initValue : function(){
43132         if(this.value !== undefined){
43133             this.setValue(this.value);
43134         }else if(this.el.dom.value.length > 0){
43135             this.setValue(this.el.dom.value);
43136         }
43137     },
43138
43139     /**
43140      * Returns true if this field has been changed since it was originally loaded and is not disabled.
43141      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
43142      */
43143     isDirty : function() {
43144         if(this.disabled) {
43145             return false;
43146         }
43147         return String(this.getValue()) !== String(this.originalValue);
43148     },
43149
43150     /**
43151      * stores the current value in loadedValue
43152      */
43153     resetHasChanged : function()
43154     {
43155         this.loadedValue = String(this.getValue());
43156     },
43157     /**
43158      * checks the current value against the 'loaded' value.
43159      * Note - will return false if 'resetHasChanged' has not been called first.
43160      */
43161     hasChanged : function()
43162     {
43163         if(this.disabled || this.readOnly) {
43164             return false;
43165         }
43166         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
43167     },
43168     
43169     
43170     
43171     // private
43172     afterRender : function(){
43173         Roo.form.Field.superclass.afterRender.call(this);
43174         this.initEvents();
43175     },
43176
43177     // private
43178     fireKey : function(e){
43179         //Roo.log('field ' + e.getKey());
43180         if(e.isNavKeyPress()){
43181             this.fireEvent("specialkey", this, e);
43182         }
43183     },
43184
43185     /**
43186      * Resets the current field value to the originally loaded value and clears any validation messages
43187      */
43188     reset : function(){
43189         this.setValue(this.resetValue);
43190         this.clearInvalid();
43191     },
43192
43193     // private
43194     initEvents : function(){
43195         // safari killled keypress - so keydown is now used..
43196         this.el.on("keydown" , this.fireKey,  this);
43197         this.el.on("focus", this.onFocus,  this);
43198         this.el.on("blur", this.onBlur,  this);
43199         this.el.relayEvent('keyup', this);
43200
43201         // reference to original value for reset
43202         this.originalValue = this.getValue();
43203         this.resetValue =  this.getValue();
43204     },
43205
43206     // private
43207     onFocus : function(){
43208         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43209             this.el.addClass(this.focusClass);
43210         }
43211         if(!this.hasFocus){
43212             this.hasFocus = true;
43213             this.startValue = this.getValue();
43214             this.fireEvent("focus", this);
43215         }
43216     },
43217
43218     beforeBlur : Roo.emptyFn,
43219
43220     // private
43221     onBlur : function(){
43222         this.beforeBlur();
43223         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43224             this.el.removeClass(this.focusClass);
43225         }
43226         this.hasFocus = false;
43227         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43228             this.validate();
43229         }
43230         var v = this.getValue();
43231         if(String(v) !== String(this.startValue)){
43232             this.fireEvent('change', this, v, this.startValue);
43233         }
43234         this.fireEvent("blur", this);
43235     },
43236
43237     /**
43238      * Returns whether or not the field value is currently valid
43239      * @param {Boolean} preventMark True to disable marking the field invalid
43240      * @return {Boolean} True if the value is valid, else false
43241      */
43242     isValid : function(preventMark){
43243         if(this.disabled){
43244             return true;
43245         }
43246         var restore = this.preventMark;
43247         this.preventMark = preventMark === true;
43248         var v = this.validateValue(this.processValue(this.getRawValue()));
43249         this.preventMark = restore;
43250         return v;
43251     },
43252
43253     /**
43254      * Validates the field value
43255      * @return {Boolean} True if the value is valid, else false
43256      */
43257     validate : function(){
43258         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
43259             this.clearInvalid();
43260             return true;
43261         }
43262         return false;
43263     },
43264
43265     processValue : function(value){
43266         return value;
43267     },
43268
43269     // private
43270     // Subclasses should provide the validation implementation by overriding this
43271     validateValue : function(value){
43272         return true;
43273     },
43274
43275     /**
43276      * Mark this field as invalid
43277      * @param {String} msg The validation message
43278      */
43279     markInvalid : function(msg){
43280         if(!this.rendered || this.preventMark){ // not rendered
43281             return;
43282         }
43283         
43284         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
43285         
43286         obj.el.addClass(this.invalidClass);
43287         msg = msg || this.invalidText;
43288         switch(this.msgTarget){
43289             case 'qtip':
43290                 obj.el.dom.qtip = msg;
43291                 obj.el.dom.qclass = 'x-form-invalid-tip';
43292                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
43293                     Roo.QuickTips.enable();
43294                 }
43295                 break;
43296             case 'title':
43297                 this.el.dom.title = msg;
43298                 break;
43299             case 'under':
43300                 if(!this.errorEl){
43301                     var elp = this.el.findParent('.x-form-element', 5, true);
43302                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
43303                     this.errorEl.setWidth(elp.getWidth(true)-20);
43304                 }
43305                 this.errorEl.update(msg);
43306                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
43307                 break;
43308             case 'side':
43309                 if(!this.errorIcon){
43310                     var elp = this.el.findParent('.x-form-element', 5, true);
43311                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
43312                 }
43313                 this.alignErrorIcon();
43314                 this.errorIcon.dom.qtip = msg;
43315                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
43316                 this.errorIcon.show();
43317                 this.on('resize', this.alignErrorIcon, this);
43318                 break;
43319             default:
43320                 var t = Roo.getDom(this.msgTarget);
43321                 t.innerHTML = msg;
43322                 t.style.display = this.msgDisplay;
43323                 break;
43324         }
43325         this.fireEvent('invalid', this, msg);
43326     },
43327
43328     // private
43329     alignErrorIcon : function(){
43330         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
43331     },
43332
43333     /**
43334      * Clear any invalid styles/messages for this field
43335      */
43336     clearInvalid : function(){
43337         if(!this.rendered || this.preventMark){ // not rendered
43338             return;
43339         }
43340         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
43341         
43342         obj.el.removeClass(this.invalidClass);
43343         switch(this.msgTarget){
43344             case 'qtip':
43345                 obj.el.dom.qtip = '';
43346                 break;
43347             case 'title':
43348                 this.el.dom.title = '';
43349                 break;
43350             case 'under':
43351                 if(this.errorEl){
43352                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
43353                 }
43354                 break;
43355             case 'side':
43356                 if(this.errorIcon){
43357                     this.errorIcon.dom.qtip = '';
43358                     this.errorIcon.hide();
43359                     this.un('resize', this.alignErrorIcon, this);
43360                 }
43361                 break;
43362             default:
43363                 var t = Roo.getDom(this.msgTarget);
43364                 t.innerHTML = '';
43365                 t.style.display = 'none';
43366                 break;
43367         }
43368         this.fireEvent('valid', this);
43369     },
43370
43371     /**
43372      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
43373      * @return {Mixed} value The field value
43374      */
43375     getRawValue : function(){
43376         var v = this.el.getValue();
43377         
43378         return v;
43379     },
43380
43381     /**
43382      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
43383      * @return {Mixed} value The field value
43384      */
43385     getValue : function(){
43386         var v = this.el.getValue();
43387          
43388         return v;
43389     },
43390
43391     /**
43392      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
43393      * @param {Mixed} value The value to set
43394      */
43395     setRawValue : function(v){
43396         return this.el.dom.value = (v === null || v === undefined ? '' : v);
43397     },
43398
43399     /**
43400      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
43401      * @param {Mixed} value The value to set
43402      */
43403     setValue : function(v){
43404         this.value = v;
43405         if(this.rendered){
43406             this.el.dom.value = (v === null || v === undefined ? '' : v);
43407              this.validate();
43408         }
43409     },
43410
43411     adjustSize : function(w, h){
43412         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
43413         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
43414         return s;
43415     },
43416
43417     adjustWidth : function(tag, w){
43418         tag = tag.toLowerCase();
43419         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
43420             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
43421                 if(tag == 'input'){
43422                     return w + 2;
43423                 }
43424                 if(tag == 'textarea'){
43425                     return w-2;
43426                 }
43427             }else if(Roo.isOpera){
43428                 if(tag == 'input'){
43429                     return w + 2;
43430                 }
43431                 if(tag == 'textarea'){
43432                     return w-2;
43433                 }
43434             }
43435         }
43436         return w;
43437     }
43438 });
43439
43440
43441 // anything other than normal should be considered experimental
43442 Roo.form.Field.msgFx = {
43443     normal : {
43444         show: function(msgEl, f){
43445             msgEl.setDisplayed('block');
43446         },
43447
43448         hide : function(msgEl, f){
43449             msgEl.setDisplayed(false).update('');
43450         }
43451     },
43452
43453     slide : {
43454         show: function(msgEl, f){
43455             msgEl.slideIn('t', {stopFx:true});
43456         },
43457
43458         hide : function(msgEl, f){
43459             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
43460         }
43461     },
43462
43463     slideRight : {
43464         show: function(msgEl, f){
43465             msgEl.fixDisplay();
43466             msgEl.alignTo(f.el, 'tl-tr');
43467             msgEl.slideIn('l', {stopFx:true});
43468         },
43469
43470         hide : function(msgEl, f){
43471             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
43472         }
43473     }
43474 };/*
43475  * Based on:
43476  * Ext JS Library 1.1.1
43477  * Copyright(c) 2006-2007, Ext JS, LLC.
43478  *
43479  * Originally Released Under LGPL - original licence link has changed is not relivant.
43480  *
43481  * Fork - LGPL
43482  * <script type="text/javascript">
43483  */
43484  
43485
43486 /**
43487  * @class Roo.form.TextField
43488  * @extends Roo.form.Field
43489  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
43490  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
43491  * @constructor
43492  * Creates a new TextField
43493  * @param {Object} config Configuration options
43494  */
43495 Roo.form.TextField = function(config){
43496     Roo.form.TextField.superclass.constructor.call(this, config);
43497     this.addEvents({
43498         /**
43499          * @event autosize
43500          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
43501          * according to the default logic, but this event provides a hook for the developer to apply additional
43502          * logic at runtime to resize the field if needed.
43503              * @param {Roo.form.Field} this This text field
43504              * @param {Number} width The new field width
43505              */
43506         autosize : true
43507     });
43508 };
43509
43510 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
43511     /**
43512      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
43513      */
43514     grow : false,
43515     /**
43516      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
43517      */
43518     growMin : 30,
43519     /**
43520      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
43521      */
43522     growMax : 800,
43523     /**
43524      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
43525      */
43526     vtype : null,
43527     /**
43528      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
43529      */
43530     maskRe : null,
43531     /**
43532      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
43533      */
43534     disableKeyFilter : false,
43535     /**
43536      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
43537      */
43538     allowBlank : true,
43539     /**
43540      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
43541      */
43542     minLength : 0,
43543     /**
43544      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
43545      */
43546     maxLength : Number.MAX_VALUE,
43547     /**
43548      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
43549      */
43550     minLengthText : "The minimum length for this field is {0}",
43551     /**
43552      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
43553      */
43554     maxLengthText : "The maximum length for this field is {0}",
43555     /**
43556      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
43557      */
43558     selectOnFocus : false,
43559     /**
43560      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
43561      */
43562     blankText : "This field is required",
43563     /**
43564      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
43565      * If available, this function will be called only after the basic validators all return true, and will be passed the
43566      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
43567      */
43568     validator : null,
43569     /**
43570      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
43571      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
43572      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
43573      */
43574     regex : null,
43575     /**
43576      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
43577      */
43578     regexText : "",
43579     /**
43580      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
43581      */
43582     emptyText : null,
43583    
43584
43585     // private
43586     initEvents : function()
43587     {
43588         if (this.emptyText) {
43589             this.el.attr('placeholder', this.emptyText);
43590         }
43591         
43592         Roo.form.TextField.superclass.initEvents.call(this);
43593         if(this.validationEvent == 'keyup'){
43594             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43595             this.el.on('keyup', this.filterValidation, this);
43596         }
43597         else if(this.validationEvent !== false){
43598             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43599         }
43600         
43601         if(this.selectOnFocus){
43602             this.on("focus", this.preFocus, this);
43603             
43604         }
43605         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43606             this.el.on("keypress", this.filterKeys, this);
43607         }
43608         if(this.grow){
43609             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
43610             this.el.on("click", this.autoSize,  this);
43611         }
43612         if(this.el.is('input[type=password]') && Roo.isSafari){
43613             this.el.on('keydown', this.SafariOnKeyDown, this);
43614         }
43615     },
43616
43617     processValue : function(value){
43618         if(this.stripCharsRe){
43619             var newValue = value.replace(this.stripCharsRe, '');
43620             if(newValue !== value){
43621                 this.setRawValue(newValue);
43622                 return newValue;
43623             }
43624         }
43625         return value;
43626     },
43627
43628     filterValidation : function(e){
43629         if(!e.isNavKeyPress()){
43630             this.validationTask.delay(this.validationDelay);
43631         }
43632     },
43633
43634     // private
43635     onKeyUp : function(e){
43636         if(!e.isNavKeyPress()){
43637             this.autoSize();
43638         }
43639     },
43640
43641     /**
43642      * Resets the current field value to the originally-loaded value and clears any validation messages.
43643      *  
43644      */
43645     reset : function(){
43646         Roo.form.TextField.superclass.reset.call(this);
43647        
43648     },
43649
43650     
43651     // private
43652     preFocus : function(){
43653         
43654         if(this.selectOnFocus){
43655             this.el.dom.select();
43656         }
43657     },
43658
43659     
43660     // private
43661     filterKeys : function(e){
43662         var k = e.getKey();
43663         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
43664             return;
43665         }
43666         var c = e.getCharCode(), cc = String.fromCharCode(c);
43667         if(Roo.isIE && (e.isSpecialKey() || !cc)){
43668             return;
43669         }
43670         if(!this.maskRe.test(cc)){
43671             e.stopEvent();
43672         }
43673     },
43674
43675     setValue : function(v){
43676         
43677         Roo.form.TextField.superclass.setValue.apply(this, arguments);
43678         
43679         this.autoSize();
43680     },
43681
43682     /**
43683      * Validates a value according to the field's validation rules and marks the field as invalid
43684      * if the validation fails
43685      * @param {Mixed} value The value to validate
43686      * @return {Boolean} True if the value is valid, else false
43687      */
43688     validateValue : function(value){
43689         if(value.length < 1)  { // if it's blank
43690              if(this.allowBlank){
43691                 this.clearInvalid();
43692                 return true;
43693              }else{
43694                 this.markInvalid(this.blankText);
43695                 return false;
43696              }
43697         }
43698         if(value.length < this.minLength){
43699             this.markInvalid(String.format(this.minLengthText, this.minLength));
43700             return false;
43701         }
43702         if(value.length > this.maxLength){
43703             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
43704             return false;
43705         }
43706         if(this.vtype){
43707             var vt = Roo.form.VTypes;
43708             if(!vt[this.vtype](value, this)){
43709                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
43710                 return false;
43711             }
43712         }
43713         if(typeof this.validator == "function"){
43714             var msg = this.validator(value);
43715             if(msg !== true){
43716                 this.markInvalid(msg);
43717                 return false;
43718             }
43719         }
43720         if(this.regex && !this.regex.test(value)){
43721             this.markInvalid(this.regexText);
43722             return false;
43723         }
43724         return true;
43725     },
43726
43727     /**
43728      * Selects text in this field
43729      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
43730      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
43731      */
43732     selectText : function(start, end){
43733         var v = this.getRawValue();
43734         if(v.length > 0){
43735             start = start === undefined ? 0 : start;
43736             end = end === undefined ? v.length : end;
43737             var d = this.el.dom;
43738             if(d.setSelectionRange){
43739                 d.setSelectionRange(start, end);
43740             }else if(d.createTextRange){
43741                 var range = d.createTextRange();
43742                 range.moveStart("character", start);
43743                 range.moveEnd("character", v.length-end);
43744                 range.select();
43745             }
43746         }
43747     },
43748
43749     /**
43750      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
43751      * This only takes effect if grow = true, and fires the autosize event.
43752      */
43753     autoSize : function(){
43754         if(!this.grow || !this.rendered){
43755             return;
43756         }
43757         if(!this.metrics){
43758             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
43759         }
43760         var el = this.el;
43761         var v = el.dom.value;
43762         var d = document.createElement('div');
43763         d.appendChild(document.createTextNode(v));
43764         v = d.innerHTML;
43765         d = null;
43766         v += "&#160;";
43767         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
43768         this.el.setWidth(w);
43769         this.fireEvent("autosize", this, w);
43770     },
43771     
43772     // private
43773     SafariOnKeyDown : function(event)
43774     {
43775         // this is a workaround for a password hang bug on chrome/ webkit.
43776         
43777         var isSelectAll = false;
43778         
43779         if(this.el.dom.selectionEnd > 0){
43780             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
43781         }
43782         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
43783             event.preventDefault();
43784             this.setValue('');
43785             return;
43786         }
43787         
43788         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
43789             
43790             event.preventDefault();
43791             // this is very hacky as keydown always get's upper case.
43792             
43793             var cc = String.fromCharCode(event.getCharCode());
43794             
43795             
43796             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
43797             
43798         }
43799         
43800         
43801     }
43802 });/*
43803  * Based on:
43804  * Ext JS Library 1.1.1
43805  * Copyright(c) 2006-2007, Ext JS, LLC.
43806  *
43807  * Originally Released Under LGPL - original licence link has changed is not relivant.
43808  *
43809  * Fork - LGPL
43810  * <script type="text/javascript">
43811  */
43812  
43813 /**
43814  * @class Roo.form.Hidden
43815  * @extends Roo.form.TextField
43816  * Simple Hidden element used on forms 
43817  * 
43818  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
43819  * 
43820  * @constructor
43821  * Creates a new Hidden form element.
43822  * @param {Object} config Configuration options
43823  */
43824
43825
43826
43827 // easy hidden field...
43828 Roo.form.Hidden = function(config){
43829     Roo.form.Hidden.superclass.constructor.call(this, config);
43830 };
43831   
43832 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
43833     fieldLabel:      '',
43834     inputType:      'hidden',
43835     width:          50,
43836     allowBlank:     true,
43837     labelSeparator: '',
43838     hidden:         true,
43839     itemCls :       'x-form-item-display-none'
43840
43841
43842 });
43843
43844
43845 /*
43846  * Based on:
43847  * Ext JS Library 1.1.1
43848  * Copyright(c) 2006-2007, Ext JS, LLC.
43849  *
43850  * Originally Released Under LGPL - original licence link has changed is not relivant.
43851  *
43852  * Fork - LGPL
43853  * <script type="text/javascript">
43854  */
43855  
43856 /**
43857  * @class Roo.form.TriggerField
43858  * @extends Roo.form.TextField
43859  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
43860  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
43861  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
43862  * for which you can provide a custom implementation.  For example:
43863  * <pre><code>
43864 var trigger = new Roo.form.TriggerField();
43865 trigger.onTriggerClick = myTriggerFn;
43866 trigger.applyTo('my-field');
43867 </code></pre>
43868  *
43869  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
43870  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
43871  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
43872  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
43873  * @constructor
43874  * Create a new TriggerField.
43875  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
43876  * to the base TextField)
43877  */
43878 Roo.form.TriggerField = function(config){
43879     this.mimicing = false;
43880     Roo.form.TriggerField.superclass.constructor.call(this, config);
43881 };
43882
43883 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
43884     /**
43885      * @cfg {String} triggerClass A CSS class to apply to the trigger
43886      */
43887     /**
43888      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43889      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
43890      */
43891     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
43892     /**
43893      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
43894      */
43895     hideTrigger:false,
43896
43897     /** @cfg {Boolean} grow @hide */
43898     /** @cfg {Number} growMin @hide */
43899     /** @cfg {Number} growMax @hide */
43900
43901     /**
43902      * @hide 
43903      * @method
43904      */
43905     autoSize: Roo.emptyFn,
43906     // private
43907     monitorTab : true,
43908     // private
43909     deferHeight : true,
43910
43911     
43912     actionMode : 'wrap',
43913     // private
43914     onResize : function(w, h){
43915         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
43916         if(typeof w == 'number'){
43917             var x = w - this.trigger.getWidth();
43918             this.el.setWidth(this.adjustWidth('input', x));
43919             this.trigger.setStyle('left', x+'px');
43920         }
43921     },
43922
43923     // private
43924     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43925
43926     // private
43927     getResizeEl : function(){
43928         return this.wrap;
43929     },
43930
43931     // private
43932     getPositionEl : function(){
43933         return this.wrap;
43934     },
43935
43936     // private
43937     alignErrorIcon : function(){
43938         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
43939     },
43940
43941     // private
43942     onRender : function(ct, position){
43943         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
43944         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
43945         this.trigger = this.wrap.createChild(this.triggerConfig ||
43946                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
43947         if(this.hideTrigger){
43948             this.trigger.setDisplayed(false);
43949         }
43950         this.initTrigger();
43951         if(!this.width){
43952             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
43953         }
43954     },
43955
43956     // private
43957     initTrigger : function(){
43958         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43959         this.trigger.addClassOnOver('x-form-trigger-over');
43960         this.trigger.addClassOnClick('x-form-trigger-click');
43961     },
43962
43963     // private
43964     onDestroy : function(){
43965         if(this.trigger){
43966             this.trigger.removeAllListeners();
43967             this.trigger.remove();
43968         }
43969         if(this.wrap){
43970             this.wrap.remove();
43971         }
43972         Roo.form.TriggerField.superclass.onDestroy.call(this);
43973     },
43974
43975     // private
43976     onFocus : function(){
43977         Roo.form.TriggerField.superclass.onFocus.call(this);
43978         if(!this.mimicing){
43979             this.wrap.addClass('x-trigger-wrap-focus');
43980             this.mimicing = true;
43981             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
43982             if(this.monitorTab){
43983                 this.el.on("keydown", this.checkTab, this);
43984             }
43985         }
43986     },
43987
43988     // private
43989     checkTab : function(e){
43990         if(e.getKey() == e.TAB){
43991             this.triggerBlur();
43992         }
43993     },
43994
43995     // private
43996     onBlur : function(){
43997         // do nothing
43998     },
43999
44000     // private
44001     mimicBlur : function(e, t){
44002         if(!this.wrap.contains(t) && this.validateBlur()){
44003             this.triggerBlur();
44004         }
44005     },
44006
44007     // private
44008     triggerBlur : function(){
44009         this.mimicing = false;
44010         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
44011         if(this.monitorTab){
44012             this.el.un("keydown", this.checkTab, this);
44013         }
44014         this.wrap.removeClass('x-trigger-wrap-focus');
44015         Roo.form.TriggerField.superclass.onBlur.call(this);
44016     },
44017
44018     // private
44019     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
44020     validateBlur : function(e, t){
44021         return true;
44022     },
44023
44024     // private
44025     onDisable : function(){
44026         Roo.form.TriggerField.superclass.onDisable.call(this);
44027         if(this.wrap){
44028             this.wrap.addClass('x-item-disabled');
44029         }
44030     },
44031
44032     // private
44033     onEnable : function(){
44034         Roo.form.TriggerField.superclass.onEnable.call(this);
44035         if(this.wrap){
44036             this.wrap.removeClass('x-item-disabled');
44037         }
44038     },
44039
44040     // private
44041     onShow : function(){
44042         var ae = this.getActionEl();
44043         
44044         if(ae){
44045             ae.dom.style.display = '';
44046             ae.dom.style.visibility = 'visible';
44047         }
44048     },
44049
44050     // private
44051     
44052     onHide : function(){
44053         var ae = this.getActionEl();
44054         ae.dom.style.display = 'none';
44055     },
44056
44057     /**
44058      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
44059      * by an implementing function.
44060      * @method
44061      * @param {EventObject} e
44062      */
44063     onTriggerClick : Roo.emptyFn
44064 });
44065
44066 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
44067 // to be extended by an implementing class.  For an example of implementing this class, see the custom
44068 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
44069 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
44070     initComponent : function(){
44071         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
44072
44073         this.triggerConfig = {
44074             tag:'span', cls:'x-form-twin-triggers', cn:[
44075             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
44076             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
44077         ]};
44078     },
44079
44080     getTrigger : function(index){
44081         return this.triggers[index];
44082     },
44083
44084     initTrigger : function(){
44085         var ts = this.trigger.select('.x-form-trigger', true);
44086         this.wrap.setStyle('overflow', 'hidden');
44087         var triggerField = this;
44088         ts.each(function(t, all, index){
44089             t.hide = function(){
44090                 var w = triggerField.wrap.getWidth();
44091                 this.dom.style.display = 'none';
44092                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
44093             };
44094             t.show = function(){
44095                 var w = triggerField.wrap.getWidth();
44096                 this.dom.style.display = '';
44097                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
44098             };
44099             var triggerIndex = 'Trigger'+(index+1);
44100
44101             if(this['hide'+triggerIndex]){
44102                 t.dom.style.display = 'none';
44103             }
44104             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
44105             t.addClassOnOver('x-form-trigger-over');
44106             t.addClassOnClick('x-form-trigger-click');
44107         }, this);
44108         this.triggers = ts.elements;
44109     },
44110
44111     onTrigger1Click : Roo.emptyFn,
44112     onTrigger2Click : Roo.emptyFn
44113 });/*
44114  * Based on:
44115  * Ext JS Library 1.1.1
44116  * Copyright(c) 2006-2007, Ext JS, LLC.
44117  *
44118  * Originally Released Under LGPL - original licence link has changed is not relivant.
44119  *
44120  * Fork - LGPL
44121  * <script type="text/javascript">
44122  */
44123  
44124 /**
44125  * @class Roo.form.TextArea
44126  * @extends Roo.form.TextField
44127  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
44128  * support for auto-sizing.
44129  * @constructor
44130  * Creates a new TextArea
44131  * @param {Object} config Configuration options
44132  */
44133 Roo.form.TextArea = function(config){
44134     Roo.form.TextArea.superclass.constructor.call(this, config);
44135     // these are provided exchanges for backwards compat
44136     // minHeight/maxHeight were replaced by growMin/growMax to be
44137     // compatible with TextField growing config values
44138     if(this.minHeight !== undefined){
44139         this.growMin = this.minHeight;
44140     }
44141     if(this.maxHeight !== undefined){
44142         this.growMax = this.maxHeight;
44143     }
44144 };
44145
44146 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
44147     /**
44148      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
44149      */
44150     growMin : 60,
44151     /**
44152      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
44153      */
44154     growMax: 1000,
44155     /**
44156      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
44157      * in the field (equivalent to setting overflow: hidden, defaults to false)
44158      */
44159     preventScrollbars: false,
44160     /**
44161      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44162      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
44163      */
44164
44165     // private
44166     onRender : function(ct, position){
44167         if(!this.el){
44168             this.defaultAutoCreate = {
44169                 tag: "textarea",
44170                 style:"width:300px;height:60px;",
44171                 autocomplete: "new-password"
44172             };
44173         }
44174         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
44175         if(this.grow){
44176             this.textSizeEl = Roo.DomHelper.append(document.body, {
44177                 tag: "pre", cls: "x-form-grow-sizer"
44178             });
44179             if(this.preventScrollbars){
44180                 this.el.setStyle("overflow", "hidden");
44181             }
44182             this.el.setHeight(this.growMin);
44183         }
44184     },
44185
44186     onDestroy : function(){
44187         if(this.textSizeEl){
44188             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
44189         }
44190         Roo.form.TextArea.superclass.onDestroy.call(this);
44191     },
44192
44193     // private
44194     onKeyUp : function(e){
44195         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
44196             this.autoSize();
44197         }
44198     },
44199
44200     /**
44201      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
44202      * This only takes effect if grow = true, and fires the autosize event if the height changes.
44203      */
44204     autoSize : function(){
44205         if(!this.grow || !this.textSizeEl){
44206             return;
44207         }
44208         var el = this.el;
44209         var v = el.dom.value;
44210         var ts = this.textSizeEl;
44211
44212         ts.innerHTML = '';
44213         ts.appendChild(document.createTextNode(v));
44214         v = ts.innerHTML;
44215
44216         Roo.fly(ts).setWidth(this.el.getWidth());
44217         if(v.length < 1){
44218             v = "&#160;&#160;";
44219         }else{
44220             if(Roo.isIE){
44221                 v = v.replace(/\n/g, '<p>&#160;</p>');
44222             }
44223             v += "&#160;\n&#160;";
44224         }
44225         ts.innerHTML = v;
44226         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
44227         if(h != this.lastHeight){
44228             this.lastHeight = h;
44229             this.el.setHeight(h);
44230             this.fireEvent("autosize", this, h);
44231         }
44232     }
44233 });/*
44234  * Based on:
44235  * Ext JS Library 1.1.1
44236  * Copyright(c) 2006-2007, Ext JS, LLC.
44237  *
44238  * Originally Released Under LGPL - original licence link has changed is not relivant.
44239  *
44240  * Fork - LGPL
44241  * <script type="text/javascript">
44242  */
44243  
44244
44245 /**
44246  * @class Roo.form.NumberField
44247  * @extends Roo.form.TextField
44248  * Numeric text field that provides automatic keystroke filtering and numeric validation.
44249  * @constructor
44250  * Creates a new NumberField
44251  * @param {Object} config Configuration options
44252  */
44253 Roo.form.NumberField = function(config){
44254     Roo.form.NumberField.superclass.constructor.call(this, config);
44255 };
44256
44257 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
44258     /**
44259      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
44260      */
44261     fieldClass: "x-form-field x-form-num-field",
44262     /**
44263      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44264      */
44265     allowDecimals : true,
44266     /**
44267      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44268      */
44269     decimalSeparator : ".",
44270     /**
44271      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44272      */
44273     decimalPrecision : 2,
44274     /**
44275      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44276      */
44277     allowNegative : true,
44278     /**
44279      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44280      */
44281     minValue : Number.NEGATIVE_INFINITY,
44282     /**
44283      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44284      */
44285     maxValue : Number.MAX_VALUE,
44286     /**
44287      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44288      */
44289     minText : "The minimum value for this field is {0}",
44290     /**
44291      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44292      */
44293     maxText : "The maximum value for this field is {0}",
44294     /**
44295      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44296      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44297      */
44298     nanText : "{0} is not a valid number",
44299
44300     // private
44301     initEvents : function(){
44302         Roo.form.NumberField.superclass.initEvents.call(this);
44303         var allowed = "0123456789";
44304         if(this.allowDecimals){
44305             allowed += this.decimalSeparator;
44306         }
44307         if(this.allowNegative){
44308             allowed += "-";
44309         }
44310         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44311         var keyPress = function(e){
44312             var k = e.getKey();
44313             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44314                 return;
44315             }
44316             var c = e.getCharCode();
44317             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44318                 e.stopEvent();
44319             }
44320         };
44321         this.el.on("keypress", keyPress, this);
44322     },
44323
44324     // private
44325     validateValue : function(value){
44326         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
44327             return false;
44328         }
44329         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
44330              return true;
44331         }
44332         var num = this.parseValue(value);
44333         if(isNaN(num)){
44334             this.markInvalid(String.format(this.nanText, value));
44335             return false;
44336         }
44337         if(num < this.minValue){
44338             this.markInvalid(String.format(this.minText, this.minValue));
44339             return false;
44340         }
44341         if(num > this.maxValue){
44342             this.markInvalid(String.format(this.maxText, this.maxValue));
44343             return false;
44344         }
44345         return true;
44346     },
44347
44348     getValue : function(){
44349         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
44350     },
44351
44352     // private
44353     parseValue : function(value){
44354         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44355         return isNaN(value) ? '' : value;
44356     },
44357
44358     // private
44359     fixPrecision : function(value){
44360         var nan = isNaN(value);
44361         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44362             return nan ? '' : value;
44363         }
44364         return parseFloat(value).toFixed(this.decimalPrecision);
44365     },
44366
44367     setValue : function(v){
44368         v = this.fixPrecision(v);
44369         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
44370     },
44371
44372     // private
44373     decimalPrecisionFcn : function(v){
44374         return Math.floor(v);
44375     },
44376
44377     beforeBlur : function(){
44378         var v = this.parseValue(this.getRawValue());
44379         if(v){
44380             this.setValue(v);
44381         }
44382     }
44383 });/*
44384  * Based on:
44385  * Ext JS Library 1.1.1
44386  * Copyright(c) 2006-2007, Ext JS, LLC.
44387  *
44388  * Originally Released Under LGPL - original licence link has changed is not relivant.
44389  *
44390  * Fork - LGPL
44391  * <script type="text/javascript">
44392  */
44393  
44394 /**
44395  * @class Roo.form.DateField
44396  * @extends Roo.form.TriggerField
44397  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
44398 * @constructor
44399 * Create a new DateField
44400 * @param {Object} config
44401  */
44402 Roo.form.DateField = function(config){
44403     Roo.form.DateField.superclass.constructor.call(this, config);
44404     
44405       this.addEvents({
44406          
44407         /**
44408          * @event select
44409          * Fires when a date is selected
44410              * @param {Roo.form.DateField} combo This combo box
44411              * @param {Date} date The date selected
44412              */
44413         'select' : true
44414          
44415     });
44416     
44417     
44418     if(typeof this.minValue == "string") {
44419         this.minValue = this.parseDate(this.minValue);
44420     }
44421     if(typeof this.maxValue == "string") {
44422         this.maxValue = this.parseDate(this.maxValue);
44423     }
44424     this.ddMatch = null;
44425     if(this.disabledDates){
44426         var dd = this.disabledDates;
44427         var re = "(?:";
44428         for(var i = 0; i < dd.length; i++){
44429             re += dd[i];
44430             if(i != dd.length-1) {
44431                 re += "|";
44432             }
44433         }
44434         this.ddMatch = new RegExp(re + ")");
44435     }
44436 };
44437
44438 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
44439     /**
44440      * @cfg {String} format
44441      * The default date format string which can be overriden for localization support.  The format must be
44442      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
44443      */
44444     format : "m/d/y",
44445     /**
44446      * @cfg {String} altFormats
44447      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
44448      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
44449      */
44450     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
44451     /**
44452      * @cfg {Array} disabledDays
44453      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
44454      */
44455     disabledDays : null,
44456     /**
44457      * @cfg {String} disabledDaysText
44458      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
44459      */
44460     disabledDaysText : "Disabled",
44461     /**
44462      * @cfg {Array} disabledDates
44463      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
44464      * expression so they are very powerful. Some examples:
44465      * <ul>
44466      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
44467      * <li>["03/08", "09/16"] would disable those days for every year</li>
44468      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
44469      * <li>["03/../2006"] would disable every day in March 2006</li>
44470      * <li>["^03"] would disable every day in every March</li>
44471      * </ul>
44472      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
44473      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
44474      */
44475     disabledDates : null,
44476     /**
44477      * @cfg {String} disabledDatesText
44478      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
44479      */
44480     disabledDatesText : "Disabled",
44481     /**
44482      * @cfg {Date/String} minValue
44483      * The minimum allowed date. Can be either a Javascript date object or a string date in a
44484      * valid format (defaults to null).
44485      */
44486     minValue : null,
44487     /**
44488      * @cfg {Date/String} maxValue
44489      * The maximum allowed date. Can be either a Javascript date object or a string date in a
44490      * valid format (defaults to null).
44491      */
44492     maxValue : null,
44493     /**
44494      * @cfg {String} minText
44495      * The error text to display when the date in the cell is before minValue (defaults to
44496      * 'The date in this field must be after {minValue}').
44497      */
44498     minText : "The date in this field must be equal to or after {0}",
44499     /**
44500      * @cfg {String} maxText
44501      * The error text to display when the date in the cell is after maxValue (defaults to
44502      * 'The date in this field must be before {maxValue}').
44503      */
44504     maxText : "The date in this field must be equal to or before {0}",
44505     /**
44506      * @cfg {String} invalidText
44507      * The error text to display when the date in the field is invalid (defaults to
44508      * '{value} is not a valid date - it must be in the format {format}').
44509      */
44510     invalidText : "{0} is not a valid date - it must be in the format {1}",
44511     /**
44512      * @cfg {String} triggerClass
44513      * An additional CSS class used to style the trigger button.  The trigger will always get the
44514      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
44515      * which displays a calendar icon).
44516      */
44517     triggerClass : 'x-form-date-trigger',
44518     
44519
44520     /**
44521      * @cfg {Boolean} useIso
44522      * if enabled, then the date field will use a hidden field to store the 
44523      * real value as iso formated date. default (false)
44524      */ 
44525     useIso : false,
44526     /**
44527      * @cfg {String/Object} autoCreate
44528      * A DomHelper element spec, or true for a default element spec (defaults to
44529      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
44530      */ 
44531     // private
44532     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
44533     
44534     // private
44535     hiddenField: false,
44536     
44537     onRender : function(ct, position)
44538     {
44539         Roo.form.DateField.superclass.onRender.call(this, ct, position);
44540         if (this.useIso) {
44541             //this.el.dom.removeAttribute('name'); 
44542             Roo.log("Changing name?");
44543             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
44544             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
44545                     'before', true);
44546             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
44547             // prevent input submission
44548             this.hiddenName = this.name;
44549         }
44550             
44551             
44552     },
44553     
44554     // private
44555     validateValue : function(value)
44556     {
44557         value = this.formatDate(value);
44558         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
44559             Roo.log('super failed');
44560             return false;
44561         }
44562         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
44563              return true;
44564         }
44565         var svalue = value;
44566         value = this.parseDate(value);
44567         if(!value){
44568             Roo.log('parse date failed' + svalue);
44569             this.markInvalid(String.format(this.invalidText, svalue, this.format));
44570             return false;
44571         }
44572         var time = value.getTime();
44573         if(this.minValue && time < this.minValue.getTime()){
44574             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
44575             return false;
44576         }
44577         if(this.maxValue && time > this.maxValue.getTime()){
44578             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
44579             return false;
44580         }
44581         if(this.disabledDays){
44582             var day = value.getDay();
44583             for(var i = 0; i < this.disabledDays.length; i++) {
44584                 if(day === this.disabledDays[i]){
44585                     this.markInvalid(this.disabledDaysText);
44586                     return false;
44587                 }
44588             }
44589         }
44590         var fvalue = this.formatDate(value);
44591         if(this.ddMatch && this.ddMatch.test(fvalue)){
44592             this.markInvalid(String.format(this.disabledDatesText, fvalue));
44593             return false;
44594         }
44595         return true;
44596     },
44597
44598     // private
44599     // Provides logic to override the default TriggerField.validateBlur which just returns true
44600     validateBlur : function(){
44601         return !this.menu || !this.menu.isVisible();
44602     },
44603     
44604     getName: function()
44605     {
44606         // returns hidden if it's set..
44607         if (!this.rendered) {return ''};
44608         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
44609         
44610     },
44611
44612     /**
44613      * Returns the current date value of the date field.
44614      * @return {Date} The date value
44615      */
44616     getValue : function(){
44617         
44618         return  this.hiddenField ?
44619                 this.hiddenField.value :
44620                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
44621     },
44622
44623     /**
44624      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
44625      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
44626      * (the default format used is "m/d/y").
44627      * <br />Usage:
44628      * <pre><code>
44629 //All of these calls set the same date value (May 4, 2006)
44630
44631 //Pass a date object:
44632 var dt = new Date('5/4/06');
44633 dateField.setValue(dt);
44634
44635 //Pass a date string (default format):
44636 dateField.setValue('5/4/06');
44637
44638 //Pass a date string (custom format):
44639 dateField.format = 'Y-m-d';
44640 dateField.setValue('2006-5-4');
44641 </code></pre>
44642      * @param {String/Date} date The date or valid date string
44643      */
44644     setValue : function(date){
44645         if (this.hiddenField) {
44646             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
44647         }
44648         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
44649         // make sure the value field is always stored as a date..
44650         this.value = this.parseDate(date);
44651         
44652         
44653     },
44654
44655     // private
44656     parseDate : function(value){
44657         if(!value || value instanceof Date){
44658             return value;
44659         }
44660         var v = Date.parseDate(value, this.format);
44661          if (!v && this.useIso) {
44662             v = Date.parseDate(value, 'Y-m-d');
44663         }
44664         if(!v && this.altFormats){
44665             if(!this.altFormatsArray){
44666                 this.altFormatsArray = this.altFormats.split("|");
44667             }
44668             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
44669                 v = Date.parseDate(value, this.altFormatsArray[i]);
44670             }
44671         }
44672         return v;
44673     },
44674
44675     // private
44676     formatDate : function(date, fmt){
44677         return (!date || !(date instanceof Date)) ?
44678                date : date.dateFormat(fmt || this.format);
44679     },
44680
44681     // private
44682     menuListeners : {
44683         select: function(m, d){
44684             
44685             this.setValue(d);
44686             this.fireEvent('select', this, d);
44687         },
44688         show : function(){ // retain focus styling
44689             this.onFocus();
44690         },
44691         hide : function(){
44692             this.focus.defer(10, this);
44693             var ml = this.menuListeners;
44694             this.menu.un("select", ml.select,  this);
44695             this.menu.un("show", ml.show,  this);
44696             this.menu.un("hide", ml.hide,  this);
44697         }
44698     },
44699
44700     // private
44701     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
44702     onTriggerClick : function(){
44703         if(this.disabled){
44704             return;
44705         }
44706         if(this.menu == null){
44707             this.menu = new Roo.menu.DateMenu();
44708         }
44709         Roo.apply(this.menu.picker,  {
44710             showClear: this.allowBlank,
44711             minDate : this.minValue,
44712             maxDate : this.maxValue,
44713             disabledDatesRE : this.ddMatch,
44714             disabledDatesText : this.disabledDatesText,
44715             disabledDays : this.disabledDays,
44716             disabledDaysText : this.disabledDaysText,
44717             format : this.useIso ? 'Y-m-d' : this.format,
44718             minText : String.format(this.minText, this.formatDate(this.minValue)),
44719             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
44720         });
44721         this.menu.on(Roo.apply({}, this.menuListeners, {
44722             scope:this
44723         }));
44724         this.menu.picker.setValue(this.getValue() || new Date());
44725         this.menu.show(this.el, "tl-bl?");
44726     },
44727
44728     beforeBlur : function(){
44729         var v = this.parseDate(this.getRawValue());
44730         if(v){
44731             this.setValue(v);
44732         }
44733     },
44734
44735     /*@
44736      * overide
44737      * 
44738      */
44739     isDirty : function() {
44740         if(this.disabled) {
44741             return false;
44742         }
44743         
44744         if(typeof(this.startValue) === 'undefined'){
44745             return false;
44746         }
44747         
44748         return String(this.getValue()) !== String(this.startValue);
44749         
44750     }
44751 });/*
44752  * Based on:
44753  * Ext JS Library 1.1.1
44754  * Copyright(c) 2006-2007, Ext JS, LLC.
44755  *
44756  * Originally Released Under LGPL - original licence link has changed is not relivant.
44757  *
44758  * Fork - LGPL
44759  * <script type="text/javascript">
44760  */
44761  
44762 /**
44763  * @class Roo.form.MonthField
44764  * @extends Roo.form.TriggerField
44765  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
44766 * @constructor
44767 * Create a new MonthField
44768 * @param {Object} config
44769  */
44770 Roo.form.MonthField = function(config){
44771     
44772     Roo.form.MonthField.superclass.constructor.call(this, config);
44773     
44774       this.addEvents({
44775          
44776         /**
44777          * @event select
44778          * Fires when a date is selected
44779              * @param {Roo.form.MonthFieeld} combo This combo box
44780              * @param {Date} date The date selected
44781              */
44782         'select' : true
44783          
44784     });
44785     
44786     
44787     if(typeof this.minValue == "string") {
44788         this.minValue = this.parseDate(this.minValue);
44789     }
44790     if(typeof this.maxValue == "string") {
44791         this.maxValue = this.parseDate(this.maxValue);
44792     }
44793     this.ddMatch = null;
44794     if(this.disabledDates){
44795         var dd = this.disabledDates;
44796         var re = "(?:";
44797         for(var i = 0; i < dd.length; i++){
44798             re += dd[i];
44799             if(i != dd.length-1) {
44800                 re += "|";
44801             }
44802         }
44803         this.ddMatch = new RegExp(re + ")");
44804     }
44805 };
44806
44807 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
44808     /**
44809      * @cfg {String} format
44810      * The default date format string which can be overriden for localization support.  The format must be
44811      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
44812      */
44813     format : "M Y",
44814     /**
44815      * @cfg {String} altFormats
44816      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
44817      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
44818      */
44819     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
44820     /**
44821      * @cfg {Array} disabledDays
44822      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
44823      */
44824     disabledDays : [0,1,2,3,4,5,6],
44825     /**
44826      * @cfg {String} disabledDaysText
44827      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
44828      */
44829     disabledDaysText : "Disabled",
44830     /**
44831      * @cfg {Array} disabledDates
44832      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
44833      * expression so they are very powerful. Some examples:
44834      * <ul>
44835      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
44836      * <li>["03/08", "09/16"] would disable those days for every year</li>
44837      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
44838      * <li>["03/../2006"] would disable every day in March 2006</li>
44839      * <li>["^03"] would disable every day in every March</li>
44840      * </ul>
44841      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
44842      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
44843      */
44844     disabledDates : null,
44845     /**
44846      * @cfg {String} disabledDatesText
44847      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
44848      */
44849     disabledDatesText : "Disabled",
44850     /**
44851      * @cfg {Date/String} minValue
44852      * The minimum allowed date. Can be either a Javascript date object or a string date in a
44853      * valid format (defaults to null).
44854      */
44855     minValue : null,
44856     /**
44857      * @cfg {Date/String} maxValue
44858      * The maximum allowed date. Can be either a Javascript date object or a string date in a
44859      * valid format (defaults to null).
44860      */
44861     maxValue : null,
44862     /**
44863      * @cfg {String} minText
44864      * The error text to display when the date in the cell is before minValue (defaults to
44865      * 'The date in this field must be after {minValue}').
44866      */
44867     minText : "The date in this field must be equal to or after {0}",
44868     /**
44869      * @cfg {String} maxTextf
44870      * The error text to display when the date in the cell is after maxValue (defaults to
44871      * 'The date in this field must be before {maxValue}').
44872      */
44873     maxText : "The date in this field must be equal to or before {0}",
44874     /**
44875      * @cfg {String} invalidText
44876      * The error text to display when the date in the field is invalid (defaults to
44877      * '{value} is not a valid date - it must be in the format {format}').
44878      */
44879     invalidText : "{0} is not a valid date - it must be in the format {1}",
44880     /**
44881      * @cfg {String} triggerClass
44882      * An additional CSS class used to style the trigger button.  The trigger will always get the
44883      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
44884      * which displays a calendar icon).
44885      */
44886     triggerClass : 'x-form-date-trigger',
44887     
44888
44889     /**
44890      * @cfg {Boolean} useIso
44891      * if enabled, then the date field will use a hidden field to store the 
44892      * real value as iso formated date. default (true)
44893      */ 
44894     useIso : true,
44895     /**
44896      * @cfg {String/Object} autoCreate
44897      * A DomHelper element spec, or true for a default element spec (defaults to
44898      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
44899      */ 
44900     // private
44901     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
44902     
44903     // private
44904     hiddenField: false,
44905     
44906     hideMonthPicker : false,
44907     
44908     onRender : function(ct, position)
44909     {
44910         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
44911         if (this.useIso) {
44912             this.el.dom.removeAttribute('name'); 
44913             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
44914                     'before', true);
44915             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
44916             // prevent input submission
44917             this.hiddenName = this.name;
44918         }
44919             
44920             
44921     },
44922     
44923     // private
44924     validateValue : function(value)
44925     {
44926         value = this.formatDate(value);
44927         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
44928             return false;
44929         }
44930         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
44931              return true;
44932         }
44933         var svalue = value;
44934         value = this.parseDate(value);
44935         if(!value){
44936             this.markInvalid(String.format(this.invalidText, svalue, this.format));
44937             return false;
44938         }
44939         var time = value.getTime();
44940         if(this.minValue && time < this.minValue.getTime()){
44941             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
44942             return false;
44943         }
44944         if(this.maxValue && time > this.maxValue.getTime()){
44945             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
44946             return false;
44947         }
44948         /*if(this.disabledDays){
44949             var day = value.getDay();
44950             for(var i = 0; i < this.disabledDays.length; i++) {
44951                 if(day === this.disabledDays[i]){
44952                     this.markInvalid(this.disabledDaysText);
44953                     return false;
44954                 }
44955             }
44956         }
44957         */
44958         var fvalue = this.formatDate(value);
44959         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
44960             this.markInvalid(String.format(this.disabledDatesText, fvalue));
44961             return false;
44962         }
44963         */
44964         return true;
44965     },
44966
44967     // private
44968     // Provides logic to override the default TriggerField.validateBlur which just returns true
44969     validateBlur : function(){
44970         return !this.menu || !this.menu.isVisible();
44971     },
44972
44973     /**
44974      * Returns the current date value of the date field.
44975      * @return {Date} The date value
44976      */
44977     getValue : function(){
44978         
44979         
44980         
44981         return  this.hiddenField ?
44982                 this.hiddenField.value :
44983                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
44984     },
44985
44986     /**
44987      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
44988      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
44989      * (the default format used is "m/d/y").
44990      * <br />Usage:
44991      * <pre><code>
44992 //All of these calls set the same date value (May 4, 2006)
44993
44994 //Pass a date object:
44995 var dt = new Date('5/4/06');
44996 monthField.setValue(dt);
44997
44998 //Pass a date string (default format):
44999 monthField.setValue('5/4/06');
45000
45001 //Pass a date string (custom format):
45002 monthField.format = 'Y-m-d';
45003 monthField.setValue('2006-5-4');
45004 </code></pre>
45005      * @param {String/Date} date The date or valid date string
45006      */
45007     setValue : function(date){
45008         Roo.log('month setValue' + date);
45009         // can only be first of month..
45010         
45011         var val = this.parseDate(date);
45012         
45013         if (this.hiddenField) {
45014             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
45015         }
45016         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
45017         this.value = this.parseDate(date);
45018     },
45019
45020     // private
45021     parseDate : function(value){
45022         if(!value || value instanceof Date){
45023             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
45024             return value;
45025         }
45026         var v = Date.parseDate(value, this.format);
45027         if (!v && this.useIso) {
45028             v = Date.parseDate(value, 'Y-m-d');
45029         }
45030         if (v) {
45031             // 
45032             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
45033         }
45034         
45035         
45036         if(!v && this.altFormats){
45037             if(!this.altFormatsArray){
45038                 this.altFormatsArray = this.altFormats.split("|");
45039             }
45040             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
45041                 v = Date.parseDate(value, this.altFormatsArray[i]);
45042             }
45043         }
45044         return v;
45045     },
45046
45047     // private
45048     formatDate : function(date, fmt){
45049         return (!date || !(date instanceof Date)) ?
45050                date : date.dateFormat(fmt || this.format);
45051     },
45052
45053     // private
45054     menuListeners : {
45055         select: function(m, d){
45056             this.setValue(d);
45057             this.fireEvent('select', this, d);
45058         },
45059         show : function(){ // retain focus styling
45060             this.onFocus();
45061         },
45062         hide : function(){
45063             this.focus.defer(10, this);
45064             var ml = this.menuListeners;
45065             this.menu.un("select", ml.select,  this);
45066             this.menu.un("show", ml.show,  this);
45067             this.menu.un("hide", ml.hide,  this);
45068         }
45069     },
45070     // private
45071     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
45072     onTriggerClick : function(){
45073         if(this.disabled){
45074             return;
45075         }
45076         if(this.menu == null){
45077             this.menu = new Roo.menu.DateMenu();
45078            
45079         }
45080         
45081         Roo.apply(this.menu.picker,  {
45082             
45083             showClear: this.allowBlank,
45084             minDate : this.minValue,
45085             maxDate : this.maxValue,
45086             disabledDatesRE : this.ddMatch,
45087             disabledDatesText : this.disabledDatesText,
45088             
45089             format : this.useIso ? 'Y-m-d' : this.format,
45090             minText : String.format(this.minText, this.formatDate(this.minValue)),
45091             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
45092             
45093         });
45094          this.menu.on(Roo.apply({}, this.menuListeners, {
45095             scope:this
45096         }));
45097        
45098         
45099         var m = this.menu;
45100         var p = m.picker;
45101         
45102         // hide month picker get's called when we called by 'before hide';
45103         
45104         var ignorehide = true;
45105         p.hideMonthPicker  = function(disableAnim){
45106             if (ignorehide) {
45107                 return;
45108             }
45109              if(this.monthPicker){
45110                 Roo.log("hideMonthPicker called");
45111                 if(disableAnim === true){
45112                     this.monthPicker.hide();
45113                 }else{
45114                     this.monthPicker.slideOut('t', {duration:.2});
45115                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
45116                     p.fireEvent("select", this, this.value);
45117                     m.hide();
45118                 }
45119             }
45120         }
45121         
45122         Roo.log('picker set value');
45123         Roo.log(this.getValue());
45124         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
45125         m.show(this.el, 'tl-bl?');
45126         ignorehide  = false;
45127         // this will trigger hideMonthPicker..
45128         
45129         
45130         // hidden the day picker
45131         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
45132         
45133         
45134         
45135       
45136         
45137         p.showMonthPicker.defer(100, p);
45138     
45139         
45140        
45141     },
45142
45143     beforeBlur : function(){
45144         var v = this.parseDate(this.getRawValue());
45145         if(v){
45146             this.setValue(v);
45147         }
45148     }
45149
45150     /** @cfg {Boolean} grow @hide */
45151     /** @cfg {Number} growMin @hide */
45152     /** @cfg {Number} growMax @hide */
45153     /**
45154      * @hide
45155      * @method autoSize
45156      */
45157 });/*
45158  * Based on:
45159  * Ext JS Library 1.1.1
45160  * Copyright(c) 2006-2007, Ext JS, LLC.
45161  *
45162  * Originally Released Under LGPL - original licence link has changed is not relivant.
45163  *
45164  * Fork - LGPL
45165  * <script type="text/javascript">
45166  */
45167  
45168
45169 /**
45170  * @class Roo.form.ComboBox
45171  * @extends Roo.form.TriggerField
45172  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
45173  * @constructor
45174  * Create a new ComboBox.
45175  * @param {Object} config Configuration options
45176  */
45177 Roo.form.ComboBox = function(config){
45178     Roo.form.ComboBox.superclass.constructor.call(this, config);
45179     this.addEvents({
45180         /**
45181          * @event expand
45182          * Fires when the dropdown list is expanded
45183              * @param {Roo.form.ComboBox} combo This combo box
45184              */
45185         'expand' : true,
45186         /**
45187          * @event collapse
45188          * Fires when the dropdown list is collapsed
45189              * @param {Roo.form.ComboBox} combo This combo box
45190              */
45191         'collapse' : true,
45192         /**
45193          * @event beforeselect
45194          * Fires before a list item is selected. Return false to cancel the selection.
45195              * @param {Roo.form.ComboBox} combo This combo box
45196              * @param {Roo.data.Record} record The data record returned from the underlying store
45197              * @param {Number} index The index of the selected item in the dropdown list
45198              */
45199         'beforeselect' : true,
45200         /**
45201          * @event select
45202          * Fires when a list item is selected
45203              * @param {Roo.form.ComboBox} combo This combo box
45204              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
45205              * @param {Number} index The index of the selected item in the dropdown list
45206              */
45207         'select' : true,
45208         /**
45209          * @event beforequery
45210          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
45211          * The event object passed has these properties:
45212              * @param {Roo.form.ComboBox} combo This combo box
45213              * @param {String} query The query
45214              * @param {Boolean} forceAll true to force "all" query
45215              * @param {Boolean} cancel true to cancel the query
45216              * @param {Object} e The query event object
45217              */
45218         'beforequery': true,
45219          /**
45220          * @event add
45221          * Fires when the 'add' icon is pressed (add a listener to enable add button)
45222              * @param {Roo.form.ComboBox} combo This combo box
45223              */
45224         'add' : true,
45225         /**
45226          * @event edit
45227          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
45228              * @param {Roo.form.ComboBox} combo This combo box
45229              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
45230              */
45231         'edit' : true
45232         
45233         
45234     });
45235     if(this.transform){
45236         this.allowDomMove = false;
45237         var s = Roo.getDom(this.transform);
45238         if(!this.hiddenName){
45239             this.hiddenName = s.name;
45240         }
45241         if(!this.store){
45242             this.mode = 'local';
45243             var d = [], opts = s.options;
45244             for(var i = 0, len = opts.length;i < len; i++){
45245                 var o = opts[i];
45246                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
45247                 if(o.selected) {
45248                     this.value = value;
45249                 }
45250                 d.push([value, o.text]);
45251             }
45252             this.store = new Roo.data.SimpleStore({
45253                 'id': 0,
45254                 fields: ['value', 'text'],
45255                 data : d
45256             });
45257             this.valueField = 'value';
45258             this.displayField = 'text';
45259         }
45260         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
45261         if(!this.lazyRender){
45262             this.target = true;
45263             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
45264             s.parentNode.removeChild(s); // remove it
45265             this.render(this.el.parentNode);
45266         }else{
45267             s.parentNode.removeChild(s); // remove it
45268         }
45269
45270     }
45271     if (this.store) {
45272         this.store = Roo.factory(this.store, Roo.data);
45273     }
45274     
45275     this.selectedIndex = -1;
45276     if(this.mode == 'local'){
45277         if(config.queryDelay === undefined){
45278             this.queryDelay = 10;
45279         }
45280         if(config.minChars === undefined){
45281             this.minChars = 0;
45282         }
45283     }
45284 };
45285
45286 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
45287     /**
45288      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
45289      */
45290     /**
45291      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
45292      * rendering into an Roo.Editor, defaults to false)
45293      */
45294     /**
45295      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
45296      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
45297      */
45298     /**
45299      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
45300      */
45301     /**
45302      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
45303      * the dropdown list (defaults to undefined, with no header element)
45304      */
45305
45306      /**
45307      * @cfg {String/Roo.Template} tpl The template to use to render the output
45308      */
45309      
45310     // private
45311     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
45312     /**
45313      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
45314      */
45315     listWidth: undefined,
45316     /**
45317      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
45318      * mode = 'remote' or 'text' if mode = 'local')
45319      */
45320     displayField: undefined,
45321     /**
45322      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
45323      * mode = 'remote' or 'value' if mode = 'local'). 
45324      * Note: use of a valueField requires the user make a selection
45325      * in order for a value to be mapped.
45326      */
45327     valueField: undefined,
45328     
45329     
45330     /**
45331      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
45332      * field's data value (defaults to the underlying DOM element's name)
45333      */
45334     hiddenName: undefined,
45335     /**
45336      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
45337      */
45338     listClass: '',
45339     /**
45340      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
45341      */
45342     selectedClass: 'x-combo-selected',
45343     /**
45344      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
45345      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
45346      * which displays a downward arrow icon).
45347      */
45348     triggerClass : 'x-form-arrow-trigger',
45349     /**
45350      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
45351      */
45352     shadow:'sides',
45353     /**
45354      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
45355      * anchor positions (defaults to 'tl-bl')
45356      */
45357     listAlign: 'tl-bl?',
45358     /**
45359      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
45360      */
45361     maxHeight: 300,
45362     /**
45363      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
45364      * query specified by the allQuery config option (defaults to 'query')
45365      */
45366     triggerAction: 'query',
45367     /**
45368      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
45369      * (defaults to 4, does not apply if editable = false)
45370      */
45371     minChars : 4,
45372     /**
45373      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
45374      * delay (typeAheadDelay) if it matches a known value (defaults to false)
45375      */
45376     typeAhead: false,
45377     /**
45378      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
45379      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
45380      */
45381     queryDelay: 500,
45382     /**
45383      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
45384      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
45385      */
45386     pageSize: 0,
45387     /**
45388      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
45389      * when editable = true (defaults to false)
45390      */
45391     selectOnFocus:false,
45392     /**
45393      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
45394      */
45395     queryParam: 'query',
45396     /**
45397      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
45398      * when mode = 'remote' (defaults to 'Loading...')
45399      */
45400     loadingText: 'Loading...',
45401     /**
45402      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
45403      */
45404     resizable: false,
45405     /**
45406      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
45407      */
45408     handleHeight : 8,
45409     /**
45410      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
45411      * traditional select (defaults to true)
45412      */
45413     editable: true,
45414     /**
45415      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
45416      */
45417     allQuery: '',
45418     /**
45419      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
45420      */
45421     mode: 'remote',
45422     /**
45423      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
45424      * listWidth has a higher value)
45425      */
45426     minListWidth : 70,
45427     /**
45428      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
45429      * allow the user to set arbitrary text into the field (defaults to false)
45430      */
45431     forceSelection:false,
45432     /**
45433      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
45434      * if typeAhead = true (defaults to 250)
45435      */
45436     typeAheadDelay : 250,
45437     /**
45438      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
45439      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
45440      */
45441     valueNotFoundText : undefined,
45442     /**
45443      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
45444      */
45445     blockFocus : false,
45446     
45447     /**
45448      * @cfg {Boolean} disableClear Disable showing of clear button.
45449      */
45450     disableClear : false,
45451     /**
45452      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
45453      */
45454     alwaysQuery : false,
45455     
45456     //private
45457     addicon : false,
45458     editicon: false,
45459     
45460     // element that contains real text value.. (when hidden is used..)
45461      
45462     // private
45463     onRender : function(ct, position){
45464         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
45465         if(this.hiddenName){
45466             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
45467                     'before', true);
45468             this.hiddenField.value =
45469                 this.hiddenValue !== undefined ? this.hiddenValue :
45470                 this.value !== undefined ? this.value : '';
45471
45472             // prevent input submission
45473             this.el.dom.removeAttribute('name');
45474              
45475              
45476         }
45477         if(Roo.isGecko){
45478             this.el.dom.setAttribute('autocomplete', 'off');
45479         }
45480
45481         var cls = 'x-combo-list';
45482
45483         this.list = new Roo.Layer({
45484             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
45485         });
45486
45487         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
45488         this.list.setWidth(lw);
45489         this.list.swallowEvent('mousewheel');
45490         this.assetHeight = 0;
45491
45492         if(this.title){
45493             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
45494             this.assetHeight += this.header.getHeight();
45495         }
45496
45497         this.innerList = this.list.createChild({cls:cls+'-inner'});
45498         this.innerList.on('mouseover', this.onViewOver, this);
45499         this.innerList.on('mousemove', this.onViewMove, this);
45500         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
45501         
45502         if(this.allowBlank && !this.pageSize && !this.disableClear){
45503             this.footer = this.list.createChild({cls:cls+'-ft'});
45504             this.pageTb = new Roo.Toolbar(this.footer);
45505            
45506         }
45507         if(this.pageSize){
45508             this.footer = this.list.createChild({cls:cls+'-ft'});
45509             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
45510                     {pageSize: this.pageSize});
45511             
45512         }
45513         
45514         if (this.pageTb && this.allowBlank && !this.disableClear) {
45515             var _this = this;
45516             this.pageTb.add(new Roo.Toolbar.Fill(), {
45517                 cls: 'x-btn-icon x-btn-clear',
45518                 text: '&#160;',
45519                 handler: function()
45520                 {
45521                     _this.collapse();
45522                     _this.clearValue();
45523                     _this.onSelect(false, -1);
45524                 }
45525             });
45526         }
45527         if (this.footer) {
45528             this.assetHeight += this.footer.getHeight();
45529         }
45530         
45531
45532         if(!this.tpl){
45533             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
45534         }
45535
45536         this.view = new Roo.View(this.innerList, this.tpl, {
45537             singleSelect:true, store: this.store, selectedClass: this.selectedClass
45538         });
45539
45540         this.view.on('click', this.onViewClick, this);
45541
45542         this.store.on('beforeload', this.onBeforeLoad, this);
45543         this.store.on('load', this.onLoad, this);
45544         this.store.on('loadexception', this.onLoadException, this);
45545
45546         if(this.resizable){
45547             this.resizer = new Roo.Resizable(this.list,  {
45548                pinned:true, handles:'se'
45549             });
45550             this.resizer.on('resize', function(r, w, h){
45551                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
45552                 this.listWidth = w;
45553                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
45554                 this.restrictHeight();
45555             }, this);
45556             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
45557         }
45558         if(!this.editable){
45559             this.editable = true;
45560             this.setEditable(false);
45561         }  
45562         
45563         
45564         if (typeof(this.events.add.listeners) != 'undefined') {
45565             
45566             this.addicon = this.wrap.createChild(
45567                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
45568        
45569             this.addicon.on('click', function(e) {
45570                 this.fireEvent('add', this);
45571             }, this);
45572         }
45573         if (typeof(this.events.edit.listeners) != 'undefined') {
45574             
45575             this.editicon = this.wrap.createChild(
45576                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
45577             if (this.addicon) {
45578                 this.editicon.setStyle('margin-left', '40px');
45579             }
45580             this.editicon.on('click', function(e) {
45581                 
45582                 // we fire even  if inothing is selected..
45583                 this.fireEvent('edit', this, this.lastData );
45584                 
45585             }, this);
45586         }
45587         
45588         
45589         
45590     },
45591
45592     // private
45593     initEvents : function(){
45594         Roo.form.ComboBox.superclass.initEvents.call(this);
45595
45596         this.keyNav = new Roo.KeyNav(this.el, {
45597             "up" : function(e){
45598                 this.inKeyMode = true;
45599                 this.selectPrev();
45600             },
45601
45602             "down" : function(e){
45603                 if(!this.isExpanded()){
45604                     this.onTriggerClick();
45605                 }else{
45606                     this.inKeyMode = true;
45607                     this.selectNext();
45608                 }
45609             },
45610
45611             "enter" : function(e){
45612                 this.onViewClick();
45613                 //return true;
45614             },
45615
45616             "esc" : function(e){
45617                 this.collapse();
45618             },
45619
45620             "tab" : function(e){
45621                 this.onViewClick(false);
45622                 this.fireEvent("specialkey", this, e);
45623                 return true;
45624             },
45625
45626             scope : this,
45627
45628             doRelay : function(foo, bar, hname){
45629                 if(hname == 'down' || this.scope.isExpanded()){
45630                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45631                 }
45632                 return true;
45633             },
45634
45635             forceKeyDown: true
45636         });
45637         this.queryDelay = Math.max(this.queryDelay || 10,
45638                 this.mode == 'local' ? 10 : 250);
45639         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
45640         if(this.typeAhead){
45641             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
45642         }
45643         if(this.editable !== false){
45644             this.el.on("keyup", this.onKeyUp, this);
45645         }
45646         if(this.forceSelection){
45647             this.on('blur', this.doForce, this);
45648         }
45649     },
45650
45651     onDestroy : function(){
45652         if(this.view){
45653             this.view.setStore(null);
45654             this.view.el.removeAllListeners();
45655             this.view.el.remove();
45656             this.view.purgeListeners();
45657         }
45658         if(this.list){
45659             this.list.destroy();
45660         }
45661         if(this.store){
45662             this.store.un('beforeload', this.onBeforeLoad, this);
45663             this.store.un('load', this.onLoad, this);
45664             this.store.un('loadexception', this.onLoadException, this);
45665         }
45666         Roo.form.ComboBox.superclass.onDestroy.call(this);
45667     },
45668
45669     // private
45670     fireKey : function(e){
45671         if(e.isNavKeyPress() && !this.list.isVisible()){
45672             this.fireEvent("specialkey", this, e);
45673         }
45674     },
45675
45676     // private
45677     onResize: function(w, h){
45678         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
45679         
45680         if(typeof w != 'number'){
45681             // we do not handle it!?!?
45682             return;
45683         }
45684         var tw = this.trigger.getWidth();
45685         tw += this.addicon ? this.addicon.getWidth() : 0;
45686         tw += this.editicon ? this.editicon.getWidth() : 0;
45687         var x = w - tw;
45688         this.el.setWidth( this.adjustWidth('input', x));
45689             
45690         this.trigger.setStyle('left', x+'px');
45691         
45692         if(this.list && this.listWidth === undefined){
45693             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
45694             this.list.setWidth(lw);
45695             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
45696         }
45697         
45698     
45699         
45700     },
45701
45702     /**
45703      * Allow or prevent the user from directly editing the field text.  If false is passed,
45704      * the user will only be able to select from the items defined in the dropdown list.  This method
45705      * is the runtime equivalent of setting the 'editable' config option at config time.
45706      * @param {Boolean} value True to allow the user to directly edit the field text
45707      */
45708     setEditable : function(value){
45709         if(value == this.editable){
45710             return;
45711         }
45712         this.editable = value;
45713         if(!value){
45714             this.el.dom.setAttribute('readOnly', true);
45715             this.el.on('mousedown', this.onTriggerClick,  this);
45716             this.el.addClass('x-combo-noedit');
45717         }else{
45718             this.el.dom.setAttribute('readOnly', false);
45719             this.el.un('mousedown', this.onTriggerClick,  this);
45720             this.el.removeClass('x-combo-noedit');
45721         }
45722     },
45723
45724     // private
45725     onBeforeLoad : function(){
45726         if(!this.hasFocus){
45727             return;
45728         }
45729         this.innerList.update(this.loadingText ?
45730                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
45731         this.restrictHeight();
45732         this.selectedIndex = -1;
45733     },
45734
45735     // private
45736     onLoad : function(){
45737         if(!this.hasFocus){
45738             return;
45739         }
45740         if(this.store.getCount() > 0){
45741             this.expand();
45742             this.restrictHeight();
45743             if(this.lastQuery == this.allQuery){
45744                 if(this.editable){
45745                     this.el.dom.select();
45746                 }
45747                 if(!this.selectByValue(this.value, true)){
45748                     this.select(0, true);
45749                 }
45750             }else{
45751                 this.selectNext();
45752                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
45753                     this.taTask.delay(this.typeAheadDelay);
45754                 }
45755             }
45756         }else{
45757             this.onEmptyResults();
45758         }
45759         //this.el.focus();
45760     },
45761     // private
45762     onLoadException : function()
45763     {
45764         this.collapse();
45765         Roo.log(this.store.reader.jsonData);
45766         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
45767             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
45768         }
45769         
45770         
45771     },
45772     // private
45773     onTypeAhead : function(){
45774         if(this.store.getCount() > 0){
45775             var r = this.store.getAt(0);
45776             var newValue = r.data[this.displayField];
45777             var len = newValue.length;
45778             var selStart = this.getRawValue().length;
45779             if(selStart != len){
45780                 this.setRawValue(newValue);
45781                 this.selectText(selStart, newValue.length);
45782             }
45783         }
45784     },
45785
45786     // private
45787     onSelect : function(record, index){
45788         if(this.fireEvent('beforeselect', this, record, index) !== false){
45789             this.setFromData(index > -1 ? record.data : false);
45790             this.collapse();
45791             this.fireEvent('select', this, record, index);
45792         }
45793     },
45794
45795     /**
45796      * Returns the currently selected field value or empty string if no value is set.
45797      * @return {String} value The selected value
45798      */
45799     getValue : function(){
45800         if(this.valueField){
45801             return typeof this.value != 'undefined' ? this.value : '';
45802         }
45803         return Roo.form.ComboBox.superclass.getValue.call(this);
45804     },
45805
45806     /**
45807      * Clears any text/value currently set in the field
45808      */
45809     clearValue : function(){
45810         if(this.hiddenField){
45811             this.hiddenField.value = '';
45812         }
45813         this.value = '';
45814         this.setRawValue('');
45815         this.lastSelectionText = '';
45816         
45817     },
45818
45819     /**
45820      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
45821      * will be displayed in the field.  If the value does not match the data value of an existing item,
45822      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
45823      * Otherwise the field will be blank (although the value will still be set).
45824      * @param {String} value The value to match
45825      */
45826     setValue : function(v){
45827         var text = v;
45828         if(this.valueField){
45829             var r = this.findRecord(this.valueField, v);
45830             if(r){
45831                 text = r.data[this.displayField];
45832             }else if(this.valueNotFoundText !== undefined){
45833                 text = this.valueNotFoundText;
45834             }
45835         }
45836         this.lastSelectionText = text;
45837         if(this.hiddenField){
45838             this.hiddenField.value = v;
45839         }
45840         Roo.form.ComboBox.superclass.setValue.call(this, text);
45841         this.value = v;
45842     },
45843     /**
45844      * @property {Object} the last set data for the element
45845      */
45846     
45847     lastData : false,
45848     /**
45849      * Sets the value of the field based on a object which is related to the record format for the store.
45850      * @param {Object} value the value to set as. or false on reset?
45851      */
45852     setFromData : function(o){
45853         var dv = ''; // display value
45854         var vv = ''; // value value..
45855         this.lastData = o;
45856         if (this.displayField) {
45857             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
45858         } else {
45859             // this is an error condition!!!
45860             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
45861         }
45862         
45863         if(this.valueField){
45864             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
45865         }
45866         if(this.hiddenField){
45867             this.hiddenField.value = vv;
45868             
45869             this.lastSelectionText = dv;
45870             Roo.form.ComboBox.superclass.setValue.call(this, dv);
45871             this.value = vv;
45872             return;
45873         }
45874         // no hidden field.. - we store the value in 'value', but still display
45875         // display field!!!!
45876         this.lastSelectionText = dv;
45877         Roo.form.ComboBox.superclass.setValue.call(this, dv);
45878         this.value = vv;
45879         
45880         
45881     },
45882     // private
45883     reset : function(){
45884         // overridden so that last data is reset..
45885         this.setValue(this.resetValue);
45886         this.clearInvalid();
45887         this.lastData = false;
45888         if (this.view) {
45889             this.view.clearSelections();
45890         }
45891     },
45892     // private
45893     findRecord : function(prop, value){
45894         var record;
45895         if(this.store.getCount() > 0){
45896             this.store.each(function(r){
45897                 if(r.data[prop] == value){
45898                     record = r;
45899                     return false;
45900                 }
45901                 return true;
45902             });
45903         }
45904         return record;
45905     },
45906     
45907     getName: function()
45908     {
45909         // returns hidden if it's set..
45910         if (!this.rendered) {return ''};
45911         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
45912         
45913     },
45914     // private
45915     onViewMove : function(e, t){
45916         this.inKeyMode = false;
45917     },
45918
45919     // private
45920     onViewOver : function(e, t){
45921         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
45922             return;
45923         }
45924         var item = this.view.findItemFromChild(t);
45925         if(item){
45926             var index = this.view.indexOf(item);
45927             this.select(index, false);
45928         }
45929     },
45930
45931     // private
45932     onViewClick : function(doFocus)
45933     {
45934         var index = this.view.getSelectedIndexes()[0];
45935         var r = this.store.getAt(index);
45936         if(r){
45937             this.onSelect(r, index);
45938         }
45939         if(doFocus !== false && !this.blockFocus){
45940             this.el.focus();
45941         }
45942     },
45943
45944     // private
45945     restrictHeight : function(){
45946         this.innerList.dom.style.height = '';
45947         var inner = this.innerList.dom;
45948         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
45949         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
45950         this.list.beginUpdate();
45951         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
45952         this.list.alignTo(this.el, this.listAlign);
45953         this.list.endUpdate();
45954     },
45955
45956     // private
45957     onEmptyResults : function(){
45958         this.collapse();
45959     },
45960
45961     /**
45962      * Returns true if the dropdown list is expanded, else false.
45963      */
45964     isExpanded : function(){
45965         return this.list.isVisible();
45966     },
45967
45968     /**
45969      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
45970      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
45971      * @param {String} value The data value of the item to select
45972      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
45973      * selected item if it is not currently in view (defaults to true)
45974      * @return {Boolean} True if the value matched an item in the list, else false
45975      */
45976     selectByValue : function(v, scrollIntoView){
45977         if(v !== undefined && v !== null){
45978             var r = this.findRecord(this.valueField || this.displayField, v);
45979             if(r){
45980                 this.select(this.store.indexOf(r), scrollIntoView);
45981                 return true;
45982             }
45983         }
45984         return false;
45985     },
45986
45987     /**
45988      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
45989      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
45990      * @param {Number} index The zero-based index of the list item to select
45991      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
45992      * selected item if it is not currently in view (defaults to true)
45993      */
45994     select : function(index, scrollIntoView){
45995         this.selectedIndex = index;
45996         this.view.select(index);
45997         if(scrollIntoView !== false){
45998             var el = this.view.getNode(index);
45999             if(el){
46000                 this.innerList.scrollChildIntoView(el, false);
46001             }
46002         }
46003     },
46004
46005     // private
46006     selectNext : function(){
46007         var ct = this.store.getCount();
46008         if(ct > 0){
46009             if(this.selectedIndex == -1){
46010                 this.select(0);
46011             }else if(this.selectedIndex < ct-1){
46012                 this.select(this.selectedIndex+1);
46013             }
46014         }
46015     },
46016
46017     // private
46018     selectPrev : function(){
46019         var ct = this.store.getCount();
46020         if(ct > 0){
46021             if(this.selectedIndex == -1){
46022                 this.select(0);
46023             }else if(this.selectedIndex != 0){
46024                 this.select(this.selectedIndex-1);
46025             }
46026         }
46027     },
46028
46029     // private
46030     onKeyUp : function(e){
46031         if(this.editable !== false && !e.isSpecialKey()){
46032             this.lastKey = e.getKey();
46033             this.dqTask.delay(this.queryDelay);
46034         }
46035     },
46036
46037     // private
46038     validateBlur : function(){
46039         return !this.list || !this.list.isVisible();   
46040     },
46041
46042     // private
46043     initQuery : function(){
46044         this.doQuery(this.getRawValue());
46045     },
46046
46047     // private
46048     doForce : function(){
46049         if(this.el.dom.value.length > 0){
46050             this.el.dom.value =
46051                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
46052              
46053         }
46054     },
46055
46056     /**
46057      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
46058      * query allowing the query action to be canceled if needed.
46059      * @param {String} query The SQL query to execute
46060      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
46061      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
46062      * saved in the current store (defaults to false)
46063      */
46064     doQuery : function(q, forceAll){
46065         if(q === undefined || q === null){
46066             q = '';
46067         }
46068         var qe = {
46069             query: q,
46070             forceAll: forceAll,
46071             combo: this,
46072             cancel:false
46073         };
46074         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
46075             return false;
46076         }
46077         q = qe.query;
46078         forceAll = qe.forceAll;
46079         if(forceAll === true || (q.length >= this.minChars)){
46080             if(this.lastQuery != q || this.alwaysQuery){
46081                 this.lastQuery = q;
46082                 if(this.mode == 'local'){
46083                     this.selectedIndex = -1;
46084                     if(forceAll){
46085                         this.store.clearFilter();
46086                     }else{
46087                         this.store.filter(this.displayField, q);
46088                     }
46089                     this.onLoad();
46090                 }else{
46091                     this.store.baseParams[this.queryParam] = q;
46092                     this.store.load({
46093                         params: this.getParams(q)
46094                     });
46095                     this.expand();
46096                 }
46097             }else{
46098                 this.selectedIndex = -1;
46099                 this.onLoad();   
46100             }
46101         }
46102     },
46103
46104     // private
46105     getParams : function(q){
46106         var p = {};
46107         //p[this.queryParam] = q;
46108         if(this.pageSize){
46109             p.start = 0;
46110             p.limit = this.pageSize;
46111         }
46112         return p;
46113     },
46114
46115     /**
46116      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
46117      */
46118     collapse : function(){
46119         if(!this.isExpanded()){
46120             return;
46121         }
46122         this.list.hide();
46123         Roo.get(document).un('mousedown', this.collapseIf, this);
46124         Roo.get(document).un('mousewheel', this.collapseIf, this);
46125         if (!this.editable) {
46126             Roo.get(document).un('keydown', this.listKeyPress, this);
46127         }
46128         this.fireEvent('collapse', this);
46129     },
46130
46131     // private
46132     collapseIf : function(e){
46133         if(!e.within(this.wrap) && !e.within(this.list)){
46134             this.collapse();
46135         }
46136     },
46137
46138     /**
46139      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
46140      */
46141     expand : function(){
46142         if(this.isExpanded() || !this.hasFocus){
46143             return;
46144         }
46145         this.list.alignTo(this.el, this.listAlign);
46146         this.list.show();
46147         Roo.get(document).on('mousedown', this.collapseIf, this);
46148         Roo.get(document).on('mousewheel', this.collapseIf, this);
46149         if (!this.editable) {
46150             Roo.get(document).on('keydown', this.listKeyPress, this);
46151         }
46152         
46153         this.fireEvent('expand', this);
46154     },
46155
46156     // private
46157     // Implements the default empty TriggerField.onTriggerClick function
46158     onTriggerClick : function(){
46159         if(this.disabled){
46160             return;
46161         }
46162         if(this.isExpanded()){
46163             this.collapse();
46164             if (!this.blockFocus) {
46165                 this.el.focus();
46166             }
46167             
46168         }else {
46169             this.hasFocus = true;
46170             if(this.triggerAction == 'all') {
46171                 this.doQuery(this.allQuery, true);
46172             } else {
46173                 this.doQuery(this.getRawValue());
46174             }
46175             if (!this.blockFocus) {
46176                 this.el.focus();
46177             }
46178         }
46179     },
46180     listKeyPress : function(e)
46181     {
46182         //Roo.log('listkeypress');
46183         // scroll to first matching element based on key pres..
46184         if (e.isSpecialKey()) {
46185             return false;
46186         }
46187         var k = String.fromCharCode(e.getKey()).toUpperCase();
46188         //Roo.log(k);
46189         var match  = false;
46190         var csel = this.view.getSelectedNodes();
46191         var cselitem = false;
46192         if (csel.length) {
46193             var ix = this.view.indexOf(csel[0]);
46194             cselitem  = this.store.getAt(ix);
46195             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
46196                 cselitem = false;
46197             }
46198             
46199         }
46200         
46201         this.store.each(function(v) { 
46202             if (cselitem) {
46203                 // start at existing selection.
46204                 if (cselitem.id == v.id) {
46205                     cselitem = false;
46206                 }
46207                 return;
46208             }
46209                 
46210             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
46211                 match = this.store.indexOf(v);
46212                 return false;
46213             }
46214         }, this);
46215         
46216         if (match === false) {
46217             return true; // no more action?
46218         }
46219         // scroll to?
46220         this.view.select(match);
46221         var sn = Roo.get(this.view.getSelectedNodes()[0]);
46222         sn.scrollIntoView(sn.dom.parentNode, false);
46223     }
46224
46225     /** 
46226     * @cfg {Boolean} grow 
46227     * @hide 
46228     */
46229     /** 
46230     * @cfg {Number} growMin 
46231     * @hide 
46232     */
46233     /** 
46234     * @cfg {Number} growMax 
46235     * @hide 
46236     */
46237     /**
46238      * @hide
46239      * @method autoSize
46240      */
46241 });/*
46242  * Copyright(c) 2010-2012, Roo J Solutions Limited
46243  *
46244  * Licence LGPL
46245  *
46246  */
46247
46248 /**
46249  * @class Roo.form.ComboBoxArray
46250  * @extends Roo.form.TextField
46251  * A facebook style adder... for lists of email / people / countries  etc...
46252  * pick multiple items from a combo box, and shows each one.
46253  *
46254  *  Fred [x]  Brian [x]  [Pick another |v]
46255  *
46256  *
46257  *  For this to work: it needs various extra information
46258  *    - normal combo problay has
46259  *      name, hiddenName
46260  *    + displayField, valueField
46261  *
46262  *    For our purpose...
46263  *
46264  *
46265  *   If we change from 'extends' to wrapping...
46266  *   
46267  *  
46268  *
46269  
46270  
46271  * @constructor
46272  * Create a new ComboBoxArray.
46273  * @param {Object} config Configuration options
46274  */
46275  
46276
46277 Roo.form.ComboBoxArray = function(config)
46278 {
46279     this.addEvents({
46280         /**
46281          * @event beforeremove
46282          * Fires before remove the value from the list
46283              * @param {Roo.form.ComboBoxArray} _self This combo box array
46284              * @param {Roo.form.ComboBoxArray.Item} item removed item
46285              */
46286         'beforeremove' : true,
46287         /**
46288          * @event remove
46289          * Fires when remove the value from the list
46290              * @param {Roo.form.ComboBoxArray} _self This combo box array
46291              * @param {Roo.form.ComboBoxArray.Item} item removed item
46292              */
46293         'remove' : true
46294         
46295         
46296     });
46297     
46298     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
46299     
46300     this.items = new Roo.util.MixedCollection(false);
46301     
46302     // construct the child combo...
46303     
46304     
46305     
46306     
46307    
46308     
46309 }
46310
46311  
46312 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
46313
46314     /**
46315      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
46316      */
46317     
46318     lastData : false,
46319     
46320     // behavies liek a hiddne field
46321     inputType:      'hidden',
46322     /**
46323      * @cfg {Number} width The width of the box that displays the selected element
46324      */ 
46325     width:          300,
46326
46327     
46328     
46329     /**
46330      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
46331      */
46332     name : false,
46333     /**
46334      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
46335      */
46336     hiddenName : false,
46337     
46338     
46339     // private the array of items that are displayed..
46340     items  : false,
46341     // private - the hidden field el.
46342     hiddenEl : false,
46343     // private - the filed el..
46344     el : false,
46345     
46346     //validateValue : function() { return true; }, // all values are ok!
46347     //onAddClick: function() { },
46348     
46349     onRender : function(ct, position) 
46350     {
46351         
46352         // create the standard hidden element
46353         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
46354         
46355         
46356         // give fake names to child combo;
46357         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
46358         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
46359         
46360         this.combo = Roo.factory(this.combo, Roo.form);
46361         this.combo.onRender(ct, position);
46362         if (typeof(this.combo.width) != 'undefined') {
46363             this.combo.onResize(this.combo.width,0);
46364         }
46365         
46366         this.combo.initEvents();
46367         
46368         // assigned so form know we need to do this..
46369         this.store          = this.combo.store;
46370         this.valueField     = this.combo.valueField;
46371         this.displayField   = this.combo.displayField ;
46372         
46373         
46374         this.combo.wrap.addClass('x-cbarray-grp');
46375         
46376         var cbwrap = this.combo.wrap.createChild(
46377             {tag: 'div', cls: 'x-cbarray-cb'},
46378             this.combo.el.dom
46379         );
46380         
46381              
46382         this.hiddenEl = this.combo.wrap.createChild({
46383             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
46384         });
46385         this.el = this.combo.wrap.createChild({
46386             tag: 'input',  type:'hidden' , name: this.name, value : ''
46387         });
46388          //   this.el.dom.removeAttribute("name");
46389         
46390         
46391         this.outerWrap = this.combo.wrap;
46392         this.wrap = cbwrap;
46393         
46394         this.outerWrap.setWidth(this.width);
46395         this.outerWrap.dom.removeChild(this.el.dom);
46396         
46397         this.wrap.dom.appendChild(this.el.dom);
46398         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
46399         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
46400         
46401         this.combo.trigger.setStyle('position','relative');
46402         this.combo.trigger.setStyle('left', '0px');
46403         this.combo.trigger.setStyle('top', '2px');
46404         
46405         this.combo.el.setStyle('vertical-align', 'text-bottom');
46406         
46407         //this.trigger.setStyle('vertical-align', 'top');
46408         
46409         // this should use the code from combo really... on('add' ....)
46410         if (this.adder) {
46411             
46412         
46413             this.adder = this.outerWrap.createChild(
46414                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
46415             var _t = this;
46416             this.adder.on('click', function(e) {
46417                 _t.fireEvent('adderclick', this, e);
46418             }, _t);
46419         }
46420         //var _t = this;
46421         //this.adder.on('click', this.onAddClick, _t);
46422         
46423         
46424         this.combo.on('select', function(cb, rec, ix) {
46425             this.addItem(rec.data);
46426             
46427             cb.setValue('');
46428             cb.el.dom.value = '';
46429             //cb.lastData = rec.data;
46430             // add to list
46431             
46432         }, this);
46433         
46434         
46435     },
46436     
46437     
46438     getName: function()
46439     {
46440         // returns hidden if it's set..
46441         if (!this.rendered) {return ''};
46442         return  this.hiddenName ? this.hiddenName : this.name;
46443         
46444     },
46445     
46446     
46447     onResize: function(w, h){
46448         
46449         return;
46450         // not sure if this is needed..
46451         //this.combo.onResize(w,h);
46452         
46453         if(typeof w != 'number'){
46454             // we do not handle it!?!?
46455             return;
46456         }
46457         var tw = this.combo.trigger.getWidth();
46458         tw += this.addicon ? this.addicon.getWidth() : 0;
46459         tw += this.editicon ? this.editicon.getWidth() : 0;
46460         var x = w - tw;
46461         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
46462             
46463         this.combo.trigger.setStyle('left', '0px');
46464         
46465         if(this.list && this.listWidth === undefined){
46466             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
46467             this.list.setWidth(lw);
46468             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
46469         }
46470         
46471     
46472         
46473     },
46474     
46475     addItem: function(rec)
46476     {
46477         var valueField = this.combo.valueField;
46478         var displayField = this.combo.displayField;
46479         if (this.items.indexOfKey(rec[valueField]) > -1) {
46480             //console.log("GOT " + rec.data.id);
46481             return;
46482         }
46483         
46484         var x = new Roo.form.ComboBoxArray.Item({
46485             //id : rec[this.idField],
46486             data : rec,
46487             displayField : displayField ,
46488             tipField : displayField ,
46489             cb : this
46490         });
46491         // use the 
46492         this.items.add(rec[valueField],x);
46493         // add it before the element..
46494         this.updateHiddenEl();
46495         x.render(this.outerWrap, this.wrap.dom);
46496         // add the image handler..
46497     },
46498     
46499     updateHiddenEl : function()
46500     {
46501         this.validate();
46502         if (!this.hiddenEl) {
46503             return;
46504         }
46505         var ar = [];
46506         var idField = this.combo.valueField;
46507         
46508         this.items.each(function(f) {
46509             ar.push(f.data[idField]);
46510            
46511         });
46512         this.hiddenEl.dom.value = ar.join(',');
46513         this.validate();
46514     },
46515     
46516     reset : function()
46517     {
46518         this.items.clear();
46519         
46520         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
46521            el.remove();
46522         });
46523         
46524         this.el.dom.value = '';
46525         if (this.hiddenEl) {
46526             this.hiddenEl.dom.value = '';
46527         }
46528         
46529     },
46530     getValue: function()
46531     {
46532         return this.hiddenEl ? this.hiddenEl.dom.value : '';
46533     },
46534     setValue: function(v) // not a valid action - must use addItems..
46535     {
46536          
46537         this.reset();
46538         
46539         
46540         
46541         if (this.store.isLocal && (typeof(v) == 'string')) {
46542             // then we can use the store to find the values..
46543             // comma seperated at present.. this needs to allow JSON based encoding..
46544             this.hiddenEl.value  = v;
46545             var v_ar = [];
46546             Roo.each(v.split(','), function(k) {
46547                 Roo.log("CHECK " + this.valueField + ',' + k);
46548                 var li = this.store.query(this.valueField, k);
46549                 if (!li.length) {
46550                     return;
46551                 }
46552                 var add = {};
46553                 add[this.valueField] = k;
46554                 add[this.displayField] = li.item(0).data[this.displayField];
46555                 
46556                 this.addItem(add);
46557             }, this) 
46558              
46559         }
46560         if (typeof(v) == 'object' ) {
46561             // then let's assume it's an array of objects..
46562             Roo.each(v, function(l) {
46563                 this.addItem(l);
46564             }, this);
46565              
46566         }
46567         
46568         
46569     },
46570     setFromData: function(v)
46571     {
46572         // this recieves an object, if setValues is called.
46573         this.reset();
46574         this.el.dom.value = v[this.displayField];
46575         this.hiddenEl.dom.value = v[this.valueField];
46576         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
46577             return;
46578         }
46579         var kv = v[this.valueField];
46580         var dv = v[this.displayField];
46581         kv = typeof(kv) != 'string' ? '' : kv;
46582         dv = typeof(dv) != 'string' ? '' : dv;
46583         
46584         
46585         var keys = kv.split(',');
46586         var display = dv.split(',');
46587         for (var i = 0 ; i < keys.length; i++) {
46588             
46589             add = {};
46590             add[this.valueField] = keys[i];
46591             add[this.displayField] = display[i];
46592             this.addItem(add);
46593         }
46594       
46595         
46596     },
46597     
46598     /**
46599      * Validates the combox array value
46600      * @return {Boolean} True if the value is valid, else false
46601      */
46602     validate : function(){
46603         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
46604             this.clearInvalid();
46605             return true;
46606         }
46607         return false;
46608     },
46609     
46610     validateValue : function(value){
46611         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
46612         
46613     },
46614     
46615     /*@
46616      * overide
46617      * 
46618      */
46619     isDirty : function() {
46620         if(this.disabled) {
46621             return false;
46622         }
46623         
46624         try {
46625             var d = Roo.decode(String(this.originalValue));
46626         } catch (e) {
46627             return String(this.getValue()) !== String(this.originalValue);
46628         }
46629         
46630         var originalValue = [];
46631         
46632         for (var i = 0; i < d.length; i++){
46633             originalValue.push(d[i][this.valueField]);
46634         }
46635         
46636         return String(this.getValue()) !== String(originalValue.join(','));
46637         
46638     }
46639     
46640 });
46641
46642
46643
46644 /**
46645  * @class Roo.form.ComboBoxArray.Item
46646  * @extends Roo.BoxComponent
46647  * A selected item in the list
46648  *  Fred [x]  Brian [x]  [Pick another |v]
46649  * 
46650  * @constructor
46651  * Create a new item.
46652  * @param {Object} config Configuration options
46653  */
46654  
46655 Roo.form.ComboBoxArray.Item = function(config) {
46656     config.id = Roo.id();
46657     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
46658 }
46659
46660 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
46661     data : {},
46662     cb: false,
46663     displayField : false,
46664     tipField : false,
46665     
46666     
46667     defaultAutoCreate : {
46668         tag: 'div',
46669         cls: 'x-cbarray-item',
46670         cn : [ 
46671             { tag: 'div' },
46672             {
46673                 tag: 'img',
46674                 width:16,
46675                 height : 16,
46676                 src : Roo.BLANK_IMAGE_URL ,
46677                 align: 'center'
46678             }
46679         ]
46680         
46681     },
46682     
46683  
46684     onRender : function(ct, position)
46685     {
46686         Roo.form.Field.superclass.onRender.call(this, ct, position);
46687         
46688         if(!this.el){
46689             var cfg = this.getAutoCreate();
46690             this.el = ct.createChild(cfg, position);
46691         }
46692         
46693         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
46694         
46695         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
46696             this.cb.renderer(this.data) :
46697             String.format('{0}',this.data[this.displayField]);
46698         
46699             
46700         this.el.child('div').dom.setAttribute('qtip',
46701                         String.format('{0}',this.data[this.tipField])
46702         );
46703         
46704         this.el.child('img').on('click', this.remove, this);
46705         
46706     },
46707    
46708     remove : function()
46709     {
46710         if(this.cb.disabled){
46711             return;
46712         }
46713         
46714         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
46715             this.cb.items.remove(this);
46716             this.el.child('img').un('click', this.remove, this);
46717             this.el.remove();
46718             this.cb.updateHiddenEl();
46719
46720             this.cb.fireEvent('remove', this.cb, this);
46721         }
46722         
46723     }
46724 });/*
46725  * Based on:
46726  * Ext JS Library 1.1.1
46727  * Copyright(c) 2006-2007, Ext JS, LLC.
46728  *
46729  * Originally Released Under LGPL - original licence link has changed is not relivant.
46730  *
46731  * Fork - LGPL
46732  * <script type="text/javascript">
46733  */
46734 /**
46735  * @class Roo.form.Checkbox
46736  * @extends Roo.form.Field
46737  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
46738  * @constructor
46739  * Creates a new Checkbox
46740  * @param {Object} config Configuration options
46741  */
46742 Roo.form.Checkbox = function(config){
46743     Roo.form.Checkbox.superclass.constructor.call(this, config);
46744     this.addEvents({
46745         /**
46746          * @event check
46747          * Fires when the checkbox is checked or unchecked.
46748              * @param {Roo.form.Checkbox} this This checkbox
46749              * @param {Boolean} checked The new checked value
46750              */
46751         check : true
46752     });
46753 };
46754
46755 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
46756     /**
46757      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46758      */
46759     focusClass : undefined,
46760     /**
46761      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46762      */
46763     fieldClass: "x-form-field",
46764     /**
46765      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
46766      */
46767     checked: false,
46768     /**
46769      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46770      * {tag: "input", type: "checkbox", autocomplete: "off"})
46771      */
46772     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46773     /**
46774      * @cfg {String} boxLabel The text that appears beside the checkbox
46775      */
46776     boxLabel : "",
46777     /**
46778      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
46779      */  
46780     inputValue : '1',
46781     /**
46782      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
46783      */
46784      valueOff: '0', // value when not checked..
46785
46786     actionMode : 'viewEl', 
46787     //
46788     // private
46789     itemCls : 'x-menu-check-item x-form-item',
46790     groupClass : 'x-menu-group-item',
46791     inputType : 'hidden',
46792     
46793     
46794     inSetChecked: false, // check that we are not calling self...
46795     
46796     inputElement: false, // real input element?
46797     basedOn: false, // ????
46798     
46799     isFormField: true, // not sure where this is needed!!!!
46800
46801     onResize : function(){
46802         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46803         if(!this.boxLabel){
46804             this.el.alignTo(this.wrap, 'c-c');
46805         }
46806     },
46807
46808     initEvents : function(){
46809         Roo.form.Checkbox.superclass.initEvents.call(this);
46810         this.el.on("click", this.onClick,  this);
46811         this.el.on("change", this.onClick,  this);
46812     },
46813
46814
46815     getResizeEl : function(){
46816         return this.wrap;
46817     },
46818
46819     getPositionEl : function(){
46820         return this.wrap;
46821     },
46822
46823     // private
46824     onRender : function(ct, position){
46825         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46826         /*
46827         if(this.inputValue !== undefined){
46828             this.el.dom.value = this.inputValue;
46829         }
46830         */
46831         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
46832         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
46833         var viewEl = this.wrap.createChild({ 
46834             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
46835         this.viewEl = viewEl;   
46836         this.wrap.on('click', this.onClick,  this); 
46837         
46838         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46839         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46840         
46841         
46842         
46843         if(this.boxLabel){
46844             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
46845         //    viewEl.on('click', this.onClick,  this); 
46846         }
46847         //if(this.checked){
46848             this.setChecked(this.checked);
46849         //}else{
46850             //this.checked = this.el.dom;
46851         //}
46852
46853     },
46854
46855     // private
46856     initValue : Roo.emptyFn,
46857
46858     /**
46859      * Returns the checked state of the checkbox.
46860      * @return {Boolean} True if checked, else false
46861      */
46862     getValue : function(){
46863         if(this.el){
46864             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
46865         }
46866         return this.valueOff;
46867         
46868     },
46869
46870         // private
46871     onClick : function(){ 
46872         if (this.disabled) {
46873             return;
46874         }
46875         this.setChecked(!this.checked);
46876
46877         //if(this.el.dom.checked != this.checked){
46878         //    this.setValue(this.el.dom.checked);
46879        // }
46880     },
46881
46882     /**
46883      * Sets the checked state of the checkbox.
46884      * On is always based on a string comparison between inputValue and the param.
46885      * @param {Boolean/String} value - the value to set 
46886      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46887      */
46888     setValue : function(v,suppressEvent){
46889         
46890         
46891         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
46892         //if(this.el && this.el.dom){
46893         //    this.el.dom.checked = this.checked;
46894         //    this.el.dom.defaultChecked = this.checked;
46895         //}
46896         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
46897         //this.fireEvent("check", this, this.checked);
46898     },
46899     // private..
46900     setChecked : function(state,suppressEvent)
46901     {
46902         if (this.inSetChecked) {
46903             this.checked = state;
46904             return;
46905         }
46906         
46907     
46908         if(this.wrap){
46909             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
46910         }
46911         this.checked = state;
46912         if(suppressEvent !== true){
46913             this.fireEvent('check', this, state);
46914         }
46915         this.inSetChecked = true;
46916         this.el.dom.value = state ? this.inputValue : this.valueOff;
46917         this.inSetChecked = false;
46918         
46919     },
46920     // handle setting of hidden value by some other method!!?!?
46921     setFromHidden: function()
46922     {
46923         if(!this.el){
46924             return;
46925         }
46926         //console.log("SET FROM HIDDEN");
46927         //alert('setFrom hidden');
46928         this.setValue(this.el.dom.value);
46929     },
46930     
46931     onDestroy : function()
46932     {
46933         if(this.viewEl){
46934             Roo.get(this.viewEl).remove();
46935         }
46936          
46937         Roo.form.Checkbox.superclass.onDestroy.call(this);
46938     },
46939     
46940     setBoxLabel : function(str)
46941     {
46942         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
46943     }
46944
46945 });/*
46946  * Based on:
46947  * Ext JS Library 1.1.1
46948  * Copyright(c) 2006-2007, Ext JS, LLC.
46949  *
46950  * Originally Released Under LGPL - original licence link has changed is not relivant.
46951  *
46952  * Fork - LGPL
46953  * <script type="text/javascript">
46954  */
46955  
46956 /**
46957  * @class Roo.form.Radio
46958  * @extends Roo.form.Checkbox
46959  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
46960  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
46961  * @constructor
46962  * Creates a new Radio
46963  * @param {Object} config Configuration options
46964  */
46965 Roo.form.Radio = function(){
46966     Roo.form.Radio.superclass.constructor.apply(this, arguments);
46967 };
46968 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
46969     inputType: 'radio',
46970
46971     /**
46972      * If this radio is part of a group, it will return the selected value
46973      * @return {String}
46974      */
46975     getGroupValue : function(){
46976         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
46977     },
46978     
46979     
46980     onRender : function(ct, position){
46981         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46982         
46983         if(this.inputValue !== undefined){
46984             this.el.dom.value = this.inputValue;
46985         }
46986          
46987         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
46988         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
46989         //var viewEl = this.wrap.createChild({ 
46990         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
46991         //this.viewEl = viewEl;   
46992         //this.wrap.on('click', this.onClick,  this); 
46993         
46994         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46995         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
46996         
46997         
46998         
46999         if(this.boxLabel){
47000             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
47001         //    viewEl.on('click', this.onClick,  this); 
47002         }
47003          if(this.checked){
47004             this.el.dom.checked =   'checked' ;
47005         }
47006          
47007     } 
47008     
47009     
47010 });//<script type="text/javascript">
47011
47012 /*
47013  * Based  Ext JS Library 1.1.1
47014  * Copyright(c) 2006-2007, Ext JS, LLC.
47015  * LGPL
47016  *
47017  */
47018  
47019 /**
47020  * @class Roo.HtmlEditorCore
47021  * @extends Roo.Component
47022  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
47023  *
47024  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
47025  */
47026
47027 Roo.HtmlEditorCore = function(config){
47028     
47029     
47030     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
47031     
47032     
47033     this.addEvents({
47034         /**
47035          * @event initialize
47036          * Fires when the editor is fully initialized (including the iframe)
47037          * @param {Roo.HtmlEditorCore} this
47038          */
47039         initialize: true,
47040         /**
47041          * @event activate
47042          * Fires when the editor is first receives the focus. Any insertion must wait
47043          * until after this event.
47044          * @param {Roo.HtmlEditorCore} this
47045          */
47046         activate: true,
47047          /**
47048          * @event beforesync
47049          * Fires before the textarea is updated with content from the editor iframe. Return false
47050          * to cancel the sync.
47051          * @param {Roo.HtmlEditorCore} this
47052          * @param {String} html
47053          */
47054         beforesync: true,
47055          /**
47056          * @event beforepush
47057          * Fires before the iframe editor is updated with content from the textarea. Return false
47058          * to cancel the push.
47059          * @param {Roo.HtmlEditorCore} this
47060          * @param {String} html
47061          */
47062         beforepush: true,
47063          /**
47064          * @event sync
47065          * Fires when the textarea is updated with content from the editor iframe.
47066          * @param {Roo.HtmlEditorCore} this
47067          * @param {String} html
47068          */
47069         sync: true,
47070          /**
47071          * @event push
47072          * Fires when the iframe editor is updated with content from the textarea.
47073          * @param {Roo.HtmlEditorCore} this
47074          * @param {String} html
47075          */
47076         push: true,
47077         
47078         /**
47079          * @event editorevent
47080          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
47081          * @param {Roo.HtmlEditorCore} this
47082          */
47083         editorevent: true
47084         
47085     });
47086     
47087     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
47088     
47089     // defaults : white / black...
47090     this.applyBlacklists();
47091     
47092     
47093     
47094 };
47095
47096
47097 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
47098
47099
47100      /**
47101      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
47102      */
47103     
47104     owner : false,
47105     
47106      /**
47107      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
47108      *                        Roo.resizable.
47109      */
47110     resizable : false,
47111      /**
47112      * @cfg {Number} height (in pixels)
47113      */   
47114     height: 300,
47115    /**
47116      * @cfg {Number} width (in pixels)
47117      */   
47118     width: 500,
47119     
47120     /**
47121      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
47122      * 
47123      */
47124     stylesheets: false,
47125     
47126     // id of frame..
47127     frameId: false,
47128     
47129     // private properties
47130     validationEvent : false,
47131     deferHeight: true,
47132     initialized : false,
47133     activated : false,
47134     sourceEditMode : false,
47135     onFocus : Roo.emptyFn,
47136     iframePad:3,
47137     hideMode:'offsets',
47138     
47139     clearUp: true,
47140     
47141     // blacklist + whitelisted elements..
47142     black: false,
47143     white: false,
47144      
47145     
47146
47147     /**
47148      * Protected method that will not generally be called directly. It
47149      * is called when the editor initializes the iframe with HTML contents. Override this method if you
47150      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
47151      */
47152     getDocMarkup : function(){
47153         // body styles..
47154         var st = '';
47155         
47156         // inherit styels from page...?? 
47157         if (this.stylesheets === false) {
47158             
47159             Roo.get(document.head).select('style').each(function(node) {
47160                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
47161             });
47162             
47163             Roo.get(document.head).select('link').each(function(node) { 
47164                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
47165             });
47166             
47167         } else if (!this.stylesheets.length) {
47168                 // simple..
47169                 st = '<style type="text/css">' +
47170                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
47171                    '</style>';
47172         } else { 
47173             
47174         }
47175         
47176         st +=  '<style type="text/css">' +
47177             'IMG { cursor: pointer } ' +
47178         '</style>';
47179
47180         
47181         return '<html><head>' + st  +
47182             //<style type="text/css">' +
47183             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
47184             //'</style>' +
47185             ' </head><body class="roo-htmleditor-body"></body></html>';
47186     },
47187
47188     // private
47189     onRender : function(ct, position)
47190     {
47191         var _t = this;
47192         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
47193         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
47194         
47195         
47196         this.el.dom.style.border = '0 none';
47197         this.el.dom.setAttribute('tabIndex', -1);
47198         this.el.addClass('x-hidden hide');
47199         
47200         
47201         
47202         if(Roo.isIE){ // fix IE 1px bogus margin
47203             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
47204         }
47205        
47206         
47207         this.frameId = Roo.id();
47208         
47209          
47210         
47211         var iframe = this.owner.wrap.createChild({
47212             tag: 'iframe',
47213             cls: 'form-control', // bootstrap..
47214             id: this.frameId,
47215             name: this.frameId,
47216             frameBorder : 'no',
47217             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
47218         }, this.el
47219         );
47220         
47221         
47222         this.iframe = iframe.dom;
47223
47224          this.assignDocWin();
47225         
47226         this.doc.designMode = 'on';
47227        
47228         this.doc.open();
47229         this.doc.write(this.getDocMarkup());
47230         this.doc.close();
47231
47232         
47233         var task = { // must defer to wait for browser to be ready
47234             run : function(){
47235                 //console.log("run task?" + this.doc.readyState);
47236                 this.assignDocWin();
47237                 if(this.doc.body || this.doc.readyState == 'complete'){
47238                     try {
47239                         this.doc.designMode="on";
47240                     } catch (e) {
47241                         return;
47242                     }
47243                     Roo.TaskMgr.stop(task);
47244                     this.initEditor.defer(10, this);
47245                 }
47246             },
47247             interval : 10,
47248             duration: 10000,
47249             scope: this
47250         };
47251         Roo.TaskMgr.start(task);
47252
47253     },
47254
47255     // private
47256     onResize : function(w, h)
47257     {
47258          Roo.log('resize: ' +w + ',' + h );
47259         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
47260         if(!this.iframe){
47261             return;
47262         }
47263         if(typeof w == 'number'){
47264             
47265             this.iframe.style.width = w + 'px';
47266         }
47267         if(typeof h == 'number'){
47268             
47269             this.iframe.style.height = h + 'px';
47270             if(this.doc){
47271                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
47272             }
47273         }
47274         
47275     },
47276
47277     /**
47278      * Toggles the editor between standard and source edit mode.
47279      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
47280      */
47281     toggleSourceEdit : function(sourceEditMode){
47282         
47283         this.sourceEditMode = sourceEditMode === true;
47284         
47285         if(this.sourceEditMode){
47286  
47287             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
47288             
47289         }else{
47290             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
47291             //this.iframe.className = '';
47292             this.deferFocus();
47293         }
47294         //this.setSize(this.owner.wrap.getSize());
47295         //this.fireEvent('editmodechange', this, this.sourceEditMode);
47296     },
47297
47298     
47299   
47300
47301     /**
47302      * Protected method that will not generally be called directly. If you need/want
47303      * custom HTML cleanup, this is the method you should override.
47304      * @param {String} html The HTML to be cleaned
47305      * return {String} The cleaned HTML
47306      */
47307     cleanHtml : function(html){
47308         html = String(html);
47309         if(html.length > 5){
47310             if(Roo.isSafari){ // strip safari nonsense
47311                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
47312             }
47313         }
47314         if(html == '&nbsp;'){
47315             html = '';
47316         }
47317         return html;
47318     },
47319
47320     /**
47321      * HTML Editor -> Textarea
47322      * Protected method that will not generally be called directly. Syncs the contents
47323      * of the editor iframe with the textarea.
47324      */
47325     syncValue : function(){
47326         if(this.initialized){
47327             var bd = (this.doc.body || this.doc.documentElement);
47328             //this.cleanUpPaste(); -- this is done else where and causes havoc..
47329             var html = bd.innerHTML;
47330             if(Roo.isSafari){
47331                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
47332                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
47333                 if(m && m[1]){
47334                     html = '<div style="'+m[0]+'">' + html + '</div>';
47335                 }
47336             }
47337             html = this.cleanHtml(html);
47338             // fix up the special chars.. normaly like back quotes in word...
47339             // however we do not want to do this with chinese..
47340             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
47341                 var cc = b.charCodeAt();
47342                 if (
47343                     (cc >= 0x4E00 && cc < 0xA000 ) ||
47344                     (cc >= 0x3400 && cc < 0x4E00 ) ||
47345                     (cc >= 0xf900 && cc < 0xfb00 )
47346                 ) {
47347                         return b;
47348                 }
47349                 return "&#"+cc+";" 
47350             });
47351             if(this.owner.fireEvent('beforesync', this, html) !== false){
47352                 this.el.dom.value = html;
47353                 this.owner.fireEvent('sync', this, html);
47354             }
47355         }
47356     },
47357
47358     /**
47359      * Protected method that will not generally be called directly. Pushes the value of the textarea
47360      * into the iframe editor.
47361      */
47362     pushValue : function(){
47363         if(this.initialized){
47364             var v = this.el.dom.value.trim();
47365             
47366 //            if(v.length < 1){
47367 //                v = '&#160;';
47368 //            }
47369             
47370             if(this.owner.fireEvent('beforepush', this, v) !== false){
47371                 var d = (this.doc.body || this.doc.documentElement);
47372                 d.innerHTML = v;
47373                 this.cleanUpPaste();
47374                 this.el.dom.value = d.innerHTML;
47375                 this.owner.fireEvent('push', this, v);
47376             }
47377         }
47378     },
47379
47380     // private
47381     deferFocus : function(){
47382         this.focus.defer(10, this);
47383     },
47384
47385     // doc'ed in Field
47386     focus : function(){
47387         if(this.win && !this.sourceEditMode){
47388             this.win.focus();
47389         }else{
47390             this.el.focus();
47391         }
47392     },
47393     
47394     assignDocWin: function()
47395     {
47396         var iframe = this.iframe;
47397         
47398          if(Roo.isIE){
47399             this.doc = iframe.contentWindow.document;
47400             this.win = iframe.contentWindow;
47401         } else {
47402 //            if (!Roo.get(this.frameId)) {
47403 //                return;
47404 //            }
47405 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47406 //            this.win = Roo.get(this.frameId).dom.contentWindow;
47407             
47408             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
47409                 return;
47410             }
47411             
47412             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47413             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
47414         }
47415     },
47416     
47417     // private
47418     initEditor : function(){
47419         //console.log("INIT EDITOR");
47420         this.assignDocWin();
47421         
47422         
47423         
47424         this.doc.designMode="on";
47425         this.doc.open();
47426         this.doc.write(this.getDocMarkup());
47427         this.doc.close();
47428         
47429         var dbody = (this.doc.body || this.doc.documentElement);
47430         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
47431         // this copies styles from the containing element into thsi one..
47432         // not sure why we need all of this..
47433         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
47434         
47435         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
47436         //ss['background-attachment'] = 'fixed'; // w3c
47437         dbody.bgProperties = 'fixed'; // ie
47438         //Roo.DomHelper.applyStyles(dbody, ss);
47439         Roo.EventManager.on(this.doc, {
47440             //'mousedown': this.onEditorEvent,
47441             'mouseup': this.onEditorEvent,
47442             'dblclick': this.onEditorEvent,
47443             'click': this.onEditorEvent,
47444             'keyup': this.onEditorEvent,
47445             buffer:100,
47446             scope: this
47447         });
47448         if(Roo.isGecko){
47449             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
47450         }
47451         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
47452             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
47453         }
47454         this.initialized = true;
47455
47456         this.owner.fireEvent('initialize', this);
47457         this.pushValue();
47458     },
47459
47460     // private
47461     onDestroy : function(){
47462         
47463         
47464         
47465         if(this.rendered){
47466             
47467             //for (var i =0; i < this.toolbars.length;i++) {
47468             //    // fixme - ask toolbars for heights?
47469             //    this.toolbars[i].onDestroy();
47470            // }
47471             
47472             //this.wrap.dom.innerHTML = '';
47473             //this.wrap.remove();
47474         }
47475     },
47476
47477     // private
47478     onFirstFocus : function(){
47479         
47480         this.assignDocWin();
47481         
47482         
47483         this.activated = true;
47484          
47485     
47486         if(Roo.isGecko){ // prevent silly gecko errors
47487             this.win.focus();
47488             var s = this.win.getSelection();
47489             if(!s.focusNode || s.focusNode.nodeType != 3){
47490                 var r = s.getRangeAt(0);
47491                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
47492                 r.collapse(true);
47493                 this.deferFocus();
47494             }
47495             try{
47496                 this.execCmd('useCSS', true);
47497                 this.execCmd('styleWithCSS', false);
47498             }catch(e){}
47499         }
47500         this.owner.fireEvent('activate', this);
47501     },
47502
47503     // private
47504     adjustFont: function(btn){
47505         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
47506         //if(Roo.isSafari){ // safari
47507         //    adjust *= 2;
47508        // }
47509         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
47510         if(Roo.isSafari){ // safari
47511             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
47512             v =  (v < 10) ? 10 : v;
47513             v =  (v > 48) ? 48 : v;
47514             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
47515             
47516         }
47517         
47518         
47519         v = Math.max(1, v+adjust);
47520         
47521         this.execCmd('FontSize', v  );
47522     },
47523
47524     onEditorEvent : function(e)
47525     {
47526         this.owner.fireEvent('editorevent', this, e);
47527       //  this.updateToolbar();
47528         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
47529     },
47530
47531     insertTag : function(tg)
47532     {
47533         // could be a bit smarter... -> wrap the current selected tRoo..
47534         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
47535             
47536             range = this.createRange(this.getSelection());
47537             var wrappingNode = this.doc.createElement(tg.toLowerCase());
47538             wrappingNode.appendChild(range.extractContents());
47539             range.insertNode(wrappingNode);
47540
47541             return;
47542             
47543             
47544             
47545         }
47546         this.execCmd("formatblock",   tg);
47547         
47548     },
47549     
47550     insertText : function(txt)
47551     {
47552         
47553         
47554         var range = this.createRange();
47555         range.deleteContents();
47556                //alert(Sender.getAttribute('label'));
47557                
47558         range.insertNode(this.doc.createTextNode(txt));
47559     } ,
47560     
47561      
47562
47563     /**
47564      * Executes a Midas editor command on the editor document and performs necessary focus and
47565      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
47566      * @param {String} cmd The Midas command
47567      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47568      */
47569     relayCmd : function(cmd, value){
47570         this.win.focus();
47571         this.execCmd(cmd, value);
47572         this.owner.fireEvent('editorevent', this);
47573         //this.updateToolbar();
47574         this.owner.deferFocus();
47575     },
47576
47577     /**
47578      * Executes a Midas editor command directly on the editor document.
47579      * For visual commands, you should use {@link #relayCmd} instead.
47580      * <b>This should only be called after the editor is initialized.</b>
47581      * @param {String} cmd The Midas command
47582      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47583      */
47584     execCmd : function(cmd, value){
47585         this.doc.execCommand(cmd, false, value === undefined ? null : value);
47586         this.syncValue();
47587     },
47588  
47589  
47590    
47591     /**
47592      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
47593      * to insert tRoo.
47594      * @param {String} text | dom node.. 
47595      */
47596     insertAtCursor : function(text)
47597     {
47598         
47599         
47600         
47601         if(!this.activated){
47602             return;
47603         }
47604         /*
47605         if(Roo.isIE){
47606             this.win.focus();
47607             var r = this.doc.selection.createRange();
47608             if(r){
47609                 r.collapse(true);
47610                 r.pasteHTML(text);
47611                 this.syncValue();
47612                 this.deferFocus();
47613             
47614             }
47615             return;
47616         }
47617         */
47618         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
47619             this.win.focus();
47620             
47621             
47622             // from jquery ui (MIT licenced)
47623             var range, node;
47624             var win = this.win;
47625             
47626             if (win.getSelection && win.getSelection().getRangeAt) {
47627                 range = win.getSelection().getRangeAt(0);
47628                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
47629                 range.insertNode(node);
47630             } else if (win.document.selection && win.document.selection.createRange) {
47631                 // no firefox support
47632                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47633                 win.document.selection.createRange().pasteHTML(txt);
47634             } else {
47635                 // no firefox support
47636                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47637                 this.execCmd('InsertHTML', txt);
47638             } 
47639             
47640             this.syncValue();
47641             
47642             this.deferFocus();
47643         }
47644     },
47645  // private
47646     mozKeyPress : function(e){
47647         if(e.ctrlKey){
47648             var c = e.getCharCode(), cmd;
47649           
47650             if(c > 0){
47651                 c = String.fromCharCode(c).toLowerCase();
47652                 switch(c){
47653                     case 'b':
47654                         cmd = 'bold';
47655                         break;
47656                     case 'i':
47657                         cmd = 'italic';
47658                         break;
47659                     
47660                     case 'u':
47661                         cmd = 'underline';
47662                         break;
47663                     
47664                     case 'v':
47665                         this.cleanUpPaste.defer(100, this);
47666                         return;
47667                         
47668                 }
47669                 if(cmd){
47670                     this.win.focus();
47671                     this.execCmd(cmd);
47672                     this.deferFocus();
47673                     e.preventDefault();
47674                 }
47675                 
47676             }
47677         }
47678     },
47679
47680     // private
47681     fixKeys : function(){ // load time branching for fastest keydown performance
47682         if(Roo.isIE){
47683             return function(e){
47684                 var k = e.getKey(), r;
47685                 if(k == e.TAB){
47686                     e.stopEvent();
47687                     r = this.doc.selection.createRange();
47688                     if(r){
47689                         r.collapse(true);
47690                         r.pasteHTML('&#160;&#160;&#160;&#160;');
47691                         this.deferFocus();
47692                     }
47693                     return;
47694                 }
47695                 
47696                 if(k == e.ENTER){
47697                     r = this.doc.selection.createRange();
47698                     if(r){
47699                         var target = r.parentElement();
47700                         if(!target || target.tagName.toLowerCase() != 'li'){
47701                             e.stopEvent();
47702                             r.pasteHTML('<br />');
47703                             r.collapse(false);
47704                             r.select();
47705                         }
47706                     }
47707                 }
47708                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47709                     this.cleanUpPaste.defer(100, this);
47710                     return;
47711                 }
47712                 
47713                 
47714             };
47715         }else if(Roo.isOpera){
47716             return function(e){
47717                 var k = e.getKey();
47718                 if(k == e.TAB){
47719                     e.stopEvent();
47720                     this.win.focus();
47721                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
47722                     this.deferFocus();
47723                 }
47724                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47725                     this.cleanUpPaste.defer(100, this);
47726                     return;
47727                 }
47728                 
47729             };
47730         }else if(Roo.isSafari){
47731             return function(e){
47732                 var k = e.getKey();
47733                 
47734                 if(k == e.TAB){
47735                     e.stopEvent();
47736                     this.execCmd('InsertText','\t');
47737                     this.deferFocus();
47738                     return;
47739                 }
47740                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47741                     this.cleanUpPaste.defer(100, this);
47742                     return;
47743                 }
47744                 
47745              };
47746         }
47747     }(),
47748     
47749     getAllAncestors: function()
47750     {
47751         var p = this.getSelectedNode();
47752         var a = [];
47753         if (!p) {
47754             a.push(p); // push blank onto stack..
47755             p = this.getParentElement();
47756         }
47757         
47758         
47759         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
47760             a.push(p);
47761             p = p.parentNode;
47762         }
47763         a.push(this.doc.body);
47764         return a;
47765     },
47766     lastSel : false,
47767     lastSelNode : false,
47768     
47769     
47770     getSelection : function() 
47771     {
47772         this.assignDocWin();
47773         return Roo.isIE ? this.doc.selection : this.win.getSelection();
47774     },
47775     
47776     getSelectedNode: function() 
47777     {
47778         // this may only work on Gecko!!!
47779         
47780         // should we cache this!!!!
47781         
47782         
47783         
47784          
47785         var range = this.createRange(this.getSelection()).cloneRange();
47786         
47787         if (Roo.isIE) {
47788             var parent = range.parentElement();
47789             while (true) {
47790                 var testRange = range.duplicate();
47791                 testRange.moveToElementText(parent);
47792                 if (testRange.inRange(range)) {
47793                     break;
47794                 }
47795                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47796                     break;
47797                 }
47798                 parent = parent.parentElement;
47799             }
47800             return parent;
47801         }
47802         
47803         // is ancestor a text element.
47804         var ac =  range.commonAncestorContainer;
47805         if (ac.nodeType == 3) {
47806             ac = ac.parentNode;
47807         }
47808         
47809         var ar = ac.childNodes;
47810          
47811         var nodes = [];
47812         var other_nodes = [];
47813         var has_other_nodes = false;
47814         for (var i=0;i<ar.length;i++) {
47815             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
47816                 continue;
47817             }
47818             // fullly contained node.
47819             
47820             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47821                 nodes.push(ar[i]);
47822                 continue;
47823             }
47824             
47825             // probably selected..
47826             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47827                 other_nodes.push(ar[i]);
47828                 continue;
47829             }
47830             // outer..
47831             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
47832                 continue;
47833             }
47834             
47835             
47836             has_other_nodes = true;
47837         }
47838         if (!nodes.length && other_nodes.length) {
47839             nodes= other_nodes;
47840         }
47841         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47842             return false;
47843         }
47844         
47845         return nodes[0];
47846     },
47847     createRange: function(sel)
47848     {
47849         // this has strange effects when using with 
47850         // top toolbar - not sure if it's a great idea.
47851         //this.editor.contentWindow.focus();
47852         if (typeof sel != "undefined") {
47853             try {
47854                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47855             } catch(e) {
47856                 return this.doc.createRange();
47857             }
47858         } else {
47859             return this.doc.createRange();
47860         }
47861     },
47862     getParentElement: function()
47863     {
47864         
47865         this.assignDocWin();
47866         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47867         
47868         var range = this.createRange(sel);
47869          
47870         try {
47871             var p = range.commonAncestorContainer;
47872             while (p.nodeType == 3) { // text node
47873                 p = p.parentNode;
47874             }
47875             return p;
47876         } catch (e) {
47877             return null;
47878         }
47879     
47880     },
47881     /***
47882      *
47883      * Range intersection.. the hard stuff...
47884      *  '-1' = before
47885      *  '0' = hits..
47886      *  '1' = after.
47887      *         [ -- selected range --- ]
47888      *   [fail]                        [fail]
47889      *
47890      *    basically..
47891      *      if end is before start or  hits it. fail.
47892      *      if start is after end or hits it fail.
47893      *
47894      *   if either hits (but other is outside. - then it's not 
47895      *   
47896      *    
47897      **/
47898     
47899     
47900     // @see http://www.thismuchiknow.co.uk/?p=64.
47901     rangeIntersectsNode : function(range, node)
47902     {
47903         var nodeRange = node.ownerDocument.createRange();
47904         try {
47905             nodeRange.selectNode(node);
47906         } catch (e) {
47907             nodeRange.selectNodeContents(node);
47908         }
47909     
47910         var rangeStartRange = range.cloneRange();
47911         rangeStartRange.collapse(true);
47912     
47913         var rangeEndRange = range.cloneRange();
47914         rangeEndRange.collapse(false);
47915     
47916         var nodeStartRange = nodeRange.cloneRange();
47917         nodeStartRange.collapse(true);
47918     
47919         var nodeEndRange = nodeRange.cloneRange();
47920         nodeEndRange.collapse(false);
47921     
47922         return rangeStartRange.compareBoundaryPoints(
47923                  Range.START_TO_START, nodeEndRange) == -1 &&
47924                rangeEndRange.compareBoundaryPoints(
47925                  Range.START_TO_START, nodeStartRange) == 1;
47926         
47927          
47928     },
47929     rangeCompareNode : function(range, node)
47930     {
47931         var nodeRange = node.ownerDocument.createRange();
47932         try {
47933             nodeRange.selectNode(node);
47934         } catch (e) {
47935             nodeRange.selectNodeContents(node);
47936         }
47937         
47938         
47939         range.collapse(true);
47940     
47941         nodeRange.collapse(true);
47942      
47943         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47944         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
47945          
47946         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47947         
47948         var nodeIsBefore   =  ss == 1;
47949         var nodeIsAfter    = ee == -1;
47950         
47951         if (nodeIsBefore && nodeIsAfter) {
47952             return 0; // outer
47953         }
47954         if (!nodeIsBefore && nodeIsAfter) {
47955             return 1; //right trailed.
47956         }
47957         
47958         if (nodeIsBefore && !nodeIsAfter) {
47959             return 2;  // left trailed.
47960         }
47961         // fully contined.
47962         return 3;
47963     },
47964
47965     // private? - in a new class?
47966     cleanUpPaste :  function()
47967     {
47968         // cleans up the whole document..
47969         Roo.log('cleanuppaste');
47970         
47971         this.cleanUpChildren(this.doc.body);
47972         var clean = this.cleanWordChars(this.doc.body.innerHTML);
47973         if (clean != this.doc.body.innerHTML) {
47974             this.doc.body.innerHTML = clean;
47975         }
47976         
47977     },
47978     
47979     cleanWordChars : function(input) {// change the chars to hex code
47980         var he = Roo.HtmlEditorCore;
47981         
47982         var output = input;
47983         Roo.each(he.swapCodes, function(sw) { 
47984             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47985             
47986             output = output.replace(swapper, sw[1]);
47987         });
47988         
47989         return output;
47990     },
47991     
47992     
47993     cleanUpChildren : function (n)
47994     {
47995         if (!n.childNodes.length) {
47996             return;
47997         }
47998         for (var i = n.childNodes.length-1; i > -1 ; i--) {
47999            this.cleanUpChild(n.childNodes[i]);
48000         }
48001     },
48002     
48003     
48004         
48005     
48006     cleanUpChild : function (node)
48007     {
48008         var ed = this;
48009         //console.log(node);
48010         if (node.nodeName == "#text") {
48011             // clean up silly Windows -- stuff?
48012             return; 
48013         }
48014         if (node.nodeName == "#comment") {
48015             node.parentNode.removeChild(node);
48016             // clean up silly Windows -- stuff?
48017             return; 
48018         }
48019         var lcname = node.tagName.toLowerCase();
48020         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
48021         // whitelist of tags..
48022         
48023         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
48024             // remove node.
48025             node.parentNode.removeChild(node);
48026             return;
48027             
48028         }
48029         
48030         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
48031         
48032         // remove <a name=....> as rendering on yahoo mailer is borked with this.
48033         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
48034         
48035         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
48036         //    remove_keep_children = true;
48037         //}
48038         
48039         if (remove_keep_children) {
48040             this.cleanUpChildren(node);
48041             // inserts everything just before this node...
48042             while (node.childNodes.length) {
48043                 var cn = node.childNodes[0];
48044                 node.removeChild(cn);
48045                 node.parentNode.insertBefore(cn, node);
48046             }
48047             node.parentNode.removeChild(node);
48048             return;
48049         }
48050         
48051         if (!node.attributes || !node.attributes.length) {
48052             this.cleanUpChildren(node);
48053             return;
48054         }
48055         
48056         function cleanAttr(n,v)
48057         {
48058             
48059             if (v.match(/^\./) || v.match(/^\//)) {
48060                 return;
48061             }
48062             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
48063                 return;
48064             }
48065             if (v.match(/^#/)) {
48066                 return;
48067             }
48068 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
48069             node.removeAttribute(n);
48070             
48071         }
48072         
48073         var cwhite = this.cwhite;
48074         var cblack = this.cblack;
48075             
48076         function cleanStyle(n,v)
48077         {
48078             if (v.match(/expression/)) { //XSS?? should we even bother..
48079                 node.removeAttribute(n);
48080                 return;
48081             }
48082             
48083             var parts = v.split(/;/);
48084             var clean = [];
48085             
48086             Roo.each(parts, function(p) {
48087                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
48088                 if (!p.length) {
48089                     return true;
48090                 }
48091                 var l = p.split(':').shift().replace(/\s+/g,'');
48092                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
48093                 
48094                 if ( cwhite.length && cblack.indexOf(l) > -1) {
48095 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
48096                     //node.removeAttribute(n);
48097                     return true;
48098                 }
48099                 //Roo.log()
48100                 // only allow 'c whitelisted system attributes'
48101                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
48102 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
48103                     //node.removeAttribute(n);
48104                     return true;
48105                 }
48106                 
48107                 
48108                  
48109                 
48110                 clean.push(p);
48111                 return true;
48112             });
48113             if (clean.length) { 
48114                 node.setAttribute(n, clean.join(';'));
48115             } else {
48116                 node.removeAttribute(n);
48117             }
48118             
48119         }
48120         
48121         
48122         for (var i = node.attributes.length-1; i > -1 ; i--) {
48123             var a = node.attributes[i];
48124             //console.log(a);
48125             
48126             if (a.name.toLowerCase().substr(0,2)=='on')  {
48127                 node.removeAttribute(a.name);
48128                 continue;
48129             }
48130             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
48131                 node.removeAttribute(a.name);
48132                 continue;
48133             }
48134             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
48135                 cleanAttr(a.name,a.value); // fixme..
48136                 continue;
48137             }
48138             if (a.name == 'style') {
48139                 cleanStyle(a.name,a.value);
48140                 continue;
48141             }
48142             /// clean up MS crap..
48143             // tecnically this should be a list of valid class'es..
48144             
48145             
48146             if (a.name == 'class') {
48147                 if (a.value.match(/^Mso/)) {
48148                     node.className = '';
48149                 }
48150                 
48151                 if (a.value.match(/body/)) {
48152                     node.className = '';
48153                 }
48154                 continue;
48155             }
48156             
48157             // style cleanup!?
48158             // class cleanup?
48159             
48160         }
48161         
48162         
48163         this.cleanUpChildren(node);
48164         
48165         
48166     },
48167     
48168     /**
48169      * Clean up MS wordisms...
48170      */
48171     cleanWord : function(node)
48172     {
48173         
48174         
48175         if (!node) {
48176             this.cleanWord(this.doc.body);
48177             return;
48178         }
48179         if (node.nodeName == "#text") {
48180             // clean up silly Windows -- stuff?
48181             return; 
48182         }
48183         if (node.nodeName == "#comment") {
48184             node.parentNode.removeChild(node);
48185             // clean up silly Windows -- stuff?
48186             return; 
48187         }
48188         
48189         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
48190             node.parentNode.removeChild(node);
48191             return;
48192         }
48193         
48194         // remove - but keep children..
48195         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
48196             while (node.childNodes.length) {
48197                 var cn = node.childNodes[0];
48198                 node.removeChild(cn);
48199                 node.parentNode.insertBefore(cn, node);
48200             }
48201             node.parentNode.removeChild(node);
48202             this.iterateChildren(node, this.cleanWord);
48203             return;
48204         }
48205         // clean styles
48206         if (node.className.length) {
48207             
48208             var cn = node.className.split(/\W+/);
48209             var cna = [];
48210             Roo.each(cn, function(cls) {
48211                 if (cls.match(/Mso[a-zA-Z]+/)) {
48212                     return;
48213                 }
48214                 cna.push(cls);
48215             });
48216             node.className = cna.length ? cna.join(' ') : '';
48217             if (!cna.length) {
48218                 node.removeAttribute("class");
48219             }
48220         }
48221         
48222         if (node.hasAttribute("lang")) {
48223             node.removeAttribute("lang");
48224         }
48225         
48226         if (node.hasAttribute("style")) {
48227             
48228             var styles = node.getAttribute("style").split(";");
48229             var nstyle = [];
48230             Roo.each(styles, function(s) {
48231                 if (!s.match(/:/)) {
48232                     return;
48233                 }
48234                 var kv = s.split(":");
48235                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
48236                     return;
48237                 }
48238                 // what ever is left... we allow.
48239                 nstyle.push(s);
48240             });
48241             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
48242             if (!nstyle.length) {
48243                 node.removeAttribute('style');
48244             }
48245         }
48246         this.iterateChildren(node, this.cleanWord);
48247         
48248         
48249         
48250     },
48251     /**
48252      * iterateChildren of a Node, calling fn each time, using this as the scole..
48253      * @param {DomNode} node node to iterate children of.
48254      * @param {Function} fn method of this class to call on each item.
48255      */
48256     iterateChildren : function(node, fn)
48257     {
48258         if (!node.childNodes.length) {
48259                 return;
48260         }
48261         for (var i = node.childNodes.length-1; i > -1 ; i--) {
48262            fn.call(this, node.childNodes[i])
48263         }
48264     },
48265     
48266     
48267     /**
48268      * cleanTableWidths.
48269      *
48270      * Quite often pasting from word etc.. results in tables with column and widths.
48271      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
48272      *
48273      */
48274     cleanTableWidths : function(node)
48275     {
48276          
48277          
48278         if (!node) {
48279             this.cleanTableWidths(this.doc.body);
48280             return;
48281         }
48282         
48283         // ignore list...
48284         if (node.nodeName == "#text" || node.nodeName == "#comment") {
48285             return; 
48286         }
48287         Roo.log(node.tagName);
48288         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
48289             this.iterateChildren(node, this.cleanTableWidths);
48290             return;
48291         }
48292         if (node.hasAttribute('width')) {
48293             node.removeAttribute('width');
48294         }
48295         
48296          
48297         if (node.hasAttribute("style")) {
48298             // pretty basic...
48299             
48300             var styles = node.getAttribute("style").split(";");
48301             var nstyle = [];
48302             Roo.each(styles, function(s) {
48303                 if (!s.match(/:/)) {
48304                     return;
48305                 }
48306                 var kv = s.split(":");
48307                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
48308                     return;
48309                 }
48310                 // what ever is left... we allow.
48311                 nstyle.push(s);
48312             });
48313             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
48314             if (!nstyle.length) {
48315                 node.removeAttribute('style');
48316             }
48317         }
48318         
48319         this.iterateChildren(node, this.cleanTableWidths);
48320         
48321         
48322     },
48323     
48324     
48325     
48326     
48327     domToHTML : function(currentElement, depth, nopadtext) {
48328         
48329         depth = depth || 0;
48330         nopadtext = nopadtext || false;
48331     
48332         if (!currentElement) {
48333             return this.domToHTML(this.doc.body);
48334         }
48335         
48336         //Roo.log(currentElement);
48337         var j;
48338         var allText = false;
48339         var nodeName = currentElement.nodeName;
48340         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
48341         
48342         if  (nodeName == '#text') {
48343             
48344             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
48345         }
48346         
48347         
48348         var ret = '';
48349         if (nodeName != 'BODY') {
48350              
48351             var i = 0;
48352             // Prints the node tagName, such as <A>, <IMG>, etc
48353             if (tagName) {
48354                 var attr = [];
48355                 for(i = 0; i < currentElement.attributes.length;i++) {
48356                     // quoting?
48357                     var aname = currentElement.attributes.item(i).name;
48358                     if (!currentElement.attributes.item(i).value.length) {
48359                         continue;
48360                     }
48361                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
48362                 }
48363                 
48364                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
48365             } 
48366             else {
48367                 
48368                 // eack
48369             }
48370         } else {
48371             tagName = false;
48372         }
48373         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
48374             return ret;
48375         }
48376         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
48377             nopadtext = true;
48378         }
48379         
48380         
48381         // Traverse the tree
48382         i = 0;
48383         var currentElementChild = currentElement.childNodes.item(i);
48384         var allText = true;
48385         var innerHTML  = '';
48386         lastnode = '';
48387         while (currentElementChild) {
48388             // Formatting code (indent the tree so it looks nice on the screen)
48389             var nopad = nopadtext;
48390             if (lastnode == 'SPAN') {
48391                 nopad  = true;
48392             }
48393             // text
48394             if  (currentElementChild.nodeName == '#text') {
48395                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
48396                 toadd = nopadtext ? toadd : toadd.trim();
48397                 if (!nopad && toadd.length > 80) {
48398                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
48399                 }
48400                 innerHTML  += toadd;
48401                 
48402                 i++;
48403                 currentElementChild = currentElement.childNodes.item(i);
48404                 lastNode = '';
48405                 continue;
48406             }
48407             allText = false;
48408             
48409             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
48410                 
48411             // Recursively traverse the tree structure of the child node
48412             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
48413             lastnode = currentElementChild.nodeName;
48414             i++;
48415             currentElementChild=currentElement.childNodes.item(i);
48416         }
48417         
48418         ret += innerHTML;
48419         
48420         if (!allText) {
48421                 // The remaining code is mostly for formatting the tree
48422             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
48423         }
48424         
48425         
48426         if (tagName) {
48427             ret+= "</"+tagName+">";
48428         }
48429         return ret;
48430         
48431     },
48432         
48433     applyBlacklists : function()
48434     {
48435         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
48436         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
48437         
48438         this.white = [];
48439         this.black = [];
48440         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
48441             if (b.indexOf(tag) > -1) {
48442                 return;
48443             }
48444             this.white.push(tag);
48445             
48446         }, this);
48447         
48448         Roo.each(w, function(tag) {
48449             if (b.indexOf(tag) > -1) {
48450                 return;
48451             }
48452             if (this.white.indexOf(tag) > -1) {
48453                 return;
48454             }
48455             this.white.push(tag);
48456             
48457         }, this);
48458         
48459         
48460         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
48461             if (w.indexOf(tag) > -1) {
48462                 return;
48463             }
48464             this.black.push(tag);
48465             
48466         }, this);
48467         
48468         Roo.each(b, function(tag) {
48469             if (w.indexOf(tag) > -1) {
48470                 return;
48471             }
48472             if (this.black.indexOf(tag) > -1) {
48473                 return;
48474             }
48475             this.black.push(tag);
48476             
48477         }, this);
48478         
48479         
48480         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
48481         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
48482         
48483         this.cwhite = [];
48484         this.cblack = [];
48485         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
48486             if (b.indexOf(tag) > -1) {
48487                 return;
48488             }
48489             this.cwhite.push(tag);
48490             
48491         }, this);
48492         
48493         Roo.each(w, function(tag) {
48494             if (b.indexOf(tag) > -1) {
48495                 return;
48496             }
48497             if (this.cwhite.indexOf(tag) > -1) {
48498                 return;
48499             }
48500             this.cwhite.push(tag);
48501             
48502         }, this);
48503         
48504         
48505         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
48506             if (w.indexOf(tag) > -1) {
48507                 return;
48508             }
48509             this.cblack.push(tag);
48510             
48511         }, this);
48512         
48513         Roo.each(b, function(tag) {
48514             if (w.indexOf(tag) > -1) {
48515                 return;
48516             }
48517             if (this.cblack.indexOf(tag) > -1) {
48518                 return;
48519             }
48520             this.cblack.push(tag);
48521             
48522         }, this);
48523     },
48524     
48525     setStylesheets : function(stylesheets)
48526     {
48527         if(typeof(stylesheets) == 'string'){
48528             Roo.get(this.iframe.contentDocument.head).createChild({
48529                 tag : 'link',
48530                 rel : 'stylesheet',
48531                 type : 'text/css',
48532                 href : stylesheets
48533             });
48534             
48535             return;
48536         }
48537         var _this = this;
48538      
48539         Roo.each(stylesheets, function(s) {
48540             if(!s.length){
48541                 return;
48542             }
48543             
48544             Roo.get(_this.iframe.contentDocument.head).createChild({
48545                 tag : 'link',
48546                 rel : 'stylesheet',
48547                 type : 'text/css',
48548                 href : s
48549             });
48550         });
48551
48552         
48553     },
48554     
48555     removeStylesheets : function()
48556     {
48557         var _this = this;
48558         
48559         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
48560             s.remove();
48561         });
48562     }
48563     
48564     // hide stuff that is not compatible
48565     /**
48566      * @event blur
48567      * @hide
48568      */
48569     /**
48570      * @event change
48571      * @hide
48572      */
48573     /**
48574      * @event focus
48575      * @hide
48576      */
48577     /**
48578      * @event specialkey
48579      * @hide
48580      */
48581     /**
48582      * @cfg {String} fieldClass @hide
48583      */
48584     /**
48585      * @cfg {String} focusClass @hide
48586      */
48587     /**
48588      * @cfg {String} autoCreate @hide
48589      */
48590     /**
48591      * @cfg {String} inputType @hide
48592      */
48593     /**
48594      * @cfg {String} invalidClass @hide
48595      */
48596     /**
48597      * @cfg {String} invalidText @hide
48598      */
48599     /**
48600      * @cfg {String} msgFx @hide
48601      */
48602     /**
48603      * @cfg {String} validateOnBlur @hide
48604      */
48605 });
48606
48607 Roo.HtmlEditorCore.white = [
48608         'area', 'br', 'img', 'input', 'hr', 'wbr',
48609         
48610        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
48611        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
48612        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
48613        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
48614        'table',   'ul',         'xmp', 
48615        
48616        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
48617       'thead',   'tr', 
48618      
48619       'dir', 'menu', 'ol', 'ul', 'dl',
48620        
48621       'embed',  'object'
48622 ];
48623
48624
48625 Roo.HtmlEditorCore.black = [
48626     //    'embed',  'object', // enable - backend responsiblity to clean thiese
48627         'applet', // 
48628         'base',   'basefont', 'bgsound', 'blink',  'body', 
48629         'frame',  'frameset', 'head',    'html',   'ilayer', 
48630         'iframe', 'layer',  'link',     'meta',    'object',   
48631         'script', 'style' ,'title',  'xml' // clean later..
48632 ];
48633 Roo.HtmlEditorCore.clean = [
48634     'script', 'style', 'title', 'xml'
48635 ];
48636 Roo.HtmlEditorCore.remove = [
48637     'font'
48638 ];
48639 // attributes..
48640
48641 Roo.HtmlEditorCore.ablack = [
48642     'on'
48643 ];
48644     
48645 Roo.HtmlEditorCore.aclean = [ 
48646     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
48647 ];
48648
48649 // protocols..
48650 Roo.HtmlEditorCore.pwhite= [
48651         'http',  'https',  'mailto'
48652 ];
48653
48654 // white listed style attributes.
48655 Roo.HtmlEditorCore.cwhite= [
48656       //  'text-align', /// default is to allow most things..
48657       
48658          
48659 //        'font-size'//??
48660 ];
48661
48662 // black listed style attributes.
48663 Roo.HtmlEditorCore.cblack= [
48664       //  'font-size' -- this can be set by the project 
48665 ];
48666
48667
48668 Roo.HtmlEditorCore.swapCodes   =[ 
48669     [    8211, "--" ], 
48670     [    8212, "--" ], 
48671     [    8216,  "'" ],  
48672     [    8217, "'" ],  
48673     [    8220, '"' ],  
48674     [    8221, '"' ],  
48675     [    8226, "*" ],  
48676     [    8230, "..." ]
48677 ]; 
48678
48679     //<script type="text/javascript">
48680
48681 /*
48682  * Ext JS Library 1.1.1
48683  * Copyright(c) 2006-2007, Ext JS, LLC.
48684  * Licence LGPL
48685  * 
48686  */
48687  
48688  
48689 Roo.form.HtmlEditor = function(config){
48690     
48691     
48692     
48693     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
48694     
48695     if (!this.toolbars) {
48696         this.toolbars = [];
48697     }
48698     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
48699     
48700     
48701 };
48702
48703 /**
48704  * @class Roo.form.HtmlEditor
48705  * @extends Roo.form.Field
48706  * Provides a lightweight HTML Editor component.
48707  *
48708  * This has been tested on Fireforx / Chrome.. IE may not be so great..
48709  * 
48710  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
48711  * supported by this editor.</b><br/><br/>
48712  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
48713  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
48714  */
48715 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
48716     /**
48717      * @cfg {Boolean} clearUp
48718      */
48719     clearUp : true,
48720       /**
48721      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
48722      */
48723     toolbars : false,
48724    
48725      /**
48726      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
48727      *                        Roo.resizable.
48728      */
48729     resizable : false,
48730      /**
48731      * @cfg {Number} height (in pixels)
48732      */   
48733     height: 300,
48734    /**
48735      * @cfg {Number} width (in pixels)
48736      */   
48737     width: 500,
48738     
48739     /**
48740      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
48741      * 
48742      */
48743     stylesheets: false,
48744     
48745     
48746      /**
48747      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
48748      * 
48749      */
48750     cblack: false,
48751     /**
48752      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
48753      * 
48754      */
48755     cwhite: false,
48756     
48757      /**
48758      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
48759      * 
48760      */
48761     black: false,
48762     /**
48763      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
48764      * 
48765      */
48766     white: false,
48767     
48768     // id of frame..
48769     frameId: false,
48770     
48771     // private properties
48772     validationEvent : false,
48773     deferHeight: true,
48774     initialized : false,
48775     activated : false,
48776     
48777     onFocus : Roo.emptyFn,
48778     iframePad:3,
48779     hideMode:'offsets',
48780     
48781     actionMode : 'container', // defaults to hiding it...
48782     
48783     defaultAutoCreate : { // modified by initCompnoent..
48784         tag: "textarea",
48785         style:"width:500px;height:300px;",
48786         autocomplete: "new-password"
48787     },
48788
48789     // private
48790     initComponent : function(){
48791         this.addEvents({
48792             /**
48793              * @event initialize
48794              * Fires when the editor is fully initialized (including the iframe)
48795              * @param {HtmlEditor} this
48796              */
48797             initialize: true,
48798             /**
48799              * @event activate
48800              * Fires when the editor is first receives the focus. Any insertion must wait
48801              * until after this event.
48802              * @param {HtmlEditor} this
48803              */
48804             activate: true,
48805              /**
48806              * @event beforesync
48807              * Fires before the textarea is updated with content from the editor iframe. Return false
48808              * to cancel the sync.
48809              * @param {HtmlEditor} this
48810              * @param {String} html
48811              */
48812             beforesync: true,
48813              /**
48814              * @event beforepush
48815              * Fires before the iframe editor is updated with content from the textarea. Return false
48816              * to cancel the push.
48817              * @param {HtmlEditor} this
48818              * @param {String} html
48819              */
48820             beforepush: true,
48821              /**
48822              * @event sync
48823              * Fires when the textarea is updated with content from the editor iframe.
48824              * @param {HtmlEditor} this
48825              * @param {String} html
48826              */
48827             sync: true,
48828              /**
48829              * @event push
48830              * Fires when the iframe editor is updated with content from the textarea.
48831              * @param {HtmlEditor} this
48832              * @param {String} html
48833              */
48834             push: true,
48835              /**
48836              * @event editmodechange
48837              * Fires when the editor switches edit modes
48838              * @param {HtmlEditor} this
48839              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
48840              */
48841             editmodechange: true,
48842             /**
48843              * @event editorevent
48844              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
48845              * @param {HtmlEditor} this
48846              */
48847             editorevent: true,
48848             /**
48849              * @event firstfocus
48850              * Fires when on first focus - needed by toolbars..
48851              * @param {HtmlEditor} this
48852              */
48853             firstfocus: true,
48854             /**
48855              * @event autosave
48856              * Auto save the htmlEditor value as a file into Events
48857              * @param {HtmlEditor} this
48858              */
48859             autosave: true,
48860             /**
48861              * @event savedpreview
48862              * preview the saved version of htmlEditor
48863              * @param {HtmlEditor} this
48864              */
48865             savedpreview: true,
48866             
48867             /**
48868             * @event stylesheetsclick
48869             * Fires when press the Sytlesheets button
48870             * @param {Roo.HtmlEditorCore} this
48871             */
48872             stylesheetsclick: true
48873         });
48874         this.defaultAutoCreate =  {
48875             tag: "textarea",
48876             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
48877             autocomplete: "new-password"
48878         };
48879     },
48880
48881     /**
48882      * Protected method that will not generally be called directly. It
48883      * is called when the editor creates its toolbar. Override this method if you need to
48884      * add custom toolbar buttons.
48885      * @param {HtmlEditor} editor
48886      */
48887     createToolbar : function(editor){
48888         Roo.log("create toolbars");
48889         if (!editor.toolbars || !editor.toolbars.length) {
48890             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
48891         }
48892         
48893         for (var i =0 ; i < editor.toolbars.length;i++) {
48894             editor.toolbars[i] = Roo.factory(
48895                     typeof(editor.toolbars[i]) == 'string' ?
48896                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
48897                 Roo.form.HtmlEditor);
48898             editor.toolbars[i].init(editor);
48899         }
48900          
48901         
48902     },
48903
48904      
48905     // private
48906     onRender : function(ct, position)
48907     {
48908         var _t = this;
48909         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
48910         
48911         this.wrap = this.el.wrap({
48912             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
48913         });
48914         
48915         this.editorcore.onRender(ct, position);
48916          
48917         if (this.resizable) {
48918             this.resizeEl = new Roo.Resizable(this.wrap, {
48919                 pinned : true,
48920                 wrap: true,
48921                 dynamic : true,
48922                 minHeight : this.height,
48923                 height: this.height,
48924                 handles : this.resizable,
48925                 width: this.width,
48926                 listeners : {
48927                     resize : function(r, w, h) {
48928                         _t.onResize(w,h); // -something
48929                     }
48930                 }
48931             });
48932             
48933         }
48934         this.createToolbar(this);
48935        
48936         
48937         if(!this.width){
48938             this.setSize(this.wrap.getSize());
48939         }
48940         if (this.resizeEl) {
48941             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
48942             // should trigger onReize..
48943         }
48944         
48945         this.keyNav = new Roo.KeyNav(this.el, {
48946             
48947             "tab" : function(e){
48948                 e.preventDefault();
48949                 
48950                 var value = this.getValue();
48951                 
48952                 var start = this.el.dom.selectionStart;
48953                 var end = this.el.dom.selectionEnd;
48954                 
48955                 if(!e.shiftKey){
48956                     
48957                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
48958                     this.el.dom.setSelectionRange(end + 1, end + 1);
48959                     return;
48960                 }
48961                 
48962                 var f = value.substring(0, start).split("\t");
48963                 
48964                 if(f.pop().length != 0){
48965                     return;
48966                 }
48967                 
48968                 this.setValue(f.join("\t") + value.substring(end));
48969                 this.el.dom.setSelectionRange(start - 1, start - 1);
48970                 
48971             },
48972             
48973             "home" : function(e){
48974                 e.preventDefault();
48975                 
48976                 var curr = this.el.dom.selectionStart;
48977                 var lines = this.getValue().split("\n");
48978                 
48979                 if(!lines.length){
48980                     return;
48981                 }
48982                 
48983                 if(e.ctrlKey){
48984                     this.el.dom.setSelectionRange(0, 0);
48985                     return;
48986                 }
48987                 
48988                 var pos = 0;
48989                 
48990                 for (var i = 0; i < lines.length;i++) {
48991                     pos += lines[i].length;
48992                     
48993                     if(i != 0){
48994                         pos += 1;
48995                     }
48996                     
48997                     if(pos < curr){
48998                         continue;
48999                     }
49000                     
49001                     pos -= lines[i].length;
49002                     
49003                     break;
49004                 }
49005                 
49006                 if(!e.shiftKey){
49007                     this.el.dom.setSelectionRange(pos, pos);
49008                     return;
49009                 }
49010                 
49011                 this.el.dom.selectionStart = pos;
49012                 this.el.dom.selectionEnd = curr;
49013             },
49014             
49015             "end" : function(e){
49016                 e.preventDefault();
49017                 
49018                 var curr = this.el.dom.selectionStart;
49019                 var lines = this.getValue().split("\n");
49020                 
49021                 if(!lines.length){
49022                     return;
49023                 }
49024                 
49025                 if(e.ctrlKey){
49026                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
49027                     return;
49028                 }
49029                 
49030                 var pos = 0;
49031                 
49032                 for (var i = 0; i < lines.length;i++) {
49033                     
49034                     pos += lines[i].length;
49035                     
49036                     if(i != 0){
49037                         pos += 1;
49038                     }
49039                     
49040                     if(pos < curr){
49041                         continue;
49042                     }
49043                     
49044                     break;
49045                 }
49046                 
49047                 if(!e.shiftKey){
49048                     this.el.dom.setSelectionRange(pos, pos);
49049                     return;
49050                 }
49051                 
49052                 this.el.dom.selectionStart = curr;
49053                 this.el.dom.selectionEnd = pos;
49054             },
49055
49056             scope : this,
49057
49058             doRelay : function(foo, bar, hname){
49059                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
49060             },
49061
49062             forceKeyDown: true
49063         });
49064         
49065 //        if(this.autosave && this.w){
49066 //            this.autoSaveFn = setInterval(this.autosave, 1000);
49067 //        }
49068     },
49069
49070     // private
49071     onResize : function(w, h)
49072     {
49073         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
49074         var ew = false;
49075         var eh = false;
49076         
49077         if(this.el ){
49078             if(typeof w == 'number'){
49079                 var aw = w - this.wrap.getFrameWidth('lr');
49080                 this.el.setWidth(this.adjustWidth('textarea', aw));
49081                 ew = aw;
49082             }
49083             if(typeof h == 'number'){
49084                 var tbh = 0;
49085                 for (var i =0; i < this.toolbars.length;i++) {
49086                     // fixme - ask toolbars for heights?
49087                     tbh += this.toolbars[i].tb.el.getHeight();
49088                     if (this.toolbars[i].footer) {
49089                         tbh += this.toolbars[i].footer.el.getHeight();
49090                     }
49091                 }
49092                 
49093                 
49094                 
49095                 
49096                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
49097                 ah -= 5; // knock a few pixes off for look..
49098 //                Roo.log(ah);
49099                 this.el.setHeight(this.adjustWidth('textarea', ah));
49100                 var eh = ah;
49101             }
49102         }
49103         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
49104         this.editorcore.onResize(ew,eh);
49105         
49106     },
49107
49108     /**
49109      * Toggles the editor between standard and source edit mode.
49110      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
49111      */
49112     toggleSourceEdit : function(sourceEditMode)
49113     {
49114         this.editorcore.toggleSourceEdit(sourceEditMode);
49115         
49116         if(this.editorcore.sourceEditMode){
49117             Roo.log('editor - showing textarea');
49118             
49119 //            Roo.log('in');
49120 //            Roo.log(this.syncValue());
49121             this.editorcore.syncValue();
49122             this.el.removeClass('x-hidden');
49123             this.el.dom.removeAttribute('tabIndex');
49124             this.el.focus();
49125             
49126             for (var i = 0; i < this.toolbars.length; i++) {
49127                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
49128                     this.toolbars[i].tb.hide();
49129                     this.toolbars[i].footer.hide();
49130                 }
49131             }
49132             
49133         }else{
49134             Roo.log('editor - hiding textarea');
49135 //            Roo.log('out')
49136 //            Roo.log(this.pushValue()); 
49137             this.editorcore.pushValue();
49138             
49139             this.el.addClass('x-hidden');
49140             this.el.dom.setAttribute('tabIndex', -1);
49141             
49142             for (var i = 0; i < this.toolbars.length; i++) {
49143                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
49144                     this.toolbars[i].tb.show();
49145                     this.toolbars[i].footer.show();
49146                 }
49147             }
49148             
49149             //this.deferFocus();
49150         }
49151         
49152         this.setSize(this.wrap.getSize());
49153         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
49154         
49155         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
49156     },
49157  
49158     // private (for BoxComponent)
49159     adjustSize : Roo.BoxComponent.prototype.adjustSize,
49160
49161     // private (for BoxComponent)
49162     getResizeEl : function(){
49163         return this.wrap;
49164     },
49165
49166     // private (for BoxComponent)
49167     getPositionEl : function(){
49168         return this.wrap;
49169     },
49170
49171     // private
49172     initEvents : function(){
49173         this.originalValue = this.getValue();
49174     },
49175
49176     /**
49177      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
49178      * @method
49179      */
49180     markInvalid : Roo.emptyFn,
49181     /**
49182      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
49183      * @method
49184      */
49185     clearInvalid : Roo.emptyFn,
49186
49187     setValue : function(v){
49188         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
49189         this.editorcore.pushValue();
49190     },
49191
49192      
49193     // private
49194     deferFocus : function(){
49195         this.focus.defer(10, this);
49196     },
49197
49198     // doc'ed in Field
49199     focus : function(){
49200         this.editorcore.focus();
49201         
49202     },
49203       
49204
49205     // private
49206     onDestroy : function(){
49207         
49208         
49209         
49210         if(this.rendered){
49211             
49212             for (var i =0; i < this.toolbars.length;i++) {
49213                 // fixme - ask toolbars for heights?
49214                 this.toolbars[i].onDestroy();
49215             }
49216             
49217             this.wrap.dom.innerHTML = '';
49218             this.wrap.remove();
49219         }
49220     },
49221
49222     // private
49223     onFirstFocus : function(){
49224         //Roo.log("onFirstFocus");
49225         this.editorcore.onFirstFocus();
49226          for (var i =0; i < this.toolbars.length;i++) {
49227             this.toolbars[i].onFirstFocus();
49228         }
49229         
49230     },
49231     
49232     // private
49233     syncValue : function()
49234     {
49235         this.editorcore.syncValue();
49236     },
49237     
49238     pushValue : function()
49239     {
49240         this.editorcore.pushValue();
49241     },
49242     
49243     setStylesheets : function(stylesheets)
49244     {
49245         this.editorcore.setStylesheets(stylesheets);
49246     },
49247     
49248     removeStylesheets : function()
49249     {
49250         this.editorcore.removeStylesheets();
49251     }
49252      
49253     
49254     // hide stuff that is not compatible
49255     /**
49256      * @event blur
49257      * @hide
49258      */
49259     /**
49260      * @event change
49261      * @hide
49262      */
49263     /**
49264      * @event focus
49265      * @hide
49266      */
49267     /**
49268      * @event specialkey
49269      * @hide
49270      */
49271     /**
49272      * @cfg {String} fieldClass @hide
49273      */
49274     /**
49275      * @cfg {String} focusClass @hide
49276      */
49277     /**
49278      * @cfg {String} autoCreate @hide
49279      */
49280     /**
49281      * @cfg {String} inputType @hide
49282      */
49283     /**
49284      * @cfg {String} invalidClass @hide
49285      */
49286     /**
49287      * @cfg {String} invalidText @hide
49288      */
49289     /**
49290      * @cfg {String} msgFx @hide
49291      */
49292     /**
49293      * @cfg {String} validateOnBlur @hide
49294      */
49295 });
49296  
49297     // <script type="text/javascript">
49298 /*
49299  * Based on
49300  * Ext JS Library 1.1.1
49301  * Copyright(c) 2006-2007, Ext JS, LLC.
49302  *  
49303  
49304  */
49305
49306 /**
49307  * @class Roo.form.HtmlEditorToolbar1
49308  * Basic Toolbar
49309  * 
49310  * Usage:
49311  *
49312  new Roo.form.HtmlEditor({
49313     ....
49314     toolbars : [
49315         new Roo.form.HtmlEditorToolbar1({
49316             disable : { fonts: 1 , format: 1, ..., ... , ...],
49317             btns : [ .... ]
49318         })
49319     }
49320      
49321  * 
49322  * @cfg {Object} disable List of elements to disable..
49323  * @cfg {Array} btns List of additional buttons.
49324  * 
49325  * 
49326  * NEEDS Extra CSS? 
49327  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
49328  */
49329  
49330 Roo.form.HtmlEditor.ToolbarStandard = function(config)
49331 {
49332     
49333     Roo.apply(this, config);
49334     
49335     // default disabled, based on 'good practice'..
49336     this.disable = this.disable || {};
49337     Roo.applyIf(this.disable, {
49338         fontSize : true,
49339         colors : true,
49340         specialElements : true
49341     });
49342     
49343     
49344     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49345     // dont call parent... till later.
49346 }
49347
49348 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
49349     
49350     tb: false,
49351     
49352     rendered: false,
49353     
49354     editor : false,
49355     editorcore : false,
49356     /**
49357      * @cfg {Object} disable  List of toolbar elements to disable
49358          
49359      */
49360     disable : false,
49361     
49362     
49363      /**
49364      * @cfg {String} createLinkText The default text for the create link prompt
49365      */
49366     createLinkText : 'Please enter the URL for the link:',
49367     /**
49368      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
49369      */
49370     defaultLinkValue : 'http:/'+'/',
49371    
49372     
49373       /**
49374      * @cfg {Array} fontFamilies An array of available font families
49375      */
49376     fontFamilies : [
49377         'Arial',
49378         'Courier New',
49379         'Tahoma',
49380         'Times New Roman',
49381         'Verdana'
49382     ],
49383     
49384     specialChars : [
49385            "&#169;",
49386           "&#174;",     
49387           "&#8482;",    
49388           "&#163;" ,    
49389          // "&#8212;",    
49390           "&#8230;",    
49391           "&#247;" ,    
49392         //  "&#225;" ,     ?? a acute?
49393            "&#8364;"    , //Euro
49394        //   "&#8220;"    ,
49395         //  "&#8221;"    ,
49396         //  "&#8226;"    ,
49397           "&#176;"  //   , // degrees
49398
49399          // "&#233;"     , // e ecute
49400          // "&#250;"     , // u ecute?
49401     ],
49402     
49403     specialElements : [
49404         {
49405             text: "Insert Table",
49406             xtype: 'MenuItem',
49407             xns : Roo.Menu,
49408             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
49409                 
49410         },
49411         {    
49412             text: "Insert Image",
49413             xtype: 'MenuItem',
49414             xns : Roo.Menu,
49415             ihtml : '<img src="about:blank"/>'
49416             
49417         }
49418         
49419          
49420     ],
49421     
49422     
49423     inputElements : [ 
49424             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
49425             "input:submit", "input:button", "select", "textarea", "label" ],
49426     formats : [
49427         ["p"] ,  
49428         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
49429         ["pre"],[ "code"], 
49430         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
49431         ['div'],['span']
49432     ],
49433     
49434     cleanStyles : [
49435         "font-size"
49436     ],
49437      /**
49438      * @cfg {String} defaultFont default font to use.
49439      */
49440     defaultFont: 'tahoma',
49441    
49442     fontSelect : false,
49443     
49444     
49445     formatCombo : false,
49446     
49447     init : function(editor)
49448     {
49449         this.editor = editor;
49450         this.editorcore = editor.editorcore ? editor.editorcore : editor;
49451         var editorcore = this.editorcore;
49452         
49453         var _t = this;
49454         
49455         var fid = editorcore.frameId;
49456         var etb = this;
49457         function btn(id, toggle, handler){
49458             var xid = fid + '-'+ id ;
49459             return {
49460                 id : xid,
49461                 cmd : id,
49462                 cls : 'x-btn-icon x-edit-'+id,
49463                 enableToggle:toggle !== false,
49464                 scope: _t, // was editor...
49465                 handler:handler||_t.relayBtnCmd,
49466                 clickEvent:'mousedown',
49467                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49468                 tabIndex:-1
49469             };
49470         }
49471         
49472         
49473         
49474         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49475         this.tb = tb;
49476          // stop form submits
49477         tb.el.on('click', function(e){
49478             e.preventDefault(); // what does this do?
49479         });
49480
49481         if(!this.disable.font) { // && !Roo.isSafari){
49482             /* why no safari for fonts 
49483             editor.fontSelect = tb.el.createChild({
49484                 tag:'select',
49485                 tabIndex: -1,
49486                 cls:'x-font-select',
49487                 html: this.createFontOptions()
49488             });
49489             
49490             editor.fontSelect.on('change', function(){
49491                 var font = editor.fontSelect.dom.value;
49492                 editor.relayCmd('fontname', font);
49493                 editor.deferFocus();
49494             }, editor);
49495             
49496             tb.add(
49497                 editor.fontSelect.dom,
49498                 '-'
49499             );
49500             */
49501             
49502         };
49503         if(!this.disable.formats){
49504             this.formatCombo = new Roo.form.ComboBox({
49505                 store: new Roo.data.SimpleStore({
49506                     id : 'tag',
49507                     fields: ['tag'],
49508                     data : this.formats // from states.js
49509                 }),
49510                 blockFocus : true,
49511                 name : '',
49512                 //autoCreate : {tag: "div",  size: "20"},
49513                 displayField:'tag',
49514                 typeAhead: false,
49515                 mode: 'local',
49516                 editable : false,
49517                 triggerAction: 'all',
49518                 emptyText:'Add tag',
49519                 selectOnFocus:true,
49520                 width:135,
49521                 listeners : {
49522                     'select': function(c, r, i) {
49523                         editorcore.insertTag(r.get('tag'));
49524                         editor.focus();
49525                     }
49526                 }
49527
49528             });
49529             tb.addField(this.formatCombo);
49530             
49531         }
49532         
49533         if(!this.disable.format){
49534             tb.add(
49535                 btn('bold'),
49536                 btn('italic'),
49537                 btn('underline'),
49538                 btn('strikethrough')
49539             );
49540         };
49541         if(!this.disable.fontSize){
49542             tb.add(
49543                 '-',
49544                 
49545                 
49546                 btn('increasefontsize', false, editorcore.adjustFont),
49547                 btn('decreasefontsize', false, editorcore.adjustFont)
49548             );
49549         };
49550         
49551         
49552         if(!this.disable.colors){
49553             tb.add(
49554                 '-', {
49555                     id:editorcore.frameId +'-forecolor',
49556                     cls:'x-btn-icon x-edit-forecolor',
49557                     clickEvent:'mousedown',
49558                     tooltip: this.buttonTips['forecolor'] || undefined,
49559                     tabIndex:-1,
49560                     menu : new Roo.menu.ColorMenu({
49561                         allowReselect: true,
49562                         focus: Roo.emptyFn,
49563                         value:'000000',
49564                         plain:true,
49565                         selectHandler: function(cp, color){
49566                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
49567                             editor.deferFocus();
49568                         },
49569                         scope: editorcore,
49570                         clickEvent:'mousedown'
49571                     })
49572                 }, {
49573                     id:editorcore.frameId +'backcolor',
49574                     cls:'x-btn-icon x-edit-backcolor',
49575                     clickEvent:'mousedown',
49576                     tooltip: this.buttonTips['backcolor'] || undefined,
49577                     tabIndex:-1,
49578                     menu : new Roo.menu.ColorMenu({
49579                         focus: Roo.emptyFn,
49580                         value:'FFFFFF',
49581                         plain:true,
49582                         allowReselect: true,
49583                         selectHandler: function(cp, color){
49584                             if(Roo.isGecko){
49585                                 editorcore.execCmd('useCSS', false);
49586                                 editorcore.execCmd('hilitecolor', color);
49587                                 editorcore.execCmd('useCSS', true);
49588                                 editor.deferFocus();
49589                             }else{
49590                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
49591                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
49592                                 editor.deferFocus();
49593                             }
49594                         },
49595                         scope:editorcore,
49596                         clickEvent:'mousedown'
49597                     })
49598                 }
49599             );
49600         };
49601         // now add all the items...
49602         
49603
49604         if(!this.disable.alignments){
49605             tb.add(
49606                 '-',
49607                 btn('justifyleft'),
49608                 btn('justifycenter'),
49609                 btn('justifyright')
49610             );
49611         };
49612
49613         //if(!Roo.isSafari){
49614             if(!this.disable.links){
49615                 tb.add(
49616                     '-',
49617                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
49618                 );
49619             };
49620
49621             if(!this.disable.lists){
49622                 tb.add(
49623                     '-',
49624                     btn('insertorderedlist'),
49625                     btn('insertunorderedlist')
49626                 );
49627             }
49628             if(!this.disable.sourceEdit){
49629                 tb.add(
49630                     '-',
49631                     btn('sourceedit', true, function(btn){
49632                         this.toggleSourceEdit(btn.pressed);
49633                     })
49634                 );
49635             }
49636         //}
49637         
49638         var smenu = { };
49639         // special menu.. - needs to be tidied up..
49640         if (!this.disable.special) {
49641             smenu = {
49642                 text: "&#169;",
49643                 cls: 'x-edit-none',
49644                 
49645                 menu : {
49646                     items : []
49647                 }
49648             };
49649             for (var i =0; i < this.specialChars.length; i++) {
49650                 smenu.menu.items.push({
49651                     
49652                     html: this.specialChars[i],
49653                     handler: function(a,b) {
49654                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
49655                         //editor.insertAtCursor(a.html);
49656                         
49657                     },
49658                     tabIndex:-1
49659                 });
49660             }
49661             
49662             
49663             tb.add(smenu);
49664             
49665             
49666         }
49667         
49668         var cmenu = { };
49669         if (!this.disable.cleanStyles) {
49670             cmenu = {
49671                 cls: 'x-btn-icon x-btn-clear',
49672                 
49673                 menu : {
49674                     items : []
49675                 }
49676             };
49677             for (var i =0; i < this.cleanStyles.length; i++) {
49678                 cmenu.menu.items.push({
49679                     actiontype : this.cleanStyles[i],
49680                     html: 'Remove ' + this.cleanStyles[i],
49681                     handler: function(a,b) {
49682 //                        Roo.log(a);
49683 //                        Roo.log(b);
49684                         var c = Roo.get(editorcore.doc.body);
49685                         c.select('[style]').each(function(s) {
49686                             s.dom.style.removeProperty(a.actiontype);
49687                         });
49688                         editorcore.syncValue();
49689                     },
49690                     tabIndex:-1
49691                 });
49692             }
49693              cmenu.menu.items.push({
49694                 actiontype : 'tablewidths',
49695                 html: 'Remove Table Widths',
49696                 handler: function(a,b) {
49697                     editorcore.cleanTableWidths();
49698                     editorcore.syncValue();
49699                 },
49700                 tabIndex:-1
49701             });
49702             cmenu.menu.items.push({
49703                 actiontype : 'word',
49704                 html: 'Remove MS Word Formating',
49705                 handler: function(a,b) {
49706                     editorcore.cleanWord();
49707                     editorcore.syncValue();
49708                 },
49709                 tabIndex:-1
49710             });
49711             
49712             cmenu.menu.items.push({
49713                 actiontype : 'all',
49714                 html: 'Remove All Styles',
49715                 handler: function(a,b) {
49716                     
49717                     var c = Roo.get(editorcore.doc.body);
49718                     c.select('[style]').each(function(s) {
49719                         s.dom.removeAttribute('style');
49720                     });
49721                     editorcore.syncValue();
49722                 },
49723                 tabIndex:-1
49724             });
49725             
49726             cmenu.menu.items.push({
49727                 actiontype : 'all',
49728                 html: 'Remove All CSS Classes',
49729                 handler: function(a,b) {
49730                     
49731                     var c = Roo.get(editorcore.doc.body);
49732                     c.select('[class]').each(function(s) {
49733                         s.dom.className = '';
49734                     });
49735                     editorcore.syncValue();
49736                 },
49737                 tabIndex:-1
49738             });
49739             
49740              cmenu.menu.items.push({
49741                 actiontype : 'tidy',
49742                 html: 'Tidy HTML Source',
49743                 handler: function(a,b) {
49744                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
49745                     editorcore.syncValue();
49746                 },
49747                 tabIndex:-1
49748             });
49749             
49750             
49751             tb.add(cmenu);
49752         }
49753          
49754         if (!this.disable.specialElements) {
49755             var semenu = {
49756                 text: "Other;",
49757                 cls: 'x-edit-none',
49758                 menu : {
49759                     items : []
49760                 }
49761             };
49762             for (var i =0; i < this.specialElements.length; i++) {
49763                 semenu.menu.items.push(
49764                     Roo.apply({ 
49765                         handler: function(a,b) {
49766                             editor.insertAtCursor(this.ihtml);
49767                         }
49768                     }, this.specialElements[i])
49769                 );
49770                     
49771             }
49772             
49773             tb.add(semenu);
49774             
49775             
49776         }
49777          
49778         
49779         if (this.btns) {
49780             for(var i =0; i< this.btns.length;i++) {
49781                 var b = Roo.factory(this.btns[i],Roo.form);
49782                 b.cls =  'x-edit-none';
49783                 
49784                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
49785                     b.cls += ' x-init-enable';
49786                 }
49787                 
49788                 b.scope = editorcore;
49789                 tb.add(b);
49790             }
49791         
49792         }
49793         
49794         
49795         
49796         // disable everything...
49797         
49798         this.tb.items.each(function(item){
49799             
49800            if(
49801                 item.id != editorcore.frameId+ '-sourceedit' && 
49802                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
49803             ){
49804                 
49805                 item.disable();
49806             }
49807         });
49808         this.rendered = true;
49809         
49810         // the all the btns;
49811         editor.on('editorevent', this.updateToolbar, this);
49812         // other toolbars need to implement this..
49813         //editor.on('editmodechange', this.updateToolbar, this);
49814     },
49815     
49816     
49817     relayBtnCmd : function(btn) {
49818         this.editorcore.relayCmd(btn.cmd);
49819     },
49820     // private used internally
49821     createLink : function(){
49822         Roo.log("create link?");
49823         var url = prompt(this.createLinkText, this.defaultLinkValue);
49824         if(url && url != 'http:/'+'/'){
49825             this.editorcore.relayCmd('createlink', url);
49826         }
49827     },
49828
49829     
49830     /**
49831      * Protected method that will not generally be called directly. It triggers
49832      * a toolbar update by reading the markup state of the current selection in the editor.
49833      */
49834     updateToolbar: function(){
49835
49836         if(!this.editorcore.activated){
49837             this.editor.onFirstFocus();
49838             return;
49839         }
49840
49841         var btns = this.tb.items.map, 
49842             doc = this.editorcore.doc,
49843             frameId = this.editorcore.frameId;
49844
49845         if(!this.disable.font && !Roo.isSafari){
49846             /*
49847             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
49848             if(name != this.fontSelect.dom.value){
49849                 this.fontSelect.dom.value = name;
49850             }
49851             */
49852         }
49853         if(!this.disable.format){
49854             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
49855             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
49856             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
49857             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
49858         }
49859         if(!this.disable.alignments){
49860             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
49861             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
49862             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
49863         }
49864         if(!Roo.isSafari && !this.disable.lists){
49865             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
49866             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
49867         }
49868         
49869         var ans = this.editorcore.getAllAncestors();
49870         if (this.formatCombo) {
49871             
49872             
49873             var store = this.formatCombo.store;
49874             this.formatCombo.setValue("");
49875             for (var i =0; i < ans.length;i++) {
49876                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
49877                     // select it..
49878                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
49879                     break;
49880                 }
49881             }
49882         }
49883         
49884         
49885         
49886         // hides menus... - so this cant be on a menu...
49887         Roo.menu.MenuMgr.hideAll();
49888
49889         //this.editorsyncValue();
49890     },
49891    
49892     
49893     createFontOptions : function(){
49894         var buf = [], fs = this.fontFamilies, ff, lc;
49895         
49896         
49897         
49898         for(var i = 0, len = fs.length; i< len; i++){
49899             ff = fs[i];
49900             lc = ff.toLowerCase();
49901             buf.push(
49902                 '<option value="',lc,'" style="font-family:',ff,';"',
49903                     (this.defaultFont == lc ? ' selected="true">' : '>'),
49904                     ff,
49905                 '</option>'
49906             );
49907         }
49908         return buf.join('');
49909     },
49910     
49911     toggleSourceEdit : function(sourceEditMode){
49912         
49913         Roo.log("toolbar toogle");
49914         if(sourceEditMode === undefined){
49915             sourceEditMode = !this.sourceEditMode;
49916         }
49917         this.sourceEditMode = sourceEditMode === true;
49918         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
49919         // just toggle the button?
49920         if(btn.pressed !== this.sourceEditMode){
49921             btn.toggle(this.sourceEditMode);
49922             return;
49923         }
49924         
49925         if(sourceEditMode){
49926             Roo.log("disabling buttons");
49927             this.tb.items.each(function(item){
49928                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
49929                     item.disable();
49930                 }
49931             });
49932           
49933         }else{
49934             Roo.log("enabling buttons");
49935             if(this.editorcore.initialized){
49936                 this.tb.items.each(function(item){
49937                     item.enable();
49938                 });
49939             }
49940             
49941         }
49942         Roo.log("calling toggole on editor");
49943         // tell the editor that it's been pressed..
49944         this.editor.toggleSourceEdit(sourceEditMode);
49945        
49946     },
49947      /**
49948      * Object collection of toolbar tooltips for the buttons in the editor. The key
49949      * is the command id associated with that button and the value is a valid QuickTips object.
49950      * For example:
49951 <pre><code>
49952 {
49953     bold : {
49954         title: 'Bold (Ctrl+B)',
49955         text: 'Make the selected text bold.',
49956         cls: 'x-html-editor-tip'
49957     },
49958     italic : {
49959         title: 'Italic (Ctrl+I)',
49960         text: 'Make the selected text italic.',
49961         cls: 'x-html-editor-tip'
49962     },
49963     ...
49964 </code></pre>
49965     * @type Object
49966      */
49967     buttonTips : {
49968         bold : {
49969             title: 'Bold (Ctrl+B)',
49970             text: 'Make the selected text bold.',
49971             cls: 'x-html-editor-tip'
49972         },
49973         italic : {
49974             title: 'Italic (Ctrl+I)',
49975             text: 'Make the selected text italic.',
49976             cls: 'x-html-editor-tip'
49977         },
49978         underline : {
49979             title: 'Underline (Ctrl+U)',
49980             text: 'Underline the selected text.',
49981             cls: 'x-html-editor-tip'
49982         },
49983         strikethrough : {
49984             title: 'Strikethrough',
49985             text: 'Strikethrough the selected text.',
49986             cls: 'x-html-editor-tip'
49987         },
49988         increasefontsize : {
49989             title: 'Grow Text',
49990             text: 'Increase the font size.',
49991             cls: 'x-html-editor-tip'
49992         },
49993         decreasefontsize : {
49994             title: 'Shrink Text',
49995             text: 'Decrease the font size.',
49996             cls: 'x-html-editor-tip'
49997         },
49998         backcolor : {
49999             title: 'Text Highlight Color',
50000             text: 'Change the background color of the selected text.',
50001             cls: 'x-html-editor-tip'
50002         },
50003         forecolor : {
50004             title: 'Font Color',
50005             text: 'Change the color of the selected text.',
50006             cls: 'x-html-editor-tip'
50007         },
50008         justifyleft : {
50009             title: 'Align Text Left',
50010             text: 'Align text to the left.',
50011             cls: 'x-html-editor-tip'
50012         },
50013         justifycenter : {
50014             title: 'Center Text',
50015             text: 'Center text in the editor.',
50016             cls: 'x-html-editor-tip'
50017         },
50018         justifyright : {
50019             title: 'Align Text Right',
50020             text: 'Align text to the right.',
50021             cls: 'x-html-editor-tip'
50022         },
50023         insertunorderedlist : {
50024             title: 'Bullet List',
50025             text: 'Start a bulleted list.',
50026             cls: 'x-html-editor-tip'
50027         },
50028         insertorderedlist : {
50029             title: 'Numbered List',
50030             text: 'Start a numbered list.',
50031             cls: 'x-html-editor-tip'
50032         },
50033         createlink : {
50034             title: 'Hyperlink',
50035             text: 'Make the selected text a hyperlink.',
50036             cls: 'x-html-editor-tip'
50037         },
50038         sourceedit : {
50039             title: 'Source Edit',
50040             text: 'Switch to source editing mode.',
50041             cls: 'x-html-editor-tip'
50042         }
50043     },
50044     // private
50045     onDestroy : function(){
50046         if(this.rendered){
50047             
50048             this.tb.items.each(function(item){
50049                 if(item.menu){
50050                     item.menu.removeAll();
50051                     if(item.menu.el){
50052                         item.menu.el.destroy();
50053                     }
50054                 }
50055                 item.destroy();
50056             });
50057              
50058         }
50059     },
50060     onFirstFocus: function() {
50061         this.tb.items.each(function(item){
50062            item.enable();
50063         });
50064     }
50065 });
50066
50067
50068
50069
50070 // <script type="text/javascript">
50071 /*
50072  * Based on
50073  * Ext JS Library 1.1.1
50074  * Copyright(c) 2006-2007, Ext JS, LLC.
50075  *  
50076  
50077  */
50078
50079  
50080 /**
50081  * @class Roo.form.HtmlEditor.ToolbarContext
50082  * Context Toolbar
50083  * 
50084  * Usage:
50085  *
50086  new Roo.form.HtmlEditor({
50087     ....
50088     toolbars : [
50089         { xtype: 'ToolbarStandard', styles : {} }
50090         { xtype: 'ToolbarContext', disable : {} }
50091     ]
50092 })
50093
50094      
50095  * 
50096  * @config : {Object} disable List of elements to disable.. (not done yet.)
50097  * @config : {Object} styles  Map of styles available.
50098  * 
50099  */
50100
50101 Roo.form.HtmlEditor.ToolbarContext = function(config)
50102 {
50103     
50104     Roo.apply(this, config);
50105     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
50106     // dont call parent... till later.
50107     this.styles = this.styles || {};
50108 }
50109
50110  
50111
50112 Roo.form.HtmlEditor.ToolbarContext.types = {
50113     'IMG' : {
50114         width : {
50115             title: "Width",
50116             width: 40
50117         },
50118         height:  {
50119             title: "Height",
50120             width: 40
50121         },
50122         align: {
50123             title: "Align",
50124             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
50125             width : 80
50126             
50127         },
50128         border: {
50129             title: "Border",
50130             width: 40
50131         },
50132         alt: {
50133             title: "Alt",
50134             width: 120
50135         },
50136         src : {
50137             title: "Src",
50138             width: 220
50139         }
50140         
50141     },
50142     'A' : {
50143         name : {
50144             title: "Name",
50145             width: 50
50146         },
50147         target:  {
50148             title: "Target",
50149             width: 120
50150         },
50151         href:  {
50152             title: "Href",
50153             width: 220
50154         } // border?
50155         
50156     },
50157     'TABLE' : {
50158         rows : {
50159             title: "Rows",
50160             width: 20
50161         },
50162         cols : {
50163             title: "Cols",
50164             width: 20
50165         },
50166         width : {
50167             title: "Width",
50168             width: 40
50169         },
50170         height : {
50171             title: "Height",
50172             width: 40
50173         },
50174         border : {
50175             title: "Border",
50176             width: 20
50177         }
50178     },
50179     'TD' : {
50180         width : {
50181             title: "Width",
50182             width: 40
50183         },
50184         height : {
50185             title: "Height",
50186             width: 40
50187         },   
50188         align: {
50189             title: "Align",
50190             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
50191             width: 80
50192         },
50193         valign: {
50194             title: "Valign",
50195             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
50196             width: 80
50197         },
50198         colspan: {
50199             title: "Colspan",
50200             width: 20
50201             
50202         },
50203          'font-family'  : {
50204             title : "Font",
50205             style : 'fontFamily',
50206             displayField: 'display',
50207             optname : 'font-family',
50208             width: 140
50209         }
50210     },
50211     'INPUT' : {
50212         name : {
50213             title: "name",
50214             width: 120
50215         },
50216         value : {
50217             title: "Value",
50218             width: 120
50219         },
50220         width : {
50221             title: "Width",
50222             width: 40
50223         }
50224     },
50225     'LABEL' : {
50226         'for' : {
50227             title: "For",
50228             width: 120
50229         }
50230     },
50231     'TEXTAREA' : {
50232           name : {
50233             title: "name",
50234             width: 120
50235         },
50236         rows : {
50237             title: "Rows",
50238             width: 20
50239         },
50240         cols : {
50241             title: "Cols",
50242             width: 20
50243         }
50244     },
50245     'SELECT' : {
50246         name : {
50247             title: "name",
50248             width: 120
50249         },
50250         selectoptions : {
50251             title: "Options",
50252             width: 200
50253         }
50254     },
50255     
50256     // should we really allow this??
50257     // should this just be 
50258     'BODY' : {
50259         title : {
50260             title: "Title",
50261             width: 200,
50262             disabled : true
50263         }
50264     },
50265     'SPAN' : {
50266         'font-family'  : {
50267             title : "Font",
50268             style : 'fontFamily',
50269             displayField: 'display',
50270             optname : 'font-family',
50271             width: 140
50272         }
50273     },
50274     'DIV' : {
50275         'font-family'  : {
50276             title : "Font",
50277             style : 'fontFamily',
50278             displayField: 'display',
50279             optname : 'font-family',
50280             width: 140
50281         }
50282     },
50283      'P' : {
50284         'font-family'  : {
50285             title : "Font",
50286             style : 'fontFamily',
50287             displayField: 'display',
50288             optname : 'font-family',
50289             width: 140
50290         }
50291     },
50292     
50293     '*' : {
50294         // empty..
50295     }
50296
50297 };
50298
50299 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
50300 Roo.form.HtmlEditor.ToolbarContext.stores = false;
50301
50302 Roo.form.HtmlEditor.ToolbarContext.options = {
50303         'font-family'  : [ 
50304                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
50305                 [ 'Courier New', 'Courier New'],
50306                 [ 'Tahoma', 'Tahoma'],
50307                 [ 'Times New Roman,serif', 'Times'],
50308                 [ 'Verdana','Verdana' ]
50309         ]
50310 };
50311
50312 // fixme - these need to be configurable..
50313  
50314
50315 //Roo.form.HtmlEditor.ToolbarContext.types
50316
50317
50318 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
50319     
50320     tb: false,
50321     
50322     rendered: false,
50323     
50324     editor : false,
50325     editorcore : false,
50326     /**
50327      * @cfg {Object} disable  List of toolbar elements to disable
50328          
50329      */
50330     disable : false,
50331     /**
50332      * @cfg {Object} styles List of styles 
50333      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
50334      *
50335      * These must be defined in the page, so they get rendered correctly..
50336      * .headline { }
50337      * TD.underline { }
50338      * 
50339      */
50340     styles : false,
50341     
50342     options: false,
50343     
50344     toolbars : false,
50345     
50346     init : function(editor)
50347     {
50348         this.editor = editor;
50349         this.editorcore = editor.editorcore ? editor.editorcore : editor;
50350         var editorcore = this.editorcore;
50351         
50352         var fid = editorcore.frameId;
50353         var etb = this;
50354         function btn(id, toggle, handler){
50355             var xid = fid + '-'+ id ;
50356             return {
50357                 id : xid,
50358                 cmd : id,
50359                 cls : 'x-btn-icon x-edit-'+id,
50360                 enableToggle:toggle !== false,
50361                 scope: editorcore, // was editor...
50362                 handler:handler||editorcore.relayBtnCmd,
50363                 clickEvent:'mousedown',
50364                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50365                 tabIndex:-1
50366             };
50367         }
50368         // create a new element.
50369         var wdiv = editor.wrap.createChild({
50370                 tag: 'div'
50371             }, editor.wrap.dom.firstChild.nextSibling, true);
50372         
50373         // can we do this more than once??
50374         
50375          // stop form submits
50376       
50377  
50378         // disable everything...
50379         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
50380         this.toolbars = {};
50381            
50382         for (var i in  ty) {
50383           
50384             this.toolbars[i] = this.buildToolbar(ty[i],i);
50385         }
50386         this.tb = this.toolbars.BODY;
50387         this.tb.el.show();
50388         this.buildFooter();
50389         this.footer.show();
50390         editor.on('hide', function( ) { this.footer.hide() }, this);
50391         editor.on('show', function( ) { this.footer.show() }, this);
50392         
50393          
50394         this.rendered = true;
50395         
50396         // the all the btns;
50397         editor.on('editorevent', this.updateToolbar, this);
50398         // other toolbars need to implement this..
50399         //editor.on('editmodechange', this.updateToolbar, this);
50400     },
50401     
50402     
50403     
50404     /**
50405      * Protected method that will not generally be called directly. It triggers
50406      * a toolbar update by reading the markup state of the current selection in the editor.
50407      *
50408      * Note you can force an update by calling on('editorevent', scope, false)
50409      */
50410     updateToolbar: function(editor,ev,sel){
50411
50412         //Roo.log(ev);
50413         // capture mouse up - this is handy for selecting images..
50414         // perhaps should go somewhere else...
50415         if(!this.editorcore.activated){
50416              this.editor.onFirstFocus();
50417             return;
50418         }
50419         
50420         
50421         
50422         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
50423         // selectNode - might want to handle IE?
50424         if (ev &&
50425             (ev.type == 'mouseup' || ev.type == 'click' ) &&
50426             ev.target && ev.target.tagName == 'IMG') {
50427             // they have click on an image...
50428             // let's see if we can change the selection...
50429             sel = ev.target;
50430          
50431               var nodeRange = sel.ownerDocument.createRange();
50432             try {
50433                 nodeRange.selectNode(sel);
50434             } catch (e) {
50435                 nodeRange.selectNodeContents(sel);
50436             }
50437             //nodeRange.collapse(true);
50438             var s = this.editorcore.win.getSelection();
50439             s.removeAllRanges();
50440             s.addRange(nodeRange);
50441         }  
50442         
50443       
50444         var updateFooter = sel ? false : true;
50445         
50446         
50447         var ans = this.editorcore.getAllAncestors();
50448         
50449         // pick
50450         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
50451         
50452         if (!sel) { 
50453             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
50454             sel = sel ? sel : this.editorcore.doc.body;
50455             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
50456             
50457         }
50458         // pick a menu that exists..
50459         var tn = sel.tagName.toUpperCase();
50460         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
50461         
50462         tn = sel.tagName.toUpperCase();
50463         
50464         var lastSel = this.tb.selectedNode;
50465         
50466         this.tb.selectedNode = sel;
50467         
50468         // if current menu does not match..
50469         
50470         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
50471                 
50472             this.tb.el.hide();
50473             ///console.log("show: " + tn);
50474             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
50475             this.tb.el.show();
50476             // update name
50477             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
50478             
50479             
50480             // update attributes
50481             if (this.tb.fields) {
50482                 this.tb.fields.each(function(e) {
50483                     if (e.stylename) {
50484                         e.setValue(sel.style[e.stylename]);
50485                         return;
50486                     } 
50487                    e.setValue(sel.getAttribute(e.attrname));
50488                 });
50489             }
50490             
50491             var hasStyles = false;
50492             for(var i in this.styles) {
50493                 hasStyles = true;
50494                 break;
50495             }
50496             
50497             // update styles
50498             if (hasStyles) { 
50499                 var st = this.tb.fields.item(0);
50500                 
50501                 st.store.removeAll();
50502                
50503                 
50504                 var cn = sel.className.split(/\s+/);
50505                 
50506                 var avs = [];
50507                 if (this.styles['*']) {
50508                     
50509                     Roo.each(this.styles['*'], function(v) {
50510                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
50511                     });
50512                 }
50513                 if (this.styles[tn]) { 
50514                     Roo.each(this.styles[tn], function(v) {
50515                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
50516                     });
50517                 }
50518                 
50519                 st.store.loadData(avs);
50520                 st.collapse();
50521                 st.setValue(cn);
50522             }
50523             // flag our selected Node.
50524             this.tb.selectedNode = sel;
50525            
50526            
50527             Roo.menu.MenuMgr.hideAll();
50528
50529         }
50530         
50531         if (!updateFooter) {
50532             //this.footDisp.dom.innerHTML = ''; 
50533             return;
50534         }
50535         // update the footer
50536         //
50537         var html = '';
50538         
50539         this.footerEls = ans.reverse();
50540         Roo.each(this.footerEls, function(a,i) {
50541             if (!a) { return; }
50542             html += html.length ? ' &gt; '  :  '';
50543             
50544             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
50545             
50546         });
50547        
50548         // 
50549         var sz = this.footDisp.up('td').getSize();
50550         this.footDisp.dom.style.width = (sz.width -10) + 'px';
50551         this.footDisp.dom.style.marginLeft = '5px';
50552         
50553         this.footDisp.dom.style.overflow = 'hidden';
50554         
50555         this.footDisp.dom.innerHTML = html;
50556             
50557         //this.editorsyncValue();
50558     },
50559      
50560     
50561    
50562        
50563     // private
50564     onDestroy : function(){
50565         if(this.rendered){
50566             
50567             this.tb.items.each(function(item){
50568                 if(item.menu){
50569                     item.menu.removeAll();
50570                     if(item.menu.el){
50571                         item.menu.el.destroy();
50572                     }
50573                 }
50574                 item.destroy();
50575             });
50576              
50577         }
50578     },
50579     onFirstFocus: function() {
50580         // need to do this for all the toolbars..
50581         this.tb.items.each(function(item){
50582            item.enable();
50583         });
50584     },
50585     buildToolbar: function(tlist, nm)
50586     {
50587         var editor = this.editor;
50588         var editorcore = this.editorcore;
50589          // create a new element.
50590         var wdiv = editor.wrap.createChild({
50591                 tag: 'div'
50592             }, editor.wrap.dom.firstChild.nextSibling, true);
50593         
50594        
50595         var tb = new Roo.Toolbar(wdiv);
50596         // add the name..
50597         
50598         tb.add(nm+ ":&nbsp;");
50599         
50600         var styles = [];
50601         for(var i in this.styles) {
50602             styles.push(i);
50603         }
50604         
50605         // styles...
50606         if (styles && styles.length) {
50607             
50608             // this needs a multi-select checkbox...
50609             tb.addField( new Roo.form.ComboBox({
50610                 store: new Roo.data.SimpleStore({
50611                     id : 'val',
50612                     fields: ['val', 'selected'],
50613                     data : [] 
50614                 }),
50615                 name : '-roo-edit-className',
50616                 attrname : 'className',
50617                 displayField: 'val',
50618                 typeAhead: false,
50619                 mode: 'local',
50620                 editable : false,
50621                 triggerAction: 'all',
50622                 emptyText:'Select Style',
50623                 selectOnFocus:true,
50624                 width: 130,
50625                 listeners : {
50626                     'select': function(c, r, i) {
50627                         // initial support only for on class per el..
50628                         tb.selectedNode.className =  r ? r.get('val') : '';
50629                         editorcore.syncValue();
50630                     }
50631                 }
50632     
50633             }));
50634         }
50635         
50636         var tbc = Roo.form.HtmlEditor.ToolbarContext;
50637         var tbops = tbc.options;
50638         
50639         for (var i in tlist) {
50640             
50641             var item = tlist[i];
50642             tb.add(item.title + ":&nbsp;");
50643             
50644             
50645             //optname == used so you can configure the options available..
50646             var opts = item.opts ? item.opts : false;
50647             if (item.optname) {
50648                 opts = tbops[item.optname];
50649            
50650             }
50651             
50652             if (opts) {
50653                 // opts == pulldown..
50654                 tb.addField( new Roo.form.ComboBox({
50655                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
50656                         id : 'val',
50657                         fields: ['val', 'display'],
50658                         data : opts  
50659                     }),
50660                     name : '-roo-edit-' + i,
50661                     attrname : i,
50662                     stylename : item.style ? item.style : false,
50663                     displayField: item.displayField ? item.displayField : 'val',
50664                     valueField :  'val',
50665                     typeAhead: false,
50666                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
50667                     editable : false,
50668                     triggerAction: 'all',
50669                     emptyText:'Select',
50670                     selectOnFocus:true,
50671                     width: item.width ? item.width  : 130,
50672                     listeners : {
50673                         'select': function(c, r, i) {
50674                             if (c.stylename) {
50675                                 tb.selectedNode.style[c.stylename] =  r.get('val');
50676                                 return;
50677                             }
50678                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
50679                         }
50680                     }
50681
50682                 }));
50683                 continue;
50684                     
50685                  
50686                 
50687                 tb.addField( new Roo.form.TextField({
50688                     name: i,
50689                     width: 100,
50690                     //allowBlank:false,
50691                     value: ''
50692                 }));
50693                 continue;
50694             }
50695             tb.addField( new Roo.form.TextField({
50696                 name: '-roo-edit-' + i,
50697                 attrname : i,
50698                 
50699                 width: item.width,
50700                 //allowBlank:true,
50701                 value: '',
50702                 listeners: {
50703                     'change' : function(f, nv, ov) {
50704                         tb.selectedNode.setAttribute(f.attrname, nv);
50705                         editorcore.syncValue();
50706                     }
50707                 }
50708             }));
50709              
50710         }
50711         
50712         var _this = this;
50713         
50714         if(nm == 'BODY'){
50715             tb.addSeparator();
50716         
50717             tb.addButton( {
50718                 text: 'Stylesheets',
50719
50720                 listeners : {
50721                     click : function ()
50722                     {
50723                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
50724                     }
50725                 }
50726             });
50727         }
50728         
50729         tb.addFill();
50730         tb.addButton( {
50731             text: 'Remove Tag',
50732     
50733             listeners : {
50734                 click : function ()
50735                 {
50736                     // remove
50737                     // undo does not work.
50738                      
50739                     var sn = tb.selectedNode;
50740                     
50741                     var pn = sn.parentNode;
50742                     
50743                     var stn =  sn.childNodes[0];
50744                     var en = sn.childNodes[sn.childNodes.length - 1 ];
50745                     while (sn.childNodes.length) {
50746                         var node = sn.childNodes[0];
50747                         sn.removeChild(node);
50748                         //Roo.log(node);
50749                         pn.insertBefore(node, sn);
50750                         
50751                     }
50752                     pn.removeChild(sn);
50753                     var range = editorcore.createRange();
50754         
50755                     range.setStart(stn,0);
50756                     range.setEnd(en,0); //????
50757                     //range.selectNode(sel);
50758                     
50759                     
50760                     var selection = editorcore.getSelection();
50761                     selection.removeAllRanges();
50762                     selection.addRange(range);
50763                     
50764                     
50765                     
50766                     //_this.updateToolbar(null, null, pn);
50767                     _this.updateToolbar(null, null, null);
50768                     _this.footDisp.dom.innerHTML = ''; 
50769                 }
50770             }
50771             
50772                     
50773                 
50774             
50775         });
50776         
50777         
50778         tb.el.on('click', function(e){
50779             e.preventDefault(); // what does this do?
50780         });
50781         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
50782         tb.el.hide();
50783         tb.name = nm;
50784         // dont need to disable them... as they will get hidden
50785         return tb;
50786          
50787         
50788     },
50789     buildFooter : function()
50790     {
50791         
50792         var fel = this.editor.wrap.createChild();
50793         this.footer = new Roo.Toolbar(fel);
50794         // toolbar has scrolly on left / right?
50795         var footDisp= new Roo.Toolbar.Fill();
50796         var _t = this;
50797         this.footer.add(
50798             {
50799                 text : '&lt;',
50800                 xtype: 'Button',
50801                 handler : function() {
50802                     _t.footDisp.scrollTo('left',0,true)
50803                 }
50804             }
50805         );
50806         this.footer.add( footDisp );
50807         this.footer.add( 
50808             {
50809                 text : '&gt;',
50810                 xtype: 'Button',
50811                 handler : function() {
50812                     // no animation..
50813                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
50814                 }
50815             }
50816         );
50817         var fel = Roo.get(footDisp.el);
50818         fel.addClass('x-editor-context');
50819         this.footDispWrap = fel; 
50820         this.footDispWrap.overflow  = 'hidden';
50821         
50822         this.footDisp = fel.createChild();
50823         this.footDispWrap.on('click', this.onContextClick, this)
50824         
50825         
50826     },
50827     onContextClick : function (ev,dom)
50828     {
50829         ev.preventDefault();
50830         var  cn = dom.className;
50831         //Roo.log(cn);
50832         if (!cn.match(/x-ed-loc-/)) {
50833             return;
50834         }
50835         var n = cn.split('-').pop();
50836         var ans = this.footerEls;
50837         var sel = ans[n];
50838         
50839          // pick
50840         var range = this.editorcore.createRange();
50841         
50842         range.selectNodeContents(sel);
50843         //range.selectNode(sel);
50844         
50845         
50846         var selection = this.editorcore.getSelection();
50847         selection.removeAllRanges();
50848         selection.addRange(range);
50849         
50850         
50851         
50852         this.updateToolbar(null, null, sel);
50853         
50854         
50855     }
50856     
50857     
50858     
50859     
50860     
50861 });
50862
50863
50864
50865
50866
50867 /*
50868  * Based on:
50869  * Ext JS Library 1.1.1
50870  * Copyright(c) 2006-2007, Ext JS, LLC.
50871  *
50872  * Originally Released Under LGPL - original licence link has changed is not relivant.
50873  *
50874  * Fork - LGPL
50875  * <script type="text/javascript">
50876  */
50877  
50878 /**
50879  * @class Roo.form.BasicForm
50880  * @extends Roo.util.Observable
50881  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
50882  * @constructor
50883  * @param {String/HTMLElement/Roo.Element} el The form element or its id
50884  * @param {Object} config Configuration options
50885  */
50886 Roo.form.BasicForm = function(el, config){
50887     this.allItems = [];
50888     this.childForms = [];
50889     Roo.apply(this, config);
50890     /*
50891      * The Roo.form.Field items in this form.
50892      * @type MixedCollection
50893      */
50894      
50895      
50896     this.items = new Roo.util.MixedCollection(false, function(o){
50897         return o.id || (o.id = Roo.id());
50898     });
50899     this.addEvents({
50900         /**
50901          * @event beforeaction
50902          * Fires before any action is performed. Return false to cancel the action.
50903          * @param {Form} this
50904          * @param {Action} action The action to be performed
50905          */
50906         beforeaction: true,
50907         /**
50908          * @event actionfailed
50909          * Fires when an action fails.
50910          * @param {Form} this
50911          * @param {Action} action The action that failed
50912          */
50913         actionfailed : true,
50914         /**
50915          * @event actioncomplete
50916          * Fires when an action is completed.
50917          * @param {Form} this
50918          * @param {Action} action The action that completed
50919          */
50920         actioncomplete : true
50921     });
50922     if(el){
50923         this.initEl(el);
50924     }
50925     Roo.form.BasicForm.superclass.constructor.call(this);
50926 };
50927
50928 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
50929     /**
50930      * @cfg {String} method
50931      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
50932      */
50933     /**
50934      * @cfg {DataReader} reader
50935      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
50936      * This is optional as there is built-in support for processing JSON.
50937      */
50938     /**
50939      * @cfg {DataReader} errorReader
50940      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
50941      * This is completely optional as there is built-in support for processing JSON.
50942      */
50943     /**
50944      * @cfg {String} url
50945      * The URL to use for form actions if one isn't supplied in the action options.
50946      */
50947     /**
50948      * @cfg {Boolean} fileUpload
50949      * Set to true if this form is a file upload.
50950      */
50951      
50952     /**
50953      * @cfg {Object} baseParams
50954      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
50955      */
50956      /**
50957      
50958     /**
50959      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
50960      */
50961     timeout: 30,
50962
50963     // private
50964     activeAction : null,
50965
50966     /**
50967      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
50968      * or setValues() data instead of when the form was first created.
50969      */
50970     trackResetOnLoad : false,
50971     
50972     
50973     /**
50974      * childForms - used for multi-tab forms
50975      * @type {Array}
50976      */
50977     childForms : false,
50978     
50979     /**
50980      * allItems - full list of fields.
50981      * @type {Array}
50982      */
50983     allItems : false,
50984     
50985     /**
50986      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50987      * element by passing it or its id or mask the form itself by passing in true.
50988      * @type Mixed
50989      */
50990     waitMsgTarget : false,
50991
50992     // private
50993     initEl : function(el){
50994         this.el = Roo.get(el);
50995         this.id = this.el.id || Roo.id();
50996         this.el.on('submit', this.onSubmit, this);
50997         this.el.addClass('x-form');
50998     },
50999
51000     // private
51001     onSubmit : function(e){
51002         e.stopEvent();
51003     },
51004
51005     /**
51006      * Returns true if client-side validation on the form is successful.
51007      * @return Boolean
51008      */
51009     isValid : function(){
51010         var valid = true;
51011         this.items.each(function(f){
51012            if(!f.validate()){
51013                valid = false;
51014            }
51015         });
51016         return valid;
51017     },
51018
51019     /**
51020      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
51021      * @return Boolean
51022      */
51023     isDirty : function(){
51024         var dirty = false;
51025         this.items.each(function(f){
51026            if(f.isDirty()){
51027                dirty = true;
51028                return false;
51029            }
51030         });
51031         return dirty;
51032     },
51033     
51034     /**
51035      * Returns true if any fields in this form have changed since their original load. (New version)
51036      * @return Boolean
51037      */
51038     
51039     hasChanged : function()
51040     {
51041         var dirty = false;
51042         this.items.each(function(f){
51043            if(f.hasChanged()){
51044                dirty = true;
51045                return false;
51046            }
51047         });
51048         return dirty;
51049         
51050     },
51051     /**
51052      * Resets all hasChanged to 'false' -
51053      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
51054      * So hasChanged storage is only to be used for this purpose
51055      * @return Boolean
51056      */
51057     resetHasChanged : function()
51058     {
51059         this.items.each(function(f){
51060            f.resetHasChanged();
51061         });
51062         
51063     },
51064     
51065     
51066     /**
51067      * Performs a predefined action (submit or load) or custom actions you define on this form.
51068      * @param {String} actionName The name of the action type
51069      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
51070      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
51071      * accept other config options):
51072      * <pre>
51073 Property          Type             Description
51074 ----------------  ---------------  ----------------------------------------------------------------------------------
51075 url               String           The url for the action (defaults to the form's url)
51076 method            String           The form method to use (defaults to the form's method, or POST if not defined)
51077 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
51078 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
51079                                    validate the form on the client (defaults to false)
51080      * </pre>
51081      * @return {BasicForm} this
51082      */
51083     doAction : function(action, options){
51084         if(typeof action == 'string'){
51085             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
51086         }
51087         if(this.fireEvent('beforeaction', this, action) !== false){
51088             this.beforeAction(action);
51089             action.run.defer(100, action);
51090         }
51091         return this;
51092     },
51093
51094     /**
51095      * Shortcut to do a submit action.
51096      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
51097      * @return {BasicForm} this
51098      */
51099     submit : function(options){
51100         this.doAction('submit', options);
51101         return this;
51102     },
51103
51104     /**
51105      * Shortcut to do a load action.
51106      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
51107      * @return {BasicForm} this
51108      */
51109     load : function(options){
51110         this.doAction('load', options);
51111         return this;
51112     },
51113
51114     /**
51115      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
51116      * @param {Record} record The record to edit
51117      * @return {BasicForm} this
51118      */
51119     updateRecord : function(record){
51120         record.beginEdit();
51121         var fs = record.fields;
51122         fs.each(function(f){
51123             var field = this.findField(f.name);
51124             if(field){
51125                 record.set(f.name, field.getValue());
51126             }
51127         }, this);
51128         record.endEdit();
51129         return this;
51130     },
51131
51132     /**
51133      * Loads an Roo.data.Record into this form.
51134      * @param {Record} record The record to load
51135      * @return {BasicForm} this
51136      */
51137     loadRecord : function(record){
51138         this.setValues(record.data);
51139         return this;
51140     },
51141
51142     // private
51143     beforeAction : function(action){
51144         var o = action.options;
51145         
51146        
51147         if(this.waitMsgTarget === true){
51148             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
51149         }else if(this.waitMsgTarget){
51150             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
51151             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
51152         }else {
51153             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
51154         }
51155          
51156     },
51157
51158     // private
51159     afterAction : function(action, success){
51160         this.activeAction = null;
51161         var o = action.options;
51162         
51163         if(this.waitMsgTarget === true){
51164             this.el.unmask();
51165         }else if(this.waitMsgTarget){
51166             this.waitMsgTarget.unmask();
51167         }else{
51168             Roo.MessageBox.updateProgress(1);
51169             Roo.MessageBox.hide();
51170         }
51171          
51172         if(success){
51173             if(o.reset){
51174                 this.reset();
51175             }
51176             Roo.callback(o.success, o.scope, [this, action]);
51177             this.fireEvent('actioncomplete', this, action);
51178             
51179         }else{
51180             
51181             // failure condition..
51182             // we have a scenario where updates need confirming.
51183             // eg. if a locking scenario exists..
51184             // we look for { errors : { needs_confirm : true }} in the response.
51185             if (
51186                 (typeof(action.result) != 'undefined')  &&
51187                 (typeof(action.result.errors) != 'undefined')  &&
51188                 (typeof(action.result.errors.needs_confirm) != 'undefined')
51189            ){
51190                 var _t = this;
51191                 Roo.MessageBox.confirm(
51192                     "Change requires confirmation",
51193                     action.result.errorMsg,
51194                     function(r) {
51195                         if (r != 'yes') {
51196                             return;
51197                         }
51198                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
51199                     }
51200                     
51201                 );
51202                 
51203                 
51204                 
51205                 return;
51206             }
51207             
51208             Roo.callback(o.failure, o.scope, [this, action]);
51209             // show an error message if no failed handler is set..
51210             if (!this.hasListener('actionfailed')) {
51211                 Roo.MessageBox.alert("Error",
51212                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
51213                         action.result.errorMsg :
51214                         "Saving Failed, please check your entries or try again"
51215                 );
51216             }
51217             
51218             this.fireEvent('actionfailed', this, action);
51219         }
51220         
51221     },
51222
51223     /**
51224      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
51225      * @param {String} id The value to search for
51226      * @return Field
51227      */
51228     findField : function(id){
51229         var field = this.items.get(id);
51230         if(!field){
51231             this.items.each(function(f){
51232                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
51233                     field = f;
51234                     return false;
51235                 }
51236             });
51237         }
51238         return field || null;
51239     },
51240
51241     /**
51242      * Add a secondary form to this one, 
51243      * Used to provide tabbed forms. One form is primary, with hidden values 
51244      * which mirror the elements from the other forms.
51245      * 
51246      * @param {Roo.form.Form} form to add.
51247      * 
51248      */
51249     addForm : function(form)
51250     {
51251        
51252         if (this.childForms.indexOf(form) > -1) {
51253             // already added..
51254             return;
51255         }
51256         this.childForms.push(form);
51257         var n = '';
51258         Roo.each(form.allItems, function (fe) {
51259             
51260             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
51261             if (this.findField(n)) { // already added..
51262                 return;
51263             }
51264             var add = new Roo.form.Hidden({
51265                 name : n
51266             });
51267             add.render(this.el);
51268             
51269             this.add( add );
51270         }, this);
51271         
51272     },
51273     /**
51274      * Mark fields in this form invalid in bulk.
51275      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
51276      * @return {BasicForm} this
51277      */
51278     markInvalid : function(errors){
51279         if(errors instanceof Array){
51280             for(var i = 0, len = errors.length; i < len; i++){
51281                 var fieldError = errors[i];
51282                 var f = this.findField(fieldError.id);
51283                 if(f){
51284                     f.markInvalid(fieldError.msg);
51285                 }
51286             }
51287         }else{
51288             var field, id;
51289             for(id in errors){
51290                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
51291                     field.markInvalid(errors[id]);
51292                 }
51293             }
51294         }
51295         Roo.each(this.childForms || [], function (f) {
51296             f.markInvalid(errors);
51297         });
51298         
51299         return this;
51300     },
51301
51302     /**
51303      * Set values for fields in this form in bulk.
51304      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
51305      * @return {BasicForm} this
51306      */
51307     setValues : function(values){
51308         if(values instanceof Array){ // array of objects
51309             for(var i = 0, len = values.length; i < len; i++){
51310                 var v = values[i];
51311                 var f = this.findField(v.id);
51312                 if(f){
51313                     f.setValue(v.value);
51314                     if(this.trackResetOnLoad){
51315                         f.originalValue = f.getValue();
51316                     }
51317                 }
51318             }
51319         }else{ // object hash
51320             var field, id;
51321             for(id in values){
51322                 if(typeof values[id] != 'function' && (field = this.findField(id))){
51323                     
51324                     if (field.setFromData && 
51325                         field.valueField && 
51326                         field.displayField &&
51327                         // combos' with local stores can 
51328                         // be queried via setValue()
51329                         // to set their value..
51330                         (field.store && !field.store.isLocal)
51331                         ) {
51332                         // it's a combo
51333                         var sd = { };
51334                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
51335                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
51336                         field.setFromData(sd);
51337                         
51338                     } else {
51339                         field.setValue(values[id]);
51340                     }
51341                     
51342                     
51343                     if(this.trackResetOnLoad){
51344                         field.originalValue = field.getValue();
51345                     }
51346                 }
51347             }
51348         }
51349         this.resetHasChanged();
51350         
51351         
51352         Roo.each(this.childForms || [], function (f) {
51353             f.setValues(values);
51354             f.resetHasChanged();
51355         });
51356                 
51357         return this;
51358     },
51359
51360     /**
51361      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
51362      * they are returned as an array.
51363      * @param {Boolean} asString
51364      * @return {Object}
51365      */
51366     getValues : function(asString){
51367         if (this.childForms) {
51368             // copy values from the child forms
51369             Roo.each(this.childForms, function (f) {
51370                 this.setValues(f.getValues());
51371             }, this);
51372         }
51373         
51374         
51375         
51376         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
51377         if(asString === true){
51378             return fs;
51379         }
51380         return Roo.urlDecode(fs);
51381     },
51382     
51383     /**
51384      * Returns the fields in this form as an object with key/value pairs. 
51385      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
51386      * @return {Object}
51387      */
51388     getFieldValues : function(with_hidden)
51389     {
51390         if (this.childForms) {
51391             // copy values from the child forms
51392             // should this call getFieldValues - probably not as we do not currently copy
51393             // hidden fields when we generate..
51394             Roo.each(this.childForms, function (f) {
51395                 this.setValues(f.getValues());
51396             }, this);
51397         }
51398         
51399         var ret = {};
51400         this.items.each(function(f){
51401             if (!f.getName()) {
51402                 return;
51403             }
51404             var v = f.getValue();
51405             if (f.inputType =='radio') {
51406                 if (typeof(ret[f.getName()]) == 'undefined') {
51407                     ret[f.getName()] = ''; // empty..
51408                 }
51409                 
51410                 if (!f.el.dom.checked) {
51411                     return;
51412                     
51413                 }
51414                 v = f.el.dom.value;
51415                 
51416             }
51417             
51418             // not sure if this supported any more..
51419             if ((typeof(v) == 'object') && f.getRawValue) {
51420                 v = f.getRawValue() ; // dates..
51421             }
51422             // combo boxes where name != hiddenName...
51423             if (f.name != f.getName()) {
51424                 ret[f.name] = f.getRawValue();
51425             }
51426             ret[f.getName()] = v;
51427         });
51428         
51429         return ret;
51430     },
51431
51432     /**
51433      * Clears all invalid messages in this form.
51434      * @return {BasicForm} this
51435      */
51436     clearInvalid : function(){
51437         this.items.each(function(f){
51438            f.clearInvalid();
51439         });
51440         
51441         Roo.each(this.childForms || [], function (f) {
51442             f.clearInvalid();
51443         });
51444         
51445         
51446         return this;
51447     },
51448
51449     /**
51450      * Resets this form.
51451      * @return {BasicForm} this
51452      */
51453     reset : function(){
51454         this.items.each(function(f){
51455             f.reset();
51456         });
51457         
51458         Roo.each(this.childForms || [], function (f) {
51459             f.reset();
51460         });
51461         this.resetHasChanged();
51462         
51463         return this;
51464     },
51465
51466     /**
51467      * Add Roo.form components to this form.
51468      * @param {Field} field1
51469      * @param {Field} field2 (optional)
51470      * @param {Field} etc (optional)
51471      * @return {BasicForm} this
51472      */
51473     add : function(){
51474         this.items.addAll(Array.prototype.slice.call(arguments, 0));
51475         return this;
51476     },
51477
51478
51479     /**
51480      * Removes a field from the items collection (does NOT remove its markup).
51481      * @param {Field} field
51482      * @return {BasicForm} this
51483      */
51484     remove : function(field){
51485         this.items.remove(field);
51486         return this;
51487     },
51488
51489     /**
51490      * Looks at the fields in this form, checks them for an id attribute,
51491      * and calls applyTo on the existing dom element with that id.
51492      * @return {BasicForm} this
51493      */
51494     render : function(){
51495         this.items.each(function(f){
51496             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
51497                 f.applyTo(f.id);
51498             }
51499         });
51500         return this;
51501     },
51502
51503     /**
51504      * Calls {@link Ext#apply} for all fields in this form with the passed object.
51505      * @param {Object} values
51506      * @return {BasicForm} this
51507      */
51508     applyToFields : function(o){
51509         this.items.each(function(f){
51510            Roo.apply(f, o);
51511         });
51512         return this;
51513     },
51514
51515     /**
51516      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
51517      * @param {Object} values
51518      * @return {BasicForm} this
51519      */
51520     applyIfToFields : function(o){
51521         this.items.each(function(f){
51522            Roo.applyIf(f, o);
51523         });
51524         return this;
51525     }
51526 });
51527
51528 // back compat
51529 Roo.BasicForm = Roo.form.BasicForm;/*
51530  * Based on:
51531  * Ext JS Library 1.1.1
51532  * Copyright(c) 2006-2007, Ext JS, LLC.
51533  *
51534  * Originally Released Under LGPL - original licence link has changed is not relivant.
51535  *
51536  * Fork - LGPL
51537  * <script type="text/javascript">
51538  */
51539
51540 /**
51541  * @class Roo.form.Form
51542  * @extends Roo.form.BasicForm
51543  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
51544  * @constructor
51545  * @param {Object} config Configuration options
51546  */
51547 Roo.form.Form = function(config){
51548     var xitems =  [];
51549     if (config.items) {
51550         xitems = config.items;
51551         delete config.items;
51552     }
51553    
51554     
51555     Roo.form.Form.superclass.constructor.call(this, null, config);
51556     this.url = this.url || this.action;
51557     if(!this.root){
51558         this.root = new Roo.form.Layout(Roo.applyIf({
51559             id: Roo.id()
51560         }, config));
51561     }
51562     this.active = this.root;
51563     /**
51564      * Array of all the buttons that have been added to this form via {@link addButton}
51565      * @type Array
51566      */
51567     this.buttons = [];
51568     this.allItems = [];
51569     this.addEvents({
51570         /**
51571          * @event clientvalidation
51572          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
51573          * @param {Form} this
51574          * @param {Boolean} valid true if the form has passed client-side validation
51575          */
51576         clientvalidation: true,
51577         /**
51578          * @event rendered
51579          * Fires when the form is rendered
51580          * @param {Roo.form.Form} form
51581          */
51582         rendered : true
51583     });
51584     
51585     if (this.progressUrl) {
51586             // push a hidden field onto the list of fields..
51587             this.addxtype( {
51588                     xns: Roo.form, 
51589                     xtype : 'Hidden', 
51590                     name : 'UPLOAD_IDENTIFIER' 
51591             });
51592         }
51593         
51594     
51595     Roo.each(xitems, this.addxtype, this);
51596     
51597     
51598     
51599 };
51600
51601 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
51602     /**
51603      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
51604      */
51605     /**
51606      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
51607      */
51608     /**
51609      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
51610      */
51611     buttonAlign:'center',
51612
51613     /**
51614      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
51615      */
51616     minButtonWidth:75,
51617
51618     /**
51619      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
51620      * This property cascades to child containers if not set.
51621      */
51622     labelAlign:'left',
51623
51624     /**
51625      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
51626      * fires a looping event with that state. This is required to bind buttons to the valid
51627      * state using the config value formBind:true on the button.
51628      */
51629     monitorValid : false,
51630
51631     /**
51632      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
51633      */
51634     monitorPoll : 200,
51635     
51636     /**
51637      * @cfg {String} progressUrl - Url to return progress data 
51638      */
51639     
51640     progressUrl : false,
51641   
51642     /**
51643      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
51644      * fields are added and the column is closed. If no fields are passed the column remains open
51645      * until end() is called.
51646      * @param {Object} config The config to pass to the column
51647      * @param {Field} field1 (optional)
51648      * @param {Field} field2 (optional)
51649      * @param {Field} etc (optional)
51650      * @return Column The column container object
51651      */
51652     column : function(c){
51653         var col = new Roo.form.Column(c);
51654         this.start(col);
51655         if(arguments.length > 1){ // duplicate code required because of Opera
51656             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51657             this.end();
51658         }
51659         return col;
51660     },
51661
51662     /**
51663      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
51664      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
51665      * until end() is called.
51666      * @param {Object} config The config to pass to the fieldset
51667      * @param {Field} field1 (optional)
51668      * @param {Field} field2 (optional)
51669      * @param {Field} etc (optional)
51670      * @return FieldSet The fieldset container object
51671      */
51672     fieldset : function(c){
51673         var fs = new Roo.form.FieldSet(c);
51674         this.start(fs);
51675         if(arguments.length > 1){ // duplicate code required because of Opera
51676             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51677             this.end();
51678         }
51679         return fs;
51680     },
51681
51682     /**
51683      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
51684      * fields are added and the container is closed. If no fields are passed the container remains open
51685      * until end() is called.
51686      * @param {Object} config The config to pass to the Layout
51687      * @param {Field} field1 (optional)
51688      * @param {Field} field2 (optional)
51689      * @param {Field} etc (optional)
51690      * @return Layout The container object
51691      */
51692     container : function(c){
51693         var l = new Roo.form.Layout(c);
51694         this.start(l);
51695         if(arguments.length > 1){ // duplicate code required because of Opera
51696             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51697             this.end();
51698         }
51699         return l;
51700     },
51701
51702     /**
51703      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
51704      * @param {Object} container A Roo.form.Layout or subclass of Layout
51705      * @return {Form} this
51706      */
51707     start : function(c){
51708         // cascade label info
51709         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
51710         this.active.stack.push(c);
51711         c.ownerCt = this.active;
51712         this.active = c;
51713         return this;
51714     },
51715
51716     /**
51717      * Closes the current open container
51718      * @return {Form} this
51719      */
51720     end : function(){
51721         if(this.active == this.root){
51722             return this;
51723         }
51724         this.active = this.active.ownerCt;
51725         return this;
51726     },
51727
51728     /**
51729      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
51730      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
51731      * as the label of the field.
51732      * @param {Field} field1
51733      * @param {Field} field2 (optional)
51734      * @param {Field} etc. (optional)
51735      * @return {Form} this
51736      */
51737     add : function(){
51738         this.active.stack.push.apply(this.active.stack, arguments);
51739         this.allItems.push.apply(this.allItems,arguments);
51740         var r = [];
51741         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
51742             if(a[i].isFormField){
51743                 r.push(a[i]);
51744             }
51745         }
51746         if(r.length > 0){
51747             Roo.form.Form.superclass.add.apply(this, r);
51748         }
51749         return this;
51750     },
51751     
51752
51753     
51754     
51755     
51756      /**
51757      * Find any element that has been added to a form, using it's ID or name
51758      * This can include framesets, columns etc. along with regular fields..
51759      * @param {String} id - id or name to find.
51760      
51761      * @return {Element} e - or false if nothing found.
51762      */
51763     findbyId : function(id)
51764     {
51765         var ret = false;
51766         if (!id) {
51767             return ret;
51768         }
51769         Roo.each(this.allItems, function(f){
51770             if (f.id == id || f.name == id ){
51771                 ret = f;
51772                 return false;
51773             }
51774         });
51775         return ret;
51776     },
51777
51778     
51779     
51780     /**
51781      * Render this form into the passed container. This should only be called once!
51782      * @param {String/HTMLElement/Element} container The element this component should be rendered into
51783      * @return {Form} this
51784      */
51785     render : function(ct)
51786     {
51787         
51788         
51789         
51790         ct = Roo.get(ct);
51791         var o = this.autoCreate || {
51792             tag: 'form',
51793             method : this.method || 'POST',
51794             id : this.id || Roo.id()
51795         };
51796         this.initEl(ct.createChild(o));
51797
51798         this.root.render(this.el);
51799         
51800        
51801              
51802         this.items.each(function(f){
51803             f.render('x-form-el-'+f.id);
51804         });
51805
51806         if(this.buttons.length > 0){
51807             // tables are required to maintain order and for correct IE layout
51808             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51809                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51810                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51811             }}, null, true);
51812             var tr = tb.getElementsByTagName('tr')[0];
51813             for(var i = 0, len = this.buttons.length; i < len; i++) {
51814                 var b = this.buttons[i];
51815                 var td = document.createElement('td');
51816                 td.className = 'x-form-btn-td';
51817                 b.render(tr.appendChild(td));
51818             }
51819         }
51820         if(this.monitorValid){ // initialize after render
51821             this.startMonitoring();
51822         }
51823         this.fireEvent('rendered', this);
51824         return this;
51825     },
51826
51827     /**
51828      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51829      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51830      * object or a valid Roo.DomHelper element config
51831      * @param {Function} handler The function called when the button is clicked
51832      * @param {Object} scope (optional) The scope of the handler function
51833      * @return {Roo.Button}
51834      */
51835     addButton : function(config, handler, scope){
51836         var bc = {
51837             handler: handler,
51838             scope: scope,
51839             minWidth: this.minButtonWidth,
51840             hideParent:true
51841         };
51842         if(typeof config == "string"){
51843             bc.text = config;
51844         }else{
51845             Roo.apply(bc, config);
51846         }
51847         var btn = new Roo.Button(null, bc);
51848         this.buttons.push(btn);
51849         return btn;
51850     },
51851
51852      /**
51853      * Adds a series of form elements (using the xtype property as the factory method.
51854      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51855      * @param {Object} config 
51856      */
51857     
51858     addxtype : function()
51859     {
51860         var ar = Array.prototype.slice.call(arguments, 0);
51861         var ret = false;
51862         for(var i = 0; i < ar.length; i++) {
51863             if (!ar[i]) {
51864                 continue; // skip -- if this happends something invalid got sent, we 
51865                 // should ignore it, as basically that interface element will not show up
51866                 // and that should be pretty obvious!!
51867             }
51868             
51869             if (Roo.form[ar[i].xtype]) {
51870                 ar[i].form = this;
51871                 var fe = Roo.factory(ar[i], Roo.form);
51872                 if (!ret) {
51873                     ret = fe;
51874                 }
51875                 fe.form = this;
51876                 if (fe.store) {
51877                     fe.store.form = this;
51878                 }
51879                 if (fe.isLayout) {  
51880                          
51881                     this.start(fe);
51882                     this.allItems.push(fe);
51883                     if (fe.items && fe.addxtype) {
51884                         fe.addxtype.apply(fe, fe.items);
51885                         delete fe.items;
51886                     }
51887                      this.end();
51888                     continue;
51889                 }
51890                 
51891                 
51892                  
51893                 this.add(fe);
51894               //  console.log('adding ' + ar[i].xtype);
51895             }
51896             if (ar[i].xtype == 'Button') {  
51897                 //console.log('adding button');
51898                 //console.log(ar[i]);
51899                 this.addButton(ar[i]);
51900                 this.allItems.push(fe);
51901                 continue;
51902             }
51903             
51904             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51905                 alert('end is not supported on xtype any more, use items');
51906             //    this.end();
51907             //    //console.log('adding end');
51908             }
51909             
51910         }
51911         return ret;
51912     },
51913     
51914     /**
51915      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51916      * option "monitorValid"
51917      */
51918     startMonitoring : function(){
51919         if(!this.bound){
51920             this.bound = true;
51921             Roo.TaskMgr.start({
51922                 run : this.bindHandler,
51923                 interval : this.monitorPoll || 200,
51924                 scope: this
51925             });
51926         }
51927     },
51928
51929     /**
51930      * Stops monitoring of the valid state of this form
51931      */
51932     stopMonitoring : function(){
51933         this.bound = false;
51934     },
51935
51936     // private
51937     bindHandler : function(){
51938         if(!this.bound){
51939             return false; // stops binding
51940         }
51941         var valid = true;
51942         this.items.each(function(f){
51943             if(!f.isValid(true)){
51944                 valid = false;
51945                 return false;
51946             }
51947         });
51948         for(var i = 0, len = this.buttons.length; i < len; i++){
51949             var btn = this.buttons[i];
51950             if(btn.formBind === true && btn.disabled === valid){
51951                 btn.setDisabled(!valid);
51952             }
51953         }
51954         this.fireEvent('clientvalidation', this, valid);
51955     }
51956     
51957     
51958     
51959     
51960     
51961     
51962     
51963     
51964 });
51965
51966
51967 // back compat
51968 Roo.Form = Roo.form.Form;
51969 /*
51970  * Based on:
51971  * Ext JS Library 1.1.1
51972  * Copyright(c) 2006-2007, Ext JS, LLC.
51973  *
51974  * Originally Released Under LGPL - original licence link has changed is not relivant.
51975  *
51976  * Fork - LGPL
51977  * <script type="text/javascript">
51978  */
51979
51980 // as we use this in bootstrap.
51981 Roo.namespace('Roo.form');
51982  /**
51983  * @class Roo.form.Action
51984  * Internal Class used to handle form actions
51985  * @constructor
51986  * @param {Roo.form.BasicForm} el The form element or its id
51987  * @param {Object} config Configuration options
51988  */
51989
51990  
51991  
51992 // define the action interface
51993 Roo.form.Action = function(form, options){
51994     this.form = form;
51995     this.options = options || {};
51996 };
51997 /**
51998  * Client Validation Failed
51999  * @const 
52000  */
52001 Roo.form.Action.CLIENT_INVALID = 'client';
52002 /**
52003  * Server Validation Failed
52004  * @const 
52005  */
52006 Roo.form.Action.SERVER_INVALID = 'server';
52007  /**
52008  * Connect to Server Failed
52009  * @const 
52010  */
52011 Roo.form.Action.CONNECT_FAILURE = 'connect';
52012 /**
52013  * Reading Data from Server Failed
52014  * @const 
52015  */
52016 Roo.form.Action.LOAD_FAILURE = 'load';
52017
52018 Roo.form.Action.prototype = {
52019     type : 'default',
52020     failureType : undefined,
52021     response : undefined,
52022     result : undefined,
52023
52024     // interface method
52025     run : function(options){
52026
52027     },
52028
52029     // interface method
52030     success : function(response){
52031
52032     },
52033
52034     // interface method
52035     handleResponse : function(response){
52036
52037     },
52038
52039     // default connection failure
52040     failure : function(response){
52041         
52042         this.response = response;
52043         this.failureType = Roo.form.Action.CONNECT_FAILURE;
52044         this.form.afterAction(this, false);
52045     },
52046
52047     processResponse : function(response){
52048         this.response = response;
52049         if(!response.responseText){
52050             return true;
52051         }
52052         this.result = this.handleResponse(response);
52053         return this.result;
52054     },
52055
52056     // utility functions used internally
52057     getUrl : function(appendParams){
52058         var url = this.options.url || this.form.url || this.form.el.dom.action;
52059         if(appendParams){
52060             var p = this.getParams();
52061             if(p){
52062                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
52063             }
52064         }
52065         return url;
52066     },
52067
52068     getMethod : function(){
52069         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
52070     },
52071
52072     getParams : function(){
52073         var bp = this.form.baseParams;
52074         var p = this.options.params;
52075         if(p){
52076             if(typeof p == "object"){
52077                 p = Roo.urlEncode(Roo.applyIf(p, bp));
52078             }else if(typeof p == 'string' && bp){
52079                 p += '&' + Roo.urlEncode(bp);
52080             }
52081         }else if(bp){
52082             p = Roo.urlEncode(bp);
52083         }
52084         return p;
52085     },
52086
52087     createCallback : function(){
52088         return {
52089             success: this.success,
52090             failure: this.failure,
52091             scope: this,
52092             timeout: (this.form.timeout*1000),
52093             upload: this.form.fileUpload ? this.success : undefined
52094         };
52095     }
52096 };
52097
52098 Roo.form.Action.Submit = function(form, options){
52099     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
52100 };
52101
52102 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
52103     type : 'submit',
52104
52105     haveProgress : false,
52106     uploadComplete : false,
52107     
52108     // uploadProgress indicator.
52109     uploadProgress : function()
52110     {
52111         if (!this.form.progressUrl) {
52112             return;
52113         }
52114         
52115         if (!this.haveProgress) {
52116             Roo.MessageBox.progress("Uploading", "Uploading");
52117         }
52118         if (this.uploadComplete) {
52119            Roo.MessageBox.hide();
52120            return;
52121         }
52122         
52123         this.haveProgress = true;
52124    
52125         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
52126         
52127         var c = new Roo.data.Connection();
52128         c.request({
52129             url : this.form.progressUrl,
52130             params: {
52131                 id : uid
52132             },
52133             method: 'GET',
52134             success : function(req){
52135                //console.log(data);
52136                 var rdata = false;
52137                 var edata;
52138                 try  {
52139                    rdata = Roo.decode(req.responseText)
52140                 } catch (e) {
52141                     Roo.log("Invalid data from server..");
52142                     Roo.log(edata);
52143                     return;
52144                 }
52145                 if (!rdata || !rdata.success) {
52146                     Roo.log(rdata);
52147                     Roo.MessageBox.alert(Roo.encode(rdata));
52148                     return;
52149                 }
52150                 var data = rdata.data;
52151                 
52152                 if (this.uploadComplete) {
52153                    Roo.MessageBox.hide();
52154                    return;
52155                 }
52156                    
52157                 if (data){
52158                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
52159                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
52160                     );
52161                 }
52162                 this.uploadProgress.defer(2000,this);
52163             },
52164        
52165             failure: function(data) {
52166                 Roo.log('progress url failed ');
52167                 Roo.log(data);
52168             },
52169             scope : this
52170         });
52171            
52172     },
52173     
52174     
52175     run : function()
52176     {
52177         // run get Values on the form, so it syncs any secondary forms.
52178         this.form.getValues();
52179         
52180         var o = this.options;
52181         var method = this.getMethod();
52182         var isPost = method == 'POST';
52183         if(o.clientValidation === false || this.form.isValid()){
52184             
52185             if (this.form.progressUrl) {
52186                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
52187                     (new Date() * 1) + '' + Math.random());
52188                     
52189             } 
52190             
52191             
52192             Roo.Ajax.request(Roo.apply(this.createCallback(), {
52193                 form:this.form.el.dom,
52194                 url:this.getUrl(!isPost),
52195                 method: method,
52196                 params:isPost ? this.getParams() : null,
52197                 isUpload: this.form.fileUpload
52198             }));
52199             
52200             this.uploadProgress();
52201
52202         }else if (o.clientValidation !== false){ // client validation failed
52203             this.failureType = Roo.form.Action.CLIENT_INVALID;
52204             this.form.afterAction(this, false);
52205         }
52206     },
52207
52208     success : function(response)
52209     {
52210         this.uploadComplete= true;
52211         if (this.haveProgress) {
52212             Roo.MessageBox.hide();
52213         }
52214         
52215         
52216         var result = this.processResponse(response);
52217         if(result === true || result.success){
52218             this.form.afterAction(this, true);
52219             return;
52220         }
52221         if(result.errors){
52222             this.form.markInvalid(result.errors);
52223             this.failureType = Roo.form.Action.SERVER_INVALID;
52224         }
52225         this.form.afterAction(this, false);
52226     },
52227     failure : function(response)
52228     {
52229         this.uploadComplete= true;
52230         if (this.haveProgress) {
52231             Roo.MessageBox.hide();
52232         }
52233         
52234         this.response = response;
52235         this.failureType = Roo.form.Action.CONNECT_FAILURE;
52236         this.form.afterAction(this, false);
52237     },
52238     
52239     handleResponse : function(response){
52240         if(this.form.errorReader){
52241             var rs = this.form.errorReader.read(response);
52242             var errors = [];
52243             if(rs.records){
52244                 for(var i = 0, len = rs.records.length; i < len; i++) {
52245                     var r = rs.records[i];
52246                     errors[i] = r.data;
52247                 }
52248             }
52249             if(errors.length < 1){
52250                 errors = null;
52251             }
52252             return {
52253                 success : rs.success,
52254                 errors : errors
52255             };
52256         }
52257         var ret = false;
52258         try {
52259             ret = Roo.decode(response.responseText);
52260         } catch (e) {
52261             ret = {
52262                 success: false,
52263                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
52264                 errors : []
52265             };
52266         }
52267         return ret;
52268         
52269     }
52270 });
52271
52272
52273 Roo.form.Action.Load = function(form, options){
52274     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
52275     this.reader = this.form.reader;
52276 };
52277
52278 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
52279     type : 'load',
52280
52281     run : function(){
52282         
52283         Roo.Ajax.request(Roo.apply(
52284                 this.createCallback(), {
52285                     method:this.getMethod(),
52286                     url:this.getUrl(false),
52287                     params:this.getParams()
52288         }));
52289     },
52290
52291     success : function(response){
52292         
52293         var result = this.processResponse(response);
52294         if(result === true || !result.success || !result.data){
52295             this.failureType = Roo.form.Action.LOAD_FAILURE;
52296             this.form.afterAction(this, false);
52297             return;
52298         }
52299         this.form.clearInvalid();
52300         this.form.setValues(result.data);
52301         this.form.afterAction(this, true);
52302     },
52303
52304     handleResponse : function(response){
52305         if(this.form.reader){
52306             var rs = this.form.reader.read(response);
52307             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
52308             return {
52309                 success : rs.success,
52310                 data : data
52311             };
52312         }
52313         return Roo.decode(response.responseText);
52314     }
52315 });
52316
52317 Roo.form.Action.ACTION_TYPES = {
52318     'load' : Roo.form.Action.Load,
52319     'submit' : Roo.form.Action.Submit
52320 };/*
52321  * Based on:
52322  * Ext JS Library 1.1.1
52323  * Copyright(c) 2006-2007, Ext JS, LLC.
52324  *
52325  * Originally Released Under LGPL - original licence link has changed is not relivant.
52326  *
52327  * Fork - LGPL
52328  * <script type="text/javascript">
52329  */
52330  
52331 /**
52332  * @class Roo.form.Layout
52333  * @extends Roo.Component
52334  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
52335  * @constructor
52336  * @param {Object} config Configuration options
52337  */
52338 Roo.form.Layout = function(config){
52339     var xitems = [];
52340     if (config.items) {
52341         xitems = config.items;
52342         delete config.items;
52343     }
52344     Roo.form.Layout.superclass.constructor.call(this, config);
52345     this.stack = [];
52346     Roo.each(xitems, this.addxtype, this);
52347      
52348 };
52349
52350 Roo.extend(Roo.form.Layout, Roo.Component, {
52351     /**
52352      * @cfg {String/Object} autoCreate
52353      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
52354      */
52355     /**
52356      * @cfg {String/Object/Function} style
52357      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
52358      * a function which returns such a specification.
52359      */
52360     /**
52361      * @cfg {String} labelAlign
52362      * Valid values are "left," "top" and "right" (defaults to "left")
52363      */
52364     /**
52365      * @cfg {Number} labelWidth
52366      * Fixed width in pixels of all field labels (defaults to undefined)
52367      */
52368     /**
52369      * @cfg {Boolean} clear
52370      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
52371      */
52372     clear : true,
52373     /**
52374      * @cfg {String} labelSeparator
52375      * The separator to use after field labels (defaults to ':')
52376      */
52377     labelSeparator : ':',
52378     /**
52379      * @cfg {Boolean} hideLabels
52380      * True to suppress the display of field labels in this layout (defaults to false)
52381      */
52382     hideLabels : false,
52383
52384     // private
52385     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
52386     
52387     isLayout : true,
52388     
52389     // private
52390     onRender : function(ct, position){
52391         if(this.el){ // from markup
52392             this.el = Roo.get(this.el);
52393         }else {  // generate
52394             var cfg = this.getAutoCreate();
52395             this.el = ct.createChild(cfg, position);
52396         }
52397         if(this.style){
52398             this.el.applyStyles(this.style);
52399         }
52400         if(this.labelAlign){
52401             this.el.addClass('x-form-label-'+this.labelAlign);
52402         }
52403         if(this.hideLabels){
52404             this.labelStyle = "display:none";
52405             this.elementStyle = "padding-left:0;";
52406         }else{
52407             if(typeof this.labelWidth == 'number'){
52408                 this.labelStyle = "width:"+this.labelWidth+"px;";
52409                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
52410             }
52411             if(this.labelAlign == 'top'){
52412                 this.labelStyle = "width:auto;";
52413                 this.elementStyle = "padding-left:0;";
52414             }
52415         }
52416         var stack = this.stack;
52417         var slen = stack.length;
52418         if(slen > 0){
52419             if(!this.fieldTpl){
52420                 var t = new Roo.Template(
52421                     '<div class="x-form-item {5}">',
52422                         '<label for="{0}" style="{2}">{1}{4}</label>',
52423                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52424                         '</div>',
52425                     '</div><div class="x-form-clear-left"></div>'
52426                 );
52427                 t.disableFormats = true;
52428                 t.compile();
52429                 Roo.form.Layout.prototype.fieldTpl = t;
52430             }
52431             for(var i = 0; i < slen; i++) {
52432                 if(stack[i].isFormField){
52433                     this.renderField(stack[i]);
52434                 }else{
52435                     this.renderComponent(stack[i]);
52436                 }
52437             }
52438         }
52439         if(this.clear){
52440             this.el.createChild({cls:'x-form-clear'});
52441         }
52442     },
52443
52444     // private
52445     renderField : function(f){
52446         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
52447                f.id, //0
52448                f.fieldLabel, //1
52449                f.labelStyle||this.labelStyle||'', //2
52450                this.elementStyle||'', //3
52451                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
52452                f.itemCls||this.itemCls||''  //5
52453        ], true).getPrevSibling());
52454     },
52455
52456     // private
52457     renderComponent : function(c){
52458         c.render(c.isLayout ? this.el : this.el.createChild());    
52459     },
52460     /**
52461      * Adds a object form elements (using the xtype property as the factory method.)
52462      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
52463      * @param {Object} config 
52464      */
52465     addxtype : function(o)
52466     {
52467         // create the lement.
52468         o.form = this.form;
52469         var fe = Roo.factory(o, Roo.form);
52470         this.form.allItems.push(fe);
52471         this.stack.push(fe);
52472         
52473         if (fe.isFormField) {
52474             this.form.items.add(fe);
52475         }
52476          
52477         return fe;
52478     }
52479 });
52480
52481 /**
52482  * @class Roo.form.Column
52483  * @extends Roo.form.Layout
52484  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
52485  * @constructor
52486  * @param {Object} config Configuration options
52487  */
52488 Roo.form.Column = function(config){
52489     Roo.form.Column.superclass.constructor.call(this, config);
52490 };
52491
52492 Roo.extend(Roo.form.Column, Roo.form.Layout, {
52493     /**
52494      * @cfg {Number/String} width
52495      * The fixed width of the column in pixels or CSS value (defaults to "auto")
52496      */
52497     /**
52498      * @cfg {String/Object} autoCreate
52499      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
52500      */
52501
52502     // private
52503     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
52504
52505     // private
52506     onRender : function(ct, position){
52507         Roo.form.Column.superclass.onRender.call(this, ct, position);
52508         if(this.width){
52509             this.el.setWidth(this.width);
52510         }
52511     }
52512 });
52513
52514
52515 /**
52516  * @class Roo.form.Row
52517  * @extends Roo.form.Layout
52518  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
52519  * @constructor
52520  * @param {Object} config Configuration options
52521  */
52522
52523  
52524 Roo.form.Row = function(config){
52525     Roo.form.Row.superclass.constructor.call(this, config);
52526 };
52527  
52528 Roo.extend(Roo.form.Row, Roo.form.Layout, {
52529       /**
52530      * @cfg {Number/String} width
52531      * The fixed width of the column in pixels or CSS value (defaults to "auto")
52532      */
52533     /**
52534      * @cfg {Number/String} height
52535      * The fixed height of the column in pixels or CSS value (defaults to "auto")
52536      */
52537     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
52538     
52539     padWidth : 20,
52540     // private
52541     onRender : function(ct, position){
52542         //console.log('row render');
52543         if(!this.rowTpl){
52544             var t = new Roo.Template(
52545                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
52546                     '<label for="{0}" style="{2}">{1}{4}</label>',
52547                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52548                     '</div>',
52549                 '</div>'
52550             );
52551             t.disableFormats = true;
52552             t.compile();
52553             Roo.form.Layout.prototype.rowTpl = t;
52554         }
52555         this.fieldTpl = this.rowTpl;
52556         
52557         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
52558         var labelWidth = 100;
52559         
52560         if ((this.labelAlign != 'top')) {
52561             if (typeof this.labelWidth == 'number') {
52562                 labelWidth = this.labelWidth
52563             }
52564             this.padWidth =  20 + labelWidth;
52565             
52566         }
52567         
52568         Roo.form.Column.superclass.onRender.call(this, ct, position);
52569         if(this.width){
52570             this.el.setWidth(this.width);
52571         }
52572         if(this.height){
52573             this.el.setHeight(this.height);
52574         }
52575     },
52576     
52577     // private
52578     renderField : function(f){
52579         f.fieldEl = this.fieldTpl.append(this.el, [
52580                f.id, f.fieldLabel,
52581                f.labelStyle||this.labelStyle||'',
52582                this.elementStyle||'',
52583                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
52584                f.itemCls||this.itemCls||'',
52585                f.width ? f.width + this.padWidth : 160 + this.padWidth
52586        ],true);
52587     }
52588 });
52589  
52590
52591 /**
52592  * @class Roo.form.FieldSet
52593  * @extends Roo.form.Layout
52594  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
52595  * @constructor
52596  * @param {Object} config Configuration options
52597  */
52598 Roo.form.FieldSet = function(config){
52599     Roo.form.FieldSet.superclass.constructor.call(this, config);
52600 };
52601
52602 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
52603     /**
52604      * @cfg {String} legend
52605      * The text to display as the legend for the FieldSet (defaults to '')
52606      */
52607     /**
52608      * @cfg {String/Object} autoCreate
52609      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
52610      */
52611
52612     // private
52613     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
52614
52615     // private
52616     onRender : function(ct, position){
52617         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
52618         if(this.legend){
52619             this.setLegend(this.legend);
52620         }
52621     },
52622
52623     // private
52624     setLegend : function(text){
52625         if(this.rendered){
52626             this.el.child('legend').update(text);
52627         }
52628     }
52629 });/*
52630  * Based on:
52631  * Ext JS Library 1.1.1
52632  * Copyright(c) 2006-2007, Ext JS, LLC.
52633  *
52634  * Originally Released Under LGPL - original licence link has changed is not relivant.
52635  *
52636  * Fork - LGPL
52637  * <script type="text/javascript">
52638  */
52639 /**
52640  * @class Roo.form.VTypes
52641  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
52642  * @singleton
52643  */
52644 Roo.form.VTypes = function(){
52645     // closure these in so they are only created once.
52646     var alpha = /^[a-zA-Z_]+$/;
52647     var alphanum = /^[a-zA-Z0-9_]+$/;
52648     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
52649     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
52650
52651     // All these messages and functions are configurable
52652     return {
52653         /**
52654          * The function used to validate email addresses
52655          * @param {String} value The email address
52656          */
52657         'email' : function(v){
52658             return email.test(v);
52659         },
52660         /**
52661          * The error text to display when the email validation function returns false
52662          * @type String
52663          */
52664         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
52665         /**
52666          * The keystroke filter mask to be applied on email input
52667          * @type RegExp
52668          */
52669         'emailMask' : /[a-z0-9_\.\-@]/i,
52670
52671         /**
52672          * The function used to validate URLs
52673          * @param {String} value The URL
52674          */
52675         'url' : function(v){
52676             return url.test(v);
52677         },
52678         /**
52679          * The error text to display when the url validation function returns false
52680          * @type String
52681          */
52682         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
52683         
52684         /**
52685          * The function used to validate alpha values
52686          * @param {String} value The value
52687          */
52688         'alpha' : function(v){
52689             return alpha.test(v);
52690         },
52691         /**
52692          * The error text to display when the alpha validation function returns false
52693          * @type String
52694          */
52695         'alphaText' : 'This field should only contain letters and _',
52696         /**
52697          * The keystroke filter mask to be applied on alpha input
52698          * @type RegExp
52699          */
52700         'alphaMask' : /[a-z_]/i,
52701
52702         /**
52703          * The function used to validate alphanumeric values
52704          * @param {String} value The value
52705          */
52706         'alphanum' : function(v){
52707             return alphanum.test(v);
52708         },
52709         /**
52710          * The error text to display when the alphanumeric validation function returns false
52711          * @type String
52712          */
52713         'alphanumText' : 'This field should only contain letters, numbers and _',
52714         /**
52715          * The keystroke filter mask to be applied on alphanumeric input
52716          * @type RegExp
52717          */
52718         'alphanumMask' : /[a-z0-9_]/i
52719     };
52720 }();//<script type="text/javascript">
52721
52722 /**
52723  * @class Roo.form.FCKeditor
52724  * @extends Roo.form.TextArea
52725  * Wrapper around the FCKEditor http://www.fckeditor.net
52726  * @constructor
52727  * Creates a new FCKeditor
52728  * @param {Object} config Configuration options
52729  */
52730 Roo.form.FCKeditor = function(config){
52731     Roo.form.FCKeditor.superclass.constructor.call(this, config);
52732     this.addEvents({
52733          /**
52734          * @event editorinit
52735          * Fired when the editor is initialized - you can add extra handlers here..
52736          * @param {FCKeditor} this
52737          * @param {Object} the FCK object.
52738          */
52739         editorinit : true
52740     });
52741     
52742     
52743 };
52744 Roo.form.FCKeditor.editors = { };
52745 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
52746 {
52747     //defaultAutoCreate : {
52748     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
52749     //},
52750     // private
52751     /**
52752      * @cfg {Object} fck options - see fck manual for details.
52753      */
52754     fckconfig : false,
52755     
52756     /**
52757      * @cfg {Object} fck toolbar set (Basic or Default)
52758      */
52759     toolbarSet : 'Basic',
52760     /**
52761      * @cfg {Object} fck BasePath
52762      */ 
52763     basePath : '/fckeditor/',
52764     
52765     
52766     frame : false,
52767     
52768     value : '',
52769     
52770    
52771     onRender : function(ct, position)
52772     {
52773         if(!this.el){
52774             this.defaultAutoCreate = {
52775                 tag: "textarea",
52776                 style:"width:300px;height:60px;",
52777                 autocomplete: "new-password"
52778             };
52779         }
52780         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52781         /*
52782         if(this.grow){
52783             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52784             if(this.preventScrollbars){
52785                 this.el.setStyle("overflow", "hidden");
52786             }
52787             this.el.setHeight(this.growMin);
52788         }
52789         */
52790         //console.log('onrender' + this.getId() );
52791         Roo.form.FCKeditor.editors[this.getId()] = this;
52792          
52793
52794         this.replaceTextarea() ;
52795         
52796     },
52797     
52798     getEditor : function() {
52799         return this.fckEditor;
52800     },
52801     /**
52802      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
52803      * @param {Mixed} value The value to set
52804      */
52805     
52806     
52807     setValue : function(value)
52808     {
52809         //console.log('setValue: ' + value);
52810         
52811         if(typeof(value) == 'undefined') { // not sure why this is happending...
52812             return;
52813         }
52814         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52815         
52816         //if(!this.el || !this.getEditor()) {
52817         //    this.value = value;
52818             //this.setValue.defer(100,this,[value]);    
52819         //    return;
52820         //} 
52821         
52822         if(!this.getEditor()) {
52823             return;
52824         }
52825         
52826         this.getEditor().SetData(value);
52827         
52828         //
52829
52830     },
52831
52832     /**
52833      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
52834      * @return {Mixed} value The field value
52835      */
52836     getValue : function()
52837     {
52838         
52839         if (this.frame && this.frame.dom.style.display == 'none') {
52840             return Roo.form.FCKeditor.superclass.getValue.call(this);
52841         }
52842         
52843         if(!this.el || !this.getEditor()) {
52844            
52845            // this.getValue.defer(100,this); 
52846             return this.value;
52847         }
52848        
52849         
52850         var value=this.getEditor().GetData();
52851         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52852         return Roo.form.FCKeditor.superclass.getValue.call(this);
52853         
52854
52855     },
52856
52857     /**
52858      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
52859      * @return {Mixed} value The field value
52860      */
52861     getRawValue : function()
52862     {
52863         if (this.frame && this.frame.dom.style.display == 'none') {
52864             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52865         }
52866         
52867         if(!this.el || !this.getEditor()) {
52868             //this.getRawValue.defer(100,this); 
52869             return this.value;
52870             return;
52871         }
52872         
52873         
52874         
52875         var value=this.getEditor().GetData();
52876         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52877         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52878          
52879     },
52880     
52881     setSize : function(w,h) {
52882         
52883         
52884         
52885         //if (this.frame && this.frame.dom.style.display == 'none') {
52886         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52887         //    return;
52888         //}
52889         //if(!this.el || !this.getEditor()) {
52890         //    this.setSize.defer(100,this, [w,h]); 
52891         //    return;
52892         //}
52893         
52894         
52895         
52896         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52897         
52898         this.frame.dom.setAttribute('width', w);
52899         this.frame.dom.setAttribute('height', h);
52900         this.frame.setSize(w,h);
52901         
52902     },
52903     
52904     toggleSourceEdit : function(value) {
52905         
52906       
52907          
52908         this.el.dom.style.display = value ? '' : 'none';
52909         this.frame.dom.style.display = value ?  'none' : '';
52910         
52911     },
52912     
52913     
52914     focus: function(tag)
52915     {
52916         if (this.frame.dom.style.display == 'none') {
52917             return Roo.form.FCKeditor.superclass.focus.call(this);
52918         }
52919         if(!this.el || !this.getEditor()) {
52920             this.focus.defer(100,this, [tag]); 
52921             return;
52922         }
52923         
52924         
52925         
52926         
52927         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52928         this.getEditor().Focus();
52929         if (tgs.length) {
52930             if (!this.getEditor().Selection.GetSelection()) {
52931                 this.focus.defer(100,this, [tag]); 
52932                 return;
52933             }
52934             
52935             
52936             var r = this.getEditor().EditorDocument.createRange();
52937             r.setStart(tgs[0],0);
52938             r.setEnd(tgs[0],0);
52939             this.getEditor().Selection.GetSelection().removeAllRanges();
52940             this.getEditor().Selection.GetSelection().addRange(r);
52941             this.getEditor().Focus();
52942         }
52943         
52944     },
52945     
52946     
52947     
52948     replaceTextarea : function()
52949     {
52950         if ( document.getElementById( this.getId() + '___Frame' ) ) {
52951             return ;
52952         }
52953         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52954         //{
52955             // We must check the elements firstly using the Id and then the name.
52956         var oTextarea = document.getElementById( this.getId() );
52957         
52958         var colElementsByName = document.getElementsByName( this.getId() ) ;
52959          
52960         oTextarea.style.display = 'none' ;
52961
52962         if ( oTextarea.tabIndex ) {            
52963             this.TabIndex = oTextarea.tabIndex ;
52964         }
52965         
52966         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52967         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52968         this.frame = Roo.get(this.getId() + '___Frame')
52969     },
52970     
52971     _getConfigHtml : function()
52972     {
52973         var sConfig = '' ;
52974
52975         for ( var o in this.fckconfig ) {
52976             sConfig += sConfig.length > 0  ? '&amp;' : '';
52977             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52978         }
52979
52980         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52981     },
52982     
52983     
52984     _getIFrameHtml : function()
52985     {
52986         var sFile = 'fckeditor.html' ;
52987         /* no idea what this is about..
52988         try
52989         {
52990             if ( (/fcksource=true/i).test( window.top.location.search ) )
52991                 sFile = 'fckeditor.original.html' ;
52992         }
52993         catch (e) { 
52994         */
52995
52996         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52997         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
52998         
52999         
53000         var html = '<iframe id="' + this.getId() +
53001             '___Frame" src="' + sLink +
53002             '" width="' + this.width +
53003             '" height="' + this.height + '"' +
53004             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
53005             ' frameborder="0" scrolling="no"></iframe>' ;
53006
53007         return html ;
53008     },
53009     
53010     _insertHtmlBefore : function( html, element )
53011     {
53012         if ( element.insertAdjacentHTML )       {
53013             // IE
53014             element.insertAdjacentHTML( 'beforeBegin', html ) ;
53015         } else { // Gecko
53016             var oRange = document.createRange() ;
53017             oRange.setStartBefore( element ) ;
53018             var oFragment = oRange.createContextualFragment( html );
53019             element.parentNode.insertBefore( oFragment, element ) ;
53020         }
53021     }
53022     
53023     
53024   
53025     
53026     
53027     
53028     
53029
53030 });
53031
53032 //Roo.reg('fckeditor', Roo.form.FCKeditor);
53033
53034 function FCKeditor_OnComplete(editorInstance){
53035     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
53036     f.fckEditor = editorInstance;
53037     //console.log("loaded");
53038     f.fireEvent('editorinit', f, editorInstance);
53039
53040   
53041
53042  
53043
53044
53045
53046
53047
53048
53049
53050
53051
53052
53053
53054
53055
53056
53057
53058 //<script type="text/javascript">
53059 /**
53060  * @class Roo.form.GridField
53061  * @extends Roo.form.Field
53062  * Embed a grid (or editable grid into a form)
53063  * STATUS ALPHA
53064  * 
53065  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
53066  * it needs 
53067  * xgrid.store = Roo.data.Store
53068  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
53069  * xgrid.store.reader = Roo.data.JsonReader 
53070  * 
53071  * 
53072  * @constructor
53073  * Creates a new GridField
53074  * @param {Object} config Configuration options
53075  */
53076 Roo.form.GridField = function(config){
53077     Roo.form.GridField.superclass.constructor.call(this, config);
53078      
53079 };
53080
53081 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
53082     /**
53083      * @cfg {Number} width  - used to restrict width of grid..
53084      */
53085     width : 100,
53086     /**
53087      * @cfg {Number} height - used to restrict height of grid..
53088      */
53089     height : 50,
53090      /**
53091      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
53092          * 
53093          *}
53094      */
53095     xgrid : false, 
53096     /**
53097      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
53098      * {tag: "input", type: "checkbox", autocomplete: "off"})
53099      */
53100    // defaultAutoCreate : { tag: 'div' },
53101     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
53102     /**
53103      * @cfg {String} addTitle Text to include for adding a title.
53104      */
53105     addTitle : false,
53106     //
53107     onResize : function(){
53108         Roo.form.Field.superclass.onResize.apply(this, arguments);
53109     },
53110
53111     initEvents : function(){
53112         // Roo.form.Checkbox.superclass.initEvents.call(this);
53113         // has no events...
53114        
53115     },
53116
53117
53118     getResizeEl : function(){
53119         return this.wrap;
53120     },
53121
53122     getPositionEl : function(){
53123         return this.wrap;
53124     },
53125
53126     // private
53127     onRender : function(ct, position){
53128         
53129         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
53130         var style = this.style;
53131         delete this.style;
53132         
53133         Roo.form.GridField.superclass.onRender.call(this, ct, position);
53134         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
53135         this.viewEl = this.wrap.createChild({ tag: 'div' });
53136         if (style) {
53137             this.viewEl.applyStyles(style);
53138         }
53139         if (this.width) {
53140             this.viewEl.setWidth(this.width);
53141         }
53142         if (this.height) {
53143             this.viewEl.setHeight(this.height);
53144         }
53145         //if(this.inputValue !== undefined){
53146         //this.setValue(this.value);
53147         
53148         
53149         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
53150         
53151         
53152         this.grid.render();
53153         this.grid.getDataSource().on('remove', this.refreshValue, this);
53154         this.grid.getDataSource().on('update', this.refreshValue, this);
53155         this.grid.on('afteredit', this.refreshValue, this);
53156  
53157     },
53158      
53159     
53160     /**
53161      * Sets the value of the item. 
53162      * @param {String} either an object  or a string..
53163      */
53164     setValue : function(v){
53165         //this.value = v;
53166         v = v || []; // empty set..
53167         // this does not seem smart - it really only affects memoryproxy grids..
53168         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
53169             var ds = this.grid.getDataSource();
53170             // assumes a json reader..
53171             var data = {}
53172             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
53173             ds.loadData( data);
53174         }
53175         // clear selection so it does not get stale.
53176         if (this.grid.sm) { 
53177             this.grid.sm.clearSelections();
53178         }
53179         
53180         Roo.form.GridField.superclass.setValue.call(this, v);
53181         this.refreshValue();
53182         // should load data in the grid really....
53183     },
53184     
53185     // private
53186     refreshValue: function() {
53187          var val = [];
53188         this.grid.getDataSource().each(function(r) {
53189             val.push(r.data);
53190         });
53191         this.el.dom.value = Roo.encode(val);
53192     }
53193     
53194      
53195     
53196     
53197 });/*
53198  * Based on:
53199  * Ext JS Library 1.1.1
53200  * Copyright(c) 2006-2007, Ext JS, LLC.
53201  *
53202  * Originally Released Under LGPL - original licence link has changed is not relivant.
53203  *
53204  * Fork - LGPL
53205  * <script type="text/javascript">
53206  */
53207 /**
53208  * @class Roo.form.DisplayField
53209  * @extends Roo.form.Field
53210  * A generic Field to display non-editable data.
53211  * @cfg {Boolean} closable (true|false) default false
53212  * @constructor
53213  * Creates a new Display Field item.
53214  * @param {Object} config Configuration options
53215  */
53216 Roo.form.DisplayField = function(config){
53217     Roo.form.DisplayField.superclass.constructor.call(this, config);
53218     
53219     this.addEvents({
53220         /**
53221          * @event close
53222          * Fires after the click the close btn
53223              * @param {Roo.form.DisplayField} this
53224              */
53225         close : true
53226     });
53227 };
53228
53229 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
53230     inputType:      'hidden',
53231     allowBlank:     true,
53232     readOnly:         true,
53233     
53234  
53235     /**
53236      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
53237      */
53238     focusClass : undefined,
53239     /**
53240      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
53241      */
53242     fieldClass: 'x-form-field',
53243     
53244      /**
53245      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
53246      */
53247     valueRenderer: undefined,
53248     
53249     width: 100,
53250     /**
53251      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
53252      * {tag: "input", type: "checkbox", autocomplete: "off"})
53253      */
53254      
53255  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
53256  
53257     closable : false,
53258     
53259     onResize : function(){
53260         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
53261         
53262     },
53263
53264     initEvents : function(){
53265         // Roo.form.Checkbox.superclass.initEvents.call(this);
53266         // has no events...
53267         
53268         if(this.closable){
53269             this.closeEl.on('click', this.onClose, this);
53270         }
53271        
53272     },
53273
53274
53275     getResizeEl : function(){
53276         return this.wrap;
53277     },
53278
53279     getPositionEl : function(){
53280         return this.wrap;
53281     },
53282
53283     // private
53284     onRender : function(ct, position){
53285         
53286         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
53287         //if(this.inputValue !== undefined){
53288         this.wrap = this.el.wrap();
53289         
53290         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
53291         
53292         if(this.closable){
53293             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
53294         }
53295         
53296         if (this.bodyStyle) {
53297             this.viewEl.applyStyles(this.bodyStyle);
53298         }
53299         //this.viewEl.setStyle('padding', '2px');
53300         
53301         this.setValue(this.value);
53302         
53303     },
53304 /*
53305     // private
53306     initValue : Roo.emptyFn,
53307
53308   */
53309
53310         // private
53311     onClick : function(){
53312         
53313     },
53314
53315     /**
53316      * Sets the checked state of the checkbox.
53317      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
53318      */
53319     setValue : function(v){
53320         this.value = v;
53321         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
53322         // this might be called before we have a dom element..
53323         if (!this.viewEl) {
53324             return;
53325         }
53326         this.viewEl.dom.innerHTML = html;
53327         Roo.form.DisplayField.superclass.setValue.call(this, v);
53328
53329     },
53330     
53331     onClose : function(e)
53332     {
53333         e.preventDefault();
53334         
53335         this.fireEvent('close', this);
53336     }
53337 });/*
53338  * 
53339  * Licence- LGPL
53340  * 
53341  */
53342
53343 /**
53344  * @class Roo.form.DayPicker
53345  * @extends Roo.form.Field
53346  * A Day picker show [M] [T] [W] ....
53347  * @constructor
53348  * Creates a new Day Picker
53349  * @param {Object} config Configuration options
53350  */
53351 Roo.form.DayPicker= function(config){
53352     Roo.form.DayPicker.superclass.constructor.call(this, config);
53353      
53354 };
53355
53356 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
53357     /**
53358      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
53359      */
53360     focusClass : undefined,
53361     /**
53362      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
53363      */
53364     fieldClass: "x-form-field",
53365    
53366     /**
53367      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
53368      * {tag: "input", type: "checkbox", autocomplete: "off"})
53369      */
53370     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
53371     
53372    
53373     actionMode : 'viewEl', 
53374     //
53375     // private
53376  
53377     inputType : 'hidden',
53378     
53379      
53380     inputElement: false, // real input element?
53381     basedOn: false, // ????
53382     
53383     isFormField: true, // not sure where this is needed!!!!
53384
53385     onResize : function(){
53386         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
53387         if(!this.boxLabel){
53388             this.el.alignTo(this.wrap, 'c-c');
53389         }
53390     },
53391
53392     initEvents : function(){
53393         Roo.form.Checkbox.superclass.initEvents.call(this);
53394         this.el.on("click", this.onClick,  this);
53395         this.el.on("change", this.onClick,  this);
53396     },
53397
53398
53399     getResizeEl : function(){
53400         return this.wrap;
53401     },
53402
53403     getPositionEl : function(){
53404         return this.wrap;
53405     },
53406
53407     
53408     // private
53409     onRender : function(ct, position){
53410         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
53411        
53412         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
53413         
53414         var r1 = '<table><tr>';
53415         var r2 = '<tr class="x-form-daypick-icons">';
53416         for (var i=0; i < 7; i++) {
53417             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
53418             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
53419         }
53420         
53421         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
53422         viewEl.select('img').on('click', this.onClick, this);
53423         this.viewEl = viewEl;   
53424         
53425         
53426         // this will not work on Chrome!!!
53427         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
53428         this.el.on('propertychange', this.setFromHidden,  this);  //ie
53429         
53430         
53431           
53432
53433     },
53434
53435     // private
53436     initValue : Roo.emptyFn,
53437
53438     /**
53439      * Returns the checked state of the checkbox.
53440      * @return {Boolean} True if checked, else false
53441      */
53442     getValue : function(){
53443         return this.el.dom.value;
53444         
53445     },
53446
53447         // private
53448     onClick : function(e){ 
53449         //this.setChecked(!this.checked);
53450         Roo.get(e.target).toggleClass('x-menu-item-checked');
53451         this.refreshValue();
53452         //if(this.el.dom.checked != this.checked){
53453         //    this.setValue(this.el.dom.checked);
53454        // }
53455     },
53456     
53457     // private
53458     refreshValue : function()
53459     {
53460         var val = '';
53461         this.viewEl.select('img',true).each(function(e,i,n)  {
53462             val += e.is(".x-menu-item-checked") ? String(n) : '';
53463         });
53464         this.setValue(val, true);
53465     },
53466
53467     /**
53468      * Sets the checked state of the checkbox.
53469      * On is always based on a string comparison between inputValue and the param.
53470      * @param {Boolean/String} value - the value to set 
53471      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
53472      */
53473     setValue : function(v,suppressEvent){
53474         if (!this.el.dom) {
53475             return;
53476         }
53477         var old = this.el.dom.value ;
53478         this.el.dom.value = v;
53479         if (suppressEvent) {
53480             return ;
53481         }
53482          
53483         // update display..
53484         this.viewEl.select('img',true).each(function(e,i,n)  {
53485             
53486             var on = e.is(".x-menu-item-checked");
53487             var newv = v.indexOf(String(n)) > -1;
53488             if (on != newv) {
53489                 e.toggleClass('x-menu-item-checked');
53490             }
53491             
53492         });
53493         
53494         
53495         this.fireEvent('change', this, v, old);
53496         
53497         
53498     },
53499    
53500     // handle setting of hidden value by some other method!!?!?
53501     setFromHidden: function()
53502     {
53503         if(!this.el){
53504             return;
53505         }
53506         //console.log("SET FROM HIDDEN");
53507         //alert('setFrom hidden');
53508         this.setValue(this.el.dom.value);
53509     },
53510     
53511     onDestroy : function()
53512     {
53513         if(this.viewEl){
53514             Roo.get(this.viewEl).remove();
53515         }
53516          
53517         Roo.form.DayPicker.superclass.onDestroy.call(this);
53518     }
53519
53520 });/*
53521  * RooJS Library 1.1.1
53522  * Copyright(c) 2008-2011  Alan Knowles
53523  *
53524  * License - LGPL
53525  */
53526  
53527
53528 /**
53529  * @class Roo.form.ComboCheck
53530  * @extends Roo.form.ComboBox
53531  * A combobox for multiple select items.
53532  *
53533  * FIXME - could do with a reset button..
53534  * 
53535  * @constructor
53536  * Create a new ComboCheck
53537  * @param {Object} config Configuration options
53538  */
53539 Roo.form.ComboCheck = function(config){
53540     Roo.form.ComboCheck.superclass.constructor.call(this, config);
53541     // should verify some data...
53542     // like
53543     // hiddenName = required..
53544     // displayField = required
53545     // valudField == required
53546     var req= [ 'hiddenName', 'displayField', 'valueField' ];
53547     var _t = this;
53548     Roo.each(req, function(e) {
53549         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
53550             throw "Roo.form.ComboCheck : missing value for: " + e;
53551         }
53552     });
53553     
53554     
53555 };
53556
53557 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
53558      
53559      
53560     editable : false,
53561      
53562     selectedClass: 'x-menu-item-checked', 
53563     
53564     // private
53565     onRender : function(ct, position){
53566         var _t = this;
53567         
53568         
53569         
53570         if(!this.tpl){
53571             var cls = 'x-combo-list';
53572
53573             
53574             this.tpl =  new Roo.Template({
53575                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
53576                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
53577                    '<span>{' + this.displayField + '}</span>' +
53578                     '</div>' 
53579                 
53580             });
53581         }
53582  
53583         
53584         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
53585         this.view.singleSelect = false;
53586         this.view.multiSelect = true;
53587         this.view.toggleSelect = true;
53588         this.pageTb.add(new Roo.Toolbar.Fill(), {
53589             
53590             text: 'Done',
53591             handler: function()
53592             {
53593                 _t.collapse();
53594             }
53595         });
53596     },
53597     
53598     onViewOver : function(e, t){
53599         // do nothing...
53600         return;
53601         
53602     },
53603     
53604     onViewClick : function(doFocus,index){
53605         return;
53606         
53607     },
53608     select: function () {
53609         //Roo.log("SELECT CALLED");
53610     },
53611      
53612     selectByValue : function(xv, scrollIntoView){
53613         var ar = this.getValueArray();
53614         var sels = [];
53615         
53616         Roo.each(ar, function(v) {
53617             if(v === undefined || v === null){
53618                 return;
53619             }
53620             var r = this.findRecord(this.valueField, v);
53621             if(r){
53622                 sels.push(this.store.indexOf(r))
53623                 
53624             }
53625         },this);
53626         this.view.select(sels);
53627         return false;
53628     },
53629     
53630     
53631     
53632     onSelect : function(record, index){
53633        // Roo.log("onselect Called");
53634        // this is only called by the clear button now..
53635         this.view.clearSelections();
53636         this.setValue('[]');
53637         if (this.value != this.valueBefore) {
53638             this.fireEvent('change', this, this.value, this.valueBefore);
53639             this.valueBefore = this.value;
53640         }
53641     },
53642     getValueArray : function()
53643     {
53644         var ar = [] ;
53645         
53646         try {
53647             //Roo.log(this.value);
53648             if (typeof(this.value) == 'undefined') {
53649                 return [];
53650             }
53651             var ar = Roo.decode(this.value);
53652             return  ar instanceof Array ? ar : []; //?? valid?
53653             
53654         } catch(e) {
53655             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
53656             return [];
53657         }
53658          
53659     },
53660     expand : function ()
53661     {
53662         
53663         Roo.form.ComboCheck.superclass.expand.call(this);
53664         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
53665         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
53666         
53667
53668     },
53669     
53670     collapse : function(){
53671         Roo.form.ComboCheck.superclass.collapse.call(this);
53672         var sl = this.view.getSelectedIndexes();
53673         var st = this.store;
53674         var nv = [];
53675         var tv = [];
53676         var r;
53677         Roo.each(sl, function(i) {
53678             r = st.getAt(i);
53679             nv.push(r.get(this.valueField));
53680         },this);
53681         this.setValue(Roo.encode(nv));
53682         if (this.value != this.valueBefore) {
53683
53684             this.fireEvent('change', this, this.value, this.valueBefore);
53685             this.valueBefore = this.value;
53686         }
53687         
53688     },
53689     
53690     setValue : function(v){
53691         // Roo.log(v);
53692         this.value = v;
53693         
53694         var vals = this.getValueArray();
53695         var tv = [];
53696         Roo.each(vals, function(k) {
53697             var r = this.findRecord(this.valueField, k);
53698             if(r){
53699                 tv.push(r.data[this.displayField]);
53700             }else if(this.valueNotFoundText !== undefined){
53701                 tv.push( this.valueNotFoundText );
53702             }
53703         },this);
53704        // Roo.log(tv);
53705         
53706         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
53707         this.hiddenField.value = v;
53708         this.value = v;
53709     }
53710     
53711 });/*
53712  * Based on:
53713  * Ext JS Library 1.1.1
53714  * Copyright(c) 2006-2007, Ext JS, LLC.
53715  *
53716  * Originally Released Under LGPL - original licence link has changed is not relivant.
53717  *
53718  * Fork - LGPL
53719  * <script type="text/javascript">
53720  */
53721  
53722 /**
53723  * @class Roo.form.Signature
53724  * @extends Roo.form.Field
53725  * Signature field.  
53726  * @constructor
53727  * 
53728  * @param {Object} config Configuration options
53729  */
53730
53731 Roo.form.Signature = function(config){
53732     Roo.form.Signature.superclass.constructor.call(this, config);
53733     
53734     this.addEvents({// not in used??
53735          /**
53736          * @event confirm
53737          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
53738              * @param {Roo.form.Signature} combo This combo box
53739              */
53740         'confirm' : true,
53741         /**
53742          * @event reset
53743          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
53744              * @param {Roo.form.ComboBox} combo This combo box
53745              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
53746              */
53747         'reset' : true
53748     });
53749 };
53750
53751 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
53752     /**
53753      * @cfg {Object} labels Label to use when rendering a form.
53754      * defaults to 
53755      * labels : { 
53756      *      clear : "Clear",
53757      *      confirm : "Confirm"
53758      *  }
53759      */
53760     labels : { 
53761         clear : "Clear",
53762         confirm : "Confirm"
53763     },
53764     /**
53765      * @cfg {Number} width The signature panel width (defaults to 300)
53766      */
53767     width: 300,
53768     /**
53769      * @cfg {Number} height The signature panel height (defaults to 100)
53770      */
53771     height : 100,
53772     /**
53773      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53774      */
53775     allowBlank : false,
53776     
53777     //private
53778     // {Object} signPanel The signature SVG panel element (defaults to {})
53779     signPanel : {},
53780     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53781     isMouseDown : false,
53782     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53783     isConfirmed : false,
53784     // {String} signatureTmp SVG mapping string (defaults to empty string)
53785     signatureTmp : '',
53786     
53787     
53788     defaultAutoCreate : { // modified by initCompnoent..
53789         tag: "input",
53790         type:"hidden"
53791     },
53792
53793     // private
53794     onRender : function(ct, position){
53795         
53796         Roo.form.Signature.superclass.onRender.call(this, ct, position);
53797         
53798         this.wrap = this.el.wrap({
53799             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53800         });
53801         
53802         this.createToolbar(this);
53803         this.signPanel = this.wrap.createChild({
53804                 tag: 'div',
53805                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53806             }, this.el
53807         );
53808             
53809         this.svgID = Roo.id();
53810         this.svgEl = this.signPanel.createChild({
53811               xmlns : 'http://www.w3.org/2000/svg',
53812               tag : 'svg',
53813               id : this.svgID + "-svg",
53814               width: this.width,
53815               height: this.height,
53816               viewBox: '0 0 '+this.width+' '+this.height,
53817               cn : [
53818                 {
53819                     tag: "rect",
53820                     id: this.svgID + "-svg-r",
53821                     width: this.width,
53822                     height: this.height,
53823                     fill: "#ffa"
53824                 },
53825                 {
53826                     tag: "line",
53827                     id: this.svgID + "-svg-l",
53828                     x1: "0", // start
53829                     y1: (this.height*0.8), // start set the line in 80% of height
53830                     x2: this.width, // end
53831                     y2: (this.height*0.8), // end set the line in 80% of height
53832                     'stroke': "#666",
53833                     'stroke-width': "1",
53834                     'stroke-dasharray': "3",
53835                     'shape-rendering': "crispEdges",
53836                     'pointer-events': "none"
53837                 },
53838                 {
53839                     tag: "path",
53840                     id: this.svgID + "-svg-p",
53841                     'stroke': "navy",
53842                     'stroke-width': "3",
53843                     'fill': "none",
53844                     'pointer-events': 'none'
53845                 }
53846               ]
53847         });
53848         this.createSVG();
53849         this.svgBox = this.svgEl.dom.getScreenCTM();
53850     },
53851     createSVG : function(){ 
53852         var svg = this.signPanel;
53853         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53854         var t = this;
53855
53856         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53857         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53858         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53859         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53860         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53861         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53862         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53863         
53864     },
53865     isTouchEvent : function(e){
53866         return e.type.match(/^touch/);
53867     },
53868     getCoords : function (e) {
53869         var pt    = this.svgEl.dom.createSVGPoint();
53870         pt.x = e.clientX; 
53871         pt.y = e.clientY;
53872         if (this.isTouchEvent(e)) {
53873             pt.x =  e.targetTouches[0].clientX;
53874             pt.y = e.targetTouches[0].clientY;
53875         }
53876         var a = this.svgEl.dom.getScreenCTM();
53877         var b = a.inverse();
53878         var mx = pt.matrixTransform(b);
53879         return mx.x + ',' + mx.y;
53880     },
53881     //mouse event headler 
53882     down : function (e) {
53883         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53884         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53885         
53886         this.isMouseDown = true;
53887         
53888         e.preventDefault();
53889     },
53890     move : function (e) {
53891         if (this.isMouseDown) {
53892             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53893             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53894         }
53895         
53896         e.preventDefault();
53897     },
53898     up : function (e) {
53899         this.isMouseDown = false;
53900         var sp = this.signatureTmp.split(' ');
53901         
53902         if(sp.length > 1){
53903             if(!sp[sp.length-2].match(/^L/)){
53904                 sp.pop();
53905                 sp.pop();
53906                 sp.push("");
53907                 this.signatureTmp = sp.join(" ");
53908             }
53909         }
53910         if(this.getValue() != this.signatureTmp){
53911             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53912             this.isConfirmed = false;
53913         }
53914         e.preventDefault();
53915     },
53916     
53917     /**
53918      * Protected method that will not generally be called directly. It
53919      * is called when the editor creates its toolbar. Override this method if you need to
53920      * add custom toolbar buttons.
53921      * @param {HtmlEditor} editor
53922      */
53923     createToolbar : function(editor){
53924          function btn(id, toggle, handler){
53925             var xid = fid + '-'+ id ;
53926             return {
53927                 id : xid,
53928                 cmd : id,
53929                 cls : 'x-btn-icon x-edit-'+id,
53930                 enableToggle:toggle !== false,
53931                 scope: editor, // was editor...
53932                 handler:handler||editor.relayBtnCmd,
53933                 clickEvent:'mousedown',
53934                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53935                 tabIndex:-1
53936             };
53937         }
53938         
53939         
53940         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53941         this.tb = tb;
53942         this.tb.add(
53943            {
53944                 cls : ' x-signature-btn x-signature-'+id,
53945                 scope: editor, // was editor...
53946                 handler: this.reset,
53947                 clickEvent:'mousedown',
53948                 text: this.labels.clear
53949             },
53950             {
53951                  xtype : 'Fill',
53952                  xns: Roo.Toolbar
53953             }, 
53954             {
53955                 cls : '  x-signature-btn x-signature-'+id,
53956                 scope: editor, // was editor...
53957                 handler: this.confirmHandler,
53958                 clickEvent:'mousedown',
53959                 text: this.labels.confirm
53960             }
53961         );
53962     
53963     },
53964     //public
53965     /**
53966      * when user is clicked confirm then show this image.....
53967      * 
53968      * @return {String} Image Data URI
53969      */
53970     getImageDataURI : function(){
53971         var svg = this.svgEl.dom.parentNode.innerHTML;
53972         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53973         return src; 
53974     },
53975     /**
53976      * 
53977      * @return {Boolean} this.isConfirmed
53978      */
53979     getConfirmed : function(){
53980         return this.isConfirmed;
53981     },
53982     /**
53983      * 
53984      * @return {Number} this.width
53985      */
53986     getWidth : function(){
53987         return this.width;
53988     },
53989     /**
53990      * 
53991      * @return {Number} this.height
53992      */
53993     getHeight : function(){
53994         return this.height;
53995     },
53996     // private
53997     getSignature : function(){
53998         return this.signatureTmp;
53999     },
54000     // private
54001     reset : function(){
54002         this.signatureTmp = '';
54003         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
54004         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
54005         this.isConfirmed = false;
54006         Roo.form.Signature.superclass.reset.call(this);
54007     },
54008     setSignature : function(s){
54009         this.signatureTmp = s;
54010         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
54011         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
54012         this.setValue(s);
54013         this.isConfirmed = false;
54014         Roo.form.Signature.superclass.reset.call(this);
54015     }, 
54016     test : function(){
54017 //        Roo.log(this.signPanel.dom.contentWindow.up())
54018     },
54019     //private
54020     setConfirmed : function(){
54021         
54022         
54023         
54024 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
54025     },
54026     // private
54027     confirmHandler : function(){
54028         if(!this.getSignature()){
54029             return;
54030         }
54031         
54032         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
54033         this.setValue(this.getSignature());
54034         this.isConfirmed = true;
54035         
54036         this.fireEvent('confirm', this);
54037     },
54038     // private
54039     // Subclasses should provide the validation implementation by overriding this
54040     validateValue : function(value){
54041         if(this.allowBlank){
54042             return true;
54043         }
54044         
54045         if(this.isConfirmed){
54046             return true;
54047         }
54048         return false;
54049     }
54050 });/*
54051  * Based on:
54052  * Ext JS Library 1.1.1
54053  * Copyright(c) 2006-2007, Ext JS, LLC.
54054  *
54055  * Originally Released Under LGPL - original licence link has changed is not relivant.
54056  *
54057  * Fork - LGPL
54058  * <script type="text/javascript">
54059  */
54060  
54061
54062 /**
54063  * @class Roo.form.ComboBox
54064  * @extends Roo.form.TriggerField
54065  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
54066  * @constructor
54067  * Create a new ComboBox.
54068  * @param {Object} config Configuration options
54069  */
54070 Roo.form.Select = function(config){
54071     Roo.form.Select.superclass.constructor.call(this, config);
54072      
54073 };
54074
54075 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
54076     /**
54077      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
54078      */
54079     /**
54080      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
54081      * rendering into an Roo.Editor, defaults to false)
54082      */
54083     /**
54084      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
54085      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
54086      */
54087     /**
54088      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
54089      */
54090     /**
54091      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
54092      * the dropdown list (defaults to undefined, with no header element)
54093      */
54094
54095      /**
54096      * @cfg {String/Roo.Template} tpl The template to use to render the output
54097      */
54098      
54099     // private
54100     defaultAutoCreate : {tag: "select"  },
54101     /**
54102      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
54103      */
54104     listWidth: undefined,
54105     /**
54106      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
54107      * mode = 'remote' or 'text' if mode = 'local')
54108      */
54109     displayField: undefined,
54110     /**
54111      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
54112      * mode = 'remote' or 'value' if mode = 'local'). 
54113      * Note: use of a valueField requires the user make a selection
54114      * in order for a value to be mapped.
54115      */
54116     valueField: undefined,
54117     
54118     
54119     /**
54120      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
54121      * field's data value (defaults to the underlying DOM element's name)
54122      */
54123     hiddenName: undefined,
54124     /**
54125      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
54126      */
54127     listClass: '',
54128     /**
54129      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
54130      */
54131     selectedClass: 'x-combo-selected',
54132     /**
54133      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
54134      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
54135      * which displays a downward arrow icon).
54136      */
54137     triggerClass : 'x-form-arrow-trigger',
54138     /**
54139      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
54140      */
54141     shadow:'sides',
54142     /**
54143      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
54144      * anchor positions (defaults to 'tl-bl')
54145      */
54146     listAlign: 'tl-bl?',
54147     /**
54148      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
54149      */
54150     maxHeight: 300,
54151     /**
54152      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
54153      * query specified by the allQuery config option (defaults to 'query')
54154      */
54155     triggerAction: 'query',
54156     /**
54157      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
54158      * (defaults to 4, does not apply if editable = false)
54159      */
54160     minChars : 4,
54161     /**
54162      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
54163      * delay (typeAheadDelay) if it matches a known value (defaults to false)
54164      */
54165     typeAhead: false,
54166     /**
54167      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
54168      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
54169      */
54170     queryDelay: 500,
54171     /**
54172      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
54173      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
54174      */
54175     pageSize: 0,
54176     /**
54177      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
54178      * when editable = true (defaults to false)
54179      */
54180     selectOnFocus:false,
54181     /**
54182      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
54183      */
54184     queryParam: 'query',
54185     /**
54186      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
54187      * when mode = 'remote' (defaults to 'Loading...')
54188      */
54189     loadingText: 'Loading...',
54190     /**
54191      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
54192      */
54193     resizable: false,
54194     /**
54195      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
54196      */
54197     handleHeight : 8,
54198     /**
54199      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
54200      * traditional select (defaults to true)
54201      */
54202     editable: true,
54203     /**
54204      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
54205      */
54206     allQuery: '',
54207     /**
54208      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
54209      */
54210     mode: 'remote',
54211     /**
54212      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
54213      * listWidth has a higher value)
54214      */
54215     minListWidth : 70,
54216     /**
54217      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
54218      * allow the user to set arbitrary text into the field (defaults to false)
54219      */
54220     forceSelection:false,
54221     /**
54222      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
54223      * if typeAhead = true (defaults to 250)
54224      */
54225     typeAheadDelay : 250,
54226     /**
54227      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
54228      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
54229      */
54230     valueNotFoundText : undefined,
54231     
54232     /**
54233      * @cfg {String} defaultValue The value displayed after loading the store.
54234      */
54235     defaultValue: '',
54236     
54237     /**
54238      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
54239      */
54240     blockFocus : false,
54241     
54242     /**
54243      * @cfg {Boolean} disableClear Disable showing of clear button.
54244      */
54245     disableClear : false,
54246     /**
54247      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
54248      */
54249     alwaysQuery : false,
54250     
54251     //private
54252     addicon : false,
54253     editicon: false,
54254     
54255     // element that contains real text value.. (when hidden is used..)
54256      
54257     // private
54258     onRender : function(ct, position){
54259         Roo.form.Field.prototype.onRender.call(this, ct, position);
54260         
54261         if(this.store){
54262             this.store.on('beforeload', this.onBeforeLoad, this);
54263             this.store.on('load', this.onLoad, this);
54264             this.store.on('loadexception', this.onLoadException, this);
54265             this.store.load({});
54266         }
54267         
54268         
54269         
54270     },
54271
54272     // private
54273     initEvents : function(){
54274         //Roo.form.ComboBox.superclass.initEvents.call(this);
54275  
54276     },
54277
54278     onDestroy : function(){
54279        
54280         if(this.store){
54281             this.store.un('beforeload', this.onBeforeLoad, this);
54282             this.store.un('load', this.onLoad, this);
54283             this.store.un('loadexception', this.onLoadException, this);
54284         }
54285         //Roo.form.ComboBox.superclass.onDestroy.call(this);
54286     },
54287
54288     // private
54289     fireKey : function(e){
54290         if(e.isNavKeyPress() && !this.list.isVisible()){
54291             this.fireEvent("specialkey", this, e);
54292         }
54293     },
54294
54295     // private
54296     onResize: function(w, h){
54297         
54298         return; 
54299     
54300         
54301     },
54302
54303     /**
54304      * Allow or prevent the user from directly editing the field text.  If false is passed,
54305      * the user will only be able to select from the items defined in the dropdown list.  This method
54306      * is the runtime equivalent of setting the 'editable' config option at config time.
54307      * @param {Boolean} value True to allow the user to directly edit the field text
54308      */
54309     setEditable : function(value){
54310          
54311     },
54312
54313     // private
54314     onBeforeLoad : function(){
54315         
54316         Roo.log("Select before load");
54317         return;
54318     
54319         this.innerList.update(this.loadingText ?
54320                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
54321         //this.restrictHeight();
54322         this.selectedIndex = -1;
54323     },
54324
54325     // private
54326     onLoad : function(){
54327
54328     
54329         var dom = this.el.dom;
54330         dom.innerHTML = '';
54331          var od = dom.ownerDocument;
54332          
54333         if (this.emptyText) {
54334             var op = od.createElement('option');
54335             op.setAttribute('value', '');
54336             op.innerHTML = String.format('{0}', this.emptyText);
54337             dom.appendChild(op);
54338         }
54339         if(this.store.getCount() > 0){
54340            
54341             var vf = this.valueField;
54342             var df = this.displayField;
54343             this.store.data.each(function(r) {
54344                 // which colmsn to use... testing - cdoe / title..
54345                 var op = od.createElement('option');
54346                 op.setAttribute('value', r.data[vf]);
54347                 op.innerHTML = String.format('{0}', r.data[df]);
54348                 dom.appendChild(op);
54349             });
54350             if (typeof(this.defaultValue != 'undefined')) {
54351                 this.setValue(this.defaultValue);
54352             }
54353             
54354              
54355         }else{
54356             //this.onEmptyResults();
54357         }
54358         //this.el.focus();
54359     },
54360     // private
54361     onLoadException : function()
54362     {
54363         dom.innerHTML = '';
54364             
54365         Roo.log("Select on load exception");
54366         return;
54367     
54368         this.collapse();
54369         Roo.log(this.store.reader.jsonData);
54370         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
54371             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
54372         }
54373         
54374         
54375     },
54376     // private
54377     onTypeAhead : function(){
54378          
54379     },
54380
54381     // private
54382     onSelect : function(record, index){
54383         Roo.log('on select?');
54384         return;
54385         if(this.fireEvent('beforeselect', this, record, index) !== false){
54386             this.setFromData(index > -1 ? record.data : false);
54387             this.collapse();
54388             this.fireEvent('select', this, record, index);
54389         }
54390     },
54391
54392     /**
54393      * Returns the currently selected field value or empty string if no value is set.
54394      * @return {String} value The selected value
54395      */
54396     getValue : function(){
54397         var dom = this.el.dom;
54398         this.value = dom.options[dom.selectedIndex].value;
54399         return this.value;
54400         
54401     },
54402
54403     /**
54404      * Clears any text/value currently set in the field
54405      */
54406     clearValue : function(){
54407         this.value = '';
54408         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
54409         
54410     },
54411
54412     /**
54413      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
54414      * will be displayed in the field.  If the value does not match the data value of an existing item,
54415      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
54416      * Otherwise the field will be blank (although the value will still be set).
54417      * @param {String} value The value to match
54418      */
54419     setValue : function(v){
54420         var d = this.el.dom;
54421         for (var i =0; i < d.options.length;i++) {
54422             if (v == d.options[i].value) {
54423                 d.selectedIndex = i;
54424                 this.value = v;
54425                 return;
54426             }
54427         }
54428         this.clearValue();
54429     },
54430     /**
54431      * @property {Object} the last set data for the element
54432      */
54433     
54434     lastData : false,
54435     /**
54436      * Sets the value of the field based on a object which is related to the record format for the store.
54437      * @param {Object} value the value to set as. or false on reset?
54438      */
54439     setFromData : function(o){
54440         Roo.log('setfrom data?');
54441          
54442         
54443         
54444     },
54445     // private
54446     reset : function(){
54447         this.clearValue();
54448     },
54449     // private
54450     findRecord : function(prop, value){
54451         
54452         return false;
54453     
54454         var record;
54455         if(this.store.getCount() > 0){
54456             this.store.each(function(r){
54457                 if(r.data[prop] == value){
54458                     record = r;
54459                     return false;
54460                 }
54461                 return true;
54462             });
54463         }
54464         return record;
54465     },
54466     
54467     getName: function()
54468     {
54469         // returns hidden if it's set..
54470         if (!this.rendered) {return ''};
54471         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
54472         
54473     },
54474      
54475
54476     
54477
54478     // private
54479     onEmptyResults : function(){
54480         Roo.log('empty results');
54481         //this.collapse();
54482     },
54483
54484     /**
54485      * Returns true if the dropdown list is expanded, else false.
54486      */
54487     isExpanded : function(){
54488         return false;
54489     },
54490
54491     /**
54492      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
54493      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54494      * @param {String} value The data value of the item to select
54495      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54496      * selected item if it is not currently in view (defaults to true)
54497      * @return {Boolean} True if the value matched an item in the list, else false
54498      */
54499     selectByValue : function(v, scrollIntoView){
54500         Roo.log('select By Value');
54501         return false;
54502     
54503         if(v !== undefined && v !== null){
54504             var r = this.findRecord(this.valueField || this.displayField, v);
54505             if(r){
54506                 this.select(this.store.indexOf(r), scrollIntoView);
54507                 return true;
54508             }
54509         }
54510         return false;
54511     },
54512
54513     /**
54514      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
54515      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54516      * @param {Number} index The zero-based index of the list item to select
54517      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54518      * selected item if it is not currently in view (defaults to true)
54519      */
54520     select : function(index, scrollIntoView){
54521         Roo.log('select ');
54522         return  ;
54523         
54524         this.selectedIndex = index;
54525         this.view.select(index);
54526         if(scrollIntoView !== false){
54527             var el = this.view.getNode(index);
54528             if(el){
54529                 this.innerList.scrollChildIntoView(el, false);
54530             }
54531         }
54532     },
54533
54534       
54535
54536     // private
54537     validateBlur : function(){
54538         
54539         return;
54540         
54541     },
54542
54543     // private
54544     initQuery : function(){
54545         this.doQuery(this.getRawValue());
54546     },
54547
54548     // private
54549     doForce : function(){
54550         if(this.el.dom.value.length > 0){
54551             this.el.dom.value =
54552                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
54553              
54554         }
54555     },
54556
54557     /**
54558      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
54559      * query allowing the query action to be canceled if needed.
54560      * @param {String} query The SQL query to execute
54561      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
54562      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
54563      * saved in the current store (defaults to false)
54564      */
54565     doQuery : function(q, forceAll){
54566         
54567         Roo.log('doQuery?');
54568         if(q === undefined || q === null){
54569             q = '';
54570         }
54571         var qe = {
54572             query: q,
54573             forceAll: forceAll,
54574             combo: this,
54575             cancel:false
54576         };
54577         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
54578             return false;
54579         }
54580         q = qe.query;
54581         forceAll = qe.forceAll;
54582         if(forceAll === true || (q.length >= this.minChars)){
54583             if(this.lastQuery != q || this.alwaysQuery){
54584                 this.lastQuery = q;
54585                 if(this.mode == 'local'){
54586                     this.selectedIndex = -1;
54587                     if(forceAll){
54588                         this.store.clearFilter();
54589                     }else{
54590                         this.store.filter(this.displayField, q);
54591                     }
54592                     this.onLoad();
54593                 }else{
54594                     this.store.baseParams[this.queryParam] = q;
54595                     this.store.load({
54596                         params: this.getParams(q)
54597                     });
54598                     this.expand();
54599                 }
54600             }else{
54601                 this.selectedIndex = -1;
54602                 this.onLoad();   
54603             }
54604         }
54605     },
54606
54607     // private
54608     getParams : function(q){
54609         var p = {};
54610         //p[this.queryParam] = q;
54611         if(this.pageSize){
54612             p.start = 0;
54613             p.limit = this.pageSize;
54614         }
54615         return p;
54616     },
54617
54618     /**
54619      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
54620      */
54621     collapse : function(){
54622         
54623     },
54624
54625     // private
54626     collapseIf : function(e){
54627         
54628     },
54629
54630     /**
54631      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
54632      */
54633     expand : function(){
54634         
54635     } ,
54636
54637     // private
54638      
54639
54640     /** 
54641     * @cfg {Boolean} grow 
54642     * @hide 
54643     */
54644     /** 
54645     * @cfg {Number} growMin 
54646     * @hide 
54647     */
54648     /** 
54649     * @cfg {Number} growMax 
54650     * @hide 
54651     */
54652     /**
54653      * @hide
54654      * @method autoSize
54655      */
54656     
54657     setWidth : function()
54658     {
54659         
54660     },
54661     getResizeEl : function(){
54662         return this.el;
54663     }
54664 });//<script type="text/javasscript">
54665  
54666
54667 /**
54668  * @class Roo.DDView
54669  * A DnD enabled version of Roo.View.
54670  * @param {Element/String} container The Element in which to create the View.
54671  * @param {String} tpl The template string used to create the markup for each element of the View
54672  * @param {Object} config The configuration properties. These include all the config options of
54673  * {@link Roo.View} plus some specific to this class.<br>
54674  * <p>
54675  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
54676  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
54677  * <p>
54678  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
54679 .x-view-drag-insert-above {
54680         border-top:1px dotted #3366cc;
54681 }
54682 .x-view-drag-insert-below {
54683         border-bottom:1px dotted #3366cc;
54684 }
54685 </code></pre>
54686  * 
54687  */
54688  
54689 Roo.DDView = function(container, tpl, config) {
54690     Roo.DDView.superclass.constructor.apply(this, arguments);
54691     this.getEl().setStyle("outline", "0px none");
54692     this.getEl().unselectable();
54693     if (this.dragGroup) {
54694                 this.setDraggable(this.dragGroup.split(","));
54695     }
54696     if (this.dropGroup) {
54697                 this.setDroppable(this.dropGroup.split(","));
54698     }
54699     if (this.deletable) {
54700         this.setDeletable();
54701     }
54702     this.isDirtyFlag = false;
54703         this.addEvents({
54704                 "drop" : true
54705         });
54706 };
54707
54708 Roo.extend(Roo.DDView, Roo.View, {
54709 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
54710 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
54711 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
54712 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
54713
54714         isFormField: true,
54715
54716         reset: Roo.emptyFn,
54717         
54718         clearInvalid: Roo.form.Field.prototype.clearInvalid,
54719
54720         validate: function() {
54721                 return true;
54722         },
54723         
54724         destroy: function() {
54725                 this.purgeListeners();
54726                 this.getEl.removeAllListeners();
54727                 this.getEl().remove();
54728                 if (this.dragZone) {
54729                         if (this.dragZone.destroy) {
54730                                 this.dragZone.destroy();
54731                         }
54732                 }
54733                 if (this.dropZone) {
54734                         if (this.dropZone.destroy) {
54735                                 this.dropZone.destroy();
54736                         }
54737                 }
54738         },
54739
54740 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
54741         getName: function() {
54742                 return this.name;
54743         },
54744
54745 /**     Loads the View from a JSON string representing the Records to put into the Store. */
54746         setValue: function(v) {
54747                 if (!this.store) {
54748                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
54749                 }
54750                 var data = {};
54751                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
54752                 this.store.proxy = new Roo.data.MemoryProxy(data);
54753                 this.store.load();
54754         },
54755
54756 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
54757         getValue: function() {
54758                 var result = '(';
54759                 this.store.each(function(rec) {
54760                         result += rec.id + ',';
54761                 });
54762                 return result.substr(0, result.length - 1) + ')';
54763         },
54764         
54765         getIds: function() {
54766                 var i = 0, result = new Array(this.store.getCount());
54767                 this.store.each(function(rec) {
54768                         result[i++] = rec.id;
54769                 });
54770                 return result;
54771         },
54772         
54773         isDirty: function() {
54774                 return this.isDirtyFlag;
54775         },
54776
54777 /**
54778  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
54779  *      whole Element becomes the target, and this causes the drop gesture to append.
54780  */
54781     getTargetFromEvent : function(e) {
54782                 var target = e.getTarget();
54783                 while ((target !== null) && (target.parentNode != this.el.dom)) {
54784                 target = target.parentNode;
54785                 }
54786                 if (!target) {
54787                         target = this.el.dom.lastChild || this.el.dom;
54788                 }
54789                 return target;
54790     },
54791
54792 /**
54793  *      Create the drag data which consists of an object which has the property "ddel" as
54794  *      the drag proxy element. 
54795  */
54796     getDragData : function(e) {
54797         var target = this.findItemFromChild(e.getTarget());
54798                 if(target) {
54799                         this.handleSelection(e);
54800                         var selNodes = this.getSelectedNodes();
54801             var dragData = {
54802                 source: this,
54803                 copy: this.copy || (this.allowCopy && e.ctrlKey),
54804                 nodes: selNodes,
54805                 records: []
54806                         };
54807                         var selectedIndices = this.getSelectedIndexes();
54808                         for (var i = 0; i < selectedIndices.length; i++) {
54809                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
54810                         }
54811                         if (selNodes.length == 1) {
54812                                 dragData.ddel = target.cloneNode(true); // the div element
54813                         } else {
54814                                 var div = document.createElement('div'); // create the multi element drag "ghost"
54815                                 div.className = 'multi-proxy';
54816                                 for (var i = 0, len = selNodes.length; i < len; i++) {
54817                                         div.appendChild(selNodes[i].cloneNode(true));
54818                                 }
54819                                 dragData.ddel = div;
54820                         }
54821             //console.log(dragData)
54822             //console.log(dragData.ddel.innerHTML)
54823                         return dragData;
54824                 }
54825         //console.log('nodragData')
54826                 return false;
54827     },
54828     
54829 /**     Specify to which ddGroup items in this DDView may be dragged. */
54830     setDraggable: function(ddGroup) {
54831         if (ddGroup instanceof Array) {
54832                 Roo.each(ddGroup, this.setDraggable, this);
54833                 return;
54834         }
54835         if (this.dragZone) {
54836                 this.dragZone.addToGroup(ddGroup);
54837         } else {
54838                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54839                                 containerScroll: true,
54840                                 ddGroup: ddGroup 
54841
54842                         });
54843 //                      Draggability implies selection. DragZone's mousedown selects the element.
54844                         if (!this.multiSelect) { this.singleSelect = true; }
54845
54846 //                      Wire the DragZone's handlers up to methods in *this*
54847                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
54848                 }
54849     },
54850
54851 /**     Specify from which ddGroup this DDView accepts drops. */
54852     setDroppable: function(ddGroup) {
54853         if (ddGroup instanceof Array) {
54854                 Roo.each(ddGroup, this.setDroppable, this);
54855                 return;
54856         }
54857         if (this.dropZone) {
54858                 this.dropZone.addToGroup(ddGroup);
54859         } else {
54860                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54861                                 containerScroll: true,
54862                                 ddGroup: ddGroup
54863                         });
54864
54865 //                      Wire the DropZone's handlers up to methods in *this*
54866                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54867                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54868                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54869                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54870                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54871                 }
54872     },
54873
54874 /**     Decide whether to drop above or below a View node. */
54875     getDropPoint : function(e, n, dd){
54876         if (n == this.el.dom) { return "above"; }
54877                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54878                 var c = t + (b - t) / 2;
54879                 var y = Roo.lib.Event.getPageY(e);
54880                 if(y <= c) {
54881                         return "above";
54882                 }else{
54883                         return "below";
54884                 }
54885     },
54886
54887     onNodeEnter : function(n, dd, e, data){
54888                 return false;
54889     },
54890     
54891     onNodeOver : function(n, dd, e, data){
54892                 var pt = this.getDropPoint(e, n, dd);
54893                 // set the insert point style on the target node
54894                 var dragElClass = this.dropNotAllowed;
54895                 if (pt) {
54896                         var targetElClass;
54897                         if (pt == "above"){
54898                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54899                                 targetElClass = "x-view-drag-insert-above";
54900                         } else {
54901                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54902                                 targetElClass = "x-view-drag-insert-below";
54903                         }
54904                         if (this.lastInsertClass != targetElClass){
54905                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54906                                 this.lastInsertClass = targetElClass;
54907                         }
54908                 }
54909                 return dragElClass;
54910         },
54911
54912     onNodeOut : function(n, dd, e, data){
54913                 this.removeDropIndicators(n);
54914     },
54915
54916     onNodeDrop : function(n, dd, e, data){
54917         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54918                 return false;
54919         }
54920         var pt = this.getDropPoint(e, n, dd);
54921                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54922                 if (pt == "below") { insertAt++; }
54923                 for (var i = 0; i < data.records.length; i++) {
54924                         var r = data.records[i];
54925                         var dup = this.store.getById(r.id);
54926                         if (dup && (dd != this.dragZone)) {
54927                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54928                         } else {
54929                                 if (data.copy) {
54930                                         this.store.insert(insertAt++, r.copy());
54931                                 } else {
54932                                         data.source.isDirtyFlag = true;
54933                                         r.store.remove(r);
54934                                         this.store.insert(insertAt++, r);
54935                                 }
54936                                 this.isDirtyFlag = true;
54937                         }
54938                 }
54939                 this.dragZone.cachedTarget = null;
54940                 return true;
54941     },
54942
54943     removeDropIndicators : function(n){
54944                 if(n){
54945                         Roo.fly(n).removeClass([
54946                                 "x-view-drag-insert-above",
54947                                 "x-view-drag-insert-below"]);
54948                         this.lastInsertClass = "_noclass";
54949                 }
54950     },
54951
54952 /**
54953  *      Utility method. Add a delete option to the DDView's context menu.
54954  *      @param {String} imageUrl The URL of the "delete" icon image.
54955  */
54956         setDeletable: function(imageUrl) {
54957                 if (!this.singleSelect && !this.multiSelect) {
54958                         this.singleSelect = true;
54959                 }
54960                 var c = this.getContextMenu();
54961                 this.contextMenu.on("itemclick", function(item) {
54962                         switch (item.id) {
54963                                 case "delete":
54964                                         this.remove(this.getSelectedIndexes());
54965                                         break;
54966                         }
54967                 }, this);
54968                 this.contextMenu.add({
54969                         icon: imageUrl,
54970                         id: "delete",
54971                         text: 'Delete'
54972                 });
54973         },
54974         
54975 /**     Return the context menu for this DDView. */
54976         getContextMenu: function() {
54977                 if (!this.contextMenu) {
54978 //                      Create the View's context menu
54979                         this.contextMenu = new Roo.menu.Menu({
54980                                 id: this.id + "-contextmenu"
54981                         });
54982                         this.el.on("contextmenu", this.showContextMenu, this);
54983                 }
54984                 return this.contextMenu;
54985         },
54986         
54987         disableContextMenu: function() {
54988                 if (this.contextMenu) {
54989                         this.el.un("contextmenu", this.showContextMenu, this);
54990                 }
54991         },
54992
54993         showContextMenu: function(e, item) {
54994         item = this.findItemFromChild(e.getTarget());
54995                 if (item) {
54996                         e.stopEvent();
54997                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54998                         this.contextMenu.showAt(e.getXY());
54999             }
55000     },
55001
55002 /**
55003  *      Remove {@link Roo.data.Record}s at the specified indices.
55004  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
55005  */
55006     remove: function(selectedIndices) {
55007                 selectedIndices = [].concat(selectedIndices);
55008                 for (var i = 0; i < selectedIndices.length; i++) {
55009                         var rec = this.store.getAt(selectedIndices[i]);
55010                         this.store.remove(rec);
55011                 }
55012     },
55013
55014 /**
55015  *      Double click fires the event, but also, if this is draggable, and there is only one other
55016  *      related DropZone, it transfers the selected node.
55017  */
55018     onDblClick : function(e){
55019         var item = this.findItemFromChild(e.getTarget());
55020         if(item){
55021             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
55022                 return false;
55023             }
55024             if (this.dragGroup) {
55025                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
55026                     while (targets.indexOf(this.dropZone) > -1) {
55027                             targets.remove(this.dropZone);
55028                                 }
55029                     if (targets.length == 1) {
55030                                         this.dragZone.cachedTarget = null;
55031                         var el = Roo.get(targets[0].getEl());
55032                         var box = el.getBox(true);
55033                         targets[0].onNodeDrop(el.dom, {
55034                                 target: el.dom,
55035                                 xy: [box.x, box.y + box.height - 1]
55036                         }, null, this.getDragData(e));
55037                     }
55038                 }
55039         }
55040     },
55041     
55042     handleSelection: function(e) {
55043                 this.dragZone.cachedTarget = null;
55044         var item = this.findItemFromChild(e.getTarget());
55045         if (!item) {
55046                 this.clearSelections(true);
55047                 return;
55048         }
55049                 if (item && (this.multiSelect || this.singleSelect)){
55050                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
55051                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
55052                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
55053                                 this.unselect(item);
55054                         } else {
55055                                 this.select(item, this.multiSelect && e.ctrlKey);
55056                                 this.lastSelection = item;
55057                         }
55058                 }
55059     },
55060
55061     onItemClick : function(item, index, e){
55062                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
55063                         return false;
55064                 }
55065                 return true;
55066     },
55067
55068     unselect : function(nodeInfo, suppressEvent){
55069                 var node = this.getNode(nodeInfo);
55070                 if(node && this.isSelected(node)){
55071                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
55072                                 Roo.fly(node).removeClass(this.selectedClass);
55073                                 this.selections.remove(node);
55074                                 if(!suppressEvent){
55075                                         this.fireEvent("selectionchange", this, this.selections);
55076                                 }
55077                         }
55078                 }
55079     }
55080 });
55081 /*
55082  * Based on:
55083  * Ext JS Library 1.1.1
55084  * Copyright(c) 2006-2007, Ext JS, LLC.
55085  *
55086  * Originally Released Under LGPL - original licence link has changed is not relivant.
55087  *
55088  * Fork - LGPL
55089  * <script type="text/javascript">
55090  */
55091  
55092 /**
55093  * @class Roo.LayoutManager
55094  * @extends Roo.util.Observable
55095  * Base class for layout managers.
55096  */
55097 Roo.LayoutManager = function(container, config){
55098     Roo.LayoutManager.superclass.constructor.call(this);
55099     this.el = Roo.get(container);
55100     // ie scrollbar fix
55101     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
55102         document.body.scroll = "no";
55103     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
55104         this.el.position('relative');
55105     }
55106     this.id = this.el.id;
55107     this.el.addClass("x-layout-container");
55108     /** false to disable window resize monitoring @type Boolean */
55109     this.monitorWindowResize = true;
55110     this.regions = {};
55111     this.addEvents({
55112         /**
55113          * @event layout
55114          * Fires when a layout is performed. 
55115          * @param {Roo.LayoutManager} this
55116          */
55117         "layout" : true,
55118         /**
55119          * @event regionresized
55120          * Fires when the user resizes a region. 
55121          * @param {Roo.LayoutRegion} region The resized region
55122          * @param {Number} newSize The new size (width for east/west, height for north/south)
55123          */
55124         "regionresized" : true,
55125         /**
55126          * @event regioncollapsed
55127          * Fires when a region is collapsed. 
55128          * @param {Roo.LayoutRegion} region The collapsed region
55129          */
55130         "regioncollapsed" : true,
55131         /**
55132          * @event regionexpanded
55133          * Fires when a region is expanded.  
55134          * @param {Roo.LayoutRegion} region The expanded region
55135          */
55136         "regionexpanded" : true
55137     });
55138     this.updating = false;
55139     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
55140 };
55141
55142 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
55143     /**
55144      * Returns true if this layout is currently being updated
55145      * @return {Boolean}
55146      */
55147     isUpdating : function(){
55148         return this.updating; 
55149     },
55150     
55151     /**
55152      * Suspend the LayoutManager from doing auto-layouts while
55153      * making multiple add or remove calls
55154      */
55155     beginUpdate : function(){
55156         this.updating = true;    
55157     },
55158     
55159     /**
55160      * Restore auto-layouts and optionally disable the manager from performing a layout
55161      * @param {Boolean} noLayout true to disable a layout update 
55162      */
55163     endUpdate : function(noLayout){
55164         this.updating = false;
55165         if(!noLayout){
55166             this.layout();
55167         }    
55168     },
55169     
55170     layout: function(){
55171         
55172     },
55173     
55174     onRegionResized : function(region, newSize){
55175         this.fireEvent("regionresized", region, newSize);
55176         this.layout();
55177     },
55178     
55179     onRegionCollapsed : function(region){
55180         this.fireEvent("regioncollapsed", region);
55181     },
55182     
55183     onRegionExpanded : function(region){
55184         this.fireEvent("regionexpanded", region);
55185     },
55186         
55187     /**
55188      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
55189      * performs box-model adjustments.
55190      * @return {Object} The size as an object {width: (the width), height: (the height)}
55191      */
55192     getViewSize : function(){
55193         var size;
55194         if(this.el.dom != document.body){
55195             size = this.el.getSize();
55196         }else{
55197             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
55198         }
55199         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
55200         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
55201         return size;
55202     },
55203     
55204     /**
55205      * Returns the Element this layout is bound to.
55206      * @return {Roo.Element}
55207      */
55208     getEl : function(){
55209         return this.el;
55210     },
55211     
55212     /**
55213      * Returns the specified region.
55214      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
55215      * @return {Roo.LayoutRegion}
55216      */
55217     getRegion : function(target){
55218         return this.regions[target.toLowerCase()];
55219     },
55220     
55221     onWindowResize : function(){
55222         if(this.monitorWindowResize){
55223             this.layout();
55224         }
55225     }
55226 });/*
55227  * Based on:
55228  * Ext JS Library 1.1.1
55229  * Copyright(c) 2006-2007, Ext JS, LLC.
55230  *
55231  * Originally Released Under LGPL - original licence link has changed is not relivant.
55232  *
55233  * Fork - LGPL
55234  * <script type="text/javascript">
55235  */
55236 /**
55237  * @class Roo.BorderLayout
55238  * @extends Roo.LayoutManager
55239  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
55240  * please see: <br><br>
55241  * <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>
55242  * <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>
55243  * Example:
55244  <pre><code>
55245  var layout = new Roo.BorderLayout(document.body, {
55246     north: {
55247         initialSize: 25,
55248         titlebar: false
55249     },
55250     west: {
55251         split:true,
55252         initialSize: 200,
55253         minSize: 175,
55254         maxSize: 400,
55255         titlebar: true,
55256         collapsible: true
55257     },
55258     east: {
55259         split:true,
55260         initialSize: 202,
55261         minSize: 175,
55262         maxSize: 400,
55263         titlebar: true,
55264         collapsible: true
55265     },
55266     south: {
55267         split:true,
55268         initialSize: 100,
55269         minSize: 100,
55270         maxSize: 200,
55271         titlebar: true,
55272         collapsible: true
55273     },
55274     center: {
55275         titlebar: true,
55276         autoScroll:true,
55277         resizeTabs: true,
55278         minTabWidth: 50,
55279         preferredTabWidth: 150
55280     }
55281 });
55282
55283 // shorthand
55284 var CP = Roo.ContentPanel;
55285
55286 layout.beginUpdate();
55287 layout.add("north", new CP("north", "North"));
55288 layout.add("south", new CP("south", {title: "South", closable: true}));
55289 layout.add("west", new CP("west", {title: "West"}));
55290 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
55291 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
55292 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
55293 layout.getRegion("center").showPanel("center1");
55294 layout.endUpdate();
55295 </code></pre>
55296
55297 <b>The container the layout is rendered into can be either the body element or any other element.
55298 If it is not the body element, the container needs to either be an absolute positioned element,
55299 or you will need to add "position:relative" to the css of the container.  You will also need to specify
55300 the container size if it is not the body element.</b>
55301
55302 * @constructor
55303 * Create a new BorderLayout
55304 * @param {String/HTMLElement/Element} container The container this layout is bound to
55305 * @param {Object} config Configuration options
55306  */
55307 Roo.BorderLayout = function(container, config){
55308     config = config || {};
55309     Roo.BorderLayout.superclass.constructor.call(this, container, config);
55310     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
55311     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
55312         var target = this.factory.validRegions[i];
55313         if(config[target]){
55314             this.addRegion(target, config[target]);
55315         }
55316     }
55317 };
55318
55319 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
55320     /**
55321      * Creates and adds a new region if it doesn't already exist.
55322      * @param {String} target The target region key (north, south, east, west or center).
55323      * @param {Object} config The regions config object
55324      * @return {BorderLayoutRegion} The new region
55325      */
55326     addRegion : function(target, config){
55327         if(!this.regions[target]){
55328             var r = this.factory.create(target, this, config);
55329             this.bindRegion(target, r);
55330         }
55331         return this.regions[target];
55332     },
55333
55334     // private (kinda)
55335     bindRegion : function(name, r){
55336         this.regions[name] = r;
55337         r.on("visibilitychange", this.layout, this);
55338         r.on("paneladded", this.layout, this);
55339         r.on("panelremoved", this.layout, this);
55340         r.on("invalidated", this.layout, this);
55341         r.on("resized", this.onRegionResized, this);
55342         r.on("collapsed", this.onRegionCollapsed, this);
55343         r.on("expanded", this.onRegionExpanded, this);
55344     },
55345
55346     /**
55347      * Performs a layout update.
55348      */
55349     layout : function(){
55350         if(this.updating) {
55351             return;
55352         }
55353         var size = this.getViewSize();
55354         var w = size.width;
55355         var h = size.height;
55356         var centerW = w;
55357         var centerH = h;
55358         var centerY = 0;
55359         var centerX = 0;
55360         //var x = 0, y = 0;
55361
55362         var rs = this.regions;
55363         var north = rs["north"];
55364         var south = rs["south"]; 
55365         var west = rs["west"];
55366         var east = rs["east"];
55367         var center = rs["center"];
55368         //if(this.hideOnLayout){ // not supported anymore
55369             //c.el.setStyle("display", "none");
55370         //}
55371         if(north && north.isVisible()){
55372             var b = north.getBox();
55373             var m = north.getMargins();
55374             b.width = w - (m.left+m.right);
55375             b.x = m.left;
55376             b.y = m.top;
55377             centerY = b.height + b.y + m.bottom;
55378             centerH -= centerY;
55379             north.updateBox(this.safeBox(b));
55380         }
55381         if(south && south.isVisible()){
55382             var b = south.getBox();
55383             var m = south.getMargins();
55384             b.width = w - (m.left+m.right);
55385             b.x = m.left;
55386             var totalHeight = (b.height + m.top + m.bottom);
55387             b.y = h - totalHeight + m.top;
55388             centerH -= totalHeight;
55389             south.updateBox(this.safeBox(b));
55390         }
55391         if(west && west.isVisible()){
55392             var b = west.getBox();
55393             var m = west.getMargins();
55394             b.height = centerH - (m.top+m.bottom);
55395             b.x = m.left;
55396             b.y = centerY + m.top;
55397             var totalWidth = (b.width + m.left + m.right);
55398             centerX += totalWidth;
55399             centerW -= totalWidth;
55400             west.updateBox(this.safeBox(b));
55401         }
55402         if(east && east.isVisible()){
55403             var b = east.getBox();
55404             var m = east.getMargins();
55405             b.height = centerH - (m.top+m.bottom);
55406             var totalWidth = (b.width + m.left + m.right);
55407             b.x = w - totalWidth + m.left;
55408             b.y = centerY + m.top;
55409             centerW -= totalWidth;
55410             east.updateBox(this.safeBox(b));
55411         }
55412         if(center){
55413             var m = center.getMargins();
55414             var centerBox = {
55415                 x: centerX + m.left,
55416                 y: centerY + m.top,
55417                 width: centerW - (m.left+m.right),
55418                 height: centerH - (m.top+m.bottom)
55419             };
55420             //if(this.hideOnLayout){
55421                 //center.el.setStyle("display", "block");
55422             //}
55423             center.updateBox(this.safeBox(centerBox));
55424         }
55425         this.el.repaint();
55426         this.fireEvent("layout", this);
55427     },
55428
55429     // private
55430     safeBox : function(box){
55431         box.width = Math.max(0, box.width);
55432         box.height = Math.max(0, box.height);
55433         return box;
55434     },
55435
55436     /**
55437      * Adds a ContentPanel (or subclass) to this layout.
55438      * @param {String} target The target region key (north, south, east, west or center).
55439      * @param {Roo.ContentPanel} panel The panel to add
55440      * @return {Roo.ContentPanel} The added panel
55441      */
55442     add : function(target, panel){
55443          
55444         target = target.toLowerCase();
55445         return this.regions[target].add(panel);
55446     },
55447
55448     /**
55449      * Remove a ContentPanel (or subclass) to this layout.
55450      * @param {String} target The target region key (north, south, east, west or center).
55451      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
55452      * @return {Roo.ContentPanel} The removed panel
55453      */
55454     remove : function(target, panel){
55455         target = target.toLowerCase();
55456         return this.regions[target].remove(panel);
55457     },
55458
55459     /**
55460      * Searches all regions for a panel with the specified id
55461      * @param {String} panelId
55462      * @return {Roo.ContentPanel} The panel or null if it wasn't found
55463      */
55464     findPanel : function(panelId){
55465         var rs = this.regions;
55466         for(var target in rs){
55467             if(typeof rs[target] != "function"){
55468                 var p = rs[target].getPanel(panelId);
55469                 if(p){
55470                     return p;
55471                 }
55472             }
55473         }
55474         return null;
55475     },
55476
55477     /**
55478      * Searches all regions for a panel with the specified id and activates (shows) it.
55479      * @param {String/ContentPanel} panelId The panels id or the panel itself
55480      * @return {Roo.ContentPanel} The shown panel or null
55481      */
55482     showPanel : function(panelId) {
55483       var rs = this.regions;
55484       for(var target in rs){
55485          var r = rs[target];
55486          if(typeof r != "function"){
55487             if(r.hasPanel(panelId)){
55488                return r.showPanel(panelId);
55489             }
55490          }
55491       }
55492       return null;
55493    },
55494
55495    /**
55496      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
55497      * @param {Roo.state.Provider} provider (optional) An alternate state provider
55498      */
55499     restoreState : function(provider){
55500         if(!provider){
55501             provider = Roo.state.Manager;
55502         }
55503         var sm = new Roo.LayoutStateManager();
55504         sm.init(this, provider);
55505     },
55506
55507     /**
55508      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
55509      * object should contain properties for each region to add ContentPanels to, and each property's value should be
55510      * a valid ContentPanel config object.  Example:
55511      * <pre><code>
55512 // Create the main layout
55513 var layout = new Roo.BorderLayout('main-ct', {
55514     west: {
55515         split:true,
55516         minSize: 175,
55517         titlebar: true
55518     },
55519     center: {
55520         title:'Components'
55521     }
55522 }, 'main-ct');
55523
55524 // Create and add multiple ContentPanels at once via configs
55525 layout.batchAdd({
55526    west: {
55527        id: 'source-files',
55528        autoCreate:true,
55529        title:'Ext Source Files',
55530        autoScroll:true,
55531        fitToFrame:true
55532    },
55533    center : {
55534        el: cview,
55535        autoScroll:true,
55536        fitToFrame:true,
55537        toolbar: tb,
55538        resizeEl:'cbody'
55539    }
55540 });
55541 </code></pre>
55542      * @param {Object} regions An object containing ContentPanel configs by region name
55543      */
55544     batchAdd : function(regions){
55545         this.beginUpdate();
55546         for(var rname in regions){
55547             var lr = this.regions[rname];
55548             if(lr){
55549                 this.addTypedPanels(lr, regions[rname]);
55550             }
55551         }
55552         this.endUpdate();
55553     },
55554
55555     // private
55556     addTypedPanels : function(lr, ps){
55557         if(typeof ps == 'string'){
55558             lr.add(new Roo.ContentPanel(ps));
55559         }
55560         else if(ps instanceof Array){
55561             for(var i =0, len = ps.length; i < len; i++){
55562                 this.addTypedPanels(lr, ps[i]);
55563             }
55564         }
55565         else if(!ps.events){ // raw config?
55566             var el = ps.el;
55567             delete ps.el; // prevent conflict
55568             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
55569         }
55570         else {  // panel object assumed!
55571             lr.add(ps);
55572         }
55573     },
55574     /**
55575      * Adds a xtype elements to the layout.
55576      * <pre><code>
55577
55578 layout.addxtype({
55579        xtype : 'ContentPanel',
55580        region: 'west',
55581        items: [ .... ]
55582    }
55583 );
55584
55585 layout.addxtype({
55586         xtype : 'NestedLayoutPanel',
55587         region: 'west',
55588         layout: {
55589            center: { },
55590            west: { }   
55591         },
55592         items : [ ... list of content panels or nested layout panels.. ]
55593    }
55594 );
55595 </code></pre>
55596      * @param {Object} cfg Xtype definition of item to add.
55597      */
55598     addxtype : function(cfg)
55599     {
55600         // basically accepts a pannel...
55601         // can accept a layout region..!?!?
55602         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
55603         
55604         if (!cfg.xtype.match(/Panel$/)) {
55605             return false;
55606         }
55607         var ret = false;
55608         
55609         if (typeof(cfg.region) == 'undefined') {
55610             Roo.log("Failed to add Panel, region was not set");
55611             Roo.log(cfg);
55612             return false;
55613         }
55614         var region = cfg.region;
55615         delete cfg.region;
55616         
55617           
55618         var xitems = [];
55619         if (cfg.items) {
55620             xitems = cfg.items;
55621             delete cfg.items;
55622         }
55623         var nb = false;
55624         
55625         switch(cfg.xtype) 
55626         {
55627             case 'ContentPanel':  // ContentPanel (el, cfg)
55628             case 'ScrollPanel':  // ContentPanel (el, cfg)
55629             case 'ViewPanel': 
55630                 if(cfg.autoCreate) {
55631                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55632                 } else {
55633                     var el = this.el.createChild();
55634                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
55635                 }
55636                 
55637                 this.add(region, ret);
55638                 break;
55639             
55640             
55641             case 'TreePanel': // our new panel!
55642                 cfg.el = this.el.createChild();
55643                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55644                 this.add(region, ret);
55645                 break;
55646             
55647             case 'NestedLayoutPanel': 
55648                 // create a new Layout (which is  a Border Layout...
55649                 var el = this.el.createChild();
55650                 var clayout = cfg.layout;
55651                 delete cfg.layout;
55652                 clayout.items   = clayout.items  || [];
55653                 // replace this exitems with the clayout ones..
55654                 xitems = clayout.items;
55655                  
55656                 
55657                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
55658                     cfg.background = false;
55659                 }
55660                 var layout = new Roo.BorderLayout(el, clayout);
55661                 
55662                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
55663                 //console.log('adding nested layout panel '  + cfg.toSource());
55664                 this.add(region, ret);
55665                 nb = {}; /// find first...
55666                 break;
55667                 
55668             case 'GridPanel': 
55669             
55670                 // needs grid and region
55671                 
55672                 //var el = this.getRegion(region).el.createChild();
55673                 var el = this.el.createChild();
55674                 // create the grid first...
55675                 
55676                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
55677                 delete cfg.grid;
55678                 if (region == 'center' && this.active ) {
55679                     cfg.background = false;
55680                 }
55681                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
55682                 
55683                 this.add(region, ret);
55684                 if (cfg.background) {
55685                     ret.on('activate', function(gp) {
55686                         if (!gp.grid.rendered) {
55687                             gp.grid.render();
55688                         }
55689                     });
55690                 } else {
55691                     grid.render();
55692                 }
55693                 break;
55694            
55695            
55696            
55697                 
55698                 
55699                 
55700             default:
55701                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
55702                     
55703                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55704                     this.add(region, ret);
55705                 } else {
55706                 
55707                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
55708                     return null;
55709                 }
55710                 
55711              // GridPanel (grid, cfg)
55712             
55713         }
55714         this.beginUpdate();
55715         // add children..
55716         var region = '';
55717         var abn = {};
55718         Roo.each(xitems, function(i)  {
55719             region = nb && i.region ? i.region : false;
55720             
55721             var add = ret.addxtype(i);
55722            
55723             if (region) {
55724                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
55725                 if (!i.background) {
55726                     abn[region] = nb[region] ;
55727                 }
55728             }
55729             
55730         });
55731         this.endUpdate();
55732
55733         // make the last non-background panel active..
55734         //if (nb) { Roo.log(abn); }
55735         if (nb) {
55736             
55737             for(var r in abn) {
55738                 region = this.getRegion(r);
55739                 if (region) {
55740                     // tried using nb[r], but it does not work..
55741                      
55742                     region.showPanel(abn[r]);
55743                    
55744                 }
55745             }
55746         }
55747         return ret;
55748         
55749     }
55750 });
55751
55752 /**
55753  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55754  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
55755  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55756  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
55757  * <pre><code>
55758 // shorthand
55759 var CP = Roo.ContentPanel;
55760
55761 var layout = Roo.BorderLayout.create({
55762     north: {
55763         initialSize: 25,
55764         titlebar: false,
55765         panels: [new CP("north", "North")]
55766     },
55767     west: {
55768         split:true,
55769         initialSize: 200,
55770         minSize: 175,
55771         maxSize: 400,
55772         titlebar: true,
55773         collapsible: true,
55774         panels: [new CP("west", {title: "West"})]
55775     },
55776     east: {
55777         split:true,
55778         initialSize: 202,
55779         minSize: 175,
55780         maxSize: 400,
55781         titlebar: true,
55782         collapsible: true,
55783         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55784     },
55785     south: {
55786         split:true,
55787         initialSize: 100,
55788         minSize: 100,
55789         maxSize: 200,
55790         titlebar: true,
55791         collapsible: true,
55792         panels: [new CP("south", {title: "South", closable: true})]
55793     },
55794     center: {
55795         titlebar: true,
55796         autoScroll:true,
55797         resizeTabs: true,
55798         minTabWidth: 50,
55799         preferredTabWidth: 150,
55800         panels: [
55801             new CP("center1", {title: "Close Me", closable: true}),
55802             new CP("center2", {title: "Center Panel", closable: false})
55803         ]
55804     }
55805 }, document.body);
55806
55807 layout.getRegion("center").showPanel("center1");
55808 </code></pre>
55809  * @param config
55810  * @param targetEl
55811  */
55812 Roo.BorderLayout.create = function(config, targetEl){
55813     var layout = new Roo.BorderLayout(targetEl || document.body, config);
55814     layout.beginUpdate();
55815     var regions = Roo.BorderLayout.RegionFactory.validRegions;
55816     for(var j = 0, jlen = regions.length; j < jlen; j++){
55817         var lr = regions[j];
55818         if(layout.regions[lr] && config[lr].panels){
55819             var r = layout.regions[lr];
55820             var ps = config[lr].panels;
55821             layout.addTypedPanels(r, ps);
55822         }
55823     }
55824     layout.endUpdate();
55825     return layout;
55826 };
55827
55828 // private
55829 Roo.BorderLayout.RegionFactory = {
55830     // private
55831     validRegions : ["north","south","east","west","center"],
55832
55833     // private
55834     create : function(target, mgr, config){
55835         target = target.toLowerCase();
55836         if(config.lightweight || config.basic){
55837             return new Roo.BasicLayoutRegion(mgr, config, target);
55838         }
55839         switch(target){
55840             case "north":
55841                 return new Roo.NorthLayoutRegion(mgr, config);
55842             case "south":
55843                 return new Roo.SouthLayoutRegion(mgr, config);
55844             case "east":
55845                 return new Roo.EastLayoutRegion(mgr, config);
55846             case "west":
55847                 return new Roo.WestLayoutRegion(mgr, config);
55848             case "center":
55849                 return new Roo.CenterLayoutRegion(mgr, config);
55850         }
55851         throw 'Layout region "'+target+'" not supported.';
55852     }
55853 };/*
55854  * Based on:
55855  * Ext JS Library 1.1.1
55856  * Copyright(c) 2006-2007, Ext JS, LLC.
55857  *
55858  * Originally Released Under LGPL - original licence link has changed is not relivant.
55859  *
55860  * Fork - LGPL
55861  * <script type="text/javascript">
55862  */
55863  
55864 /**
55865  * @class Roo.BasicLayoutRegion
55866  * @extends Roo.util.Observable
55867  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55868  * and does not have a titlebar, tabs or any other features. All it does is size and position 
55869  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55870  */
55871 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55872     this.mgr = mgr;
55873     this.position  = pos;
55874     this.events = {
55875         /**
55876          * @scope Roo.BasicLayoutRegion
55877          */
55878         
55879         /**
55880          * @event beforeremove
55881          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55882          * @param {Roo.LayoutRegion} this
55883          * @param {Roo.ContentPanel} panel The panel
55884          * @param {Object} e The cancel event object
55885          */
55886         "beforeremove" : true,
55887         /**
55888          * @event invalidated
55889          * Fires when the layout for this region is changed.
55890          * @param {Roo.LayoutRegion} this
55891          */
55892         "invalidated" : true,
55893         /**
55894          * @event visibilitychange
55895          * Fires when this region is shown or hidden 
55896          * @param {Roo.LayoutRegion} this
55897          * @param {Boolean} visibility true or false
55898          */
55899         "visibilitychange" : true,
55900         /**
55901          * @event paneladded
55902          * Fires when a panel is added. 
55903          * @param {Roo.LayoutRegion} this
55904          * @param {Roo.ContentPanel} panel The panel
55905          */
55906         "paneladded" : true,
55907         /**
55908          * @event panelremoved
55909          * Fires when a panel is removed. 
55910          * @param {Roo.LayoutRegion} this
55911          * @param {Roo.ContentPanel} panel The panel
55912          */
55913         "panelremoved" : true,
55914         /**
55915          * @event beforecollapse
55916          * Fires when this region before collapse.
55917          * @param {Roo.LayoutRegion} this
55918          */
55919         "beforecollapse" : true,
55920         /**
55921          * @event collapsed
55922          * Fires when this region is collapsed.
55923          * @param {Roo.LayoutRegion} this
55924          */
55925         "collapsed" : true,
55926         /**
55927          * @event expanded
55928          * Fires when this region is expanded.
55929          * @param {Roo.LayoutRegion} this
55930          */
55931         "expanded" : true,
55932         /**
55933          * @event slideshow
55934          * Fires when this region is slid into view.
55935          * @param {Roo.LayoutRegion} this
55936          */
55937         "slideshow" : true,
55938         /**
55939          * @event slidehide
55940          * Fires when this region slides out of view. 
55941          * @param {Roo.LayoutRegion} this
55942          */
55943         "slidehide" : true,
55944         /**
55945          * @event panelactivated
55946          * Fires when a panel is activated. 
55947          * @param {Roo.LayoutRegion} this
55948          * @param {Roo.ContentPanel} panel The activated panel
55949          */
55950         "panelactivated" : true,
55951         /**
55952          * @event resized
55953          * Fires when the user resizes this region. 
55954          * @param {Roo.LayoutRegion} this
55955          * @param {Number} newSize The new size (width for east/west, height for north/south)
55956          */
55957         "resized" : true
55958     };
55959     /** A collection of panels in this region. @type Roo.util.MixedCollection */
55960     this.panels = new Roo.util.MixedCollection();
55961     this.panels.getKey = this.getPanelId.createDelegate(this);
55962     this.box = null;
55963     this.activePanel = null;
55964     // ensure listeners are added...
55965     
55966     if (config.listeners || config.events) {
55967         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55968             listeners : config.listeners || {},
55969             events : config.events || {}
55970         });
55971     }
55972     
55973     if(skipConfig !== true){
55974         this.applyConfig(config);
55975     }
55976 };
55977
55978 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55979     getPanelId : function(p){
55980         return p.getId();
55981     },
55982     
55983     applyConfig : function(config){
55984         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55985         this.config = config;
55986         
55987     },
55988     
55989     /**
55990      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
55991      * the width, for horizontal (north, south) the height.
55992      * @param {Number} newSize The new width or height
55993      */
55994     resizeTo : function(newSize){
55995         var el = this.el ? this.el :
55996                  (this.activePanel ? this.activePanel.getEl() : null);
55997         if(el){
55998             switch(this.position){
55999                 case "east":
56000                 case "west":
56001                     el.setWidth(newSize);
56002                     this.fireEvent("resized", this, newSize);
56003                 break;
56004                 case "north":
56005                 case "south":
56006                     el.setHeight(newSize);
56007                     this.fireEvent("resized", this, newSize);
56008                 break;                
56009             }
56010         }
56011     },
56012     
56013     getBox : function(){
56014         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
56015     },
56016     
56017     getMargins : function(){
56018         return this.margins;
56019     },
56020     
56021     updateBox : function(box){
56022         this.box = box;
56023         var el = this.activePanel.getEl();
56024         el.dom.style.left = box.x + "px";
56025         el.dom.style.top = box.y + "px";
56026         this.activePanel.setSize(box.width, box.height);
56027     },
56028     
56029     /**
56030      * Returns the container element for this region.
56031      * @return {Roo.Element}
56032      */
56033     getEl : function(){
56034         return this.activePanel;
56035     },
56036     
56037     /**
56038      * Returns true if this region is currently visible.
56039      * @return {Boolean}
56040      */
56041     isVisible : function(){
56042         return this.activePanel ? true : false;
56043     },
56044     
56045     setActivePanel : function(panel){
56046         panel = this.getPanel(panel);
56047         if(this.activePanel && this.activePanel != panel){
56048             this.activePanel.setActiveState(false);
56049             this.activePanel.getEl().setLeftTop(-10000,-10000);
56050         }
56051         this.activePanel = panel;
56052         panel.setActiveState(true);
56053         if(this.box){
56054             panel.setSize(this.box.width, this.box.height);
56055         }
56056         this.fireEvent("panelactivated", this, panel);
56057         this.fireEvent("invalidated");
56058     },
56059     
56060     /**
56061      * Show the specified panel.
56062      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
56063      * @return {Roo.ContentPanel} The shown panel or null
56064      */
56065     showPanel : function(panel){
56066         if(panel = this.getPanel(panel)){
56067             this.setActivePanel(panel);
56068         }
56069         return panel;
56070     },
56071     
56072     /**
56073      * Get the active panel for this region.
56074      * @return {Roo.ContentPanel} The active panel or null
56075      */
56076     getActivePanel : function(){
56077         return this.activePanel;
56078     },
56079     
56080     /**
56081      * Add the passed ContentPanel(s)
56082      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
56083      * @return {Roo.ContentPanel} The panel added (if only one was added)
56084      */
56085     add : function(panel){
56086         if(arguments.length > 1){
56087             for(var i = 0, len = arguments.length; i < len; i++) {
56088                 this.add(arguments[i]);
56089             }
56090             return null;
56091         }
56092         if(this.hasPanel(panel)){
56093             this.showPanel(panel);
56094             return panel;
56095         }
56096         var el = panel.getEl();
56097         if(el.dom.parentNode != this.mgr.el.dom){
56098             this.mgr.el.dom.appendChild(el.dom);
56099         }
56100         if(panel.setRegion){
56101             panel.setRegion(this);
56102         }
56103         this.panels.add(panel);
56104         el.setStyle("position", "absolute");
56105         if(!panel.background){
56106             this.setActivePanel(panel);
56107             if(this.config.initialSize && this.panels.getCount()==1){
56108                 this.resizeTo(this.config.initialSize);
56109             }
56110         }
56111         this.fireEvent("paneladded", this, panel);
56112         return panel;
56113     },
56114     
56115     /**
56116      * Returns true if the panel is in this region.
56117      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
56118      * @return {Boolean}
56119      */
56120     hasPanel : function(panel){
56121         if(typeof panel == "object"){ // must be panel obj
56122             panel = panel.getId();
56123         }
56124         return this.getPanel(panel) ? true : false;
56125     },
56126     
56127     /**
56128      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56129      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
56130      * @param {Boolean} preservePanel Overrides the config preservePanel option
56131      * @return {Roo.ContentPanel} The panel that was removed
56132      */
56133     remove : function(panel, preservePanel){
56134         panel = this.getPanel(panel);
56135         if(!panel){
56136             return null;
56137         }
56138         var e = {};
56139         this.fireEvent("beforeremove", this, panel, e);
56140         if(e.cancel === true){
56141             return null;
56142         }
56143         var panelId = panel.getId();
56144         this.panels.removeKey(panelId);
56145         return panel;
56146     },
56147     
56148     /**
56149      * Returns the panel specified or null if it's not in this region.
56150      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
56151      * @return {Roo.ContentPanel}
56152      */
56153     getPanel : function(id){
56154         if(typeof id == "object"){ // must be panel obj
56155             return id;
56156         }
56157         return this.panels.get(id);
56158     },
56159     
56160     /**
56161      * Returns this regions position (north/south/east/west/center).
56162      * @return {String} 
56163      */
56164     getPosition: function(){
56165         return this.position;    
56166     }
56167 });/*
56168  * Based on:
56169  * Ext JS Library 1.1.1
56170  * Copyright(c) 2006-2007, Ext JS, LLC.
56171  *
56172  * Originally Released Under LGPL - original licence link has changed is not relivant.
56173  *
56174  * Fork - LGPL
56175  * <script type="text/javascript">
56176  */
56177  
56178 /**
56179  * @class Roo.LayoutRegion
56180  * @extends Roo.BasicLayoutRegion
56181  * This class represents a region in a layout manager.
56182  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
56183  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
56184  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
56185  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
56186  * @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})
56187  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
56188  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
56189  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
56190  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
56191  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
56192  * @cfg {String}    title           The title for the region (overrides panel titles)
56193  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
56194  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
56195  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
56196  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
56197  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
56198  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
56199  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
56200  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
56201  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
56202  * @cfg {Boolean}   showPin         True to show a pin button
56203  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
56204  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
56205  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
56206  * @cfg {Number}    width           For East/West panels
56207  * @cfg {Number}    height          For North/South panels
56208  * @cfg {Boolean}   split           To show the splitter
56209  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
56210  */
56211 Roo.LayoutRegion = function(mgr, config, pos){
56212     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
56213     var dh = Roo.DomHelper;
56214     /** This region's container element 
56215     * @type Roo.Element */
56216     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
56217     /** This region's title element 
56218     * @type Roo.Element */
56219
56220     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
56221         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
56222         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
56223     ]}, true);
56224     this.titleEl.enableDisplayMode();
56225     /** This region's title text element 
56226     * @type HTMLElement */
56227     this.titleTextEl = this.titleEl.dom.firstChild;
56228     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
56229     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
56230     this.closeBtn.enableDisplayMode();
56231     this.closeBtn.on("click", this.closeClicked, this);
56232     this.closeBtn.hide();
56233
56234     this.createBody(config);
56235     this.visible = true;
56236     this.collapsed = false;
56237
56238     if(config.hideWhenEmpty){
56239         this.hide();
56240         this.on("paneladded", this.validateVisibility, this);
56241         this.on("panelremoved", this.validateVisibility, this);
56242     }
56243     this.applyConfig(config);
56244 };
56245
56246 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
56247
56248     createBody : function(){
56249         /** This region's body element 
56250         * @type Roo.Element */
56251         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
56252     },
56253
56254     applyConfig : function(c){
56255         if(c.collapsible && this.position != "center" && !this.collapsedEl){
56256             var dh = Roo.DomHelper;
56257             if(c.titlebar !== false){
56258                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
56259                 this.collapseBtn.on("click", this.collapse, this);
56260                 this.collapseBtn.enableDisplayMode();
56261
56262                 if(c.showPin === true || this.showPin){
56263                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
56264                     this.stickBtn.enableDisplayMode();
56265                     this.stickBtn.on("click", this.expand, this);
56266                     this.stickBtn.hide();
56267                 }
56268             }
56269             /** This region's collapsed element
56270             * @type Roo.Element */
56271             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
56272                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
56273             ]}, true);
56274             if(c.floatable !== false){
56275                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
56276                this.collapsedEl.on("click", this.collapseClick, this);
56277             }
56278
56279             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
56280                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
56281                    id: "message", unselectable: "on", style:{"float":"left"}});
56282                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
56283              }
56284             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
56285             this.expandBtn.on("click", this.expand, this);
56286         }
56287         if(this.collapseBtn){
56288             this.collapseBtn.setVisible(c.collapsible == true);
56289         }
56290         this.cmargins = c.cmargins || this.cmargins ||
56291                          (this.position == "west" || this.position == "east" ?
56292                              {top: 0, left: 2, right:2, bottom: 0} :
56293                              {top: 2, left: 0, right:0, bottom: 2});
56294         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
56295         this.bottomTabs = c.tabPosition != "top";
56296         this.autoScroll = c.autoScroll || false;
56297         if(this.autoScroll){
56298             this.bodyEl.setStyle("overflow", "auto");
56299         }else{
56300             this.bodyEl.setStyle("overflow", "hidden");
56301         }
56302         //if(c.titlebar !== false){
56303             if((!c.titlebar && !c.title) || c.titlebar === false){
56304                 this.titleEl.hide();
56305             }else{
56306                 this.titleEl.show();
56307                 if(c.title){
56308                     this.titleTextEl.innerHTML = c.title;
56309                 }
56310             }
56311         //}
56312         this.duration = c.duration || .30;
56313         this.slideDuration = c.slideDuration || .45;
56314         this.config = c;
56315         if(c.collapsed){
56316             this.collapse(true);
56317         }
56318         if(c.hidden){
56319             this.hide();
56320         }
56321     },
56322     /**
56323      * Returns true if this region is currently visible.
56324      * @return {Boolean}
56325      */
56326     isVisible : function(){
56327         return this.visible;
56328     },
56329
56330     /**
56331      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
56332      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
56333      */
56334     setCollapsedTitle : function(title){
56335         title = title || "&#160;";
56336         if(this.collapsedTitleTextEl){
56337             this.collapsedTitleTextEl.innerHTML = title;
56338         }
56339     },
56340
56341     getBox : function(){
56342         var b;
56343         if(!this.collapsed){
56344             b = this.el.getBox(false, true);
56345         }else{
56346             b = this.collapsedEl.getBox(false, true);
56347         }
56348         return b;
56349     },
56350
56351     getMargins : function(){
56352         return this.collapsed ? this.cmargins : this.margins;
56353     },
56354
56355     highlight : function(){
56356         this.el.addClass("x-layout-panel-dragover");
56357     },
56358
56359     unhighlight : function(){
56360         this.el.removeClass("x-layout-panel-dragover");
56361     },
56362
56363     updateBox : function(box){
56364         this.box = box;
56365         if(!this.collapsed){
56366             this.el.dom.style.left = box.x + "px";
56367             this.el.dom.style.top = box.y + "px";
56368             this.updateBody(box.width, box.height);
56369         }else{
56370             this.collapsedEl.dom.style.left = box.x + "px";
56371             this.collapsedEl.dom.style.top = box.y + "px";
56372             this.collapsedEl.setSize(box.width, box.height);
56373         }
56374         if(this.tabs){
56375             this.tabs.autoSizeTabs();
56376         }
56377     },
56378
56379     updateBody : function(w, h){
56380         if(w !== null){
56381             this.el.setWidth(w);
56382             w -= this.el.getBorderWidth("rl");
56383             if(this.config.adjustments){
56384                 w += this.config.adjustments[0];
56385             }
56386         }
56387         if(h !== null){
56388             this.el.setHeight(h);
56389             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
56390             h -= this.el.getBorderWidth("tb");
56391             if(this.config.adjustments){
56392                 h += this.config.adjustments[1];
56393             }
56394             this.bodyEl.setHeight(h);
56395             if(this.tabs){
56396                 h = this.tabs.syncHeight(h);
56397             }
56398         }
56399         if(this.panelSize){
56400             w = w !== null ? w : this.panelSize.width;
56401             h = h !== null ? h : this.panelSize.height;
56402         }
56403         if(this.activePanel){
56404             var el = this.activePanel.getEl();
56405             w = w !== null ? w : el.getWidth();
56406             h = h !== null ? h : el.getHeight();
56407             this.panelSize = {width: w, height: h};
56408             this.activePanel.setSize(w, h);
56409         }
56410         if(Roo.isIE && this.tabs){
56411             this.tabs.el.repaint();
56412         }
56413     },
56414
56415     /**
56416      * Returns the container element for this region.
56417      * @return {Roo.Element}
56418      */
56419     getEl : function(){
56420         return this.el;
56421     },
56422
56423     /**
56424      * Hides this region.
56425      */
56426     hide : function(){
56427         if(!this.collapsed){
56428             this.el.dom.style.left = "-2000px";
56429             this.el.hide();
56430         }else{
56431             this.collapsedEl.dom.style.left = "-2000px";
56432             this.collapsedEl.hide();
56433         }
56434         this.visible = false;
56435         this.fireEvent("visibilitychange", this, false);
56436     },
56437
56438     /**
56439      * Shows this region if it was previously hidden.
56440      */
56441     show : function(){
56442         if(!this.collapsed){
56443             this.el.show();
56444         }else{
56445             this.collapsedEl.show();
56446         }
56447         this.visible = true;
56448         this.fireEvent("visibilitychange", this, true);
56449     },
56450
56451     closeClicked : function(){
56452         if(this.activePanel){
56453             this.remove(this.activePanel);
56454         }
56455     },
56456
56457     collapseClick : function(e){
56458         if(this.isSlid){
56459            e.stopPropagation();
56460            this.slideIn();
56461         }else{
56462            e.stopPropagation();
56463            this.slideOut();
56464         }
56465     },
56466
56467     /**
56468      * Collapses this region.
56469      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
56470      */
56471     collapse : function(skipAnim, skipCheck = false){
56472         if(this.collapsed) {
56473             return;
56474         }
56475         
56476         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
56477             
56478             this.collapsed = true;
56479             if(this.split){
56480                 this.split.el.hide();
56481             }
56482             if(this.config.animate && skipAnim !== true){
56483                 this.fireEvent("invalidated", this);
56484                 this.animateCollapse();
56485             }else{
56486                 this.el.setLocation(-20000,-20000);
56487                 this.el.hide();
56488                 this.collapsedEl.show();
56489                 this.fireEvent("collapsed", this);
56490                 this.fireEvent("invalidated", this);
56491             }
56492         }
56493         
56494     },
56495
56496     animateCollapse : function(){
56497         // overridden
56498     },
56499
56500     /**
56501      * Expands this region if it was previously collapsed.
56502      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
56503      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
56504      */
56505     expand : function(e, skipAnim){
56506         if(e) {
56507             e.stopPropagation();
56508         }
56509         if(!this.collapsed || this.el.hasActiveFx()) {
56510             return;
56511         }
56512         if(this.isSlid){
56513             this.afterSlideIn();
56514             skipAnim = true;
56515         }
56516         this.collapsed = false;
56517         if(this.config.animate && skipAnim !== true){
56518             this.animateExpand();
56519         }else{
56520             this.el.show();
56521             if(this.split){
56522                 this.split.el.show();
56523             }
56524             this.collapsedEl.setLocation(-2000,-2000);
56525             this.collapsedEl.hide();
56526             this.fireEvent("invalidated", this);
56527             this.fireEvent("expanded", this);
56528         }
56529     },
56530
56531     animateExpand : function(){
56532         // overridden
56533     },
56534
56535     initTabs : function()
56536     {
56537         this.bodyEl.setStyle("overflow", "hidden");
56538         var ts = new Roo.TabPanel(
56539                 this.bodyEl.dom,
56540                 {
56541                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
56542                     disableTooltips: this.config.disableTabTips,
56543                     toolbar : this.config.toolbar
56544                 }
56545         );
56546         if(this.config.hideTabs){
56547             ts.stripWrap.setDisplayed(false);
56548         }
56549         this.tabs = ts;
56550         ts.resizeTabs = this.config.resizeTabs === true;
56551         ts.minTabWidth = this.config.minTabWidth || 40;
56552         ts.maxTabWidth = this.config.maxTabWidth || 250;
56553         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
56554         ts.monitorResize = false;
56555         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56556         ts.bodyEl.addClass('x-layout-tabs-body');
56557         this.panels.each(this.initPanelAsTab, this);
56558     },
56559
56560     initPanelAsTab : function(panel){
56561         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
56562                     this.config.closeOnTab && panel.isClosable());
56563         if(panel.tabTip !== undefined){
56564             ti.setTooltip(panel.tabTip);
56565         }
56566         ti.on("activate", function(){
56567               this.setActivePanel(panel);
56568         }, this);
56569         if(this.config.closeOnTab){
56570             ti.on("beforeclose", function(t, e){
56571                 e.cancel = true;
56572                 this.remove(panel);
56573             }, this);
56574         }
56575         return ti;
56576     },
56577
56578     updatePanelTitle : function(panel, title){
56579         if(this.activePanel == panel){
56580             this.updateTitle(title);
56581         }
56582         if(this.tabs){
56583             var ti = this.tabs.getTab(panel.getEl().id);
56584             ti.setText(title);
56585             if(panel.tabTip !== undefined){
56586                 ti.setTooltip(panel.tabTip);
56587             }
56588         }
56589     },
56590
56591     updateTitle : function(title){
56592         if(this.titleTextEl && !this.config.title){
56593             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
56594         }
56595     },
56596
56597     setActivePanel : function(panel){
56598         panel = this.getPanel(panel);
56599         if(this.activePanel && this.activePanel != panel){
56600             this.activePanel.setActiveState(false);
56601         }
56602         this.activePanel = panel;
56603         panel.setActiveState(true);
56604         if(this.panelSize){
56605             panel.setSize(this.panelSize.width, this.panelSize.height);
56606         }
56607         if(this.closeBtn){
56608             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
56609         }
56610         this.updateTitle(panel.getTitle());
56611         if(this.tabs){
56612             this.fireEvent("invalidated", this);
56613         }
56614         this.fireEvent("panelactivated", this, panel);
56615     },
56616
56617     /**
56618      * Shows the specified panel.
56619      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
56620      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
56621      */
56622     showPanel : function(panel)
56623     {
56624         panel = this.getPanel(panel);
56625         if(panel){
56626             if(this.tabs){
56627                 var tab = this.tabs.getTab(panel.getEl().id);
56628                 if(tab.isHidden()){
56629                     this.tabs.unhideTab(tab.id);
56630                 }
56631                 tab.activate();
56632             }else{
56633                 this.setActivePanel(panel);
56634             }
56635         }
56636         return panel;
56637     },
56638
56639     /**
56640      * Get the active panel for this region.
56641      * @return {Roo.ContentPanel} The active panel or null
56642      */
56643     getActivePanel : function(){
56644         return this.activePanel;
56645     },
56646
56647     validateVisibility : function(){
56648         if(this.panels.getCount() < 1){
56649             this.updateTitle("&#160;");
56650             this.closeBtn.hide();
56651             this.hide();
56652         }else{
56653             if(!this.isVisible()){
56654                 this.show();
56655             }
56656         }
56657     },
56658
56659     /**
56660      * Adds the passed ContentPanel(s) to this region.
56661      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
56662      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
56663      */
56664     add : function(panel){
56665         if(arguments.length > 1){
56666             for(var i = 0, len = arguments.length; i < len; i++) {
56667                 this.add(arguments[i]);
56668             }
56669             return null;
56670         }
56671         if(this.hasPanel(panel)){
56672             this.showPanel(panel);
56673             return panel;
56674         }
56675         panel.setRegion(this);
56676         this.panels.add(panel);
56677         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
56678             this.bodyEl.dom.appendChild(panel.getEl().dom);
56679             if(panel.background !== true){
56680                 this.setActivePanel(panel);
56681             }
56682             this.fireEvent("paneladded", this, panel);
56683             return panel;
56684         }
56685         if(!this.tabs){
56686             this.initTabs();
56687         }else{
56688             this.initPanelAsTab(panel);
56689         }
56690         if(panel.background !== true){
56691             this.tabs.activate(panel.getEl().id);
56692         }
56693         this.fireEvent("paneladded", this, panel);
56694         return panel;
56695     },
56696
56697     /**
56698      * Hides the tab for the specified panel.
56699      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56700      */
56701     hidePanel : function(panel){
56702         if(this.tabs && (panel = this.getPanel(panel))){
56703             this.tabs.hideTab(panel.getEl().id);
56704         }
56705     },
56706
56707     /**
56708      * Unhides the tab for a previously hidden panel.
56709      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56710      */
56711     unhidePanel : function(panel){
56712         if(this.tabs && (panel = this.getPanel(panel))){
56713             this.tabs.unhideTab(panel.getEl().id);
56714         }
56715     },
56716
56717     clearPanels : function(){
56718         while(this.panels.getCount() > 0){
56719              this.remove(this.panels.first());
56720         }
56721     },
56722
56723     /**
56724      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56725      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56726      * @param {Boolean} preservePanel Overrides the config preservePanel option
56727      * @return {Roo.ContentPanel} The panel that was removed
56728      */
56729     remove : function(panel, preservePanel){
56730         panel = this.getPanel(panel);
56731         if(!panel){
56732             return null;
56733         }
56734         var e = {};
56735         this.fireEvent("beforeremove", this, panel, e);
56736         if(e.cancel === true){
56737             return null;
56738         }
56739         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56740         var panelId = panel.getId();
56741         this.panels.removeKey(panelId);
56742         if(preservePanel){
56743             document.body.appendChild(panel.getEl().dom);
56744         }
56745         if(this.tabs){
56746             this.tabs.removeTab(panel.getEl().id);
56747         }else if (!preservePanel){
56748             this.bodyEl.dom.removeChild(panel.getEl().dom);
56749         }
56750         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56751             var p = this.panels.first();
56752             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56753             tempEl.appendChild(p.getEl().dom);
56754             this.bodyEl.update("");
56755             this.bodyEl.dom.appendChild(p.getEl().dom);
56756             tempEl = null;
56757             this.updateTitle(p.getTitle());
56758             this.tabs = null;
56759             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56760             this.setActivePanel(p);
56761         }
56762         panel.setRegion(null);
56763         if(this.activePanel == panel){
56764             this.activePanel = null;
56765         }
56766         if(this.config.autoDestroy !== false && preservePanel !== true){
56767             try{panel.destroy();}catch(e){}
56768         }
56769         this.fireEvent("panelremoved", this, panel);
56770         return panel;
56771     },
56772
56773     /**
56774      * Returns the TabPanel component used by this region
56775      * @return {Roo.TabPanel}
56776      */
56777     getTabs : function(){
56778         return this.tabs;
56779     },
56780
56781     createTool : function(parentEl, className){
56782         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56783             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
56784         btn.addClassOnOver("x-layout-tools-button-over");
56785         return btn;
56786     }
56787 });/*
56788  * Based on:
56789  * Ext JS Library 1.1.1
56790  * Copyright(c) 2006-2007, Ext JS, LLC.
56791  *
56792  * Originally Released Under LGPL - original licence link has changed is not relivant.
56793  *
56794  * Fork - LGPL
56795  * <script type="text/javascript">
56796  */
56797  
56798
56799
56800 /**
56801  * @class Roo.SplitLayoutRegion
56802  * @extends Roo.LayoutRegion
56803  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56804  */
56805 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56806     this.cursor = cursor;
56807     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56808 };
56809
56810 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56811     splitTip : "Drag to resize.",
56812     collapsibleSplitTip : "Drag to resize. Double click to hide.",
56813     useSplitTips : false,
56814
56815     applyConfig : function(config){
56816         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56817         if(config.split){
56818             if(!this.split){
56819                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
56820                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
56821                 /** The SplitBar for this region 
56822                 * @type Roo.SplitBar */
56823                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56824                 this.split.on("moved", this.onSplitMove, this);
56825                 this.split.useShim = config.useShim === true;
56826                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56827                 if(this.useSplitTips){
56828                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56829                 }
56830                 if(config.collapsible){
56831                     this.split.el.on("dblclick", this.collapse,  this);
56832                 }
56833             }
56834             if(typeof config.minSize != "undefined"){
56835                 this.split.minSize = config.minSize;
56836             }
56837             if(typeof config.maxSize != "undefined"){
56838                 this.split.maxSize = config.maxSize;
56839             }
56840             if(config.hideWhenEmpty || config.hidden || config.collapsed){
56841                 this.hideSplitter();
56842             }
56843         }
56844     },
56845
56846     getHMaxSize : function(){
56847          var cmax = this.config.maxSize || 10000;
56848          var center = this.mgr.getRegion("center");
56849          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56850     },
56851
56852     getVMaxSize : function(){
56853          var cmax = this.config.maxSize || 10000;
56854          var center = this.mgr.getRegion("center");
56855          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56856     },
56857
56858     onSplitMove : function(split, newSize){
56859         this.fireEvent("resized", this, newSize);
56860     },
56861     
56862     /** 
56863      * Returns the {@link Roo.SplitBar} for this region.
56864      * @return {Roo.SplitBar}
56865      */
56866     getSplitBar : function(){
56867         return this.split;
56868     },
56869     
56870     hide : function(){
56871         this.hideSplitter();
56872         Roo.SplitLayoutRegion.superclass.hide.call(this);
56873     },
56874
56875     hideSplitter : function(){
56876         if(this.split){
56877             this.split.el.setLocation(-2000,-2000);
56878             this.split.el.hide();
56879         }
56880     },
56881
56882     show : function(){
56883         if(this.split){
56884             this.split.el.show();
56885         }
56886         Roo.SplitLayoutRegion.superclass.show.call(this);
56887     },
56888     
56889     beforeSlide: function(){
56890         if(Roo.isGecko){// firefox overflow auto bug workaround
56891             this.bodyEl.clip();
56892             if(this.tabs) {
56893                 this.tabs.bodyEl.clip();
56894             }
56895             if(this.activePanel){
56896                 this.activePanel.getEl().clip();
56897                 
56898                 if(this.activePanel.beforeSlide){
56899                     this.activePanel.beforeSlide();
56900                 }
56901             }
56902         }
56903     },
56904     
56905     afterSlide : function(){
56906         if(Roo.isGecko){// firefox overflow auto bug workaround
56907             this.bodyEl.unclip();
56908             if(this.tabs) {
56909                 this.tabs.bodyEl.unclip();
56910             }
56911             if(this.activePanel){
56912                 this.activePanel.getEl().unclip();
56913                 if(this.activePanel.afterSlide){
56914                     this.activePanel.afterSlide();
56915                 }
56916             }
56917         }
56918     },
56919
56920     initAutoHide : function(){
56921         if(this.autoHide !== false){
56922             if(!this.autoHideHd){
56923                 var st = new Roo.util.DelayedTask(this.slideIn, this);
56924                 this.autoHideHd = {
56925                     "mouseout": function(e){
56926                         if(!e.within(this.el, true)){
56927                             st.delay(500);
56928                         }
56929                     },
56930                     "mouseover" : function(e){
56931                         st.cancel();
56932                     },
56933                     scope : this
56934                 };
56935             }
56936             this.el.on(this.autoHideHd);
56937         }
56938     },
56939
56940     clearAutoHide : function(){
56941         if(this.autoHide !== false){
56942             this.el.un("mouseout", this.autoHideHd.mouseout);
56943             this.el.un("mouseover", this.autoHideHd.mouseover);
56944         }
56945     },
56946
56947     clearMonitor : function(){
56948         Roo.get(document).un("click", this.slideInIf, this);
56949     },
56950
56951     // these names are backwards but not changed for compat
56952     slideOut : function(){
56953         if(this.isSlid || this.el.hasActiveFx()){
56954             return;
56955         }
56956         this.isSlid = true;
56957         if(this.collapseBtn){
56958             this.collapseBtn.hide();
56959         }
56960         this.closeBtnState = this.closeBtn.getStyle('display');
56961         this.closeBtn.hide();
56962         if(this.stickBtn){
56963             this.stickBtn.show();
56964         }
56965         this.el.show();
56966         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56967         this.beforeSlide();
56968         this.el.setStyle("z-index", 10001);
56969         this.el.slideIn(this.getSlideAnchor(), {
56970             callback: function(){
56971                 this.afterSlide();
56972                 this.initAutoHide();
56973                 Roo.get(document).on("click", this.slideInIf, this);
56974                 this.fireEvent("slideshow", this);
56975             },
56976             scope: this,
56977             block: true
56978         });
56979     },
56980
56981     afterSlideIn : function(){
56982         this.clearAutoHide();
56983         this.isSlid = false;
56984         this.clearMonitor();
56985         this.el.setStyle("z-index", "");
56986         if(this.collapseBtn){
56987             this.collapseBtn.show();
56988         }
56989         this.closeBtn.setStyle('display', this.closeBtnState);
56990         if(this.stickBtn){
56991             this.stickBtn.hide();
56992         }
56993         this.fireEvent("slidehide", this);
56994     },
56995
56996     slideIn : function(cb){
56997         if(!this.isSlid || this.el.hasActiveFx()){
56998             Roo.callback(cb);
56999             return;
57000         }
57001         this.isSlid = false;
57002         this.beforeSlide();
57003         this.el.slideOut(this.getSlideAnchor(), {
57004             callback: function(){
57005                 this.el.setLeftTop(-10000, -10000);
57006                 this.afterSlide();
57007                 this.afterSlideIn();
57008                 Roo.callback(cb);
57009             },
57010             scope: this,
57011             block: true
57012         });
57013     },
57014     
57015     slideInIf : function(e){
57016         if(!e.within(this.el)){
57017             this.slideIn();
57018         }
57019     },
57020
57021     animateCollapse : function(){
57022         this.beforeSlide();
57023         this.el.setStyle("z-index", 20000);
57024         var anchor = this.getSlideAnchor();
57025         this.el.slideOut(anchor, {
57026             callback : function(){
57027                 this.el.setStyle("z-index", "");
57028                 this.collapsedEl.slideIn(anchor, {duration:.3});
57029                 this.afterSlide();
57030                 this.el.setLocation(-10000,-10000);
57031                 this.el.hide();
57032                 this.fireEvent("collapsed", this);
57033             },
57034             scope: this,
57035             block: true
57036         });
57037     },
57038
57039     animateExpand : function(){
57040         this.beforeSlide();
57041         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
57042         this.el.setStyle("z-index", 20000);
57043         this.collapsedEl.hide({
57044             duration:.1
57045         });
57046         this.el.slideIn(this.getSlideAnchor(), {
57047             callback : function(){
57048                 this.el.setStyle("z-index", "");
57049                 this.afterSlide();
57050                 if(this.split){
57051                     this.split.el.show();
57052                 }
57053                 this.fireEvent("invalidated", this);
57054                 this.fireEvent("expanded", this);
57055             },
57056             scope: this,
57057             block: true
57058         });
57059     },
57060
57061     anchors : {
57062         "west" : "left",
57063         "east" : "right",
57064         "north" : "top",
57065         "south" : "bottom"
57066     },
57067
57068     sanchors : {
57069         "west" : "l",
57070         "east" : "r",
57071         "north" : "t",
57072         "south" : "b"
57073     },
57074
57075     canchors : {
57076         "west" : "tl-tr",
57077         "east" : "tr-tl",
57078         "north" : "tl-bl",
57079         "south" : "bl-tl"
57080     },
57081
57082     getAnchor : function(){
57083         return this.anchors[this.position];
57084     },
57085
57086     getCollapseAnchor : function(){
57087         return this.canchors[this.position];
57088     },
57089
57090     getSlideAnchor : function(){
57091         return this.sanchors[this.position];
57092     },
57093
57094     getAlignAdj : function(){
57095         var cm = this.cmargins;
57096         switch(this.position){
57097             case "west":
57098                 return [0, 0];
57099             break;
57100             case "east":
57101                 return [0, 0];
57102             break;
57103             case "north":
57104                 return [0, 0];
57105             break;
57106             case "south":
57107                 return [0, 0];
57108             break;
57109         }
57110     },
57111
57112     getExpandAdj : function(){
57113         var c = this.collapsedEl, cm = this.cmargins;
57114         switch(this.position){
57115             case "west":
57116                 return [-(cm.right+c.getWidth()+cm.left), 0];
57117             break;
57118             case "east":
57119                 return [cm.right+c.getWidth()+cm.left, 0];
57120             break;
57121             case "north":
57122                 return [0, -(cm.top+cm.bottom+c.getHeight())];
57123             break;
57124             case "south":
57125                 return [0, cm.top+cm.bottom+c.getHeight()];
57126             break;
57127         }
57128     }
57129 });/*
57130  * Based on:
57131  * Ext JS Library 1.1.1
57132  * Copyright(c) 2006-2007, Ext JS, LLC.
57133  *
57134  * Originally Released Under LGPL - original licence link has changed is not relivant.
57135  *
57136  * Fork - LGPL
57137  * <script type="text/javascript">
57138  */
57139 /*
57140  * These classes are private internal classes
57141  */
57142 Roo.CenterLayoutRegion = function(mgr, config){
57143     Roo.LayoutRegion.call(this, mgr, config, "center");
57144     this.visible = true;
57145     this.minWidth = config.minWidth || 20;
57146     this.minHeight = config.minHeight || 20;
57147 };
57148
57149 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
57150     hide : function(){
57151         // center panel can't be hidden
57152     },
57153     
57154     show : function(){
57155         // center panel can't be hidden
57156     },
57157     
57158     getMinWidth: function(){
57159         return this.minWidth;
57160     },
57161     
57162     getMinHeight: function(){
57163         return this.minHeight;
57164     }
57165 });
57166
57167
57168 Roo.NorthLayoutRegion = function(mgr, config){
57169     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
57170     if(this.split){
57171         this.split.placement = Roo.SplitBar.TOP;
57172         this.split.orientation = Roo.SplitBar.VERTICAL;
57173         this.split.el.addClass("x-layout-split-v");
57174     }
57175     var size = config.initialSize || config.height;
57176     if(typeof size != "undefined"){
57177         this.el.setHeight(size);
57178     }
57179 };
57180 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
57181     orientation: Roo.SplitBar.VERTICAL,
57182     getBox : function(){
57183         if(this.collapsed){
57184             return this.collapsedEl.getBox();
57185         }
57186         var box = this.el.getBox();
57187         if(this.split){
57188             box.height += this.split.el.getHeight();
57189         }
57190         return box;
57191     },
57192     
57193     updateBox : function(box){
57194         if(this.split && !this.collapsed){
57195             box.height -= this.split.el.getHeight();
57196             this.split.el.setLeft(box.x);
57197             this.split.el.setTop(box.y+box.height);
57198             this.split.el.setWidth(box.width);
57199         }
57200         if(this.collapsed){
57201             this.updateBody(box.width, null);
57202         }
57203         Roo.LayoutRegion.prototype.updateBox.call(this, box);
57204     }
57205 });
57206
57207 Roo.SouthLayoutRegion = function(mgr, config){
57208     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
57209     if(this.split){
57210         this.split.placement = Roo.SplitBar.BOTTOM;
57211         this.split.orientation = Roo.SplitBar.VERTICAL;
57212         this.split.el.addClass("x-layout-split-v");
57213     }
57214     var size = config.initialSize || config.height;
57215     if(typeof size != "undefined"){
57216         this.el.setHeight(size);
57217     }
57218 };
57219 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
57220     orientation: Roo.SplitBar.VERTICAL,
57221     getBox : function(){
57222         if(this.collapsed){
57223             return this.collapsedEl.getBox();
57224         }
57225         var box = this.el.getBox();
57226         if(this.split){
57227             var sh = this.split.el.getHeight();
57228             box.height += sh;
57229             box.y -= sh;
57230         }
57231         return box;
57232     },
57233     
57234     updateBox : function(box){
57235         if(this.split && !this.collapsed){
57236             var sh = this.split.el.getHeight();
57237             box.height -= sh;
57238             box.y += sh;
57239             this.split.el.setLeft(box.x);
57240             this.split.el.setTop(box.y-sh);
57241             this.split.el.setWidth(box.width);
57242         }
57243         if(this.collapsed){
57244             this.updateBody(box.width, null);
57245         }
57246         Roo.LayoutRegion.prototype.updateBox.call(this, box);
57247     }
57248 });
57249
57250 Roo.EastLayoutRegion = function(mgr, config){
57251     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
57252     if(this.split){
57253         this.split.placement = Roo.SplitBar.RIGHT;
57254         this.split.orientation = Roo.SplitBar.HORIZONTAL;
57255         this.split.el.addClass("x-layout-split-h");
57256     }
57257     var size = config.initialSize || config.width;
57258     if(typeof size != "undefined"){
57259         this.el.setWidth(size);
57260     }
57261 };
57262 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
57263     orientation: Roo.SplitBar.HORIZONTAL,
57264     getBox : function(){
57265         if(this.collapsed){
57266             return this.collapsedEl.getBox();
57267         }
57268         var box = this.el.getBox();
57269         if(this.split){
57270             var sw = this.split.el.getWidth();
57271             box.width += sw;
57272             box.x -= sw;
57273         }
57274         return box;
57275     },
57276
57277     updateBox : function(box){
57278         if(this.split && !this.collapsed){
57279             var sw = this.split.el.getWidth();
57280             box.width -= sw;
57281             this.split.el.setLeft(box.x);
57282             this.split.el.setTop(box.y);
57283             this.split.el.setHeight(box.height);
57284             box.x += sw;
57285         }
57286         if(this.collapsed){
57287             this.updateBody(null, box.height);
57288         }
57289         Roo.LayoutRegion.prototype.updateBox.call(this, box);
57290     }
57291 });
57292
57293 Roo.WestLayoutRegion = function(mgr, config){
57294     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
57295     if(this.split){
57296         this.split.placement = Roo.SplitBar.LEFT;
57297         this.split.orientation = Roo.SplitBar.HORIZONTAL;
57298         this.split.el.addClass("x-layout-split-h");
57299     }
57300     var size = config.initialSize || config.width;
57301     if(typeof size != "undefined"){
57302         this.el.setWidth(size);
57303     }
57304 };
57305 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
57306     orientation: Roo.SplitBar.HORIZONTAL,
57307     getBox : function(){
57308         if(this.collapsed){
57309             return this.collapsedEl.getBox();
57310         }
57311         var box = this.el.getBox();
57312         if(this.split){
57313             box.width += this.split.el.getWidth();
57314         }
57315         return box;
57316     },
57317     
57318     updateBox : function(box){
57319         if(this.split && !this.collapsed){
57320             var sw = this.split.el.getWidth();
57321             box.width -= sw;
57322             this.split.el.setLeft(box.x+box.width);
57323             this.split.el.setTop(box.y);
57324             this.split.el.setHeight(box.height);
57325         }
57326         if(this.collapsed){
57327             this.updateBody(null, box.height);
57328         }
57329         Roo.LayoutRegion.prototype.updateBox.call(this, box);
57330     }
57331 });
57332 /*
57333  * Based on:
57334  * Ext JS Library 1.1.1
57335  * Copyright(c) 2006-2007, Ext JS, LLC.
57336  *
57337  * Originally Released Under LGPL - original licence link has changed is not relivant.
57338  *
57339  * Fork - LGPL
57340  * <script type="text/javascript">
57341  */
57342  
57343  
57344 /*
57345  * Private internal class for reading and applying state
57346  */
57347 Roo.LayoutStateManager = function(layout){
57348      // default empty state
57349      this.state = {
57350         north: {},
57351         south: {},
57352         east: {},
57353         west: {}       
57354     };
57355 };
57356
57357 Roo.LayoutStateManager.prototype = {
57358     init : function(layout, provider){
57359         this.provider = provider;
57360         var state = provider.get(layout.id+"-layout-state");
57361         if(state){
57362             var wasUpdating = layout.isUpdating();
57363             if(!wasUpdating){
57364                 layout.beginUpdate();
57365             }
57366             for(var key in state){
57367                 if(typeof state[key] != "function"){
57368                     var rstate = state[key];
57369                     var r = layout.getRegion(key);
57370                     if(r && rstate){
57371                         if(rstate.size){
57372                             r.resizeTo(rstate.size);
57373                         }
57374                         if(rstate.collapsed == true){
57375                             r.collapse(true);
57376                         }else{
57377                             r.expand(null, true);
57378                         }
57379                     }
57380                 }
57381             }
57382             if(!wasUpdating){
57383                 layout.endUpdate();
57384             }
57385             this.state = state; 
57386         }
57387         this.layout = layout;
57388         layout.on("regionresized", this.onRegionResized, this);
57389         layout.on("regioncollapsed", this.onRegionCollapsed, this);
57390         layout.on("regionexpanded", this.onRegionExpanded, this);
57391     },
57392     
57393     storeState : function(){
57394         this.provider.set(this.layout.id+"-layout-state", this.state);
57395     },
57396     
57397     onRegionResized : function(region, newSize){
57398         this.state[region.getPosition()].size = newSize;
57399         this.storeState();
57400     },
57401     
57402     onRegionCollapsed : function(region){
57403         this.state[region.getPosition()].collapsed = true;
57404         this.storeState();
57405     },
57406     
57407     onRegionExpanded : function(region){
57408         this.state[region.getPosition()].collapsed = false;
57409         this.storeState();
57410     }
57411 };/*
57412  * Based on:
57413  * Ext JS Library 1.1.1
57414  * Copyright(c) 2006-2007, Ext JS, LLC.
57415  *
57416  * Originally Released Under LGPL - original licence link has changed is not relivant.
57417  *
57418  * Fork - LGPL
57419  * <script type="text/javascript">
57420  */
57421 /**
57422  * @class Roo.ContentPanel
57423  * @extends Roo.util.Observable
57424  * A basic ContentPanel element.
57425  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
57426  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
57427  * @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
57428  * @cfg {Boolean}   closable      True if the panel can be closed/removed
57429  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
57430  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
57431  * @cfg {Toolbar}   toolbar       A toolbar for this panel
57432  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
57433  * @cfg {String} title          The title for this panel
57434  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
57435  * @cfg {String} url            Calls {@link #setUrl} with this value
57436  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
57437  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
57438  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
57439  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
57440
57441  * @constructor
57442  * Create a new ContentPanel.
57443  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
57444  * @param {String/Object} config A string to set only the title or a config object
57445  * @param {String} content (optional) Set the HTML content for this panel
57446  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
57447  */
57448 Roo.ContentPanel = function(el, config, content){
57449     
57450      
57451     /*
57452     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
57453         config = el;
57454         el = Roo.id();
57455     }
57456     if (config && config.parentLayout) { 
57457         el = config.parentLayout.el.createChild(); 
57458     }
57459     */
57460     if(el.autoCreate){ // xtype is available if this is called from factory
57461         config = el;
57462         el = Roo.id();
57463     }
57464     this.el = Roo.get(el);
57465     if(!this.el && config && config.autoCreate){
57466         if(typeof config.autoCreate == "object"){
57467             if(!config.autoCreate.id){
57468                 config.autoCreate.id = config.id||el;
57469             }
57470             this.el = Roo.DomHelper.append(document.body,
57471                         config.autoCreate, true);
57472         }else{
57473             this.el = Roo.DomHelper.append(document.body,
57474                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
57475         }
57476     }
57477     this.closable = false;
57478     this.loaded = false;
57479     this.active = false;
57480     if(typeof config == "string"){
57481         this.title = config;
57482     }else{
57483         Roo.apply(this, config);
57484     }
57485     
57486     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
57487         this.wrapEl = this.el.wrap();
57488         this.toolbar.container = this.el.insertSibling(false, 'before');
57489         this.toolbar = new Roo.Toolbar(this.toolbar);
57490     }
57491     
57492     // xtype created footer. - not sure if will work as we normally have to render first..
57493     if (this.footer && !this.footer.el && this.footer.xtype) {
57494         if (!this.wrapEl) {
57495             this.wrapEl = this.el.wrap();
57496         }
57497     
57498         this.footer.container = this.wrapEl.createChild();
57499          
57500         this.footer = Roo.factory(this.footer, Roo);
57501         
57502     }
57503     
57504     if(this.resizeEl){
57505         this.resizeEl = Roo.get(this.resizeEl, true);
57506     }else{
57507         this.resizeEl = this.el;
57508     }
57509     // handle view.xtype
57510     
57511  
57512     
57513     
57514     this.addEvents({
57515         /**
57516          * @event activate
57517          * Fires when this panel is activated. 
57518          * @param {Roo.ContentPanel} this
57519          */
57520         "activate" : true,
57521         /**
57522          * @event deactivate
57523          * Fires when this panel is activated. 
57524          * @param {Roo.ContentPanel} this
57525          */
57526         "deactivate" : true,
57527
57528         /**
57529          * @event resize
57530          * Fires when this panel is resized if fitToFrame is true.
57531          * @param {Roo.ContentPanel} this
57532          * @param {Number} width The width after any component adjustments
57533          * @param {Number} height The height after any component adjustments
57534          */
57535         "resize" : true,
57536         
57537          /**
57538          * @event render
57539          * Fires when this tab is created
57540          * @param {Roo.ContentPanel} this
57541          */
57542         "render" : true
57543         
57544         
57545         
57546     });
57547     
57548
57549     
57550     
57551     if(this.autoScroll){
57552         this.resizeEl.setStyle("overflow", "auto");
57553     } else {
57554         // fix randome scrolling
57555         this.el.on('scroll', function() {
57556             Roo.log('fix random scolling');
57557             this.scrollTo('top',0); 
57558         });
57559     }
57560     content = content || this.content;
57561     if(content){
57562         this.setContent(content);
57563     }
57564     if(config && config.url){
57565         this.setUrl(this.url, this.params, this.loadOnce);
57566     }
57567     
57568     
57569     
57570     Roo.ContentPanel.superclass.constructor.call(this);
57571     
57572     if (this.view && typeof(this.view.xtype) != 'undefined') {
57573         this.view.el = this.el.appendChild(document.createElement("div"));
57574         this.view = Roo.factory(this.view); 
57575         this.view.render  &&  this.view.render(false, '');  
57576     }
57577     
57578     
57579     this.fireEvent('render', this);
57580 };
57581
57582 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
57583     tabTip:'',
57584     setRegion : function(region){
57585         this.region = region;
57586         if(region){
57587            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
57588         }else{
57589            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
57590         } 
57591     },
57592     
57593     /**
57594      * Returns the toolbar for this Panel if one was configured. 
57595      * @return {Roo.Toolbar} 
57596      */
57597     getToolbar : function(){
57598         return this.toolbar;
57599     },
57600     
57601     setActiveState : function(active){
57602         this.active = active;
57603         if(!active){
57604             this.fireEvent("deactivate", this);
57605         }else{
57606             this.fireEvent("activate", this);
57607         }
57608     },
57609     /**
57610      * Updates this panel's element
57611      * @param {String} content The new content
57612      * @param {Boolean} loadScripts (optional) true to look for and process scripts
57613     */
57614     setContent : function(content, loadScripts){
57615         this.el.update(content, loadScripts);
57616     },
57617
57618     ignoreResize : function(w, h){
57619         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
57620             return true;
57621         }else{
57622             this.lastSize = {width: w, height: h};
57623             return false;
57624         }
57625     },
57626     /**
57627      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
57628      * @return {Roo.UpdateManager} The UpdateManager
57629      */
57630     getUpdateManager : function(){
57631         return this.el.getUpdateManager();
57632     },
57633      /**
57634      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
57635      * @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:
57636 <pre><code>
57637 panel.load({
57638     url: "your-url.php",
57639     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
57640     callback: yourFunction,
57641     scope: yourObject, //(optional scope)
57642     discardUrl: false,
57643     nocache: false,
57644     text: "Loading...",
57645     timeout: 30,
57646     scripts: false
57647 });
57648 </code></pre>
57649      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
57650      * 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.
57651      * @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}
57652      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
57653      * @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.
57654      * @return {Roo.ContentPanel} this
57655      */
57656     load : function(){
57657         var um = this.el.getUpdateManager();
57658         um.update.apply(um, arguments);
57659         return this;
57660     },
57661
57662
57663     /**
57664      * 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.
57665      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
57666      * @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)
57667      * @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)
57668      * @return {Roo.UpdateManager} The UpdateManager
57669      */
57670     setUrl : function(url, params, loadOnce){
57671         if(this.refreshDelegate){
57672             this.removeListener("activate", this.refreshDelegate);
57673         }
57674         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
57675         this.on("activate", this.refreshDelegate);
57676         return this.el.getUpdateManager();
57677     },
57678     
57679     _handleRefresh : function(url, params, loadOnce){
57680         if(!loadOnce || !this.loaded){
57681             var updater = this.el.getUpdateManager();
57682             updater.update(url, params, this._setLoaded.createDelegate(this));
57683         }
57684     },
57685     
57686     _setLoaded : function(){
57687         this.loaded = true;
57688     }, 
57689     
57690     /**
57691      * Returns this panel's id
57692      * @return {String} 
57693      */
57694     getId : function(){
57695         return this.el.id;
57696     },
57697     
57698     /** 
57699      * Returns this panel's element - used by regiosn to add.
57700      * @return {Roo.Element} 
57701      */
57702     getEl : function(){
57703         return this.wrapEl || this.el;
57704     },
57705     
57706     adjustForComponents : function(width, height)
57707     {
57708         //Roo.log('adjustForComponents ');
57709         if(this.resizeEl != this.el){
57710             width -= this.el.getFrameWidth('lr');
57711             height -= this.el.getFrameWidth('tb');
57712         }
57713         if(this.toolbar){
57714             var te = this.toolbar.getEl();
57715             height -= te.getHeight();
57716             te.setWidth(width);
57717         }
57718         if(this.footer){
57719             var te = this.footer.getEl();
57720             Roo.log("footer:" + te.getHeight());
57721             
57722             height -= te.getHeight();
57723             te.setWidth(width);
57724         }
57725         
57726         
57727         if(this.adjustments){
57728             width += this.adjustments[0];
57729             height += this.adjustments[1];
57730         }
57731         return {"width": width, "height": height};
57732     },
57733     
57734     setSize : function(width, height){
57735         if(this.fitToFrame && !this.ignoreResize(width, height)){
57736             if(this.fitContainer && this.resizeEl != this.el){
57737                 this.el.setSize(width, height);
57738             }
57739             var size = this.adjustForComponents(width, height);
57740             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57741             this.fireEvent('resize', this, size.width, size.height);
57742         }
57743     },
57744     
57745     /**
57746      * Returns this panel's title
57747      * @return {String} 
57748      */
57749     getTitle : function(){
57750         return this.title;
57751     },
57752     
57753     /**
57754      * Set this panel's title
57755      * @param {String} title
57756      */
57757     setTitle : function(title){
57758         this.title = title;
57759         if(this.region){
57760             this.region.updatePanelTitle(this, title);
57761         }
57762     },
57763     
57764     /**
57765      * Returns true is this panel was configured to be closable
57766      * @return {Boolean} 
57767      */
57768     isClosable : function(){
57769         return this.closable;
57770     },
57771     
57772     beforeSlide : function(){
57773         this.el.clip();
57774         this.resizeEl.clip();
57775     },
57776     
57777     afterSlide : function(){
57778         this.el.unclip();
57779         this.resizeEl.unclip();
57780     },
57781     
57782     /**
57783      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
57784      *   Will fail silently if the {@link #setUrl} method has not been called.
57785      *   This does not activate the panel, just updates its content.
57786      */
57787     refresh : function(){
57788         if(this.refreshDelegate){
57789            this.loaded = false;
57790            this.refreshDelegate();
57791         }
57792     },
57793     
57794     /**
57795      * Destroys this panel
57796      */
57797     destroy : function(){
57798         this.el.removeAllListeners();
57799         var tempEl = document.createElement("span");
57800         tempEl.appendChild(this.el.dom);
57801         tempEl.innerHTML = "";
57802         this.el.remove();
57803         this.el = null;
57804     },
57805     
57806     /**
57807      * form - if the content panel contains a form - this is a reference to it.
57808      * @type {Roo.form.Form}
57809      */
57810     form : false,
57811     /**
57812      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57813      *    This contains a reference to it.
57814      * @type {Roo.View}
57815      */
57816     view : false,
57817     
57818       /**
57819      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57820      * <pre><code>
57821
57822 layout.addxtype({
57823        xtype : 'Form',
57824        items: [ .... ]
57825    }
57826 );
57827
57828 </code></pre>
57829      * @param {Object} cfg Xtype definition of item to add.
57830      */
57831     
57832     addxtype : function(cfg) {
57833         // add form..
57834         if (cfg.xtype.match(/^Form$/)) {
57835             
57836             var el;
57837             //if (this.footer) {
57838             //    el = this.footer.container.insertSibling(false, 'before');
57839             //} else {
57840                 el = this.el.createChild();
57841             //}
57842
57843             this.form = new  Roo.form.Form(cfg);
57844             
57845             
57846             if ( this.form.allItems.length) {
57847                 this.form.render(el.dom);
57848             }
57849             return this.form;
57850         }
57851         // should only have one of theses..
57852         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57853             // views.. should not be just added - used named prop 'view''
57854             
57855             cfg.el = this.el.appendChild(document.createElement("div"));
57856             // factory?
57857             
57858             var ret = new Roo.factory(cfg);
57859              
57860              ret.render && ret.render(false, ''); // render blank..
57861             this.view = ret;
57862             return ret;
57863         }
57864         return false;
57865     }
57866 });
57867
57868 /**
57869  * @class Roo.GridPanel
57870  * @extends Roo.ContentPanel
57871  * @constructor
57872  * Create a new GridPanel.
57873  * @param {Roo.grid.Grid} grid The grid for this panel
57874  * @param {String/Object} config A string to set only the panel's title, or a config object
57875  */
57876 Roo.GridPanel = function(grid, config){
57877     
57878   
57879     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57880         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57881         
57882     this.wrapper.dom.appendChild(grid.getGridEl().dom);
57883     
57884     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57885     
57886     if(this.toolbar){
57887         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57888     }
57889     // xtype created footer. - not sure if will work as we normally have to render first..
57890     if (this.footer && !this.footer.el && this.footer.xtype) {
57891         
57892         this.footer.container = this.grid.getView().getFooterPanel(true);
57893         this.footer.dataSource = this.grid.dataSource;
57894         this.footer = Roo.factory(this.footer, Roo);
57895         
57896     }
57897     
57898     grid.monitorWindowResize = false; // turn off autosizing
57899     grid.autoHeight = false;
57900     grid.autoWidth = false;
57901     this.grid = grid;
57902     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57903 };
57904
57905 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57906     getId : function(){
57907         return this.grid.id;
57908     },
57909     
57910     /**
57911      * Returns the grid for this panel
57912      * @return {Roo.grid.Grid} 
57913      */
57914     getGrid : function(){
57915         return this.grid;    
57916     },
57917     
57918     setSize : function(width, height){
57919         if(!this.ignoreResize(width, height)){
57920             var grid = this.grid;
57921             var size = this.adjustForComponents(width, height);
57922             grid.getGridEl().setSize(size.width, size.height);
57923             grid.autoSize();
57924         }
57925     },
57926     
57927     beforeSlide : function(){
57928         this.grid.getView().scroller.clip();
57929     },
57930     
57931     afterSlide : function(){
57932         this.grid.getView().scroller.unclip();
57933     },
57934     
57935     destroy : function(){
57936         this.grid.destroy();
57937         delete this.grid;
57938         Roo.GridPanel.superclass.destroy.call(this); 
57939     }
57940 });
57941
57942
57943 /**
57944  * @class Roo.NestedLayoutPanel
57945  * @extends Roo.ContentPanel
57946  * @constructor
57947  * Create a new NestedLayoutPanel.
57948  * 
57949  * 
57950  * @param {Roo.BorderLayout} layout The layout for this panel
57951  * @param {String/Object} config A string to set only the title or a config object
57952  */
57953 Roo.NestedLayoutPanel = function(layout, config)
57954 {
57955     // construct with only one argument..
57956     /* FIXME - implement nicer consturctors
57957     if (layout.layout) {
57958         config = layout;
57959         layout = config.layout;
57960         delete config.layout;
57961     }
57962     if (layout.xtype && !layout.getEl) {
57963         // then layout needs constructing..
57964         layout = Roo.factory(layout, Roo);
57965     }
57966     */
57967     
57968     
57969     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57970     
57971     layout.monitorWindowResize = false; // turn off autosizing
57972     this.layout = layout;
57973     this.layout.getEl().addClass("x-layout-nested-layout");
57974     
57975     
57976     
57977     
57978 };
57979
57980 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57981
57982     setSize : function(width, height){
57983         if(!this.ignoreResize(width, height)){
57984             var size = this.adjustForComponents(width, height);
57985             var el = this.layout.getEl();
57986             el.setSize(size.width, size.height);
57987             var touch = el.dom.offsetWidth;
57988             this.layout.layout();
57989             // ie requires a double layout on the first pass
57990             if(Roo.isIE && !this.initialized){
57991                 this.initialized = true;
57992                 this.layout.layout();
57993             }
57994         }
57995     },
57996     
57997     // activate all subpanels if not currently active..
57998     
57999     setActiveState : function(active){
58000         this.active = active;
58001         if(!active){
58002             this.fireEvent("deactivate", this);
58003             return;
58004         }
58005         
58006         this.fireEvent("activate", this);
58007         // not sure if this should happen before or after..
58008         if (!this.layout) {
58009             return; // should not happen..
58010         }
58011         var reg = false;
58012         for (var r in this.layout.regions) {
58013             reg = this.layout.getRegion(r);
58014             if (reg.getActivePanel()) {
58015                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
58016                 reg.setActivePanel(reg.getActivePanel());
58017                 continue;
58018             }
58019             if (!reg.panels.length) {
58020                 continue;
58021             }
58022             reg.showPanel(reg.getPanel(0));
58023         }
58024         
58025         
58026         
58027         
58028     },
58029     
58030     /**
58031      * Returns the nested BorderLayout for this panel
58032      * @return {Roo.BorderLayout} 
58033      */
58034     getLayout : function(){
58035         return this.layout;
58036     },
58037     
58038      /**
58039      * Adds a xtype elements to the layout of the nested panel
58040      * <pre><code>
58041
58042 panel.addxtype({
58043        xtype : 'ContentPanel',
58044        region: 'west',
58045        items: [ .... ]
58046    }
58047 );
58048
58049 panel.addxtype({
58050         xtype : 'NestedLayoutPanel',
58051         region: 'west',
58052         layout: {
58053            center: { },
58054            west: { }   
58055         },
58056         items : [ ... list of content panels or nested layout panels.. ]
58057    }
58058 );
58059 </code></pre>
58060      * @param {Object} cfg Xtype definition of item to add.
58061      */
58062     addxtype : function(cfg) {
58063         return this.layout.addxtype(cfg);
58064     
58065     }
58066 });
58067
58068 Roo.ScrollPanel = function(el, config, content){
58069     config = config || {};
58070     config.fitToFrame = true;
58071     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
58072     
58073     this.el.dom.style.overflow = "hidden";
58074     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
58075     this.el.removeClass("x-layout-inactive-content");
58076     this.el.on("mousewheel", this.onWheel, this);
58077
58078     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
58079     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
58080     up.unselectable(); down.unselectable();
58081     up.on("click", this.scrollUp, this);
58082     down.on("click", this.scrollDown, this);
58083     up.addClassOnOver("x-scroller-btn-over");
58084     down.addClassOnOver("x-scroller-btn-over");
58085     up.addClassOnClick("x-scroller-btn-click");
58086     down.addClassOnClick("x-scroller-btn-click");
58087     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
58088
58089     this.resizeEl = this.el;
58090     this.el = wrap; this.up = up; this.down = down;
58091 };
58092
58093 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
58094     increment : 100,
58095     wheelIncrement : 5,
58096     scrollUp : function(){
58097         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
58098     },
58099
58100     scrollDown : function(){
58101         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
58102     },
58103
58104     afterScroll : function(){
58105         var el = this.resizeEl;
58106         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
58107         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
58108         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
58109     },
58110
58111     setSize : function(){
58112         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
58113         this.afterScroll();
58114     },
58115
58116     onWheel : function(e){
58117         var d = e.getWheelDelta();
58118         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
58119         this.afterScroll();
58120         e.stopEvent();
58121     },
58122
58123     setContent : function(content, loadScripts){
58124         this.resizeEl.update(content, loadScripts);
58125     }
58126
58127 });
58128
58129
58130
58131
58132
58133
58134
58135
58136
58137 /**
58138  * @class Roo.TreePanel
58139  * @extends Roo.ContentPanel
58140  * @constructor
58141  * Create a new TreePanel. - defaults to fit/scoll contents.
58142  * @param {String/Object} config A string to set only the panel's title, or a config object
58143  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
58144  */
58145 Roo.TreePanel = function(config){
58146     var el = config.el;
58147     var tree = config.tree;
58148     delete config.tree; 
58149     delete config.el; // hopefull!
58150     
58151     // wrapper for IE7 strict & safari scroll issue
58152     
58153     var treeEl = el.createChild();
58154     config.resizeEl = treeEl;
58155     
58156     
58157     
58158     Roo.TreePanel.superclass.constructor.call(this, el, config);
58159  
58160  
58161     this.tree = new Roo.tree.TreePanel(treeEl , tree);
58162     //console.log(tree);
58163     this.on('activate', function()
58164     {
58165         if (this.tree.rendered) {
58166             return;
58167         }
58168         //console.log('render tree');
58169         this.tree.render();
58170     });
58171     // this should not be needed.. - it's actually the 'el' that resizes?
58172     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
58173     
58174     //this.on('resize',  function (cp, w, h) {
58175     //        this.tree.innerCt.setWidth(w);
58176     //        this.tree.innerCt.setHeight(h);
58177     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
58178     //});
58179
58180         
58181     
58182 };
58183
58184 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
58185     fitToFrame : true,
58186     autoScroll : true
58187 });
58188
58189
58190
58191
58192
58193
58194
58195
58196
58197
58198
58199 /*
58200  * Based on:
58201  * Ext JS Library 1.1.1
58202  * Copyright(c) 2006-2007, Ext JS, LLC.
58203  *
58204  * Originally Released Under LGPL - original licence link has changed is not relivant.
58205  *
58206  * Fork - LGPL
58207  * <script type="text/javascript">
58208  */
58209  
58210
58211 /**
58212  * @class Roo.ReaderLayout
58213  * @extends Roo.BorderLayout
58214  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
58215  * center region containing two nested regions (a top one for a list view and one for item preview below),
58216  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
58217  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
58218  * expedites the setup of the overall layout and regions for this common application style.
58219  * Example:
58220  <pre><code>
58221 var reader = new Roo.ReaderLayout();
58222 var CP = Roo.ContentPanel;  // shortcut for adding
58223
58224 reader.beginUpdate();
58225 reader.add("north", new CP("north", "North"));
58226 reader.add("west", new CP("west", {title: "West"}));
58227 reader.add("east", new CP("east", {title: "East"}));
58228
58229 reader.regions.listView.add(new CP("listView", "List"));
58230 reader.regions.preview.add(new CP("preview", "Preview"));
58231 reader.endUpdate();
58232 </code></pre>
58233 * @constructor
58234 * Create a new ReaderLayout
58235 * @param {Object} config Configuration options
58236 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
58237 * document.body if omitted)
58238 */
58239 Roo.ReaderLayout = function(config, renderTo){
58240     var c = config || {size:{}};
58241     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
58242         north: c.north !== false ? Roo.apply({
58243             split:false,
58244             initialSize: 32,
58245             titlebar: false
58246         }, c.north) : false,
58247         west: c.west !== false ? Roo.apply({
58248             split:true,
58249             initialSize: 200,
58250             minSize: 175,
58251             maxSize: 400,
58252             titlebar: true,
58253             collapsible: true,
58254             animate: true,
58255             margins:{left:5,right:0,bottom:5,top:5},
58256             cmargins:{left:5,right:5,bottom:5,top:5}
58257         }, c.west) : false,
58258         east: c.east !== false ? Roo.apply({
58259             split:true,
58260             initialSize: 200,
58261             minSize: 175,
58262             maxSize: 400,
58263             titlebar: true,
58264             collapsible: true,
58265             animate: true,
58266             margins:{left:0,right:5,bottom:5,top:5},
58267             cmargins:{left:5,right:5,bottom:5,top:5}
58268         }, c.east) : false,
58269         center: Roo.apply({
58270             tabPosition: 'top',
58271             autoScroll:false,
58272             closeOnTab: true,
58273             titlebar:false,
58274             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
58275         }, c.center)
58276     });
58277
58278     this.el.addClass('x-reader');
58279
58280     this.beginUpdate();
58281
58282     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
58283         south: c.preview !== false ? Roo.apply({
58284             split:true,
58285             initialSize: 200,
58286             minSize: 100,
58287             autoScroll:true,
58288             collapsible:true,
58289             titlebar: true,
58290             cmargins:{top:5,left:0, right:0, bottom:0}
58291         }, c.preview) : false,
58292         center: Roo.apply({
58293             autoScroll:false,
58294             titlebar:false,
58295             minHeight:200
58296         }, c.listView)
58297     });
58298     this.add('center', new Roo.NestedLayoutPanel(inner,
58299             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
58300
58301     this.endUpdate();
58302
58303     this.regions.preview = inner.getRegion('south');
58304     this.regions.listView = inner.getRegion('center');
58305 };
58306
58307 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
58308  * Based on:
58309  * Ext JS Library 1.1.1
58310  * Copyright(c) 2006-2007, Ext JS, LLC.
58311  *
58312  * Originally Released Under LGPL - original licence link has changed is not relivant.
58313  *
58314  * Fork - LGPL
58315  * <script type="text/javascript">
58316  */
58317  
58318 /**
58319  * @class Roo.grid.Grid
58320  * @extends Roo.util.Observable
58321  * This class represents the primary interface of a component based grid control.
58322  * <br><br>Usage:<pre><code>
58323  var grid = new Roo.grid.Grid("my-container-id", {
58324      ds: myDataStore,
58325      cm: myColModel,
58326      selModel: mySelectionModel,
58327      autoSizeColumns: true,
58328      monitorWindowResize: false,
58329      trackMouseOver: true
58330  });
58331  // set any options
58332  grid.render();
58333  * </code></pre>
58334  * <b>Common Problems:</b><br/>
58335  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
58336  * element will correct this<br/>
58337  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
58338  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
58339  * are unpredictable.<br/>
58340  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
58341  * grid to calculate dimensions/offsets.<br/>
58342   * @constructor
58343  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58344  * The container MUST have some type of size defined for the grid to fill. The container will be
58345  * automatically set to position relative if it isn't already.
58346  * @param {Object} config A config object that sets properties on this grid.
58347  */
58348 Roo.grid.Grid = function(container, config){
58349         // initialize the container
58350         this.container = Roo.get(container);
58351         this.container.update("");
58352         this.container.setStyle("overflow", "hidden");
58353     this.container.addClass('x-grid-container');
58354
58355     this.id = this.container.id;
58356
58357     Roo.apply(this, config);
58358     // check and correct shorthanded configs
58359     if(this.ds){
58360         this.dataSource = this.ds;
58361         delete this.ds;
58362     }
58363     if(this.cm){
58364         this.colModel = this.cm;
58365         delete this.cm;
58366     }
58367     if(this.sm){
58368         this.selModel = this.sm;
58369         delete this.sm;
58370     }
58371
58372     if (this.selModel) {
58373         this.selModel = Roo.factory(this.selModel, Roo.grid);
58374         this.sm = this.selModel;
58375         this.sm.xmodule = this.xmodule || false;
58376     }
58377     if (typeof(this.colModel.config) == 'undefined') {
58378         this.colModel = new Roo.grid.ColumnModel(this.colModel);
58379         this.cm = this.colModel;
58380         this.cm.xmodule = this.xmodule || false;
58381     }
58382     if (this.dataSource) {
58383         this.dataSource= Roo.factory(this.dataSource, Roo.data);
58384         this.ds = this.dataSource;
58385         this.ds.xmodule = this.xmodule || false;
58386          
58387     }
58388     
58389     
58390     
58391     if(this.width){
58392         this.container.setWidth(this.width);
58393     }
58394
58395     if(this.height){
58396         this.container.setHeight(this.height);
58397     }
58398     /** @private */
58399         this.addEvents({
58400         // raw events
58401         /**
58402          * @event click
58403          * The raw click event for the entire grid.
58404          * @param {Roo.EventObject} e
58405          */
58406         "click" : true,
58407         /**
58408          * @event dblclick
58409          * The raw dblclick event for the entire grid.
58410          * @param {Roo.EventObject} e
58411          */
58412         "dblclick" : true,
58413         /**
58414          * @event contextmenu
58415          * The raw contextmenu event for the entire grid.
58416          * @param {Roo.EventObject} e
58417          */
58418         "contextmenu" : true,
58419         /**
58420          * @event mousedown
58421          * The raw mousedown event for the entire grid.
58422          * @param {Roo.EventObject} e
58423          */
58424         "mousedown" : true,
58425         /**
58426          * @event mouseup
58427          * The raw mouseup event for the entire grid.
58428          * @param {Roo.EventObject} e
58429          */
58430         "mouseup" : true,
58431         /**
58432          * @event mouseover
58433          * The raw mouseover event for the entire grid.
58434          * @param {Roo.EventObject} e
58435          */
58436         "mouseover" : true,
58437         /**
58438          * @event mouseout
58439          * The raw mouseout event for the entire grid.
58440          * @param {Roo.EventObject} e
58441          */
58442         "mouseout" : true,
58443         /**
58444          * @event keypress
58445          * The raw keypress event for the entire grid.
58446          * @param {Roo.EventObject} e
58447          */
58448         "keypress" : true,
58449         /**
58450          * @event keydown
58451          * The raw keydown event for the entire grid.
58452          * @param {Roo.EventObject} e
58453          */
58454         "keydown" : true,
58455
58456         // custom events
58457
58458         /**
58459          * @event cellclick
58460          * Fires when a cell is clicked
58461          * @param {Grid} this
58462          * @param {Number} rowIndex
58463          * @param {Number} columnIndex
58464          * @param {Roo.EventObject} e
58465          */
58466         "cellclick" : true,
58467         /**
58468          * @event celldblclick
58469          * Fires when a cell is double clicked
58470          * @param {Grid} this
58471          * @param {Number} rowIndex
58472          * @param {Number} columnIndex
58473          * @param {Roo.EventObject} e
58474          */
58475         "celldblclick" : true,
58476         /**
58477          * @event rowclick
58478          * Fires when a row is clicked
58479          * @param {Grid} this
58480          * @param {Number} rowIndex
58481          * @param {Roo.EventObject} e
58482          */
58483         "rowclick" : true,
58484         /**
58485          * @event rowdblclick
58486          * Fires when a row is double clicked
58487          * @param {Grid} this
58488          * @param {Number} rowIndex
58489          * @param {Roo.EventObject} e
58490          */
58491         "rowdblclick" : true,
58492         /**
58493          * @event headerclick
58494          * Fires when a header is clicked
58495          * @param {Grid} this
58496          * @param {Number} columnIndex
58497          * @param {Roo.EventObject} e
58498          */
58499         "headerclick" : true,
58500         /**
58501          * @event headerdblclick
58502          * Fires when a header cell is double clicked
58503          * @param {Grid} this
58504          * @param {Number} columnIndex
58505          * @param {Roo.EventObject} e
58506          */
58507         "headerdblclick" : true,
58508         /**
58509          * @event rowcontextmenu
58510          * Fires when a row is right clicked
58511          * @param {Grid} this
58512          * @param {Number} rowIndex
58513          * @param {Roo.EventObject} e
58514          */
58515         "rowcontextmenu" : true,
58516         /**
58517          * @event cellcontextmenu
58518          * Fires when a cell is right clicked
58519          * @param {Grid} this
58520          * @param {Number} rowIndex
58521          * @param {Number} cellIndex
58522          * @param {Roo.EventObject} e
58523          */
58524          "cellcontextmenu" : true,
58525         /**
58526          * @event headercontextmenu
58527          * Fires when a header is right clicked
58528          * @param {Grid} this
58529          * @param {Number} columnIndex
58530          * @param {Roo.EventObject} e
58531          */
58532         "headercontextmenu" : true,
58533         /**
58534          * @event bodyscroll
58535          * Fires when the body element is scrolled
58536          * @param {Number} scrollLeft
58537          * @param {Number} scrollTop
58538          */
58539         "bodyscroll" : true,
58540         /**
58541          * @event columnresize
58542          * Fires when the user resizes a column
58543          * @param {Number} columnIndex
58544          * @param {Number} newSize
58545          */
58546         "columnresize" : true,
58547         /**
58548          * @event columnmove
58549          * Fires when the user moves a column
58550          * @param {Number} oldIndex
58551          * @param {Number} newIndex
58552          */
58553         "columnmove" : true,
58554         /**
58555          * @event startdrag
58556          * Fires when row(s) start being dragged
58557          * @param {Grid} this
58558          * @param {Roo.GridDD} dd The drag drop object
58559          * @param {event} e The raw browser event
58560          */
58561         "startdrag" : true,
58562         /**
58563          * @event enddrag
58564          * Fires when a drag operation is complete
58565          * @param {Grid} this
58566          * @param {Roo.GridDD} dd The drag drop object
58567          * @param {event} e The raw browser event
58568          */
58569         "enddrag" : true,
58570         /**
58571          * @event dragdrop
58572          * Fires when dragged row(s) are dropped on a valid DD target
58573          * @param {Grid} this
58574          * @param {Roo.GridDD} dd The drag drop object
58575          * @param {String} targetId The target drag drop object
58576          * @param {event} e The raw browser event
58577          */
58578         "dragdrop" : true,
58579         /**
58580          * @event dragover
58581          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
58582          * @param {Grid} this
58583          * @param {Roo.GridDD} dd The drag drop object
58584          * @param {String} targetId The target drag drop object
58585          * @param {event} e The raw browser event
58586          */
58587         "dragover" : true,
58588         /**
58589          * @event dragenter
58590          *  Fires when the dragged row(s) first cross another DD target while being dragged
58591          * @param {Grid} this
58592          * @param {Roo.GridDD} dd The drag drop object
58593          * @param {String} targetId The target drag drop object
58594          * @param {event} e The raw browser event
58595          */
58596         "dragenter" : true,
58597         /**
58598          * @event dragout
58599          * Fires when the dragged row(s) leave another DD target while being dragged
58600          * @param {Grid} this
58601          * @param {Roo.GridDD} dd The drag drop object
58602          * @param {String} targetId The target drag drop object
58603          * @param {event} e The raw browser event
58604          */
58605         "dragout" : true,
58606         /**
58607          * @event rowclass
58608          * Fires when a row is rendered, so you can change add a style to it.
58609          * @param {GridView} gridview   The grid view
58610          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
58611          */
58612         'rowclass' : true,
58613
58614         /**
58615          * @event render
58616          * Fires when the grid is rendered
58617          * @param {Grid} grid
58618          */
58619         'render' : true
58620     });
58621
58622     Roo.grid.Grid.superclass.constructor.call(this);
58623 };
58624 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
58625     
58626     /**
58627      * @cfg {String} ddGroup - drag drop group.
58628      */
58629
58630     /**
58631      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
58632      */
58633     minColumnWidth : 25,
58634
58635     /**
58636      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
58637      * <b>on initial render.</b> It is more efficient to explicitly size the columns
58638      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
58639      */
58640     autoSizeColumns : false,
58641
58642     /**
58643      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
58644      */
58645     autoSizeHeaders : true,
58646
58647     /**
58648      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
58649      */
58650     monitorWindowResize : true,
58651
58652     /**
58653      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
58654      * rows measured to get a columns size. Default is 0 (all rows).
58655      */
58656     maxRowsToMeasure : 0,
58657
58658     /**
58659      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
58660      */
58661     trackMouseOver : true,
58662
58663     /**
58664     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
58665     */
58666     
58667     /**
58668     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
58669     */
58670     enableDragDrop : false,
58671     
58672     /**
58673     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
58674     */
58675     enableColumnMove : true,
58676     
58677     /**
58678     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
58679     */
58680     enableColumnHide : true,
58681     
58682     /**
58683     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
58684     */
58685     enableRowHeightSync : false,
58686     
58687     /**
58688     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
58689     */
58690     stripeRows : true,
58691     
58692     /**
58693     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
58694     */
58695     autoHeight : false,
58696
58697     /**
58698      * @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.
58699      */
58700     autoExpandColumn : false,
58701
58702     /**
58703     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
58704     * Default is 50.
58705     */
58706     autoExpandMin : 50,
58707
58708     /**
58709     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
58710     */
58711     autoExpandMax : 1000,
58712
58713     /**
58714     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58715     */
58716     view : null,
58717
58718     /**
58719     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58720     */
58721     loadMask : false,
58722     /**
58723     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58724     */
58725     dropTarget: false,
58726     
58727    
58728     
58729     // private
58730     rendered : false,
58731
58732     /**
58733     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58734     * of a fixed width. Default is false.
58735     */
58736     /**
58737     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58738     */
58739     /**
58740      * Called once after all setup has been completed and the grid is ready to be rendered.
58741      * @return {Roo.grid.Grid} this
58742      */
58743     render : function()
58744     {
58745         var c = this.container;
58746         // try to detect autoHeight/width mode
58747         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58748             this.autoHeight = true;
58749         }
58750         var view = this.getView();
58751         view.init(this);
58752
58753         c.on("click", this.onClick, this);
58754         c.on("dblclick", this.onDblClick, this);
58755         c.on("contextmenu", this.onContextMenu, this);
58756         c.on("keydown", this.onKeyDown, this);
58757         if (Roo.isTouch) {
58758             c.on("touchstart", this.onTouchStart, this);
58759         }
58760
58761         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58762
58763         this.getSelectionModel().init(this);
58764
58765         view.render();
58766
58767         if(this.loadMask){
58768             this.loadMask = new Roo.LoadMask(this.container,
58769                     Roo.apply({store:this.dataSource}, this.loadMask));
58770         }
58771         
58772         
58773         if (this.toolbar && this.toolbar.xtype) {
58774             this.toolbar.container = this.getView().getHeaderPanel(true);
58775             this.toolbar = new Roo.Toolbar(this.toolbar);
58776         }
58777         if (this.footer && this.footer.xtype) {
58778             this.footer.dataSource = this.getDataSource();
58779             this.footer.container = this.getView().getFooterPanel(true);
58780             this.footer = Roo.factory(this.footer, Roo);
58781         }
58782         if (this.dropTarget && this.dropTarget.xtype) {
58783             delete this.dropTarget.xtype;
58784             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58785         }
58786         
58787         
58788         this.rendered = true;
58789         this.fireEvent('render', this);
58790         return this;
58791     },
58792
58793         /**
58794          * Reconfigures the grid to use a different Store and Column Model.
58795          * The View will be bound to the new objects and refreshed.
58796          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58797          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58798          */
58799     reconfigure : function(dataSource, colModel){
58800         if(this.loadMask){
58801             this.loadMask.destroy();
58802             this.loadMask = new Roo.LoadMask(this.container,
58803                     Roo.apply({store:dataSource}, this.loadMask));
58804         }
58805         this.view.bind(dataSource, colModel);
58806         this.dataSource = dataSource;
58807         this.colModel = colModel;
58808         this.view.refresh(true);
58809     },
58810
58811     // private
58812     onKeyDown : function(e){
58813         this.fireEvent("keydown", e);
58814     },
58815
58816     /**
58817      * Destroy this grid.
58818      * @param {Boolean} removeEl True to remove the element
58819      */
58820     destroy : function(removeEl, keepListeners){
58821         if(this.loadMask){
58822             this.loadMask.destroy();
58823         }
58824         var c = this.container;
58825         c.removeAllListeners();
58826         this.view.destroy();
58827         this.colModel.purgeListeners();
58828         if(!keepListeners){
58829             this.purgeListeners();
58830         }
58831         c.update("");
58832         if(removeEl === true){
58833             c.remove();
58834         }
58835     },
58836
58837     // private
58838     processEvent : function(name, e){
58839         // does this fire select???
58840         //Roo.log('grid:processEvent '  + name);
58841         
58842         if (name != 'touchstart' ) {
58843             this.fireEvent(name, e);    
58844         }
58845         
58846         var t = e.getTarget();
58847         var v = this.view;
58848         var header = v.findHeaderIndex(t);
58849         if(header !== false){
58850             var ename = name == 'touchstart' ? 'click' : name;
58851              
58852             this.fireEvent("header" + ename, this, header, e);
58853         }else{
58854             var row = v.findRowIndex(t);
58855             var cell = v.findCellIndex(t);
58856             if (name == 'touchstart') {
58857                 // first touch is always a click.
58858                 // hopefull this happens after selection is updated.?
58859                 name = false;
58860                 
58861                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58862                     var cs = this.selModel.getSelectedCell();
58863                     if (row == cs[0] && cell == cs[1]){
58864                         name = 'dblclick';
58865                     }
58866                 }
58867                 if (typeof(this.selModel.getSelections) != 'undefined') {
58868                     var cs = this.selModel.getSelections();
58869                     var ds = this.dataSource;
58870                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
58871                         name = 'dblclick';
58872                     }
58873                 }
58874                 if (!name) {
58875                     return;
58876                 }
58877             }
58878             
58879             
58880             if(row !== false){
58881                 this.fireEvent("row" + name, this, row, e);
58882                 if(cell !== false){
58883                     this.fireEvent("cell" + name, this, row, cell, e);
58884                 }
58885             }
58886         }
58887     },
58888
58889     // private
58890     onClick : function(e){
58891         this.processEvent("click", e);
58892     },
58893    // private
58894     onTouchStart : function(e){
58895         this.processEvent("touchstart", e);
58896     },
58897
58898     // private
58899     onContextMenu : function(e, t){
58900         this.processEvent("contextmenu", e);
58901     },
58902
58903     // private
58904     onDblClick : function(e){
58905         this.processEvent("dblclick", e);
58906     },
58907
58908     // private
58909     walkCells : function(row, col, step, fn, scope){
58910         var cm = this.colModel, clen = cm.getColumnCount();
58911         var ds = this.dataSource, rlen = ds.getCount(), first = true;
58912         if(step < 0){
58913             if(col < 0){
58914                 row--;
58915                 first = false;
58916             }
58917             while(row >= 0){
58918                 if(!first){
58919                     col = clen-1;
58920                 }
58921                 first = false;
58922                 while(col >= 0){
58923                     if(fn.call(scope || this, row, col, cm) === true){
58924                         return [row, col];
58925                     }
58926                     col--;
58927                 }
58928                 row--;
58929             }
58930         } else {
58931             if(col >= clen){
58932                 row++;
58933                 first = false;
58934             }
58935             while(row < rlen){
58936                 if(!first){
58937                     col = 0;
58938                 }
58939                 first = false;
58940                 while(col < clen){
58941                     if(fn.call(scope || this, row, col, cm) === true){
58942                         return [row, col];
58943                     }
58944                     col++;
58945                 }
58946                 row++;
58947             }
58948         }
58949         return null;
58950     },
58951
58952     // private
58953     getSelections : function(){
58954         return this.selModel.getSelections();
58955     },
58956
58957     /**
58958      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58959      * but if manual update is required this method will initiate it.
58960      */
58961     autoSize : function(){
58962         if(this.rendered){
58963             this.view.layout();
58964             if(this.view.adjustForScroll){
58965                 this.view.adjustForScroll();
58966             }
58967         }
58968     },
58969
58970     /**
58971      * Returns the grid's underlying element.
58972      * @return {Element} The element
58973      */
58974     getGridEl : function(){
58975         return this.container;
58976     },
58977
58978     // private for compatibility, overridden by editor grid
58979     stopEditing : function(){},
58980
58981     /**
58982      * Returns the grid's SelectionModel.
58983      * @return {SelectionModel}
58984      */
58985     getSelectionModel : function(){
58986         if(!this.selModel){
58987             this.selModel = new Roo.grid.RowSelectionModel();
58988         }
58989         return this.selModel;
58990     },
58991
58992     /**
58993      * Returns the grid's DataSource.
58994      * @return {DataSource}
58995      */
58996     getDataSource : function(){
58997         return this.dataSource;
58998     },
58999
59000     /**
59001      * Returns the grid's ColumnModel.
59002      * @return {ColumnModel}
59003      */
59004     getColumnModel : function(){
59005         return this.colModel;
59006     },
59007
59008     /**
59009      * Returns the grid's GridView object.
59010      * @return {GridView}
59011      */
59012     getView : function(){
59013         if(!this.view){
59014             this.view = new Roo.grid.GridView(this.viewConfig);
59015         }
59016         return this.view;
59017     },
59018     /**
59019      * Called to get grid's drag proxy text, by default returns this.ddText.
59020      * @return {String}
59021      */
59022     getDragDropText : function(){
59023         var count = this.selModel.getCount();
59024         return String.format(this.ddText, count, count == 1 ? '' : 's');
59025     }
59026 });
59027 /**
59028  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
59029  * %0 is replaced with the number of selected rows.
59030  * @type String
59031  */
59032 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
59033  * Based on:
59034  * Ext JS Library 1.1.1
59035  * Copyright(c) 2006-2007, Ext JS, LLC.
59036  *
59037  * Originally Released Under LGPL - original licence link has changed is not relivant.
59038  *
59039  * Fork - LGPL
59040  * <script type="text/javascript">
59041  */
59042  
59043 Roo.grid.AbstractGridView = function(){
59044         this.grid = null;
59045         
59046         this.events = {
59047             "beforerowremoved" : true,
59048             "beforerowsinserted" : true,
59049             "beforerefresh" : true,
59050             "rowremoved" : true,
59051             "rowsinserted" : true,
59052             "rowupdated" : true,
59053             "refresh" : true
59054         };
59055     Roo.grid.AbstractGridView.superclass.constructor.call(this);
59056 };
59057
59058 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
59059     rowClass : "x-grid-row",
59060     cellClass : "x-grid-cell",
59061     tdClass : "x-grid-td",
59062     hdClass : "x-grid-hd",
59063     splitClass : "x-grid-hd-split",
59064     
59065     init: function(grid){
59066         this.grid = grid;
59067                 var cid = this.grid.getGridEl().id;
59068         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
59069         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
59070         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
59071         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
59072         },
59073         
59074     getColumnRenderers : function(){
59075         var renderers = [];
59076         var cm = this.grid.colModel;
59077         var colCount = cm.getColumnCount();
59078         for(var i = 0; i < colCount; i++){
59079             renderers[i] = cm.getRenderer(i);
59080         }
59081         return renderers;
59082     },
59083     
59084     getColumnIds : function(){
59085         var ids = [];
59086         var cm = this.grid.colModel;
59087         var colCount = cm.getColumnCount();
59088         for(var i = 0; i < colCount; i++){
59089             ids[i] = cm.getColumnId(i);
59090         }
59091         return ids;
59092     },
59093     
59094     getDataIndexes : function(){
59095         if(!this.indexMap){
59096             this.indexMap = this.buildIndexMap();
59097         }
59098         return this.indexMap.colToData;
59099     },
59100     
59101     getColumnIndexByDataIndex : function(dataIndex){
59102         if(!this.indexMap){
59103             this.indexMap = this.buildIndexMap();
59104         }
59105         return this.indexMap.dataToCol[dataIndex];
59106     },
59107     
59108     /**
59109      * Set a css style for a column dynamically. 
59110      * @param {Number} colIndex The index of the column
59111      * @param {String} name The css property name
59112      * @param {String} value The css value
59113      */
59114     setCSSStyle : function(colIndex, name, value){
59115         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
59116         Roo.util.CSS.updateRule(selector, name, value);
59117     },
59118     
59119     generateRules : function(cm){
59120         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
59121         Roo.util.CSS.removeStyleSheet(rulesId);
59122         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59123             var cid = cm.getColumnId(i);
59124             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
59125                          this.tdSelector, cid, " {\n}\n",
59126                          this.hdSelector, cid, " {\n}\n",
59127                          this.splitSelector, cid, " {\n}\n");
59128         }
59129         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59130     }
59131 });/*
59132  * Based on:
59133  * Ext JS Library 1.1.1
59134  * Copyright(c) 2006-2007, Ext JS, LLC.
59135  *
59136  * Originally Released Under LGPL - original licence link has changed is not relivant.
59137  *
59138  * Fork - LGPL
59139  * <script type="text/javascript">
59140  */
59141
59142 // private
59143 // This is a support class used internally by the Grid components
59144 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
59145     this.grid = grid;
59146     this.view = grid.getView();
59147     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
59148     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
59149     if(hd2){
59150         this.setHandleElId(Roo.id(hd));
59151         this.setOuterHandleElId(Roo.id(hd2));
59152     }
59153     this.scroll = false;
59154 };
59155 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
59156     maxDragWidth: 120,
59157     getDragData : function(e){
59158         var t = Roo.lib.Event.getTarget(e);
59159         var h = this.view.findHeaderCell(t);
59160         if(h){
59161             return {ddel: h.firstChild, header:h};
59162         }
59163         return false;
59164     },
59165
59166     onInitDrag : function(e){
59167         this.view.headersDisabled = true;
59168         var clone = this.dragData.ddel.cloneNode(true);
59169         clone.id = Roo.id();
59170         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
59171         this.proxy.update(clone);
59172         return true;
59173     },
59174
59175     afterValidDrop : function(){
59176         var v = this.view;
59177         setTimeout(function(){
59178             v.headersDisabled = false;
59179         }, 50);
59180     },
59181
59182     afterInvalidDrop : function(){
59183         var v = this.view;
59184         setTimeout(function(){
59185             v.headersDisabled = false;
59186         }, 50);
59187     }
59188 });
59189 /*
59190  * Based on:
59191  * Ext JS Library 1.1.1
59192  * Copyright(c) 2006-2007, Ext JS, LLC.
59193  *
59194  * Originally Released Under LGPL - original licence link has changed is not relivant.
59195  *
59196  * Fork - LGPL
59197  * <script type="text/javascript">
59198  */
59199 // private
59200 // This is a support class used internally by the Grid components
59201 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
59202     this.grid = grid;
59203     this.view = grid.getView();
59204     // split the proxies so they don't interfere with mouse events
59205     this.proxyTop = Roo.DomHelper.append(document.body, {
59206         cls:"col-move-top", html:"&#160;"
59207     }, true);
59208     this.proxyBottom = Roo.DomHelper.append(document.body, {
59209         cls:"col-move-bottom", html:"&#160;"
59210     }, true);
59211     this.proxyTop.hide = this.proxyBottom.hide = function(){
59212         this.setLeftTop(-100,-100);
59213         this.setStyle("visibility", "hidden");
59214     };
59215     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
59216     // temporarily disabled
59217     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
59218     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
59219 };
59220 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
59221     proxyOffsets : [-4, -9],
59222     fly: Roo.Element.fly,
59223
59224     getTargetFromEvent : function(e){
59225         var t = Roo.lib.Event.getTarget(e);
59226         var cindex = this.view.findCellIndex(t);
59227         if(cindex !== false){
59228             return this.view.getHeaderCell(cindex);
59229         }
59230         return null;
59231     },
59232
59233     nextVisible : function(h){
59234         var v = this.view, cm = this.grid.colModel;
59235         h = h.nextSibling;
59236         while(h){
59237             if(!cm.isHidden(v.getCellIndex(h))){
59238                 return h;
59239             }
59240             h = h.nextSibling;
59241         }
59242         return null;
59243     },
59244
59245     prevVisible : function(h){
59246         var v = this.view, cm = this.grid.colModel;
59247         h = h.prevSibling;
59248         while(h){
59249             if(!cm.isHidden(v.getCellIndex(h))){
59250                 return h;
59251             }
59252             h = h.prevSibling;
59253         }
59254         return null;
59255     },
59256
59257     positionIndicator : function(h, n, e){
59258         var x = Roo.lib.Event.getPageX(e);
59259         var r = Roo.lib.Dom.getRegion(n.firstChild);
59260         var px, pt, py = r.top + this.proxyOffsets[1];
59261         if((r.right - x) <= (r.right-r.left)/2){
59262             px = r.right+this.view.borderWidth;
59263             pt = "after";
59264         }else{
59265             px = r.left;
59266             pt = "before";
59267         }
59268         var oldIndex = this.view.getCellIndex(h);
59269         var newIndex = this.view.getCellIndex(n);
59270
59271         if(this.grid.colModel.isFixed(newIndex)){
59272             return false;
59273         }
59274
59275         var locked = this.grid.colModel.isLocked(newIndex);
59276
59277         if(pt == "after"){
59278             newIndex++;
59279         }
59280         if(oldIndex < newIndex){
59281             newIndex--;
59282         }
59283         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
59284             return false;
59285         }
59286         px +=  this.proxyOffsets[0];
59287         this.proxyTop.setLeftTop(px, py);
59288         this.proxyTop.show();
59289         if(!this.bottomOffset){
59290             this.bottomOffset = this.view.mainHd.getHeight();
59291         }
59292         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
59293         this.proxyBottom.show();
59294         return pt;
59295     },
59296
59297     onNodeEnter : function(n, dd, e, data){
59298         if(data.header != n){
59299             this.positionIndicator(data.header, n, e);
59300         }
59301     },
59302
59303     onNodeOver : function(n, dd, e, data){
59304         var result = false;
59305         if(data.header != n){
59306             result = this.positionIndicator(data.header, n, e);
59307         }
59308         if(!result){
59309             this.proxyTop.hide();
59310             this.proxyBottom.hide();
59311         }
59312         return result ? this.dropAllowed : this.dropNotAllowed;
59313     },
59314
59315     onNodeOut : function(n, dd, e, data){
59316         this.proxyTop.hide();
59317         this.proxyBottom.hide();
59318     },
59319
59320     onNodeDrop : function(n, dd, e, data){
59321         var h = data.header;
59322         if(h != n){
59323             var cm = this.grid.colModel;
59324             var x = Roo.lib.Event.getPageX(e);
59325             var r = Roo.lib.Dom.getRegion(n.firstChild);
59326             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
59327             var oldIndex = this.view.getCellIndex(h);
59328             var newIndex = this.view.getCellIndex(n);
59329             var locked = cm.isLocked(newIndex);
59330             if(pt == "after"){
59331                 newIndex++;
59332             }
59333             if(oldIndex < newIndex){
59334                 newIndex--;
59335             }
59336             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
59337                 return false;
59338             }
59339             cm.setLocked(oldIndex, locked, true);
59340             cm.moveColumn(oldIndex, newIndex);
59341             this.grid.fireEvent("columnmove", oldIndex, newIndex);
59342             return true;
59343         }
59344         return false;
59345     }
59346 });
59347 /*
59348  * Based on:
59349  * Ext JS Library 1.1.1
59350  * Copyright(c) 2006-2007, Ext JS, LLC.
59351  *
59352  * Originally Released Under LGPL - original licence link has changed is not relivant.
59353  *
59354  * Fork - LGPL
59355  * <script type="text/javascript">
59356  */
59357   
59358 /**
59359  * @class Roo.grid.GridView
59360  * @extends Roo.util.Observable
59361  *
59362  * @constructor
59363  * @param {Object} config
59364  */
59365 Roo.grid.GridView = function(config){
59366     Roo.grid.GridView.superclass.constructor.call(this);
59367     this.el = null;
59368
59369     Roo.apply(this, config);
59370 };
59371
59372 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
59373
59374     unselectable :  'unselectable="on"',
59375     unselectableCls :  'x-unselectable',
59376     
59377     
59378     rowClass : "x-grid-row",
59379
59380     cellClass : "x-grid-col",
59381
59382     tdClass : "x-grid-td",
59383
59384     hdClass : "x-grid-hd",
59385
59386     splitClass : "x-grid-split",
59387
59388     sortClasses : ["sort-asc", "sort-desc"],
59389
59390     enableMoveAnim : false,
59391
59392     hlColor: "C3DAF9",
59393
59394     dh : Roo.DomHelper,
59395
59396     fly : Roo.Element.fly,
59397
59398     css : Roo.util.CSS,
59399
59400     borderWidth: 1,
59401
59402     splitOffset: 3,
59403
59404     scrollIncrement : 22,
59405
59406     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
59407
59408     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
59409
59410     bind : function(ds, cm){
59411         if(this.ds){
59412             this.ds.un("load", this.onLoad, this);
59413             this.ds.un("datachanged", this.onDataChange, this);
59414             this.ds.un("add", this.onAdd, this);
59415             this.ds.un("remove", this.onRemove, this);
59416             this.ds.un("update", this.onUpdate, this);
59417             this.ds.un("clear", this.onClear, this);
59418         }
59419         if(ds){
59420             ds.on("load", this.onLoad, this);
59421             ds.on("datachanged", this.onDataChange, this);
59422             ds.on("add", this.onAdd, this);
59423             ds.on("remove", this.onRemove, this);
59424             ds.on("update", this.onUpdate, this);
59425             ds.on("clear", this.onClear, this);
59426         }
59427         this.ds = ds;
59428
59429         if(this.cm){
59430             this.cm.un("widthchange", this.onColWidthChange, this);
59431             this.cm.un("headerchange", this.onHeaderChange, this);
59432             this.cm.un("hiddenchange", this.onHiddenChange, this);
59433             this.cm.un("columnmoved", this.onColumnMove, this);
59434             this.cm.un("columnlockchange", this.onColumnLock, this);
59435         }
59436         if(cm){
59437             this.generateRules(cm);
59438             cm.on("widthchange", this.onColWidthChange, this);
59439             cm.on("headerchange", this.onHeaderChange, this);
59440             cm.on("hiddenchange", this.onHiddenChange, this);
59441             cm.on("columnmoved", this.onColumnMove, this);
59442             cm.on("columnlockchange", this.onColumnLock, this);
59443         }
59444         this.cm = cm;
59445     },
59446
59447     init: function(grid){
59448         Roo.grid.GridView.superclass.init.call(this, grid);
59449
59450         this.bind(grid.dataSource, grid.colModel);
59451
59452         grid.on("headerclick", this.handleHeaderClick, this);
59453
59454         if(grid.trackMouseOver){
59455             grid.on("mouseover", this.onRowOver, this);
59456             grid.on("mouseout", this.onRowOut, this);
59457         }
59458         grid.cancelTextSelection = function(){};
59459         this.gridId = grid.id;
59460
59461         var tpls = this.templates || {};
59462
59463         if(!tpls.master){
59464             tpls.master = new Roo.Template(
59465                '<div class="x-grid" hidefocus="true">',
59466                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
59467                   '<div class="x-grid-topbar"></div>',
59468                   '<div class="x-grid-scroller"><div></div></div>',
59469                   '<div class="x-grid-locked">',
59470                       '<div class="x-grid-header">{lockedHeader}</div>',
59471                       '<div class="x-grid-body">{lockedBody}</div>',
59472                   "</div>",
59473                   '<div class="x-grid-viewport">',
59474                       '<div class="x-grid-header">{header}</div>',
59475                       '<div class="x-grid-body">{body}</div>',
59476                   "</div>",
59477                   '<div class="x-grid-bottombar"></div>',
59478                  
59479                   '<div class="x-grid-resize-proxy">&#160;</div>',
59480                "</div>"
59481             );
59482             tpls.master.disableformats = true;
59483         }
59484
59485         if(!tpls.header){
59486             tpls.header = new Roo.Template(
59487                '<table border="0" cellspacing="0" cellpadding="0">',
59488                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
59489                "</table>{splits}"
59490             );
59491             tpls.header.disableformats = true;
59492         }
59493         tpls.header.compile();
59494
59495         if(!tpls.hcell){
59496             tpls.hcell = new Roo.Template(
59497                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
59498                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
59499                 "</div></td>"
59500              );
59501              tpls.hcell.disableFormats = true;
59502         }
59503         tpls.hcell.compile();
59504
59505         if(!tpls.hsplit){
59506             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
59507                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
59508             tpls.hsplit.disableFormats = true;
59509         }
59510         tpls.hsplit.compile();
59511
59512         if(!tpls.body){
59513             tpls.body = new Roo.Template(
59514                '<table border="0" cellspacing="0" cellpadding="0">',
59515                "<tbody>{rows}</tbody>",
59516                "</table>"
59517             );
59518             tpls.body.disableFormats = true;
59519         }
59520         tpls.body.compile();
59521
59522         if(!tpls.row){
59523             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
59524             tpls.row.disableFormats = true;
59525         }
59526         tpls.row.compile();
59527
59528         if(!tpls.cell){
59529             tpls.cell = new Roo.Template(
59530                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
59531                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
59532                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
59533                 "</td>"
59534             );
59535             tpls.cell.disableFormats = true;
59536         }
59537         tpls.cell.compile();
59538
59539         this.templates = tpls;
59540     },
59541
59542     // remap these for backwards compat
59543     onColWidthChange : function(){
59544         this.updateColumns.apply(this, arguments);
59545     },
59546     onHeaderChange : function(){
59547         this.updateHeaders.apply(this, arguments);
59548     }, 
59549     onHiddenChange : function(){
59550         this.handleHiddenChange.apply(this, arguments);
59551     },
59552     onColumnMove : function(){
59553         this.handleColumnMove.apply(this, arguments);
59554     },
59555     onColumnLock : function(){
59556         this.handleLockChange.apply(this, arguments);
59557     },
59558
59559     onDataChange : function(){
59560         this.refresh();
59561         this.updateHeaderSortState();
59562     },
59563
59564     onClear : function(){
59565         this.refresh();
59566     },
59567
59568     onUpdate : function(ds, record){
59569         this.refreshRow(record);
59570     },
59571
59572     refreshRow : function(record){
59573         var ds = this.ds, index;
59574         if(typeof record == 'number'){
59575             index = record;
59576             record = ds.getAt(index);
59577         }else{
59578             index = ds.indexOf(record);
59579         }
59580         this.insertRows(ds, index, index, true);
59581         this.onRemove(ds, record, index+1, true);
59582         this.syncRowHeights(index, index);
59583         this.layout();
59584         this.fireEvent("rowupdated", this, index, record);
59585     },
59586
59587     onAdd : function(ds, records, index){
59588         this.insertRows(ds, index, index + (records.length-1));
59589     },
59590
59591     onRemove : function(ds, record, index, isUpdate){
59592         if(isUpdate !== true){
59593             this.fireEvent("beforerowremoved", this, index, record);
59594         }
59595         var bt = this.getBodyTable(), lt = this.getLockedTable();
59596         if(bt.rows[index]){
59597             bt.firstChild.removeChild(bt.rows[index]);
59598         }
59599         if(lt.rows[index]){
59600             lt.firstChild.removeChild(lt.rows[index]);
59601         }
59602         if(isUpdate !== true){
59603             this.stripeRows(index);
59604             this.syncRowHeights(index, index);
59605             this.layout();
59606             this.fireEvent("rowremoved", this, index, record);
59607         }
59608     },
59609
59610     onLoad : function(){
59611         this.scrollToTop();
59612     },
59613
59614     /**
59615      * Scrolls the grid to the top
59616      */
59617     scrollToTop : function(){
59618         if(this.scroller){
59619             this.scroller.dom.scrollTop = 0;
59620             this.syncScroll();
59621         }
59622     },
59623
59624     /**
59625      * Gets a panel in the header of the grid that can be used for toolbars etc.
59626      * After modifying the contents of this panel a call to grid.autoSize() may be
59627      * required to register any changes in size.
59628      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
59629      * @return Roo.Element
59630      */
59631     getHeaderPanel : function(doShow){
59632         if(doShow){
59633             this.headerPanel.show();
59634         }
59635         return this.headerPanel;
59636     },
59637
59638     /**
59639      * Gets a panel in the footer of the grid that can be used for toolbars etc.
59640      * After modifying the contents of this panel a call to grid.autoSize() may be
59641      * required to register any changes in size.
59642      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
59643      * @return Roo.Element
59644      */
59645     getFooterPanel : function(doShow){
59646         if(doShow){
59647             this.footerPanel.show();
59648         }
59649         return this.footerPanel;
59650     },
59651
59652     initElements : function(){
59653         var E = Roo.Element;
59654         var el = this.grid.getGridEl().dom.firstChild;
59655         var cs = el.childNodes;
59656
59657         this.el = new E(el);
59658         
59659          this.focusEl = new E(el.firstChild);
59660         this.focusEl.swallowEvent("click", true);
59661         
59662         this.headerPanel = new E(cs[1]);
59663         this.headerPanel.enableDisplayMode("block");
59664
59665         this.scroller = new E(cs[2]);
59666         this.scrollSizer = new E(this.scroller.dom.firstChild);
59667
59668         this.lockedWrap = new E(cs[3]);
59669         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59670         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59671
59672         this.mainWrap = new E(cs[4]);
59673         this.mainHd = new E(this.mainWrap.dom.firstChild);
59674         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59675
59676         this.footerPanel = new E(cs[5]);
59677         this.footerPanel.enableDisplayMode("block");
59678
59679         this.resizeProxy = new E(cs[6]);
59680
59681         this.headerSelector = String.format(
59682            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59683            this.lockedHd.id, this.mainHd.id
59684         );
59685
59686         this.splitterSelector = String.format(
59687            '#{0} div.x-grid-split, #{1} div.x-grid-split',
59688            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59689         );
59690     },
59691     idToCssName : function(s)
59692     {
59693         return s.replace(/[^a-z0-9]+/ig, '-');
59694     },
59695
59696     getHeaderCell : function(index){
59697         return Roo.DomQuery.select(this.headerSelector)[index];
59698     },
59699
59700     getHeaderCellMeasure : function(index){
59701         return this.getHeaderCell(index).firstChild;
59702     },
59703
59704     getHeaderCellText : function(index){
59705         return this.getHeaderCell(index).firstChild.firstChild;
59706     },
59707
59708     getLockedTable : function(){
59709         return this.lockedBody.dom.firstChild;
59710     },
59711
59712     getBodyTable : function(){
59713         return this.mainBody.dom.firstChild;
59714     },
59715
59716     getLockedRow : function(index){
59717         return this.getLockedTable().rows[index];
59718     },
59719
59720     getRow : function(index){
59721         return this.getBodyTable().rows[index];
59722     },
59723
59724     getRowComposite : function(index){
59725         if(!this.rowEl){
59726             this.rowEl = new Roo.CompositeElementLite();
59727         }
59728         var els = [], lrow, mrow;
59729         if(lrow = this.getLockedRow(index)){
59730             els.push(lrow);
59731         }
59732         if(mrow = this.getRow(index)){
59733             els.push(mrow);
59734         }
59735         this.rowEl.elements = els;
59736         return this.rowEl;
59737     },
59738     /**
59739      * Gets the 'td' of the cell
59740      * 
59741      * @param {Integer} rowIndex row to select
59742      * @param {Integer} colIndex column to select
59743      * 
59744      * @return {Object} 
59745      */
59746     getCell : function(rowIndex, colIndex){
59747         var locked = this.cm.getLockedCount();
59748         var source;
59749         if(colIndex < locked){
59750             source = this.lockedBody.dom.firstChild;
59751         }else{
59752             source = this.mainBody.dom.firstChild;
59753             colIndex -= locked;
59754         }
59755         return source.rows[rowIndex].childNodes[colIndex];
59756     },
59757
59758     getCellText : function(rowIndex, colIndex){
59759         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59760     },
59761
59762     getCellBox : function(cell){
59763         var b = this.fly(cell).getBox();
59764         if(Roo.isOpera){ // opera fails to report the Y
59765             b.y = cell.offsetTop + this.mainBody.getY();
59766         }
59767         return b;
59768     },
59769
59770     getCellIndex : function(cell){
59771         var id = String(cell.className).match(this.cellRE);
59772         if(id){
59773             return parseInt(id[1], 10);
59774         }
59775         return 0;
59776     },
59777
59778     findHeaderIndex : function(n){
59779         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59780         return r ? this.getCellIndex(r) : false;
59781     },
59782
59783     findHeaderCell : function(n){
59784         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59785         return r ? r : false;
59786     },
59787
59788     findRowIndex : function(n){
59789         if(!n){
59790             return false;
59791         }
59792         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59793         return r ? r.rowIndex : false;
59794     },
59795
59796     findCellIndex : function(node){
59797         var stop = this.el.dom;
59798         while(node && node != stop){
59799             if(this.findRE.test(node.className)){
59800                 return this.getCellIndex(node);
59801             }
59802             node = node.parentNode;
59803         }
59804         return false;
59805     },
59806
59807     getColumnId : function(index){
59808         return this.cm.getColumnId(index);
59809     },
59810
59811     getSplitters : function()
59812     {
59813         if(this.splitterSelector){
59814            return Roo.DomQuery.select(this.splitterSelector);
59815         }else{
59816             return null;
59817       }
59818     },
59819
59820     getSplitter : function(index){
59821         return this.getSplitters()[index];
59822     },
59823
59824     onRowOver : function(e, t){
59825         var row;
59826         if((row = this.findRowIndex(t)) !== false){
59827             this.getRowComposite(row).addClass("x-grid-row-over");
59828         }
59829     },
59830
59831     onRowOut : function(e, t){
59832         var row;
59833         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59834             this.getRowComposite(row).removeClass("x-grid-row-over");
59835         }
59836     },
59837
59838     renderHeaders : function(){
59839         var cm = this.cm;
59840         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59841         var cb = [], lb = [], sb = [], lsb = [], p = {};
59842         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59843             p.cellId = "x-grid-hd-0-" + i;
59844             p.splitId = "x-grid-csplit-0-" + i;
59845             p.id = cm.getColumnId(i);
59846             p.value = cm.getColumnHeader(i) || "";
59847             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
59848             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59849             if(!cm.isLocked(i)){
59850                 cb[cb.length] = ct.apply(p);
59851                 sb[sb.length] = st.apply(p);
59852             }else{
59853                 lb[lb.length] = ct.apply(p);
59854                 lsb[lsb.length] = st.apply(p);
59855             }
59856         }
59857         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59858                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59859     },
59860
59861     updateHeaders : function(){
59862         var html = this.renderHeaders();
59863         this.lockedHd.update(html[0]);
59864         this.mainHd.update(html[1]);
59865     },
59866
59867     /**
59868      * Focuses the specified row.
59869      * @param {Number} row The row index
59870      */
59871     focusRow : function(row)
59872     {
59873         //Roo.log('GridView.focusRow');
59874         var x = this.scroller.dom.scrollLeft;
59875         this.focusCell(row, 0, false);
59876         this.scroller.dom.scrollLeft = x;
59877     },
59878
59879     /**
59880      * Focuses the specified cell.
59881      * @param {Number} row The row index
59882      * @param {Number} col The column index
59883      * @param {Boolean} hscroll false to disable horizontal scrolling
59884      */
59885     focusCell : function(row, col, hscroll)
59886     {
59887         //Roo.log('GridView.focusCell');
59888         var el = this.ensureVisible(row, col, hscroll);
59889         this.focusEl.alignTo(el, "tl-tl");
59890         if(Roo.isGecko){
59891             this.focusEl.focus();
59892         }else{
59893             this.focusEl.focus.defer(1, this.focusEl);
59894         }
59895     },
59896
59897     /**
59898      * Scrolls the specified cell into view
59899      * @param {Number} row The row index
59900      * @param {Number} col The column index
59901      * @param {Boolean} hscroll false to disable horizontal scrolling
59902      */
59903     ensureVisible : function(row, col, hscroll)
59904     {
59905         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59906         //return null; //disable for testing.
59907         if(typeof row != "number"){
59908             row = row.rowIndex;
59909         }
59910         if(row < 0 && row >= this.ds.getCount()){
59911             return  null;
59912         }
59913         col = (col !== undefined ? col : 0);
59914         var cm = this.grid.colModel;
59915         while(cm.isHidden(col)){
59916             col++;
59917         }
59918
59919         var el = this.getCell(row, col);
59920         if(!el){
59921             return null;
59922         }
59923         var c = this.scroller.dom;
59924
59925         var ctop = parseInt(el.offsetTop, 10);
59926         var cleft = parseInt(el.offsetLeft, 10);
59927         var cbot = ctop + el.offsetHeight;
59928         var cright = cleft + el.offsetWidth;
59929         
59930         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59931         var stop = parseInt(c.scrollTop, 10);
59932         var sleft = parseInt(c.scrollLeft, 10);
59933         var sbot = stop + ch;
59934         var sright = sleft + c.clientWidth;
59935         /*
59936         Roo.log('GridView.ensureVisible:' +
59937                 ' ctop:' + ctop +
59938                 ' c.clientHeight:' + c.clientHeight +
59939                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59940                 ' stop:' + stop +
59941                 ' cbot:' + cbot +
59942                 ' sbot:' + sbot +
59943                 ' ch:' + ch  
59944                 );
59945         */
59946         if(ctop < stop){
59947              c.scrollTop = ctop;
59948             //Roo.log("set scrolltop to ctop DISABLE?");
59949         }else if(cbot > sbot){
59950             //Roo.log("set scrolltop to cbot-ch");
59951             c.scrollTop = cbot-ch;
59952         }
59953         
59954         if(hscroll !== false){
59955             if(cleft < sleft){
59956                 c.scrollLeft = cleft;
59957             }else if(cright > sright){
59958                 c.scrollLeft = cright-c.clientWidth;
59959             }
59960         }
59961          
59962         return el;
59963     },
59964
59965     updateColumns : function(){
59966         this.grid.stopEditing();
59967         var cm = this.grid.colModel, colIds = this.getColumnIds();
59968         //var totalWidth = cm.getTotalWidth();
59969         var pos = 0;
59970         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59971             //if(cm.isHidden(i)) continue;
59972             var w = cm.getColumnWidth(i);
59973             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59974             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59975         }
59976         this.updateSplitters();
59977     },
59978
59979     generateRules : function(cm){
59980         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59981         Roo.util.CSS.removeStyleSheet(rulesId);
59982         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59983             var cid = cm.getColumnId(i);
59984             var align = '';
59985             if(cm.config[i].align){
59986                 align = 'text-align:'+cm.config[i].align+';';
59987             }
59988             var hidden = '';
59989             if(cm.isHidden(i)){
59990                 hidden = 'display:none;';
59991             }
59992             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59993             ruleBuf.push(
59994                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59995                     this.hdSelector, cid, " {\n", align, width, "}\n",
59996                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
59997                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
59998         }
59999         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
60000     },
60001
60002     updateSplitters : function(){
60003         var cm = this.cm, s = this.getSplitters();
60004         if(s){ // splitters not created yet
60005             var pos = 0, locked = true;
60006             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
60007                 if(cm.isHidden(i)) {
60008                     continue;
60009                 }
60010                 var w = cm.getColumnWidth(i); // make sure it's a number
60011                 if(!cm.isLocked(i) && locked){
60012                     pos = 0;
60013                     locked = false;
60014                 }
60015                 pos += w;
60016                 s[i].style.left = (pos-this.splitOffset) + "px";
60017             }
60018         }
60019     },
60020
60021     handleHiddenChange : function(colModel, colIndex, hidden){
60022         if(hidden){
60023             this.hideColumn(colIndex);
60024         }else{
60025             this.unhideColumn(colIndex);
60026         }
60027     },
60028
60029     hideColumn : function(colIndex){
60030         var cid = this.getColumnId(colIndex);
60031         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
60032         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
60033         if(Roo.isSafari){
60034             this.updateHeaders();
60035         }
60036         this.updateSplitters();
60037         this.layout();
60038     },
60039
60040     unhideColumn : function(colIndex){
60041         var cid = this.getColumnId(colIndex);
60042         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
60043         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
60044
60045         if(Roo.isSafari){
60046             this.updateHeaders();
60047         }
60048         this.updateSplitters();
60049         this.layout();
60050     },
60051
60052     insertRows : function(dm, firstRow, lastRow, isUpdate){
60053         if(firstRow == 0 && lastRow == dm.getCount()-1){
60054             this.refresh();
60055         }else{
60056             if(!isUpdate){
60057                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
60058             }
60059             var s = this.getScrollState();
60060             var markup = this.renderRows(firstRow, lastRow);
60061             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
60062             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
60063             this.restoreScroll(s);
60064             if(!isUpdate){
60065                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
60066                 this.syncRowHeights(firstRow, lastRow);
60067                 this.stripeRows(firstRow);
60068                 this.layout();
60069             }
60070         }
60071     },
60072
60073     bufferRows : function(markup, target, index){
60074         var before = null, trows = target.rows, tbody = target.tBodies[0];
60075         if(index < trows.length){
60076             before = trows[index];
60077         }
60078         var b = document.createElement("div");
60079         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
60080         var rows = b.firstChild.rows;
60081         for(var i = 0, len = rows.length; i < len; i++){
60082             if(before){
60083                 tbody.insertBefore(rows[0], before);
60084             }else{
60085                 tbody.appendChild(rows[0]);
60086             }
60087         }
60088         b.innerHTML = "";
60089         b = null;
60090     },
60091
60092     deleteRows : function(dm, firstRow, lastRow){
60093         if(dm.getRowCount()<1){
60094             this.fireEvent("beforerefresh", this);
60095             this.mainBody.update("");
60096             this.lockedBody.update("");
60097             this.fireEvent("refresh", this);
60098         }else{
60099             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
60100             var bt = this.getBodyTable();
60101             var tbody = bt.firstChild;
60102             var rows = bt.rows;
60103             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
60104                 tbody.removeChild(rows[firstRow]);
60105             }
60106             this.stripeRows(firstRow);
60107             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
60108         }
60109     },
60110
60111     updateRows : function(dataSource, firstRow, lastRow){
60112         var s = this.getScrollState();
60113         this.refresh();
60114         this.restoreScroll(s);
60115     },
60116
60117     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
60118         if(!noRefresh){
60119            this.refresh();
60120         }
60121         this.updateHeaderSortState();
60122     },
60123
60124     getScrollState : function(){
60125         
60126         var sb = this.scroller.dom;
60127         return {left: sb.scrollLeft, top: sb.scrollTop};
60128     },
60129
60130     stripeRows : function(startRow){
60131         if(!this.grid.stripeRows || this.ds.getCount() < 1){
60132             return;
60133         }
60134         startRow = startRow || 0;
60135         var rows = this.getBodyTable().rows;
60136         var lrows = this.getLockedTable().rows;
60137         var cls = ' x-grid-row-alt ';
60138         for(var i = startRow, len = rows.length; i < len; i++){
60139             var row = rows[i], lrow = lrows[i];
60140             var isAlt = ((i+1) % 2 == 0);
60141             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
60142             if(isAlt == hasAlt){
60143                 continue;
60144             }
60145             if(isAlt){
60146                 row.className += " x-grid-row-alt";
60147             }else{
60148                 row.className = row.className.replace("x-grid-row-alt", "");
60149             }
60150             if(lrow){
60151                 lrow.className = row.className;
60152             }
60153         }
60154     },
60155
60156     restoreScroll : function(state){
60157         //Roo.log('GridView.restoreScroll');
60158         var sb = this.scroller.dom;
60159         sb.scrollLeft = state.left;
60160         sb.scrollTop = state.top;
60161         this.syncScroll();
60162     },
60163
60164     syncScroll : function(){
60165         //Roo.log('GridView.syncScroll');
60166         var sb = this.scroller.dom;
60167         var sh = this.mainHd.dom;
60168         var bs = this.mainBody.dom;
60169         var lv = this.lockedBody.dom;
60170         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
60171         lv.scrollTop = bs.scrollTop = sb.scrollTop;
60172     },
60173
60174     handleScroll : function(e){
60175         this.syncScroll();
60176         var sb = this.scroller.dom;
60177         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
60178         e.stopEvent();
60179     },
60180
60181     handleWheel : function(e){
60182         var d = e.getWheelDelta();
60183         this.scroller.dom.scrollTop -= d*22;
60184         // set this here to prevent jumpy scrolling on large tables
60185         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
60186         e.stopEvent();
60187     },
60188
60189     renderRows : function(startRow, endRow){
60190         // pull in all the crap needed to render rows
60191         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
60192         var colCount = cm.getColumnCount();
60193
60194         if(ds.getCount() < 1){
60195             return ["", ""];
60196         }
60197
60198         // build a map for all the columns
60199         var cs = [];
60200         for(var i = 0; i < colCount; i++){
60201             var name = cm.getDataIndex(i);
60202             cs[i] = {
60203                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
60204                 renderer : cm.getRenderer(i),
60205                 id : cm.getColumnId(i),
60206                 locked : cm.isLocked(i),
60207                 has_editor : cm.isCellEditable(i)
60208             };
60209         }
60210
60211         startRow = startRow || 0;
60212         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
60213
60214         // records to render
60215         var rs = ds.getRange(startRow, endRow);
60216
60217         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
60218     },
60219
60220     // As much as I hate to duplicate code, this was branched because FireFox really hates
60221     // [].join("") on strings. The performance difference was substantial enough to
60222     // branch this function
60223     doRender : Roo.isGecko ?
60224             function(cs, rs, ds, startRow, colCount, stripe){
60225                 var ts = this.templates, ct = ts.cell, rt = ts.row;
60226                 // buffers
60227                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
60228                 
60229                 var hasListener = this.grid.hasListener('rowclass');
60230                 var rowcfg = {};
60231                 for(var j = 0, len = rs.length; j < len; j++){
60232                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
60233                     for(var i = 0; i < colCount; i++){
60234                         c = cs[i];
60235                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
60236                         p.id = c.id;
60237                         p.css = p.attr = "";
60238                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
60239                         if(p.value == undefined || p.value === "") {
60240                             p.value = "&#160;";
60241                         }
60242                         if(c.has_editor){
60243                             p.css += ' x-grid-editable-cell';
60244                         }
60245                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
60246                             p.css +=  ' x-grid-dirty-cell';
60247                         }
60248                         var markup = ct.apply(p);
60249                         if(!c.locked){
60250                             cb+= markup;
60251                         }else{
60252                             lcb+= markup;
60253                         }
60254                     }
60255                     var alt = [];
60256                     if(stripe && ((rowIndex+1) % 2 == 0)){
60257                         alt.push("x-grid-row-alt")
60258                     }
60259                     if(r.dirty){
60260                         alt.push(  " x-grid-dirty-row");
60261                     }
60262                     rp.cells = lcb;
60263                     if(this.getRowClass){
60264                         alt.push(this.getRowClass(r, rowIndex));
60265                     }
60266                     if (hasListener) {
60267                         rowcfg = {
60268                              
60269                             record: r,
60270                             rowIndex : rowIndex,
60271                             rowClass : ''
60272                         };
60273                         this.grid.fireEvent('rowclass', this, rowcfg);
60274                         alt.push(rowcfg.rowClass);
60275                     }
60276                     rp.alt = alt.join(" ");
60277                     lbuf+= rt.apply(rp);
60278                     rp.cells = cb;
60279                     buf+=  rt.apply(rp);
60280                 }
60281                 return [lbuf, buf];
60282             } :
60283             function(cs, rs, ds, startRow, colCount, stripe){
60284                 var ts = this.templates, ct = ts.cell, rt = ts.row;
60285                 // buffers
60286                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
60287                 var hasListener = this.grid.hasListener('rowclass');
60288  
60289                 var rowcfg = {};
60290                 for(var j = 0, len = rs.length; j < len; j++){
60291                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
60292                     for(var i = 0; i < colCount; i++){
60293                         c = cs[i];
60294                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
60295                         p.id = c.id;
60296                         p.css = p.attr = "";
60297                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
60298                         if(p.value == undefined || p.value === "") {
60299                             p.value = "&#160;";
60300                         }
60301                         //Roo.log(c);
60302                          if(c.has_editor){
60303                             p.css += ' x-grid-editable-cell';
60304                         }
60305                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
60306                             p.css += ' x-grid-dirty-cell' 
60307                         }
60308                         
60309                         var markup = ct.apply(p);
60310                         if(!c.locked){
60311                             cb[cb.length] = markup;
60312                         }else{
60313                             lcb[lcb.length] = markup;
60314                         }
60315                     }
60316                     var alt = [];
60317                     if(stripe && ((rowIndex+1) % 2 == 0)){
60318                         alt.push( "x-grid-row-alt");
60319                     }
60320                     if(r.dirty){
60321                         alt.push(" x-grid-dirty-row");
60322                     }
60323                     rp.cells = lcb;
60324                     if(this.getRowClass){
60325                         alt.push( this.getRowClass(r, rowIndex));
60326                     }
60327                     if (hasListener) {
60328                         rowcfg = {
60329                              
60330                             record: r,
60331                             rowIndex : rowIndex,
60332                             rowClass : ''
60333                         };
60334                         this.grid.fireEvent('rowclass', this, rowcfg);
60335                         alt.push(rowcfg.rowClass);
60336                     }
60337                     
60338                     rp.alt = alt.join(" ");
60339                     rp.cells = lcb.join("");
60340                     lbuf[lbuf.length] = rt.apply(rp);
60341                     rp.cells = cb.join("");
60342                     buf[buf.length] =  rt.apply(rp);
60343                 }
60344                 return [lbuf.join(""), buf.join("")];
60345             },
60346
60347     renderBody : function(){
60348         var markup = this.renderRows();
60349         var bt = this.templates.body;
60350         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
60351     },
60352
60353     /**
60354      * Refreshes the grid
60355      * @param {Boolean} headersToo
60356      */
60357     refresh : function(headersToo){
60358         this.fireEvent("beforerefresh", this);
60359         this.grid.stopEditing();
60360         var result = this.renderBody();
60361         this.lockedBody.update(result[0]);
60362         this.mainBody.update(result[1]);
60363         if(headersToo === true){
60364             this.updateHeaders();
60365             this.updateColumns();
60366             this.updateSplitters();
60367             this.updateHeaderSortState();
60368         }
60369         this.syncRowHeights();
60370         this.layout();
60371         this.fireEvent("refresh", this);
60372     },
60373
60374     handleColumnMove : function(cm, oldIndex, newIndex){
60375         this.indexMap = null;
60376         var s = this.getScrollState();
60377         this.refresh(true);
60378         this.restoreScroll(s);
60379         this.afterMove(newIndex);
60380     },
60381
60382     afterMove : function(colIndex){
60383         if(this.enableMoveAnim && Roo.enableFx){
60384             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
60385         }
60386         // if multisort - fix sortOrder, and reload..
60387         if (this.grid.dataSource.multiSort) {
60388             // the we can call sort again..
60389             var dm = this.grid.dataSource;
60390             var cm = this.grid.colModel;
60391             var so = [];
60392             for(var i = 0; i < cm.config.length; i++ ) {
60393                 
60394                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
60395                     continue; // dont' bother, it's not in sort list or being set.
60396                 }
60397                 
60398                 so.push(cm.config[i].dataIndex);
60399             };
60400             dm.sortOrder = so;
60401             dm.load(dm.lastOptions);
60402             
60403             
60404         }
60405         
60406     },
60407
60408     updateCell : function(dm, rowIndex, dataIndex){
60409         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
60410         if(typeof colIndex == "undefined"){ // not present in grid
60411             return;
60412         }
60413         var cm = this.grid.colModel;
60414         var cell = this.getCell(rowIndex, colIndex);
60415         var cellText = this.getCellText(rowIndex, colIndex);
60416
60417         var p = {
60418             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
60419             id : cm.getColumnId(colIndex),
60420             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
60421         };
60422         var renderer = cm.getRenderer(colIndex);
60423         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
60424         if(typeof val == "undefined" || val === "") {
60425             val = "&#160;";
60426         }
60427         cellText.innerHTML = val;
60428         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
60429         this.syncRowHeights(rowIndex, rowIndex);
60430     },
60431
60432     calcColumnWidth : function(colIndex, maxRowsToMeasure){
60433         var maxWidth = 0;
60434         if(this.grid.autoSizeHeaders){
60435             var h = this.getHeaderCellMeasure(colIndex);
60436             maxWidth = Math.max(maxWidth, h.scrollWidth);
60437         }
60438         var tb, index;
60439         if(this.cm.isLocked(colIndex)){
60440             tb = this.getLockedTable();
60441             index = colIndex;
60442         }else{
60443             tb = this.getBodyTable();
60444             index = colIndex - this.cm.getLockedCount();
60445         }
60446         if(tb && tb.rows){
60447             var rows = tb.rows;
60448             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
60449             for(var i = 0; i < stopIndex; i++){
60450                 var cell = rows[i].childNodes[index].firstChild;
60451                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
60452             }
60453         }
60454         return maxWidth + /*margin for error in IE*/ 5;
60455     },
60456     /**
60457      * Autofit a column to its content.
60458      * @param {Number} colIndex
60459      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
60460      */
60461      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
60462          if(this.cm.isHidden(colIndex)){
60463              return; // can't calc a hidden column
60464          }
60465         if(forceMinSize){
60466             var cid = this.cm.getColumnId(colIndex);
60467             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
60468            if(this.grid.autoSizeHeaders){
60469                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
60470            }
60471         }
60472         var newWidth = this.calcColumnWidth(colIndex);
60473         this.cm.setColumnWidth(colIndex,
60474             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
60475         if(!suppressEvent){
60476             this.grid.fireEvent("columnresize", colIndex, newWidth);
60477         }
60478     },
60479
60480     /**
60481      * Autofits all columns to their content and then expands to fit any extra space in the grid
60482      */
60483      autoSizeColumns : function(){
60484         var cm = this.grid.colModel;
60485         var colCount = cm.getColumnCount();
60486         for(var i = 0; i < colCount; i++){
60487             this.autoSizeColumn(i, true, true);
60488         }
60489         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
60490             this.fitColumns();
60491         }else{
60492             this.updateColumns();
60493             this.layout();
60494         }
60495     },
60496
60497     /**
60498      * Autofits all columns to the grid's width proportionate with their current size
60499      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
60500      */
60501     fitColumns : function(reserveScrollSpace){
60502         var cm = this.grid.colModel;
60503         var colCount = cm.getColumnCount();
60504         var cols = [];
60505         var width = 0;
60506         var i, w;
60507         for (i = 0; i < colCount; i++){
60508             if(!cm.isHidden(i) && !cm.isFixed(i)){
60509                 w = cm.getColumnWidth(i);
60510                 cols.push(i);
60511                 cols.push(w);
60512                 width += w;
60513             }
60514         }
60515         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
60516         if(reserveScrollSpace){
60517             avail -= 17;
60518         }
60519         var frac = (avail - cm.getTotalWidth())/width;
60520         while (cols.length){
60521             w = cols.pop();
60522             i = cols.pop();
60523             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
60524         }
60525         this.updateColumns();
60526         this.layout();
60527     },
60528
60529     onRowSelect : function(rowIndex){
60530         var row = this.getRowComposite(rowIndex);
60531         row.addClass("x-grid-row-selected");
60532     },
60533
60534     onRowDeselect : function(rowIndex){
60535         var row = this.getRowComposite(rowIndex);
60536         row.removeClass("x-grid-row-selected");
60537     },
60538
60539     onCellSelect : function(row, col){
60540         var cell = this.getCell(row, col);
60541         if(cell){
60542             Roo.fly(cell).addClass("x-grid-cell-selected");
60543         }
60544     },
60545
60546     onCellDeselect : function(row, col){
60547         var cell = this.getCell(row, col);
60548         if(cell){
60549             Roo.fly(cell).removeClass("x-grid-cell-selected");
60550         }
60551     },
60552
60553     updateHeaderSortState : function(){
60554         
60555         // sort state can be single { field: xxx, direction : yyy}
60556         // or   { xxx=>ASC , yyy : DESC ..... }
60557         
60558         var mstate = {};
60559         if (!this.ds.multiSort) { 
60560             var state = this.ds.getSortState();
60561             if(!state){
60562                 return;
60563             }
60564             mstate[state.field] = state.direction;
60565             // FIXME... - this is not used here.. but might be elsewhere..
60566             this.sortState = state;
60567             
60568         } else {
60569             mstate = this.ds.sortToggle;
60570         }
60571         //remove existing sort classes..
60572         
60573         var sc = this.sortClasses;
60574         var hds = this.el.select(this.headerSelector).removeClass(sc);
60575         
60576         for(var f in mstate) {
60577         
60578             var sortColumn = this.cm.findColumnIndex(f);
60579             
60580             if(sortColumn != -1){
60581                 var sortDir = mstate[f];        
60582                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
60583             }
60584         }
60585         
60586          
60587         
60588     },
60589
60590
60591     handleHeaderClick : function(g, index,e){
60592         
60593         Roo.log("header click");
60594         
60595         if (Roo.isTouch) {
60596             // touch events on header are handled by context
60597             this.handleHdCtx(g,index,e);
60598             return;
60599         }
60600         
60601         
60602         if(this.headersDisabled){
60603             return;
60604         }
60605         var dm = g.dataSource, cm = g.colModel;
60606         if(!cm.isSortable(index)){
60607             return;
60608         }
60609         g.stopEditing();
60610         
60611         if (dm.multiSort) {
60612             // update the sortOrder
60613             var so = [];
60614             for(var i = 0; i < cm.config.length; i++ ) {
60615                 
60616                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
60617                     continue; // dont' bother, it's not in sort list or being set.
60618                 }
60619                 
60620                 so.push(cm.config[i].dataIndex);
60621             };
60622             dm.sortOrder = so;
60623         }
60624         
60625         
60626         dm.sort(cm.getDataIndex(index));
60627     },
60628
60629
60630     destroy : function(){
60631         if(this.colMenu){
60632             this.colMenu.removeAll();
60633             Roo.menu.MenuMgr.unregister(this.colMenu);
60634             this.colMenu.getEl().remove();
60635             delete this.colMenu;
60636         }
60637         if(this.hmenu){
60638             this.hmenu.removeAll();
60639             Roo.menu.MenuMgr.unregister(this.hmenu);
60640             this.hmenu.getEl().remove();
60641             delete this.hmenu;
60642         }
60643         if(this.grid.enableColumnMove){
60644             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60645             if(dds){
60646                 for(var dd in dds){
60647                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
60648                         var elid = dds[dd].dragElId;
60649                         dds[dd].unreg();
60650                         Roo.get(elid).remove();
60651                     } else if(dds[dd].config.isTarget){
60652                         dds[dd].proxyTop.remove();
60653                         dds[dd].proxyBottom.remove();
60654                         dds[dd].unreg();
60655                     }
60656                     if(Roo.dd.DDM.locationCache[dd]){
60657                         delete Roo.dd.DDM.locationCache[dd];
60658                     }
60659                 }
60660                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60661             }
60662         }
60663         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60664         this.bind(null, null);
60665         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60666     },
60667
60668     handleLockChange : function(){
60669         this.refresh(true);
60670     },
60671
60672     onDenyColumnLock : function(){
60673
60674     },
60675
60676     onDenyColumnHide : function(){
60677
60678     },
60679
60680     handleHdMenuClick : function(item){
60681         var index = this.hdCtxIndex;
60682         var cm = this.cm, ds = this.ds;
60683         switch(item.id){
60684             case "asc":
60685                 ds.sort(cm.getDataIndex(index), "ASC");
60686                 break;
60687             case "desc":
60688                 ds.sort(cm.getDataIndex(index), "DESC");
60689                 break;
60690             case "lock":
60691                 var lc = cm.getLockedCount();
60692                 if(cm.getColumnCount(true) <= lc+1){
60693                     this.onDenyColumnLock();
60694                     return;
60695                 }
60696                 if(lc != index){
60697                     cm.setLocked(index, true, true);
60698                     cm.moveColumn(index, lc);
60699                     this.grid.fireEvent("columnmove", index, lc);
60700                 }else{
60701                     cm.setLocked(index, true);
60702                 }
60703             break;
60704             case "unlock":
60705                 var lc = cm.getLockedCount();
60706                 if((lc-1) != index){
60707                     cm.setLocked(index, false, true);
60708                     cm.moveColumn(index, lc-1);
60709                     this.grid.fireEvent("columnmove", index, lc-1);
60710                 }else{
60711                     cm.setLocked(index, false);
60712                 }
60713             break;
60714             case 'wider': // used to expand cols on touch..
60715             case 'narrow':
60716                 var cw = cm.getColumnWidth(index);
60717                 cw += (item.id == 'wider' ? 1 : -1) * 50;
60718                 cw = Math.max(0, cw);
60719                 cw = Math.min(cw,4000);
60720                 cm.setColumnWidth(index, cw);
60721                 break;
60722                 
60723             default:
60724                 index = cm.getIndexById(item.id.substr(4));
60725                 if(index != -1){
60726                     if(item.checked && cm.getColumnCount(true) <= 1){
60727                         this.onDenyColumnHide();
60728                         return false;
60729                     }
60730                     cm.setHidden(index, item.checked);
60731                 }
60732         }
60733         return true;
60734     },
60735
60736     beforeColMenuShow : function(){
60737         var cm = this.cm,  colCount = cm.getColumnCount();
60738         this.colMenu.removeAll();
60739         for(var i = 0; i < colCount; i++){
60740             this.colMenu.add(new Roo.menu.CheckItem({
60741                 id: "col-"+cm.getColumnId(i),
60742                 text: cm.getColumnHeader(i),
60743                 checked: !cm.isHidden(i),
60744                 hideOnClick:false
60745             }));
60746         }
60747     },
60748
60749     handleHdCtx : function(g, index, e){
60750         e.stopEvent();
60751         var hd = this.getHeaderCell(index);
60752         this.hdCtxIndex = index;
60753         var ms = this.hmenu.items, cm = this.cm;
60754         ms.get("asc").setDisabled(!cm.isSortable(index));
60755         ms.get("desc").setDisabled(!cm.isSortable(index));
60756         if(this.grid.enableColLock !== false){
60757             ms.get("lock").setDisabled(cm.isLocked(index));
60758             ms.get("unlock").setDisabled(!cm.isLocked(index));
60759         }
60760         this.hmenu.show(hd, "tl-bl");
60761     },
60762
60763     handleHdOver : function(e){
60764         var hd = this.findHeaderCell(e.getTarget());
60765         if(hd && !this.headersDisabled){
60766             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60767                this.fly(hd).addClass("x-grid-hd-over");
60768             }
60769         }
60770     },
60771
60772     handleHdOut : function(e){
60773         var hd = this.findHeaderCell(e.getTarget());
60774         if(hd){
60775             this.fly(hd).removeClass("x-grid-hd-over");
60776         }
60777     },
60778
60779     handleSplitDblClick : function(e, t){
60780         var i = this.getCellIndex(t);
60781         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60782             this.autoSizeColumn(i, true);
60783             this.layout();
60784         }
60785     },
60786
60787     render : function(){
60788
60789         var cm = this.cm;
60790         var colCount = cm.getColumnCount();
60791
60792         if(this.grid.monitorWindowResize === true){
60793             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60794         }
60795         var header = this.renderHeaders();
60796         var body = this.templates.body.apply({rows:""});
60797         var html = this.templates.master.apply({
60798             lockedBody: body,
60799             body: body,
60800             lockedHeader: header[0],
60801             header: header[1]
60802         });
60803
60804         //this.updateColumns();
60805
60806         this.grid.getGridEl().dom.innerHTML = html;
60807
60808         this.initElements();
60809         
60810         // a kludge to fix the random scolling effect in webkit
60811         this.el.on("scroll", function() {
60812             this.el.dom.scrollTop=0; // hopefully not recursive..
60813         },this);
60814
60815         this.scroller.on("scroll", this.handleScroll, this);
60816         this.lockedBody.on("mousewheel", this.handleWheel, this);
60817         this.mainBody.on("mousewheel", this.handleWheel, this);
60818
60819         this.mainHd.on("mouseover", this.handleHdOver, this);
60820         this.mainHd.on("mouseout", this.handleHdOut, this);
60821         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60822                 {delegate: "."+this.splitClass});
60823
60824         this.lockedHd.on("mouseover", this.handleHdOver, this);
60825         this.lockedHd.on("mouseout", this.handleHdOut, this);
60826         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60827                 {delegate: "."+this.splitClass});
60828
60829         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60830             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60831         }
60832
60833         this.updateSplitters();
60834
60835         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60836             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60837             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60838         }
60839
60840         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60841             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60842             this.hmenu.add(
60843                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60844                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60845             );
60846             if(this.grid.enableColLock !== false){
60847                 this.hmenu.add('-',
60848                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60849                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60850                 );
60851             }
60852             if (Roo.isTouch) {
60853                  this.hmenu.add('-',
60854                     {id:"wider", text: this.columnsWiderText},
60855                     {id:"narrow", text: this.columnsNarrowText }
60856                 );
60857                 
60858                  
60859             }
60860             
60861             if(this.grid.enableColumnHide !== false){
60862
60863                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60864                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60865                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60866
60867                 this.hmenu.add('-',
60868                     {id:"columns", text: this.columnsText, menu: this.colMenu}
60869                 );
60870             }
60871             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60872
60873             this.grid.on("headercontextmenu", this.handleHdCtx, this);
60874         }
60875
60876         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60877             this.dd = new Roo.grid.GridDragZone(this.grid, {
60878                 ddGroup : this.grid.ddGroup || 'GridDD'
60879             });
60880             
60881         }
60882
60883         /*
60884         for(var i = 0; i < colCount; i++){
60885             if(cm.isHidden(i)){
60886                 this.hideColumn(i);
60887             }
60888             if(cm.config[i].align){
60889                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60890                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60891             }
60892         }*/
60893         
60894         this.updateHeaderSortState();
60895
60896         this.beforeInitialResize();
60897         this.layout(true);
60898
60899         // two part rendering gives faster view to the user
60900         this.renderPhase2.defer(1, this);
60901     },
60902
60903     renderPhase2 : function(){
60904         // render the rows now
60905         this.refresh();
60906         if(this.grid.autoSizeColumns){
60907             this.autoSizeColumns();
60908         }
60909     },
60910
60911     beforeInitialResize : function(){
60912
60913     },
60914
60915     onColumnSplitterMoved : function(i, w){
60916         this.userResized = true;
60917         var cm = this.grid.colModel;
60918         cm.setColumnWidth(i, w, true);
60919         var cid = cm.getColumnId(i);
60920         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60921         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60922         this.updateSplitters();
60923         this.layout();
60924         this.grid.fireEvent("columnresize", i, w);
60925     },
60926
60927     syncRowHeights : function(startIndex, endIndex){
60928         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60929             startIndex = startIndex || 0;
60930             var mrows = this.getBodyTable().rows;
60931             var lrows = this.getLockedTable().rows;
60932             var len = mrows.length-1;
60933             endIndex = Math.min(endIndex || len, len);
60934             for(var i = startIndex; i <= endIndex; i++){
60935                 var m = mrows[i], l = lrows[i];
60936                 var h = Math.max(m.offsetHeight, l.offsetHeight);
60937                 m.style.height = l.style.height = h + "px";
60938             }
60939         }
60940     },
60941
60942     layout : function(initialRender, is2ndPass){
60943         var g = this.grid;
60944         var auto = g.autoHeight;
60945         var scrollOffset = 16;
60946         var c = g.getGridEl(), cm = this.cm,
60947                 expandCol = g.autoExpandColumn,
60948                 gv = this;
60949         //c.beginMeasure();
60950
60951         if(!c.dom.offsetWidth){ // display:none?
60952             if(initialRender){
60953                 this.lockedWrap.show();
60954                 this.mainWrap.show();
60955             }
60956             return;
60957         }
60958
60959         var hasLock = this.cm.isLocked(0);
60960
60961         var tbh = this.headerPanel.getHeight();
60962         var bbh = this.footerPanel.getHeight();
60963
60964         if(auto){
60965             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60966             var newHeight = ch + c.getBorderWidth("tb");
60967             if(g.maxHeight){
60968                 newHeight = Math.min(g.maxHeight, newHeight);
60969             }
60970             c.setHeight(newHeight);
60971         }
60972
60973         if(g.autoWidth){
60974             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60975         }
60976
60977         var s = this.scroller;
60978
60979         var csize = c.getSize(true);
60980
60981         this.el.setSize(csize.width, csize.height);
60982
60983         this.headerPanel.setWidth(csize.width);
60984         this.footerPanel.setWidth(csize.width);
60985
60986         var hdHeight = this.mainHd.getHeight();
60987         var vw = csize.width;
60988         var vh = csize.height - (tbh + bbh);
60989
60990         s.setSize(vw, vh);
60991
60992         var bt = this.getBodyTable();
60993         
60994         if(cm.getLockedCount() == cm.config.length){
60995             bt = this.getLockedTable();
60996         }
60997         
60998         var ltWidth = hasLock ?
60999                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
61000
61001         var scrollHeight = bt.offsetHeight;
61002         var scrollWidth = ltWidth + bt.offsetWidth;
61003         var vscroll = false, hscroll = false;
61004
61005         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
61006
61007         var lw = this.lockedWrap, mw = this.mainWrap;
61008         var lb = this.lockedBody, mb = this.mainBody;
61009
61010         setTimeout(function(){
61011             var t = s.dom.offsetTop;
61012             var w = s.dom.clientWidth,
61013                 h = s.dom.clientHeight;
61014
61015             lw.setTop(t);
61016             lw.setSize(ltWidth, h);
61017
61018             mw.setLeftTop(ltWidth, t);
61019             mw.setSize(w-ltWidth, h);
61020
61021             lb.setHeight(h-hdHeight);
61022             mb.setHeight(h-hdHeight);
61023
61024             if(is2ndPass !== true && !gv.userResized && expandCol){
61025                 // high speed resize without full column calculation
61026                 
61027                 var ci = cm.getIndexById(expandCol);
61028                 if (ci < 0) {
61029                     ci = cm.findColumnIndex(expandCol);
61030                 }
61031                 ci = Math.max(0, ci); // make sure it's got at least the first col.
61032                 var expandId = cm.getColumnId(ci);
61033                 var  tw = cm.getTotalWidth(false);
61034                 var currentWidth = cm.getColumnWidth(ci);
61035                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
61036                 if(currentWidth != cw){
61037                     cm.setColumnWidth(ci, cw, true);
61038                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
61039                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
61040                     gv.updateSplitters();
61041                     gv.layout(false, true);
61042                 }
61043             }
61044
61045             if(initialRender){
61046                 lw.show();
61047                 mw.show();
61048             }
61049             //c.endMeasure();
61050         }, 10);
61051     },
61052
61053     onWindowResize : function(){
61054         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
61055             return;
61056         }
61057         this.layout();
61058     },
61059
61060     appendFooter : function(parentEl){
61061         return null;
61062     },
61063
61064     sortAscText : "Sort Ascending",
61065     sortDescText : "Sort Descending",
61066     lockText : "Lock Column",
61067     unlockText : "Unlock Column",
61068     columnsText : "Columns",
61069  
61070     columnsWiderText : "Wider",
61071     columnsNarrowText : "Thinner"
61072 });
61073
61074
61075 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
61076     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
61077     this.proxy.el.addClass('x-grid3-col-dd');
61078 };
61079
61080 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
61081     handleMouseDown : function(e){
61082
61083     },
61084
61085     callHandleMouseDown : function(e){
61086         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
61087     }
61088 });
61089 /*
61090  * Based on:
61091  * Ext JS Library 1.1.1
61092  * Copyright(c) 2006-2007, Ext JS, LLC.
61093  *
61094  * Originally Released Under LGPL - original licence link has changed is not relivant.
61095  *
61096  * Fork - LGPL
61097  * <script type="text/javascript">
61098  */
61099  
61100 // private
61101 // This is a support class used internally by the Grid components
61102 Roo.grid.SplitDragZone = function(grid, hd, hd2){
61103     this.grid = grid;
61104     this.view = grid.getView();
61105     this.proxy = this.view.resizeProxy;
61106     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
61107         "gridSplitters" + this.grid.getGridEl().id, {
61108         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
61109     });
61110     this.setHandleElId(Roo.id(hd));
61111     this.setOuterHandleElId(Roo.id(hd2));
61112     this.scroll = false;
61113 };
61114 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
61115     fly: Roo.Element.fly,
61116
61117     b4StartDrag : function(x, y){
61118         this.view.headersDisabled = true;
61119         this.proxy.setHeight(this.view.mainWrap.getHeight());
61120         var w = this.cm.getColumnWidth(this.cellIndex);
61121         var minw = Math.max(w-this.grid.minColumnWidth, 0);
61122         this.resetConstraints();
61123         this.setXConstraint(minw, 1000);
61124         this.setYConstraint(0, 0);
61125         this.minX = x - minw;
61126         this.maxX = x + 1000;
61127         this.startPos = x;
61128         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
61129     },
61130
61131
61132     handleMouseDown : function(e){
61133         ev = Roo.EventObject.setEvent(e);
61134         var t = this.fly(ev.getTarget());
61135         if(t.hasClass("x-grid-split")){
61136             this.cellIndex = this.view.getCellIndex(t.dom);
61137             this.split = t.dom;
61138             this.cm = this.grid.colModel;
61139             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
61140                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
61141             }
61142         }
61143     },
61144
61145     endDrag : function(e){
61146         this.view.headersDisabled = false;
61147         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
61148         var diff = endX - this.startPos;
61149         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
61150     },
61151
61152     autoOffset : function(){
61153         this.setDelta(0,0);
61154     }
61155 });/*
61156  * Based on:
61157  * Ext JS Library 1.1.1
61158  * Copyright(c) 2006-2007, Ext JS, LLC.
61159  *
61160  * Originally Released Under LGPL - original licence link has changed is not relivant.
61161  *
61162  * Fork - LGPL
61163  * <script type="text/javascript">
61164  */
61165  
61166 // private
61167 // This is a support class used internally by the Grid components
61168 Roo.grid.GridDragZone = function(grid, config){
61169     this.view = grid.getView();
61170     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
61171     if(this.view.lockedBody){
61172         this.setHandleElId(Roo.id(this.view.mainBody.dom));
61173         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
61174     }
61175     this.scroll = false;
61176     this.grid = grid;
61177     this.ddel = document.createElement('div');
61178     this.ddel.className = 'x-grid-dd-wrap';
61179 };
61180
61181 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
61182     ddGroup : "GridDD",
61183
61184     getDragData : function(e){
61185         var t = Roo.lib.Event.getTarget(e);
61186         var rowIndex = this.view.findRowIndex(t);
61187         var sm = this.grid.selModel;
61188             
61189         //Roo.log(rowIndex);
61190         
61191         if (sm.getSelectedCell) {
61192             // cell selection..
61193             if (!sm.getSelectedCell()) {
61194                 return false;
61195             }
61196             if (rowIndex != sm.getSelectedCell()[0]) {
61197                 return false;
61198             }
61199         
61200         }
61201         
61202         if(rowIndex !== false){
61203             
61204             // if editorgrid.. 
61205             
61206             
61207             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
61208                
61209             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
61210               //  
61211             //}
61212             if (e.hasModifier()){
61213                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
61214             }
61215             
61216             Roo.log("getDragData");
61217             
61218             return {
61219                 grid: this.grid,
61220                 ddel: this.ddel,
61221                 rowIndex: rowIndex,
61222                 selections:sm.getSelections ? sm.getSelections() : (
61223                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
61224                 )
61225             };
61226         }
61227         return false;
61228     },
61229
61230     onInitDrag : function(e){
61231         var data = this.dragData;
61232         this.ddel.innerHTML = this.grid.getDragDropText();
61233         this.proxy.update(this.ddel);
61234         // fire start drag?
61235     },
61236
61237     afterRepair : function(){
61238         this.dragging = false;
61239     },
61240
61241     getRepairXY : function(e, data){
61242         return false;
61243     },
61244
61245     onEndDrag : function(data, e){
61246         // fire end drag?
61247     },
61248
61249     onValidDrop : function(dd, e, id){
61250         // fire drag drop?
61251         this.hideProxy();
61252     },
61253
61254     beforeInvalidDrop : function(e, id){
61255
61256     }
61257 });/*
61258  * Based on:
61259  * Ext JS Library 1.1.1
61260  * Copyright(c) 2006-2007, Ext JS, LLC.
61261  *
61262  * Originally Released Under LGPL - original licence link has changed is not relivant.
61263  *
61264  * Fork - LGPL
61265  * <script type="text/javascript">
61266  */
61267  
61268
61269 /**
61270  * @class Roo.grid.ColumnModel
61271  * @extends Roo.util.Observable
61272  * This is the default implementation of a ColumnModel used by the Grid. It defines
61273  * the columns in the grid.
61274  * <br>Usage:<br>
61275  <pre><code>
61276  var colModel = new Roo.grid.ColumnModel([
61277         {header: "Ticker", width: 60, sortable: true, locked: true},
61278         {header: "Company Name", width: 150, sortable: true},
61279         {header: "Market Cap.", width: 100, sortable: true},
61280         {header: "$ Sales", width: 100, sortable: true, renderer: money},
61281         {header: "Employees", width: 100, sortable: true, resizable: false}
61282  ]);
61283  </code></pre>
61284  * <p>
61285  
61286  * The config options listed for this class are options which may appear in each
61287  * individual column definition.
61288  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
61289  * @constructor
61290  * @param {Object} config An Array of column config objects. See this class's
61291  * config objects for details.
61292 */
61293 Roo.grid.ColumnModel = function(config){
61294         /**
61295      * The config passed into the constructor
61296      */
61297     this.config = config;
61298     this.lookup = {};
61299
61300     // if no id, create one
61301     // if the column does not have a dataIndex mapping,
61302     // map it to the order it is in the config
61303     for(var i = 0, len = config.length; i < len; i++){
61304         var c = config[i];
61305         if(typeof c.dataIndex == "undefined"){
61306             c.dataIndex = i;
61307         }
61308         if(typeof c.renderer == "string"){
61309             c.renderer = Roo.util.Format[c.renderer];
61310         }
61311         if(typeof c.id == "undefined"){
61312             c.id = Roo.id();
61313         }
61314         if(c.editor && c.editor.xtype){
61315             c.editor  = Roo.factory(c.editor, Roo.grid);
61316         }
61317         if(c.editor && c.editor.isFormField){
61318             c.editor = new Roo.grid.GridEditor(c.editor);
61319         }
61320         this.lookup[c.id] = c;
61321     }
61322
61323     /**
61324      * The width of columns which have no width specified (defaults to 100)
61325      * @type Number
61326      */
61327     this.defaultWidth = 100;
61328
61329     /**
61330      * Default sortable of columns which have no sortable specified (defaults to false)
61331      * @type Boolean
61332      */
61333     this.defaultSortable = false;
61334
61335     this.addEvents({
61336         /**
61337              * @event widthchange
61338              * Fires when the width of a column changes.
61339              * @param {ColumnModel} this
61340              * @param {Number} columnIndex The column index
61341              * @param {Number} newWidth The new width
61342              */
61343             "widthchange": true,
61344         /**
61345              * @event headerchange
61346              * Fires when the text of a header changes.
61347              * @param {ColumnModel} this
61348              * @param {Number} columnIndex The column index
61349              * @param {Number} newText The new header text
61350              */
61351             "headerchange": true,
61352         /**
61353              * @event hiddenchange
61354              * Fires when a column is hidden or "unhidden".
61355              * @param {ColumnModel} this
61356              * @param {Number} columnIndex The column index
61357              * @param {Boolean} hidden true if hidden, false otherwise
61358              */
61359             "hiddenchange": true,
61360             /**
61361          * @event columnmoved
61362          * Fires when a column is moved.
61363          * @param {ColumnModel} this
61364          * @param {Number} oldIndex
61365          * @param {Number} newIndex
61366          */
61367         "columnmoved" : true,
61368         /**
61369          * @event columlockchange
61370          * Fires when a column's locked state is changed
61371          * @param {ColumnModel} this
61372          * @param {Number} colIndex
61373          * @param {Boolean} locked true if locked
61374          */
61375         "columnlockchange" : true
61376     });
61377     Roo.grid.ColumnModel.superclass.constructor.call(this);
61378 };
61379 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
61380     /**
61381      * @cfg {String} header The header text to display in the Grid view.
61382      */
61383     /**
61384      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
61385      * {@link Roo.data.Record} definition from which to draw the column's value. If not
61386      * specified, the column's index is used as an index into the Record's data Array.
61387      */
61388     /**
61389      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
61390      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
61391      */
61392     /**
61393      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
61394      * Defaults to the value of the {@link #defaultSortable} property.
61395      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
61396      */
61397     /**
61398      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
61399      */
61400     /**
61401      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
61402      */
61403     /**
61404      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
61405      */
61406     /**
61407      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
61408      */
61409     /**
61410      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
61411      * given the cell's data value. See {@link #setRenderer}. If not specified, the
61412      * default renderer uses the raw data value. If an object is returned (bootstrap only)
61413      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
61414      */
61415        /**
61416      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
61417      */
61418     /**
61419      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
61420      */
61421     /**
61422      * @cfg {String} cursor (Optional)
61423      */
61424     /**
61425      * @cfg {String} tooltip (Optional)
61426      */
61427     /**
61428      * @cfg {Number} xs (Optional)
61429      */
61430     /**
61431      * @cfg {Number} sm (Optional)
61432      */
61433     /**
61434      * @cfg {Number} md (Optional)
61435      */
61436     /**
61437      * @cfg {Number} lg (Optional)
61438      */
61439     /**
61440      * Returns the id of the column at the specified index.
61441      * @param {Number} index The column index
61442      * @return {String} the id
61443      */
61444     getColumnId : function(index){
61445         return this.config[index].id;
61446     },
61447
61448     /**
61449      * Returns the column for a specified id.
61450      * @param {String} id The column id
61451      * @return {Object} the column
61452      */
61453     getColumnById : function(id){
61454         return this.lookup[id];
61455     },
61456
61457     
61458     /**
61459      * Returns the column for a specified dataIndex.
61460      * @param {String} dataIndex The column dataIndex
61461      * @return {Object|Boolean} the column or false if not found
61462      */
61463     getColumnByDataIndex: function(dataIndex){
61464         var index = this.findColumnIndex(dataIndex);
61465         return index > -1 ? this.config[index] : false;
61466     },
61467     
61468     /**
61469      * Returns the index for a specified column id.
61470      * @param {String} id The column id
61471      * @return {Number} the index, or -1 if not found
61472      */
61473     getIndexById : function(id){
61474         for(var i = 0, len = this.config.length; i < len; i++){
61475             if(this.config[i].id == id){
61476                 return i;
61477             }
61478         }
61479         return -1;
61480     },
61481     
61482     /**
61483      * Returns the index for a specified column dataIndex.
61484      * @param {String} dataIndex The column dataIndex
61485      * @return {Number} the index, or -1 if not found
61486      */
61487     
61488     findColumnIndex : function(dataIndex){
61489         for(var i = 0, len = this.config.length; i < len; i++){
61490             if(this.config[i].dataIndex == dataIndex){
61491                 return i;
61492             }
61493         }
61494         return -1;
61495     },
61496     
61497     
61498     moveColumn : function(oldIndex, newIndex){
61499         var c = this.config[oldIndex];
61500         this.config.splice(oldIndex, 1);
61501         this.config.splice(newIndex, 0, c);
61502         this.dataMap = null;
61503         this.fireEvent("columnmoved", this, oldIndex, newIndex);
61504     },
61505
61506     isLocked : function(colIndex){
61507         return this.config[colIndex].locked === true;
61508     },
61509
61510     setLocked : function(colIndex, value, suppressEvent){
61511         if(this.isLocked(colIndex) == value){
61512             return;
61513         }
61514         this.config[colIndex].locked = value;
61515         if(!suppressEvent){
61516             this.fireEvent("columnlockchange", this, colIndex, value);
61517         }
61518     },
61519
61520     getTotalLockedWidth : function(){
61521         var totalWidth = 0;
61522         for(var i = 0; i < this.config.length; i++){
61523             if(this.isLocked(i) && !this.isHidden(i)){
61524                 this.totalWidth += this.getColumnWidth(i);
61525             }
61526         }
61527         return totalWidth;
61528     },
61529
61530     getLockedCount : function(){
61531         for(var i = 0, len = this.config.length; i < len; i++){
61532             if(!this.isLocked(i)){
61533                 return i;
61534             }
61535         }
61536         
61537         return this.config.length;
61538     },
61539
61540     /**
61541      * Returns the number of columns.
61542      * @return {Number}
61543      */
61544     getColumnCount : function(visibleOnly){
61545         if(visibleOnly === true){
61546             var c = 0;
61547             for(var i = 0, len = this.config.length; i < len; i++){
61548                 if(!this.isHidden(i)){
61549                     c++;
61550                 }
61551             }
61552             return c;
61553         }
61554         return this.config.length;
61555     },
61556
61557     /**
61558      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
61559      * @param {Function} fn
61560      * @param {Object} scope (optional)
61561      * @return {Array} result
61562      */
61563     getColumnsBy : function(fn, scope){
61564         var r = [];
61565         for(var i = 0, len = this.config.length; i < len; i++){
61566             var c = this.config[i];
61567             if(fn.call(scope||this, c, i) === true){
61568                 r[r.length] = c;
61569             }
61570         }
61571         return r;
61572     },
61573
61574     /**
61575      * Returns true if the specified column is sortable.
61576      * @param {Number} col The column index
61577      * @return {Boolean}
61578      */
61579     isSortable : function(col){
61580         if(typeof this.config[col].sortable == "undefined"){
61581             return this.defaultSortable;
61582         }
61583         return this.config[col].sortable;
61584     },
61585
61586     /**
61587      * Returns the rendering (formatting) function defined for the column.
61588      * @param {Number} col The column index.
61589      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
61590      */
61591     getRenderer : function(col){
61592         if(!this.config[col].renderer){
61593             return Roo.grid.ColumnModel.defaultRenderer;
61594         }
61595         return this.config[col].renderer;
61596     },
61597
61598     /**
61599      * Sets the rendering (formatting) function for a column.
61600      * @param {Number} col The column index
61601      * @param {Function} fn The function to use to process the cell's raw data
61602      * to return HTML markup for the grid view. The render function is called with
61603      * the following parameters:<ul>
61604      * <li>Data value.</li>
61605      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
61606      * <li>css A CSS style string to apply to the table cell.</li>
61607      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
61608      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61609      * <li>Row index</li>
61610      * <li>Column index</li>
61611      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61612      */
61613     setRenderer : function(col, fn){
61614         this.config[col].renderer = fn;
61615     },
61616
61617     /**
61618      * Returns the width for the specified column.
61619      * @param {Number} col The column index
61620      * @return {Number}
61621      */
61622     getColumnWidth : function(col){
61623         return this.config[col].width * 1 || this.defaultWidth;
61624     },
61625
61626     /**
61627      * Sets the width for a column.
61628      * @param {Number} col The column index
61629      * @param {Number} width The new width
61630      */
61631     setColumnWidth : function(col, width, suppressEvent){
61632         this.config[col].width = width;
61633         this.totalWidth = null;
61634         if(!suppressEvent){
61635              this.fireEvent("widthchange", this, col, width);
61636         }
61637     },
61638
61639     /**
61640      * Returns the total width of all columns.
61641      * @param {Boolean} includeHidden True to include hidden column widths
61642      * @return {Number}
61643      */
61644     getTotalWidth : function(includeHidden){
61645         if(!this.totalWidth){
61646             this.totalWidth = 0;
61647             for(var i = 0, len = this.config.length; i < len; i++){
61648                 if(includeHidden || !this.isHidden(i)){
61649                     this.totalWidth += this.getColumnWidth(i);
61650                 }
61651             }
61652         }
61653         return this.totalWidth;
61654     },
61655
61656     /**
61657      * Returns the header for the specified column.
61658      * @param {Number} col The column index
61659      * @return {String}
61660      */
61661     getColumnHeader : function(col){
61662         return this.config[col].header;
61663     },
61664
61665     /**
61666      * Sets the header for a column.
61667      * @param {Number} col The column index
61668      * @param {String} header The new header
61669      */
61670     setColumnHeader : function(col, header){
61671         this.config[col].header = header;
61672         this.fireEvent("headerchange", this, col, header);
61673     },
61674
61675     /**
61676      * Returns the tooltip for the specified column.
61677      * @param {Number} col The column index
61678      * @return {String}
61679      */
61680     getColumnTooltip : function(col){
61681             return this.config[col].tooltip;
61682     },
61683     /**
61684      * Sets the tooltip for a column.
61685      * @param {Number} col The column index
61686      * @param {String} tooltip The new tooltip
61687      */
61688     setColumnTooltip : function(col, tooltip){
61689             this.config[col].tooltip = tooltip;
61690     },
61691
61692     /**
61693      * Returns the dataIndex for the specified column.
61694      * @param {Number} col The column index
61695      * @return {Number}
61696      */
61697     getDataIndex : function(col){
61698         return this.config[col].dataIndex;
61699     },
61700
61701     /**
61702      * Sets the dataIndex for a column.
61703      * @param {Number} col The column index
61704      * @param {Number} dataIndex The new dataIndex
61705      */
61706     setDataIndex : function(col, dataIndex){
61707         this.config[col].dataIndex = dataIndex;
61708     },
61709
61710     
61711     
61712     /**
61713      * Returns true if the cell is editable.
61714      * @param {Number} colIndex The column index
61715      * @param {Number} rowIndex The row index - this is nto actually used..?
61716      * @return {Boolean}
61717      */
61718     isCellEditable : function(colIndex, rowIndex){
61719         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61720     },
61721
61722     /**
61723      * Returns the editor defined for the cell/column.
61724      * return false or null to disable editing.
61725      * @param {Number} colIndex The column index
61726      * @param {Number} rowIndex The row index
61727      * @return {Object}
61728      */
61729     getCellEditor : function(colIndex, rowIndex){
61730         return this.config[colIndex].editor;
61731     },
61732
61733     /**
61734      * Sets if a column is editable.
61735      * @param {Number} col The column index
61736      * @param {Boolean} editable True if the column is editable
61737      */
61738     setEditable : function(col, editable){
61739         this.config[col].editable = editable;
61740     },
61741
61742
61743     /**
61744      * Returns true if the column is hidden.
61745      * @param {Number} colIndex The column index
61746      * @return {Boolean}
61747      */
61748     isHidden : function(colIndex){
61749         return this.config[colIndex].hidden;
61750     },
61751
61752
61753     /**
61754      * Returns true if the column width cannot be changed
61755      */
61756     isFixed : function(colIndex){
61757         return this.config[colIndex].fixed;
61758     },
61759
61760     /**
61761      * Returns true if the column can be resized
61762      * @return {Boolean}
61763      */
61764     isResizable : function(colIndex){
61765         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61766     },
61767     /**
61768      * Sets if a column is hidden.
61769      * @param {Number} colIndex The column index
61770      * @param {Boolean} hidden True if the column is hidden
61771      */
61772     setHidden : function(colIndex, hidden){
61773         this.config[colIndex].hidden = hidden;
61774         this.totalWidth = null;
61775         this.fireEvent("hiddenchange", this, colIndex, hidden);
61776     },
61777
61778     /**
61779      * Sets the editor for a column.
61780      * @param {Number} col The column index
61781      * @param {Object} editor The editor object
61782      */
61783     setEditor : function(col, editor){
61784         this.config[col].editor = editor;
61785     }
61786 });
61787
61788 Roo.grid.ColumnModel.defaultRenderer = function(value){
61789         if(typeof value == "string" && value.length < 1){
61790             return "&#160;";
61791         }
61792         return value;
61793 };
61794
61795 // Alias for backwards compatibility
61796 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61797 /*
61798  * Based on:
61799  * Ext JS Library 1.1.1
61800  * Copyright(c) 2006-2007, Ext JS, LLC.
61801  *
61802  * Originally Released Under LGPL - original licence link has changed is not relivant.
61803  *
61804  * Fork - LGPL
61805  * <script type="text/javascript">
61806  */
61807
61808 /**
61809  * @class Roo.grid.AbstractSelectionModel
61810  * @extends Roo.util.Observable
61811  * Abstract base class for grid SelectionModels.  It provides the interface that should be
61812  * implemented by descendant classes.  This class should not be directly instantiated.
61813  * @constructor
61814  */
61815 Roo.grid.AbstractSelectionModel = function(){
61816     this.locked = false;
61817     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61818 };
61819
61820 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
61821     /** @ignore Called by the grid automatically. Do not call directly. */
61822     init : function(grid){
61823         this.grid = grid;
61824         this.initEvents();
61825     },
61826
61827     /**
61828      * Locks the selections.
61829      */
61830     lock : function(){
61831         this.locked = true;
61832     },
61833
61834     /**
61835      * Unlocks the selections.
61836      */
61837     unlock : function(){
61838         this.locked = false;
61839     },
61840
61841     /**
61842      * Returns true if the selections are locked.
61843      * @return {Boolean}
61844      */
61845     isLocked : function(){
61846         return this.locked;
61847     }
61848 });/*
61849  * Based on:
61850  * Ext JS Library 1.1.1
61851  * Copyright(c) 2006-2007, Ext JS, LLC.
61852  *
61853  * Originally Released Under LGPL - original licence link has changed is not relivant.
61854  *
61855  * Fork - LGPL
61856  * <script type="text/javascript">
61857  */
61858 /**
61859  * @extends Roo.grid.AbstractSelectionModel
61860  * @class Roo.grid.RowSelectionModel
61861  * The default SelectionModel used by {@link Roo.grid.Grid}.
61862  * It supports multiple selections and keyboard selection/navigation. 
61863  * @constructor
61864  * @param {Object} config
61865  */
61866 Roo.grid.RowSelectionModel = function(config){
61867     Roo.apply(this, config);
61868     this.selections = new Roo.util.MixedCollection(false, function(o){
61869         return o.id;
61870     });
61871
61872     this.last = false;
61873     this.lastActive = false;
61874
61875     this.addEvents({
61876         /**
61877              * @event selectionchange
61878              * Fires when the selection changes
61879              * @param {SelectionModel} this
61880              */
61881             "selectionchange" : true,
61882         /**
61883              * @event afterselectionchange
61884              * Fires after the selection changes (eg. by key press or clicking)
61885              * @param {SelectionModel} this
61886              */
61887             "afterselectionchange" : true,
61888         /**
61889              * @event beforerowselect
61890              * Fires when a row is selected being selected, return false to cancel.
61891              * @param {SelectionModel} this
61892              * @param {Number} rowIndex The selected index
61893              * @param {Boolean} keepExisting False if other selections will be cleared
61894              */
61895             "beforerowselect" : true,
61896         /**
61897              * @event rowselect
61898              * Fires when a row is selected.
61899              * @param {SelectionModel} this
61900              * @param {Number} rowIndex The selected index
61901              * @param {Roo.data.Record} r The record
61902              */
61903             "rowselect" : true,
61904         /**
61905              * @event rowdeselect
61906              * Fires when a row is deselected.
61907              * @param {SelectionModel} this
61908              * @param {Number} rowIndex The selected index
61909              */
61910         "rowdeselect" : true
61911     });
61912     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61913     this.locked = false;
61914 };
61915
61916 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
61917     /**
61918      * @cfg {Boolean} singleSelect
61919      * True to allow selection of only one row at a time (defaults to false)
61920      */
61921     singleSelect : false,
61922
61923     // private
61924     initEvents : function(){
61925
61926         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61927             this.grid.on("mousedown", this.handleMouseDown, this);
61928         }else{ // allow click to work like normal
61929             this.grid.on("rowclick", this.handleDragableRowClick, this);
61930         }
61931
61932         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61933             "up" : function(e){
61934                 if(!e.shiftKey){
61935                     this.selectPrevious(e.shiftKey);
61936                 }else if(this.last !== false && this.lastActive !== false){
61937                     var last = this.last;
61938                     this.selectRange(this.last,  this.lastActive-1);
61939                     this.grid.getView().focusRow(this.lastActive);
61940                     if(last !== false){
61941                         this.last = last;
61942                     }
61943                 }else{
61944                     this.selectFirstRow();
61945                 }
61946                 this.fireEvent("afterselectionchange", this);
61947             },
61948             "down" : function(e){
61949                 if(!e.shiftKey){
61950                     this.selectNext(e.shiftKey);
61951                 }else if(this.last !== false && this.lastActive !== false){
61952                     var last = this.last;
61953                     this.selectRange(this.last,  this.lastActive+1);
61954                     this.grid.getView().focusRow(this.lastActive);
61955                     if(last !== false){
61956                         this.last = last;
61957                     }
61958                 }else{
61959                     this.selectFirstRow();
61960                 }
61961                 this.fireEvent("afterselectionchange", this);
61962             },
61963             scope: this
61964         });
61965
61966         var view = this.grid.view;
61967         view.on("refresh", this.onRefresh, this);
61968         view.on("rowupdated", this.onRowUpdated, this);
61969         view.on("rowremoved", this.onRemove, this);
61970     },
61971
61972     // private
61973     onRefresh : function(){
61974         var ds = this.grid.dataSource, i, v = this.grid.view;
61975         var s = this.selections;
61976         s.each(function(r){
61977             if((i = ds.indexOfId(r.id)) != -1){
61978                 v.onRowSelect(i);
61979                 s.add(ds.getAt(i)); // updating the selection relate data
61980             }else{
61981                 s.remove(r);
61982             }
61983         });
61984     },
61985
61986     // private
61987     onRemove : function(v, index, r){
61988         this.selections.remove(r);
61989     },
61990
61991     // private
61992     onRowUpdated : function(v, index, r){
61993         if(this.isSelected(r)){
61994             v.onRowSelect(index);
61995         }
61996     },
61997
61998     /**
61999      * Select records.
62000      * @param {Array} records The records to select
62001      * @param {Boolean} keepExisting (optional) True to keep existing selections
62002      */
62003     selectRecords : function(records, keepExisting){
62004         if(!keepExisting){
62005             this.clearSelections();
62006         }
62007         var ds = this.grid.dataSource;
62008         for(var i = 0, len = records.length; i < len; i++){
62009             this.selectRow(ds.indexOf(records[i]), true);
62010         }
62011     },
62012
62013     /**
62014      * Gets the number of selected rows.
62015      * @return {Number}
62016      */
62017     getCount : function(){
62018         return this.selections.length;
62019     },
62020
62021     /**
62022      * Selects the first row in the grid.
62023      */
62024     selectFirstRow : function(){
62025         this.selectRow(0);
62026     },
62027
62028     /**
62029      * Select the last row.
62030      * @param {Boolean} keepExisting (optional) True to keep existing selections
62031      */
62032     selectLastRow : function(keepExisting){
62033         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
62034     },
62035
62036     /**
62037      * Selects the row immediately following the last selected row.
62038      * @param {Boolean} keepExisting (optional) True to keep existing selections
62039      */
62040     selectNext : function(keepExisting){
62041         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
62042             this.selectRow(this.last+1, keepExisting);
62043             this.grid.getView().focusRow(this.last);
62044         }
62045     },
62046
62047     /**
62048      * Selects the row that precedes the last selected row.
62049      * @param {Boolean} keepExisting (optional) True to keep existing selections
62050      */
62051     selectPrevious : function(keepExisting){
62052         if(this.last){
62053             this.selectRow(this.last-1, keepExisting);
62054             this.grid.getView().focusRow(this.last);
62055         }
62056     },
62057
62058     /**
62059      * Returns the selected records
62060      * @return {Array} Array of selected records
62061      */
62062     getSelections : function(){
62063         return [].concat(this.selections.items);
62064     },
62065
62066     /**
62067      * Returns the first selected record.
62068      * @return {Record}
62069      */
62070     getSelected : function(){
62071         return this.selections.itemAt(0);
62072     },
62073
62074
62075     /**
62076      * Clears all selections.
62077      */
62078     clearSelections : function(fast){
62079         if(this.locked) {
62080             return;
62081         }
62082         if(fast !== true){
62083             var ds = this.grid.dataSource;
62084             var s = this.selections;
62085             s.each(function(r){
62086                 this.deselectRow(ds.indexOfId(r.id));
62087             }, this);
62088             s.clear();
62089         }else{
62090             this.selections.clear();
62091         }
62092         this.last = false;
62093     },
62094
62095
62096     /**
62097      * Selects all rows.
62098      */
62099     selectAll : function(){
62100         if(this.locked) {
62101             return;
62102         }
62103         this.selections.clear();
62104         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
62105             this.selectRow(i, true);
62106         }
62107     },
62108
62109     /**
62110      * Returns True if there is a selection.
62111      * @return {Boolean}
62112      */
62113     hasSelection : function(){
62114         return this.selections.length > 0;
62115     },
62116
62117     /**
62118      * Returns True if the specified row is selected.
62119      * @param {Number/Record} record The record or index of the record to check
62120      * @return {Boolean}
62121      */
62122     isSelected : function(index){
62123         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
62124         return (r && this.selections.key(r.id) ? true : false);
62125     },
62126
62127     /**
62128      * Returns True if the specified record id is selected.
62129      * @param {String} id The id of record to check
62130      * @return {Boolean}
62131      */
62132     isIdSelected : function(id){
62133         return (this.selections.key(id) ? true : false);
62134     },
62135
62136     // private
62137     handleMouseDown : function(e, t){
62138         var view = this.grid.getView(), rowIndex;
62139         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
62140             return;
62141         };
62142         if(e.shiftKey && this.last !== false){
62143             var last = this.last;
62144             this.selectRange(last, rowIndex, e.ctrlKey);
62145             this.last = last; // reset the last
62146             view.focusRow(rowIndex);
62147         }else{
62148             var isSelected = this.isSelected(rowIndex);
62149             if(e.button !== 0 && isSelected){
62150                 view.focusRow(rowIndex);
62151             }else if(e.ctrlKey && isSelected){
62152                 this.deselectRow(rowIndex);
62153             }else if(!isSelected){
62154                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
62155                 view.focusRow(rowIndex);
62156             }
62157         }
62158         this.fireEvent("afterselectionchange", this);
62159     },
62160     // private
62161     handleDragableRowClick :  function(grid, rowIndex, e) 
62162     {
62163         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
62164             this.selectRow(rowIndex, false);
62165             grid.view.focusRow(rowIndex);
62166              this.fireEvent("afterselectionchange", this);
62167         }
62168     },
62169     
62170     /**
62171      * Selects multiple rows.
62172      * @param {Array} rows Array of the indexes of the row to select
62173      * @param {Boolean} keepExisting (optional) True to keep existing selections
62174      */
62175     selectRows : function(rows, keepExisting){
62176         if(!keepExisting){
62177             this.clearSelections();
62178         }
62179         for(var i = 0, len = rows.length; i < len; i++){
62180             this.selectRow(rows[i], true);
62181         }
62182     },
62183
62184     /**
62185      * Selects a range of rows. All rows in between startRow and endRow are also selected.
62186      * @param {Number} startRow The index of the first row in the range
62187      * @param {Number} endRow The index of the last row in the range
62188      * @param {Boolean} keepExisting (optional) True to retain existing selections
62189      */
62190     selectRange : function(startRow, endRow, keepExisting){
62191         if(this.locked) {
62192             return;
62193         }
62194         if(!keepExisting){
62195             this.clearSelections();
62196         }
62197         if(startRow <= endRow){
62198             for(var i = startRow; i <= endRow; i++){
62199                 this.selectRow(i, true);
62200             }
62201         }else{
62202             for(var i = startRow; i >= endRow; i--){
62203                 this.selectRow(i, true);
62204             }
62205         }
62206     },
62207
62208     /**
62209      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
62210      * @param {Number} startRow The index of the first row in the range
62211      * @param {Number} endRow The index of the last row in the range
62212      */
62213     deselectRange : function(startRow, endRow, preventViewNotify){
62214         if(this.locked) {
62215             return;
62216         }
62217         for(var i = startRow; i <= endRow; i++){
62218             this.deselectRow(i, preventViewNotify);
62219         }
62220     },
62221
62222     /**
62223      * Selects a row.
62224      * @param {Number} row The index of the row to select
62225      * @param {Boolean} keepExisting (optional) True to keep existing selections
62226      */
62227     selectRow : function(index, keepExisting, preventViewNotify){
62228         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
62229             return;
62230         }
62231         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
62232             if(!keepExisting || this.singleSelect){
62233                 this.clearSelections();
62234             }
62235             var r = this.grid.dataSource.getAt(index);
62236             this.selections.add(r);
62237             this.last = this.lastActive = index;
62238             if(!preventViewNotify){
62239                 this.grid.getView().onRowSelect(index);
62240             }
62241             this.fireEvent("rowselect", this, index, r);
62242             this.fireEvent("selectionchange", this);
62243         }
62244     },
62245
62246     /**
62247      * Deselects a row.
62248      * @param {Number} row The index of the row to deselect
62249      */
62250     deselectRow : function(index, preventViewNotify){
62251         if(this.locked) {
62252             return;
62253         }
62254         if(this.last == index){
62255             this.last = false;
62256         }
62257         if(this.lastActive == index){
62258             this.lastActive = false;
62259         }
62260         var r = this.grid.dataSource.getAt(index);
62261         this.selections.remove(r);
62262         if(!preventViewNotify){
62263             this.grid.getView().onRowDeselect(index);
62264         }
62265         this.fireEvent("rowdeselect", this, index);
62266         this.fireEvent("selectionchange", this);
62267     },
62268
62269     // private
62270     restoreLast : function(){
62271         if(this._last){
62272             this.last = this._last;
62273         }
62274     },
62275
62276     // private
62277     acceptsNav : function(row, col, cm){
62278         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62279     },
62280
62281     // private
62282     onEditorKey : function(field, e){
62283         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
62284         if(k == e.TAB){
62285             e.stopEvent();
62286             ed.completeEdit();
62287             if(e.shiftKey){
62288                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62289             }else{
62290                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62291             }
62292         }else if(k == e.ENTER && !e.ctrlKey){
62293             e.stopEvent();
62294             ed.completeEdit();
62295             if(e.shiftKey){
62296                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
62297             }else{
62298                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
62299             }
62300         }else if(k == e.ESC){
62301             ed.cancelEdit();
62302         }
62303         if(newCell){
62304             g.startEditing(newCell[0], newCell[1]);
62305         }
62306     }
62307 });/*
62308  * Based on:
62309  * Ext JS Library 1.1.1
62310  * Copyright(c) 2006-2007, Ext JS, LLC.
62311  *
62312  * Originally Released Under LGPL - original licence link has changed is not relivant.
62313  *
62314  * Fork - LGPL
62315  * <script type="text/javascript">
62316  */
62317 /**
62318  * @class Roo.grid.CellSelectionModel
62319  * @extends Roo.grid.AbstractSelectionModel
62320  * This class provides the basic implementation for cell selection in a grid.
62321  * @constructor
62322  * @param {Object} config The object containing the configuration of this model.
62323  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
62324  */
62325 Roo.grid.CellSelectionModel = function(config){
62326     Roo.apply(this, config);
62327
62328     this.selection = null;
62329
62330     this.addEvents({
62331         /**
62332              * @event beforerowselect
62333              * Fires before a cell is selected.
62334              * @param {SelectionModel} this
62335              * @param {Number} rowIndex The selected row index
62336              * @param {Number} colIndex The selected cell index
62337              */
62338             "beforecellselect" : true,
62339         /**
62340              * @event cellselect
62341              * Fires when a cell is selected.
62342              * @param {SelectionModel} this
62343              * @param {Number} rowIndex The selected row index
62344              * @param {Number} colIndex The selected cell index
62345              */
62346             "cellselect" : true,
62347         /**
62348              * @event selectionchange
62349              * Fires when the active selection changes.
62350              * @param {SelectionModel} this
62351              * @param {Object} selection null for no selection or an object (o) with two properties
62352                 <ul>
62353                 <li>o.record: the record object for the row the selection is in</li>
62354                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
62355                 </ul>
62356              */
62357             "selectionchange" : true,
62358         /**
62359              * @event tabend
62360              * Fires when the tab (or enter) was pressed on the last editable cell
62361              * You can use this to trigger add new row.
62362              * @param {SelectionModel} this
62363              */
62364             "tabend" : true,
62365          /**
62366              * @event beforeeditnext
62367              * Fires before the next editable sell is made active
62368              * You can use this to skip to another cell or fire the tabend
62369              *    if you set cell to false
62370              * @param {Object} eventdata object : { cell : [ row, col ] } 
62371              */
62372             "beforeeditnext" : true
62373     });
62374     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
62375 };
62376
62377 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
62378     
62379     enter_is_tab: false,
62380
62381     /** @ignore */
62382     initEvents : function(){
62383         this.grid.on("mousedown", this.handleMouseDown, this);
62384         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
62385         var view = this.grid.view;
62386         view.on("refresh", this.onViewChange, this);
62387         view.on("rowupdated", this.onRowUpdated, this);
62388         view.on("beforerowremoved", this.clearSelections, this);
62389         view.on("beforerowsinserted", this.clearSelections, this);
62390         if(this.grid.isEditor){
62391             this.grid.on("beforeedit", this.beforeEdit,  this);
62392         }
62393     },
62394
62395         //private
62396     beforeEdit : function(e){
62397         this.select(e.row, e.column, false, true, e.record);
62398     },
62399
62400         //private
62401     onRowUpdated : function(v, index, r){
62402         if(this.selection && this.selection.record == r){
62403             v.onCellSelect(index, this.selection.cell[1]);
62404         }
62405     },
62406
62407         //private
62408     onViewChange : function(){
62409         this.clearSelections(true);
62410     },
62411
62412         /**
62413          * Returns the currently selected cell,.
62414          * @return {Array} The selected cell (row, column) or null if none selected.
62415          */
62416     getSelectedCell : function(){
62417         return this.selection ? this.selection.cell : null;
62418     },
62419
62420     /**
62421      * Clears all selections.
62422      * @param {Boolean} true to prevent the gridview from being notified about the change.
62423      */
62424     clearSelections : function(preventNotify){
62425         var s = this.selection;
62426         if(s){
62427             if(preventNotify !== true){
62428                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
62429             }
62430             this.selection = null;
62431             this.fireEvent("selectionchange", this, null);
62432         }
62433     },
62434
62435     /**
62436      * Returns true if there is a selection.
62437      * @return {Boolean}
62438      */
62439     hasSelection : function(){
62440         return this.selection ? true : false;
62441     },
62442
62443     /** @ignore */
62444     handleMouseDown : function(e, t){
62445         var v = this.grid.getView();
62446         if(this.isLocked()){
62447             return;
62448         };
62449         var row = v.findRowIndex(t);
62450         var cell = v.findCellIndex(t);
62451         if(row !== false && cell !== false){
62452             this.select(row, cell);
62453         }
62454     },
62455
62456     /**
62457      * Selects a cell.
62458      * @param {Number} rowIndex
62459      * @param {Number} collIndex
62460      */
62461     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
62462         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
62463             this.clearSelections();
62464             r = r || this.grid.dataSource.getAt(rowIndex);
62465             this.selection = {
62466                 record : r,
62467                 cell : [rowIndex, colIndex]
62468             };
62469             if(!preventViewNotify){
62470                 var v = this.grid.getView();
62471                 v.onCellSelect(rowIndex, colIndex);
62472                 if(preventFocus !== true){
62473                     v.focusCell(rowIndex, colIndex);
62474                 }
62475             }
62476             this.fireEvent("cellselect", this, rowIndex, colIndex);
62477             this.fireEvent("selectionchange", this, this.selection);
62478         }
62479     },
62480
62481         //private
62482     isSelectable : function(rowIndex, colIndex, cm){
62483         return !cm.isHidden(colIndex);
62484     },
62485
62486     /** @ignore */
62487     handleKeyDown : function(e){
62488         //Roo.log('Cell Sel Model handleKeyDown');
62489         if(!e.isNavKeyPress()){
62490             return;
62491         }
62492         var g = this.grid, s = this.selection;
62493         if(!s){
62494             e.stopEvent();
62495             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
62496             if(cell){
62497                 this.select(cell[0], cell[1]);
62498             }
62499             return;
62500         }
62501         var sm = this;
62502         var walk = function(row, col, step){
62503             return g.walkCells(row, col, step, sm.isSelectable,  sm);
62504         };
62505         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
62506         var newCell;
62507
62508       
62509
62510         switch(k){
62511             case e.TAB:
62512                 // handled by onEditorKey
62513                 if (g.isEditor && g.editing) {
62514                     return;
62515                 }
62516                 if(e.shiftKey) {
62517                     newCell = walk(r, c-1, -1);
62518                 } else {
62519                     newCell = walk(r, c+1, 1);
62520                 }
62521                 break;
62522             
62523             case e.DOWN:
62524                newCell = walk(r+1, c, 1);
62525                 break;
62526             
62527             case e.UP:
62528                 newCell = walk(r-1, c, -1);
62529                 break;
62530             
62531             case e.RIGHT:
62532                 newCell = walk(r, c+1, 1);
62533                 break;
62534             
62535             case e.LEFT:
62536                 newCell = walk(r, c-1, -1);
62537                 break;
62538             
62539             case e.ENTER:
62540                 
62541                 if(g.isEditor && !g.editing){
62542                    g.startEditing(r, c);
62543                    e.stopEvent();
62544                    return;
62545                 }
62546                 
62547                 
62548              break;
62549         };
62550         if(newCell){
62551             this.select(newCell[0], newCell[1]);
62552             e.stopEvent();
62553             
62554         }
62555     },
62556
62557     acceptsNav : function(row, col, cm){
62558         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62559     },
62560     /**
62561      * Selects a cell.
62562      * @param {Number} field (not used) - as it's normally used as a listener
62563      * @param {Number} e - event - fake it by using
62564      *
62565      * var e = Roo.EventObjectImpl.prototype;
62566      * e.keyCode = e.TAB
62567      *
62568      * 
62569      */
62570     onEditorKey : function(field, e){
62571         
62572         var k = e.getKey(),
62573             newCell,
62574             g = this.grid,
62575             ed = g.activeEditor,
62576             forward = false;
62577         ///Roo.log('onEditorKey' + k);
62578         
62579         
62580         if (this.enter_is_tab && k == e.ENTER) {
62581             k = e.TAB;
62582         }
62583         
62584         if(k == e.TAB){
62585             if(e.shiftKey){
62586                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62587             }else{
62588                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62589                 forward = true;
62590             }
62591             
62592             e.stopEvent();
62593             
62594         } else if(k == e.ENTER &&  !e.ctrlKey){
62595             ed.completeEdit();
62596             e.stopEvent();
62597             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62598         
62599                 } else if(k == e.ESC){
62600             ed.cancelEdit();
62601         }
62602                 
62603         if (newCell) {
62604             var ecall = { cell : newCell, forward : forward };
62605             this.fireEvent('beforeeditnext', ecall );
62606             newCell = ecall.cell;
62607                         forward = ecall.forward;
62608         }
62609                 
62610         if(newCell){
62611             //Roo.log('next cell after edit');
62612             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62613         } else if (forward) {
62614             // tabbed past last
62615             this.fireEvent.defer(100, this, ['tabend',this]);
62616         }
62617     }
62618 });/*
62619  * Based on:
62620  * Ext JS Library 1.1.1
62621  * Copyright(c) 2006-2007, Ext JS, LLC.
62622  *
62623  * Originally Released Under LGPL - original licence link has changed is not relivant.
62624  *
62625  * Fork - LGPL
62626  * <script type="text/javascript">
62627  */
62628  
62629 /**
62630  * @class Roo.grid.EditorGrid
62631  * @extends Roo.grid.Grid
62632  * Class for creating and editable grid.
62633  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
62634  * The container MUST have some type of size defined for the grid to fill. The container will be 
62635  * automatically set to position relative if it isn't already.
62636  * @param {Object} dataSource The data model to bind to
62637  * @param {Object} colModel The column model with info about this grid's columns
62638  */
62639 Roo.grid.EditorGrid = function(container, config){
62640     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62641     this.getGridEl().addClass("xedit-grid");
62642
62643     if(!this.selModel){
62644         this.selModel = new Roo.grid.CellSelectionModel();
62645     }
62646
62647     this.activeEditor = null;
62648
62649         this.addEvents({
62650             /**
62651              * @event beforeedit
62652              * Fires before cell editing is triggered. The edit event object has the following properties <br />
62653              * <ul style="padding:5px;padding-left:16px;">
62654              * <li>grid - This grid</li>
62655              * <li>record - The record being edited</li>
62656              * <li>field - The field name being edited</li>
62657              * <li>value - The value for the field being edited.</li>
62658              * <li>row - The grid row index</li>
62659              * <li>column - The grid column index</li>
62660              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62661              * </ul>
62662              * @param {Object} e An edit event (see above for description)
62663              */
62664             "beforeedit" : true,
62665             /**
62666              * @event afteredit
62667              * Fires after a cell is edited. <br />
62668              * <ul style="padding:5px;padding-left:16px;">
62669              * <li>grid - This grid</li>
62670              * <li>record - The record being edited</li>
62671              * <li>field - The field name being edited</li>
62672              * <li>value - The value being set</li>
62673              * <li>originalValue - The original value for the field, before the edit.</li>
62674              * <li>row - The grid row index</li>
62675              * <li>column - The grid column index</li>
62676              * </ul>
62677              * @param {Object} e An edit event (see above for description)
62678              */
62679             "afteredit" : true,
62680             /**
62681              * @event validateedit
62682              * Fires after a cell is edited, but before the value is set in the record. 
62683          * You can use this to modify the value being set in the field, Return false
62684              * to cancel the change. The edit event object has the following properties <br />
62685              * <ul style="padding:5px;padding-left:16px;">
62686          * <li>editor - This editor</li>
62687              * <li>grid - This grid</li>
62688              * <li>record - The record being edited</li>
62689              * <li>field - The field name being edited</li>
62690              * <li>value - The value being set</li>
62691              * <li>originalValue - The original value for the field, before the edit.</li>
62692              * <li>row - The grid row index</li>
62693              * <li>column - The grid column index</li>
62694              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62695              * </ul>
62696              * @param {Object} e An edit event (see above for description)
62697              */
62698             "validateedit" : true
62699         });
62700     this.on("bodyscroll", this.stopEditing,  this);
62701     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
62702 };
62703
62704 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62705     /**
62706      * @cfg {Number} clicksToEdit
62707      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62708      */
62709     clicksToEdit: 2,
62710
62711     // private
62712     isEditor : true,
62713     // private
62714     trackMouseOver: false, // causes very odd FF errors
62715
62716     onCellDblClick : function(g, row, col){
62717         this.startEditing(row, col);
62718     },
62719
62720     onEditComplete : function(ed, value, startValue){
62721         this.editing = false;
62722         this.activeEditor = null;
62723         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62724         var r = ed.record;
62725         var field = this.colModel.getDataIndex(ed.col);
62726         var e = {
62727             grid: this,
62728             record: r,
62729             field: field,
62730             originalValue: startValue,
62731             value: value,
62732             row: ed.row,
62733             column: ed.col,
62734             cancel:false,
62735             editor: ed
62736         };
62737         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62738         cell.show();
62739           
62740         if(String(value) !== String(startValue)){
62741             
62742             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62743                 r.set(field, e.value);
62744                 // if we are dealing with a combo box..
62745                 // then we also set the 'name' colum to be the displayField
62746                 if (ed.field.displayField && ed.field.name) {
62747                     r.set(ed.field.name, ed.field.el.dom.value);
62748                 }
62749                 
62750                 delete e.cancel; //?? why!!!
62751                 this.fireEvent("afteredit", e);
62752             }
62753         } else {
62754             this.fireEvent("afteredit", e); // always fire it!
62755         }
62756         this.view.focusCell(ed.row, ed.col);
62757     },
62758
62759     /**
62760      * Starts editing the specified for the specified row/column
62761      * @param {Number} rowIndex
62762      * @param {Number} colIndex
62763      */
62764     startEditing : function(row, col){
62765         this.stopEditing();
62766         if(this.colModel.isCellEditable(col, row)){
62767             this.view.ensureVisible(row, col, true);
62768           
62769             var r = this.dataSource.getAt(row);
62770             var field = this.colModel.getDataIndex(col);
62771             var cell = Roo.get(this.view.getCell(row,col));
62772             var e = {
62773                 grid: this,
62774                 record: r,
62775                 field: field,
62776                 value: r.data[field],
62777                 row: row,
62778                 column: col,
62779                 cancel:false 
62780             };
62781             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62782                 this.editing = true;
62783                 var ed = this.colModel.getCellEditor(col, row);
62784                 
62785                 if (!ed) {
62786                     return;
62787                 }
62788                 if(!ed.rendered){
62789                     ed.render(ed.parentEl || document.body);
62790                 }
62791                 ed.field.reset();
62792                
62793                 cell.hide();
62794                 
62795                 (function(){ // complex but required for focus issues in safari, ie and opera
62796                     ed.row = row;
62797                     ed.col = col;
62798                     ed.record = r;
62799                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
62800                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
62801                     this.activeEditor = ed;
62802                     var v = r.data[field];
62803                     ed.startEdit(this.view.getCell(row, col), v);
62804                     // combo's with 'displayField and name set
62805                     if (ed.field.displayField && ed.field.name) {
62806                         ed.field.el.dom.value = r.data[ed.field.name];
62807                     }
62808                     
62809                     
62810                 }).defer(50, this);
62811             }
62812         }
62813     },
62814         
62815     /**
62816      * Stops any active editing
62817      */
62818     stopEditing : function(){
62819         if(this.activeEditor){
62820             this.activeEditor.completeEdit();
62821         }
62822         this.activeEditor = null;
62823     },
62824         
62825          /**
62826      * Called to get grid's drag proxy text, by default returns this.ddText.
62827      * @return {String}
62828      */
62829     getDragDropText : function(){
62830         var count = this.selModel.getSelectedCell() ? 1 : 0;
62831         return String.format(this.ddText, count, count == 1 ? '' : 's');
62832     }
62833         
62834 });/*
62835  * Based on:
62836  * Ext JS Library 1.1.1
62837  * Copyright(c) 2006-2007, Ext JS, LLC.
62838  *
62839  * Originally Released Under LGPL - original licence link has changed is not relivant.
62840  *
62841  * Fork - LGPL
62842  * <script type="text/javascript">
62843  */
62844
62845 // private - not really -- you end up using it !
62846 // This is a support class used internally by the Grid components
62847
62848 /**
62849  * @class Roo.grid.GridEditor
62850  * @extends Roo.Editor
62851  * Class for creating and editable grid elements.
62852  * @param {Object} config any settings (must include field)
62853  */
62854 Roo.grid.GridEditor = function(field, config){
62855     if (!config && field.field) {
62856         config = field;
62857         field = Roo.factory(config.field, Roo.form);
62858     }
62859     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62860     field.monitorTab = false;
62861 };
62862
62863 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62864     
62865     /**
62866      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62867      */
62868     
62869     alignment: "tl-tl",
62870     autoSize: "width",
62871     hideEl : false,
62872     cls: "x-small-editor x-grid-editor",
62873     shim:false,
62874     shadow:"frame"
62875 });/*
62876  * Based on:
62877  * Ext JS Library 1.1.1
62878  * Copyright(c) 2006-2007, Ext JS, LLC.
62879  *
62880  * Originally Released Under LGPL - original licence link has changed is not relivant.
62881  *
62882  * Fork - LGPL
62883  * <script type="text/javascript">
62884  */
62885   
62886
62887   
62888 Roo.grid.PropertyRecord = Roo.data.Record.create([
62889     {name:'name',type:'string'},  'value'
62890 ]);
62891
62892
62893 Roo.grid.PropertyStore = function(grid, source){
62894     this.grid = grid;
62895     this.store = new Roo.data.Store({
62896         recordType : Roo.grid.PropertyRecord
62897     });
62898     this.store.on('update', this.onUpdate,  this);
62899     if(source){
62900         this.setSource(source);
62901     }
62902     Roo.grid.PropertyStore.superclass.constructor.call(this);
62903 };
62904
62905
62906
62907 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62908     setSource : function(o){
62909         this.source = o;
62910         this.store.removeAll();
62911         var data = [];
62912         for(var k in o){
62913             if(this.isEditableValue(o[k])){
62914                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62915             }
62916         }
62917         this.store.loadRecords({records: data}, {}, true);
62918     },
62919
62920     onUpdate : function(ds, record, type){
62921         if(type == Roo.data.Record.EDIT){
62922             var v = record.data['value'];
62923             var oldValue = record.modified['value'];
62924             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62925                 this.source[record.id] = v;
62926                 record.commit();
62927                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62928             }else{
62929                 record.reject();
62930             }
62931         }
62932     },
62933
62934     getProperty : function(row){
62935        return this.store.getAt(row);
62936     },
62937
62938     isEditableValue: function(val){
62939         if(val && val instanceof Date){
62940             return true;
62941         }else if(typeof val == 'object' || typeof val == 'function'){
62942             return false;
62943         }
62944         return true;
62945     },
62946
62947     setValue : function(prop, value){
62948         this.source[prop] = value;
62949         this.store.getById(prop).set('value', value);
62950     },
62951
62952     getSource : function(){
62953         return this.source;
62954     }
62955 });
62956
62957 Roo.grid.PropertyColumnModel = function(grid, store){
62958     this.grid = grid;
62959     var g = Roo.grid;
62960     g.PropertyColumnModel.superclass.constructor.call(this, [
62961         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62962         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62963     ]);
62964     this.store = store;
62965     this.bselect = Roo.DomHelper.append(document.body, {
62966         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62967             {tag: 'option', value: 'true', html: 'true'},
62968             {tag: 'option', value: 'false', html: 'false'}
62969         ]
62970     });
62971     Roo.id(this.bselect);
62972     var f = Roo.form;
62973     this.editors = {
62974         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62975         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62976         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62977         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62978         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62979     };
62980     this.renderCellDelegate = this.renderCell.createDelegate(this);
62981     this.renderPropDelegate = this.renderProp.createDelegate(this);
62982 };
62983
62984 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62985     
62986     
62987     nameText : 'Name',
62988     valueText : 'Value',
62989     
62990     dateFormat : 'm/j/Y',
62991     
62992     
62993     renderDate : function(dateVal){
62994         return dateVal.dateFormat(this.dateFormat);
62995     },
62996
62997     renderBool : function(bVal){
62998         return bVal ? 'true' : 'false';
62999     },
63000
63001     isCellEditable : function(colIndex, rowIndex){
63002         return colIndex == 1;
63003     },
63004
63005     getRenderer : function(col){
63006         return col == 1 ?
63007             this.renderCellDelegate : this.renderPropDelegate;
63008     },
63009
63010     renderProp : function(v){
63011         return this.getPropertyName(v);
63012     },
63013
63014     renderCell : function(val){
63015         var rv = val;
63016         if(val instanceof Date){
63017             rv = this.renderDate(val);
63018         }else if(typeof val == 'boolean'){
63019             rv = this.renderBool(val);
63020         }
63021         return Roo.util.Format.htmlEncode(rv);
63022     },
63023
63024     getPropertyName : function(name){
63025         var pn = this.grid.propertyNames;
63026         return pn && pn[name] ? pn[name] : name;
63027     },
63028
63029     getCellEditor : function(colIndex, rowIndex){
63030         var p = this.store.getProperty(rowIndex);
63031         var n = p.data['name'], val = p.data['value'];
63032         
63033         if(typeof(this.grid.customEditors[n]) == 'string'){
63034             return this.editors[this.grid.customEditors[n]];
63035         }
63036         if(typeof(this.grid.customEditors[n]) != 'undefined'){
63037             return this.grid.customEditors[n];
63038         }
63039         if(val instanceof Date){
63040             return this.editors['date'];
63041         }else if(typeof val == 'number'){
63042             return this.editors['number'];
63043         }else if(typeof val == 'boolean'){
63044             return this.editors['boolean'];
63045         }else{
63046             return this.editors['string'];
63047         }
63048     }
63049 });
63050
63051 /**
63052  * @class Roo.grid.PropertyGrid
63053  * @extends Roo.grid.EditorGrid
63054  * This class represents the  interface of a component based property grid control.
63055  * <br><br>Usage:<pre><code>
63056  var grid = new Roo.grid.PropertyGrid("my-container-id", {
63057       
63058  });
63059  // set any options
63060  grid.render();
63061  * </code></pre>
63062   
63063  * @constructor
63064  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
63065  * The container MUST have some type of size defined for the grid to fill. The container will be
63066  * automatically set to position relative if it isn't already.
63067  * @param {Object} config A config object that sets properties on this grid.
63068  */
63069 Roo.grid.PropertyGrid = function(container, config){
63070     config = config || {};
63071     var store = new Roo.grid.PropertyStore(this);
63072     this.store = store;
63073     var cm = new Roo.grid.PropertyColumnModel(this, store);
63074     store.store.sort('name', 'ASC');
63075     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
63076         ds: store.store,
63077         cm: cm,
63078         enableColLock:false,
63079         enableColumnMove:false,
63080         stripeRows:false,
63081         trackMouseOver: false,
63082         clicksToEdit:1
63083     }, config));
63084     this.getGridEl().addClass('x-props-grid');
63085     this.lastEditRow = null;
63086     this.on('columnresize', this.onColumnResize, this);
63087     this.addEvents({
63088          /**
63089              * @event beforepropertychange
63090              * Fires before a property changes (return false to stop?)
63091              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
63092              * @param {String} id Record Id
63093              * @param {String} newval New Value
63094          * @param {String} oldval Old Value
63095              */
63096         "beforepropertychange": true,
63097         /**
63098              * @event propertychange
63099              * Fires after a property changes
63100              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
63101              * @param {String} id Record Id
63102              * @param {String} newval New Value
63103          * @param {String} oldval Old Value
63104              */
63105         "propertychange": true
63106     });
63107     this.customEditors = this.customEditors || {};
63108 };
63109 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
63110     
63111      /**
63112      * @cfg {Object} customEditors map of colnames=> custom editors.
63113      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
63114      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
63115      * false disables editing of the field.
63116          */
63117     
63118       /**
63119      * @cfg {Object} propertyNames map of property Names to their displayed value
63120          */
63121     
63122     render : function(){
63123         Roo.grid.PropertyGrid.superclass.render.call(this);
63124         this.autoSize.defer(100, this);
63125     },
63126
63127     autoSize : function(){
63128         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
63129         if(this.view){
63130             this.view.fitColumns();
63131         }
63132     },
63133
63134     onColumnResize : function(){
63135         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
63136         this.autoSize();
63137     },
63138     /**
63139      * Sets the data for the Grid
63140      * accepts a Key => Value object of all the elements avaiable.
63141      * @param {Object} data  to appear in grid.
63142      */
63143     setSource : function(source){
63144         this.store.setSource(source);
63145         //this.autoSize();
63146     },
63147     /**
63148      * Gets all the data from the grid.
63149      * @return {Object} data  data stored in grid
63150      */
63151     getSource : function(){
63152         return this.store.getSource();
63153     }
63154 });/*
63155   
63156  * Licence LGPL
63157  
63158  */
63159  
63160 /**
63161  * @class Roo.grid.Calendar
63162  * @extends Roo.util.Grid
63163  * This class extends the Grid to provide a calendar widget
63164  * <br><br>Usage:<pre><code>
63165  var grid = new Roo.grid.Calendar("my-container-id", {
63166      ds: myDataStore,
63167      cm: myColModel,
63168      selModel: mySelectionModel,
63169      autoSizeColumns: true,
63170      monitorWindowResize: false,
63171      trackMouseOver: true
63172      eventstore : real data store..
63173  });
63174  // set any options
63175  grid.render();
63176   
63177   * @constructor
63178  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
63179  * The container MUST have some type of size defined for the grid to fill. The container will be
63180  * automatically set to position relative if it isn't already.
63181  * @param {Object} config A config object that sets properties on this grid.
63182  */
63183 Roo.grid.Calendar = function(container, config){
63184         // initialize the container
63185         this.container = Roo.get(container);
63186         this.container.update("");
63187         this.container.setStyle("overflow", "hidden");
63188     this.container.addClass('x-grid-container');
63189
63190     this.id = this.container.id;
63191
63192     Roo.apply(this, config);
63193     // check and correct shorthanded configs
63194     
63195     var rows = [];
63196     var d =1;
63197     for (var r = 0;r < 6;r++) {
63198         
63199         rows[r]=[];
63200         for (var c =0;c < 7;c++) {
63201             rows[r][c]= '';
63202         }
63203     }
63204     if (this.eventStore) {
63205         this.eventStore= Roo.factory(this.eventStore, Roo.data);
63206         this.eventStore.on('load',this.onLoad, this);
63207         this.eventStore.on('beforeload',this.clearEvents, this);
63208          
63209     }
63210     
63211     this.dataSource = new Roo.data.Store({
63212             proxy: new Roo.data.MemoryProxy(rows),
63213             reader: new Roo.data.ArrayReader({}, [
63214                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
63215     });
63216
63217     this.dataSource.load();
63218     this.ds = this.dataSource;
63219     this.ds.xmodule = this.xmodule || false;
63220     
63221     
63222     var cellRender = function(v,x,r)
63223     {
63224         return String.format(
63225             '<div class="fc-day  fc-widget-content"><div>' +
63226                 '<div class="fc-event-container"></div>' +
63227                 '<div class="fc-day-number">{0}</div>'+
63228                 
63229                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
63230             '</div></div>', v);
63231     
63232     }
63233     
63234     
63235     this.colModel = new Roo.grid.ColumnModel( [
63236         {
63237             xtype: 'ColumnModel',
63238             xns: Roo.grid,
63239             dataIndex : 'weekday0',
63240             header : 'Sunday',
63241             renderer : cellRender
63242         },
63243         {
63244             xtype: 'ColumnModel',
63245             xns: Roo.grid,
63246             dataIndex : 'weekday1',
63247             header : 'Monday',
63248             renderer : cellRender
63249         },
63250         {
63251             xtype: 'ColumnModel',
63252             xns: Roo.grid,
63253             dataIndex : 'weekday2',
63254             header : 'Tuesday',
63255             renderer : cellRender
63256         },
63257         {
63258             xtype: 'ColumnModel',
63259             xns: Roo.grid,
63260             dataIndex : 'weekday3',
63261             header : 'Wednesday',
63262             renderer : cellRender
63263         },
63264         {
63265             xtype: 'ColumnModel',
63266             xns: Roo.grid,
63267             dataIndex : 'weekday4',
63268             header : 'Thursday',
63269             renderer : cellRender
63270         },
63271         {
63272             xtype: 'ColumnModel',
63273             xns: Roo.grid,
63274             dataIndex : 'weekday5',
63275             header : 'Friday',
63276             renderer : cellRender
63277         },
63278         {
63279             xtype: 'ColumnModel',
63280             xns: Roo.grid,
63281             dataIndex : 'weekday6',
63282             header : 'Saturday',
63283             renderer : cellRender
63284         }
63285     ]);
63286     this.cm = this.colModel;
63287     this.cm.xmodule = this.xmodule || false;
63288  
63289         
63290           
63291     //this.selModel = new Roo.grid.CellSelectionModel();
63292     //this.sm = this.selModel;
63293     //this.selModel.init(this);
63294     
63295     
63296     if(this.width){
63297         this.container.setWidth(this.width);
63298     }
63299
63300     if(this.height){
63301         this.container.setHeight(this.height);
63302     }
63303     /** @private */
63304         this.addEvents({
63305         // raw events
63306         /**
63307          * @event click
63308          * The raw click event for the entire grid.
63309          * @param {Roo.EventObject} e
63310          */
63311         "click" : true,
63312         /**
63313          * @event dblclick
63314          * The raw dblclick event for the entire grid.
63315          * @param {Roo.EventObject} e
63316          */
63317         "dblclick" : true,
63318         /**
63319          * @event contextmenu
63320          * The raw contextmenu event for the entire grid.
63321          * @param {Roo.EventObject} e
63322          */
63323         "contextmenu" : true,
63324         /**
63325          * @event mousedown
63326          * The raw mousedown event for the entire grid.
63327          * @param {Roo.EventObject} e
63328          */
63329         "mousedown" : true,
63330         /**
63331          * @event mouseup
63332          * The raw mouseup event for the entire grid.
63333          * @param {Roo.EventObject} e
63334          */
63335         "mouseup" : true,
63336         /**
63337          * @event mouseover
63338          * The raw mouseover event for the entire grid.
63339          * @param {Roo.EventObject} e
63340          */
63341         "mouseover" : true,
63342         /**
63343          * @event mouseout
63344          * The raw mouseout event for the entire grid.
63345          * @param {Roo.EventObject} e
63346          */
63347         "mouseout" : true,
63348         /**
63349          * @event keypress
63350          * The raw keypress event for the entire grid.
63351          * @param {Roo.EventObject} e
63352          */
63353         "keypress" : true,
63354         /**
63355          * @event keydown
63356          * The raw keydown event for the entire grid.
63357          * @param {Roo.EventObject} e
63358          */
63359         "keydown" : true,
63360
63361         // custom events
63362
63363         /**
63364          * @event cellclick
63365          * Fires when a cell is clicked
63366          * @param {Grid} this
63367          * @param {Number} rowIndex
63368          * @param {Number} columnIndex
63369          * @param {Roo.EventObject} e
63370          */
63371         "cellclick" : true,
63372         /**
63373          * @event celldblclick
63374          * Fires when a cell is double clicked
63375          * @param {Grid} this
63376          * @param {Number} rowIndex
63377          * @param {Number} columnIndex
63378          * @param {Roo.EventObject} e
63379          */
63380         "celldblclick" : true,
63381         /**
63382          * @event rowclick
63383          * Fires when a row is clicked
63384          * @param {Grid} this
63385          * @param {Number} rowIndex
63386          * @param {Roo.EventObject} e
63387          */
63388         "rowclick" : true,
63389         /**
63390          * @event rowdblclick
63391          * Fires when a row is double clicked
63392          * @param {Grid} this
63393          * @param {Number} rowIndex
63394          * @param {Roo.EventObject} e
63395          */
63396         "rowdblclick" : true,
63397         /**
63398          * @event headerclick
63399          * Fires when a header is clicked
63400          * @param {Grid} this
63401          * @param {Number} columnIndex
63402          * @param {Roo.EventObject} e
63403          */
63404         "headerclick" : true,
63405         /**
63406          * @event headerdblclick
63407          * Fires when a header cell is double clicked
63408          * @param {Grid} this
63409          * @param {Number} columnIndex
63410          * @param {Roo.EventObject} e
63411          */
63412         "headerdblclick" : true,
63413         /**
63414          * @event rowcontextmenu
63415          * Fires when a row is right clicked
63416          * @param {Grid} this
63417          * @param {Number} rowIndex
63418          * @param {Roo.EventObject} e
63419          */
63420         "rowcontextmenu" : true,
63421         /**
63422          * @event cellcontextmenu
63423          * Fires when a cell is right clicked
63424          * @param {Grid} this
63425          * @param {Number} rowIndex
63426          * @param {Number} cellIndex
63427          * @param {Roo.EventObject} e
63428          */
63429          "cellcontextmenu" : true,
63430         /**
63431          * @event headercontextmenu
63432          * Fires when a header is right clicked
63433          * @param {Grid} this
63434          * @param {Number} columnIndex
63435          * @param {Roo.EventObject} e
63436          */
63437         "headercontextmenu" : true,
63438         /**
63439          * @event bodyscroll
63440          * Fires when the body element is scrolled
63441          * @param {Number} scrollLeft
63442          * @param {Number} scrollTop
63443          */
63444         "bodyscroll" : true,
63445         /**
63446          * @event columnresize
63447          * Fires when the user resizes a column
63448          * @param {Number} columnIndex
63449          * @param {Number} newSize
63450          */
63451         "columnresize" : true,
63452         /**
63453          * @event columnmove
63454          * Fires when the user moves a column
63455          * @param {Number} oldIndex
63456          * @param {Number} newIndex
63457          */
63458         "columnmove" : true,
63459         /**
63460          * @event startdrag
63461          * Fires when row(s) start being dragged
63462          * @param {Grid} this
63463          * @param {Roo.GridDD} dd The drag drop object
63464          * @param {event} e The raw browser event
63465          */
63466         "startdrag" : true,
63467         /**
63468          * @event enddrag
63469          * Fires when a drag operation is complete
63470          * @param {Grid} this
63471          * @param {Roo.GridDD} dd The drag drop object
63472          * @param {event} e The raw browser event
63473          */
63474         "enddrag" : true,
63475         /**
63476          * @event dragdrop
63477          * Fires when dragged row(s) are dropped on a valid DD target
63478          * @param {Grid} this
63479          * @param {Roo.GridDD} dd The drag drop object
63480          * @param {String} targetId The target drag drop object
63481          * @param {event} e The raw browser event
63482          */
63483         "dragdrop" : true,
63484         /**
63485          * @event dragover
63486          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
63487          * @param {Grid} this
63488          * @param {Roo.GridDD} dd The drag drop object
63489          * @param {String} targetId The target drag drop object
63490          * @param {event} e The raw browser event
63491          */
63492         "dragover" : true,
63493         /**
63494          * @event dragenter
63495          *  Fires when the dragged row(s) first cross another DD target while being dragged
63496          * @param {Grid} this
63497          * @param {Roo.GridDD} dd The drag drop object
63498          * @param {String} targetId The target drag drop object
63499          * @param {event} e The raw browser event
63500          */
63501         "dragenter" : true,
63502         /**
63503          * @event dragout
63504          * Fires when the dragged row(s) leave another DD target while being dragged
63505          * @param {Grid} this
63506          * @param {Roo.GridDD} dd The drag drop object
63507          * @param {String} targetId The target drag drop object
63508          * @param {event} e The raw browser event
63509          */
63510         "dragout" : true,
63511         /**
63512          * @event rowclass
63513          * Fires when a row is rendered, so you can change add a style to it.
63514          * @param {GridView} gridview   The grid view
63515          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
63516          */
63517         'rowclass' : true,
63518
63519         /**
63520          * @event render
63521          * Fires when the grid is rendered
63522          * @param {Grid} grid
63523          */
63524         'render' : true,
63525             /**
63526              * @event select
63527              * Fires when a date is selected
63528              * @param {DatePicker} this
63529              * @param {Date} date The selected date
63530              */
63531         'select': true,
63532         /**
63533              * @event monthchange
63534              * Fires when the displayed month changes 
63535              * @param {DatePicker} this
63536              * @param {Date} date The selected month
63537              */
63538         'monthchange': true,
63539         /**
63540              * @event evententer
63541              * Fires when mouse over an event
63542              * @param {Calendar} this
63543              * @param {event} Event
63544              */
63545         'evententer': true,
63546         /**
63547              * @event eventleave
63548              * Fires when the mouse leaves an
63549              * @param {Calendar} this
63550              * @param {event}
63551              */
63552         'eventleave': true,
63553         /**
63554              * @event eventclick
63555              * Fires when the mouse click an
63556              * @param {Calendar} this
63557              * @param {event}
63558              */
63559         'eventclick': true,
63560         /**
63561              * @event eventrender
63562              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63563              * @param {Calendar} this
63564              * @param {data} data to be modified
63565              */
63566         'eventrender': true
63567         
63568     });
63569
63570     Roo.grid.Grid.superclass.constructor.call(this);
63571     this.on('render', function() {
63572         this.view.el.addClass('x-grid-cal'); 
63573         
63574         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63575
63576     },this);
63577     
63578     if (!Roo.grid.Calendar.style) {
63579         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63580             
63581             
63582             '.x-grid-cal .x-grid-col' :  {
63583                 height: 'auto !important',
63584                 'vertical-align': 'top'
63585             },
63586             '.x-grid-cal  .fc-event-hori' : {
63587                 height: '14px'
63588             }
63589              
63590             
63591         }, Roo.id());
63592     }
63593
63594     
63595     
63596 };
63597 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63598     /**
63599      * @cfg {Store} eventStore The store that loads events.
63600      */
63601     eventStore : 25,
63602
63603      
63604     activeDate : false,
63605     startDay : 0,
63606     autoWidth : true,
63607     monitorWindowResize : false,
63608
63609     
63610     resizeColumns : function() {
63611         var col = (this.view.el.getWidth() / 7) - 3;
63612         // loop through cols, and setWidth
63613         for(var i =0 ; i < 7 ; i++){
63614             this.cm.setColumnWidth(i, col);
63615         }
63616     },
63617      setDate :function(date) {
63618         
63619         Roo.log('setDate?');
63620         
63621         this.resizeColumns();
63622         var vd = this.activeDate;
63623         this.activeDate = date;
63624 //        if(vd && this.el){
63625 //            var t = date.getTime();
63626 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63627 //                Roo.log('using add remove');
63628 //                
63629 //                this.fireEvent('monthchange', this, date);
63630 //                
63631 //                this.cells.removeClass("fc-state-highlight");
63632 //                this.cells.each(function(c){
63633 //                   if(c.dateValue == t){
63634 //                       c.addClass("fc-state-highlight");
63635 //                       setTimeout(function(){
63636 //                            try{c.dom.firstChild.focus();}catch(e){}
63637 //                       }, 50);
63638 //                       return false;
63639 //                   }
63640 //                   return true;
63641 //                });
63642 //                return;
63643 //            }
63644 //        }
63645         
63646         var days = date.getDaysInMonth();
63647         
63648         var firstOfMonth = date.getFirstDateOfMonth();
63649         var startingPos = firstOfMonth.getDay()-this.startDay;
63650         
63651         if(startingPos < this.startDay){
63652             startingPos += 7;
63653         }
63654         
63655         var pm = date.add(Date.MONTH, -1);
63656         var prevStart = pm.getDaysInMonth()-startingPos;
63657 //        
63658         
63659         
63660         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63661         
63662         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63663         //this.cells.addClassOnOver('fc-state-hover');
63664         
63665         var cells = this.cells.elements;
63666         var textEls = this.textNodes;
63667         
63668         //Roo.each(cells, function(cell){
63669         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63670         //});
63671         
63672         days += startingPos;
63673
63674         // convert everything to numbers so it's fast
63675         var day = 86400000;
63676         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63677         //Roo.log(d);
63678         //Roo.log(pm);
63679         //Roo.log(prevStart);
63680         
63681         var today = new Date().clearTime().getTime();
63682         var sel = date.clearTime().getTime();
63683         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63684         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63685         var ddMatch = this.disabledDatesRE;
63686         var ddText = this.disabledDatesText;
63687         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63688         var ddaysText = this.disabledDaysText;
63689         var format = this.format;
63690         
63691         var setCellClass = function(cal, cell){
63692             
63693             //Roo.log('set Cell Class');
63694             cell.title = "";
63695             var t = d.getTime();
63696             
63697             //Roo.log(d);
63698             
63699             
63700             cell.dateValue = t;
63701             if(t == today){
63702                 cell.className += " fc-today";
63703                 cell.className += " fc-state-highlight";
63704                 cell.title = cal.todayText;
63705             }
63706             if(t == sel){
63707                 // disable highlight in other month..
63708                 cell.className += " fc-state-highlight";
63709                 
63710             }
63711             // disabling
63712             if(t < min) {
63713                 //cell.className = " fc-state-disabled";
63714                 cell.title = cal.minText;
63715                 return;
63716             }
63717             if(t > max) {
63718                 //cell.className = " fc-state-disabled";
63719                 cell.title = cal.maxText;
63720                 return;
63721             }
63722             if(ddays){
63723                 if(ddays.indexOf(d.getDay()) != -1){
63724                     // cell.title = ddaysText;
63725                    // cell.className = " fc-state-disabled";
63726                 }
63727             }
63728             if(ddMatch && format){
63729                 var fvalue = d.dateFormat(format);
63730                 if(ddMatch.test(fvalue)){
63731                     cell.title = ddText.replace("%0", fvalue);
63732                    cell.className = " fc-state-disabled";
63733                 }
63734             }
63735             
63736             if (!cell.initialClassName) {
63737                 cell.initialClassName = cell.dom.className;
63738             }
63739             
63740             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
63741         };
63742
63743         var i = 0;
63744         
63745         for(; i < startingPos; i++) {
63746             cells[i].dayName =  (++prevStart);
63747             Roo.log(textEls[i]);
63748             d.setDate(d.getDate()+1);
63749             
63750             //cells[i].className = "fc-past fc-other-month";
63751             setCellClass(this, cells[i]);
63752         }
63753         
63754         var intDay = 0;
63755         
63756         for(; i < days; i++){
63757             intDay = i - startingPos + 1;
63758             cells[i].dayName =  (intDay);
63759             d.setDate(d.getDate()+1);
63760             
63761             cells[i].className = ''; // "x-date-active";
63762             setCellClass(this, cells[i]);
63763         }
63764         var extraDays = 0;
63765         
63766         for(; i < 42; i++) {
63767             //textEls[i].innerHTML = (++extraDays);
63768             
63769             d.setDate(d.getDate()+1);
63770             cells[i].dayName = (++extraDays);
63771             cells[i].className = "fc-future fc-other-month";
63772             setCellClass(this, cells[i]);
63773         }
63774         
63775         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63776         
63777         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63778         
63779         // this will cause all the cells to mis
63780         var rows= [];
63781         var i =0;
63782         for (var r = 0;r < 6;r++) {
63783             for (var c =0;c < 7;c++) {
63784                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63785             }    
63786         }
63787         
63788         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63789         for(i=0;i<cells.length;i++) {
63790             
63791             this.cells.elements[i].dayName = cells[i].dayName ;
63792             this.cells.elements[i].className = cells[i].className;
63793             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63794             this.cells.elements[i].title = cells[i].title ;
63795             this.cells.elements[i].dateValue = cells[i].dateValue ;
63796         }
63797         
63798         
63799         
63800         
63801         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63802         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63803         
63804         ////if(totalRows != 6){
63805             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63806            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63807        // }
63808         
63809         this.fireEvent('monthchange', this, date);
63810         
63811         
63812     },
63813  /**
63814      * Returns the grid's SelectionModel.
63815      * @return {SelectionModel}
63816      */
63817     getSelectionModel : function(){
63818         if(!this.selModel){
63819             this.selModel = new Roo.grid.CellSelectionModel();
63820         }
63821         return this.selModel;
63822     },
63823
63824     load: function() {
63825         this.eventStore.load()
63826         
63827         
63828         
63829     },
63830     
63831     findCell : function(dt) {
63832         dt = dt.clearTime().getTime();
63833         var ret = false;
63834         this.cells.each(function(c){
63835             //Roo.log("check " +c.dateValue + '?=' + dt);
63836             if(c.dateValue == dt){
63837                 ret = c;
63838                 return false;
63839             }
63840             return true;
63841         });
63842         
63843         return ret;
63844     },
63845     
63846     findCells : function(rec) {
63847         var s = rec.data.start_dt.clone().clearTime().getTime();
63848        // Roo.log(s);
63849         var e= rec.data.end_dt.clone().clearTime().getTime();
63850        // Roo.log(e);
63851         var ret = [];
63852         this.cells.each(function(c){
63853              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63854             
63855             if(c.dateValue > e){
63856                 return ;
63857             }
63858             if(c.dateValue < s){
63859                 return ;
63860             }
63861             ret.push(c);
63862         });
63863         
63864         return ret;    
63865     },
63866     
63867     findBestRow: function(cells)
63868     {
63869         var ret = 0;
63870         
63871         for (var i =0 ; i < cells.length;i++) {
63872             ret  = Math.max(cells[i].rows || 0,ret);
63873         }
63874         return ret;
63875         
63876     },
63877     
63878     
63879     addItem : function(rec)
63880     {
63881         // look for vertical location slot in
63882         var cells = this.findCells(rec);
63883         
63884         rec.row = this.findBestRow(cells);
63885         
63886         // work out the location.
63887         
63888         var crow = false;
63889         var rows = [];
63890         for(var i =0; i < cells.length; i++) {
63891             if (!crow) {
63892                 crow = {
63893                     start : cells[i],
63894                     end :  cells[i]
63895                 };
63896                 continue;
63897             }
63898             if (crow.start.getY() == cells[i].getY()) {
63899                 // on same row.
63900                 crow.end = cells[i];
63901                 continue;
63902             }
63903             // different row.
63904             rows.push(crow);
63905             crow = {
63906                 start: cells[i],
63907                 end : cells[i]
63908             };
63909             
63910         }
63911         
63912         rows.push(crow);
63913         rec.els = [];
63914         rec.rows = rows;
63915         rec.cells = cells;
63916         for (var i = 0; i < cells.length;i++) {
63917             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63918             
63919         }
63920         
63921         
63922     },
63923     
63924     clearEvents: function() {
63925         
63926         if (!this.eventStore.getCount()) {
63927             return;
63928         }
63929         // reset number of rows in cells.
63930         Roo.each(this.cells.elements, function(c){
63931             c.rows = 0;
63932         });
63933         
63934         this.eventStore.each(function(e) {
63935             this.clearEvent(e);
63936         },this);
63937         
63938     },
63939     
63940     clearEvent : function(ev)
63941     {
63942         if (ev.els) {
63943             Roo.each(ev.els, function(el) {
63944                 el.un('mouseenter' ,this.onEventEnter, this);
63945                 el.un('mouseleave' ,this.onEventLeave, this);
63946                 el.remove();
63947             },this);
63948             ev.els = [];
63949         }
63950     },
63951     
63952     
63953     renderEvent : function(ev,ctr) {
63954         if (!ctr) {
63955              ctr = this.view.el.select('.fc-event-container',true).first();
63956         }
63957         
63958          
63959         this.clearEvent(ev);
63960             //code
63961        
63962         
63963         
63964         ev.els = [];
63965         var cells = ev.cells;
63966         var rows = ev.rows;
63967         this.fireEvent('eventrender', this, ev);
63968         
63969         for(var i =0; i < rows.length; i++) {
63970             
63971             cls = '';
63972             if (i == 0) {
63973                 cls += ' fc-event-start';
63974             }
63975             if ((i+1) == rows.length) {
63976                 cls += ' fc-event-end';
63977             }
63978             
63979             //Roo.log(ev.data);
63980             // how many rows should it span..
63981             var cg = this.eventTmpl.append(ctr,Roo.apply({
63982                 fccls : cls
63983                 
63984             }, ev.data) , true);
63985             
63986             
63987             cg.on('mouseenter' ,this.onEventEnter, this, ev);
63988             cg.on('mouseleave' ,this.onEventLeave, this, ev);
63989             cg.on('click', this.onEventClick, this, ev);
63990             
63991             ev.els.push(cg);
63992             
63993             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63994             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63995             //Roo.log(cg);
63996              
63997             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
63998             cg.setWidth(ebox.right - sbox.x -2);
63999         }
64000     },
64001     
64002     renderEvents: function()
64003     {   
64004         // first make sure there is enough space..
64005         
64006         if (!this.eventTmpl) {
64007             this.eventTmpl = new Roo.Template(
64008                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
64009                     '<div class="fc-event-inner">' +
64010                         '<span class="fc-event-time">{time}</span>' +
64011                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
64012                     '</div>' +
64013                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
64014                 '</div>'
64015             );
64016                 
64017         }
64018                
64019         
64020         
64021         this.cells.each(function(c) {
64022             //Roo.log(c.select('.fc-day-content div',true).first());
64023             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
64024         });
64025         
64026         var ctr = this.view.el.select('.fc-event-container',true).first();
64027         
64028         var cls;
64029         this.eventStore.each(function(ev){
64030             
64031             this.renderEvent(ev);
64032              
64033              
64034         }, this);
64035         this.view.layout();
64036         
64037     },
64038     
64039     onEventEnter: function (e, el,event,d) {
64040         this.fireEvent('evententer', this, el, event);
64041     },
64042     
64043     onEventLeave: function (e, el,event,d) {
64044         this.fireEvent('eventleave', this, el, event);
64045     },
64046     
64047     onEventClick: function (e, el,event,d) {
64048         this.fireEvent('eventclick', this, el, event);
64049     },
64050     
64051     onMonthChange: function () {
64052         this.store.load();
64053     },
64054     
64055     onLoad: function () {
64056         
64057         //Roo.log('calendar onload');
64058 //         
64059         if(this.eventStore.getCount() > 0){
64060             
64061            
64062             
64063             this.eventStore.each(function(d){
64064                 
64065                 
64066                 // FIXME..
64067                 var add =   d.data;
64068                 if (typeof(add.end_dt) == 'undefined')  {
64069                     Roo.log("Missing End time in calendar data: ");
64070                     Roo.log(d);
64071                     return;
64072                 }
64073                 if (typeof(add.start_dt) == 'undefined')  {
64074                     Roo.log("Missing Start time in calendar data: ");
64075                     Roo.log(d);
64076                     return;
64077                 }
64078                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
64079                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
64080                 add.id = add.id || d.id;
64081                 add.title = add.title || '??';
64082                 
64083                 this.addItem(d);
64084                 
64085              
64086             },this);
64087         }
64088         
64089         this.renderEvents();
64090     }
64091     
64092
64093 });
64094 /*
64095  grid : {
64096                 xtype: 'Grid',
64097                 xns: Roo.grid,
64098                 listeners : {
64099                     render : function ()
64100                     {
64101                         _this.grid = this;
64102                         
64103                         if (!this.view.el.hasClass('course-timesheet')) {
64104                             this.view.el.addClass('course-timesheet');
64105                         }
64106                         if (this.tsStyle) {
64107                             this.ds.load({});
64108                             return; 
64109                         }
64110                         Roo.log('width');
64111                         Roo.log(_this.grid.view.el.getWidth());
64112                         
64113                         
64114                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
64115                             '.course-timesheet .x-grid-row' : {
64116                                 height: '80px'
64117                             },
64118                             '.x-grid-row td' : {
64119                                 'vertical-align' : 0
64120                             },
64121                             '.course-edit-link' : {
64122                                 'color' : 'blue',
64123                                 'text-overflow' : 'ellipsis',
64124                                 'overflow' : 'hidden',
64125                                 'white-space' : 'nowrap',
64126                                 'cursor' : 'pointer'
64127                             },
64128                             '.sub-link' : {
64129                                 'color' : 'green'
64130                             },
64131                             '.de-act-sup-link' : {
64132                                 'color' : 'purple',
64133                                 'text-decoration' : 'line-through'
64134                             },
64135                             '.de-act-link' : {
64136                                 'color' : 'red',
64137                                 'text-decoration' : 'line-through'
64138                             },
64139                             '.course-timesheet .course-highlight' : {
64140                                 'border-top-style': 'dashed !important',
64141                                 'border-bottom-bottom': 'dashed !important'
64142                             },
64143                             '.course-timesheet .course-item' : {
64144                                 'font-family'   : 'tahoma, arial, helvetica',
64145                                 'font-size'     : '11px',
64146                                 'overflow'      : 'hidden',
64147                                 'padding-left'  : '10px',
64148                                 'padding-right' : '10px',
64149                                 'padding-top' : '10px' 
64150                             }
64151                             
64152                         }, Roo.id());
64153                                 this.ds.load({});
64154                     }
64155                 },
64156                 autoWidth : true,
64157                 monitorWindowResize : false,
64158                 cellrenderer : function(v,x,r)
64159                 {
64160                     return v;
64161                 },
64162                 sm : {
64163                     xtype: 'CellSelectionModel',
64164                     xns: Roo.grid
64165                 },
64166                 dataSource : {
64167                     xtype: 'Store',
64168                     xns: Roo.data,
64169                     listeners : {
64170                         beforeload : function (_self, options)
64171                         {
64172                             options.params = options.params || {};
64173                             options.params._month = _this.monthField.getValue();
64174                             options.params.limit = 9999;
64175                             options.params['sort'] = 'when_dt';    
64176                             options.params['dir'] = 'ASC';    
64177                             this.proxy.loadResponse = this.loadResponse;
64178                             Roo.log("load?");
64179                             //this.addColumns();
64180                         },
64181                         load : function (_self, records, options)
64182                         {
64183                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
64184                                 // if you click on the translation.. you can edit it...
64185                                 var el = Roo.get(this);
64186                                 var id = el.dom.getAttribute('data-id');
64187                                 var d = el.dom.getAttribute('data-date');
64188                                 var t = el.dom.getAttribute('data-time');
64189                                 //var id = this.child('span').dom.textContent;
64190                                 
64191                                 //Roo.log(this);
64192                                 Pman.Dialog.CourseCalendar.show({
64193                                     id : id,
64194                                     when_d : d,
64195                                     when_t : t,
64196                                     productitem_active : id ? 1 : 0
64197                                 }, function() {
64198                                     _this.grid.ds.load({});
64199                                 });
64200                            
64201                            });
64202                            
64203                            _this.panel.fireEvent('resize', [ '', '' ]);
64204                         }
64205                     },
64206                     loadResponse : function(o, success, response){
64207                             // this is overridden on before load..
64208                             
64209                             Roo.log("our code?");       
64210                             //Roo.log(success);
64211                             //Roo.log(response)
64212                             delete this.activeRequest;
64213                             if(!success){
64214                                 this.fireEvent("loadexception", this, o, response);
64215                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64216                                 return;
64217                             }
64218                             var result;
64219                             try {
64220                                 result = o.reader.read(response);
64221                             }catch(e){
64222                                 Roo.log("load exception?");
64223                                 this.fireEvent("loadexception", this, o, response, e);
64224                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64225                                 return;
64226                             }
64227                             Roo.log("ready...");        
64228                             // loop through result.records;
64229                             // and set this.tdate[date] = [] << array of records..
64230                             _this.tdata  = {};
64231                             Roo.each(result.records, function(r){
64232                                 //Roo.log(r.data);
64233                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
64234                                     _this.tdata[r.data.when_dt.format('j')] = [];
64235                                 }
64236                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
64237                             });
64238                             
64239                             //Roo.log(_this.tdata);
64240                             
64241                             result.records = [];
64242                             result.totalRecords = 6;
64243                     
64244                             // let's generate some duumy records for the rows.
64245                             //var st = _this.dateField.getValue();
64246                             
64247                             // work out monday..
64248                             //st = st.add(Date.DAY, -1 * st.format('w'));
64249                             
64250                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64251                             
64252                             var firstOfMonth = date.getFirstDayOfMonth();
64253                             var days = date.getDaysInMonth();
64254                             var d = 1;
64255                             var firstAdded = false;
64256                             for (var i = 0; i < result.totalRecords ; i++) {
64257                                 //var d= st.add(Date.DAY, i);
64258                                 var row = {};
64259                                 var added = 0;
64260                                 for(var w = 0 ; w < 7 ; w++){
64261                                     if(!firstAdded && firstOfMonth != w){
64262                                         continue;
64263                                     }
64264                                     if(d > days){
64265                                         continue;
64266                                     }
64267                                     firstAdded = true;
64268                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
64269                                     row['weekday'+w] = String.format(
64270                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
64271                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
64272                                                     d,
64273                                                     date.format('Y-m-')+dd
64274                                                 );
64275                                     added++;
64276                                     if(typeof(_this.tdata[d]) != 'undefined'){
64277                                         Roo.each(_this.tdata[d], function(r){
64278                                             var is_sub = '';
64279                                             var deactive = '';
64280                                             var id = r.id;
64281                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
64282                                             if(r.parent_id*1>0){
64283                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
64284                                                 id = r.parent_id;
64285                                             }
64286                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
64287                                                 deactive = 'de-act-link';
64288                                             }
64289                                             
64290                                             row['weekday'+w] += String.format(
64291                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
64292                                                     id, //0
64293                                                     r.product_id_name, //1
64294                                                     r.when_dt.format('h:ia'), //2
64295                                                     is_sub, //3
64296                                                     deactive, //4
64297                                                     desc // 5
64298                                             );
64299                                         });
64300                                     }
64301                                     d++;
64302                                 }
64303                                 
64304                                 // only do this if something added..
64305                                 if(added > 0){ 
64306                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
64307                                 }
64308                                 
64309                                 
64310                                 // push it twice. (second one with an hour..
64311                                 
64312                             }
64313                             //Roo.log(result);
64314                             this.fireEvent("load", this, o, o.request.arg);
64315                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
64316                         },
64317                     sortInfo : {field: 'when_dt', direction : 'ASC' },
64318                     proxy : {
64319                         xtype: 'HttpProxy',
64320                         xns: Roo.data,
64321                         method : 'GET',
64322                         url : baseURL + '/Roo/Shop_course.php'
64323                     },
64324                     reader : {
64325                         xtype: 'JsonReader',
64326                         xns: Roo.data,
64327                         id : 'id',
64328                         fields : [
64329                             {
64330                                 'name': 'id',
64331                                 'type': 'int'
64332                             },
64333                             {
64334                                 'name': 'when_dt',
64335                                 'type': 'string'
64336                             },
64337                             {
64338                                 'name': 'end_dt',
64339                                 'type': 'string'
64340                             },
64341                             {
64342                                 'name': 'parent_id',
64343                                 'type': 'int'
64344                             },
64345                             {
64346                                 'name': 'product_id',
64347                                 'type': 'int'
64348                             },
64349                             {
64350                                 'name': 'productitem_id',
64351                                 'type': 'int'
64352                             },
64353                             {
64354                                 'name': 'guid',
64355                                 'type': 'int'
64356                             }
64357                         ]
64358                     }
64359                 },
64360                 toolbar : {
64361                     xtype: 'Toolbar',
64362                     xns: Roo,
64363                     items : [
64364                         {
64365                             xtype: 'Button',
64366                             xns: Roo.Toolbar,
64367                             listeners : {
64368                                 click : function (_self, e)
64369                                 {
64370                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64371                                     sd.setMonth(sd.getMonth()-1);
64372                                     _this.monthField.setValue(sd.format('Y-m-d'));
64373                                     _this.grid.ds.load({});
64374                                 }
64375                             },
64376                             text : "Back"
64377                         },
64378                         {
64379                             xtype: 'Separator',
64380                             xns: Roo.Toolbar
64381                         },
64382                         {
64383                             xtype: 'MonthField',
64384                             xns: Roo.form,
64385                             listeners : {
64386                                 render : function (_self)
64387                                 {
64388                                     _this.monthField = _self;
64389                                    // _this.monthField.set  today
64390                                 },
64391                                 select : function (combo, date)
64392                                 {
64393                                     _this.grid.ds.load({});
64394                                 }
64395                             },
64396                             value : (function() { return new Date(); })()
64397                         },
64398                         {
64399                             xtype: 'Separator',
64400                             xns: Roo.Toolbar
64401                         },
64402                         {
64403                             xtype: 'TextItem',
64404                             xns: Roo.Toolbar,
64405                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
64406                         },
64407                         {
64408                             xtype: 'Fill',
64409                             xns: Roo.Toolbar
64410                         },
64411                         {
64412                             xtype: 'Button',
64413                             xns: Roo.Toolbar,
64414                             listeners : {
64415                                 click : function (_self, e)
64416                                 {
64417                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64418                                     sd.setMonth(sd.getMonth()+1);
64419                                     _this.monthField.setValue(sd.format('Y-m-d'));
64420                                     _this.grid.ds.load({});
64421                                 }
64422                             },
64423                             text : "Next"
64424                         }
64425                     ]
64426                 },
64427                  
64428             }
64429         };
64430         
64431         *//*
64432  * Based on:
64433  * Ext JS Library 1.1.1
64434  * Copyright(c) 2006-2007, Ext JS, LLC.
64435  *
64436  * Originally Released Under LGPL - original licence link has changed is not relivant.
64437  *
64438  * Fork - LGPL
64439  * <script type="text/javascript">
64440  */
64441  
64442 /**
64443  * @class Roo.LoadMask
64444  * A simple utility class for generically masking elements while loading data.  If the element being masked has
64445  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
64446  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
64447  * element's UpdateManager load indicator and will be destroyed after the initial load.
64448  * @constructor
64449  * Create a new LoadMask
64450  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
64451  * @param {Object} config The config object
64452  */
64453 Roo.LoadMask = function(el, config){
64454     this.el = Roo.get(el);
64455     Roo.apply(this, config);
64456     if(this.store){
64457         this.store.on('beforeload', this.onBeforeLoad, this);
64458         this.store.on('load', this.onLoad, this);
64459         this.store.on('loadexception', this.onLoadException, this);
64460         this.removeMask = false;
64461     }else{
64462         var um = this.el.getUpdateManager();
64463         um.showLoadIndicator = false; // disable the default indicator
64464         um.on('beforeupdate', this.onBeforeLoad, this);
64465         um.on('update', this.onLoad, this);
64466         um.on('failure', this.onLoad, this);
64467         this.removeMask = true;
64468     }
64469 };
64470
64471 Roo.LoadMask.prototype = {
64472     /**
64473      * @cfg {Boolean} removeMask
64474      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
64475      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
64476      */
64477     /**
64478      * @cfg {String} msg
64479      * The text to display in a centered loading message box (defaults to 'Loading...')
64480      */
64481     msg : 'Loading...',
64482     /**
64483      * @cfg {String} msgCls
64484      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
64485      */
64486     msgCls : 'x-mask-loading',
64487
64488     /**
64489      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
64490      * @type Boolean
64491      */
64492     disabled: false,
64493
64494     /**
64495      * Disables the mask to prevent it from being displayed
64496      */
64497     disable : function(){
64498        this.disabled = true;
64499     },
64500
64501     /**
64502      * Enables the mask so that it can be displayed
64503      */
64504     enable : function(){
64505         this.disabled = false;
64506     },
64507     
64508     onLoadException : function()
64509     {
64510         Roo.log(arguments);
64511         
64512         if (typeof(arguments[3]) != 'undefined') {
64513             Roo.MessageBox.alert("Error loading",arguments[3]);
64514         } 
64515         /*
64516         try {
64517             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
64518                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
64519             }   
64520         } catch(e) {
64521             
64522         }
64523         */
64524     
64525         
64526         
64527         this.el.unmask(this.removeMask);
64528     },
64529     // private
64530     onLoad : function()
64531     {
64532         this.el.unmask(this.removeMask);
64533     },
64534
64535     // private
64536     onBeforeLoad : function(){
64537         if(!this.disabled){
64538             this.el.mask(this.msg, this.msgCls);
64539         }
64540     },
64541
64542     // private
64543     destroy : function(){
64544         if(this.store){
64545             this.store.un('beforeload', this.onBeforeLoad, this);
64546             this.store.un('load', this.onLoad, this);
64547             this.store.un('loadexception', this.onLoadException, this);
64548         }else{
64549             var um = this.el.getUpdateManager();
64550             um.un('beforeupdate', this.onBeforeLoad, this);
64551             um.un('update', this.onLoad, this);
64552             um.un('failure', this.onLoad, this);
64553         }
64554     }
64555 };/*
64556  * Based on:
64557  * Ext JS Library 1.1.1
64558  * Copyright(c) 2006-2007, Ext JS, LLC.
64559  *
64560  * Originally Released Under LGPL - original licence link has changed is not relivant.
64561  *
64562  * Fork - LGPL
64563  * <script type="text/javascript">
64564  */
64565
64566
64567 /**
64568  * @class Roo.XTemplate
64569  * @extends Roo.Template
64570  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64571 <pre><code>
64572 var t = new Roo.XTemplate(
64573         '&lt;select name="{name}"&gt;',
64574                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
64575         '&lt;/select&gt;'
64576 );
64577  
64578 // then append, applying the master template values
64579  </code></pre>
64580  *
64581  * Supported features:
64582  *
64583  *  Tags:
64584
64585 <pre><code>
64586       {a_variable} - output encoded.
64587       {a_variable.format:("Y-m-d")} - call a method on the variable
64588       {a_variable:raw} - unencoded output
64589       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64590       {a_variable:this.method_on_template(...)} - call a method on the template object.
64591  
64592 </code></pre>
64593  *  The tpl tag:
64594 <pre><code>
64595         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
64596         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
64597         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
64598         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
64599   
64600         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
64601         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
64602 </code></pre>
64603  *      
64604  */
64605 Roo.XTemplate = function()
64606 {
64607     Roo.XTemplate.superclass.constructor.apply(this, arguments);
64608     if (this.html) {
64609         this.compile();
64610     }
64611 };
64612
64613
64614 Roo.extend(Roo.XTemplate, Roo.Template, {
64615
64616     /**
64617      * The various sub templates
64618      */
64619     tpls : false,
64620     /**
64621      *
64622      * basic tag replacing syntax
64623      * WORD:WORD()
64624      *
64625      * // you can fake an object call by doing this
64626      *  x.t:(test,tesT) 
64627      * 
64628      */
64629     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64630
64631     /**
64632      * compile the template
64633      *
64634      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64635      *
64636      */
64637     compile: function()
64638     {
64639         var s = this.html;
64640      
64641         s = ['<tpl>', s, '</tpl>'].join('');
64642     
64643         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64644             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64645             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
64646             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64647             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
64648             m,
64649             id     = 0,
64650             tpls   = [];
64651     
64652         while(true == !!(m = s.match(re))){
64653             var forMatch   = m[0].match(nameRe),
64654                 ifMatch   = m[0].match(ifRe),
64655                 execMatch   = m[0].match(execRe),
64656                 namedMatch   = m[0].match(namedRe),
64657                 
64658                 exp  = null, 
64659                 fn   = null,
64660                 exec = null,
64661                 name = forMatch && forMatch[1] ? forMatch[1] : '';
64662                 
64663             if (ifMatch) {
64664                 // if - puts fn into test..
64665                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64666                 if(exp){
64667                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64668                 }
64669             }
64670             
64671             if (execMatch) {
64672                 // exec - calls a function... returns empty if true is  returned.
64673                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64674                 if(exp){
64675                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64676                 }
64677             }
64678             
64679             
64680             if (name) {
64681                 // for = 
64682                 switch(name){
64683                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64684                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64685                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64686                 }
64687             }
64688             var uid = namedMatch ? namedMatch[1] : id;
64689             
64690             
64691             tpls.push({
64692                 id:     namedMatch ? namedMatch[1] : id,
64693                 target: name,
64694                 exec:   exec,
64695                 test:   fn,
64696                 body:   m[1] || ''
64697             });
64698             if (namedMatch) {
64699                 s = s.replace(m[0], '');
64700             } else { 
64701                 s = s.replace(m[0], '{xtpl'+ id + '}');
64702             }
64703             ++id;
64704         }
64705         this.tpls = [];
64706         for(var i = tpls.length-1; i >= 0; --i){
64707             this.compileTpl(tpls[i]);
64708             this.tpls[tpls[i].id] = tpls[i];
64709         }
64710         this.master = tpls[tpls.length-1];
64711         return this;
64712     },
64713     /**
64714      * same as applyTemplate, except it's done to one of the subTemplates
64715      * when using named templates, you can do:
64716      *
64717      * var str = pl.applySubTemplate('your-name', values);
64718      *
64719      * 
64720      * @param {Number} id of the template
64721      * @param {Object} values to apply to template
64722      * @param {Object} parent (normaly the instance of this object)
64723      */
64724     applySubTemplate : function(id, values, parent)
64725     {
64726         
64727         
64728         var t = this.tpls[id];
64729         
64730         
64731         try { 
64732             if(t.test && !t.test.call(this, values, parent)){
64733                 return '';
64734             }
64735         } catch(e) {
64736             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64737             Roo.log(e.toString());
64738             Roo.log(t.test);
64739             return ''
64740         }
64741         try { 
64742             
64743             if(t.exec && t.exec.call(this, values, parent)){
64744                 return '';
64745             }
64746         } catch(e) {
64747             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64748             Roo.log(e.toString());
64749             Roo.log(t.exec);
64750             return ''
64751         }
64752         try {
64753             var vs = t.target ? t.target.call(this, values, parent) : values;
64754             parent = t.target ? values : parent;
64755             if(t.target && vs instanceof Array){
64756                 var buf = [];
64757                 for(var i = 0, len = vs.length; i < len; i++){
64758                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
64759                 }
64760                 return buf.join('');
64761             }
64762             return t.compiled.call(this, vs, parent);
64763         } catch (e) {
64764             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64765             Roo.log(e.toString());
64766             Roo.log(t.compiled);
64767             return '';
64768         }
64769     },
64770
64771     compileTpl : function(tpl)
64772     {
64773         var fm = Roo.util.Format;
64774         var useF = this.disableFormats !== true;
64775         var sep = Roo.isGecko ? "+" : ",";
64776         var undef = function(str) {
64777             Roo.log("Property not found :"  + str);
64778             return '';
64779         };
64780         
64781         var fn = function(m, name, format, args)
64782         {
64783             //Roo.log(arguments);
64784             args = args ? args.replace(/\\'/g,"'") : args;
64785             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64786             if (typeof(format) == 'undefined') {
64787                 format= 'htmlEncode';
64788             }
64789             if (format == 'raw' ) {
64790                 format = false;
64791             }
64792             
64793             if(name.substr(0, 4) == 'xtpl'){
64794                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64795             }
64796             
64797             // build an array of options to determine if value is undefined..
64798             
64799             // basically get 'xxxx.yyyy' then do
64800             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64801             //    (function () { Roo.log("Property not found"); return ''; })() :
64802             //    ......
64803             
64804             var udef_ar = [];
64805             var lookfor = '';
64806             Roo.each(name.split('.'), function(st) {
64807                 lookfor += (lookfor.length ? '.': '') + st;
64808                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
64809             });
64810             
64811             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64812             
64813             
64814             if(format && useF){
64815                 
64816                 args = args ? ',' + args : "";
64817                  
64818                 if(format.substr(0, 5) != "this."){
64819                     format = "fm." + format + '(';
64820                 }else{
64821                     format = 'this.call("'+ format.substr(5) + '", ';
64822                     args = ", values";
64823                 }
64824                 
64825                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
64826             }
64827              
64828             if (args.length) {
64829                 // called with xxyx.yuu:(test,test)
64830                 // change to ()
64831                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
64832             }
64833             // raw.. - :raw modifier..
64834             return "'"+ sep + udef_st  + name + ")"+sep+"'";
64835             
64836         };
64837         var body;
64838         // branched to use + in gecko and [].join() in others
64839         if(Roo.isGecko){
64840             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
64841                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64842                     "';};};";
64843         }else{
64844             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
64845             body.push(tpl.body.replace(/(\r\n|\n)/g,
64846                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64847             body.push("'].join('');};};");
64848             body = body.join('');
64849         }
64850         
64851         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64852        
64853         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
64854         eval(body);
64855         
64856         return this;
64857     },
64858
64859     applyTemplate : function(values){
64860         return this.master.compiled.call(this, values, {});
64861         //var s = this.subs;
64862     },
64863
64864     apply : function(){
64865         return this.applyTemplate.apply(this, arguments);
64866     }
64867
64868  });
64869
64870 Roo.XTemplate.from = function(el){
64871     el = Roo.getDom(el);
64872     return new Roo.XTemplate(el.value || el.innerHTML);
64873 };