parent
63a649405b
commit
116d4c385c
@ -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 |
||||
|
@ -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> |
@ -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" |
@ -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 |
@ -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/ |
@ -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> |
@ -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> |
@ -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) |
@ -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> |
After Width: | Height: | Size: 92 KiB |
@ -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> |
@ -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