Lucas Stadler преди 9 години
родител
ревизия
1da656c9ab
променени са 2 файла, в които са добавени 172 реда и са изтрити 8 реда
  1. 153 8
      go/blog/blog.go
  2. 19 0
      go/blog/blog.yaml

+ 153 - 8
go/blog/blog.go

@ -4,6 +4,7 @@ import (
4 4
	"crypto/md5"
5 5
	"crypto/rand"
6 6
	"encoding/hex"
7
	"encoding/json"
7 8
	"flag"
8 9
	"fmt"
9 10
	"html/template"
@ -11,6 +12,7 @@ import (
11 12
	"io/ioutil"
12 13
	"net/url"
13 14
	"os"
15
	"sort"
14 16
	"strings"
15 17
	"unicode"
16 18
@ -19,12 +21,13 @@ import (
19 21
)
20 22
21 23
type Post struct {
22
	Id      string `yaml:"id"`
23
	Title   string `yaml:"title"`
24
	URL     string `yaml:"url"`
25
	Content string `yaml:"content"`
26
	Date    string `yaml:"date"`
27
	Type    string `yaml:"type"`
24
	Id      string   `yaml:"id"`
25
	Title   string   `yaml:"title"`
26
	URL     string   `yaml:"url"`
27
	Content string   `yaml:"content"`
28
	Tags    []string `yaml:"tags"`
29
	Date    string   `yaml:"date"`
30
	Type    string   `yaml:"type"`
28 31
}
29 32
30 33
var flags struct {
@ -38,7 +41,7 @@ var dataPath string = "blog.yaml"
38 41
39 42
var defaultStyle = `
40 43
article {
41
	margin-bottom: 1em;
44
	margin-bottom: 2em;
42 45
}
43 46
44 47
article header {
@ -71,6 +74,31 @@ article img {
71 74
	max-width: 80vw;
72 75
	max-height: 50vh;
73 76
}
77
78
.tags {
79
	list-style-type: none;
80
	padding: 0;
81
}
82
83
.tags .tag-link {
84
	float: left;
85
	color: black;
86
	margin-right: 0.5em;
87
}
88
89
article .tags .tag-link:visited {
90
	color: #555;
91
}
92
93
article.does-not-match {
94
	display: none;
95
}
96
97
#tags {
98
	color: #555;
99
	font-size: smaller;
100
	margin-bottom: 1em;
101
}
74 102
`
75 103
76 104
func init() {
@ -138,12 +166,19 @@ func main() {
138 166
		posts = reversePosts
139 167
	}
140 168
169
	tags := map[string]bool{}
141 170
	for i, post := range posts {
142 171
		if post.Id == "" {
143 172
			posts[i].Id = generateId(post)
144 173
			post = posts[i]
145 174
		}
146 175
176
		if post.Tags != nil {
177
			for _, tag := range post.Tags {
178
				tags[tag] = true
179
			}
180
		}
181
147 182
		var err error
148 183
		switch post.Type {
149 184
		case "shell":
@ -176,6 +211,93 @@ func main() {
176 211
		}
177 212
	}
178 213
214
	sortedTags := []string{}
215
	for tag, _ := range tags {
216
		sortedTags = append(sortedTags, tag)
217
	}
218
	sort.Strings(sortedTags)
219
	err = tagsTmpl.Execute(out, sortedTags)
220
	if err != nil {
221
		exit(err)
222
	}
223
224
	fmt.Fprintf(out, `
225
226
	<script>
227
	var currentFilter = null;
228
229
	window.addEventListener("DOMContentLoaded", function(ev) {
230
		filterFromURL(document.location);
231
	});
232
233
	window.addEventListener("hashchange", function(ev) {
234
		filterFromURL(new URL(ev.newURL));
235
	});
236
237
	window.addEventListener("click", function(ev) {
238
		if (ev.target.classList.contains("tag-link")) {
239
			if (ev.target.href == "") {
240
				return;
241
			}
242
243
			var tag = tagFromURL(new URL(ev.target.href));
244
			if (currentFilter == tag) {
245
				clearFilter();
246
				location.hash = "";
247
				ev.preventDefault();
248
			} else {
249
				filterTag(tag);
250
			}
251
		}
252
	});
253
254
	function filterFromURL(u) {
255
		var tag = tagFromURL(u);
256
		if (tag == null) {
257
			clearFilter();
258
		} else {
259
			filterTag(tag);
260
		}
261
	}
262
263
	function tagFromURL(u) {
264
		if (!u.hash.startsWith("#tag:")) {
265
			return null;
266
		}
267
		return u.hash.substr(5);
268
	}
269
270
	function filterTag(tag) {
271
		currentFilter = tag;
272
273
		var articles = document.querySelectorAll("article");
274
		for (var i = 0; i < articles.length; i++) {
275
			var article = articles[i];
276
			var matches = false;
277
			if (article && 'tags' in article.dataset) {
278
				var tags = JSON.parse(article.dataset.tags);
279
				for (var j = 0; j < tags.length; j++) {
280
					if (tags[j] == tag) {
281
						matches = true;
282
						break;
283
					}
284
				}
285
			}
286
			if (!matches) {
287
				article.classList.add("does-not-match");
288
			}
289
		}
290
	}
291
292
	function clearFilter() {
293
		var articles = document.querySelectorAll("article.does-not-match");
294
		for (var i = 0; i < articles.length; i++) {
295
			articles[i].classList.remove("does-not-match");
296
		}
297
298
		currentFilter = null;
299
	}
300
	</script>`)
179 301
	fmt.Fprintf(out, "\n</body>\n</html>\n")
180 302
	out.Close()
181 303
@ -195,6 +317,10 @@ var funcs = template.FuncMap{
195 317
	"safe_url": func(s string) template.URL {
196 318
		return template.URL(s)
197 319
	},
320
	"json": func(v interface{}) (string, error) {
321
		buf, err := json.Marshal(v)
322
		return string(buf), err
323
	},
198 324
}
199 325
200 326
var baseTmpl = template.Must(template.New("base").
@ -208,6 +334,14 @@ var baseTmpl = template.Must(template.New("base").
208 334
		<time>{{ .Date }}</time>{{ end }}
209 335
	</header>
210 336
{{ end }}
337
338
{{ define "tags" }}
339
	<ul class="tags">
340
	{{- range . }}
341
		<li><a class="tag-link" href="#tag:{{ safe_url . }}">{{ . }}</a></li>
342
	{{ end -}}
343
	</ul>
344
{{ end }}
211 345
`))
212 346
213 347
var shellTmpl = template.Must(baseTmpl.New("shell").
@ -270,12 +404,16 @@ var songTmpl = template.Must(baseTmpl.New("song").
270 404
271 405
var textTmpl = template.Must(baseTmpl.New("text").
272 406
	Funcs(funcs).Parse(`
273
<article id="{{ .Id }}" class="text">
407
<article id="{{ .Id }}" class="text" {{- if .Tags }} data-tags="{{ json .Tags }}"{{ end }}>
274 408
	{{- template "title" . }}
275 409
	{{- if .Content }}
276 410
277 411
	{{ markdown .Content }}
278 412
	{{- end -}}
413
414
	<footer>
415
		{{ template "tags" .Tags }}
416
	</footer>
279 417
</article>
280 418
`))
281 419
@ -290,6 +428,13 @@ var videoTmpl = template.Must(baseTmpl.Parse(`
290 428
</article>
291 429
`))
292 430
431
var tagsTmpl = template.Must(baseTmpl.New("tags-list").Parse(`
432
	<section id="tags">
433
		<h1>All tags:</h1>
434
		{{ template "tags" . }}
435
	</section>
436
`))
437
293 438
func exit(err error) {
294 439
	fmt.Fprintf(os.Stderr, "Error: %s\n", err)
295 440
	os.Exit(1)

+ 19 - 0
go/blog/blog.yaml

@ -63,3 +63,22 @@
63 63
- url: https://www.youtube.com/watch?v=yLuOzNeHw5I
64 64
  content: Lights, by Archive.
65 65
  type: video
66
- title: There are tags now!
67
  content: |
68
    You can now tag articles.  Any kind of string is allowed, have
69
    some fun with it...  :)
70
71
    Use them as follows:
72
73
        - # attributes as usual
74
          tags: [fancy, tags, "stuff:with-interesting!-characters"]
75
76
    Or alternatively:
77
78
        - # as usual
79
          tags:
80
            - fancy
81
            - tags
82
            - "stuff:with-interesting!-characters"
83
  tags: [tags, feature, announcement, "author:lu"]
84
  type: text