if not modules then modules = { } end modules ['cloze'] = {
  version   = '0.1',
  comment   = 'cloze',
  author    = 'Josef Friedrich, R.-M. Huber',
  copyright = 'Josef Friedrich, R.-M. Huber',
  license   = 'The LaTeX Project Public License Version 1.3c 2008-05-04'
}
local nodex = {}
local registry = {}
registry.user_id = 3121978
registry.storage = {}
registry.defaults = {
  ['align'] = 'l',
  ['distance'] = '1.5pt',
  ['hide'] = false,
  ['linecolor'] = '0 0 0 rg 0 0 0 RG', -- black
  ['margin'] = '3pt',
  ['resetcolor'] = '0 0 0 rg 0 0 0 RG', -- black
  ['show_text'] = true,
  ['show'] = true,
  ['textcolor'] = '0 0 1 rg 0 0 1 RG', -- blue
  ['thickness'] = '0.4pt',
  ['width'] = '2cm',
}
registry.global_options = {}
registry.local_options = {}
local cloze = {}
local base = {}
base.is_registered = {}
function nodex.create_colorstack(data)
  if not data then
    data = '0 0 0 rg 0 0 0 RG' -- black
  end
  local whatsit = node.new('whatsit', 'pdf_colorstack')
  whatsit.stack = 0
  whatsit.data = data
  return whatsit
end
function nodex.create_color(option)
  local data
  if option == 'line' then
    data = registry.get_value('linecolor')
  elseif option == 'text' then
    data = registry.get_value('textcolor')
  elseif option == 'reset' then
    data = nil
  else
    data = nil
  end
  return nodex.create_colorstack(data)
end
function nodex.create_line(width)
  local rule = node.new(node.id('rule'))
  local thickness = tex.sp(registry.get_value('thickness'))
  local distance = tex.sp(registry.get_value('distance'))
  rule.depth = distance + thickness
  rule.height = - distance
  rule.width = width
  return rule
end
function nodex.insert_line(head, current, width)
  local n = {} -- node
  n.color_line = nodex.create_color('line')
  head, n.color_line = node.insert_after(head, current, n.color_line)
  n.line = nodex.create_line(width)
  head, n.line = node.insert_after(head, n.color_line, n.line)
  n.color_reset = nodex.create_color('reset')
  return node.insert_after(head, n.line, n.color_reset)
end
function nodex.write_line()
  node.write(nodex.create_color('line'))
  node.write(nodex.create_line(tex.sp(registry.get_value('width'))))
  node.write(nodex.create_color('reset'))
end
function nodex.create_linefil()
  local glue = node.new('glue')
  glue.subtype = 100
  local glue_spec = node.new('glue_spec')
  glue_spec.stretch = 65536
  glue_spec.stretch_order = 3
  glue.spec = glue_spec
  local rule = nodex.create_line(0)
  rule.dir = 'TLT'
  glue.leader = rule
  return glue
end
function nodex.write_linefil()
  node.write(nodex.create_color('line'))
  node.write(nodex.create_linefil())
  node.write(nodex.create_color('reset'))
end
function nodex.create_kern(width)
  local kern = node.new(node.id('kern'))
  kern.kern = width
  return kern
end
function nodex.strut_to_hlist(hlist)
  local n = {} -- node
  n.head = hlist.head
  n.kern = nodex.create_kern(0)
  n.strut = node.insert_before(n.head, n.head, n.kern)
  hlist.head = n.head.prev
  return hlist, n.strut, n.head
end
function nodex.write_margin()
  local kern = nodex.create_kern(tex.sp(registry.get_value('margin')))
  node.write(kern)
end
function registry.create_marker(index)
  local marker = node.new('whatsit','user_defined')
  marker.type = 100 -- number
  marker.user_id = registry.user_id
  marker.value = index
  return marker
end
function registry.write_marker(mode, position)
  local index = registry.set_storage(mode, position)
  local marker = registry.create_marker(index)
  node.write(marker)
end
function registry.check_marker(item, mode, position)
  local data = registry.get_marker_data(item)
  if data and data.mode == mode and data.position == position then
    return true
  else
    return false
  end
end
function registry.get_marker(item, mode, position)
  local out
  if registry.check_marker(item, mode, position) then
    out = item
  else
    out = false
  end
  if out and position == 'start' then
    registry.get_marker_values(item)
  end
  return out
end
function registry.get_marker_data(item)
  if item.id == node.id('whatsit')
    and item.subtype == 44
    and item.user_id == registry.user_id then
    return registry.get_storage(item.value)
  else
    return false
  end
end
function registry.get_marker_values(marker)
  local data = registry.get_marker_data(marker)
  registry.local_options = data.values
  return data.values
end
function registry.get_index()
  if not registry.index then
    registry.index = 0
  end
  registry.index = registry.index + 1
  return registry.index
end
function registry.set_storage(mode, position)
  local index = registry.get_index()
  local data = {
    ['mode'] = mode,
    ['position'] = position
  }
  data.values = registry.local_options
  registry.storage[index] = data
  return index
end
function registry.get_storage(index)
  return registry.storage[index]
end
function registry.set_option(key, value)
  if value == '' or value == '\\color@ ' then
    return false
  end
  if registry.is_global == true then
    registry.global_options[key] = value
  else
    registry.local_options[key] = value
  end
end
function registry.set_is_global(value)
  registry.is_global = value
end
function registry.unset_local_options()
  registry.local_options = {}
end
function registry.unset_global_options()
  registry.global_options = {}
end
function registry.get_value(key)
  if registry.has_value(registry.local_options[key]) then
    return registry.local_options[key]
  end
  if registry.has_value(registry.global_options[key]) then
    return registry.global_options[key]
  end
  return registry.defaults[key]
end
function registry.get_value_show()
  if
    registry.get_value('show') == true
  or
    registry.get_value('show') == 'true'
  then
    return true
  else
    return false
  end
end
function registry.has_value(value)
  if value == nil or value == '' or value == '\\color@ ' then
    return false
  else
    return true
  end
end
function registry.get_defaults(option)
  return registry.defaults[option]
end
function cloze.basic_make(head, hlist, start, stop)
  local n = {}
  local l = {}
  if not start or not stop then
    return
  end
  n.start = start
  n.stop = stop
  l.width = node.dimensions(
    hlist.glue_set,
    hlist.glue_sign,
    hlist.glue_order,
    n.start,
    n.stop
  )
  head, n.line = nodex.insert_line(head, n.start, l.width)
  n.color_text = nodex.create_color('text')
  head, n.color_text = node.insert_after(
    hlist.head,
    n.line,
    n.color_text
  )
  if registry.get_value_show() then
    n.kern = nodex.create_kern(-l.width)
    node.insert_after(head, n.color_text, n.kern)
    n.color_reset = nodex.create_color('reset')
    node.insert_after(head, n.stop, n.color_reset)
  else
    n.line.next = n.stop.next
    n.stop.prev = n.line.prev
  end

end
function cloze.basic(head)
  local n = {} -- node
  local b = {} -- boolean
  local l = {} -- length
  local t = {} -- temp
  for hlist in node.traverse_id(node.id('hlist'), head) do
    hlist = nodex.strut_to_hlist(hlist)
    if b.line_end then
      b.init_cloze = true
    end
    n.current = hlist.head
    while n.current do
      if
        registry.check_marker(n.current, 'basic', 'start')
      or
        b.init_cloze
      then
        b.init_cloze = false
        n.start = n.current
        while n.current do
          b.line_end = true
          n.stop = n.current
          if registry.check_marker(n.stop, 'basic', 'stop') then
            b.line_end = false
            break
          end
          n.current = n.current.next
        end
        cloze.basic_make(head, hlist, n.start, n.stop)
        n.current = n.stop
      else
        n.current = n.current.next
      end
    end
  end
  return head
end
function cloze.fix_make(head, start, stop)
  local l = {} -- length
  l.width = tex.sp(registry.get_value('width'))
  local n = {} -- node
  n.start = start
  n.stop = stop
  l.text_width = node.dimensions(n.start, n.stop)
  local align = registry.get_value('align')
  if align == 'right' then
    l.kern_start = -l.text_width
    l.kern_stop = 0
  elseif align == 'center' then
    l.half = (l.width - l.text_width) / 2
    l.kern_start = -l.half - l.text_width
    l.kern_stop = l.half
  else
    l.kern_start = -l.width
    l.kern_stop = l.width - l.text_width
  end
  head, n.line = nodex.insert_line(head, n.start, l.width)
  if registry.get_value_show() then
    n.kern_start = nodex.create_kern(l.kern_start)
    head, n.kern_start = node.insert_after(head, n.line, n.kern_start)
    n.color_text = nodex.create_color('text')
    node.insert_after(head, n.kern_start, n.color_text)
    n.color_reset = nodex.create_color('reset')
    node.insert_before(head, n.stop, n.color_reset)
    n.kern_stop = nodex.create_kern(l.kern_stop)
    node.insert_before(head, n.stop, n.kern_stop)
  else
    n.line.next = n.stop.next
  end
end
function cloze.fix(head)
  local n = {} -- node
  n.start, n.stop = false
  for current in node.traverse_id(node.id('whatsit'), head) do
    if not n.start then
      n.start = registry.get_marker(current, 'fix', 'start')
    end
    if not n.stop then
      n.stop = registry.get_marker(current, 'fix', 'stop')
    end
    if n.start and n.stop then
      cloze.fix_make(head, n.start, n.stop)
      n.start, n.stop = false
    end
  end
  return head
end
function cloze.par(head)
  local l = {} -- length
  local n = {} -- node
  for hlist in node.traverse_id(node.id('hlist'), head) do
    for whatsit in node.traverse_id(node.id('whatsit'), hlist.head) do
      registry.get_marker(whatsit, 'par', 'start')
    end
    l.width = hlist.width
    hlist, n.strut, n.head = nodex.strut_to_hlist(hlist)
    head, n.line = nodex.insert_line(head, n.strut, l.width)
    if registry.get_value_show() then
      n.kern = nodex.create_kern(-l.width)
      head, n.kern = node.insert_after(head, n.line, n.kern)
      n.color_text = nodex.create_color('text')
      node.insert_after(head, n.kern, n.color_text)
      n.tail = node.tail(n.head)
      n.color_reset = nodex.create_color('reset')
      node.insert_after(n.head, n.tail, n.color_reset)
    else
      n.line.next = nil
    end
  end
  return head
end
function base.register(mode)
  if mode == 'par' then
    luatexbase.add_to_callback(
      'post_linebreak_filter',
      cloze.par,
      mode
    )
    return true
  end
  if not base.is_registered[mode] then
    if mode == 'basic' then
      luatexbase.add_to_callback(
        'post_linebreak_filter',
        cloze.basic,
        mode
      )
    elseif mode == 'fix' then
      luatexbase.add_to_callback(
        'pre_linebreak_filter',
        cloze.fix,
        mode
      )
    else
      return false
    end
    base.is_registered[mode] = true
  end
end
function base.unregister(mode)
  if mode == 'basic' then
    luatexbase.remove_from_callback('post_linebreak_filter', mode)
  elseif mode == 'fix' then
    luatexbase.remove_from_callback('pre_linebreak_filter', mode)
  else
    luatexbase.remove_from_callback('post_linebreak_filter', mode)
  end
end
base.linefil = nodex.write_linefil
base.line = nodex.write_line
base.margin = nodex.write_margin
base.set_option = registry.set_option
base.set_is_global = registry.set_is_global
base.unset_local_options = registry.unset_local_options
base.reset = registry.unset_global_options
base.get_defaults = registry.get_defaults
base.marker = registry.write_marker
return base
