From f82e75e8919cfd98b30c1f05a02598636f634cf6 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 24 Sep 2015 11:10:02 +0200 Subject: [PATCH 1/3] new embed framework - WIP --- lsp/newembed.js | 381 +++++++++++++++++++++++++++++++++++++++++ lsp/test_newembed.html | 49 ++++++ 2 files changed, 430 insertions(+) create mode 100644 lsp/newembed.js create mode 100644 lsp/test_newembed.html diff --git a/lsp/newembed.js b/lsp/newembed.js new file mode 100644 index 00000000..f3c79d2e --- /dev/null +++ b/lsp/newembed.js @@ -0,0 +1,381 @@ +if (typeof mistplayers == 'undefined') { + //no need to define this if it's already there + + var mistplayers = {}; + + //create prototype with empty functions the player should have, so that any that have not been defined return false + function MistPlayer() { + + //return true if this player is to be used for this mimetype + this.formimetype = function (fullmimetype){ return false; }; + + this.name = 'Generic player'; + + //mimetype: e.g. 'video/mp4'; that is withouth the html5, flash, or whatever first part. + this.supported = function(mimetype){ return false; }; + + /* + shape of build options: + { + container: html DOM element, + width: (int) video width, + height: (int) video height, + name: (string) the stream name, + src: (string) the video source string, + mimetype: (string) the mimetype, + islive: (boolean) whether or not this is a live video, + autoplay: (boolean) whether or not to enable autoplay + } + */ + this.build = function(options){ return false; }; + + //creates the player element, including custom functions + this.element = function(tag){ + var ele = document.createElement(tag); + ele.mistplay = function(){ return false; }; + ele.mistpause = function(){ return false; }; + + //(double) value: 0 for muted, 1 for max + ele.mistsetvolume = function(value){ return false; }; + //return the current position, in seconds + ele.mistposition = function(){ return false; }; + + return ele; + }; + } + + /////////////////////////////////////////////////// + //ADD AVAILABLE PLAYERS TO THE MISTPLAYERS OBJECT// + /////////////////////////////////////////////////// + + ////HTML5//////////////////////////////////////////// + var html5 = new MistPlayer(); + mistplayers.html5 = html5; + html5.name = 'HTML5 video element'; + html5.formimetype = function(fullmimetype){ + var t = fullmimetype.split('/'); + return ((t[0] == 'html5') && (t[1] == 'video')); + }; + html5.supported = function(mimetype) { + var support = false; + try { + var v = document.createElement('video'); + if ((v) && (v.canPlayType(mimetype) != "")) { + support = v.canPlayType(mimetype); + } + } catch(e){} + return support; + } + html5.build = function(options){ + var ele = this.element('video'); + + ele.setAttribute('width',options.width); + ele.setAttribute('height',options.height); + ele.setAttribute('src',encodeURI(options.src)); + ele.setAttribute('controls','controls'); + + if (options.autoplay) { + ele.setAttribute('autoplay','controls'); + } + + ele.mistplay = function(){ + this.play(); + }; + ele.mistpause = function(){ + this.pause(); + }; + ele.mistsetvolume = function(value){ + this.volume = value; + }; + ele.mistposition = function(){ + return this.currentTime; + } + + options.container.appendChild(ele); + return ele; + } + + ////FLASH//////////////////////////////////////////// + var flash = new MistPlayer(); + mistplayers.flash = flash; + flash.name = 'Flash object'; + flash.formimetype = function(fullmimetype){ + var t = fullmimetype.split('/'); + return (t[0] == 'flash'); + }; + flash.supported = function(mimetype) { + + var version = 0; + try { + // check in the mimeTypes + version = navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin.description.replace(/([^0-9\.])/g, '').split('.')[0]; + } catch(e){} + try { + // for our special friend IE + version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable("$version").replace(/([^0-9\,])/g, '').split(',')[0]; + } catch(e){} + version = parseInt(version); + + return version >= parseInt(mimetype); + } + flash.build = function(options){ + var ele = this.element('object'); + ele.setAttribute('id',options.name); + ele.setAttribute('width',options.width); + ele.setAttribute('height',options.height); + + //set flashvars + var flashvars = { + src: encodeURIComponent(options.src), + controlBarMode: 'floating', + initialBufferTime: 5, + expandedBufferTime: 5, + minContinuousPlaybackTime: 3 + }; + //set param elements + var params = { + movie: 'http://fpdownload.adobe.com/strobe/FlashMediaPlayback.swf', + flashvars: [], + allowFullScreen: 'true', + allowscriptaccess: 'always', + wmode: 'direct' + }; + if (options.autoplay) { + params.autoPlay = 'true'; + flashvars.autoPlay = 'true'; + } + if (options.islive) { + flashvars.streamType = 'live'; + } + if (parseInt(options.mimetype) >= 10) { + params.movie = 'http://fpdownload.adobe.com/strobe/FlashMediaPlayback_101.swf'; + } + for (var i in flashvars) { + params.flashvars.push(i+'='+flashvars[i]); + } + params.flashvars = params.flashvars.join('&'); + for (var i in params) { + var param = document.createElement('param'); + ele.appendChild(param); + param.setAttribute('name',i); + param.setAttribute('value',params[i]); + } + + var embed = document.createElement('embed'); + embed.setAttribute('name',options.name); + embed.setAttribute('src',params.movie); + embed.setAttribute('type','application/x-shockwave-flash'); + embed.setAttribute('allowscriptaccess','always'); + embed.setAttribute('allowfullscreen','true'); + embed.setAttribute('width',options.width); + embed.setAttribute('height',options.height); + embed.setAttribute('flashvars',params.flashvars); + ele.appendChild(embed); + + options.container.appendChild(ele); + return ele; + } + + ////SILVERLIGHT////////////////////////////////////// + var silverlight = new MistPlayer(); + mistplayers.silverlight = silverlight; + silverlight.name = 'Silverlight'; + silverlight.formimetype = function(fullmimetype){ + return (fullmimetype == 'silverlight'); + }; + silverlight.supported = function(mimetype) { + var plugin; + + try { + // check in the mimeTypes + plugin = navigator.plugins["Silverlight Plug-In"]; + return !!plugin; + } catch(e){} + try { + // for our special friend IE + plugin = new ActiveXObject('AgControl.AgControl'); + return true; + } catch(e){} + + return false; + } + silverlight.build = function(options){ + var ele = this.element('object'); + + ele.setAttribute('data','data:application/x-silverlight,'); //yes that comma needs to be there + ele.setAttribute('type','application/x-silverlight'); + ele.setAttribute('width',options.width); + ele.setAttribute('height',options.height); + + var params = { + source: encodeURI(options.src), + onerror: 'onSilverlightError', + autoUpgrade: 'true', + background: 'black', + enableHtmlAccess: 'true', + minRuntimeVersion: '3.0.40624.0', + initparams: [] + }; + var initparams = { + autoload: 'false', + enablecaptions: 'true', + joinLive: 'true', + muted: 'false', + playlist: document.createElement('playList') + }; + if (options.autoplay) { + initparams.autoplay = 'true'; + } + var playitems = document.createElement('playListItems'); + initparams.playlist.appendChild(playitems); + var playitem = document.createElement('playListItem'); + playitems.appendChild(playitem); + playitems.setAttribute('mediaSource',encodeURI(options.src)); + playitems.setAttribute('adaptiveStreaming','true'); + initparams.playlist = initparams.playlist.outerHTML; + + for (var i in initparams) { + params.initparams.push(i+'='+initparams[i]); + } + params.initparams = params.initparams.join(','); + for (var i in params) { + var param = document.createElement('param'); + ele.appendChild(param); + param.setAttribute('name',i); + param.setAttribute('value',params[i]); + } + + ele.innerHTML += 'Get Microsoft Silverlight'; + + options.container.appendChild(ele); + return ele; + } + +} //end of player definitions + +function mistembed(streamname) { + + function findPlayer(fullmimetype) { + for (var i in mistplayers) { + if (mistplayers[i].formimetype(fullmimetype)) { + return mistplayers[i]; + } + } + return false; + } + + var video = mistvideo[streamname]; + container = document.createElement('div'), + scripts = document.getElementsByTagName('script'), + me = scripts[scripts.length - 1]; + + if (me.parentNode.hasAttribute('data-forcetype')) { + var forceType = me.parentNode.getAttribute('data-forcetype'); + } + if (me.parentNode.hasAttribute('data-forcesupportcheck')) { + var forceSupportCheck = true; + } + if (me.parentNode.hasAttribute('data-autoplay')) { + var autoplay = true; + } + + // create the container + me.parentNode.insertBefore(container, me); + // set the class to 'mistvideo' + container.setAttribute('class', 'mistvideo'); + // remove script tag + me.parentNode.removeChild(me); + + if(video.error) { + // there was an error; display it + container.innerHTML = 'Error: '+video.error+''; + } + else if ((typeof video.source == 'undefined') || (video.source.length < 1)) { + // no stream sources + container.innerHTML = 'Error: no protocols found'; + } + else { + // no error, and sources found. Check the video types and output the best + // available video player. + + var foundPlayer = false; + + for (var i in video.source) { + if ((forceType) && (video.source[i].type.indexOf(forceType) < 0)) { + video.source[i].rejected = 'This source type is not the one being forced.'; + continue; + } + + var player = findPlayer(video.source[i].type); + var shortmime = video.source[i].type.split('/'); + shortmime.shift(); + shortmime = shortmime.join('/'); + video.source[i].browser_support = false; + + if (player) { + var support = player.supported(shortmime); + if ((support) || (forceType)) { + //either the player is supported by the browser, or this source type is being enforced + + video.source[i].browser_support = Boolean(support); + if (foundPlayer === false) { + foundPlayer = { + protocol: video.source[i], + player: player, + shortmime: shortmime + }; + } + if (!forceSupportCheck) { + break; + } + } + else { + video.source[i].rejected = 'The player for this source type ('+player.name+') is not supported by your browser.'; + } + } + else { + video.source[i].rejected = 'No compatible player found for this source type.'; + } + } + } + + if (foundPlayer) { + // we support this kind of video, so build it. + + //calculations for the size + videowidth = video.width || 250; + videoheight = video.height || 250; + var ratio; + var containerwidth = parseInt(container.scrollWidth); + var containerheight = parseInt(container.scrollHeight); + if(videowidth > containerwidth && containerwidth > 0) { + ratio = videowidth / containerwidth; + videowidth /= ratio; + videoheight /= ratio; + } + if(videoheight > containerheight && containerheight > 0) { + ratio = videoheight / containerheight; + videowidth /= ratio; + videoheight /= ratio; + } + + video.embedded = foundPlayer.player.build({ + container: container, + width: videowidth, + height: videoheight, + src: foundPlayer.protocol.url, + name: streamname, + mimetype: foundPlayer.shortmime, + islive: (video.type == 'live'), + autoplay: autoplay + }); + + return foundPlayer.protocol.type; + } + else { + // of all the source types given, none was supported (eg. no flash and HTML5 video). Display error + container.innerHTML = 'No support for any player found'; + } + + return false; +} diff --git a/lsp/test_newembed.html b/lsp/test_newembed.html new file mode 100644 index 00000000..1a9779f6 --- /dev/null +++ b/lsp/test_newembed.html @@ -0,0 +1,49 @@ + + + Test the stream embedding + + + + + + + +
+
+ +
+
+
+ + \ No newline at end of file From f7b4e1c1c38abab7e860cb9b375e0fdf039f0ed2 Mon Sep 17 00:00:00 2001 From: cat Date: Fri, 15 May 2015 14:22:31 +0200 Subject: [PATCH 2/3] LSP - hooks and triggers --- lsp/main.css | 18 ++++ lsp/minified.js | 270 +++++++++++++++++++++++++----------------------- lsp/mist.js | 248 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 396 insertions(+), 140 deletions(-) diff --git a/lsp/main.css b/lsp/main.css index ba9d7aa3..79a59513 100644 --- a/lsp/main.css +++ b/lsp/main.css @@ -512,6 +512,24 @@ th.sorting-desc:after { margin-left: 1em; flex-grow: 1; } +.input_container .field_container .field.checkcontainer { + font-size: 0.8em; + border: 1px solid #bbb; +} +.input_container .field_container .field .controls { + border-bottom: 1px dashed #bbb; +} +.input_container .field_container .field .checklist { + -webkit-column-width: 10em; + -moz-column-width: 10em; + column-width: 10em; + min-height: 0; + max-height: 10em; + overflow-y: auto; +} +.input_container .field_container .field .checklist label { + display: block; +} input[type=radio] { height: 1.1em; width: 1.1em; diff --git a/lsp/minified.js b/lsp/minified.js index a66d9490..d9efe70d 100644 --- a/lsp/minified.js +++ b/lsp/minified.js @@ -1,14 +1,14 @@ -var MD5=function(a){function c(a,c){var b,d,h,i,e;h=a&2147483648;i=c&2147483648;b=a&1073741824;d=c&1073741824;e=(a&1073741823)+(c&1073741823);return b&d?e^2147483648^h^i:b|d?e&1073741824?e^3221225472^h^i:e^1073741824^h^i:e^h^i}function b(a,b,d,h,i,e,g){a=c(a,c(c(b&d|~b&h,i),g));return c(a<>>32-e,b)}function d(a,b,d,h,i,e,g){a=c(a,c(c(b&h|d&~h,i),g));return c(a<>>32-e,b)}function f(a,b,d,h,i,e,g){a=c(a,c(c(b^d^h,i),g));return c(a<>>32-e,b)}function l(a,b,d,h,i,e,g){a=c(a,c(c(d^(b|~h), -i),g));return c(a<>>32-e,b)}function m(a){var b="",c="",d;for(d=0;3>=d;d++)c=a>>>8*d&255,c="0"+c.toString(16),b+=c.substr(c.length-2,2);return b}var e=[],p,n,i,v,h,k,j,g,e=a.replace(/\r\n/g,"\n"),a="";for(p=0;pn?a+=String.fromCharCode(n):(127n?a+=String.fromCharCode(n>>6|192):(a+=String.fromCharCode(n>>12|224),a+=String.fromCharCode(n>>6&63|128)),a+=String.fromCharCode(n&63|128));e=a;a=e.length;p=a+8;n=16*((p-p%64)/64+1);i=Array(n-1);for(h=v=0;h>>29;e=i;h=1732584193;k=4023233417;j=2562383102;g=271733878;for(a=0;ab?1*d:a .menu"),secondary_menu:$("nav.secondary_menu"),main:$("main"),connection:{status:$("#connection"),user_and_host:$("#user_and_host"),msg:$("#message")}};UI.buildMenu();UI.stored.getOpts();if(location.hash){var a=location.hash.substring(1).split("@")[0].split("&");mist.user.name=a[0];a[1]&&(mist.user.host=a[1])}mist.send(function(){$(window).trigger("hashchange")},{},{timeout:5,hide:!0})}); +var MD5=function(a){function b(a,b){var c,d,i,h,e;i=a&2147483648;h=b&2147483648;c=a&1073741824;d=b&1073741824;e=(a&1073741823)+(b&1073741823);return c&d?e^2147483648^i^h:c|d?e&1073741824?e^3221225472^i^h:e^1073741824^i^h:e^i^h}function c(a,c,d,i,h,e,g){a=b(a,b(b(c&d|~c&i,h),g));return b(a<>>32-e,c)}function d(a,c,d,i,h,e,g){a=b(a,b(b(c&i|d&~i,h),g));return b(a<>>32-e,c)}function f(a,c,d,i,h,e,g){a=b(a,b(b(c^d^i,h),g));return b(a<>>32-e,c)}function k(a,c,d,i,h,e,g){a=b(a,b(b(d^(c|~i), +h),g));return b(a<>>32-e,c)}function m(a){var c="",b="",d;for(d=0;3>=d;d++)b=a>>>8*d&255,b="0"+b.toString(16),c+=b.substr(b.length-2,2);return c}var e=[],r,o,i,v,h,l,j,g,e=a.replace(/\r\n/g,"\n"),a="";for(r=0;ro?a+=String.fromCharCode(o):(127o?a+=String.fromCharCode(o>>6|192):(a+=String.fromCharCode(o>>12|224),a+=String.fromCharCode(o>>6&63|128)),a+=String.fromCharCode(o&63|128));e=a;a=e.length;r=a+8;o=16*((r-r%64)/64+1);i=Array(o-1);for(h=v=0;h>>29;e=i;h=1732584193;l=4023233417;j=2562383102;g=271733878;for(a=0;ac?1*d:a .menu"),secondary_menu:$("nav.secondary_menu"),main:$("main"),connection:{status:$("#connection"),user_and_host:$("#user_and_host"),msg:$("#message")}};UI.buildMenu();UI.stored.getOpts();if(location.hash){var a=location.hash.substring(1).split("@")[0].split("&");mist.user.name=a[0];a[1]&&(mist.user.host=a[1])}mist.send(function(){$(window).trigger("hashchange")},{},{timeout:5,hide:!0})}); $(window).on("hashchange",function(){var a=location.hash.substring(1).split("@");a[1]||(a[1]="");a=a[1].split("&");""==a[0]&&(a[0]="Overview");UI.showTab(a[0],a[1])}); -var UI={debug:!1,elements:{},stored:{getOpts:function(){var a=localStorage.stored;a&&(a=JSON.parse(a));$.extend(!0,this.vars,a);return this.vars},saveOpt:function(a,c){this.vars[a]=c;localStorage.stored=JSON.stringify(this.vars);return this.vars},vars:{helpme:!0}},interval:{clear:function(){"undefined"!=typeof this.opts&&(clearInterval(this.opts.id),delete this.opts)},set:function(a,c){this.opts&&log("[interval]","Set called on interval, but an interval is already active.");this.opts={delay:c,callback:a}; -this.opts.id=setInterval(a,c)}},returnTab:["Overview"],countrylist:{AF:"Afghanistan",AX:"Åland Islands",AL:"Albania",DZ:"Algeria",AS:"American Samoa",AD:"Andorra",AO:"Angola",AI:"Anguilla",AQ:"Antarctica",AG:"Antigua and Barbuda",AR:"Argentina",AM:"Armenia",AW:"Aruba",AU:"Australia",AT:"Austria",AZ:"Azerbaijan",BS:"Bahamas",BH:"Bahrain",BD:"Bangladesh",BB:"Barbados",BY:"Belarus",BE:"Belgium",BZ:"Belize",BJ:"Benin",BM:"Bermuda",BT:"Bhutan",BO:"Bolivia, Plurinational State of",BQ:"Bonaire, Sint Eustatius and Saba", +var UI={debug:!1,elements:{},stored:{getOpts:function(){var a=localStorage.stored;a&&(a=JSON.parse(a));$.extend(!0,this.vars,a);return this.vars},saveOpt:function(a,b){this.vars[a]=b;localStorage.stored=JSON.stringify(this.vars);return this.vars},vars:{helpme:!0}},interval:{clear:function(){"undefined"!=typeof this.opts&&(clearInterval(this.opts.id),delete this.opts)},set:function(a,b){this.opts&&log("[interval]","Set called on interval, but an interval is already active.");this.opts={delay:b,callback:a}; +this.opts.id=setInterval(a,b)}},returnTab:["Overview"],countrylist:{AF:"Afghanistan",AX:"Åland Islands",AL:"Albania",DZ:"Algeria",AS:"American Samoa",AD:"Andorra",AO:"Angola",AI:"Anguilla",AQ:"Antarctica",AG:"Antigua and Barbuda",AR:"Argentina",AM:"Armenia",AW:"Aruba",AU:"Australia",AT:"Austria",AZ:"Azerbaijan",BS:"Bahamas",BH:"Bahrain",BD:"Bangladesh",BB:"Barbados",BY:"Belarus",BE:"Belgium",BZ:"Belize",BJ:"Benin",BM:"Bermuda",BT:"Bhutan",BO:"Bolivia, Plurinational State of",BQ:"Bonaire, Sint Eustatius and Saba", BA:"Bosnia and Herzegovina",BW:"Botswana",BV:"Bouvet Island",BR:"Brazil",IO:"British Indian Ocean Territory",BN:"Brunei Darussalam",BG:"Bulgaria",BF:"Burkina Faso",BI:"Burundi",KH:"Cambodia",CM:"Cameroon",CA:"Canada",CV:"Cape Verde",KY:"Cayman Islands",CF:"Central African Republic",TD:"Chad",CL:"Chile",CN:"China",CX:"Christmas Island",CC:"Cocos (Keeling) Islands",CO:"Colombia",KM:"Comoros",CG:"Congo",CD:"Congo, the Democratic Republic of the",CK:"Cook Islands",CR:"Costa Rica",CI:"Côte d'Ivoire", HR:"Croatia",CU:"Cuba",CW:"Curaçao",CY:"Cyprus",CZ:"Czech Republic",DK:"Denmark",DJ:"Djibouti",DM:"Dominica",DO:"Dominican Republic",EC:"Ecuador",EG:"Egypt",SV:"El Salvador",GQ:"Equatorial Guinea",ER:"Eritrea",EE:"Estonia",ET:"Ethiopia",FK:"Falkland Islands (Malvinas)",FO:"Faroe Islands",FJ:"Fiji",FI:"Finland",FR:"France",GF:"French Guiana",PF:"French Polynesia",TF:"French Southern Territories",GA:"Gabon",GM:"Gambia",GE:"Georgia",DE:"Germany",GH:"Ghana",GI:"Gibraltar",GR:"Greece",GL:"Greenland", GD:"Grenada",GP:"Guadeloupe",GU:"Guam",GT:"Guatemala",GG:"Guernsey",GN:"Guinea",GW:"Guinea-Bissau",GY:"Guyana",HT:"Haiti",HM:"Heard Island and McDonald Islands",VA:"Holy See (Vatican City State)",HN:"Honduras",HK:"Hong Kong",HU:"Hungary",IS:"Iceland",IN:"India",ID:"Indonesia",IR:"Iran, Islamic Republic of",IQ:"Iraq",IE:"Ireland",IM:"Isle of Man",IL:"Israel",IT:"Italy",JM:"Jamaica",JP:"Japan",JE:"Jersey",JO:"Jordan",KZ:"Kazakhstan",KE:"Kenya",KI:"Kiribati",KP:"Korea, Democratic People's Republic of", @@ -16,133 +16,141 @@ KR:"Korea, Republic of",KW:"Kuwait",KG:"Kyrgyzstan",LA:"Lao People's Democratic MN:"Mongolia",ME:"Montenegro",MS:"Montserrat",MA:"Morocco",MZ:"Mozambique",MM:"Myanmar",NA:"Namibia",NR:"Nauru",NP:"Nepal",NL:"Netherlands",NC:"New Caledonia",NZ:"New Zealand",NI:"Nicaragua",NE:"Niger",NG:"Nigeria",NU:"Niue",NF:"Norfolk Island",MP:"Northern Mariana Islands",NO:"Norway",OM:"Oman",PK:"Pakistan",PW:"Palau",PS:"Palestine, State of",PA:"Panama",PG:"Papua New Guinea",PY:"Paraguay",PE:"Peru",PH:"Philippines",PN:"Pitcairn",PL:"Poland",PT:"Portugal",PR:"Puerto Rico",QA:"Qatar",RE:"Réunion", RO:"Romania",RU:"Russian Federation",RW:"Rwanda",BL:"Saint Barthélemy",SH:"Saint Helena, Ascension and Tristan da Cunha",KN:"Saint Kitts and Nevis",LC:"Saint Lucia",MF:"Saint Martin (French part)",PM:"Saint Pierre and Miquelon",VC:"Saint Vincent and the Grenadines",WS:"Samoa",SM:"San Marino",ST:"Sao Tome and Principe",SA:"Saudi Arabia",SN:"Senegal",RS:"Serbia",SC:"Seychelles",SL:"Sierra Leone",SG:"Singapore",SX:"Sint Maarten (Dutch part)",SK:"Slovakia",SI:"Slovenia",SB:"Solomon Islands",SO:"Somalia", ZA:"South Africa",GS:"South Georgia and the South Sandwich Islands",SS:"South Sudan",ES:"Spain",LK:"Sri Lanka",SD:"Sudan",SR:"Suriname",SJ:"Svalbard and Jan Mayen",SZ:"Swaziland",SE:"Sweden",CH:"Switzerland",SY:"Syrian Arab Republic",TW:"Taiwan, Province of China",TJ:"Tajikistan",TZ:"Tanzania, United Republic of",TH:"Thailand",TL:"Timor-Leste",TG:"Togo",TK:"Tokelau",TO:"Tonga",TT:"Trinidad and Tobago",TN:"Tunisia",TR:"Turkey",TM:"Turkmenistan",TC:"Turks and Caicos Islands",TV:"Tuvalu",UG:"Uganda", -UA:"Ukraine",AE:"United Arab Emirates",GB:"United Kingdom",US:"United States",UM:"United States Minor Outlying Islands",UY:"Uruguay",UZ:"Uzbekistan",VU:"Vanuatu",VE:"Venezuela, Bolivarian Republic of",VN:"Viet Nam",VG:"Virgin Islands, British",VI:"Virgin Islands, U.S.",WF:"Wallis and Futuna",EH:"Western Sahara",YE:"Yemen",ZM:"Zambia",ZW:"Zimbabwe"},tooltip:{show:function(a,c){$tooltip=this.element;$.contains(document.body,$tooltip[0])||$("body").append($tooltip);$tooltip.html(c);clearTimeout(this.hiding); -delete this.hiding;var b=$(document).height()-$tooltip.outerHeight(),d=$(document).width()-$tooltip.outerWidth();$tooltip.css("left",Math.min(a.pageX+10,d-10));$tooltip.css("top",Math.min(a.pageY+25,b-10));$tooltip.show().addClass("show")},hide:function(){$tooltip=this.element;$tooltip.removeClass("show");this.hiding=setTimeout(function(){$tooltip.hide()},500)},element:$("
").attr("id","tooltip")},popup:{element:null,show:function(a){this.element=$("
").attr("id","popup").append($("