OverviewFilesCommitsIssuesDocsReleasesPeers

holepunchto-deps / @holepunchto / keet-editor-parser / src / segment / to-html.js

130 lines · javascript ref:
const { DISPLAY_TYPES } = require('../constants.js')

const {
  MENTION,
  HTTP_LINK,
  PEAR_LINK,
  BOLD,
  ITALIC,
  CODE,
  EMOJI,
  CODE_BLOCK,
  STRIKE_THROUGH
} = DISPLAY_TYPES

const map = {
  [BOLD]: bold,
  [ITALIC]: italic,
  [STRIKE_THROUGH]: strike,
  [CODE]: code,
  [CODE_BLOCK]: codeBlock,
  [EMOJI]: emoji,
  [MENTION]: mention,
  [PEAR_LINK]: link,
  [HTTP_LINK]: link
}

module.exports = { segmentToHtml }

function segmentToHtml(text, segments, opts = {}) {
  if (!segments) return escapeHTML(text) || ''

  let html = ''

  for (const { start, end, styles } of segments) {
    let htmlSegment = escapeHTML(text.slice(start, end))

    for (const display of styles) {
      htmlSegment = map[display.type](htmlSegment, display, opts)
    }

    html += htmlSegment
  }

  return html.replace(/\n/g, '<br />')
}

function escapeHTML(str) {
  if (!str) return ''
  return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
}

function bold(text) {
  return `<b>${text}</b>`
}

function italic(text) {
  return `<i>${text}</i>`
}

function strike(text) {
  return `<s>${text}</s>`
}

function code(text) {
  return `<code>${text}</code>`
}

function codeBlock(text) {
  return `<pre>${text}</pre>`
}

function emoji(text, display, opts) {
  const { urlRiv, url, emoji } = opts.getEmojiData(display.content) || {}

  let children

  if (urlRiv || url) {
    const attributes = {
      class: 'emoji-img',
      src: urlRiv || url,
      alt: `:${display.content}:`
    }
    children = `<img ${objToStr(attributes)} />`
  } else {
    children = text
  }

  const attributes = {
    class: 'emoji',
    data: {
      shortcode: display.content,
      emoji
    }
  }

  return `<span ${objToStr(attributes)}>${children}</span>`
}

function mention(text, display) {
  const [id, role] = display.content.split('/')

  const attributes = {
    class: 'mention',
    data: {
      type: 'mention',
      id,
      label: text.startsWith('@') ? text.slice(1) : text
    }
  }

  if (role) {
    attributes.data.role = role
  }

  return `<span ${objToStr(attributes)}>${text}</span>`
}

function link(text, display) {
  return `<a href="${display.content}">${text}</a>`
}

function objToStr(attr) {
  const { data, ...rest } = attr
  const parts = Object.keys(rest).map((key) => `${key}="${rest[key]}"`)
  if (data) {
    Object.keys(data).forEach((key) => parts.push(`data-${key}="${data[key]}"`))
  }
  return parts.join(' ')
}