Initial commit

This commit is contained in:
Renne Rocha 2020-05-19 22:25:48 -03:00
commit 1f637ed41e
33 changed files with 832 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
public/

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "themes/hugo-tania"]
path = themes/hugo-tania
url = https://github.com/WingLim/hugo-tania

6
archetypes/default.md Normal file
View file

@ -0,0 +1,6 @@
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
---

1
build_blog Executable file
View file

@ -0,0 +1 @@
rm -rf public && hugo && rsync -av --delete public/* renne@personal:/home/renne/blog/content

66
config.yaml Normal file
View file

@ -0,0 +1,66 @@
baseurl: "https://rennerocha.com/"
languageCode: "pt-br"
title: "Renne Rocha"
theme: "hugo-tania"
paginate: 6
params:
author: "Renne Rocha"
siteName: "Hugo Tania is Amazing"
siteDesc: "Hugo is Absurdly Fast!"
colorScheme:
toggle: true
default: auto
# Limit how many categories filter show above search input.
# Default to 5
maxCategoryToShow: 10
# Show your socail information with icon on index bio with `_index.md` content.
socialOptions:
email: "renne@rocha.dev.br"
github: "https://github.com/rennerocha"
twitter: "https://twitter.com/rennerocha"
# Comments settings
comments:
enabled: true
provider: giscus
giscus:
repo: WingLim/hugo-tania
id: MDEwOlJlcG9zaXRvcnkzMTYyNjQzMDc=
category:
name: Comments
id: DIC_kwDOEtnPc84B_WKP
menu:
header:
- name: Articles
url: "/articles/"
footer:
- name: RSS
url: "/index.xml"
markup:
highlight:
noClasses: false
lineNos: true
codeFences: true
guessSyntax: false
hl_Lines: ""
lineNoStart: 1
lineNos: false
lineNumbersInTable: true
noClasses: true
style: "monokai"
tabWidth: 4
goldmark:
renderer:
unsafe: true
outputs:
home: ["HTML", "RSS", "JSON"]

View file

@ -0,0 +1,159 @@
---
title: "Validando dados extraídos com Scrapy"
publishdate: 2019-03-08
tags: ["scrapy", "scraping", "monitoramento"]
---
Como você garante a qualidade e a confiabilidade dos dados que você está
extraindo em seu crawler?
Se o seu crawler foi desenvolvido com [Scrapy](https://scrapy.org), você tem a
disposição a extensão [Spidermon](https://spidermon.readthedocs.io/), que junto
com a biblioteca [Schematics](https://schematics.readthedocs.io/) permite que
você inclua validações de dados de maneira simples e altamente customizável.
Definindo modelos de dados, é possível comparar os items retornados com uma
estrutura pré-determinada, garantido que todos os campos contém dados no
formato esperado.
## Nosso exemplo
Para explicar o uso do **[Spidermon](https://spidermon.readthedocs.io/)**
para realizar a validação dos dados, criamos um projeto de exemplo simples,
que extrai citações do site [http://quotes.toscrape.com/](http://quotes.toscrape.com/).
Começamos instalando as bibliotecas necessárias:
{{<highlight bash>}}
$ python3 -m venv .venv
$ source .venv/bin/activate
(.venv) $ pip install scrapy spidermon schematics
{{</highlight>}}
Em seguida criamos um novo projeto **[Scrapy](https://scrapy.org)**:
{{<highlight bash>}}
$ scrapy startproject quotes_crawler
$ cd myproject & scrapy genspider quotes quotes.com
{{</highlight>}}
Definimos um [item](https://docs.scrapy.org/en/latest/topics/items.html) que
será retornado pelo nosso
[spider](https://docs.scrapy.org/en/latest/topics/spiders.html):
{{<highlight python>}}
# quotes_crawler/spiders/items.py
import scrapy
class QuoteItem(scrapy.Item):
quote = scrapy.Field()
author = scrapy.Field()
author_url = scrapy.Field()
tags = scrapy.Field()
{{</highlight>}}
E finalmente criamos o nosso spider:
{{<highlight python>}}
# quotes_crawler/spiders/quotes.py
import scrapy
from myproject.items import QuoteItem
class QuotesSpider(scrapy.Spider):
name = "quotes"
allowed_domains = ["quotes.toscrape.com"]
start_urls = ["http://quotes.toscrape.com/"]
def parse(self, response):
for quote in response.css(".quote"):
item = QuoteItem(
quote=quote.css(".text::text").get(),
author=quote.css(".author::text").get(),
author_url=response.urljoin(
quote.css(".author a::attr(href)").get()),
tags=quote.css(".tag *::text").getall(),
)
yield item
yield scrapy.Request(
response.urljoin(response.css(
".next a::attr(href)").get())
)
{{</highlight>}}
Com isso já é possível executar o nosso crawler e obter os dados desejados:
{{<highlight bash>}}
$ scrapy crawl quotes -o quotes.csv
{{</highlight>}}
## Definindo nosso modelo de validação
**[Schematics](https://schematics.readthedocs.io/)** é uma biblioteca de validação de dados baseada em modelos (muito
parecidos com modelos do Django). Esses modelos incluem alguns tipos de dados
comuns e validadores, mas também é possível extendê-los e definir regras de
validação customizadas.
Baseado no nosso exemplo anterior, vamos criar um modelo para o nosso item
contendo algumas regras básicas de validação:
{{<highlight python>}}
# quotes_crawler/spiders/validators.py
from schematics.models import Model
from schematics.types import URLType, StringType, ListType
class QuoteItem(Model):
# String de preenchimento obrigatório
quote = StringType(required=True)
# String de preenchimento obrigatório
author = StringType(required=True)
# Uma string que represente uma URL de preenchimento obrigatório
author_url = URLType(required=True)
# Lista de Strings de preenchimento não obrigatório
tags = ListType(StringType)
{{</highlight>}}
Precisamos agora habilitar o **Spidermon** e configurá-lo para que os campos
retornados pelo nosso spider sejam validados contra esse modelo:
{{<highlight python>}}
# quotes_crawler/settings.py
SPIDERMON_ENABLED = True
EXTENSIONS = {
"spidermon.contrib.scrapy.extensions.Spidermon": 500,
}
ITEM_PIPELINES = {
"spidermon.contrib.scrapy.pipelines.ItemValidationPipeline": 800
}
SPIDERMON_VALIDATION_MODELS = (
"quotes_crawler.validators.QuoteItem",
)
# Inclui um campo '_validation' nos items que contenham erros
# com detalhes do problema encontrado
SPIDERMON_VALIDATION_ADD_ERRORS_TO_ITEMS = True
{{</highlight>}}
Conforme os dados forem extraidos, eles serão validados de acordo com as regras
definidas no modelo e, caso algum erro seja encontrado, um novo campo
`_validation` será incluído no item com detalhes do erro.
Caso um item receba uma URL inválida no campo `author_url` por exemplo, o
resultado será:
{{<highlight js>}}
{
'_validation': defaultdict(
<class 'list'>, {'author_url': ['Invalid URL']}),
'author': 'C.S. Lewis',
'author_url': 'invalid_url',
'quote': 'Some day you will be old enough to start reading fairy tales '
'again.',
'tags': ['age', 'fairytales', 'growing-up']
}
{{</highlight>}}

20
themes/er/LICENSE.md Normal file
View file

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2018 Lingyi Hu
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

68
themes/er/README.md Normal file
View file

@ -0,0 +1,68 @@
# Er.
A configurable blog theme for Hugo, with elements inspired by [this blog](https://aranair.github.io/posts/).
## Features
- produces an Atom feed at `feed.xml`(taken from [this discussion](https://github.com/comfusion/after-dark/issues/32)). It generates the latest 15 posts by default.
- Open graph tags
- tag and category pages
- table of contents for your posts (from [tocbot](https://github.com/tscanlin/tocbot))
- renders Math with KaTeX
- tag cloud on big screens
## Configurations
### Favicon
You can put your favicon at `static/favicon.ico`, the theme will automatically look for it there. If you want to choose a different path, please set the `favicon` parameter in `[params]` in the config.
### Atom feed
In order to enable Atom feed (instead of RSS), put this in your `config.toml`:
```toml
[outputs]
home = ["HTML", "Atom"]
[mediaTypes]
[mediaTypes."application/atom"]
suffix = "xml"
[outputFormats.Atom]
mediaType = "application/atom"
baseName = "feed"
isPlainText = false
disableKinds = ['RSS']
```
### Customize the colors
This theme uses [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables), and if you want to change the colours on this theme, you can override the `variables.css` in `layouts/partials/css`. This is loaded before the theme css.
### Google analytics
Google analytics tracking code can be added to `config.toml` like this:
```toml
googleAnalytics = "UA-123-45"
```
### Markdown TOC
Table of contents is activated by default, if it detects markdown headings. To turn it off, just add `toc = false` in the frontmatter. Alternatively, you can turn off contents page for the whole site by setting `showtoc = false` under the `[params]` section of `config.toml`, like this:
```toml
[params]
showtoc = false
```
### Math rendering
Math rendering is off by default, can be turned on for individual posts or pages by setting `math = true` in the frontmatter.
### Tag cloud
Tag cloud is shown by default. To disable, add `showTagCloud = false` under the `[params]` section, similar to `showtoc`. You can also configure the maximum number of tags you want to show in your tag cloud, using the `maxTags` key under `[params]`. This number is 50 by default.
### back to top button
Back to top button is also shown by default. To disable, add `showScrollToTop = false` under `[params]`.

View file

@ -0,0 +1,2 @@
+++
+++

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

BIN
themes/er/images/tn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View file

@ -0,0 +1,9 @@
{{ define "main" }}
<main class="center mv4 content-width ph3">
<div class="f2 fw6 heading-color heading-font">404</div>
<p class="lh-copy post-content f4">
Sorry, but nothing exists here. <br>
Find something <a href='{{ "" | relURL }}'>interesting to read.</a>
</p>
</main>
{{ end }}

View file

@ -0,0 +1,10 @@
<!doctype html>
<html lang="en">
<head>{{ partial "header.html" . }}</head>
<body class="global-font">
{{ partial "nav.html" . }}
{{ block "main" . }}{{ end }}
{{ partial "footer.html" . }}
{{ partial "scripts.html" . }}
</body>
</html>

View file

@ -0,0 +1,14 @@
{{ define "main" }}
<main class="center mv4 content-width ph3">
<h1 class="f2 fw6 heading-font">{{ .Title }}</h1>
{{ .Content }}
<ul class="list-pages">
{{ range .Data.Pages }}
<li class="lh-copy"><a href="{{ .Permalink | relURL }}" class="link f5">{{ .Title }}</a> - <time>{{ .Date.Format "02 Jan 2006" }}</time></li>
{{ end }}
</ul>
</main>
<div class="pagination tc db fixed-l bottom-2-l right-2-l mb3 mb0-l">
{{ partial "back-to-top.html" . }}
</div>
{{ end }}

View file

@ -0,0 +1,26 @@
{{ define "main" }}
<main class="center mv4 content-width ph3">
<div class="f3 fw6 heading-color heading-font post-title">{{ .Title }}</div>
<p class="silver f6 mt1 mb4 post-meta">
{{ if not .Date.IsZero }}<time>{{ .Date.Format "02 Jan 2006" }}</time> {{ end }}
{{ if or .Params.tags .Params.categories .Params.series }} | {{ end }}
{{ if isset .Params "categories" }}
categories: [ {{ range .Params.categories }}<a href='{{ "/categories/" | relLangURL }}{{ . | urlize }}' class="link silver">{{ . }}</a> {{ end }} ]
{{ end }}
{{ if isset .Params "tags" }}
tags: [ {{ range .Params.tags }}<a href='{{ "/tags/" | relLangURL }}{{ . | urlize }}' class="link silver">{{ . }}</a> {{ end }} ]
{{ end }}
</p>
<div class="lh-copy post-content">{{ .Content }}</div>
</main>
{{ partial "table-of-contents" . }}
<div class="pagination tc tr-l db fixed-l bottom-2-l right-2-l mb3 mb0-l">
{{ partial "back-to-top.html" . }}<br>
<p class="mb0 mt2">
{{ with .PrevInSection }}{{ if . }}<a href="{{ .Permalink }}">prev post</a>{{ end }}{{ end }}
{{ with .NextInSection }}{{ if . }}<a href="{{ .Permalink }}">next post</a>{{ end }}{{ end }}
</p>
</div>
{{ end }}

View file

@ -0,0 +1,14 @@
{{ define "main" }}
<main class="center mv4 content-width ph3">
<h1 class="f2 fw6 heading-font"><span class="secondary-color">#</span> {{ .Data.Term }}</h1>
{{ .Content }}
<ul class="list-pages">
{{ range .Data.Pages }}
<li class="lh-copy"><a href="{{ .Permalink | relURL }}" class="link f5">{{ .Title }}</a> - <time>{{ .Date.Format "02 Jan 2006" }}</time></li>
{{ end }}
</ul>
</main>
<div class="pagination tc db fixed-l bottom-2-l right-2-l mb3 mb0-l">
{{ partial "back-to-top.html" . }}
</div>
{{ end }}

View file

@ -0,0 +1,15 @@
{{ define "main" }}
<main class="center mv4 content-width ph3">
{{ $type := .Type }}
<h1 class="f2 fw6 heading-font">{{ .Title }}</h1>
{{ .Content }}
<ul class="list-pages list pl0">
{{ range $key, $value := .Data.Terms }}
<li class="lh-copy di bg-light-gray ph1"><a href="{{ $key | urlize }}" class="link f5">{{ $key }}&nbsp;({{ len $value }})</a></li>
{{ end }}
</ul>
</main>
<div class="pagination tc db fixed-l bottom-2-l right-2-l mb3 mb0-l">
{{ partial "back-to-top.html" . }}
</div>
{{ end }}

View file

@ -0,0 +1,27 @@
<feed xmlns="http://www.w3.org/2005/Atom">
{{ if .IsHome }}
<title>{{ .Title }}</title>
{{ else }}
<title>{{ .Title }} - {{ .Site.Title }}</title>
{{ end }}
<link href="{{ .Permalink }}index.xml" rel="self"/>
<link href="{{ .Permalink }}"/>{{ if not .Date.IsZero }}
<updated>{{ .Date.Format "2006-01-02T15:04:05-07:00" | safeHTML }}</updated>{{ end }}
<id>{{ .Permalink }}</id>{{ with .Site.Author.name }}
<author>
<name>{{.}}</name>{{ with $.Site.Author.email }}
<email>{{.}}</email>{{end}}
</author>{{end}}
<generator>Hugo -- gohugo.io</generator>{{ range first 15 (where .Data.Pages "Type" "in" .Site.Params.mainSections) }}
<entry>
{{ `<title type="html"><![CDATA[` | safeHTML }}{{ .Title }}]]></title>
<link href="{{ .Permalink }}"/>
<id>{{ .Permalink }}</id>{{ with .Site.Params.Author }}
<author>
<name>{{.}}</name>
</author>{{end}}
<published>{{ .Date.Format "2006-01-02T15:04:05-07:00" | safeHTML }}</published>
<updated>{{ .Lastmod.Format "2006-01-02T15:04:05-07:00" | safeHTML }}</updated>
{{ `<content type="html"><![CDATA[` | safeHTML }}{{ .Content }}]]></content>
</entry>{{ end }}
</feed>

View file

@ -0,0 +1,24 @@
{{ define "main" }}
{{ $paginator := .Paginate (where .Site.RegularPages "Type" "in" .Site.Params.mainSections) }}
<main class="center mv4 content-width ph3">
<ul class="list pa0">
{{ range $paginator.Pages }}
<li class="mb2">
<a class="f4 heading-color heading-font fw6 no-underline" href="{{ .Permalink }}">{{ .Title }}</a>
<p class="f5 mt1 silver">
<time>{{ .Date.Format "02 Jan 2006" }}</time>
{{ if or .Params.tags .Params.categories }} | {{ end }}
{{ if isset .Params "categories" }}
categories: [ {{ range .Params.categories }}<a href='{{ "/categories/" | relLangURL }}{{ . | urlize }}' class="link silver">{{ . }}</a> {{ end }} ]
{{ end }}
{{ if isset .Params "tags" }}
tags: [ {{ range .Params.tags }}<a href='{{ "/tags/" | relLangURL }}{{ . | urlize }}' class="link silver">{{ . }}</a> {{ end }} ]
{{ end }}
</p>
</li>
{{ end }}
</ul>
</main>
{{ partial "tag_cloud.html" . }}
{{ partial "pagination.html" . }}
{{ end }}

View file

@ -0,0 +1,3 @@
{{ if .Site.Params.showScrollToTop | default true }}
<a id="scroll-to-top" class="f6 o-0 link br2 ph2 pv1 mb1 bg-main-color pointer" onclick="topFunction()" style="color: #fff; visibility: hidden; display: none; transition: opacity .5s, visibility .5s;" title="back to top">back to top</a>
{{ end }}

View file

@ -0,0 +1,9 @@
/*https://coolors.co/afd5aa-f0f2ef-a69f98-3d3d3d-8c6057*/
:root {
--main-color: #8C6056;
--secondary-color: #AFD5AA;
--logo-text-color: #fff;
--body-text-color: #3d3d3d;
--heading-text-color: #383838;
--background-color: #fff;
}

View file

@ -0,0 +1,5 @@
<footer class="content-width mt0 mt5-l mb4 f6 center ph3 gray tc tl-l">
<hr class="dn db-l ml0-l gray w3"><br>
Powered by <a href="https://gohugo.io/" target="_blank" class="link gray dim">Hugo</a>, based on the <a href="https://github.com/lingxz/er" target="_blank" class="link gray dim">Er</a> theme. <br>
{{ with .Site.Copyright }}{{ . }}{{end}}
</footer>

View file

@ -0,0 +1,22 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta content='text/html; charset=utf-8' http-equiv='content-type' />
{{ partial "open_graph.html" . }}
{{ hugo.Generator }}
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600" rel="stylesheet">
<style type="text/css">{{ partial "css/variables.css" . | safeCSS }}</style>
<link href='{{ "css/tachyons.min.css" | relURL }}' rel="stylesheet">
<link href='{{ "css/styles.css" | relURL }}' rel="stylesheet">
<!-- Icon -->
<link rel="icon"
{{ if .Site.Params.favicon }}
href="{{ .Site.Params.favicon | relURL }}"
{{ else }}
href='{{ "/favicon.ico" | relURL }}'
{{ end }}
type="image/x-icon"/>
<link href='{{ "/feed.xml" | relURL }}' rel="alternate" type="application/atom+xml" title="{{ .Site.Title }}" />
{{ template "_internal/google_analytics_async.html" . }}

View file

@ -0,0 +1,12 @@
<nav class="{{ with .Site.Menus.main }}{{ if lt (len .) 3 }} flex {{ else }} flex-ns{{ end }}{{ end }} justify-between border-box pa3 pl3-l pr2-l mt1 mt0-ns" id="navbar">
<div class="flex">
<a class="f4 fw6 ttu no-underline dim bg-main-color pv1 ph2 br2" id="site-title" href='{{ "" | relURL }}' title="Home">{{ .Site.Title }}</a>
</div>
{{ with .Site.Menus.main }}
<div class="{{ if lt (len . ) 3 }} flex-grow {{ else }} flex-ns mt2 mt0-ns{{ end }} pv1">
{{ range . }}
<a class="link dim dark-gray f6 dib mr2 mr3-l ttu tracked" href='{{.URL}}' title="{{ .Name }}">{{ .Name }}</a>
{{ end }}
</div>
{{ end }}
</nav>

View file

@ -0,0 +1,48 @@
{{ if .IsHome }}
<title>{{ .Title }}</title>
<meta content='{{ .Title }}' property='title' />
<meta content='{{ .Title }}' property='og:title' />
{{ else }}
<title>{{ .Title }} - {{ .Site.Title }}</title>
<meta content='{{ .Title }} - {{ .Site.Title }}' property='title' />
<meta content='{{ .Title }} - {{ .Site.Title }}' property='og:title' />
{{ end }}
<meta property="og:description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end }}" />
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}" />
<meta property="og:url" content="{{ .Permalink }}" />
{{ with .Params.images }}{{ range first 6 . }}
<meta property="og:image" content="{{ . | absURL }}" />
{{ end }}{{ end }}
{{ if .IsPage }}
{{ if not .PublishDate.IsZero }}<meta property="article:published_time" content="{{ .PublishDate.Format "2006-01-02T15:04:05-07:00" | safeHTML }}"/>
{{ else if not .Date.IsZero }}<meta property="article:published_time" content="{{ .Date.Format "2006-01-02T15:04:05-07:00" | safeHTML }}"/>{{ end }}
{{ if not .Lastmod.IsZero }}<meta property="article:modified_time" content="{{ .Lastmod.Format "2006-01-02T15:04:05-07:00" | safeHTML }}"/>{{ end }}
{{ else }}
{{ if not .Date.IsZero }}<meta property="og:updated_time" content="{{ .Date.Format "2006-01-02T15:04:05-07:00" | safeHTML }}"/>{{ end }}
{{ end }}{{ with .Params.audio }}
<meta property="og:audio" content="{{ . }}" />{{ end }}{{ with .Params.locale }}
<meta property="og:locale" content="{{ . }}" />{{ end }}{{ with .Site.Params.title }}
<meta property="og:site_name" content="{{ . }}" />{{ end }}{{ with .Params.videos }}
{{ range .Params.videos }}
<meta property="og:video" content="{{ . | absURL }}" />
{{ end }}{{ end }}
<!-- If it is part of a series, link to related articles -->
{{ $permalink := .Permalink }}
{{ $siteSeries := .Site.Taxonomies.series }}{{ with .Params.series }}
{{ range $name := . }}
{{ $series := index $siteSeries $name }}
{{ range $page := first 6 $series.Pages }}
{{ if ne $page.Permalink $permalink }}<meta property="og:see_also" content="{{ $page.Permalink }}" />{{ end }}
{{ end }}
{{ end }}{{ end }}
{{ if .IsPage }}
{{ range .Site.Authors }}{{ with .Social.facebook }}
<meta property="article:author" content="https://www.facebook.com/{{ . }}" />{{ end }}{{ with .Site.Social.facebook }}
<meta property="article:publisher" content="https://www.facebook.com/{{ . }}" />{{ end }}
<meta property="article:section" content="{{ .Section }}" />
{{ with .Params.tags }}{{ range first 6 . }}
<meta property="article:tag" content="{{ . }}" />{{ end }}{{ end }}
{{ end }}{{ end }}
<!-- Facebook Page Admin ID for Domain Insights -->
{{ with .Site.Social.facebook_admin }}<meta property="fb:admins" content="{{ . }}" />{{ end }}

View file

@ -0,0 +1,9 @@
{{ if gt .Paginator.TotalPages 1 }}
<div class="pagination tc db fixed-l bottom-2-l right-2-l mb3 mb0-l">
{{ partial "back-to-top.html" . }}<br>
<p class="mb0 mt2">Page {{ .Paginator.PageNumber }} of {{ .Paginator.TotalPages }} <br>
{{ if .Paginator.HasNext }}<a href="{{ .Paginator.Next.URL | relURL }}">Older Posts</a>{{ end }}
{{ if .Paginator.HasPrev }}<a href="{{ .Paginator.Prev.URL | relURL }}">Newer Posts</a>{{ end }}
</p>
</div>
{{ end }}

View file

@ -0,0 +1,101 @@
{{ if .Site.Params.showtoc | default true }}
{{ $headers := findRE "<h[1-6].*?>(.|\n])+?</h[1-6]>" .Content }}
{{ $has_headers := ge (len $headers) 1 }}
{{ if and $has_headers (ne .Params.toc false) }}
<script src="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.4.2/tocbot.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.4.2/tocbot.css">
<style>.is-active-link::before { background-color: var(--secondary-color); }</style>
{{ end }}
{{ end }}
{{ if or (.Site.Params.showTagCloud | default true) (.Site.Params.showtoc | default true) (.Site.Params.showScrollToTop | default true) }}
<script type="text/javascript">
var prevScrollpos = window.pageYOffset;
window.onscroll = function() {
var currentScrollPos = window.pageYOffset;
{{ if .Site.Params.showTagCloud | default true }}
if (document.getElementById("tag-cloud") !== null) {
if (prevScrollpos > currentScrollPos) { // scroll up
document.getElementById("tag-cloud").style.visibility = "visible";
document.getElementById("tag-cloud").style.opacity = "1";
} else {
document.getElementById("tag-cloud").style.visibility = "hidden";
document.getElementById("tag-cloud").style.opacity = "0";
}
}
{{ end }}
{{ if .Site.Params.showScrollToTop | default true }}
if (document.body.scrollTop > 1000 || document.documentElement.scrollTop > 1000) {
document.getElementById("scroll-to-top").style.display = "inline";
document.getElementById("scroll-to-top").style.visibility = "visible";
document.getElementById("scroll-to-top").style.opacity = "1";
} else {
document.getElementById("scroll-to-top").style.visibility = "hidden";
document.getElementById("scroll-to-top").style.opacity = "0";
}
{{ end }}
prevScrollpos = currentScrollPos;
}
{{ if .Site.Params.showScrollToTop | default true }}
// When the user clicks on the button, scroll to the top of the document
function topFunction() {
document.body.scrollTop = 0; // For Safari
document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera
}
{{ end }}
{{ if .Site.Params.showtoc | default true }}
{{ $headers := findRE "<h[1-6].*?>(.|\n])+?</h[1-6]>" .Content }}
{{ $has_headers := ge (len $headers) 1 }}
{{ if and $has_headers (ne .Params.toc false) }}
if (document.getElementById("contents-list") !== null && document.getElementsByClassName("post-content").length !== 0) {
tocbot.init({
// Where to render the table of contents.
tocSelector: '#contents-list',
// Where to grab the headings to build the table of contents.
contentSelector: '.post-content',
// Which headings to grab inside of the contentSelector element.
headingSelector: 'h1, h2, h3',
});
}
{{ end }}
{{ end }}
</script>
{{ end }}
{{ if .Params.math }}
<!-- Load KaTeX -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.7.1/contrib/auto-render.min.js" integrity="sha384-dq1/gEHSxPZQ7DdrM82ID4YVol9BYyU7GbWlIwnwyPzotpoc57wDw/guX8EaYGPx" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.css">
<script>
renderMathInElement(document.body,
{
delimiters: [
{left: "$$", right: "$$", display: true},
{left: "$", right: "$", display: false},
]
}
);
var inlineMathArray = document.querySelectorAll("script[type='math/tex']");
for (var i = 0; i < inlineMathArray.length; i++) {
var inlineMath = inlineMathArray[i];
var tex = inlineMath.innerText || inlineMath.textContent;
var replaced = document.createElement("span");
replaced.innerHTML = katex.renderToString(tex, {displayMode: false});
inlineMath.parentNode.replaceChild(replaced, inlineMath);
}
var displayMathArray = document.querySelectorAll("script[type='math/tex; mode=display']");
for (var i = 0; i < displayMathArray.length; i++) {
var displayMath = displayMathArray[i];
var tex = displayMath.innerHTML;
var replaced = document.createElement("span");
replaced.innerHTML = katex.renderToString(tex.replace(/%.*/g, ''), {displayMode: true});
displayMath.parentNode.replaceChild(replaced, displayMath);
}
</script>
{{end}}

View file

@ -0,0 +1,9 @@
<!-- ignore empty links with + -->
{{ $headers := findRE "<h[1-6].*?>(.|\n])+?</h[1-6]>" .Content }}
<!-- at least one header to link to -->
{{ $has_headers := ge (len $headers) 1 }}
<!-- a post can explicitly disable Table of Contents with toc: false -->
{{ $show_toc := and (ne .Params.toc false) (ne .Site.Params.showtoc false) }}
{{ if and $has_headers $show_toc }}
<div class="tl fixed list-pages lh-copy" id="contents-list"></div>
{{ end }}

View file

@ -0,0 +1,9 @@
{{ $maxtags := .Site.Params.maxTags | default 50 }}
{{ if .Site.Params.showTagCloud | default true }}
<div class="dn tr fixed right-2" id="tag-cloud">
tags <br>
{{ range first $maxtags .Site.Taxonomies.tags.ByCount }}
<a class="silver no-underline" href="{{ "/tags/" | relLangURL }}{{ .Name | urlize }}">{{ .Name }}</a></li>
{{ end }}
</div>
{{ end }}

View file

@ -0,0 +1,122 @@
html { font-size: 16px; }
body { color: var(--body-text-color); background-color: var(--background-color);}
.main-color { color: var(--main-color); }
.secondary-color { color: var(--secondary-color); }
.bg-main-color { background-color: var(--main-color); color: var(--logo-text-color); }
.heading-font, .global-font { font-family: "Source Sans Pro"; }
.heading-color { color: var(--heading-text-color); }
.post-content a, .list-pages a, .pagination a {
border-bottom: 1px solid rgba(0,0,0,0.4);
text-decoration: none;
color: inherit;
}
main a:hover, .list-pages a:hover, .pagination a:hover, #tag-cloud a:hover {
border-bottom: 1px solid var(--body-text-color);
color: var(--body-text-color);
}
.content-width { max-width: 650px; }
h1, h2, h3, h4, h5, h6 {
letter-spacing: 0px;
color: var(--main-color);
font-weight: 600;
}
pre {
font-size: 0.9em;
padding: 10px 20px 10px 20px;
overflow: auto;
line-height: 1.25em;
}
p code {
padding: .25rem .5rem;
font-size: 0.9em;
background-color: #f8f8f8;
}
#tag-cloud { max-width: 250px; }
#contents-list>.toc-list {
max-width: 250px;
overflow: hidden;
position: fixed;
right: 2rem;
}
@media (min-width: 1200px) {
#contents-list>.toc-list, #tag-cloud {
display: block;
top: 100px;
}
#tag-cloud {
-webkit-transition: opacity .5s, visibility .5s;
transition: opacity .5s, visibility .5s;
}
}
@media (min-width: 1330px) { #contents-list>.toc-list { max-width: 300px; right: 3rem;}}
#contents-list ol { list-style: none; }
figure {
margin: 2em 1em;
}
img {
display: block;
margin-left: auto;
margin-right: auto;
box-shadow: 1px 1px 2px 2px #bbb;
}
figcaption {
text-align: center;
margin-top: 0.5em;
}
blockquote {
border-left-style: solid;
border-left-width: 1px;
border-color: var(--secondary-color);
border-width: .25rem;
margin-left: 0;
margin-top: 0;
margin-right: 0;
padding-left: 1rem;
}
blockquote p {
color: #666;
margin-top: 0;
line-height: 1.5;
font-style: italic;
}
blockquote p>cite {
text-transform: uppercase;
font-style: normal;
letter-spacing: .1em;
font-size: .875rem;
display: block;
}
table {
font-size: 14px;
border-collapse:collapse;
margin: 2rem auto;
padding:0;
}
table tr {
border-top:1px solid #ccc;
background-color:#fff;
margin:0;
padding:0;
}
table tr:nth-child(2n) {
background-color:#f8f8f8;
}
table tr th[align="center"], table tr td[align="center"] {
text-align:center;
}
table tr th, table tr td {
border:1px solid #ccc;
text-align:left;
margin:0;
padding:6px 13px;
}

2
themes/er/static/css/tachyons.min.css vendored Normal file

File diff suppressed because one or more lines are too long

15
themes/er/theme.toml Normal file
View file

@ -0,0 +1,15 @@
# theme.toml template for a Hugo theme
# See https://github.com/gohugoio/hugoThemes#themetoml for an example
name = "Er"
license = "MIT"
licenselink = "https://github.com/lingxz/er/blob/master/LICENSE.md"
description = "A clean Hugo theme for blogging"
homepage = "http://github.com/lingxz/er"
tags = ["responsive", "white", "blog"]
features = ["responsive", "blog"]
min_version = "0.57.0"
[author]
name = "Lingyi Hu"
homepage = "https://theconfused.me"

1
themes/hugo-tania Submodule

@ -0,0 +1 @@
Subproject commit c52acfb01bb0188da4f76b1378d9045041772a99