embed.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. // @ts-check
  2. const allowedPrefixes = (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.dataset.allowedPrefixes) ? document.currentScript.dataset.allowedPrefixes.split(' ') : [];
  3. (function () {
  4. 'use strict';
  5. /**
  6. * @param {() => void} loaded
  7. */
  8. var ready = function (loaded) {
  9. if (document.readyState === 'complete') {
  10. loaded();
  11. } else {
  12. document.addEventListener('readystatechange', function () {
  13. if (document.readyState === 'complete') {
  14. loaded();
  15. }
  16. });
  17. }
  18. };
  19. /**
  20. * @param {Map} map
  21. */
  22. var generateId = function (map) {
  23. var id = 0, failCount = 0, idBuffer = new Uint32Array(1);
  24. while (id === 0 || map.has(id)) {
  25. id = crypto.getRandomValues(idBuffer)[0];
  26. failCount++;
  27. if (failCount > 100) {
  28. // give up and assign (easily guessable) unique number if getRandomValues is broken or no luck
  29. id = -(map.size + 1);
  30. break;
  31. }
  32. }
  33. return id;
  34. };
  35. ready(function () {
  36. /** @type {Map<number, HTMLQuoteElement | HTMLIFrameElement>} */
  37. var embeds = new Map();
  38. window.addEventListener('message', function (e) {
  39. var data = e.data || {};
  40. if (typeof data !== 'object' || data.type !== 'setHeight' || !embeds.has(data.id)) {
  41. return;
  42. }
  43. var embed = embeds.get(data.id);
  44. if (embed instanceof HTMLIFrameElement) {
  45. embed.height = data.height;
  46. }
  47. if (embed instanceof HTMLQuoteElement) {
  48. var iframe = embed.querySelector('iframe');
  49. if (!iframe || ('source' in e && iframe.contentWindow !== e.source)) {
  50. return;
  51. }
  52. iframe.height = data.height;
  53. var placeholder = embed.querySelector('a');
  54. if (!placeholder) return;
  55. embed.removeChild(placeholder);
  56. }
  57. });
  58. // Legacy embeds
  59. document.querySelectorAll('iframe.mastodon-embed').forEach(iframe => {
  60. var id = generateId(embeds);
  61. embeds.set(id, iframe);
  62. iframe.allow = 'fullscreen';
  63. iframe.sandbox = 'allow-scripts allow-same-origin allow-popups';
  64. iframe.style.border = 0;
  65. iframe.style.overflow = 'hidden';
  66. iframe.style.display = 'block';
  67. iframe.onload = function () {
  68. iframe.contentWindow.postMessage({
  69. type: 'setHeight',
  70. id: id,
  71. }, '*');
  72. };
  73. iframe.onload(); // In case the script is executing after the iframe has already loaded
  74. });
  75. // New generation of embeds
  76. document.querySelectorAll('blockquote.mastodon-embed').forEach(container => {
  77. var id = generateId(embeds);
  78. embeds.set(id, container);
  79. var iframe = document.createElement('iframe');
  80. var embedUrl = new URL(container.getAttribute('data-embed-url'));
  81. if (embedUrl.protocol !== 'https:' && embedUrl.protocol !== 'http:') return;
  82. if (allowedPrefixes.every((allowedPrefix) => !embedUrl.toString().startsWith(allowedPrefix))) return;
  83. iframe.src = embedUrl.toString();
  84. iframe.width = container.clientWidth;
  85. iframe.height = 0;
  86. iframe.allow = 'fullscreen';
  87. iframe.sandbox = 'allow-scripts allow-same-origin allow-popups';
  88. iframe.style.border = 0;
  89. iframe.style.overflow = 'hidden';
  90. iframe.style.display = 'block';
  91. iframe.onload = function () {
  92. iframe.contentWindow.postMessage({
  93. type: 'setHeight',
  94. id: id,
  95. }, '*');
  96. };
  97. container.appendChild(iframe);
  98. });
  99. });
  100. })();