subtitles_timedtext.js

import DOMHelpers from "../domhelpers"

/**
 * Safely checks if an attribute exists on an element.
 * Browsers < DOM Level 2 do not have 'hasAttribute'
 *
 * The interesting case - can be null when it isn't there or "", but then can also return "" when there is an attribute with no value.
 * For subs this is good enough. There should not be attributes without values.
 * @param {Element} el HTML Element
 * @param {String} attribute attribute to check for
 */
function hasAttribute(el, attribute) {
  return !!el.getAttribute(attribute)
}

function hasNestedTime(element) {
  return !hasAttribute(element, "begin") || !hasAttribute(element, "end")
}

function TimedText(timedPieceNode, toStyleFunc) {
  const start = timeStampToSeconds(timedPieceNode.getAttribute("begin"))
  const end = timeStampToSeconds(timedPieceNode.getAttribute("end"))
  const _node = timedPieceNode
  let htmlElementNode

  function timeStampToSeconds(timeStamp) {
    const timePieces = timeStamp.split(":")
    let timeSeconds = parseFloat(timePieces.pop(), 10)
    if (timePieces.length) {
      timeSeconds += 60 * parseInt(timePieces.pop(), 10)
    }
    if (timePieces.length) {
      timeSeconds += 60 * 60 * parseInt(timePieces.pop(), 10)
    }
    return timeSeconds
  }

  function removeFromDomIfExpired(time) {
    if (time > end || time < start) {
      DOMHelpers.safeRemoveElement(htmlElementNode)
      return true
    }
    return false
  }

  function addToDom(parentNode) {
    const node = htmlElementNode || generateHtmlElementNode()
    parentNode.appendChild(node)
  }

  function generateHtmlElementNode(node) {
    const source = node || _node
    let localName = source.localName || source.tagName

    // We lose line breaks with nested TimePieces, so this provides similar layout
    const parentNodeLocalName =
      (source.parentNode && source.parentNode.localName) || (source.parentNode && source.parentNode.tagName)
    if (localName === "span" && parentNodeLocalName === "p" && hasNestedTime(source.parentNode)) {
      localName = "p"
    }

    const html = document.createElement(localName)
    const style = toStyleFunc(source)
    if (style) {
      html.setAttribute("style", style)
      html.style.cssText = style
    }

    if (localName === "p") {
      html.style.margin = "0px"
    }

    for (let i = 0, j = source.childNodes.length; i < j; i++) {
      const n = source.childNodes[i]
      if (n.nodeType === 3) {
        html.appendChild(document.createTextNode(n.data))
      } else if (n.nodeType === 1) {
        html.appendChild(generateHtmlElementNode(n))
      }
    }
    if (!node) {
      htmlElementNode = html
    }

    return html
  }

  return {
    start: start,
    end: end,
    // TODO: can we stop this from adding/removing itself from the DOM? Just expose the 'generateNode' function? OR just generate the node at creation and have a property...
    removeFromDomIfExpired: removeFromDomIfExpired,
    addToDom: addToDom,
  }
}

export default TimedText