Browse Source

Merge branch 'tags'

Lucas Stadler 9 years ago
parent
commit
1da656c9ab
2 changed files with 172 additions and 8 deletions
  1. 153 8
      go/blog/blog.go
  2. 19 0
      go/blog/blog.yaml

+ 153 - 8
go/blog/blog.go

4
	"crypto/md5"
4
	"crypto/md5"
5
	"crypto/rand"
5
	"crypto/rand"
6
	"encoding/hex"
6
	"encoding/hex"
7
	"encoding/json"
7
	"flag"
8
	"flag"
8
	"fmt"
9
	"fmt"
9
	"html/template"
10
	"html/template"
11
	"io/ioutil"
12
	"io/ioutil"
12
	"net/url"
13
	"net/url"
13
	"os"
14
	"os"
15
	"sort"
14
	"strings"
16
	"strings"
15
	"unicode"
17
	"unicode"
16
18
19
)
21
)
20
22
21
type Post struct {
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
var flags struct {
33
var flags struct {
38
41
39
var defaultStyle = `
42
var defaultStyle = `
40
article {
43
article {
41
	margin-bottom: 1em;
44
	margin-bottom: 2em;
42
}
45
}
43
46
44
article header {
47
article header {
71
	max-width: 80vw;
74
	max-width: 80vw;
72
	max-height: 50vh;
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
func init() {
104
func init() {
138
		posts = reversePosts
166
		posts = reversePosts
139
	}
167
	}
140
168
169
	tags := map[string]bool{}
141
	for i, post := range posts {
170
	for i, post := range posts {
142
		if post.Id == "" {
171
		if post.Id == "" {
143
			posts[i].Id = generateId(post)
172
			posts[i].Id = generateId(post)
144
			post = posts[i]
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
		var err error
182
		var err error
148
		switch post.Type {
183
		switch post.Type {
149
		case "shell":
184
		case "shell":
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
	fmt.Fprintf(out, "\n</body>\n</html>\n")
301
	fmt.Fprintf(out, "\n</body>\n</html>\n")
180
	out.Close()
302
	out.Close()
181
303
195
	"safe_url": func(s string) template.URL {
317
	"safe_url": func(s string) template.URL {
196
		return template.URL(s)
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
var baseTmpl = template.Must(template.New("base").
326
var baseTmpl = template.Must(template.New("base").
208
		<time>{{ .Date }}</time>{{ end }}
334
		<time>{{ .Date }}</time>{{ end }}
209
	</header>
335
	</header>
210
{{ end }}
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
var shellTmpl = template.Must(baseTmpl.New("shell").
347
var shellTmpl = template.Must(baseTmpl.New("shell").
270
404
271
var textTmpl = template.Must(baseTmpl.New("text").
405
var textTmpl = template.Must(baseTmpl.New("text").
272
	Funcs(funcs).Parse(`
406
	Funcs(funcs).Parse(`
273
<article id="{{ .Id }}" class="text">
407
<article id="{{ .Id }}" class="text" {{- if .Tags }} data-tags="{{ json .Tags }}"{{ end }}>
274
	{{- template "title" . }}
408
	{{- template "title" . }}
275
	{{- if .Content }}
409
	{{- if .Content }}
276
410
277
	{{ markdown .Content }}
411
	{{ markdown .Content }}
278
	{{- end -}}
412
	{{- end -}}
413
414
	<footer>
415
		{{ template "tags" .Tags }}
416
	</footer>
279
</article>
417
</article>
280
`))
418
`))
281
419
290
</article>
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
func exit(err error) {
438
func exit(err error) {
294
	fmt.Fprintf(os.Stderr, "Error: %s\n", err)
439
	fmt.Fprintf(os.Stderr, "Error: %s\n", err)
295
	os.Exit(1)
440
	os.Exit(1)

+ 19 - 0
go/blog/blog.yaml

63
- url: https://www.youtube.com/watch?v=yLuOzNeHw5I
63
- url: https://www.youtube.com/watch?v=yLuOzNeHw5I
64
  content: Lights, by Archive.
64
  content: Lights, by Archive.
65
  type: video
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