Home Reference Source

src/formats/skin.js

import { cpexError, cpexLog, isDOMObject, addIframe, addStyle, isObject } from '../utils.js'

/**
 * HTML creative rendered to the page background
 */
export default class Skin {
  constructor (elementId, adUnit, main, width, height) {
    this.main = main
    this.type = 'skin'
    this.adUnit = adUnit
    this.elementId = elementId
    this.settings = main.settings.formats.skin
    this.width = width
    this.height = height
    this.loaded = false
    this.hidden = []
    this.offset = 0
    this.iframeCSS = `width: ${width}px; height: ${height}px; border:none; display:block; overflow:hidden; margin-left: 50%; transform: translateX(-50%); max-width: initial; ` // Needed here for HB reRender
    cpexLog('Skin: Caught Skin custom format, in elementId ' + elementId)
  }

  /**
   * Places the skin container in the page background and styles the page content
   */
  renderIframe () {
    // Handle settings
    if (!this.validateSettings(this.settings)) { return cpexError('Skin: Incorrect skin settings: ' + JSON.stringify(this.settings)) }
    // Temporary: Simplify after moving all settings to function selectors (backgroundEl = this.settings.backgroundEl() || document.body)
    this.backgroundEl = (typeof this.settings.backgroundEl === 'function' ? this.settings.backgroundEl() : this.settings.backgroundEl) || document.body
    this.contentEl = typeof this.settings.contentEl === 'function' ? this.settings.contentEl() : this.settings.contentEl
    // Save original style and hide things, based on settings
    if (isDOMObject(this.contentEl)) { this.contentStyleBackup = window.getComputedStyle(this.contentEl) }
    // Add general temporary CSS
    if (this.settings.generalCSS) {
      this.generalCSSEl = addStyle(document, this.settings.generalCSS)
    }
    // Hide obtrusive elements
    if (Array.isArray(this.settings.hide) && this.settings.hide.length > 0) {
      this.settings.hide.forEach(el => {
        const hideEl = typeof el === 'function' ? el() : el
        if (isDOMObject(hideEl)) {
          this.hidden.push({ element: hideEl, display: hideEl.style.display })
          hideEl.style.setProperty('display', 'none', 'important')
        }
      })
    }
    // Add wrapper element
    this.element = document.createElement('div')
    this.element.id = 'cpex-skin'
    this.getOffset()
    this.element.style.cssText = `position: fixed; top: ${this.offset}px; left: 0px; width: 100%; ${typeof this.settings.zIndex === 'number' ? 'z-index:' + this.settings.zIndex : ''};`
    this.backgroundEl.prepend(this.element)
    // Label the original element as moved, mainly for debug tag rendering
    const targetEl = document.getElementById(this.elementId)
    if (targetEl) { targetEl.setAttribute('data-target-id-moved', this.element.id) }
    // Style content
    if (this.contentEl) { this.contentEl.style.cssText = this.settings.contentCSS }
    // Add iframe
    this.iframe = addIframe(this.element, { id: this.elementId + '-iframe' }, (iframe) => { // onload callback
      this.loaded = true
      cpexLog('Skin: Rendered into ', this.backgroundEl)
    })
    this.iframe.style.cssText = this.iframeCSS
    // Register scrolling offset
    if (this.settings.offset && this.settings.offsetScroll) {
      this.updateOffset()
      window.addEventListener('scroll', () => { this.updateOffset() })
    }
    return this.iframe
  }

  getOffset () {
    if (typeof this.settings.offset === 'function') {
      this.offset = this.settings.offset()
    } else if (typeof this.settings.offset === 'number') {
      this.offset = this.settings.offset
    }
  }

  updateOffset () {
    this.element.style.top = window.scrollY < this.offset ? this.offset - window.scrollY + 'px' : '0px'
  }

  /**
   * Returns the page to its original state
   */
  reset () {
    this.element.remove()
    if (this.contentEl) { this.contentEl.style = this.contentStyleBackup }
    if (this.generalCSSEl) {
      this.generalCSSEl.remove()
      delete this.generalCSSEl
    }
    this.hidden.forEach(item => { item.element.style.display = item.display })
    const ads = window.cpexPackage.customAds
    if (ads) { delete ads[this.elementId] }
  }

  /**
   * Check skin settings if they contain html elements
   */
  validateSettings (settings) {
    return isObject(settings)
  }
}