aboutsummaryrefslogtreecommitdiff
path: root/src/layout.nim
blob: 14eebaa6a8140b7994eb91b15ac7613123827fc6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import std/options
import formats/html

# const inline_elements = []

const block_elements = [
  "html", "body", "article", "section", "nav", "aside",
  "h1", "h2", "h3", "h4", "h5", "h6", "hgroup", "header",
  "footer", "address", "p", "hr", "pre", "blockquote",
  "ol", "ul", "menu", "li", "dl", "dt", "dd", "figure",
  "figcaption", "main", "div", "table", "form", "fieldset",
  "legend", "details", "summary"
]

type LayoutKind = enum
  Inline, Block, Document

# Improve error messages: suggest a ref object when failing from recursion
type Layout = ref object
  case kind*: LayoutKind:
    of Inline:
      weight: int
    of Block:
      discard
    of Document:
      discard
  node: Node
  parent: Option[Layout]
  previous: Option[Layout]
  children: seq[Layout]
  # x, y, width, height: float

# Recursively construct the layout tree
func layout(node: Node, parent: Option[Layout], previous: Option[Layout]): Layout =
  result = Layout() # !!! ref types are nil by default :-(
  var children: seq[Layout] = @[]
  var prevchild: Option[Layout] = none(Layout)
  var kind: LayoutKind = Inline
  case node.kind:
    of Element:
      for child in node.children:
        # FIXME: parent nodes are broken. we pass a ref to result, which is different from the returned layout.
        let current = child.layout(parent=some(result), previous=prevchild)
        children.add(current)
        prevchild = some(current)
        if kind == Inline and child.kind == Element and child.tag in block_elements:
          kind = Block
      if node.children.len == 0:
        kind = Block
    of Text:
      discard
  return Layout(node: node, parent: parent, previous: previous, children: children, kind: kind)

# Assuming the first node of an HTML object is the <html> tag.
# Right now, HTML generation is Bad so this will change in the future
func layout(html: Html): Layout =
  result = Layout(kind: Document, node: html[0], parent: none(Layout), previous: none(Layout), children: @[])
  for child in html[0].children:
    result.children.add(child.layout(none(Layout), none(Layout)))

# TODO: change Html into a distinct Node and adjust layout accordingly

#[type DocumentLayout = ref object
  node: Node
  children: seq[Layout]]#

#[func layout_mode(node: Node): LayoutKind =
  if node.kind == Text:
    return Inline
  elif node.children.len > 0:
    for child in node.children:
      if child.kind == Text: continue
      if child.tag in block_elements:
        return Block
    return Inline
  else:
    return Block]#

#[func layout(node: Node, parent: Layout, previous: Layout = nil): Layout =
  if node.kind == Element and node.children.len > 0:
    var previous: Layout = previous
    var current: Layout
    for child in node.children:
      if previous == nil:
        current = child.layout(result)
        result.children.add(current)
      else:
        current = child.layout(result, previous)
        result.children.add(current)
      previous = current
  else:
    result = Layout(node: node, parent: parent, previous: previous, children: @[])]#

when isMainModule:
  import formats/uri, protocols/http, std/strutils, print

  proc printLayout(layout: Layout, indentation=0) =
    if layout.node.kind == Element:
      stdout.write(" ".repeat(indentation))
      stdout.write(layout.node.tag)
      stdout.write(" ")
      stdout.write(layout.kind)
      stdout.write("\n")
      print layout.parent
      for child in layout.children:
        child.printLayout(indentation + 2)

  let text = parseHTML(httpRequest(parseURL("https://example.org:443/index.html")).body)
  print layout(text)
  for node in text:
    printLayout node.layout(none(Layout), none(Layout))