--- /dev/null
+#!/usr/bin/env ruby
+require 'open3'
+require 'fileutils'
+require 'webrick'
+require 'redcarpet'
+
+# TODO:
+# * Copy auxiliary files over
+# * Add page attachment mechanism
+# * Add edit shortcut
+# * Add save shortcut
+# * Add a create method
+# * Add a delete method
+# * Add a move/rename method
+# * Add git revisioning for pages
+
+SITE_TITLE = "awiki"
+SERVER_PORT = (ARGV[0] || 8080)
+HTML_DIR = "./html/"
+PAGES_DIR = "./pages/"
+VIEW_TEMPLATE = "config/page.html.erb"
+EDIT_TEMPLATE = "config/editor.html.erb"
+PLANTUML_JAR = "config/plantuml.jar"
+PLANTUML_CMD = "java -jar #{PLANTUML_JAR} -tsvg -pipe"
+
+class Page
+ def initialize(src)
+ @markdown = (File.exist?(src) ? File.read(src) : "")
+ @sanitized = @markdown.gsub(/</,"<").gsub(/>/,">")
+ @site_title = SITE_TITLE
+ @page_path = src.gsub(%r{/+},'/')
+ @gendate = Time.now.to_s
+ @page_title = (@markdown.match(/#\s*(.*)$/) || [])[1]
+ end
+
+ def generate_graph(graph)
+ $plantuml_in.puts graph
+ data = ""
+ while not $plantuml_out.eof?
+ data += $plantuml_out.read_nonblock(32768)
+ break if data =~ /<\/svg>/
+ end
+ "<p>"+data.sub(/<\?[^?]*\?>/,'')+"</p>"
+ end
+
+ def parse_graphs
+ graph = nil
+ text = []
+ @markdown.split("\n").each do |ln|
+ if graph.nil? and ln =~ /^@startuml/ then
+ graph = [ln]
+ elsif graph and ln =~ /^@enduml/ then
+ graph << ln
+ text << generate_graph(graph.join("\n"))
+ graph = nil
+ elsif graph
+ graph << ln
+ else
+ text << ln
+ end
+ end
+ text.join("\n")
+ end
+
+ def view()
+ if not @article
+ @markdown = parse_graphs()
+ @article = $markdown.render(@markdown)
+ end
+ ERB.new(File.read(VIEW_TEMPLATE)).result(binding)
+ end
+
+ def edit()
+ @article = ERB.new(File.read(EDIT_TEMPLATE)).result(binding)
+ view()
+ end
+end
+
+def genallpages()
+ FileUtils.rm_rf(HTML_DIR)
+ Dir.glob("#{PAGES_DIR}/**/*.md").each do |f|
+ genpage(nil, f, true)
+ end
+ FileUtils.cp_r(Dir.glob("files/*"), HTML_DIR)
+end
+
+def gensitemap()
+ links = []
+ Dir.glob("#{PAGES_DIR}/**/*.md").each do |f|
+ path = f.sub(/\.([^.]*)$/, '.html').sub(/^#{PAGES_DIR}/, "")
+ title = (File.read(f).match(/#\s*(.*)$/) || [])[1] || path
+ links << { path: path, title: title }
+ end
+ File.open("#{PAGES_DIR}/sitemap.md", "w") do |f|
+ f.puts "<!-- WARNING: This page autogenerated. Manual changes will be lost -->"
+ f.puts "# Sitemap\n\n"
+ links.sort_by{|h| h[:title] }.each {|l| f.puts "[#{l[:title]}](#{l[:path]})<br/>" }
+ end
+end
+
+def genpage(path, src, force=false)
+ html = ""
+ htmlfile = src.sub(/\.([^.]*)$/, '.html').sub(/^#{PAGES_DIR}/, HTML_DIR)
+ puts "#{src} -> #{htmlfile}"
+ if not FileUtils.uptodate?(htmlfile, [src, VIEW_TEMPLATE]) or force
+ FileUtils.mkdir_p(File.dirname(htmlfile))
+ html = Page.new(src).view()
+ File.open(htmlfile, "w") { |f| f.puts html }
+ else
+ html = File.read(htmlfile)
+ end
+ gensitemap()
+ html.sub("<!--EDIT-->",
+ "<a class='navLink shrink' href='#{path}?edit=true'>[edit]</a>")
+end
+
+def do_post(req, res, path)
+ page_path = (PAGES_DIR + req.path).sub(/\.html$/, '.md')
+ FileUtils.mkdir_p(File.dirname(page_path))
+ markup = req.query["markup"].gsub(/<br\/?>/,"\n").gsub("\r\n","\n").gsub(/</,"<").gsub(/>/,">")
+ File.open(page_path, "wb") {|f| f.write(markup) }
+ res.status = 302
+ res["Location"] = req.path
+end
+
+def do_edit(req, resp, path, src)
+ html = Page.new(src).edit()
+ resp.body = html.sub("<!--EDIT-->",
+ "<a class='navLink shrink' onclick='submit()' href='#'>[save]</a>")
+end
+
+def do_get(req, res, path)
+ if path.end_with?(".html")
+ page_path = PAGES_DIR + path.sub(/^\/html\//,'').sub(/\.html$/, ".md")
+ if (req.query["edit"] == "true") || (not File.exist?(page_path))
+ do_edit(req, res, path, page_path)
+ else
+ res.body = genpage(path, page_path)
+ end
+ elsif File.exist?(HTML_DIR + path)
+ res.body = File.read(HTML_DIR + path)
+ else
+ res.status = 404
+ res.body = "404 - file not found"
+ end
+end
+
+#-------------------------------------------------------------------------------
+
+$plantuml_in, $plantuml_out, _ = Open3.popen2(PLANTUML_CMD)
+
+$markdown = Redcarpet::Markdown.new(
+ Redcarpet::Render::HTML.new(
+ escape_html: false,
+ with_toc_data: true
+ ),
+ no_intra_emphasis: true,
+ tables: true,
+ fenced_code_blocks: true,
+ autolink: true,
+ footnotes: true,
+ quotes: true,
+ highlights: true,
+ underline: true
+)
+
+# Generate all pages from scratch
+genallpages()
+
+$server = WEBrick::HTTPServer.new(
+ :Port => SERVER_PORT,
+ :SSLEnable => false,
+ :DocumentRoot => HTML_DIR,
+ :ServerAlias => 'localhost'
+)
+
+$server.mount_proc '/' do |req, res|
+ rqtype = req.request_method
+ path = (req.path.end_with?("/") ? req.path + "index.html" : req.path)
+ if rqtype == "GET"
+ do_get(req, res, path)
+ elsif rqtype == "POST"
+ do_post(req, res, path)
+ end
+end
+
+trap 'INT' do
+ $server.shutdown
+ genallpages()
+end
+$server.start
+
--- /dev/null
+<!DOCTYPE html>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<head>
+ <meta name="viewport" content="initial-scale=1">
+ <title>
+ <%= @site_title + (@page_title.nil? ? "" : " - #{@page_title}") %>
+ </title>
+ <link rel="shortcut icon" type="image/x-icon" href="./logo.png" />
+ <style>
+ body {
+ font-family: Verdana, Geneva, sans-serif;
+ line-height: 1.5em;
+ padding: 0;
+ margin: 1em;
+ max-width: 900px;
+ width: 90%;
+ margin-left: auto;
+ margin-right: auto;
+ color: #222222;
+ background: #F5F5F0;
+ }
+
+ h1, h2, h3, h4, h5, h6 {
+ margin: 1.5em 0em 0rem 0em;
+ font-weight: 100;
+ }
+ h1 { font-size: 2.00em; }
+ h2 { font-size: 1.50em; }
+ h3 { font-size: 1.17em; }
+ h4 { font-size: 1.12em; }
+ h5 { font-size: 0.83em; }
+ h6 { font-size: 0.67em; }
+
+ a { text-decoration: none; }
+ p { margin: 1em 0 0 0; }
+ td { border-top: 1px solid #222222; }
+ pre { white-space: pre-wrap; }
+
+ hr {
+ border: 0;
+ height: 1px;
+ background: #222222;
+ }
+
+ img, svg {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+ }
+
+ table {
+ width: 100%;
+ text-align: left;
+ border-collapse: collapse;
+ border-spacing: 0;
+ }
+
+ blockquote {
+ border-left: 3px solid #0000EE;
+ margin: 0em 1em 0em 1em;
+ padding: 0em 1em 0em 1em;
+ }
+
+ code {
+ display: inline;
+ padding: 0.25em 0.25em 0.25em 0.25em;
+ background-color: #ffffff;
+ border: 1px solid #222222;
+ }
+
+ pre code {
+ display: block;
+ padding: 0.5em 1em 0.5em 1em;
+ }
+
+ .headerLink, .navLink {
+ color: #222222;
+ text-decoration: none;
+ }
+
+ .headerLink {
+ font-size: 1.5rem;
+ margin-left: 1rem;
+ }
+
+ .navLink {
+ font-size: 1rem;
+ margin-right: 1rem;
+ }
+
+ .box {
+ width: 100%;
+ display: flex;
+ flex-wrap: wrap;
+ }
+ .grow { flex-grow: 1; }
+ .shrink { flex-shrink: 1; }
+ </style>
+</head>
+<body>
+ <div class="header">
+ <hr/>
+ <div class="box">
+ <a class="headerLink grow" href="./index.html">
+ <%= @site_title %>
+ </a>
+ <a class="navLink shrink" href='./index.html'>home</a>
+ <a class="navLink shrink" href='./sitemap.html'>sitemap</a>
+ <!--EDIT-->
+ </div>
+ <hr/>
+ </div>
+
+ <article>
+ <%= @article %>
+ </article>
+
+ <div class="footer">
+ <hr style="margin-top: 2em"/>
+ <div style="text-align: center">
+ <%= @gendate %>
+ </div>
+ </div>
+</body>
+</html>
--- /dev/null
+# Style
+Simple showcase of styling and features available.
+
+# Heading 1
+## Heading 2
+### Heading 3
+#### Heading 4
+##### Heading 5
+###### Heading 6
+
+---
+
+[A link](https://www.example.com)
+
+https://www.example.com
+
+**Bold Text**
+
+*Italicized Text*
+
+_underlined text_
+
+~~The world is flat.~~
+
+"highlighted text"
+
+"quoted text"
+
+> This is a blockquote
+
+Here is some `code` inline.
+
+ This is
+ a code block
+
+Unordered List:
+
+* item 1
+* item 2
+* item 3
+
+Ordered List:
+
+1. item 1
+2. item 2
+3. item 3
+
+dummy paragraph
+
+## Table Example
+
+<br/><table>
+ <tr>
+ <th>This</th>
+ <th>is</th>
+ <th>a</th>
+ <th>table</th>
+ </tr>
+ <tr>
+ <td>This</td>
+ <td>is</td>
+ <td>a</td>
+ <td>table</td>
+ </tr>
+</table>
+
+## Lorem Ipsum
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras eleifend purus non tempor finibus. Donec non turpis id magna egestas laoreet vel et erat. Mauris pulvinar fringilla nunc, quis sollicitudin arcu euismod ut. Phasellus eget gravida eros. Aenean aliquam purus vel semper facilisis. Nulla iaculis nec odio eget posuere. Nunc non justo sed justo ultrices ultrices. Maecenas ut aliquet tortor. Proin eu lectus a velit vestibulum posuere. Vivamus efficitur pulvinar volutpat. Vestibulum iaculis, lorem eget ultricies egestas, velit mi facilisis erat, ut congue sem orci eu metus. Nulla risus massa, suscipit id placerat at, molestie ac nulla. Nunc suscipit augue a risus porttitor egestas. In congue pretium faucibus. Sed porttitor, sapien ac consequat finibus, magna ante aliquet ipsum, quis luctus sem eros eget magna. Cras vitae ligula massa.
+
+Pellentesque mattis urna commodo dolor dictum eleifend. Phasellus ullamcorper venenatis diam eu finibus. Phasellus pellentesque eu leo et vehicula. Vivamus aliquam ante a tortor faucibus maximus ac eu urna. Maecenas et condimentum nisl. Vestibulum tristique lacus aliquet eros molestie, tincidunt ultrices quam porta. Morbi rhoncus ex in scelerisque rutrum. Fusce egestas dui nulla, nec tincidunt ante elementum volutpat. Nam mattis vehicula metus ac elementum. Aliquam erat volutpat. Mauris augue nibh, mollis at ligula nec, posuere tincidunt tortor.
+
+## UML Diagrams
+
+@startuml
+actor Foo
+actor Bar
+Foo -> Bar : This is a label
+@enduml
+
+## Flexbox Example
+
+<br/>
+<div class="box">
+ <div class="grow">ONE</div>
+ <div class="grow">TWO</div>
+ <div class="grow">THREE</div>
+ <div class="grow">FOUR</div>
+ <div class="grow">FIVE</div>
+ <div class="grow">SIX</div>
+ <div class="grow">SEVEN</div>
+ <div class="grow">EIGHT</div>
+ <div class="grow">NINE</div>
+ <div class="grow">TEN</div>
+ <div class="grow">ELEVEN</div>
+ <div class="grow">TWELVE</div>
+</div>
+
+## Footnotes
+
+This references a footnote here [^1] and here[^2];
+
+here are the references:
+[^1]: Some reference...
+[^2]: Some other reference...