Home Reference Source

src/formats/interscroller.js

import { cpexLog, cpexError, addIframe, isMobile } from '../utils.js'

/**
 * HTML creative with fixed position relative to scrolling, clipped within a rectangle
 */
export default class Interscroller {
  constructor (elementId, adUnit, main, width, height) {
    this.main = main
    this.type = 'interscroller'
    this.adUnit = adUnit
    this.elementId = elementId
    this.settings = main.settings.formats.interscroller
    this.width = width
    this.height = height
    this.loaded = false
    this.iframeCSS = `width: ${this.width}px; height: ${this.height}px` // Needed for HB reRender
    this.offset = this.settings.offset || 0
    window.addEventListener('resize', () => { this.resize() })
    cpexLog('Interscroller: Caught Interscroller custom format, in elementId ' + elementId)
  }

  /**
   * Resizes wrapper to cover full viewport width
   */
  resize () {
    this.element.style.height = this.finalHeight
    if (this.wrapper) {
      // Center wrapper based on element's position
      const elementRect = this.element.getBoundingClientRect()
      this.wrapper.style.setProperty('margin-left', `${-elementRect.left}px`, 'important')

      // Zoom iframe
      const horizontalAspect = document.documentElement.clientWidth / this.width
      const verticalAspect = document.documentElement.clientHeight / this.height
      const largestAspect = verticalAspect < horizontalAspect ? verticalAspect : horizontalAspect
      this.iframe.style.transform = `scale(${largestAspect}) translateY(${this.offset}px)`

      // Center iframe
      const wrapperRect = this.wrapper.getBoundingClientRect()
      const leftOffset = (wrapperRect.width - this.width) / 2
      this.iframe.style.left = leftOffset + 'px'
    }
  }

  /**
   * Places the interscroller container into the page
   */
  renderIframe () {
    // Add CSS rule
    const style = document.createElement('style')
    document.head.appendChild(style)
    style.sheet.insertRule('@keyframes ios-clip-hack { from { top: 0 } to { top: 0.01px }; }')
    style.sheet.insertRule('div.cpex-interscroller-wrapper { position: absolute; top: 0px; left: 0px; width: 100vw; height: 100%; clip: rect(0px, auto, auto, 0px); }')
    style.sheet.insertRule('iframe.cpex-interscroller-iframe { position: fixed; top: 0px; margin: auto; display: block; border: none; animation: ios-clip-hack .1s infinite; max-width: initial; }')

    // Set interscroller height on the target element
    this.element = document.getElementById(this.elementId)
    if (this.element) {
      this.originalHeight = this.element.style.height
      this.finalHeight = typeof this.settings !== 'undefined' && this.settings.height ? this.settings.height : '75vh'
      this.element.style.position = 'relative'
      this.element.style.height = this.finalHeight

      // Add wrapper element
      this.wrapper = document.createElement('div')
      this.wrapper.classList.add('cpex-interscroller-wrapper')
      this.element.appendChild(this.wrapper)

      // Add ad iframe
      this.iframe = addIframe(this.wrapper, { id: this.elementId + '-iframe' }, (iframe) => { // onload callback
        this.loaded = true
        cpexLog('Interscroller: Rendered into ', this.elementId)
      })
      this.iframe.classList.add('cpex-interscroller-iframe')
      this.iframe.style.cssText = this.iframeCSS
      if (isMobile) { this.iframe.style.transformOrigin = '50% 0' } // there is a weird top offset on mobile devices otherwise
      setTimeout(() => { // giving some time to the page element to render, in case it's injected into the page
        this.resize()
      }, 0)
      return this.iframe
    } else {
      cpexError(`Interscroller: Target element '${this.elementId}' not found`)
    }
  }

  /**
   * Returns the page to its original state
   */
  reset () {
    this.wrapper.remove()
    this.element.style.height = this.originalHeight
    const ads = window.cpexPackage.customAds
    if (ads) { delete ads[this.elementId] }
  }
}