<!DOCTYPE html>
<html>
<head>
<style>
  .Page {
    --unit: 18px;
    font-family: monospace;
    font-size: calc(var(--unit) * 2);
    margin: 0;
    padding: 0;
  }

  .Main {
    display: flex;
  }

  .Markup {
    background-color: #f7f8fa;
    padding: calc(var(--unit) * 3) calc(var(--unit) * 2);
    color: #494c52;
  }

  .Chars {
    background-color: #b2eec3;
    padding: var(--unit);
  }

  .Char {
    background-color: #7edd9c;
    margin: var(--unit);
    padding: var(--unit);
    display: inline-block;
    color: #006123;
  }

  .Char_dragged {
    background-color: #3bc46c;
  }

  .Placeholders {
    background-color: #e8caff;
    padding: var(--unit);
  }

  .Placeholders_dropzone {
    background-color: #d1a2ff;
  }

  .Placeholder {
    background-color: #d1a2ff;
    margin: var(--unit);
    padding: var(--unit);
    display: inline-block;
    color: #221670;
  }

  .Placeholders_dropzone .Placeholder {
    background-color: #a466ff;
  }

  .Placeholder_dragovered {
    background-color: #8533ff !important;
  }

  .Placeholder_dragovered * {
    pointer-events: none;
  }
</style>
</head>

<body class="Page">
<script>
  function mapAndJoin(a, f, s = '') { return Array.from(a).map(f).join(s) }

  function buildHtml() {
    return `
      <div class="Main">
        <div class="Markup">{</div>
        <div class="Chars">
          ${mapAndJoin(
            'flag',
            (char) => `<div class="Char" draggable="true" data-char-id="${char}">${char}</div>`)}
        </div>
        <div class="Markup">_</div>
        <div class="Placeholders">
          ${mapAndJoin(
            Array(4),
            (_, id) => `<div class="Placeholder" data-placeholder-id="${id}">&nbsp;</div>`)}
        </div>
        <div class="Markup">}</div>
      </div>
    `
  }

  document.body.innerHTML = buildHtml()

  const chars = document.querySelector('.Chars'),
    placeholders = document.querySelector('.Placeholders')

  function addClass(n, c) { n.classList.add(c) }

  function removeClass(n, c) { n.classList.remove(c) }

  function check(placeholder, char, seven = 13) {
    return placeholder == (char.charCodeAt(0) % seven) - (10 - seven)
  }

  function isNeededChar({ target, dataTransfer }) {
    return dataTransfer.types.includes('char') &&
      ch3ck(
        target.dataset.placeholderId,
        dataTransfer.types
          .find((t) => t.startsWith('char-'))
          .replace(/^char-/, ''))
  }

  Object.entries({
    'dragstart': {
      'Char': function onCharDragStart({ target, dataTransfer }) {
        addClass(target, 'Char_dragged')
        addClass(placeholders, 'Placeholders_dropzone')

        const id = target.dataset.charId
        dataTransfer.setData('char', id)
        dataTransfer.setData('char-' + id, id)
      }
    },
    'dragend': {
      'Char': function onCharDragEnd(e) {
        removeClass(e.target, 'Char_dragged')
        removeClass(placeholders, 'Placeholders_dropzone')
        document.querySelectorAll('.Char_removed').forEach((l) => l.remove())
      }
    },
    'dragenter': {
      'Placeholder': function onPlaceholderDragEnter(e) {
        isNeededChar(e) && addClass(e.target, 'Placeholder_dragovered')
      },
    },
    'dragover': {
      'Placeholder': function onPlaceholderDragOver(e) {
        isNeededChar(e) && e.preventDefault()
      }
    },
    'dragleave': {
      'Placeholder': function onPlaceholderDragLeave(e) {
        isNeededChar(e) && removeClass(e.target, 'Placeholder_dragovered')
      }
    },
    'drop': {
      'Placeholder': function onPlaceholderDrop(e) {
        removeClass(e.target, 'Placeholder_dragovered')
        e.target.innerHTML = e.dataTransfer.getData('char')
      }
    }
  }).forEach(([eventType, handlers]) => {
    document.addEventListener(eventType, (e) => {
      for(let className in handlers)
        e.target.classList.contains(className) && handlers[className](e)
    })
  })
</script>
</body>
</html>