-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
368 lines (348 loc) · 47.2 KB
/
index.html
File metadata and controls
368 lines (348 loc) · 47.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="mobile-web-app-capable" content="yes">
<title>
Yolo Tagger - HackMD
</title>
<link rel="icon" type="image/png" href="https://hackmd.io/favicon.png">
<link rel="apple-touch-icon" href="https://hackmd.io/apple-touch-icon.png">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha256-916EbMg70RQy9LHiGkXzG8hSg9EdNy97GazNG/aiY1w=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css" integrity="sha256-3iu9jgsy9TpTwXKb7bNQzqWekRX7pPK+2OLj3R922fo=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/octicons/3.5.0/octicons.min.css" integrity="sha256-QiWfLIsCT02Sdwkogf6YMiQlj4NE84MKkzEMkZnMGdg=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/themes/prism.min.css" integrity="sha256-vtR0hSWRc3Tb26iuN2oZHt3KRUomwTufNIf5/4oeCyg=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/github-gist.min.css" integrity="sha256-tAflq+ymku3Khs+I/WcAneIlafYgDiOQ9stIHH985Wo=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/emojify.js/1.1.0/css/basic/emojify.min.css" integrity="sha256-UOrvMOsSDSrW6szVLe8ZDZezBxh5IoIfgTwdNDgTjiU=" crossorigin="anonymous" />
<style>
@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400italic,600,600italic,300italic,300|Source+Serif+Pro|Source+Code+Pro:400,300,500&subset=latin,latin-ext);.markdown-body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;font-size:16px;line-height:1.5;word-wrap:break-word}.markdown-body:after,.markdown-body:before{display:table;content:""}.markdown-body:after{clear:both}.markdown-body>:first-child{margin-top:0!important}.markdown-body>:last-child{margin-bottom:0!important}.markdown-body a:not([href]){color:inherit;text-decoration:none}.markdown-body .absent{color:#c00}.markdown-body .anchor{float:left;padding-right:4px;margin-left:-20px;line-height:1}.markdown-body .anchor:focus{outline:none}.markdown-body blockquote,.markdown-body dl,.markdown-body ol,.markdown-body p,.markdown-body pre,.markdown-body table,.markdown-body ul{margin-top:0;margin-bottom:16px}.markdown-body hr{height:.25em;padding:0;margin:24px 0;background-color:#e7e7e7;border:0}.markdown-body blockquote{padding:0 1em;color:#777;border-left:.25em solid #ddd}.markdown-body blockquote>:first-child{margin-top:0}.markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body .loweralpha{list-style-type:lower-alpha}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{margin-top:24px;margin-bottom:16px;font-weight:600;line-height:1.25}.markdown-body h1 .octicon-link,.markdown-body h2 .octicon-link,.markdown-body h3 .octicon-link,.markdown-body h4 .octicon-link,.markdown-body h5 .octicon-link,.markdown-body h6 .octicon-link{color:#000;vertical-align:middle;visibility:hidden}.markdown-body h1:hover .anchor,.markdown-body h2:hover .anchor,.markdown-body h3:hover .anchor,.markdown-body h4:hover .anchor,.markdown-body h5:hover .anchor,.markdown-body h6:hover .anchor{text-decoration:none}.markdown-body h1:hover .anchor .octicon-link,.markdown-body h2:hover .anchor .octicon-link,.markdown-body h3:hover .anchor .octicon-link,.markdown-body h4:hover .anchor .octicon-link,.markdown-body h5:hover .anchor .octicon-link,.markdown-body h6:hover .anchor .octicon-link{visibility:visible}.markdown-body h1 code,.markdown-body h1 tt,.markdown-body h2 code,.markdown-body h2 tt,.markdown-body h3 code,.markdown-body h3 tt,.markdown-body h4 code,.markdown-body h4 tt,.markdown-body h5 code,.markdown-body h5 tt,.markdown-body h6 code,.markdown-body h6 tt{font-size:inherit}.markdown-body h1{font-size:2em}.markdown-body h1,.markdown-body h2{padding-bottom:.3em;border-bottom:1px solid #eee}.markdown-body h2{font-size:1.5em}.markdown-body h3{font-size:1.25em}.markdown-body h4{font-size:1em}.markdown-body h5{font-size:.875em}.markdown-body h6{font-size:.85em;color:#777}.markdown-body ol,.markdown-body ul{padding-left:2em}.markdown-body ol.no-list,.markdown-body ul.no-list{padding:0;list-style-type:none}.markdown-body ol ol,.markdown-body ol ul,.markdown-body ul ol,.markdown-body ul ul{margin-top:0;margin-bottom:0}.markdown-body li>p{margin-top:16px}.markdown-body li+li{margin-top:.25em}.markdown-body dl{padding:0}.markdown-body dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:700}.markdown-body dl dd{padding:0 16px;margin-bottom:16px}.markdown-body table{display:block;width:100%;overflow:auto;word-break:normal;word-break:keep-all}.markdown-body table th{font-weight:700}.markdown-body table td,.markdown-body table th{padding:6px 13px;border:1px solid #ddd}.markdown-body table tr{background-color:#fff;border-top:1px solid #ccc}.markdown-body table tr:nth-child(2n){background-color:#f8f8f8}.markdown-body img{max-width:100%;box-sizing:content-box;background-color:#fff}.markdown-body img[align=right]{padding-left:20px}.markdown-body img[align=left]{padding-right:20px}.markdown-body .emoji{max-width:none;vertical-align:text-top;background-color:transparent}.markdown-body span.frame{display:block;overflow:hidden}.markdown-body span.frame>span{display:block;float:left;width:auto;padding:7px;margin:13px 0 0;overflow:hidden;border:1px solid #ddd}.markdown-body span.frame span img{display:block;float:left}.markdown-body span.frame span span{display:block;padding:5px 0 0;clear:both;color:#333}.markdown-body span.align-center{display:block;overflow:hidden;clear:both}.markdown-body span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center}.markdown-body span.align-center span img{margin:0 auto;text-align:center}.markdown-body span.align-right{display:block;overflow:hidden;clear:both}.markdown-body span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right}.markdown-body span.align-right span img{margin:0;text-align:right}.markdown-body span.float-left{display:block;float:left;margin-right:13px;overflow:hidden}.markdown-body span.float-left span{margin:13px 0 0}.markdown-body span.float-right{display:block;float:right;margin-left:13px;overflow:hidden}.markdown-body span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right}.markdown-body code,.markdown-body tt{padding:0;padding-top:.2em;padding-bottom:.2em;margin:0;font-size:85%;background-color:rgba(0,0,0,.04);border-radius:3px}.markdown-body code:after,.markdown-body code:before,.markdown-body tt:after,.markdown-body tt:before{letter-spacing:-.2em;content:"\A0"}.markdown-body code br,.markdown-body tt br{display:none}.markdown-body del code{text-decoration:inherit}.markdown-body pre{word-wrap:normal}.markdown-body pre>code{padding:0;margin:0;font-size:100%;word-break:normal;white-space:pre;background:transparent;border:0}.markdown-body .highlight{margin-bottom:16px}.markdown-body .highlight pre{margin-bottom:0;word-break:normal}.markdown-body .highlight pre,.markdown-body pre{padding:16px;overflow:auto;font-size:85%;line-height:1.45;background-color:#f7f7f7;border-radius:3px}.markdown-body pre code,.markdown-body pre tt{display:inline;max-width:auto;padding:0;margin:0;overflow:visible;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.markdown-body pre code:after,.markdown-body pre code:before,.markdown-body pre tt:after,.markdown-body pre tt:before{content:normal}.markdown-body .csv-data td,.markdown-body .csv-data th{padding:5px;overflow:hidden;font-size:12px;line-height:1;text-align:left;white-space:nowrap}.markdown-body .csv-data .blob-line-num{padding:10px 8px 9px;text-align:right;background:#fff;border:0}.markdown-body .csv-data tr{border-top:0}.markdown-body .csv-data th{font-weight:700;background:#f8f8f8;border-top:0}.markdown-body kbd{display:inline-block;padding:3px 5px;font-size:11px;line-height:10px;color:#555;vertical-align:middle;background-color:#fcfcfc;border:1px solid #ccc;border-bottom-color:#bbb;border-radius:3px;box-shadow:inset 0 -1px 0 #bbb}.news .alert .markdown-body blockquote{padding:0 0 0 40px;border:0 none}.activity-tab .news .alert .commits,.activity-tab .news .markdown-body blockquote{padding-left:0}.task-list-item{list-style-type:none}.task-list-item label{font-weight:400}.task-list-item.enabled label{cursor:pointer}.task-list-item+.task-list-item{margin-top:3px}.task-list-item-checkbox{float:left;margin:.31em 0 .2em -1.3em!important;vertical-align:middle;cursor:default!important}.markdown-body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Helvetica,Arial,sans-serif;padding-top:40px;padding-bottom:40px;max-width:758px;overflow:visible!important}.markdown-body pre{border:inherit!important}.markdown-body code{color:inherit!important}.markdown-body pre code .wrapper{display:-moz-inline-flex;display:-ms-inline-flex;display:-o-inline-flex;display:inline-flex}.markdown-body pre code .gutter{float:left;overflow:hidden;-webkit-user-select:none;user-select:none}.markdown-body pre code .gutter.linenumber{text-align:right;position:relative;display:inline-block;cursor:default;z-index:4;padding:0 8px 0 0;min-width:20px;box-sizing:content-box;color:#afafaf!important;border-right:3px solid #6ce26c!important}.markdown-body pre code .gutter.linenumber>span:before{content:attr(data-linenumber)}.markdown-body pre code .code{float:left;margin:0 0 0 16px}.markdown-body .gist .line-numbers{border-left:none;border-top:none;border-bottom:none}.markdown-body .gist .line-data{border:none}.markdown-body .gist table{border-spacing:0;border-collapse:inherit!important}.markdown-body code[data-gist-id]{background:none;padding:0}.markdown-body code[data-gist-id]:after,.markdown-body code[data-gist-id]:before{content:""}.markdown-body code[data-gist-id] .blob-num{border:unset}.markdown-body code[data-gist-id] table{overflow:unset;margin-bottom:unset}.markdown-body code[data-gist-id] table tr{background:unset}.markdown-body[dir=rtl] pre{direction:ltr}.markdown-body[dir=rtl] code{direction:ltr;unicode-bidi:embed}.markdown-body .alert>p{margin-bottom:0}.markdown-body pre.abc,.markdown-body pre.flow-chart,.markdown-body pre.graphviz,.markdown-body pre.mermaid,.markdown-body pre.sequence-diagram{text-align:center;background-color:inherit;border-radius:0;white-space:inherit}.markdown-body pre.abc>code,.markdown-body pre.flow-chart>code,.markdown-body pre.graphviz>code,.markdown-body pre.mermaid>code,.markdown-body pre.sequence-diagram>code{text-align:left}.markdown-body pre.abc>svg,.markdown-body pre.flow-chart>svg,.markdown-body pre.graphviz>svg,.markdown-body pre.mermaid>svg,.markdown-body pre.sequence-diagram>svg{max-width:100%;height:100%}.markdown-body pre>code.wrap{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}.markdown-body .alert>p,.markdown-body .alert>ul{margin-bottom:0}.vimeo,.youtube{cursor:pointer;display:table;text-align:center;background-position:50%;background-repeat:no-repeat;background-size:contain;background-color:#000;overflow:hidden}.vimeo,.youtube{position:relative;width:100%}.youtube{padding-bottom:56.25%}.vimeo img{width:100%;object-fit:contain;z-index:0}.youtube img{object-fit:cover;z-index:0}.vimeo iframe,.youtube iframe,.youtube img{width:100%;height:100%;position:absolute;top:0;left:0}.vimeo iframe,.youtube iframe{vertical-align:middle;z-index:1}.vimeo .icon,.youtube .icon{position:absolute;height:auto;width:auto;top:50%;left:50%;transform:translate(-50%,-50%);color:#fff;opacity:.3;transition:opacity .2s;z-index:0}.vimeo:hover .icon,.youtube:hover .icon{opacity:.6;transition:opacity .2s}.slideshare .inner,.speakerdeck .inner{position:relative;width:100%}.slideshare .inner iframe,.speakerdeck .inner iframe{position:absolute;top:0;bottom:0;left:0;right:0;width:100%;height:100%}.MJX_Assistive_MathML{display:none}.ui-infobar{position:relative;z-index:2;max-width:758px;margin-top:25px;margin-bottom:-25px;color:#777}.ui-toc{position:fixed;bottom:20px;z-index:10000}.ui-toc-label{opacity:.3;background-color:#ccc;border:none;transition:opacity .2s}.ui-toc .open .ui-toc-label{opacity:1;color:#fff;transition:opacity .2s}.ui-toc-label:focus{opacity:.3;background-color:#ccc;color:#000}.ui-toc-label:hover{opacity:1;background-color:#ccc;transition:opacity .2s}.ui-toc-dropdown{margin-top:23px;margin-bottom:20px;padding-left:10px;padding-right:10px;max-width:45vw;width:25vw;max-height:70vh;overflow:auto;text-align:inherit}.ui-toc-dropdown>.toc{max-height:calc(70vh - 100px);overflow:auto}.ui-toc-dropdown[dir=rtl] .nav{padding-right:0;letter-spacing:.0029em}.ui-toc-dropdown a{overflow:hidden;text-overflow:ellipsis;white-space:pre}.ui-toc-dropdown .nav>li>a{display:block;padding:4px 20px;font-size:13px;font-weight:500;color:#767676}.ui-toc-dropdown .nav>li:first-child:last-child > ul,.ui-toc-dropdown .toc.expand ul{display:block}.ui-toc-dropdown .nav>li>a:focus,.ui-toc-dropdown .nav>li>a:hover{padding-left:19px;color:#000;text-decoration:none;background-color:transparent;border-left:1px solid #000}.ui-toc-dropdown[dir=rtl] .nav>li>a:focus,.ui-toc-dropdown[dir=rtl] .nav>li>a:hover{padding-right:19px;border-left:none;border-right:1px solid #000}.ui-toc-dropdown .nav>.active:focus>a,.ui-toc-dropdown .nav>.active:hover>a,.ui-toc-dropdown .nav>.active>a{padding-left:18px;font-weight:700;color:#000;background-color:transparent;border-left:2px solid #000}.ui-toc-dropdown[dir=rtl] .nav>.active:focus>a,.ui-toc-dropdown[dir=rtl] .nav>.active:hover>a,.ui-toc-dropdown[dir=rtl] .nav>.active>a{padding-right:18px;border-left:none;border-right:2px solid #000}.ui-toc-dropdown .nav .nav{display:none;padding-bottom:10px}.ui-toc-dropdown .nav>.active>ul{display:block}.ui-toc-dropdown .nav .nav>li>a{padding-top:1px;padding-bottom:1px;padding-left:30px;font-size:12px;font-weight:400}.ui-toc-dropdown[dir=rtl] .nav .nav>li>a{padding-right:30px}.ui-toc-dropdown .nav .nav>li>ul>li>a{padding-top:1px;padding-bottom:1px;padding-left:40px;font-size:12px;font-weight:400}.ui-toc-dropdown[dir=rtl] .nav .nav>li>ul>li>a{padding-right:40px}.ui-toc-dropdown .nav .nav>li>a:focus,.ui-toc-dropdown .nav .nav>li>a:hover{padding-left:29px}.ui-toc-dropdown[dir=rtl] .nav .nav>li>a:focus,.ui-toc-dropdown[dir=rtl] .nav .nav>li>a:hover{padding-right:29px}.ui-toc-dropdown .nav .nav>li>ul>li>a:focus,.ui-toc-dropdown .nav .nav>li>ul>li>a:hover{padding-left:39px}.ui-toc-dropdown[dir=rtl] .nav .nav>li>ul>li>a:focus,.ui-toc-dropdown[dir=rtl] .nav .nav>li>ul>li>a:hover{padding-right:39px}.ui-toc-dropdown .nav .nav>.active:focus>a,.ui-toc-dropdown .nav .nav>.active:hover>a,.ui-toc-dropdown .nav .nav>.active>a{padding-left:28px;font-weight:500}.ui-toc-dropdown[dir=rtl] .nav .nav>.active:focus>a,.ui-toc-dropdown[dir=rtl] .nav .nav>.active:hover>a,.ui-toc-dropdown[dir=rtl] .nav .nav>.active>a{padding-right:28px}.ui-toc-dropdown .nav .nav>.active>.nav>.active:focus>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active:hover>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active>a{padding-left:38px;font-weight:500}.ui-toc-dropdown[dir=rtl] .nav .nav>.active>.nav>.active:focus>a,.ui-toc-dropdown[dir=rtl] .nav .nav>.active>.nav>.active:hover>a,.ui-toc-dropdown[dir=rtl] .nav .nav>.active>.nav>.active>a{padding-right:38px}.markdown-body[lang^=ja]{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Helvetica,Arial,Hiragino Kaku Gothic Pro,\\30D2\30E9\30AE\30CE\89D2\30B4 Pro W3,Osaka,Meiryo,\\30E1\30A4\30EA\30AA,MS Gothic,"\FF2D\FF33 \30B4\30B7\30C3\30AF",sans-serif}.ui-toc-dropdown[lang^=ja]{font-family:Source Sans Pro,Helvetica,Arial,Meiryo UI,MS PGothic,"\FF2D\FF33 \FF30\30B4\30B7\30C3\30AF",sans-serif}.markdown-body[lang=zh-tw]{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Helvetica,Arial,PingFang TC,Microsoft JhengHei,\\5FAE\8EDF\6B63\9ED1,sans-serif}.ui-toc-dropdown[lang=zh-tw]{font-family:Source Sans Pro,Helvetica,Arial,Microsoft JhengHei UI,\\5FAE\8EDF\6B63\9ED1UI,sans-serif}.markdown-body[lang=zh-cn]{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Helvetica,Arial,PingFang SC,Microsoft YaHei,\\5FAE\8F6F\96C5\9ED1,sans-serif}.ui-toc-dropdown[lang=zh-cn]{font-family:Source Sans Pro,Helvetica,Arial,Microsoft YaHei UI,\\5FAE\8F6F\96C5\9ED1UI,sans-serif}.ui-affix-toc{position:fixed;top:0;max-width:15vw;max-height:70vh;overflow:auto}.back-to-top,.expand-toggle,.go-to-bottom{display:block;padding:4px 10px;margin-top:10px;margin-left:10px;font-size:12px;font-weight:500;color:#999}.back-to-top:focus,.back-to-top:hover,.expand-toggle:focus,.expand-toggle:hover,.go-to-bottom:focus,.go-to-bottom:hover{color:#563d7c;text-decoration:none}.back-to-top,.go-to-bottom{margin-top:0}.ui-user-icon{width:20px;height:20px;display:block;border-radius:3px;margin-top:2px;margin-bottom:2px;margin-right:5px;background-position:50%;background-repeat:no-repeat;background-size:contain}.ui-user-icon.small{width:18px;height:18px;display:inline-block;vertical-align:middle;margin:0 0 .2em}small span{line-height:22px}small .dropdown{display:inline-block}small .dropdown a:focus,small .dropdown a:hover{text-decoration:none}.unselectable{-moz-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none}@media print{blockquote,div,img,pre,table{page-break-inside:avoid!important}a[href]:after{font-size:12px!important}}.markdown-body.slides{position:relative;z-index:1;color:#222}.markdown-body.slides:before{content:"";display:block;position:absolute;top:0;left:0;right:0;bottom:0;z-index:-1;background-color:currentColor;box-shadow:0 0 0 50vw}.markdown-body.slides section[data-markdown]{position:relative;margin-bottom:1.5em;background-color:#fff;text-align:center}.markdown-body.slides section[data-markdown] code{text-align:left}.markdown-body.slides section[data-markdown]:before{content:"";display:block;padding-bottom:56.23%}.markdown-body.slides section[data-markdown]>div:first-child{position:absolute;top:50%;left:1em;right:1em;transform:translateY(-50%);max-height:100%;overflow:hidden}.markdown-body.slides section[data-markdown]>ul{display:inline-block}.markdown-body.slides>section>section+section:after{content:"";position:absolute;top:-1.5em;right:1em;height:1.5em;border:3px solid #777}body{font-smoothing:subpixel-antialiased!important;-webkit-font-smoothing:subpixel-antialiased!important;-moz-osx-font-smoothing:auto!important;text-shadow:0 0 1em transparent,1px 1px 1.2px rgba(0,0,0,.004);-webkit-overflow-scrolling:touch;font-family:Source Sans Pro,Helvetica,Arial,sans-serif;letter-spacing:.025em}.focus,:focus{outline:none!important}::-moz-focus-inner{border:0!important}body.modal-open{overflow-y:auto;padding-right:0!important}
</style>
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js" integrity="sha256-3Jy/GbSLrg0o9y5Z5n1uw0qxZECH7C6OQpVBgNFYa0g=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js" integrity="sha256-g6iAfvZp+nDQ2TdTR/VVKJf3bGro4ub5fvWSWVRi2NE=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.9/es5-shim.min.js" integrity="sha256-8E4Is26QH0bD52WoQpcB+R/tcWQtpzlCojrybUd7Mxo=" crossorigin="anonymous"></script>
<![endif]-->
</head>
<body>
<div id="doc" class="markdown-body container-fluid"><h1 id="yolo-tagger"><a class="anchor hidden-xs" href="#yolo-tagger" title="yolo-tagger"><span class="octicon octicon-link"></span></a>Yolo Tagger</h1><p>Welcome to the documentation of Yolo Tagger. Check the following sections for an overview of our project repositories, are follow these links to get you started:</p><ul>
<li><a href="https://github.com/SwaggerTagger" target="_blank">SwaggerTagger</a>, the Github Org where our source code lives</li>
<li><a href="https://swaggertagger.github.io/impress.js/#/title-slide" target="_blank">An Introductory Presentation of Yolo Tagger</a></li>
<li><a href="https://pjreddie.com/darknet/yolo/" target="_blank">The official Yolo Website</a> (not affiliated with us in any way)</li>
</ul><h1 id="frontend"><a class="anchor hidden-xs" href="#frontend" title="frontend"><span class="octicon octicon-link"></span></a>Frontend</h1><p>This repository contains the Frontend for the <em>yolo tagger</em> project, an infrastructure built around the amazing <a href="https://pjreddie.com/darknet/yolo/" target="_blank">darknet yolo project</a>. Check it out at <a href="https://gruppe7.testsites.info" target="_blank">gruppe7.testsites.info</a>.</p><h2 id="technologies"><a class="anchor hidden-xs" href="#technologies" title="technologies"><span class="octicon octicon-link"></span></a>Technologies</h2><p>This Frontend is using the following (notable) technologies:</p><ul>
<li>The Vue Stack (<a href="https://vuejs.org/" target="_blank">VueJS</a>, <a href="https://router.vuejs.org/en/" target="_blank">vue-router</a>, <a href="https://vuex.vuejs.org/en/intro.html" target="_blank">vuex</a>), an awesome fully featured SPA Framework</li>
<li><a href="https://vuematerial.github.io/#/" target="_blank">Vue Material</a>, Material Design Components for Vue.</li>
<li><a href="https://github.com/pagekit/vue-resource" target="_blank">vue-resource</a>, an HTTP client for Vue.</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events" target="_blank">Server Sent Events</a>, for pushing classification results to the client in real time. We also support polling <a href="http://gph.is/1sXV7Iq" target="_blank">if your browser doesn’t support</a> SSE’s.</li>
</ul><h2 id="deployment0"><a class="anchor hidden-xs" href="#deployment0" title="deployment0"><span class="octicon octicon-link"></span></a>Deployment</h2><h3 id="nginx"><a class="anchor hidden-xs" href="#nginx" title="nginx"><span class="octicon octicon-link"></span></a>Nginx</h3><p>This repository contains a <a href="https://github.com/SwaggerTagger/octo-tagger-frontend/blob/master/Dockerfile" target="_blank"><code>Dockerfile</code></a>, as well as a <a href="https://github.com/SwaggerTagger/octo-tagger-frontend/blob/master/deploy/nginx.conf" target="_blank"><code>NGINX configuration</code></a>, that builds an SSL-Enabled Docker container that serves this project as static HTML and provides a reverse proxy connection to the Backend. You can use <code>push.sh</code> at the root of the repository to push modifications of the frontend to our Container Registry.</p><h3 id="re-deploying-to-kubernetes"><a class="anchor hidden-xs" href="#re-deploying-to-kubernetes" title="re-deploying-to-kubernetes"><span class="octicon octicon-link"></span></a>(re-)deploying to Kubernetes</h3><p>Together with <a href="https://github.com/SwaggerTagger/tagger-kubernetes" target="_blank">tagger-kubernetes</a>, here’s how you deploy the frontend to your cluster using one shell command:</p><pre><code class="bash hljs"><span class="hljs-built_in">alias</span> deploy-webeng-frontend=<span class="hljs-string">"npm run build && ./push.sh && kubectl delete -f ../webeng-tagger/30_frontend-deploy.yml && kubectl create -f ../webeng-tagger/30_frontend-deploy.yml"</span>
</code></pre><h2 id="build-setup"><a class="anchor hidden-xs" href="#build-setup" title="build-setup"><span class="octicon octicon-link"></span></a>Build Setup</h2><pre><code class="bash hljs"><span class="hljs-comment"># install dependencies</span>
npm install
<span class="hljs-comment"># serve with hot reload at localhost:8080</span>
npm run dev
<span class="hljs-comment"># build for production with minification</span>
npm run build
<span class="hljs-comment"># build for production and view the bundle analyzer report</span>
npm run build --report
</code></pre><h1 id="backend-application-server-for-yolo-tagger"><a class="anchor hidden-xs" href="#backend-application-server-for-yolo-tagger" title="backend-application-server-for-yolo-tagger"><span class="octicon octicon-link"></span></a>Backend Application Server for Yolo-Tagger</h1><p>This server provides the Rest-Api for the Yolo-Tagging-Service. It was created using the <a href="https://github.com/dpitkevics/play-silhouette-4.0-slick-postgres-seed" target="_blank">Play 2.5 Silhouette 4.0 Slick PostgreSQL seed</a> that provides Authorization and User Management, but customized to be accessible via a Rest-Api.</p><h2 id="notable-technologieslibraries-used"><a class="anchor hidden-xs" href="#notable-technologieslibraries-used" title="notable-technologieslibraries-used"><span class="octicon octicon-link"></span></a>Notable technologies/libraries used</h2><ul>
<li><a href="http://slick.lightbend.com/" target="_blank">Slick</a>, a database abstraction layer</li>
<li><a href="http://akka.io/" target="_blank">Akka</a>, an actor-based messaging system inside our application, that is for example used to connect to kafka</li>
<li><a href="https://www.playframework.com/" target="_blank">Play</a>, a framework for web applications</li>
</ul><h2 id="tools"><a class="anchor hidden-xs" href="#tools" title="tools"><span class="octicon octicon-link"></span></a>Tools</h2><p>You can use Postman to directly communicate with the Api.<br>
You can either:</p><ul>
<li>Go to the <a href="https://documenter.getpostman.com/collection/view/12316-7a9d5d39-d5ff-280e-88d6-f48e3775836f" target="_blank">published documentation</a></li>
<li>Use the exported version here in the repository (file octo-tagger.postman_collection.json), but you have to create the correct environment yourself%</li>
</ul><h1 id="tagger-worker"><a class="anchor hidden-xs" href="#tagger-worker" title="tagger-worker"><span class="octicon octicon-link"></span></a>Tagger Worker</h1><p>This python script will connect to Kafka, digest incoming darknet classification tasks from an input topic, classify them using a <a href="https://github.com/SwaggerTagger/darknet" target="_blank">slightly modified version</a> of <a href="https://pjreddie.com/darknet/yolo/" target="_blank">darknet YOLO</a> and put the results into an output topic.</p><h2 id="configuration"><a class="anchor hidden-xs" href="#configuration" title="configuration"><span class="octicon octicon-link"></span></a>Configuration</h2><p>Configuration works via the following Environment Variables:</p><table>
<thead>
<tr>
<th>Environment Variable</th>
<th>Default Value</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>KAFKA_BOOTSTRAP_SERVER</code></td>
<td><code>broker.kafka.svc.cluster.local</code></td>
</tr>
<tr>
<td><code>DARKNET_CMD_TEMPLATE</code></td>
<td><code>detector test cfg/yolo.cfg yolo.weights {file} -tagger-output</code></td>
</tr>
<tr>
<td><code>DARKNET_EXECUTABLE</code></td>
<td><code>/darknet/darknet</code></td>
</tr>
<tr>
<td><code>KAFKA_INCOMING_TOPIC</code></td>
<td><code>incoming-pics</code></td>
</tr>
<tr>
<td><code>KAFKA_DESTINATION_TOPIC</code></td>
<td><code>predictions</code></td>
</tr>
<tr>
<td><code>KAFKA_STATUS_TOPIC</code></td>
<td><code>classification-status</code></td>
</tr>
<tr>
<td><code>KAFKA_CONSUMER_GROUP</code></td>
<td><code>tagger-workers</code></td>
</tr>
<tr>
<td><code>DARKNET_WORKING_DIR</code></td>
<td><code>/darknet</code></td>
</tr>
</tbody>
</table><h2 id="interface"><a class="anchor hidden-xs" href="#interface" title="interface"><span class="octicon octicon-link"></span></a>Interface</h2><h3 id="incoming-messages"><a class="anchor hidden-xs" href="#incoming-messages" title="incoming-messages"><span class="octicon octicon-link"></span></a>Incoming Messages</h3><p>The Worker expects incoming messages in <code>KAFKA_INCOMING_TOPIC</code> to have a non-null keyId and have the following format:</p><pre><code class="json hljs">{
<span class="hljs-attr">"url"</span>: <span class="hljs-string">"https://..../.../.jpg"</span>
}
</code></pre><p>Every additional Json Key Value Pair is ignored.</p><h3 id="outgoing-messages"><a class="anchor hidden-xs" href="#outgoing-messages" title="outgoing-messages"><span class="octicon octicon-link"></span></a>Outgoing Messages</h3><h4 id="predictions-kafka-destination-topic"><a class="anchor hidden-xs" href="#predictions-kafka-destination-topic" title="predictions-kafka-destination-topic"><span class="octicon octicon-link"></span></a>Predictions (<code>KAFKA_DESTINATION_TOPIC</code>)</h4><p>The Worker will put classification results into <code>KAFKA_DESTINATION_TOPIC</code> with the same keyId as the incoming message and the following json format:</p><pre><code class="json hljs">{
<span class="hljs-attr">"count"</span>: <span class="hljs-number">7</span>,
<span class="hljs-attr">"input"</span>: <span class="hljs-string">"/tmp/classifyq2yg7ey1.jpg"</span>,
<span class="hljs-attr">"matches"</span>: [
{
<span class="hljs-attr">"left"</span>: <span class="hljs-number">17</span>,
<span class="hljs-attr">"right"</span>: <span class="hljs-number">32</span>,
<span class="hljs-attr">"top"</span>: <span class="hljs-number">301</span>,
<span class="hljs-attr">"class"</span>: <span class="hljs-string">"person"</span>,
<span class="hljs-attr">"probability"</span>: <span class="hljs-number">0.346982</span>,
<span class="hljs-attr">"bottom"</span>: <span class="hljs-number">333</span>
},
{
<span class="hljs-attr">"left"</span>: <span class="hljs-number">215</span>,
<span class="hljs-attr">"right"</span>: <span class="hljs-number">229</span>,
<span class="hljs-attr">"top"</span>: <span class="hljs-number">288</span>,
<span class="hljs-attr">"class"</span>: <span class="hljs-string">"person"</span>,
<span class="hljs-attr">"probability"</span>: <span class="hljs-number">0.288302</span>,
<span class="hljs-attr">"bottom"</span>: <span class="hljs-number">334</span>
},
{
<span class="hljs-attr">"left"</span>: <span class="hljs-number">276</span>,
<span class="hljs-attr">"right"</span>: <span class="hljs-number">290</span>,
<span class="hljs-attr">"top"</span>: <span class="hljs-number">289</span>,
<span class="hljs-attr">"class"</span>: <span class="hljs-string">"person"</span>,
<span class="hljs-attr">"probability"</span>: <span class="hljs-number">0.342672</span>,
<span class="hljs-attr">"bottom"</span>: <span class="hljs-number">341</span>
},
{
<span class="hljs-attr">"left"</span>: <span class="hljs-number">238</span>,
<span class="hljs-attr">"right"</span>: <span class="hljs-number">267</span>,
<span class="hljs-attr">"top"</span>: <span class="hljs-number">291</span>,
<span class="hljs-attr">"class"</span>: <span class="hljs-string">"person"</span>,
<span class="hljs-attr">"probability"</span>: <span class="hljs-number">0.292135</span>,
<span class="hljs-attr">"bottom"</span>: <span class="hljs-number">353</span>
}
],
<span class="hljs-attr">"time"</span>: <span class="hljs-number">38.847912</span>
}
</code></pre><h4 id="status-kafka-status-topic"><a class="anchor hidden-xs" href="#status-kafka-status-topic" title="status-kafka-status-topic"><span class="octicon octicon-link"></span></a>Status (<code>KAFKA_STATUS_TOPIC</code>)</h4><p>Status updates are correlated with the image they relate to using the keyId and are in the following format:</p><pre><code class="json hljs">{ <span class="hljs-attr">"status"</span>: <span class="hljs-string">"CLASSIFICATION_STARTING"</span> }
</code></pre><p>where the value of status may be one of these keywords:</p><table>
<thead>
<tr>
<th>Keyword</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>CLASSIFICATION_STARTING</code></td>
<td><code>emitted directly before classification with darknet begins</code></td>
</tr>
<tr>
<td><code>CLASSIFICATION_FAILED_INVALID_INPUT</code></td>
<td><code>the message coming from kafka is malformed</code></td>
</tr>
<tr>
<td><code>CLASSIFICATION_FAILED_DARKNET_FAILED</code></td>
<td><code>darknet returned a nonzero exit code. Messages with this status also have "stdout" and "stderr" properties for debugging purposes.</code></td>
</tr>
<tr>
<td><code>CLASSIFICATION_FAILED_UNKNOWN</code></td>
<td><code>sent when un unknown error occurs. the worker will die immediately after this status update is placed, because this error message indicated something is going seriously wrong</code></td>
</tr>
</tbody>
</table><h2 id="build-container"><a class="anchor hidden-xs" href="#build-container" title="build-container"><span class="octicon octicon-link"></span></a>Build Container</h2><pre><code class="bash hljs">$ git <span class="hljs-built_in">clone</span> https://github.com/SwaggerTagger/DarknetKafkaWorker
$ <span class="hljs-built_in">cd</span> DarknetKafkaWorker
$ git <span class="hljs-built_in">clone</span> https://github.com/SwaggerTagger/darknet
$ wget http://pjreddie.com/media/files/yolo.weights -O darknet/yolo.weights
$ docker build -t tagger-worker:latest .
</code></pre><h1 id="our-modifications-to-darknet"><a class="anchor hidden-xs" href="#our-modifications-to-darknet" title="our-modifications-to-darknet"><span class="octicon octicon-link"></span></a>Our Modifications to Darknet</h1><p>We had to fork darknet to work with our project. These modifications where made made to the darknet source code:</p><h2 id="machine-readable-output"><a class="anchor hidden-xs" href="#machine-readable-output" title="machine-readable-output"><span class="octicon octicon-link"></span></a>Machine Readable Output</h2><p>Have darknet output machine readable (json) predictions instead of writing the results directly to the bounding boxes of an image.<br>
For this purpose <code>darknet detector</code> now recognizes the <code>-tagger-output</code> switch, which will disable all writes to stdout except when the predictions are written.</p><pre><code class="bash hljs">./darknet detector <span class="hljs-built_in">test</span> cfg/coco.data cfg/yolo.cfg yolo.weights data/dog.jpg -tagger-output 2>/dev/null
{<span class="hljs-string">"input"</span>: <span class="hljs-string">"data/dog.jpg"</span>, <span class="hljs-string">"time"</span>: 13.036422, <span class="hljs-string">"matches"</span>: [
{<span class="hljs-string">"class"</span>: <span class="hljs-string">"dog"</span>, <span class="hljs-string">"probability"</span>: 0.823520, <span class="hljs-string">"left"</span>: 132, <span class="hljs-string">"right"</span>: 321, <span class="hljs-string">"top"</span>: 231, <span class="hljs-string">"bottom"</span>: 521 }
, {<span class="hljs-string">"class"</span>: <span class="hljs-string">"truck"</span>, <span class="hljs-string">"probability"</span>: 0.643006, <span class="hljs-string">"left"</span>: 467, <span class="hljs-string">"right"</span>: 680, <span class="hljs-string">"top"</span>: 84, <span class="hljs-string">"bottom"</span>: 168 }
, {<span class="hljs-string">"class"</span>: <span class="hljs-string">"bicycle"</span>, <span class="hljs-string">"probability"</span>: 0.852180, <span class="hljs-string">"left"</span>: 95, <span class="hljs-string">"right"</span>: 588, <span class="hljs-string">"top"</span>: 123, <span class="hljs-string">"bottom"</span>: 448 }
], <span class="hljs-string">"count"</span>: 3 }
</code></pre><h1 id="kubernetes"><a class="anchor hidden-xs" href="#kubernetes" title="kubernetes"><span class="octicon octicon-link"></span></a>Kubernetes</h1><p><code>Kubernetes .yml files for the tagger cluster</code></p><h2 id="overview"><a class="anchor hidden-xs" href="#overview" title="overview"><span class="octicon octicon-link"></span></a>Overview</h2><p>This repository contains all files required to boot the tagger cluster (except for Kafka, which can be deployed using <a href="https://github.com/Yolean/kubernetes-kafka" target="_blank">these .yml files</a>).</p><p>Here is a description of the Kubernetes Object that are defined in this repository (in the order they get created):</p><ul>
<li>a <code>tagger</code> namespace, where everything will live</li>
<li><code>tagger-secrets</code>, a secrets object used by the backend containing authentification to azure blob storage and a secret used for JWT signing</li>
<li><code>testsites-info-secrets</code>, which contains the <code>fullchain.pem</code> and <code>privkey.pem</code> for <a href="https://gruppe7.testsites.info" target="_blank">gruppe7.testsites.info</a>.</li>
<li><code>postgres-deploy</code>, which deploys postgresql into the cluster</li>
<li><code>postgres-service</code>, which makes the postgres deployment addressable by the cluster</li>
<li><code>tagger-backend-deploy</code>, which deploys the <a href="https://github.com/SwaggerTagger/octo-tagger-backend" target="_blank">tagger backend</a> into the cluster.</li>
<li><code>tagger-backend-service</code>, which makes the service addressable by the cluster</li>
<li><code>tagger-worker-deploy</code>, which deploys the worker into the cluster</li>
<li><code>frontend-deploy</code>, which deploys the <a href="https://github.com/SwaggerTagger/octo-tagger-frontend" target="_blank">tagger-frontend</a> into the cluster</li>
<li><code>frontend-expose</code>, which installs a load balancer in front of the frontend deployment and serves as an entry point to the outside world.</li>
</ul><p>Additionally, the following helper objects are defined:</p><ul>
<li><code>get-certs</code>, a deployment of a letsencrypt docker image that can be used to generate certificates.</li>
<li><code>kafka-manager-deploy</code>, which is used to deploy an instance of kafka-manager into the cluster.</li>
</ul><h2 id="deployment"><a class="anchor hidden-xs" href="#deployment" title="deployment"><span class="octicon octicon-link"></span></a>Deployment</h2><p>In order to deploy the tagger into your own kubernetes cluster, do the following:</p><ul>
<li>Deploy <a href="https://github.com/Yolean/kubernetes-kafka" target="_blank">Kafka</a> into your cluster</li>
<li><code>git clone https://github.com/SwaggerTagger/tagger-kubernetes && cd tagger-kubernetes</code></li>
<li><code>kubectl create -f .</code></li>
</ul></div>
<div class="ui-toc dropup unselectable hidden-print" style="display:none;">
<div class="pull-right dropdown">
<a id="tocLabel" class="ui-toc-label btn btn-default" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false" title="Table of content">
<i class="fa fa-bars"></i>
</a>
<ul id="ui-toc" class="ui-toc-dropdown dropdown-menu" aria-labelledby="tocLabel">
<div class="toc"><ul class="nav"><li><a href="#yolo-tagger">Yolo Tagger</a></li><li><a href="#frontend">Frontend</a><ul class="nav"><li><a href="#technologies">Technologies</a></li><li><a href="#deployment0">Deployment</a><ul class="nav"><li><a href="#nginx">Nginx</a></li><li><a href="#re-deploying-to-kubernetes">(re-)deploying to Kubernetes</a></li></ul></li><li><a href="#build-setup">Build Setup</a></li></ul></li><li><a href="#backend-application-server-for-yolo-tagger">Backend Application Server for Yolo-Tagger</a><ul class="nav"><li><a href="#notable-technologieslibraries-used">Notable technologies/libraries used</a></li><li><a href="#tools">Tools</a></li></ul></li><li><a href="#tagger-worker">Tagger Worker</a><ul class="nav"><li><a href="#configuration">Configuration</a></li><li><a href="#interface">Interface</a><ul class="nav"><li><a href="#incoming-messages">Incoming Messages</a></li><li><a href="#outgoing-messages">Outgoing Messages</a></li></ul></li><li><a href="#build-container">Build Container</a></li></ul></li><li class=""><a href="#our-modifications-to-darknet">Our Modifications to Darknet</a><ul class="nav"><li class=""><a href="#machine-readable-output">Machine Readable Output</a></li></ul></li><li class=""><a href="#kubernetes">Kubernetes</a><ul class="nav"><li class=""><a href="#overview">Overview</a></li><li class=""><a href="#deployment">Deployment</a></li></ul></li></ul></div><div class="toc-menu"><a class="expand-toggle" href="#">Expand all</a><a class="back-to-top" href="#">Back to top</a><a class="go-to-bottom" href="#">Go to bottom</a></div>
</ul>
</div>
</div>
<div id="ui-toc-affix" class="ui-affix-toc ui-toc-dropdown unselectable hidden-print" data-spy="affix" style="top:17px;display:none;" >
<div class="toc"><ul class="nav"><li><a href="#yolo-tagger">Yolo Tagger</a></li><li><a href="#frontend">Frontend</a><ul class="nav"><li><a href="#technologies">Technologies</a></li><li><a href="#deployment0">Deployment</a><ul class="nav"><li><a href="#nginx">Nginx</a></li><li><a href="#re-deploying-to-kubernetes">(re-)deploying to Kubernetes</a></li></ul></li><li><a href="#build-setup">Build Setup</a></li></ul></li><li><a href="#backend-application-server-for-yolo-tagger">Backend Application Server for Yolo-Tagger</a><ul class="nav"><li><a href="#notable-technologieslibraries-used">Notable technologies/libraries used</a></li><li><a href="#tools">Tools</a></li></ul></li><li class=""><a href="#tagger-worker">Tagger Worker</a><ul class="nav"><li><a href="#configuration">Configuration</a></li><li><a href="#interface">Interface</a><ul class="nav"><li><a href="#incoming-messages">Incoming Messages</a></li><li><a href="#outgoing-messages">Outgoing Messages</a></li></ul></li><li class=""><a href="#build-container">Build Container</a></li></ul></li><li class=""><a href="#our-modifications-to-darknet">Our Modifications to Darknet</a><ul class="nav"><li class=""><a href="#machine-readable-output">Machine Readable Output</a></li></ul></li><li class=""><a href="#kubernetes">Kubernetes</a><ul class="nav"><li class=""><a href="#overview">Overview</a></li><li class=""><a href="#deployment">Deployment</a></li></ul></li></ul></div><div class="toc-menu"><a class="expand-toggle" href="#">Expand all</a><a class="back-to-top" href="#">Back to top</a><a class="go-to-bottom" href="#">Go to bottom</a></div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha256-U5ZEeKfGNOja007MMD3YBI0A3OSZOQbeG6z2f2Y0hu8=" crossorigin="anonymous" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gist-embed/2.6.0/gist-embed.min.js" integrity="sha256-KyF2D6xPIJUW5sUDSs93vWyZm+1RzIpKCexxElmxl8g=" crossorigin="anonymous" defer></script>
<script>
var markdown = $(".markdown-body");
//smooth all hash trigger scrolling
function smoothHashScroll() {
var hashElements = $("a[href^='#']").toArray();
for (var i = 0; i < hashElements.length; i++) {
var element = hashElements[i];
var $element = $(element);
var hash = element.hash;
if (hash) {
$element.on('click', function (e) {
// store hash
var hash = this.hash;
if ($(hash).length <= 0) return;
// prevent default anchor click behavior
e.preventDefault();
// animate
$('body, html').stop(true, true).animate({
scrollTop: $(hash).offset().top
}, 100, "linear", function () {
// when done, add hash to url
// (default click behaviour)
window.location.hash = hash;
});
});
}
}
}
smoothHashScroll();
var toc = $('.ui-toc');
var tocAffix = $('.ui-affix-toc');
var tocDropdown = $('.ui-toc-dropdown');
//toc
tocDropdown.click(function (e) {
e.stopPropagation();
});
var enoughForAffixToc = true;
function generateScrollspy() {
$(document.body).scrollspy({
target: ''
});
$(document.body).scrollspy('refresh');
if (enoughForAffixToc) {
toc.hide();
tocAffix.show();
} else {
tocAffix.hide();
toc.show();
}
$(document.body).scroll();
}
function windowResize() {
//toc right
var paddingRight = parseFloat(markdown.css('padding-right'));
var right = ($(window).width() - (markdown.offset().left + markdown.outerWidth() - paddingRight));
toc.css('right', right + 'px');
//affix toc left
var newbool;
var rightMargin = (markdown.parent().outerWidth() - markdown.outerWidth()) / 2;
//for ipad or wider device
if (rightMargin >= 133) {
newbool = true;
var affixLeftMargin = (tocAffix.outerWidth() - tocAffix.width()) / 2;
var left = markdown.offset().left + markdown.outerWidth() - affixLeftMargin;
tocAffix.css('left', left + 'px');
} else {
newbool = false;
}
if (newbool != enoughForAffixToc) {
enoughForAffixToc = newbool;
generateScrollspy();
}
}
$(window).resize(function () {
windowResize();
});
$(document).ready(function () {
windowResize();
generateScrollspy();
});
//remove hash
function removeHash() {
window.location.hash = '';
}
var backtotop = $('.back-to-top');
var gotobottom = $('.go-to-bottom');
backtotop.click(function (e) {
e.preventDefault();
e.stopPropagation();
if (scrollToTop)
scrollToTop();
removeHash();
});
gotobottom.click(function (e) {
e.preventDefault();
e.stopPropagation();
if (scrollToBottom)
scrollToBottom();
removeHash();
});
var toggle = $('.expand-toggle');
var tocExpand = false;
checkExpandToggle();
toggle.click(function (e) {
e.preventDefault();
e.stopPropagation();
tocExpand = !tocExpand;
checkExpandToggle();
})
function checkExpandToggle () {
var toc = $('.ui-toc-dropdown .toc');
var toggle = $('.expand-toggle');
if (!tocExpand) {
toc.removeClass('expand');
toggle.text('Expand all');
} else {
toc.addClass('expand');
toggle.text('Collapse all');
}
}
function scrollToTop() {
$('body, html').stop(true, true).animate({
scrollTop: 0
}, 100, "linear");
}
function scrollToBottom() {
$('body, html').stop(true, true).animate({
scrollTop: $(document.body)[0].scrollHeight
}, 100, "linear");
}
</script>
</body>
</html>