media-container.js

  1. import { queryOne } from '@ecl/dom-utils';
  2. /**
  3. * @param {HTMLElement} element DOM element for component instantiation and scope
  4. * @param {Object} options
  5. * @param {String} options.iframeSelector Selector for iframe element
  6. * @param {boolean} options.useAutomaticRatio Toggle automatic ratio calculus
  7. * @param {String} options.videoTitleSelector Selector for video title
  8. */
  9. export class MediaContainer {
  10. /**
  11. * @static
  12. * Shorthand for instance creation and initialisation.
  13. *
  14. * @param {HTMLElement} root DOM element for component instantiation and scope
  15. *
  16. * @return {MediaContainer} An instance of MediaContainer.
  17. */
  18. static autoInit(root, { MEDIA_CONTAINER: defaultOptions = {} } = {}) {
  19. const mediaContainer = new MediaContainer(root, defaultOptions);
  20. mediaContainer.init();
  21. root.ECLMediaContainer = mediaContainer;
  22. return mediaContainer;
  23. }
  24. constructor(
  25. element,
  26. {
  27. iframeSelector = 'iframe',
  28. useAutomaticRatio = true,
  29. videoTitleSelector = 'data-ecl-media-container-video-title',
  30. } = {},
  31. ) {
  32. // Check element
  33. if (!element || element.nodeType !== Node.ELEMENT_NODE) {
  34. throw new TypeError(
  35. 'DOM element should be given to initialize this widget.',
  36. );
  37. }
  38. this.element = element;
  39. // Options
  40. this.iframeSelector = iframeSelector;
  41. this.useAutomaticRatio = useAutomaticRatio;
  42. this.videoTitleSelector = videoTitleSelector;
  43. // Private variables
  44. this.iframe = null;
  45. this.videoTitle = '';
  46. // Bind `this` for use in callbacks
  47. this.calculateRatio = this.calculateRatio.bind(this);
  48. this.handleParameters = this.handleParameters.bind(this);
  49. }
  50. /**
  51. * Initialise component.
  52. */
  53. init() {
  54. if (!ECL) {
  55. throw new TypeError('Called init but ECL is not present');
  56. }
  57. ECL.components = ECL.components || new Map();
  58. // Get elements
  59. this.videoTitle = this.element.getAttribute(this.videoTitleSelector);
  60. // Check if a ratio has been set manually
  61. const media = queryOne('.ecl-media-container__media', this.element);
  62. if (media && !/ecl-media-container__media--ratio/.test(media.className)) {
  63. // Get the iframe
  64. this.iframe = queryOne(this.iframeSelector, this.element);
  65. // Check if there is an iframe to handle
  66. if (this.iframe) {
  67. this.handleParameters();
  68. if (this.useAutomaticRatio) this.calculateRatio();
  69. }
  70. }
  71. // Set ecl initialized attribute
  72. this.element.setAttribute('data-ecl-auto-initialized', 'true');
  73. ECL.components.set(this.element, this);
  74. }
  75. /**
  76. * Destroy component.
  77. */
  78. destroy() {
  79. if (this.element) {
  80. this.element.removeAttribute('data-ecl-auto-initialized');
  81. ECL.components.delete(this.element);
  82. }
  83. }
  84. /**
  85. * Handle the parameters of the iframe video.
  86. */
  87. handleParameters() {
  88. const iframeUrl = new URL(this.iframe.src);
  89. // Youtube
  90. if (iframeUrl.host.includes('youtube')) {
  91. this.iframe.src = iframeUrl;
  92. }
  93. // Update iframe title
  94. if (this.videoTitle) {
  95. this.iframe.setAttribute('title', this.videoTitle);
  96. }
  97. }
  98. /**
  99. * Calculate the ratio of the iframe video.
  100. */
  101. calculateRatio() {
  102. // Get dimension if they are provided
  103. let iframeWidth = this.iframe.width;
  104. let iframeHeight = this.iframe.height;
  105. // If at least one dimension in not provided, calculate them (less reliable)
  106. if (!iframeWidth || !iframeHeight) {
  107. iframeWidth = this.iframe.offsetWidth;
  108. iframeHeight = this.iframe.offsetHeight;
  109. }
  110. // Set aspect ratio
  111. this.iframe.style.aspectRatio = `${iframeWidth}/${iframeHeight}`;
  112. }
  113. }
  114. export default MediaContainer;