Build docs as HTML

This commit is contained in:
RunasSudo 2021-10-28 01:23:33 +11:00
parent 63a649405b
commit 116d4c385c
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
14 changed files with 525 additions and 5 deletions

7
.gitignore vendored
View File

@ -1,3 +1,10 @@
/target /target
/html/opentally.js /html/opentally.js
/html/opentally_*.wasm /html/opentally_*.wasm
# Jekyll
/homepage/_site
/homepage/.sass-cache
/homepage/.jekyll-cache
/homepage/.jekyll-metadata
/homepage/vendor

View File

@ -30,7 +30,7 @@ OpenTally is highly customisable, including options for:
After preparing the [BLT file](https://yingtongli.me/git/OpenTally/about/docs/blt.md), open the web UI. Select the BLT file, and click *Count*. OpenTally will count the election and display the results in a count sheet. After preparing the [BLT file](https://yingtongli.me/git/OpenTally/about/docs/blt.md), open the web UI. Select the BLT file, and click *Count*. OpenTally will count the election and display the results in a count sheet.
By clicking *Show advanced options*, you can customise the options used for the count. A detailed explanation of the various options can be found [here](https://yingtongli.me/git/OpenTally/about/docs/options.md). By clicking *Show advanced options*, you can customise the options used for the count. A detailed explanation of the various options can be found [here](/opentally/docs/options.html).
Once the count is complete, you can click *Print result* to generate a printable result report. Once the count is complete, you can click *Print result* to generate a printable result report.

View File

@ -27,7 +27,7 @@ The preset dropdown allows you to choose from a hardcoded list of preloaded STV
Exceptions: Exceptions:
* [E1] When generating random numbers, OpenTally uses a [deterministic random number generator based on SHA-256](rng.md), rather than the Wichmann–Hill(-based) algorithm. * [E1] When generating random numbers, OpenTally uses a [deterministic random number generator based on SHA-256](https://yingtongli.me/git/OpenTally/about/docs/rng.md), rather than the Wichmann–Hill(-based) algorithm.
* [E2] When breaking ties backwards, OpenTally selects the candidate who had more/fewer votes at the last stage when *any* tied candidate had more/fewer votes, rather than the method described in the legislation (when each all had unequal votes). The OpenTally developers regard the method described in the legislation as a defect. For an independent discussion, see <a href="https://dl.acm.org/doi/10.1145/3014812.3014837">Conway et al.</a> * [E2] When breaking ties backwards, OpenTally selects the candidate who had more/fewer votes at the last stage when *any* tied candidate had more/fewer votes, rather than the method described in the legislation (when each all had unequal votes). The OpenTally developers regard the method described in the legislation as a defect. For an independent discussion, see <a href="https://dl.acm.org/doi/10.1145/3014812.3014837">Conway et al.</a>
* [E3] A tie between 2 candidates for the final vacancy will be broken backwards then at random, rather than the method described in the legislation. * [E3] A tie between 2 candidates for the final vacancy will be broken backwards then at random, rather than the method described in the legislation.
* [E4] Bulk exclusion is not performed, as the prescribed rules are more conservative than OpenTally's. See also the section on *Bulk exclusion* for further discussion. * [E4] Bulk exclusion is not performed, as the prescribed rules are more conservative than OpenTally's. See also the section on *Bulk exclusion* for further discussion.
@ -36,7 +36,7 @@ Exceptions:
* [E7] No distinction is made between stages and substages (during exclusion). This affects only the numbering of stages and not the result. * [E7] No distinction is made between stages and substages (during exclusion). This affects only the numbering of stages and not the result.
* [E8] By default, the quota is always calculated to 2 decimal places. For full ERS76 (ERS73) compliance, set *Round quota to 0 d.p.* when the quota is more than 100 (100 or more). * [E8] By default, the quota is always calculated to 2 decimal places. For full ERS76 (ERS73) compliance, set *Round quota to 0 d.p.* when the quota is more than 100 (100 or more).
For details of validation, see [validation.md](validation.md). For details of validation, see [validation.md](https://yingtongli.me/git/OpenTally/about/docs/validation.md).
This functionality is not available on the command line. This functionality is not available on the command line.
@ -158,11 +158,11 @@ This option allows you to input an arbitrary value to seed the deterministic ran
The default value is the current date, formatted YYYYMMDD. The default value is the current date, formatted YYYYMMDD.
The algorithm used by the random number generator is specified at [rng.md](rng.md). The algorithm used by the random number generator is specified at [rng.md](https://yingtongli.me/git/OpenTally/about/docs/rng.md).
## Constraints (--constraints) ## Constraints (--constraints)
This file selector allows you to load a [CON file](con.md) specifying constraints on the election. For example, if a certain minimum or maximum number of candidates can be elected from a particular category. This file selector allows you to load a [CON file](https://yingtongli.me/git/OpenTally/about/docs/con.md) specifying constraints on the election. For example, if a certain minimum or maximum number of candidates can be elected from a particular category.
OpenTally applies constraints using the Grey–Fitzgerald method. Whenever a candidate is declared elected or excluded, any candidate who must be elected to secure a conformant result is deemed *guarded*, and any candidate who must not be elected to secure a conformant result is deemed *doomed*. Any candidate who is doomed is excluded at the next opportunity. Any candidate who is guarded is prevented from being excluded. OpenTally applies constraints using the Grey–Fitzgerald method. Whenever a candidate is declared elected or excluded, any candidate who must be elected to secure a conformant result is deemed *guarded*, and any candidate who must not be elected to secure a conformant result is deemed *doomed*. Any candidate who is doomed is excluded at the next opportunity. Any candidate who is guarded is prevented from being excluded.

25
homepage/404.html Normal file
View File

@ -0,0 +1,25 @@
---
permalink: /404.html
layout: default
---
<style type="text/css" media="screen">
.container {
margin: 10px auto;
max-width: 600px;
text-align: center;
}
h1 {
margin: 30px 0;
font-size: 4em;
line-height: 1;
letter-spacing: -1px;
}
</style>
<div class="container">
<h1>404</h1>
<p><strong>Page not found :(</strong></p>
<p>The requested page could not be found.</p>
</div>

15
homepage/Gemfile Normal file
View File

@ -0,0 +1,15 @@
source "https://rubygems.org"
gem "jekyll", "~> 4.2.0"
# Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem
# and associated library.
platforms :mingw, :x64_mingw, :mswin, :jruby do
gem "tzinfo", "~> 1.2"
gem "tzinfo-data"
end
# Performance-booster for watching directories on Windows
gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin]
# For Ruby 3.0
gem "webrick", "~> 1.7"

72
homepage/Gemfile.lock Normal file
View File

@ -0,0 +1,72 @@
GEM
remote: https://rubygems.org/
specs:
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
colorator (1.1.0)
concurrent-ruby (1.1.9)
em-websocket (0.5.2)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0.6.0)
eventmachine (1.2.7)
ffi (1.15.4)
forwardable-extended (2.6.0)
http_parser.rb (0.6.0)
i18n (1.8.10)
concurrent-ruby (~> 1.0)
jekyll (4.2.1)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
i18n (~> 1.0)
jekyll-sass-converter (~> 2.0)
jekyll-watch (~> 2.0)
kramdown (~> 2.3)
kramdown-parser-gfm (~> 1.0)
liquid (~> 4.0)
mercenary (~> 0.4.0)
pathutil (~> 0.9)
rouge (~> 3.0)
safe_yaml (~> 1.0)
terminal-table (~> 2.0)
jekyll-sass-converter (2.1.0)
sassc (> 2.0.1, < 3.0)
jekyll-watch (2.2.1)
listen (~> 3.0)
kramdown (2.3.1)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
liquid (4.0.3)
listen (3.7.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.4.0)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (4.0.6)
rb-fsevent (0.11.0)
rb-inotify (0.10.1)
ffi (~> 1.0)
rexml (3.2.5)
rouge (3.26.1)
safe_yaml (1.0.5)
sassc (2.4.0)
ffi (~> 1.9)
terminal-table (2.0.0)
unicode-display_width (~> 1.1, >= 1.1.1)
unicode-display_width (1.8.0)
webrick (1.7.0)
PLATFORMS
x86_64-linux
DEPENDENCIES
jekyll (~> 4.2.0)
tzinfo (~> 1.2)
tzinfo-data
wdm (~> 0.1.1)
webrick (~> 1.7)
BUNDLED WITH
2.2.26

28
homepage/_config.yml Normal file
View File

@ -0,0 +1,28 @@
# Site settings
title: OpenTally
baseurl: "/opentally"
url: "https://yingtongli.me"
git_url: "https://yingtongli.me/git/OpenTally"
# Build settings
plugins: []
kramdown:
smart_quotes: ["apos", "apos", "quot", "quot"]
typographic_symbols: {"mdash": "---", "ndash": "--"}
# Exclude from processing.
# exclude:
# - .sass-cache/
# - .jekyll-cache/
# - gemfiles/
# - Gemfile
# - Gemfile.lock
# - node_modules/
# - vendor/bundle/
# - vendor/cache/
# - vendor/gems/
# - vendor/ruby/

View File

@ -0,0 +1,48 @@
---
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>{% if post.title %}{{ post.title }}{% else %}{{ site.title }}{% endif %}</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css" integrity="sha256-PDJQdTN7dolQWDASIoBVrjkuOEaI137FI15sqI3Oxu8=" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
<body class="d-flex flex-column h-100">
<main class="flex-shrink-0">
<!-- Navigation-->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container px-5">
<a class="navbar-brand" href="{{ site.baseurl }}/">{{ site.title }}</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
<li class="nav-item"><a class="nav-link" href="{{ site.baseurl }}/">Home</a></li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" id="navbarDropdownBlog" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">Documentation</a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdownBlog">
<li><a class="dropdown-item" href="{{ site.baseurl }}/about.html">About OpenTally</a></li>
<li><a class="dropdown-item" href="{{ site.baseurl }}/docs/options.html">Advanced Options</a></li>
</ul>
</li>
<li class="nav-item"><a class="nav-link" href="{{ site.git_url }}/tree/">Source Code</a></li>
</ul>
</div>
</div>
</nav>
{{ content }}
</main>
<!-- Footer-->
<footer class="bg-dark py-4 mt-auto">
<div class="container px-5">
<div class="row align-items-center justify-content-between flex-column flex-sm-row">
<div class="col-auto"><div class="small m-0 text-white">Copyright &copy; <a href="{{ site.url }}" style="color:inherit;">Lee Yingtong Li</a> (RunasSudo) 2021</div></div>
</div>
</div>
</footer>
<!-- Bootstrap core JS-->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js" integrity="sha256-XDbijJp72GS2c+Ij234ZNJIyJ1Nv+9+HH1i28JuayMk=" crossorigin="anonymous"></script>
</body>
</html>

View File

@ -0,0 +1,11 @@
---
layout: default
---
<section class="py-5">
<div class="container px-5">
<h1 class="mb-4">{{ page.title }}</h1>
{{ content }}
</div>
</section>

View File

@ -0,0 +1,168 @@
module Jekyll
module Tags
class IncludeAbsoluteTagError < StandardError
attr_accessor :path
def initialize(msg, path)
super(msg)
@path = path
end
end
class IncludeAbsoluteTag < Liquid::Tag
VALID_SYNTAX = %r!
([\w-]+)\s*=\s*
(?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w\.-]+))
!x
VARIABLE_SYNTAX = %r!
(?<variable>[^{]*(\{\{\s*[\w\-\.]+\s*(\|.*)?\}\}[^\s{}]*)+)
(?<params>.*)
!mx
FULL_VALID_SYNTAX = %r!\A\s*(?:#{VALID_SYNTAX}(?=\s|\z)\s*)*\z!
VALID_FILENAME_CHARS = %r!^[\w/\.-]+$!
def initialize(tag_name, markup, tokens)
super
matched = markup.strip.match(VARIABLE_SYNTAX)
if matched
@file = matched["variable"].strip
@params = matched["params"].strip
else
@file, @params = markup.strip.split(%r!\s+!, 2)
end
validate_params if @params
@tag_name = tag_name
end
def syntax_example
"{% #{@tag_name} 'file.ext' param='value' param2='value' %}"
end
def parse_params(context)
params = {}
markup = @params
while (match = VALID_SYNTAX.match(markup))
markup = markup[match.end(0)..-1]
value = if match[2]
match[2].gsub(%r!\\"!, '"')
elsif match[3]
match[3].gsub(%r!\\'!, "'")
elsif match[4]
context[match[4]]
end
params[match[1]] = value
end
params
end
def validate_file_name(file)
if file !~ VALID_FILENAME_CHARS
raise ArgumentError, <<-MSG
Invalid syntax for include tag. File contains invalid characters or sequences:
#{file}
Valid syntax:
#{syntax_example}
MSG
end
end
def validate_params
unless @params =~ FULL_VALID_SYNTAX
raise ArgumentError, <<-MSG
Invalid syntax for include tag:
#{@params}
Valid syntax:
#{syntax_example}
MSG
end
end
# Grab file read opts in the context
def file_read_opts(context)
context.registers[:site].file_read_opts
end
# Render the variable if required
def render_variable(context)
if @file =~ VARIABLE_SYNTAX
partial = context.registers[:site]
.liquid_renderer
.file("(variable)")
.parse(@file)
partial.render!(context)
end
end
def render(context)
site = context.registers[:site]
file = render_variable(context) || @file
# strip leading and trailing quote's
file = file.gsub!(/\A'|'\Z/, '')
validate_file_name(file)
source = File.expand_path(context.registers[:site].config['source']).freeze
path = File.join(source, file)
return unless path
partial = Liquid::Template.parse(read_file(path, context))
context.stack do
context["include"] = parse_params(context) if @params
begin
partial.render!(context)
rescue Liquid::Error => e
e.template_name = path
e.markup_context = "included " if e.markup_context.nil?
raise e
end
end
end
def valid_include_file?(path, dir, safe)
!outside_site_source?(path, dir, safe) && File.file?(path)
end
def outside_site_source?(path, dir, safe)
safe && !realpath_prefixed_with?(path, dir)
end
def realpath_prefixed_with?(path, dir)
File.exist?(path) && File.realpath(path).start_with?(dir)
rescue StandardError
false
end
# This method allows to modify the file content by inheriting from the class.
def read_file(file, context)
File.read(file, **file_read_opts(context))
end
private
def could_not_locate_message(file, includes_dirs, safe)
message = "Could not locate the included file '#{file}' in any of "\
"#{includes_dirs}. Ensure it exists in one of those directories and"
message + if safe
" is not a symlink as those are not allowed in safe mode."
else
", if it is a symlink, does not point outside your site source."
end
end
end
end
end
Liquid::Template.register_tag("include_absolute", Jekyll::Tags::IncludeAbsoluteTag)

12
homepage/about.md Normal file
View File

@ -0,0 +1,12 @@
---
layout: post
title: "About OpenTally"
---
<div class="md-content" markdown="1">{% include_absolute '../README.md' %}</div>
<style type="text/css">
.md-content h1 {
display: none;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

23
homepage/docs/options.md Normal file
View File

@ -0,0 +1,23 @@
---
layout: post
title: "Advanced options"
---
<div class="md-content" markdown="1">{% include_absolute '../docs/options.md' %}</div>
<style type="text/css">
.md-content h1 {
display: none;
}
.md-content h2 {
margin-bottom: 1rem;
}
.md-content h3 {
font-size: 1.5rem;
margin-bottom: 1rem;
}
</style>
<script>
document.querySelectorAll('.md-content table').forEach(el => el.classList.add('table'));
</script>

111
homepage/index.html Normal file
View File

@ -0,0 +1,111 @@
---
layout: default
---
<!-- Header-->
<header class="bg-dark py-5">
<div class="container px-5">
<div class="row gx-5 align-items-center justify-content-center">
<div class="col-lg-8 col-xl-7 col-xxl-6">
<div class="my-5 text-center text-xl-start">
<h1 class="display-5 fw-bolder text-white mb-2">Advanced online election counting</h1>
<p class="lead fw-normal text-white-50 mb-4">Count instant runoff and single transferable vote elections for free, no downloads or sign-up required</p>
<div class="d-grid gap-3 d-sm-flex justify-content-sm-center justify-content-xl-start">
<a class="btn btn-primary btn-lg px-4 me-sm-3" href="{{ site.baseurl }}/stv/">Get Started</a>
<a class="btn btn-outline-light btn-lg px-4" href="{{ site.baseurl }}/about.html">Learn More</a>
</div>
</div>
</div>
<div class="col-xl-5 col-xxl-6 d-none d-xl-block text-center"><img class="img-fluid rounded-3 my-5" src="{{ site.baseurl }}/assets/headerimg.png" alt="Screenshot of OpenTally" /></div>
</div>
</div>
</header>
<!-- Features section-->
<section class="py-5 bg-light" id="features">
<div class="container px-5 my-5">
<div class="row gx-5">
<div class="col-lg-4 mb-5 mb-lg-0"><h2 class="fw-bolder mb-0">Key features</h2></div>
<div class="col-lg-8">
<div class="row gx-5 row-cols-1 row-cols-md-2">
<div class="col mb-5 h-100">
<h2 class="h5">Runs in your browser</h2>
<p class="mb-0">No downloads or sign-ups are required. OpenTally counts are computed entirely inside your browser, and no data ever leaves your computer.</p>
</div>
<div class="col mb-5 h-100">
<h2 class="h5">Wide range of STV systems</h2>
<p class="mb-0">OpenTally supports Gregory (inclusive and exclusive, weighted and unweighted), Meek and Wright variants of the single transferable vote.</p>
</div>
<div class="col mb-5 mb-md-0 h-100">
<h2 class="h5">Support for arbitrary constraints</h2>
<p class="mb-0">OpenTally is the only publicly available election counting software to support arbitrary combinations of constraints, such as gender quotas and other affirmative action requirements.</p>
</div>
<div class="col h-100">
<h2 class="h5">Free and open source</h2>
<p class="mb-0">Source code for OpenTally is publicly available under the <a href="{{ site.git_url }}/tree/COPYING">GNU AGPLv3</a>.</p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Blog preview section-->
<section class="py-5">
<div class="container px-5 my-5">
<div class="row gx-5 justify-content-center">
<div class="col-lg-8 col-xl-6">
<div class="text-center">
<h2 class="fw-bolder">From our blog</h2>
<p class="lead fw-normal text-muted mb-5">Latest news and posts from the OpenTally blog</p>
</div>
</div>
</div>
<div class="row gx-5">
<div class="col-lg-4 mb-5">
<div class="card h-100 shadow border-0">
<div class="card-body p-4">
<div class="badge bg-primary bg-gradient rounded-pill mb-2">Dev Log</div>
<a class="text-decoration-none link-dark stretched-link" href="{{ site.url }}/blog/2021/08/21/stv-parcels.html"><h5 class="card-title mb-3">Parcels? Subparcels? Not just for STV hand counts!</h5></a>
<p class="card-text mb-0">Single transferable vote rules designed for hand-counting often contain references to ‘parcels’ (or ‘bundles’ or ‘batches’), ‘further parcels’ and sometimes even ‘subparcels’.</p>
<p>For example, consider the <a href="#">4th stage of the ERS97 model election</a>. 107 of Glazier's and Wright's ballot papers are aggregated according to…</p>
</div>
<div class="card-footer p-4 pt-0 bg-transparent border-top-0">
<div class="small text-muted">21 August 2021</div>
</div>
</div>
</div>
<div class="col-lg-4 mb-5">
<div class="card h-100 shadow border-0">
<div class="card-body p-4">
<div class="badge bg-primary bg-gradient rounded-pill mb-2">Dev Log</div>
<a class="text-decoration-none link-dark stretched-link" href="{{ site.url }}/blog/2021/07/30/blt-parser.html"><h5 class="card-title mb-3">Implementing a BLT parser by hand in Rust (vs pest and combine)</h5></a>
<p class="card-text"><a href="#">OpenTally</a> is open-source software which can count single transferable vote elections specified using the <a href="#">BLT file format</a>.</p>
<p class="card-text mb-0">Earlier this month, I <a href="#">replaced</a> OpenTally's previous naive <a href="#">string-manipulation-based BLT parser</a> with one using <a href="#">pest</a>. A new parser was necessary to support <a href="#">extensions to the BLT</a></p>
</div>
<div class="card-footer p-4 pt-0 bg-transparent border-top-0">
<div class="small text-muted">30 July 2021</div>
</div>
</div>
</div>
<div class="col-lg-4 mb-5">
<div class="card h-100 shadow border-0">
<div class="card-body p-4">
<div class="badge bg-primary bg-gradient rounded-pill mb-2">Dev Log</div>
<a class="text-decoration-none link-dark stretched-link" href="{{ site.url }}/blog/2021/07/28/asyncify-vanilla.html"><h5 class="card-title mb-3">Asyncify with vanilla JS/<wbr>WebAssembly (wasm-bindgen compatible)</h5></a>
<p class="card-text">WebAssembly is a technology for executing compiled programs in the web browser at near-native speeds. However, it has a number of current limitations, including that it does not support coroutines/<wbr>asynchronicity.</p>
<p class="card-text mb-0">In <a href="#">OpenTally</a>, WebAssembly is used to run code for counting an election. This…</p>
</div>
<div class="card-footer p-4 pt-0 bg-transparent border-top-0">
<div class="small text-muted">28 July 2021</div>
</div>
</div>
</div>
</div>
<div class="row gx-5 justify-content-center">
<div class="col-8 text-center">
<a class="btn btn-outline-primary btn-lg px-4" href="{{ site.url }}/blog/tag/opentally/">Read More</a>
</div>
</div>
</div>
</section>