text: 'use strict'
  
  var high = require('highlight.js/lib/core')
  var fault = require('fault')
  
  exports.highlight = highlight
  exports.highlightAuto = highlightAuto
  exports.registerLanguage = registerLanguage
  exports.listLanguages = listLanguages
  exports.registerAlias = registerAlias
  
  Emitter.prototype.addText = text
  Emitter.prototype.addKeyword = addKeyword
  Emitter.prototype.addSublanguage = addSublanguage
  Emitter.prototype.openNode = open
  Emitter.prototype.closeNode = close
  Emitter.prototype.closeAllNodes = noop
  Emitter.prototype.finalize = noop
  Emitter.prototype.toHTML = toHtmlNoop
  
  var defaultPrefix = 'hljs-'
  
  // Highlighting `value` in the language `name`.
  function highlight(name, value, options) {
    var before = high.configure({})
    var settings = options || {}
    var prefix = settings.prefix
    var result
  
    if (typeof name !== 'string') {
      throw fault('Expected `string` for name, got `%s`', name)
    }
  
    if (!high.getLanguage(name)) {
      throw fault('Unknown language: `%s` is not registered', name)
    }
  
    if (typeof value !== 'string') {
      throw fault('Expected `string` for value, got `%s`', value)
    }
  
    if (prefix === null || prefix === undefined) {
      prefix = defaultPrefix
    }
  
    high.configure({__emitter: Emitter, classPrefix: prefix})
  
    result = high.highlight(name, value, true)
  
    high.configure(before || {})
  
    /* istanbul ignore if - Highlight.js seems to use this (currently) for broken
     * grammars, so let’s keep it in there just to be sure. */
    if (result.errorRaised) {
      throw result.errorRaised
    }
  
    return {
      relevance: result.relevance,
      language: result.language,
      value: result.emitter.rootNode.children
    }
  }
  
  function highlightAuto(value, options) {
    var settings = options || {}
    var subset = settings.subset || high.listLanguages()
    var prefix = settings.prefix
    var length = subset.length
    var index = -1
    var result
    var secondBest
    var current
    var name
  
    if (prefix === null || prefix === undefined) {
      prefix = defaultPrefix
    }
  
    if (typeof value !== 'string') {
      throw fault('Expected `string` for value, got `%s`', value)
    }
  
    secondBest = {relevance: 0, language: null, value: []}
    result = {relevance: 0, language: null, value: []}
  
    while (++index < length) {
      name = subset[index]
  
      if (!high.getLanguage(name)) {
        continue
      }
  
      current = highlight(name, value, options)
      current.language = name
  
      if (current.relevance > secondBest.relevance) {
        secondBest = current
      }
  
      if (current.relevance > result.relevance) {
        secondBest = result
        result = current
      }
    }
  
    if (secondBest.language) {
      result.secondBest = secondBest
    }
  
    return result
  }
  
  // Register a language.
  function registerLanguage(name, syntax) {
    high.registerLanguage(name, syntax)
  }
  
  // Get a list of all registered languages.
  function listLanguages() {
    return high.listLanguages()
  }
  
  // Register more aliases for an already registered language.
  function registerAlias(name, alias) {
    var map = name
    var key
  
    if (alias) {
      map = {}
      map[name] = alias
    }
  
    for (key in map) {
      high.registerAliases(map[key], {languageName: key})
    }
  }
  
  function Emitter(options) {
    this.options = options
    this.rootNode = {children: []}
    this.stack = [this.rootNode]
  }
  
  function addKeyword(value, name) {
    this.openNode(name)
    this.addText(value)
    this.closeNode()
  }
  
  function addSublanguage(other, name) {
    var stack = this.stack
    var current = stack[stack.length - 1]
    var results = other.rootNode.children
    var node = name
      ? {
          type: 'element',
          tagName: 'span',
          properties: {className: [name]},
          children: results
        }
      : results
  
    current.children = current.children.concat(node)
  }
  
  function text(value) {
    var stack = this.stack
    var current
    var tail
  
    if (value === '') return
  
    current = stack[stack.length - 1]
    tail = current.children[current.children.length - 1]
  
    if (tail && tail.type === 'text') {
      tail.value += value
    } else {
      current.children.push({type: 'text', value: value})
    }
  }
  
  function open(name) {
    var stack = this.stack
    var className = this.options.classPrefix + name
    var current = stack[stack.length - 1]
    var child = {
      type: 'element',
      tagName: 'span',
      properties: {className: [className]},
      children: []
    }
  
    current.children.push(child)
    stack.push(child)
  }
  
  function close() {
    this.stack.pop()
  }
  
  function toHtmlNoop() {
    return ''
  }
  
  function noop() {}
  