Build docs as HTML
This commit is contained in:
parent
63a649405b
commit
116d4c385c
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,3 +1,10 @@
|
||||
/target
|
||||
/html/opentally.js
|
||||
/html/opentally_*.wasm
|
||||
|
||||
# Jekyll
|
||||
/homepage/_site
|
||||
/homepage/.sass-cache
|
||||
/homepage/.jekyll-cache
|
||||
/homepage/.jekyll-metadata
|
||||
/homepage/vendor
|
||||
|
@ -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.
|
||||
|
||||
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.
|
||||
|
||||
|
@ -27,7 +27,7 @@ The preset dropdown allows you to choose from a hardcoded list of preloaded STV
|
||||
|
||||
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>
|
||||
* [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.
|
||||
@ -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.
|
||||
* [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.
|
||||
|
||||
@ -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 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)
|
||||
|
||||
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.
|
||||
|
||||
|
25
homepage/404.html
Normal file
25
homepage/404.html
Normal 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
15
homepage/Gemfile
Normal 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
72
homepage/Gemfile.lock
Normal 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
28
homepage/_config.yml
Normal 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/
|
48
homepage/_layouts/default.html
Normal file
48
homepage/_layouts/default.html
Normal 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 © <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>
|
11
homepage/_layouts/post.html
Normal file
11
homepage/_layouts/post.html
Normal 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>
|
168
homepage/_plugins/include_absolute.rb
Normal file
168
homepage/_plugins/include_absolute.rb
Normal 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
12
homepage/about.md
Normal 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>
|
BIN
homepage/assets/headerimg.png
Normal file
BIN
homepage/assets/headerimg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 92 KiB |
23
homepage/docs/options.md
Normal file
23
homepage/docs/options.md
Normal 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
111
homepage/index.html
Normal 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>
|
Loading…
Reference in New Issue
Block a user