- simplifications - re-added future support - fix for exists of category - new footer - footnote display fixes
@@ -1,4 +1,5 @@
#!/usr/bin/env python3 +import _json __author__ = "Peter Molnar" __copyright__ = "Copyright 2017-2018, Peter Molnar"@@ -15,6 +16,7 @@ import imghdr
import logging import asyncio import sqlite3 +import json from shutil import copy2 as cp from math import ceil from urllib.parse import urlparse@@ -28,6 +30,7 @@ import markdown
from feedgen.feed import FeedGenerator from bleach import clean from emoji import UNICODE_EMOJI +from slugify import slugify import requests import exiftool import settings@@ -171,20 +174,11 @@ source, fext = os.path.splitext(os.path.basename(self.fpath))
return source -class Redirect(object): +class Redirect(Gone): """ Redirect object for entries that moved """ - def __init__(self, fpath): - self.fpath = fpath - self.mtime = os.path.getmtime(fpath) - - @property - def source(self): - source, fext = os.path.splitext(os.path.basename(self.fpath)) - return source - @property @cached() def target(self):@@ -311,14 +305,7 @@
@property @cached() def html_summary(self): - return markdown.Markdown( - output_format='html5', - extensions=[ - 'extra', - 'codehilite', - 'headerid', - ], - ).convert(self.summary) + return MD.reset().convert(self.summary) @property def title(self):@@ -342,23 +329,23 @@ if self.is_photo:
urls.append("https://brid.gy/publish/flickr") return urls - # def baseN(self, num, b=36, - # numerals="0123456789abcdefghijklmnopqrstuvwxyz"): - # """ - # Creates short, lowercase slug for a number (an epoch) passed - # """ - #num = int(num) - # return ((num == 0) and numerals[0]) or ( - # self.baseN( - #num // b, - # b, - # numerals - # ).lstrip(numerals[0]) + numerals[num % b] - # ) + def baseN(self, num, b=36, + numerals="0123456789abcdefghijklmnopqrstuvwxyz"): + """ + Creates short, lowercase slug for a number (an epoch) passed + """ + num = int(num) + return ((num == 0) and numerals[0]) or ( + self.baseN( + num // b, + b, + numerals + ).lstrip(numerals[0]) + numerals[num % b] + ) - # @property - # def shortslug(self): - # return self.baseN(self.published.timestamp) + @property + def shortslug(self): + return self.baseN(self.published.timestamp) @property def published(self):@@ -478,6 +465,10 @@ self.name,
self.summary, self.content, ]) + + async def copyfiles(self): + # TODO: plain copy non-image files from entry directory to build/entry directory + return async def render(self): if self.exists:@@ -809,7 +800,7 @@ if self.crop:
thumb.liquid_rescale(self.size, self.size, 1, 1) if self.parent.meta.get('FileType', 'jpeg').lower() == 'jpeg': - thumb.compression_quality = 94 + thumb.compression_quality = 88 thumb.unsharp_mask( radius=1, sigma=0.5,@@ -920,10 +911,14 @@
@property def url(self): if len(self.name): - url = "/category/%s/" % (self.name) + url = "%s/category/%s/" % (settings.site.get('url'), self.name) else: - url = '/' + url = '%s/' % (settings.site.get('url')) return url + + @property + def feed(self): + return "%sfeed/" % (self.url) @property def template(self):@@ -952,13 +947,13 @@ }
@property def mtime(self): - return self[self.sortedkeys[0]].mtime + return arrow.get(self[self.sortedkeys[0]].published).timestamp @property def exists(self): if settings.args.get('force'): return False - renderfile = os.path.join(self.renderdir, 'index.html') + renderfile = os.path.join(self.renderdir, 'feed', 'index.xml') if not os.path.exists(renderfile): return False elif self.mtime > os.path.getmtime(renderfile):@@ -966,19 +961,6 @@ return False
else: return True - def ping_websub(self): - return - # TODO aiohttp? - # ping pubsub - # r = requests.post( - # shared.site.get('websub').get('hub'), - # data={ - # 'hub.mode': 'publish', - # 'hub.url': flink - # } - # ) - # logging.info(r.text) - def render_feed(self): logging.info('rendering category "%s" ATOM feed', self.name) start = 0@@ -989,29 +971,46 @@ if not os.path.isdir(dirname):
os.makedirs(dirname) fg = FeedGenerator() - - flink = "%s%sfeed/" % (settings.site.get('url'), self.url) - - fg.id(flink) - fg.link(href=flink, rel='self') + fg.id(self.feed) + fg.link(href=self.feed, rel='self') fg.title(self.title) - fg.author({ 'name': settings.author.get('name'), 'email': settings.author.get('email') }) - fg.logo('%s/favicon.png' % settings.site.get('url')) - fg.updated(arrow.get(self.mtime).to('utc').datetime) + jselements = [] + jsfeed = { + 'items': jselements, + 'version': 'https://jsonfeed.org/version/1', + 'title': self.title, + 'home_page_url': settings.site.get('url'), + 'feed_url': self.feed, + 'author': { + 'name': settings.author.get('name'), + 'url': settings.author.get('url'), + 'email': settings.author.get('email'), + } + } + for post in self.get_posts(start, end): dt = arrow.get(post.get('pubtime')) + jselements.append({ + "title": post.get('title'), + "date_published": post.get('pubtime'), + "id": post.get('url'), + "url": post.get('url'), + "content_html": post.get('html_content') + }) + fe = fg.add_entry() fe.id(post.get('url')) fe.link(href=post.get('url')) fe.title(post.get('title')) fe.published(dt.datetime) + fe.updated(dt.datetime) fe.content( post.get('html_content'), type='CDATA'@@ -1024,7 +1023,6 @@ dt.format('YYYY')
)) if 'enclosure' in post: enc = post.get('enclosure') - logging.info("enclosure found:", enc) fe.enclosure( enc.get('url'), "%d" % enc.get('size'),@@ -1034,6 +1032,10 @@ atom = os.path.join(dirname, 'index.xml')
with open(atom, 'wb') as f: logging.info('writing file: %s', atom) f.write(fg.atom_str(pretty=True)) + jsfile = os.path.join(dirname, 'index.json') + with open(jsfile, 'wt') as f: + logging.info('writing file: %s', jsfile) + f.write(json.dumps(jsfeed, indent=4, sort_keys=True)) def render_page(self, pagenum=1, pages=1): if self.display == 'flat':@@ -1084,7 +1086,6 @@ while page <= pages:
self.render_page(page, pages) page = page + 1 self.render_feed() - self.ping_websub() class Search(object):@@ -1292,27 +1293,29 @@ categories['/'] = Category()
for e in sorted(glob.glob(os.path.join(content, '*', '*', 'index.md'))): post = Singular(e) - if post.category not in categories: - categories[post.category] = Category(post.category) - c = categories[post.category] - c[post.published.timestamp] = post - if post.is_front: - c = categories['/'] - c[post.published.timestamp] = post + worker.append(post.render()) + worker.append(post.copyfiles()) for i in post.images.values(): worker.append(i.downsize()) - worker.append(post.render()) - if post.ctime > last: - last = post.ctime - sitemap[post.url] = post.mtime - search.append( - url=post.url, - mtime=post.mtime, - name=post.name, - title=post.title, - category=post.category, - content=post.content - ) + if post.is_future: + continue + else: + if post.category not in categories: + categories[post.category] = Category(post.category) + categories[post.category][post.published.timestamp] = post + if post.is_front: + categories['/'][post.published.timestamp] = post + if post.ctime > last: + last = post.ctime + sitemap[post.url] = post.mtime + search.append( + url=post.url, + mtime=post.mtime, + name=post.name, + title=post.title, + category=post.category, + content=post.content + ) search.__exit__() worker.append(search.render())
@@ -64,6 +64,7 @@ 'cv': 'https://petermolnar.net/about.html',
'xmpp': 'mail@petermolnar.net', 'flickr': 'petermolnareu', 'github': 'petermolnar', + 'twitter': 'petermolnar' } paths = {
@@ -2,14 +2,14 @@ {% extends "base.j2.html" %}
{% block lang %}{% endblock %} {% block title %}{{ category.title }}{% endblock %} {% block meta %} - <link rel="alternate" type="application/atom+xml" title="{{ category.title }} feed" href="{{ site.url }}{{ category.feed }}" /> + <link rel="alternate" type="application/atom+xml" title="{{ category.title }} feed" href="{{ category.feed }}" /> {% endblock %} {% block content %} <section class="content-body h-feed"> <header> <nav> <p class="follow"> - <a title="follow {{ category.title }}" rel="feed" href="{{ site.url }}{{ category.feed }}"> + <a title="follow {{ category.title }}" rel="feed" href="{{ category.feed }}"> <svg class="icon" width="16" height="16"> <use xlink:href="#icon-rss" /> </svg>@@ -61,7 +61,7 @@ </header>
{% if post.summary %} <div class="e-summary entry-summary"> - {{ post.summary }} + {{ post.html_summary }} <p class="more"> <a href="/{{ post.slug }}" title="{{ post.title }}"> {% if post.lang == 'hu' %}Tovรกbb ยป{% else %}Continue ยป{% endif %}
@@ -338,90 +338,92 @@
<footer class="content-footer" id="main-footer"> <nav class="footer-contact p-author h-card vcard limit"> - <h2>Site author</h2> - <dl> - <dt> - <img class="photo avatar u-photo u-avatar" - src="https://petermolnar.net/molnar_peter_avatar.jpg" - alt="Photo of Peter Molnar" /> - </dt> - <dd> - <a class="fn p-name url u-url u-uid" href="/about.html"> - Peter Molnar - </a> - </dd> - <dt>email</dt> - <dd> - <a rel="me" class="u-email email" href="mailto:{{ author.email }}"> - {{ author.email }} - </a> - </dd> -{% if author.sip %} - <dt>SIP</dt> - <dd> - <a rel="me" class="u-sip sip" href="sip:{{ author.sip }}"> - {{ author.sip }} - </a> - </dd> -{% endif %} -{% if author.xmpp %} - <dt>XMPP</dt> - <dd> - <a rel="me" class="u-xmpp xmpp" href="xmpp:{{ author.xmpp }}">{{ author.xmpp }}</a> - </dd> -{% endif %} -{% if author.gpg %} - <dt>GPG</dt> - <dd> - <a rel="me" class="u-gpg gpg" href="/{{ author.gpg }}">key</a> - </dd> -{% endif %} -{% if author.flickr %} - <dt>flickr</dt> - <dd> - <a rel="me" class="u-flickr" href="https://flickr.com/people/{{ author.flickr }}">{{ author.flickr }}</a> - </dd> -{% endif %} -{% if author.github %} - <dt>github</dt> - <dd> - <a rel="me" class="u-github" href="https://github.com/{{ author.github }}">{{ author.github }}</a> - </dd> -{% endif %} - </dl> + <h2>Contact</h2> + <div class="twothird"> + <dl> + <dt> + <img class="photo avatar u-photo u-avatar" + src="https://petermolnar.net/molnar_peter_avatar.jpg" + alt="Photo of Peter Molnar" /> + </dt> + <dd> + <a class="fn p-name url u-url u-uid" href="/about.html"> + Peter Molnar + </a> + </dd> + <dt>email</dt> + <dd> + <a rel="me" class="u-email email" href="mailto:{{ author.email }}"> + {{ author.email }} + </a> + </dd> + {% if author.gpg %} + <dt>GPG</dt> + <dd> + <a rel="me" class="u-gpg gpg" href="/{{ author.gpg }}">key</a> + </dd> + {% endif %} + <dt>url</dt> + <dd> + <a rel="me" class="u-url url" href="{{ author.url }}"> + {{ author.url }} + </a> + </dd> + </dl> + </div> + <div class="onethird"> + <dl> + {% if author.xmpp %} + <dt>XMPP</dt> + <dd> + <a rel="me" class="u-xmpp xmpp" href="xmpp:{{ author.xmpp }}">{{ author.xmpp }}</a> + </dd> + {% endif %} + {% if author.sip %} + <dt>SIP</dt> + <dd> + <a rel="me" class="u-sip sip" href="sip:{{ author.sip }}"> + {{ author.sip }} + </a> + </dd> + {% endif %} + {% if author.icq %} + <dt>ICQ</dt> + <dd> + <a rel="me" class="u-icq icq" href="icq:{{ author.icq }}"> + {{ author.icq }} + </a> + </dd> + {% endif %} + {% if author.flickr %} + <dt>flickr</dt> + <dd> + <a rel="me" class="u-flickr" href="https://flickr.com/people/{{ author.flickr }}">{{ author.flickr }}</a> + </dd> + {% endif %} + {% if author.github %} + <dt>github</dt> + <dd> + <a rel="me" class="u-github" href="https://github.com/{{ author.github }}">{{ author.github }}</a> + </dd> + {% endif %} + {% if author.twitter %} + <dt>twitter</dt> + <dd> + <a rel="me" class="u-twitter" href="https://twitter.com/{{ author.twitter }}">{{ author.twitter }}</a> + </dd> + {% endif %} + </dl> + </div> </nav> + <div class="webring"> + <a href="https://xn--sr8hvo.ws/๐ป๐ฎ๐ข/previous">โ</a> + Member of <a href="https://xn--sr8hvo.ws">IndieWeb Webring</a> ๐ธ๐ + <a href="https://xn--sr8hvo.ws/๐ป๐ฎ๐ข/next">โ</a> + </div> </footer> {% include 'symbols.svg' %} - - <!-- - This is a self-hosted analytics code, called Matomo - I configured it to avoid placing cookies, respect Do Not Track, - remove the last segment of IP information, so it stores as little - information as possible. - --> - <script type="text/javascript"> - var _paq = _paq || []; - _paq.push(["setDoNotTrack", true]); - _paq.push(["disableCookies"]); - _paq.push(['trackPageView']); - _paq.push(['enableLinkTracking']); - (function() { - var u="//stats.petermolnar.net/"; - _paq.push(['setTrackerUrl', u+'piwik.php']); - _paq.push(['setSiteId', '{{ site.piwik.id }}']); - var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; - g.type='text/javascript'; - g.async=true; - g.defer=true; - g.src=u+'piwik.js'; - s.parentNode.insertBefore(g,s); - })(); - </script> - <noscript> - <img src="https://{{ site.piwik.domain }}/piwik.php?idsite=1&rec=1" style="border:0" alt="" /> - </noscript> - </body> </html>
@@ -177,82 +177,82 @@ }
.codehilite .hll { background-color: #222222 } .codehilite { background: #000000; color: #cccccc } -.codehilite .c { color: #000080 } /* Comment */ -.codehilite .err { color: #cccccc; border: 1px solid #FF0000 } /* Error */ -.codehilite .esc { color: #cccccc } /* Escape */ -.codehilite .g { color: #cccccc } /* Generic */ -.codehilite .k { color: #cdcd00 } /* Keyword */ -.codehilite .l { color: #cccccc } /* Literal */ -.codehilite .n { color: #cccccc } /* Name */ -.codehilite .o { color: #3399cc } /* Operator */ -.codehilite .x { color: #cccccc } /* Other */ -.codehilite .p { color: #cccccc } /* Punctuation */ -.codehilite .ch { color: #000080 } /* Comment.Hashbang */ -.codehilite .cm { color: #000080 } /* Comment.Multiline */ -.codehilite .cp { color: #000080 } /* Comment.Preproc */ -.codehilite .cpf { color: #000080 } /* Comment.PreprocFile */ -.codehilite .c1 { color: #000080 } /* Comment.Single */ -.codehilite .cs { color: #cd0000; font-weight: bold } /* Comment.Special */ -.codehilite .gd { color: #cd0000 } /* Generic.Deleted */ -.codehilite .ge { color: #cccccc; font-style: italic } /* Generic.Emph */ -.codehilite .gr { color: #FF0000 } /* Generic.Error */ -.codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.codehilite .gi { color: #00cd00 } /* Generic.Inserted */ -.codehilite .go { color: #888888 } /* Generic.Output */ -.codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ -.codehilite .gs { color: #cccccc; font-weight: bold } /* Generic.Strong */ -.codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.codehilite .gt { color: #0044DD } /* Generic.Traceback */ -.codehilite .kc { color: #cdcd00 } /* Keyword.Constant */ -.codehilite .kd { color: #00cd00 } /* Keyword.Declaration */ -.codehilite .kn { color: #cd00cd } /* Keyword.Namespace */ -.codehilite .kp { color: #cdcd00 } /* Keyword.Pseudo */ -.codehilite .kr { color: #cdcd00 } /* Keyword.Reserved */ -.codehilite .kt { color: #00cd00 } /* Keyword.Type */ -.codehilite .ld { color: #cccccc } /* Literal.Date */ -.codehilite .m { color: #cd00cd } /* Literal.Number */ -.codehilite .s { color: #cd0000 } /* Literal.String */ -.codehilite .na { color: #cccccc } /* Name.Attribute */ -.codehilite .nb { color: #cd00cd } /* Name.Builtin */ -.codehilite .nc { color: #00cdcd } /* Name.Class */ -.codehilite .no { color: #cccccc } /* Name.Constant */ -.codehilite .nd { color: #cccccc } /* Name.Decorator */ -.codehilite .ni { color: #cccccc } /* Name.Entity */ -.codehilite .ne { color: #666699; font-weight: bold } /* Name.Exception */ -.codehilite .nf { color: #cccccc } /* Name.Function */ -.codehilite .nl { color: #cccccc } /* Name.Label */ -.codehilite .nn { color: #cccccc } /* Name.Namespace */ -.codehilite .nx { color: #cccccc } /* Name.Other */ -.codehilite .py { color: #cccccc } /* Name.Property */ -.codehilite .nt { color: #cccccc } /* Name.Tag */ -.codehilite .nv { color: #00cdcd } /* Name.Variable */ -.codehilite .ow { color: #cdcd00 } /* Operator.Word */ -.codehilite .w { color: #cccccc } /* Text.Whitespace */ -.codehilite .mb { color: #cd00cd } /* Literal.Number.Bin */ -.codehilite .mf { color: #cd00cd } /* Literal.Number.Float */ -.codehilite .mh { color: #cd00cd } /* Literal.Number.Hex */ -.codehilite .mi { color: #cd00cd } /* Literal.Number.Integer */ -.codehilite .mo { color: #cd00cd } /* Literal.Number.Oct */ -.codehilite .sa { color: #cd0000 } /* Literal.String.Affix */ -.codehilite .sb { color: #cd0000 } /* Literal.String.Backtick */ -.codehilite .sc { color: #cd0000 } /* Literal.String.Char */ -.codehilite .dl { color: #cd0000 } /* Literal.String.Delimiter */ -.codehilite .sd { color: #cd0000 } /* Literal.String.Doc */ -.codehilite .s2 { color: #cd0000 } /* Literal.String.Double */ -.codehilite .se { color: #cd0000 } /* Literal.String.Escape */ -.codehilite .sh { color: #cd0000 } /* Literal.String.Heredoc */ -.codehilite .si { color: #cd0000 } /* Literal.String.Interpol */ -.codehilite .sx { color: #cd0000 } /* Literal.String.Other */ -.codehilite .sr { color: #cd0000 } /* Literal.String.Regex */ -.codehilite .s1 { color: #cd0000 } /* Literal.String.Single */ -.codehilite .ss { color: #cd0000 } /* Literal.String.Symbol */ -.codehilite .bp { color: #cd00cd } /* Name.Builtin.Pseudo */ -.codehilite .fm { color: #cccccc } /* Name.Function.Magic */ -.codehilite .vc { color: #00cdcd } /* Name.Variable.Class */ -.codehilite .vg { color: #00cdcd } /* Name.Variable.Global */ -.codehilite .vi { color: #00cdcd } /* Name.Variable.Instance */ -.codehilite .vm { color: #00cdcd } /* Name.Variable.Magic */ -.codehilite .il { color: #cd00cd } /* Literal.Number.Integer.Long */ +.codehilite .c { color: #000080 } +.codehilite .err { color: #cccccc; border: 1px solid #FF0000 } +.codehilite .esc { color: #cccccc } +.codehilite .g { color: #cccccc } +.codehilite .k { color: #cdcd00 } +.codehilite .l { color: #cccccc } +.codehilite .n { color: #cccccc } +.codehilite .o { color: #3399cc } +.codehilite .x { color: #cccccc } +.codehilite .p { color: #cccccc } +.codehilite .ch { color: #000080 } +.codehilite .cm { color: #000080 } +.codehilite .cp { color: #000080 } +.codehilite .cpf { color: #000080 } +.codehilite .c1 { color: #000080 } +.codehilite .cs { color: #cd0000; font-weight: bold } +.codehilite .gd { color: #cd0000 } +.codehilite .ge { color: #cccccc; font-style: italic } +.codehilite .gr { color: #FF0000 } +.codehilite .gh { color: #000080; font-weight: bold } +.codehilite .gi { color: #00cd00 } +.codehilite .go { color: #888888 } +.codehilite .gp { color: #000080; font-weight: bold } +.codehilite .gs { color: #cccccc; font-weight: bold } +.codehilite .gu { color: #800080; font-weight: bold } +.codehilite .gt { color: #0044DD } +.codehilite .kc { color: #cdcd00 } +.codehilite .kd { color: #00cd00 } +.codehilite .kn { color: #cd00cd } +.codehilite .kp { color: #cdcd00 } +.codehilite .kr { color: #cdcd00 } +.codehilite .kt { color: #00cd00 } +.codehilite .ld { color: #cccccc } +.codehilite .m { color: #cd00cd } +.codehilite .s { color: #cd0000 } +.codehilite .na { color: #cccccc } +.codehilite .nb { color: #cd00cd } +.codehilite .nc { color: #00cdcd } +.codehilite .no { color: #cccccc } +.codehilite .nd { color: #cccccc } +.codehilite .ni { color: #cccccc } +.codehilite .ne { color: #666699; font-weight: bold } +.codehilite .nf { color: #cccccc } +.codehilite .nl { color: #cccccc } +.codehilite .nn { color: #cccccc } +.codehilite .nx { color: #cccccc } +.codehilite .py { color: #cccccc } +.codehilite .nt { color: #cccccc } +.codehilite .nv { color: #00cdcd } +.codehilite .ow { color: #cdcd00 } +.codehilite .w { color: #cccccc } +.codehilite .mb { color: #cd00cd } +.codehilite .mf { color: #cd00cd } +.codehilite .mh { color: #cd00cd } +.codehilite .mi { color: #cd00cd } +.codehilite .mo { color: #cd00cd } +.codehilite .sa { color: #cd0000 } +.codehilite .sb { color: #cd0000 } +.codehilite .sc { color: #cd0000 } +.codehilite .dl { color: #cd0000 } +.codehilite .sd { color: #cd0000 } +.codehilite .s2 { color: #cd0000 } +.codehilite .se { color: #cd0000 } +.codehilite .sh { color: #cd0000 } +.codehilite .si { color: #cd0000 } +.codehilite .sx { color: #cd0000 } +.codehilite .sr { color: #cd0000 } +.codehilite .s1 { color: #cd0000 } +.codehilite .ss { color: #cd0000 } +.codehilite .bp { color: #cd00cd } +.codehilite .fm { color: #cccccc } +.codehilite .vc { color: #00cdcd } +.codehilite .vg { color: #00cdcd } +.codehilite .vi { color: #00cdcd } +.codehilite .vm { color: #00cdcd } +.codehilite .il { color: #cd00cd } .limit, .content-body {@@ -358,6 +358,7 @@
figure { position:relative; overflow: hidden; + margin: 2em 0; } figure figcaption {@@ -399,18 +400,26 @@ .exif .icon {
width: 1em; } -.footnoteRef, -.footnoteRef:hover { +.footnote-ref { border: 0; white-space: nowrap; - margin-left: 0.2em; + margin-left: 0.2em; } -.footnotes ol li p { +.footnote-ref::before { + content: '['; +} + +.footnote-ref::after { + content: ']'; +} + +.footnote ol li p { margin: 0; + padding: 0; } -.footnotes ol li a { +.footnote ol li a { display: inline-block; overflow: hidden; white-space: nowrap;@@ -419,22 +428,35 @@ vertical-align: top;
max-width: 80%; } -.footnotes ol li a[href^="#"] { +.footnote ol li a[href^="#"] { margin-left: 0.6em; max-width: 20%; font-size: 0.6em; } -.footnotes ol li a[href^="#"]:after { +.footnote ol li a[href^="#"]:after { content: "back to text"; margin-left: 0.2em; color: #666; } -.footnotes ol li a[href^="#"]:hover:after { +.footnote ol li a[href^="#"]:hover:after { color: #ccc; } +.twothird { + display: inline-block; + width: 65%; + vertical-align:text-top; +} + +.onethird { + display: inline-block; + width: 33%; + vertical-align:text-top; +} + + .content-footer { margin-top: 2em; padding: 1em 0;@@ -454,15 +476,17 @@ .content-footer a:hover {
color: #fff; } -.content-footer dl * { - display: inline-block; - padding: 0; - margin: 0; - line-height: 1.6em; +.content-footer dl { + position: relative; +} + +.content-footer dt { + display: block; } .content-footer dd { - margin-right: 1em; + display: block; + margin: -2.3em 0 0 4em; } .content-footer img {@@ -552,3 +576,8 @@ .donation li a:hover {
color: #fff; border-bottom: 3px solid #c66 !important; } + +.webring { + padding: 2em 0 0 0; + text-align: center; +}
@@ -20,73 +20,81 @@ }
.codehilite .hll { background-color: #ffffcc } .codehilite { background: #f8f8f8; } -.codehilite .c { color: #408080; font-style: italic } /* Comment */ -.codehilite .err { border: 1px solid #FF0000 } /* Error */ -.codehilite .k { color: #008000; font-weight: bold } /* Keyword */ -.codehilite .o { color: #666666 } /* Operator */ -.codehilite .ch { color: #408080; font-style: italic } /* Comment.Hashbang */ -.codehilite .cm { color: #408080; font-style: italic } /* Comment.Multiline */ -.codehilite .cp { color: #BC7A00 } /* Comment.Preproc */ -.codehilite .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */ -.codehilite .c1 { color: #408080; font-style: italic } /* Comment.Single */ -.codehilite .cs { color: #408080; font-style: italic } /* Comment.Special */ -.codehilite .gd { color: #A00000 } /* Generic.Deleted */ -.codehilite .ge { font-style: italic } /* Generic.Emph */ -.codehilite .gr { color: #FF0000 } /* Generic.Error */ +.codehilite .c { color: #8f5902; font-style: italic } /* Comment */ +.codehilite .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ +.codehilite .g { color: #000000 } /* Generic */ +.codehilite .k { color: #204a87; font-weight: bold } /* Keyword */ +.codehilite .l { color: #000000 } /* Literal */ +.codehilite .n { color: #000000 } /* Name */ +.codehilite .o { color: #ce5c00; font-weight: bold } /* Operator */ +.codehilite .x { color: #000000 } /* Other */ +.codehilite .p { color: #000000; font-weight: bold } /* Punctuation */ +.codehilite .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */ +.codehilite .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ +.codehilite .cp { color: #8f5902; font-style: italic } /* Comment.Preproc */ +.codehilite .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */ +.codehilite .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ +.codehilite .cs { color: #8f5902; font-style: italic } /* Comment.Special */ +.codehilite .gd { color: #a40000 } /* Generic.Deleted */ +.codehilite .ge { color: #000000; font-style: italic } /* Generic.Emph */ +.codehilite .gr { color: #ef2929 } /* Generic.Error */ .codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .codehilite .gi { color: #00A000 } /* Generic.Inserted */ -.codehilite .go { color: #888888 } /* Generic.Output */ -.codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ -.codehilite .gs { font-weight: bold } /* Generic.Strong */ +.codehilite .go { color: #000000; font-style: italic } /* Generic.Output */ +.codehilite .gp { color: #8f5902 } /* Generic.Prompt */ +.codehilite .gs { color: #000000; font-weight: bold } /* Generic.Strong */ .codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.codehilite .gt { color: #0044DD } /* Generic.Traceback */ -.codehilite .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ -.codehilite .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ -.codehilite .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ -.codehilite .kp { color: #008000 } /* Keyword.Pseudo */ -.codehilite .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ -.codehilite .kt { color: #B00040 } /* Keyword.Type */ -.codehilite .m { color: #666666 } /* Literal.Number */ -.codehilite .s { color: #BA2121 } /* Literal.String */ -.codehilite .na { color: #7D9029 } /* Name.Attribute */ -.codehilite .nb { color: #008000 } /* Name.Builtin */ -.codehilite .nc { color: #0000FF; font-weight: bold } /* Name.Class */ -.codehilite .no { color: #880000 } /* Name.Constant */ -.codehilite .nd { color: #AA22FF } /* Name.Decorator */ -.codehilite .ni { color: #999999; font-weight: bold } /* Name.Entity */ -.codehilite .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ -.codehilite .nf { color: #0000FF } /* Name.Function */ -.codehilite .nl { color: #A0A000 } /* Name.Label */ -.codehilite .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ -.codehilite .nt { color: #008000; font-weight: bold } /* Name.Tag */ -.codehilite .nv { color: #19177C } /* Name.Variable */ -.codehilite .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ -.codehilite .w { color: #bbbbbb } /* Text.Whitespace */ -.codehilite .mb { color: #666666 } /* Literal.Number.Bin */ -.codehilite .mf { color: #666666 } /* Literal.Number.Float */ -.codehilite .mh { color: #666666 } /* Literal.Number.Hex */ -.codehilite .mi { color: #666666 } /* Literal.Number.Integer */ -.codehilite .mo { color: #666666 } /* Literal.Number.Oct */ -.codehilite .sa { color: #BA2121 } /* Literal.String.Affix */ -.codehilite .sb { color: #BA2121 } /* Literal.String.Backtick */ -.codehilite .sc { color: #BA2121 } /* Literal.String.Char */ -.codehilite .dl { color: #BA2121 } /* Literal.String.Delimiter */ -.codehilite .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ -.codehilite .s2 { color: #BA2121 } /* Literal.String.Double */ -.codehilite .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ -.codehilite .sh { color: #BA2121 } /* Literal.String.Heredoc */ -.codehilite .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ -.codehilite .sx { color: #008000 } /* Literal.String.Other */ -.codehilite .sr { color: #BB6688 } /* Literal.String.Regex */ -.codehilite .s1 { color: #BA2121 } /* Literal.String.Single */ -.codehilite .ss { color: #19177C } /* Literal.String.Symbol */ -.codehilite .bp { color: #008000 } /* Name.Builtin.Pseudo */ -.codehilite .fm { color: #0000FF } /* Name.Function.Magic */ -.codehilite .vc { color: #19177C } /* Name.Variable.Class */ -.codehilite .vg { color: #19177C } /* Name.Variable.Global */ -.codehilite .vi { color: #19177C } /* Name.Variable.Instance */ -.codehilite .vm { color: #19177C } /* Name.Variable.Magic */ -.codehilite .il { color: #666666 } /* Literal.Number.Integer.Long */ +.codehilite .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ +.codehilite .kc { color: #204a87; font-weight: bold } /* Keyword.Constant */ +.codehilite .kd { color: #204a87; font-weight: bold } /* Keyword.Declaration */ +.codehilite .kn { color: #204a87; font-weight: bold } /* Keyword.Namespace */ +.codehilite .kp { color: #204a87; font-weight: bold } /* Keyword.Pseudo */ +.codehilite .kr { color: #204a87; font-weight: bold } /* Keyword.Reserved */ +.codehilite .kt { color: #204a87; font-weight: bold } /* Keyword.Type */ +.codehilite .ld { color: #000000 } /* Literal.Date */ +.codehilite .m { color: #0000cf; font-weight: bold } /* Literal.Number */ +.codehilite .s { color: #4e9a06 } /* Literal.String */ +.codehilite .na { color: #c4a000 } /* Name.Attribute */ +.codehilite .nb { color: #204a87 } /* Name.Builtin */ +.codehilite .nc { color: #000000 } /* Name.Class */ +.codehilite .no { color: #000000 } /* Name.Constant */ +.codehilite .nd { color: #5c35cc; font-weight: bold } /* Name.Decorator */ +.codehilite .ni { color: #ce5c00 } /* Name.Entity */ +.codehilite .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ +.codehilite .nf { color: #000000 } /* Name.Function */ +.codehilite .nl { color: #f57900 } /* Name.Label */ +.codehilite .nn { color: #000000 } /* Name.Namespace */ +.codehilite .nx { color: #000000 } /* Name.Other */ +.codehilite .py { color: #000000 } /* Name.Property */ +.codehilite .nt { color: #204a87; font-weight: bold } /* Name.Tag */ +.codehilite .nv { color: #000000 } /* Name.Variable */ +.codehilite .ow { color: #204a87; font-weight: bold } /* Operator.Word */ +.codehilite .w { color: #f8f8f8; text-decoration: underline } /* Text.Whitespace */ +.codehilite .mb { color: #0000cf; font-weight: bold } /* Literal.Number.Bin */ +.codehilite .mf { color: #0000cf; font-weight: bold } /* Literal.Number.Float */ +.codehilite .mh { color: #0000cf; font-weight: bold } /* Literal.Number.Hex */ +.codehilite .mi { color: #0000cf; font-weight: bold } /* Literal.Number.Integer */ +.codehilite .mo { color: #0000cf; font-weight: bold } /* Literal.Number.Oct */ +.codehilite .sa { color: #4e9a06 } /* Literal.String.Affix */ +.codehilite .sb { color: #4e9a06 } /* Literal.String.Backtick */ +.codehilite .sc { color: #4e9a06 } /* Literal.String.Char */ +.codehilite .dl { color: #4e9a06 } /* Literal.String.Delimiter */ +.codehilite .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ +.codehilite .s2 { color: #4e9a06 } /* Literal.String.Double */ +.codehilite .se { color: #4e9a06 } /* Literal.String.Escape */ +.codehilite .sh { color: #4e9a06 } /* Literal.String.Heredoc */ +.codehilite .si { color: #4e9a06 } /* Literal.String.Interpol */ +.codehilite .sx { color: #4e9a06 } /* Literal.String.Other */ +.codehilite .sr { color: #4e9a06 } /* Literal.String.Regex */ +.codehilite .s1 { color: #4e9a06 } /* Literal.String.Single */ +.codehilite .ss { color: #4e9a06 } /* Literal.String.Symbol */ +.codehilite .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ +.codehilite .fm { color: #000000 } /* Name.Function.Magic */ +.codehilite .vc { color: #000000 } /* Name.Variable.Class */ +.codehilite .vg { color: #000000 } /* Name.Variable.Global */ +.codehilite .vi { color: #000000 } /* Name.Variable.Instance */ +.codehilite .vm { color: #000000 } /* Name.Variable.Magic */ +.codehilite .il { color: #0000cf; font-weight: bold } /* Literal.Number.Integer.Long */ .donation li a { color: #333;
@@ -79,13 +79,83 @@ border: 1pt dotted #666;
padding: 0.6em; } -.sourceCode span { color: black; } -.sourceCode .co { color: darkgray; } -.sourceCode .ex { color: darkorange; } -.sourceCode .fu { color: darkorange; } -.sourceCode .kw { color: darkcyan; } -.sourceCode .st { color: magenta; } -.sourceCode .va { color: darkturquoise; } +.codehilite .hll { background-color: #ffffcc } +.codehilite { background: #f8f8f8; } +.codehilite .c { color: #8f5902; font-style: italic } +.codehilite .err { color: #a40000; border: 1px solid #ef2929 } +.codehilite .g { color: #000000 } +.codehilite .k { color: #204a87; font-weight: bold } +.codehilite .l { color: #000000 } +.codehilite .n { color: #000000 } +.codehilite .o { color: #ce5c00; font-weight: bold } +.codehilite .x { color: #000000 } +.codehilite .p { color: #000000; font-weight: bold } +.codehilite .ch { color: #8f5902; font-style: italic } +.codehilite .cm { color: #8f5902; font-style: italic } +.codehilite .cp { color: #8f5902; font-style: italic } +.codehilite .cpf { color: #8f5902; font-style: italic } +.codehilite .c1 { color: #8f5902; font-style: italic } +.codehilite .cs { color: #8f5902; font-style: italic } +.codehilite .gd { color: #a40000 } +.codehilite .ge { color: #000000; font-style: italic } +.codehilite .gr { color: #ef2929 } +.codehilite .gh { color: #000080; font-weight: bold } +.codehilite .gi { color: #00A000 } +.codehilite .go { color: #000000; font-style: italic } +.codehilite .gp { color: #8f5902 } +.codehilite .gs { color: #000000; font-weight: bold } +.codehilite .gu { color: #800080; font-weight: bold } +.codehilite .gt { color: #a40000; font-weight: bold } +.codehilite .kc { color: #204a87; font-weight: bold } +.codehilite .kd { color: #204a87; font-weight: bold } +.codehilite .kn { color: #204a87; font-weight: bold } +.codehilite .kp { color: #204a87; font-weight: bold } +.codehilite .kr { color: #204a87; font-weight: bold } +.codehilite .kt { color: #204a87; font-weight: bold } +.codehilite .ld { color: #000000 } +.codehilite .m { color: #0000cf; font-weight: bold } +.codehilite .s { color: #4e9a06 } +.codehilite .na { color: #c4a000 } +.codehilite .nb { color: #204a87 } +.codehilite .nc { color: #000000 } +.codehilite .no { color: #000000 } +.codehilite .nd { color: #5c35cc; font-weight: bold } +.codehilite .ni { color: #ce5c00 } +.codehilite .ne { color: #cc0000; font-weight: bold } +.codehilite .nf { color: #000000 } +.codehilite .nl { color: #f57900 } +.codehilite .nn { color: #000000 } +.codehilite .nx { color: #000000 } +.codehilite .py { color: #000000 } +.codehilite .nt { color: #204a87; font-weight: bold } +.codehilite .nv { color: #000000 } +.codehilite .ow { color: #204a87; font-weight: bold } +.codehilite .w { color: #f8f8f8; text-decoration: underline } +.codehilite .mb { color: #0000cf; font-weight: bold } +.codehilite .mf { color: #0000cf; font-weight: bold } +.codehilite .mh { color: #0000cf; font-weight: bold } +.codehilite .mi { color: #0000cf; font-weight: bold } +.codehilite .mo { color: #0000cf; font-weight: bold } +.codehilite .sa { color: #4e9a06 } +.codehilite .sb { color: #4e9a06 } +.codehilite .sc { color: #4e9a06 } +.codehilite .dl { color: #4e9a06 } +.codehilite .sd { color: #8f5902; font-style: italic } +.codehilite .s2 { color: #4e9a06 } +.codehilite .se { color: #4e9a06 } +.codehilite .sh { color: #4e9a06 } +.codehilite .si { color: #4e9a06 } +.codehilite .sx { color: #4e9a06 } +.codehilite .sr { color: #4e9a06 } +.codehilite .s1 { color: #4e9a06 } +.codehilite .ss { color: #4e9a06 } +.codehilite .bp { color: #3465a4 } +.codehilite .fm { color: #000000 } +.codehilite .vc { color: #000000 } +.codehilite .vg { color: #000000 } +.codehilite .vi { color: #000000 } +.codehilite .vm { color: #000000 } +.codehilite .il { color: #0000cf; font-weight: bold } figcaption { font-size: 0.9em;