
1 // 2 // Copyright (c) 2007, Brian Frank and Andy Frank 3 // Licensed under the Academic Free License version 3.0 4 // 5 // History: 6 // 17 Feb 07 Brian Frank Creation 7 // 8 9 ************************************************************************** 10 ** DocNodeId 11 ************************************************************************** 12 13 enum DocNodeId 14 { 15 text, 16 doc, 17 heading, 18 para, 19 pre, 20 blockQuote, 21 orderedList, 22 unorderedList, 23 listItem, 24 emphasis, 25 strong, 26 code, 27 link, 28 image 29 } 30 31 ************************************************************************** 32 ** Node 33 ************************************************************************** 34 35 ** 36 ** DocNode is the base class for nodes in a fandoc model. 37 ** There are two type of nodes: DocElem and DocText. 38 ** 39 abstract class DocNode 40 { 41 42 ** 43 ** Get node id for node type. 44 ** 45 abstract DocNodeId id() 46 47 ** 48 ** Write this node to the specified DocWriter. 49 ** 50 abstract Void write(DocWriter out) 51 52 ** 53 ** Debug dump to output stream. 54 ** 55 Void dump(OutStream out := Sys.out) 56 { 57 html := HtmlDocWriter.make(out) 58 write(html) 59 html.out.flush 60 } 61 } 62 63 ************************************************************************** 64 ** DocText 65 ************************************************************************** 66 67 ** 68 ** DocText segment. 69 ** 70 class DocText : DocNode 71 { 72 new make(Str str) { this.str = str } 73 74 override DocNodeId id() { return DocNodeId.text } 75 76 override Void write(DocWriter out) 77 { 78 out.text(this) 79 } 80 81 override Str toStr() { return str } 82 83 Str str 84 } 85 86 ************************************************************************** 87 ** DocElem 88 ************************************************************************** 89 90 ** 91 ** DocElem is a container node which models a branch of the doc tree. 92 ** 93 abstract class DocElem : DocNode 94 { 95 ** 96 ** Get the HTML element name to use for this element. 97 ** 98 abstract Str htmlName() 99 100 ** 101 ** Is this an inline versus a block element. 102 ** 103 abstract Bool isInline() 104 105 ** 106 ** Is this a block element versus an inline element. 107 ** 108 Bool isBlock() { return !isInline } 109 110 ** 111 ** Write this element and its children to the specified DocWriter. 112 ** 113 override Void write(DocWriter out) 114 { 115 out.elemStart(this) 116 children.each |DocNode child| { child.write(out) } 117 out.elemEnd(this) 118 } 119 120 Void addChild(DocNode node) 121 { 122 // if adding two text nodes, then merge them 123 if (node.id == DocNodeId.text && !children.isEmpty) 124 { 125 if (children.last.id == DocNodeId.text) 126 { 127 ((DocText)children.last).str += ((DocText)node).str 128 return 129 } 130 } 131 132 children.add(node) 133 } 134 135 DocNode[] children := DocNode[,] 136 Str anchorId 137 } 138 139 ************************************************************************** 140 ** Doc 141 ************************************************************************** 142 143 ** 144 ** Doc models the top level node of a fandoc document. 145 ** 146 class Doc : DocElem 147 { 148 override DocNodeId id() { return DocNodeId.doc } 149 override Str htmlName() { return "body" } 150 override Bool isInline() { return false } 151 152 override Void write(DocWriter out) 153 { 154 out.docStart(this) 155 super.write(out) 156 out.docEnd(this) 157 } 158 159 Str:Str meta := Str:Str[:] 160 } 161 162 ************************************************************************** 163 ** Heading 164 ************************************************************************** 165 166 ** 167 ** Heading 168 ** 169 class Heading : DocElem 170 { 171 new make(Int level) { this.level = level } 172 override DocNodeId id() { return DocNodeId.heading } 173 override Str htmlName() { return "h$level" } 174 override Bool isInline() { return false } 175 const Int level 176 } 177 178 ************************************************************************** 179 ** Para 180 ************************************************************************** 181 182 ** 183 ** Para models a paragraph of text. 184 ** 185 class Para : DocElem 186 { 187 override DocNodeId id() { return DocNodeId.para } 188 override Str htmlName() { return "p" } 189 override Bool isInline() { return false } 190 Str admonition // WARNING, NOTE, TODO, etc 191 } 192 193 ************************************************************************** 194 ** Pre 195 ************************************************************************** 196 197 ** 198 ** Pre models a pre-formated code block. 199 ** 200 class Pre : DocElem 201 { 202 override DocNodeId id() { return DocNodeId.pre } 203 override Str htmlName() { return "pre" } 204 override Bool isInline() { return false } 205 } 206 207 ************************************************************************** 208 ** BlockQuote 209 ************************************************************************** 210 211 ** 212 ** BlockQuote models a block of quoted text. 213 ** 214 class BlockQuote : DocElem 215 { 216 override DocNodeId id() { return DocNodeId.blockQuote } 217 override Str htmlName() { return "blockquote" } 218 override Bool isInline() { return false } 219 } 220 221 ************************************************************************** 222 ** OrderedList 223 ************************************************************************** 224 225 ** 226 ** OrderedList models a numbered list 227 ** 228 class OrderedList : DocElem 229 { 230 new make(OrderedListStyle style) { this.style = style } 231 override DocNodeId id() { return DocNodeId.orderedList } 232 override Str htmlName() { return "ol" } 233 override Bool isInline() { return false } 234 OrderedListStyle style 235 } 236 237 ** 238 ** OrderedListStyle 239 ** 240 enum OrderedListStyle 241 { 242 number, // 1, 2, 3, 4 243 upperAlpha, // A, B, C, D 244 lowerAlpha, // a, b, c, d 245 upperRoman, // I, II, III, IV 246 lowerRoman // i, ii, iii, iv 247 248 Str htmlType() 249 { 250 switch (this) 251 { 252 case number: return "1" 253 case upperAlpha: return "A" 254 case lowerAlpha: return "a" 255 case upperRoman: return "I" 256 case lowerRoman: return "i" 257 default: throw Err.make(toStr) 258 } 259 } 260 261 static OrderedListStyle fromFirstChar(Int ch) 262 { 263 if (ch == 'I') return upperRoman 264 if (ch == 'i') return lowerRoman 265 if (ch.isUpper) return upperAlpha 266 if (ch.isLower) return lowerAlpha 267 return number 268 } 269 } 270 271 ************************************************************************** 272 ** UnorderedList 273 ************************************************************************** 274 275 ** 276 ** UnorderedList models a bullet list 277 ** 278 class UnorderedList : DocElem 279 { 280 override DocNodeId id() { return DocNodeId.unorderedList } 281 override Str htmlName() { return "ul" } 282 override Bool isInline() { return false } 283 } 284 285 ************************************************************************** 286 ** ListItem 287 ************************************************************************** 288 289 ** 290 ** ListItem is an item in an OrderedList and UnorderedList. 291 ** 292 class ListItem : DocElem 293 { 294 override DocNodeId id() { return DocNodeId.listItem } 295 override Str htmlName() { return "li" } 296 override Bool isInline() { return false } 297 } 298 299 ************************************************************************** 300 ** Emphasis 301 ************************************************************************** 302 303 ** 304 ** Emphasis is italic text 305 ** 306 class Emphasis : DocElem 307 { 308 override DocNodeId id() { return DocNodeId.emphasis } 309 override Str htmlName() { return "em" } 310 override Bool isInline() { return true } 311 } 312 313 ************************************************************************** 314 ** Strong 315 ************************************************************************** 316 317 ** 318 ** Strong is bold text 319 ** 320 class Strong : DocElem 321 { 322 override DocNodeId id() { return DocNodeId.strong } 323 override Str htmlName() { return "strong" } 324 override Bool isInline() { return true } 325 } 326 327 ************************************************************************** 328 ** Code 329 ************************************************************************** 330 331 ** 332 ** Code is inline code 333 ** 334 class Code : DocElem 335 { 336 override DocNodeId id() { return DocNodeId.code } 337 override Str htmlName() { return "code" } 338 override Bool isInline() { return true } 339 } 340 341 ************************************************************************** 342 ** Link 343 ************************************************************************** 344 345 ** 346 ** Link is a hyperlink. 347 ** 348 class Link : DocElem 349 { 350 new make(Str uri) { this.uri = uri } 351 override DocNodeId id() { return DocNodeId.link } 352 override Str htmlName() { return "a" } 353 override Bool isInline() { return true } 354 Bool isCode := false // when uri resolves to a type or slot 355 Str uri 356 } 357 358 ************************************************************************** 359 ** Image 360 ************************************************************************** 361 362 ** 363 ** Image is a reference to an image file 364 ** 365 class Image : DocElem 366 { 367 new make(Str uri, Str alt) { this.uri = uri; this.alt = alt } 368 override DocNodeId id() { return DocNodeId.image } 369 override Str htmlName() { return "img" } 370 override Bool isInline() { return true } 371 Str uri 372 Str alt 373 } 374