├── dark.jpg ├── light.jpg ├── favicon.ico ├── .dockerignore ├── .gitignore ├── .gitattributes ├── web ├── views │ ├── article.html │ ├── errors │ │ ├── 404.html │ │ └── 500.html │ ├── home.html │ ├── categories.html │ └── layouts │ │ └── layout.html └── assets │ ├── fonts │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ └── fontawesome-webfont.woff2 │ ├── css │ ├── animation.css.map │ ├── style.css.map │ ├── jquery.simplyscroll.css │ ├── prism.css │ ├── style-dark.css.map │ ├── animation.css │ ├── style.css │ └── style-dark.css │ └── js │ ├── main.js │ ├── typography.js │ ├── jquery.appear.js │ ├── jquery.slimscroll.min.js │ ├── jquery-migrate-1.2.1.min.js │ ├── jquery.simplyscroll.min.js │ └── bootstrap.min.js ├── internal ├── types │ ├── githubstr.go │ ├── analyzer.go │ ├── gitalk.go │ └── GlobleDatas.go ├── utils │ ├── TimeTrack.go │ ├── helper.go │ └── explorer.go ├── api │ └── ErrorResponse.go ├── app │ ├── filebaseReader.go │ ├── githubReader.go │ └── app.go └── bindata │ └── views │ └── views.go ├── Dockerfile.Develop ├── Dockerfile ├── .vscode └── launch.json ├── config └── config.yml.tmp ├── makefile ├── go.mod ├── package.sh ├── main.go ├── README.md └── go.sum /dark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotuns/go-markdown-book/HEAD/dark.jpg -------------------------------------------------------------------------------- /light.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotuns/go-markdown-book/HEAD/light.jpg -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotuns/go-markdown-book/HEAD/favicon.ico -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | md 3 | package 4 | cache 5 | web 6 | config 7 | internal 8 | bin -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | md/ 3 | cache/ 4 | package/ 5 | build/ 6 | config/* 7 | !config/config.yml.tmp -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=go 2 | *.css linguist-language=go 3 | *.html linguist-language=go 4 | -------------------------------------------------------------------------------- /web/views/article.html: -------------------------------------------------------------------------------- 1 |
2 | {{.Article}} 3 |
4 | 5 |
6 | -------------------------------------------------------------------------------- /web/assets/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotuns/go-markdown-book/HEAD/web/assets/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /web/assets/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotuns/go-markdown-book/HEAD/web/assets/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /web/assets/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotuns/go-markdown-book/HEAD/web/assets/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /web/assets/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hotuns/go-markdown-book/HEAD/web/assets/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /internal/types/githubstr.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | type GithubStr struct { 4 | Repo string `json:"repo"` 5 | Owner string `json:"owner"` 6 | } 7 | -------------------------------------------------------------------------------- /Dockerfile.Develop: -------------------------------------------------------------------------------- 1 | FROM scratch as runner 2 | LABEL maintainer="hedongshu " 3 | COPY ./build/markdown-book-linux-amd64/markdown-book /data/app/ 4 | 5 | EXPOSE 5006 6 | 7 | ENTRYPOINT ["/data/app/markdown-book", "web"] 8 | -------------------------------------------------------------------------------- /internal/utils/TimeTrack.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "log" 5 | "time" 6 | ) 7 | 8 | func TimeTrack(start time.Time, name string) { 9 | elapsed := time.Since(start) 10 | log.Printf("%s took %s", name, elapsed) 11 | } 12 | -------------------------------------------------------------------------------- /internal/api/ErrorResponse.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/kataras/iris/v12" 4 | 5 | func NotFound(ctx iris.Context) { 6 | ctx.View("web/views/errors/404.html") 7 | } 8 | 9 | func InternalServerError(ctx iris.Context) { 10 | ctx.View("web/views/errors/500.html") 11 | } 12 | -------------------------------------------------------------------------------- /internal/types/analyzer.go: -------------------------------------------------------------------------------- 1 | // Generated by https://quicktype.io 2 | package types 3 | 4 | // 分析器 5 | type Analyzer struct { 6 | Baidu string `json:"baidu"` 7 | Google string `json:"google"` 8 | } 9 | 10 | func (a *Analyzer) SetAnalyzer(b string, g string) { 11 | a.Baidu = b 12 | a.Google = g 13 | } 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=$TARGETPLATFORM scratch as runner 2 | 3 | LABEL maintainer="hedongshu " 4 | 5 | ARG TARGETOS 6 | ARG TARGETARCH 7 | 8 | COPY ./build/markdown-book-${TARGETOS}-${TARGETARCH}/markdown-book /data/app/ 9 | 10 | EXPOSE 5006 11 | 12 | ENTRYPOINT ["/data/app/markdown-book", "web"] 13 | -------------------------------------------------------------------------------- /web/views/errors/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 404 Not Found 8 | 9 | 10 |

404 Not Found

11 | 12 | -------------------------------------------------------------------------------- /web/views/errors/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Oups something went wrong, try again 9 | 10 | 11 | 12 |

13 |
Oups something went wrong, try again
14 |

15 | 16 | 17 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "auto", 12 | "program": "${workspaceFolder}", 13 | "env": {}, 14 | "args": ["web", "--config", "./config/config.yml"] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /web/views/home.html: -------------------------------------------------------------------------------- 1 | {{range .Articles}} 2 |
3 |

{{.Title}}

4 | 11 |

{{.Preview}}

12 |
13 | {{end}} 14 | -------------------------------------------------------------------------------- /internal/types/gitalk.go: -------------------------------------------------------------------------------- 1 | // Generated by https://quicktype.io 2 | package types 3 | 4 | type Gitalk struct { 5 | ClientID string `json:"client_id"` 6 | ClientSecret string `json:"client_secret"` 7 | Repo string `json:"repo"` 8 | Owner string `json:"owner"` 9 | Id string `json:"id"` 10 | Admin []string `json:"admin"` 11 | Labels []string `json:"labels"` 12 | } 13 | 14 | func (g *Gitalk) SetGitalk(clientID string, clientSecret string, repo string, owner string, admin []string, labels []string) { 15 | g.ClientID = clientID 16 | g.ClientSecret = clientSecret 17 | g.Repo = repo 18 | g.Owner = owner 19 | g.Admin = admin 20 | g.Labels = labels 21 | } 22 | -------------------------------------------------------------------------------- /config/config.yml.tmp: -------------------------------------------------------------------------------- 1 | title: "My Blog" 2 | dir: "./md" 3 | port: 5006 4 | env: dev 5 | cache: 3 6 | 7 | gitalk: 8 | client-id: "Your github oauth app client-id, required fields. eg: ad549a9d085d7f5736d3" 9 | client-secret: "Your github oauth app client-secret, required fields. eg: 510d1a6bb875fd5031f0d613cd606b1d" 10 | repo: "Github repo for gitalk, required fields. eg: blog-issue" 11 | owner: "Your github account, required fields." 12 | admin: 13 | - "Your github account" 14 | labels: 15 | - "Custom issue labels, eg: gitalk" 16 | 17 | analyzer-baidu: "Your Baidu analyzer code" 18 | analyzer-google: "Your Google analyzer code" 19 | 20 | ignore-file: 21 | - "demo.md" 22 | ignore-path: 23 | - "demo" -------------------------------------------------------------------------------- /internal/types/GlobleDatas.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // Article 文章 4 | type Article struct { 5 | // 文章标题 6 | Title string `json:"title"` 7 | // 分类 8 | Category string `json:"category"` 9 | // 发布时间 10 | PublishTime string `json:"publishTime"` 11 | // 文章预览, 截取文章前100个字符 12 | Preview string `json:"preview"` 13 | // 链接 14 | Link string `json:"link"` 15 | } 16 | 17 | type TreeArticle struct { 18 | CategorieName string `json:"categorieName"` 19 | List []Article `json:"list"` 20 | } 21 | 22 | type GlobleData struct { 23 | Categories []string `json:"categories"` // 一级目录,即分类 24 | Articles []Article `json:"articles"` // 扁平化文章列表 25 | TreeArticles []TreeArticle `json:"tree_articles"` // 树结构文章列表 26 | } 27 | -------------------------------------------------------------------------------- /web/assets/css/animation.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sourceRoot":"","sources":["../scss/animation.scss"],"names":[],"mappings":"AAAA;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAKJ;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA","file":"animation.css"} -------------------------------------------------------------------------------- /web/views/categories.html: -------------------------------------------------------------------------------- 1 | {{if .ShowAll}} 2 |
3 |

分类

4 |
    5 | {{range .TreeArticles}} 6 |
  • 7 |
    8 |

    {{.CategorieName}} 9 |

    10 | {{getArticlesLen .List}} 篇 11 |
    12 |
  • 13 | {{end}} 14 |
15 |
16 | {{else}} 17 |
18 |

分类 {{.Categorie}}

19 |
    20 | {{range .theList}} 21 |
  • 22 |
    23 |

    {{.Title}}

    24 |
    25 | {{.PublishTime}} 26 |
    27 |
    28 |
  • 29 | {{end}} 30 |
31 |
32 | {{end}} 33 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | GO111MODULE=on 2 | 3 | .PHONY: build 4 | build: bindata markdown-book 5 | 6 | .PHONY: docker-push 7 | docker-push: package-all 8 | docker buildx build --platform linux/arm64,linux/amd64 -t hedongshu/markdown-book:latest . --push 9 | 10 | .PHONY: docker-build 11 | docker-build: package-all 12 | docker build -t hedongshu/markdown-book:dev -f ./Dockerfile.Develop . 13 | 14 | .PHONY: bindata 15 | bindata: 16 | go install github.com/go-bindata/go-bindata/v3/go-bindata@latest 17 | go generate ./... 18 | 19 | .PHONY: markdown-book 20 | markdown-book: 21 | go build $(RACE) -o bin/markdown-book ./ 22 | 23 | .PHONY: build-race 24 | build-race: enable-race build 25 | 26 | .PHONY: run 27 | run: build 28 | ./bin/markdown-book web --config ./config/config.yml 29 | 30 | .PHONY: run-race 31 | run-race: enable-race run 32 | 33 | .PHONY: test 34 | test: 35 | go test $(RACE) ./... 36 | 37 | .PHONY: test-race 38 | test-race: enable-race test 39 | 40 | .PHONY: enable-race 41 | enable-race: 42 | $(eval RACE = -race) 43 | 44 | .PHONY: package 45 | package: build 46 | bash ./package.sh 47 | 48 | .PHONY: package-all 49 | package-all: build 50 | bash ./package.sh -p 'linux darwin windows' -a 'amd64 arm64' 51 | 52 | .PHONY: clean 53 | clean: 54 | rm ./bin/markdown-book 55 | -------------------------------------------------------------------------------- /web/assets/js/main.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function () { 2 | // theme切换 3 | var KEY_THEME_STATE = "blog_theme_state"; 4 | var $themeAction = $(".js-theme-action"); 5 | var $themeCss = document.getElementById("theme-css"); 6 | 7 | changeTheme(true); 8 | 9 | $themeAction.on("click", function (event) { 10 | event.stopPropagation(); 11 | changeTheme(false); 12 | }); 13 | 14 | function changeTheme(isInit = false) { 15 | color = isInit 16 | ? getThemeState().color 17 | : getThemeState().color == "dark" 18 | ? "white" 19 | : "dark"; 20 | 21 | setThemeState(color); 22 | saveThemeState(color); 23 | } 24 | 25 | function getThemeState() { 26 | var themeState = JSON.parse(sessionStorage.getItem(KEY_THEME_STATE)) || {}; 27 | themeState.color !== undefined || (themeState.color = "dark"); 28 | return themeState; 29 | } 30 | 31 | function saveThemeState(color) { 32 | sessionStorage.setItem( 33 | KEY_THEME_STATE, 34 | JSON.stringify({ 35 | color: color, 36 | }) 37 | ); 38 | } 39 | 40 | function setThemeState(color) { 41 | if (color == "dark") { 42 | $(document.documentElement).addClass("dark"); 43 | $themeAction.html(''); 44 | $themeCss.href = "/static/css/style-dark.css"; 45 | } else { 46 | $(document.documentElement).removeClass("dark"); 47 | $themeAction.html(''); 48 | $themeCss.href = "/static/css/style.css"; 49 | } 50 | } 51 | }); 52 | -------------------------------------------------------------------------------- /web/assets/js/typography.js: -------------------------------------------------------------------------------- 1 | var stage; 2 | var siteNavShown = true; 3 | 4 | function triggerSiteNav() { 5 | return; 6 | if (siteNavShown) { 7 | $('#site-nav').hide(300); 8 | siteNavShown = false; 9 | } else { 10 | $('#site-nav').show(300); 11 | siteNavShown = true; 12 | } 13 | } 14 | function updateSidebar() { 15 | if (window.innerWidth <= 768 || window.innerHeight <= 600) { 16 | $('#side-bar').innerWidth($('#stage').width()); 17 | $('#main-container').removeClass('col-sm-9'); 18 | //$('#site-nav').hide(); 19 | //siteNavShown = false; 20 | } else { 21 | //$('#site-nav').show(); 22 | //siteNavShown = true; 23 | var sidebarW = 24 | stage.width() - $('#main-container').outerWidth() + (window.innerWidth - stage.innerWidth()) / 2; 25 | $('#side-bar').outerWidth(sidebarW); 26 | console.log("sidebarW=" + sidebarW); 27 | $('#main-container').addClass('col-sm-9'); 28 | } 29 | } 30 | $(document).ready(function () { 31 | stage = $('#stage'); 32 | $(window).resize(function () { 33 | updateSidebar(); 34 | }); 35 | updateSidebar(); 36 | $('#main-container').removeClass('invisible'); 37 | $('#main-container').addClass('fadeInTop'); 38 | if (window.innerWidth <= 768) { 39 | $('#side-bar').removeClass('invisible'); 40 | $('#side-bar').addClass('fadeInTop'); 41 | }else{ 42 | $('#side-bar').removeClass('invisible'); 43 | $('#side-bar').addClass('fadeInRight'); 44 | } 45 | $('.site-title').click(function () { 46 | $('.site-title a')[0].click(); 47 | }) 48 | }); -------------------------------------------------------------------------------- /internal/utils/helper.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bytes" 5 | "crypto/md5" 6 | "crypto/sha1" 7 | "encoding/hex" 8 | "html/template" 9 | "regexp" 10 | "runtime" 11 | "strings" 12 | 13 | "github.com/microcosm-cc/bluemonday" 14 | "github.com/russross/blackfriday/v2" 15 | ) 16 | 17 | // FormatAppVersion 格式化应用版本信息 18 | func FormatAppVersion(appVersion, GitCommit, BuildDate string) (string, error) { 19 | content := ` 20 | Version: {{.Version}} 21 | Go Version: {{.GoVersion}} 22 | Git Commit: {{.GitCommit}} 23 | Built: {{.BuildDate}} 24 | OS/ARCH: {{.GOOS}}/{{.GOARCH}} 25 | ` 26 | tpl, err := template.New("version").Parse(content) 27 | if err != nil { 28 | return "", err 29 | } 30 | var buf bytes.Buffer 31 | err = tpl.Execute(&buf, map[string]string{ 32 | "Version": appVersion, 33 | "GoVersion": runtime.Version(), 34 | "GitCommit": GitCommit, 35 | "BuildDate": BuildDate, 36 | "GOOS": runtime.GOOS, 37 | "GOARCH": runtime.GOARCH, 38 | }) 39 | if err != nil { 40 | return "", err 41 | } 42 | 43 | return buf.String(), err 44 | } 45 | 46 | func MD5(s string) string { 47 | sum := md5.Sum([]byte(s)) 48 | return hex.EncodeToString(sum[:]) 49 | } 50 | 51 | func Sha1(s string) string { 52 | sum := sha1.Sum([]byte(s)) 53 | return hex.EncodeToString(sum[:]) 54 | } 55 | 56 | // IsInSlice 判断目标字符串是否是在切片中 57 | func IsInSlice(slice []string, s string) bool { 58 | if len(slice) == 0 { 59 | return false 60 | } 61 | 62 | isIn := false 63 | for _, f := range slice { 64 | if f == s { 65 | isIn = true 66 | break 67 | } 68 | } 69 | 70 | return isIn 71 | } 72 | 73 | func MdToHtml(content []byte, TocPrefix string) template.HTML { 74 | strs := string(content) 75 | 76 | var htmlFlags blackfriday.HTMLFlags 77 | 78 | if strings.HasPrefix(strs, TocPrefix) { 79 | htmlFlags |= blackfriday.TOC 80 | strs = strings.Replace(strs, TocPrefix, "

", 1) 81 | } 82 | 83 | renderer := blackfriday.NewHTMLRenderer(blackfriday.HTMLRendererParameters{ 84 | Flags: htmlFlags, 85 | }) 86 | 87 | unsafe := blackfriday.Run([]byte(strs), blackfriday.WithRenderer(renderer), blackfriday.WithExtensions(blackfriday.CommonExtensions)) 88 | html := bluemonday.UGCPolicy().AllowAttrs("class").Matching(regexp.MustCompile("^language-[a-zA-Z0-9]+$")).OnElements("code").SanitizeBytes(unsafe) 89 | 90 | return template.HTML(string(html)) 91 | } 92 | -------------------------------------------------------------------------------- /web/assets/js/jquery.appear.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery appear plugin 3 | * 4 | * Copyright (c) 2012 Andrey Sidorov 5 | * licensed under MIT license. 6 | * 7 | * https://github.com/morr/jquery.appear/ 8 | * 9 | * Version: 0.3.3 10 | */ 11 | (function($) { 12 | var selectors = []; 13 | 14 | var check_binded = false; 15 | var check_lock = false; 16 | var defaults = { 17 | interval: 250, 18 | force_process: false 19 | } 20 | var $window = $(window); 21 | 22 | var $prior_appeared; 23 | 24 | function process() { 25 | check_lock = false; 26 | for (var index = 0; index < selectors.length; index++) { 27 | var $appeared = $(selectors[index]).filter(function() { 28 | return $(this).is(':appeared'); 29 | }); 30 | 31 | $appeared.trigger('appear', [$appeared]); 32 | 33 | if ($prior_appeared) { 34 | var $disappeared = $prior_appeared.not($appeared); 35 | $disappeared.trigger('disappear', [$disappeared]); 36 | } 37 | $prior_appeared = $appeared; 38 | } 39 | } 40 | 41 | // "appeared" custom filter 42 | $.expr[':']['appeared'] = function(element) { 43 | var $element = $(element); 44 | if (!$element.is(':visible')) { 45 | return false; 46 | } 47 | 48 | var window_left = $window.scrollLeft(); 49 | var window_top = $window.scrollTop(); 50 | var offset = $element.offset(); 51 | var left = offset.left; 52 | var top = offset.top; 53 | 54 | if (top + $element.height() >= window_top && 55 | top - ($element.data('appear-top-offset') || 0) <= window_top + $window.height() && 56 | left + $element.width() >= window_left && 57 | left - ($element.data('appear-left-offset') || 0) <= window_left + $window.width()) { 58 | return true; 59 | } else { 60 | return false; 61 | } 62 | } 63 | 64 | $.fn.extend({ 65 | // watching for element's appearance in browser viewport 66 | appear: function(options) { 67 | var opts = $.extend({}, defaults, options || {}); 68 | var selector = this.selector || this; 69 | if (!check_binded) { 70 | var on_check = function() { 71 | if (check_lock) { 72 | return; 73 | } 74 | check_lock = true; 75 | 76 | setTimeout(process, opts.interval); 77 | }; 78 | 79 | $(window).scroll(on_check).resize(on_check); 80 | check_binded = true; 81 | } 82 | 83 | if (opts.force_process) { 84 | setTimeout(process, opts.interval); 85 | } 86 | selectors.push(selector); 87 | return $(selector); 88 | } 89 | }); 90 | 91 | $.extend({ 92 | // force elements's appearance check 93 | force_appear: function() { 94 | if (check_binded) { 95 | process(); 96 | return true; 97 | }; 98 | return false; 99 | } 100 | }); 101 | })(jQuery); -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/hedongshu/go-md-book 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/PuerkitoBio/goquery v1.8.0 7 | github.com/google/go-github v17.0.0+incompatible 8 | github.com/kataras/iris/v12 v12.2.0-beta2 9 | github.com/microcosm-cc/bluemonday v1.0.21 10 | github.com/russross/blackfriday/v2 v2.1.0 11 | github.com/urfave/cli/v2 v2.23.7 12 | ) 13 | 14 | require ( 15 | github.com/BurntSushi/toml v1.2.1 // indirect 16 | github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect 17 | github.com/CloudyKit/jet/v6 v6.1.0 // indirect 18 | github.com/Shopify/goreferrer v0.0.0-20210630161223-536fa16abd6f // indirect 19 | github.com/andybalholm/brotli v1.0.4 // indirect 20 | github.com/andybalholm/cascadia v1.3.1 // indirect 21 | github.com/aymerick/douceur v0.2.0 // indirect 22 | github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect 23 | github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 // indirect 24 | github.com/fatih/structs v1.1.0 // indirect 25 | github.com/flosch/pongo2/v4 v4.0.2 // indirect 26 | github.com/goccy/go-json v0.9.8-0.20220506185958-23bd66f4c0d5 // indirect 27 | github.com/golang/snappy v0.0.4 // indirect 28 | github.com/google/go-cmp v0.5.9 // indirect 29 | github.com/google/go-querystring v1.1.0 // indirect 30 | github.com/google/uuid v1.3.0 // indirect 31 | github.com/gorilla/css v1.0.0 // indirect 32 | github.com/iris-contrib/jade v1.1.4 // indirect 33 | github.com/iris-contrib/schema v0.0.6 // indirect 34 | github.com/josharian/intern v1.0.0 // indirect 35 | github.com/json-iterator/go v1.1.12 // indirect 36 | github.com/kataras/blocks v0.0.5 // indirect 37 | github.com/kataras/golog v0.1.7 // indirect 38 | github.com/kataras/pio v0.0.10 // indirect 39 | github.com/kataras/sitemap v0.0.5 // indirect 40 | github.com/kataras/tunnel v0.0.3 // indirect 41 | github.com/klauspost/compress v1.15.3 // indirect 42 | github.com/mailgun/raymond/v2 v2.0.46 // indirect 43 | github.com/mailru/easyjson v0.7.7 // indirect 44 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect 45 | github.com/modern-go/reflect2 v1.0.2 // indirect 46 | github.com/schollz/closestmatch v2.1.0+incompatible // indirect 47 | github.com/sirupsen/logrus v1.8.1 // indirect 48 | github.com/tdewolff/minify/v2 v2.11.2 // indirect 49 | github.com/tdewolff/parse/v2 v2.5.29 // indirect 50 | github.com/valyala/bytebufferpool v1.0.0 // indirect 51 | github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect 52 | github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect 53 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect 54 | github.com/yosssi/ace v0.0.5 // indirect 55 | golang.org/x/crypto v0.5.0 // indirect 56 | golang.org/x/net v0.5.0 // indirect 57 | golang.org/x/sys v0.4.0 // indirect 58 | golang.org/x/text v0.6.0 // indirect 59 | golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect 60 | google.golang.org/protobuf v1.28.0 // indirect 61 | gopkg.in/ini.v1 v1.66.4 // indirect 62 | gopkg.in/yaml.v3 v3.0.1 // indirect 63 | ) 64 | -------------------------------------------------------------------------------- /internal/app/filebaseReader.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "time" 7 | 8 | "github.com/hedongshu/go-md-book/internal/types" 9 | "github.com/hedongshu/go-md-book/internal/utils" 10 | "github.com/kataras/iris/v12" 11 | ) 12 | 13 | func HomeHandler(ctx iris.Context) { 14 | // 记录耗时 15 | defer utils.TimeTrack(time.Now(), "homeHandler") 16 | 17 | if err := ctx.View("home.html"); err != nil { 18 | log.Println(err) 19 | } 20 | } 21 | 22 | func CategoriesHandler(ctx iris.Context) { 23 | // 记录耗时 24 | defer utils.TimeTrack(time.Now(), "categoriesHandler") 25 | 26 | categorie := ctx.Params().Get("f") 27 | showAll := false 28 | 29 | if categorie == "" { 30 | showAll = true 31 | } else { 32 | 33 | for _, item := range GlobleDatas.TreeArticles { 34 | if item.CategorieName == categorie { 35 | ctx.ViewData("theList", item.List) 36 | } 37 | } 38 | } 39 | 40 | ctx.ViewData("ShowAll", showAll) 41 | ctx.ViewData("Categorie", categorie) 42 | 43 | ctx.View("categories.html") 44 | } 45 | 46 | func ArticleHandler(ctx iris.Context) { 47 | // 记录耗时 48 | defer utils.TimeTrack(time.Now(), "articleHandler") 49 | 50 | f := ctx.Params().Get("f") 51 | 52 | // 设置 Gitalk ID 53 | Gitalk.Id = utils.MD5(f) 54 | ctx.ViewData("Gitalk", Gitalk) 55 | ctx.ViewData("ActiveNav", f) 56 | 57 | if utils.IsInSlice(IgnoreFile, f) { 58 | return 59 | } 60 | 61 | mdfile := MdDir + "/" + f + ".md" 62 | 63 | _, err := os.Stat(mdfile) 64 | if err != nil { 65 | ctx.StatusCode(404) 66 | ctx.Application().Logger().Errorf("Not Found '%s', Path is %s", mdfile, ctx.Path()) 67 | return 68 | } 69 | 70 | bytes, err := os.ReadFile(mdfile) 71 | if err != nil { 72 | ctx.StatusCode(500) 73 | ctx.Application().Logger().Errorf("ReadFile Error '%s', Path is %s", mdfile, ctx.Path()) 74 | return 75 | } 76 | 77 | ctx.ViewData("Article", utils.MdToHtml(bytes, TocPrefix)) 78 | 79 | ctx.View("article.html") 80 | } 81 | 82 | func GetAllMarkDownsFromFile() { 83 | var option utils.Option 84 | option.RootPath = []string{MdDir} 85 | option.SubFlag = true 86 | option.IgnorePath = IgnorePath 87 | option.IgnoreFile = IgnoreFile 88 | tree, _ := utils.Explorer(option) 89 | 90 | TreeArticles := make([]types.TreeArticle, 0) 91 | Categories := make([]string, 0) 92 | Articles := make([]types.Article, 0) 93 | 94 | for _, v := range tree.Children { 95 | for _, item := range v.Children { 96 | if item.IsDir { 97 | Categories = append(Categories, item.Name) 98 | } 99 | 100 | theList := getArticles(item) 101 | Articles = append(Articles, theList...) 102 | 103 | TreeArticles = append(TreeArticles, types.TreeArticle{ 104 | CategorieName: item.Name, 105 | List: theList, 106 | }) 107 | } 108 | } 109 | 110 | GlobleDatas.Articles = Articles 111 | GlobleDatas.Categories = Categories 112 | GlobleDatas.TreeArticles = TreeArticles 113 | } 114 | 115 | func getArticles(node *utils.Node) []types.Article { 116 | list := make([]types.Article, 0) 117 | 118 | if !node.IsDir { 119 | info := utils.GetArticleInfo(*node) 120 | list = append(list, info) 121 | } 122 | 123 | if len(node.Children) > 0 { 124 | for _, v := range node.Children { 125 | newinfo := getArticles(v) 126 | list = append(list, newinfo...) 127 | } 128 | } 129 | 130 | return list 131 | } 132 | -------------------------------------------------------------------------------- /web/assets/css/style.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sourceRoot":"","sources":["../scss/style.scss","../scss/animation.scss"],"names":[],"mappings":"AASQ;ACTR;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAKJ;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;ADtPF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAUE,aApBS;EAqBT,OAtBgB;;;AAyBlB;EACE,YA3BgB;;;AA8BlB;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EAGE;EAKF;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;;;AAGF;EACE;;AAEA;EACE;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAIJ;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA,aA1LY;;;AA8LhB;EACE;EACA,aA9L+B;;;AAiMjC;EACE;EACA,aAlMiC;;;AAqMnC;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE,OAtPgB;EAuPhB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;;AAGF;EACE;EACA,kBAvQc;EAwQd;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;;AAEA;EAEE;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EAEE;EACA;EACA;EACA;;;AAIJ;EACE;EACA;;AAEA;EACE;;AAEA;EAII;EAGF;EACA;EACA;;AAGF;EAII;;;AAMR;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;AACA;EACE;;AACA;EACE;;;AAMN;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIA;EACE;EACA;;;AAKF;EACE;EACA;EACA;EACA;EACA;;AAEF;EACE;;AACA;EACE;EACA;EACA;EACA;;;AAMN;AAAA;AAAA;AAAA;AAAA;AAAA;EAME;;;AAGF;EACE;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;;EAGF;IACE;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;;EAGF;IACE;IACA;;EAGF;IACE;;EAGF;IACE;IACA;IACA;;EAGF;IACE;IACA;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;;AAIJ;EACE;IACE;;EAGF;IACE;;EAGF;IACE;IACA;;EAGF;IACE;;EAGF;IACE;IACA;;EAGF;IACE;IACA;;;AAIJ;EACE;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;;EAGF;IACE;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;;EAGF;IACE;IACA;;EAGF;IACE;;EAGF;IACE;IACA;IACA;;EAGF;IACE;IACA;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE","file":"style.css"} -------------------------------------------------------------------------------- /web/assets/css/jquery.simplyscroll.css: -------------------------------------------------------------------------------- 1 | /* Container DIV - automatically generated */ 2 | .simply-scroll-container { 3 | position: relative; 4 | } 5 | 6 | /* Clip DIV - automatically generated */ 7 | .simply-scroll-clip { 8 | position: relative; 9 | overflow: hidden; 10 | } 11 | 12 | /* UL/OL/DIV - the element that simplyScroll is inited on 13 | Class name automatically added to element */ 14 | .simply-scroll-list { 15 | overflow: hidden; 16 | margin: 0; 17 | padding: 0; 18 | list-style: none; 19 | } 20 | 21 | .simply-scroll-list li { 22 | padding: 0; 23 | margin: 0; 24 | list-style: none; 25 | } 26 | 27 | .simply-scroll-list li img { 28 | border: none; 29 | display: block; 30 | } 31 | 32 | /* Custom class modifications - adds to / overrides above 33 | 34 | .simply-scroll is default base class */ 35 | 36 | /* Container DIV */ 37 | .simply-scroll { 38 | width: 576px; 39 | height: 200px; 40 | margin-bottom: 1em; 41 | } 42 | 43 | /* Clip DIV */ 44 | .simply-scroll .simply-scroll-clip { 45 | width: 100%; 46 | height: 200px; 47 | } 48 | 49 | /* Explicitly set height/width of each list item */ 50 | .simply-scroll .simply-scroll-list li { 51 | float: left; /* Horizontal scroll only */ 52 | width: 100px; 53 | height: 100px; 54 | } 55 | /*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImpxdWVyeS5zaW1wbHlzY3JvbGwuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLDRDQUFBO0FBQ0E7SUFDSSxrQkFBQTtBQUNKOztBQUVBLHVDQUFBO0FBQ0E7SUFDSSxrQkFBQTtJQUNBLGdCQUFBO0FBQ0o7O0FBRUE7MkNBQzJDO0FBQzNDO0lBQ0ksZ0JBQUE7SUFDQSxTQUFBO0lBQ0EsVUFBQTtJQUNBLGdCQUFBO0FBQ0o7O0FBRUE7SUFDSSxVQUFBO0lBQ0EsU0FBQTtJQUNBLGdCQUFBO0FBQ0o7O0FBRUE7SUFDSSxZQUFBO0lBQ0EsY0FBQTtBQUNKOztBQUVBOztzQ0FFc0M7O0FBRXRDLGtCQUFBO0FBQ0E7SUFDSSxZQUFBO0lBQ0EsYUFBQTtJQUNBLGtCQUFBO0FBQ0o7O0FBRUEsYUFBQTtBQUNBO0lBQ0ksV0FBQTtJQUNBLGFBQUE7QUFDSjs7QUFFQSxrREFBQTtBQUNBO0lBQ0ksV0FBQSxFQUFhLDJCQUFBO0lBQ2IsWUFBQTtJQUNBLGFBQUE7QUFDSiIsImZpbGUiOiJqcXVlcnkuc2ltcGx5c2Nyb2xsLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIi8qIENvbnRhaW5lciBESVYgLSBhdXRvbWF0aWNhbGx5IGdlbmVyYXRlZCAqL1xuLnNpbXBseS1zY3JvbGwtY29udGFpbmVyIHtcbiAgICBwb3NpdGlvbjogcmVsYXRpdmU7XG59XG5cbi8qIENsaXAgRElWIC0gYXV0b21hdGljYWxseSBnZW5lcmF0ZWQgKi9cbi5zaW1wbHktc2Nyb2xsLWNsaXAge1xuICAgIHBvc2l0aW9uOiByZWxhdGl2ZTtcbiAgICBvdmVyZmxvdzogaGlkZGVuO1xufVxuXG4vKiBVTC9PTC9ESVYgLSB0aGUgZWxlbWVudCB0aGF0IHNpbXBseVNjcm9sbCBpcyBpbml0ZWQgb25cbkNsYXNzIG5hbWUgYXV0b21hdGljYWxseSBhZGRlZCB0byBlbGVtZW50ICovXG4uc2ltcGx5LXNjcm9sbC1saXN0IHtcbiAgICBvdmVyZmxvdzogaGlkZGVuO1xuICAgIG1hcmdpbjogMDtcbiAgICBwYWRkaW5nOiAwO1xuICAgIGxpc3Qtc3R5bGU6IG5vbmU7XG59XG5cbi5zaW1wbHktc2Nyb2xsLWxpc3QgbGkge1xuICAgIHBhZGRpbmc6IDA7XG4gICAgbWFyZ2luOiAwO1xuICAgIGxpc3Qtc3R5bGU6IG5vbmU7XG59XG5cbi5zaW1wbHktc2Nyb2xsLWxpc3QgbGkgaW1nIHtcbiAgICBib3JkZXI6IG5vbmU7XG4gICAgZGlzcGxheTogYmxvY2s7XG59XG5cbi8qIEN1c3RvbSBjbGFzcyBtb2RpZmljYXRpb25zIC0gYWRkcyB0byAvIG92ZXJyaWRlcyBhYm92ZVxuXG4uc2ltcGx5LXNjcm9sbCBpcyBkZWZhdWx0IGJhc2UgY2xhc3MgKi9cblxuLyogQ29udGFpbmVyIERJViAqL1xuLnNpbXBseS1zY3JvbGwge1xuICAgIHdpZHRoOiA1NzZweDtcbiAgICBoZWlnaHQ6IDIwMHB4O1xuICAgIG1hcmdpbi1ib3R0b206IDFlbTtcbn1cblxuLyogQ2xpcCBESVYgKi9cbi5zaW1wbHktc2Nyb2xsIC5zaW1wbHktc2Nyb2xsLWNsaXAge1xuICAgIHdpZHRoOiAxMDAlO1xuICAgIGhlaWdodDogMjAwcHg7XG59XG5cbi8qIEV4cGxpY2l0bHkgc2V0IGhlaWdodC93aWR0aCBvZiBlYWNoIGxpc3QgaXRlbSAqL1xuLnNpbXBseS1zY3JvbGwgLnNpbXBseS1zY3JvbGwtbGlzdCBsaSB7XG4gICAgZmxvYXQ6IGxlZnQ7IC8qIEhvcml6b250YWwgc2Nyb2xsIG9ubHkgKi9cbiAgICB3aWR0aDogMTAwcHg7XG4gICAgaGVpZ2h0OiAxMDBweDtcbn0iXX0= */ -------------------------------------------------------------------------------- /web/assets/css/prism.css: -------------------------------------------------------------------------------- 1 | /* PrismJS 1.29.0 2 | https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript+abap+abnf+actionscript+ada+agda+al+antlr4+apacheconf+apex+apl+applescript+aql+arduino+arff+armasm+arturo+asciidoc+aspnet+asm6502+asmatmel+autohotkey+autoit+avisynth+avro-idl+awk+bash+basic+batch+bbcode+bbj+bicep+birb+bison+bnf+bqn+brainfuck+brightscript+bro+bsl+c+csharp+cpp+cfscript+chaiscript+cil+cilkc+cilkcpp+clojure+cmake+cobol+coffeescript+concurnas+csp+cooklang+coq+crystal+css-extras+csv+cue+cypher+d+dart+dataweave+dax+dhall+diff+django+dns-zone-file+docker+dot+ebnf+editorconfig+eiffel+ejs+elixir+elm+etlua+erb+erlang+excel-formula+fsharp+factor+false+firestore-security-rules+flow+fortran+ftl+gml+gap+gcode+gdscript+gedcom+gettext+gherkin+git+glsl+gn+linker-script+go+go-module+gradle+graphql+groovy+haml+handlebars+haskell+haxe+hcl+hlsl+hoon+http+hpkp+hsts+ichigojam+icon+icu-message-format+idris+ignore+inform7+ini+io+j+java+javadoc+javadoclike+javastacktrace+jexl+jolie+jq+jsdoc+js-extras+json+json5+jsonp+jsstacktrace+js-templates+julia+keepalived+keyman+kotlin+kumir+kusto+latex+latte+less+lilypond+liquid+lisp+livescript+llvm+log+lolcode+lua+magma+makefile+markdown+markup-templating+mata+matlab+maxscript+mel+mermaid+metafont+mizar+mongodb+monkey+moonscript+n1ql+n4js+nand2tetris-hdl+naniscript+nasm+neon+nevod+nginx+nim+nix+nsis+objectivec+ocaml+odin+opencl+openqasm+oz+parigp+parser+pascal+pascaligo+psl+pcaxis+peoplecode+perl+php+phpdoc+php-extras+plant-uml+plsql+powerquery+powershell+processing+prolog+promql+properties+protobuf+pug+puppet+pure+purebasic+purescript+python+qsharp+q+qml+qore+r+racket+cshtml+jsx+tsx+reason+regex+rego+renpy+rescript+rest+rip+roboconf+robotframework+ruby+rust+sas+sass+scss+scala+scheme+shell-session+smali+smalltalk+smarty+sml+solidity+solution-file+soy+sparql+splunk-spl+sqf+sql+squirrel+stan+stata+iecst+stylus+supercollider+swift+systemd+t4-templating+t4-cs+t4-vb+tap+tcl+tt2+textile+toml+tremor+turtle+twig+typescript+typoscript+unrealscript+uorazor+uri+v+vala+vbnet+velocity+verilog+vhdl+vim+visual-basic+warpscript+wasm+web-idl+wgsl+wiki+wolfram+wren+xeora+xml-doc+xojo+xquery+yaml+yang+zig&plugins=autoloader */ 3 | code[class*=language-],pre[class*=language-]{color:#ccc;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green} 4 | -------------------------------------------------------------------------------- /web/assets/css/style-dark.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sourceRoot":"","sources":["../scss/style.scss","../scss/animation.scss","../scss/style-dark.scss"],"names":[],"mappings":"AASQ;ACTR;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAIJ;AACA;EACE;IACE;IACA;;EAEF;IACE;IACA;;;AAKJ;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;AACA;EACE;IACE;;EAEF;IACE;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;ADtPF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAUE,aApBS;EAqBT,OEtBgB;;;AFyBlB;EACE,YE3BgB;;;AF8BlB;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EAKE;EAGF;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;;;AAGF;EACE;;AAEA;EACE;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAIJ;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA,aA1LY;;;AA8LhB;EACE;EACA,aA9L+B;;;AAiMjC;EACE;EACA,aAlMiC;;;AAqMnC;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE,OEtPgB;EFuPhB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;;AAGF;EACE;EACA,kBEvQc;EFwQd;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;;AAEA;EAEE;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EAEE;EACA;EACA;EACA;;;AAIJ;EACE;EACA;;AAEA;EACE;;AAEA;EAEI;EAKF;EACA;EACA;;AAGF;EAEI;;;AAQR;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;AACA;EACE;;AACA;EACE;;;AAMN;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIA;EACE;EACA;;;AAKF;EACE;EACA;EACA;EACA;EACA;;AAEF;EACE;;AACA;EACE;EACA;EACA;EACA;;;AAMN;AAAA;AAAA;AAAA;AAAA;AAAA;EAME;;;AAGF;EACE;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;;EAGF;IACE;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;;EAGF;IACE;IACA;;EAGF;IACE;;EAGF;IACE;IACA;IACA;;EAGF;IACE;IACA;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;;AAIJ;EACE;IACE;;EAGF;IACE;;EAGF;IACE;IACA;;EAGF;IACE;;EAGF;IACE;IACA;;EAGF;IACE;IACA;;;AAIJ;EACE;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;;EAGF;IACE;IACA;;EAGF;IACE;IACA;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;IACA;;EAGF;IACE;IACA;IACA;;EAGF;IACE;IACA;;EAGF;IACE;;EAGF;IACE;IACA;IACA;;EAGF;IACE;IACA;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE","file":"style-dark.css"} -------------------------------------------------------------------------------- /internal/utils/explorer.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bytes" 5 | "log" 6 | "os" 7 | "path" 8 | "regexp" 9 | "strings" 10 | "time" 11 | 12 | "github.com/PuerkitoBio/goquery" 13 | "github.com/hedongshu/go-md-book/internal/types" 14 | "github.com/microcosm-cc/bluemonday" 15 | "github.com/russross/blackfriday/v2" 16 | ) 17 | 18 | // Node 树节点 19 | type Node struct { 20 | Name string `json:"name"` // 目录或文件名 21 | ShowName string `json:"showName"` // 目录或文件名(不包含后缀) 22 | Path string `json:"path"` // 目录或文件完整路径 23 | Link string `json:"link"` // 文件访问URI 24 | Active string `json:"active"` // 当前活跃的文件 25 | Children []*Node `json:"children"` // 目录下的文件或子目录 26 | IsDir bool `json:"isDir"` // 是否为目录 true: 是目录 false: 不是目录 27 | ModTime int64 `json:"modTime"` // 更新时间 28 | } 29 | 30 | // Option 遍历选项 31 | type Option struct { 32 | RootPath []string `yaml:"rootPath"` // 目标根目录 33 | SubFlag bool `yaml:"subFlag"` // 遍历子目录标志 true: 遍历 false: 不遍历 34 | IgnorePath []string `yaml:"ignorePath"` // 忽略目录 35 | IgnoreFile []string `yaml:"ignoreFile"` // 忽略文件 36 | } 37 | 38 | // 当前再循环的Dir路径 39 | var CurDirPath string 40 | 41 | // Explorer 遍历多个目录 42 | // 43 | // option : 遍历选项 44 | // tree : 遍历结果 45 | func Explorer(option Option) (Node, error) { 46 | // 根节点 47 | var root Node 48 | 49 | // 多个目录搜索 50 | for _, p := range option.RootPath { 51 | // 空目录跳过 52 | if strings.TrimSpace(p) == "" { 53 | continue 54 | } 55 | 56 | var child Node 57 | 58 | // 目录路径 59 | CurDirPath = p 60 | child.Path = p 61 | 62 | // 递归 63 | explorerRecursive(&child, &option) 64 | 65 | root.Children = append(root.Children, &child) 66 | } 67 | 68 | return root, nil 69 | } 70 | 71 | // 递归遍历目录 72 | // 73 | // node : 目录节点 74 | // option : 遍历选项 75 | func explorerRecursive(node *Node, option *Option) { 76 | // 节点的信息 77 | p, err := os.Stat(node.Path) 78 | if err != nil { 79 | log.Println(err) 80 | return 81 | } 82 | // 是否为目录 83 | node.IsDir = p.IsDir() 84 | 85 | // 非目录,返回 86 | if !p.IsDir() { 87 | return 88 | } 89 | 90 | // 目录中的文件和子目录 91 | sub, err := os.ReadDir(node.Path) 92 | if err != nil { 93 | info := "目录不存在,或打开错误。" 94 | log.Printf("%v: %v", info, err) 95 | return 96 | } 97 | 98 | for _, f := range sub { 99 | tmp := path.Join(node.Path, f.Name()) 100 | var child Node 101 | // 完整子目录 102 | child.Path = tmp 103 | // 目录(或文件)名 104 | child.Name = f.Name() 105 | // 访问路径 106 | child.Link = strings.TrimPrefix(strings.TrimSuffix(tmp, path.Ext(f.Name())), CurDirPath) 107 | 108 | finfo, err := f.Info() 109 | if err != nil { 110 | log.Println(err) 111 | } else { 112 | child.ModTime = finfo.ModTime().Unix() 113 | } 114 | 115 | // 目录或文件名(不包含后缀) 116 | child.ShowName = strings.TrimSuffix(f.Name(), path.Ext(f.Name())) 117 | if strings.Contains(child.ShowName, "@") { 118 | child.ShowName = child.ShowName[strings.Index(child.ShowName, "@")+1:] 119 | } 120 | // 是否为目录 121 | child.IsDir = f.IsDir() 122 | 123 | // 目录 124 | if f.IsDir() { 125 | //查找子目录 126 | if option.SubFlag { 127 | // 不在忽略目录中的目录,进行递归查找 128 | if !IsInSlice(option.IgnorePath, f.Name()) { 129 | node.Children = append(node.Children, &child) 130 | explorerRecursive(&child, option) 131 | } 132 | } 133 | } else { // 文件 134 | 135 | // 非忽略文件,添加到结果中 136 | if !IsInSlice(option.IgnoreFile, f.Name()) { 137 | node.Children = append(node.Children, &child) 138 | } 139 | } 140 | } 141 | } 142 | 143 | func GetArticleInfo(node Node) types.Article { 144 | var article types.Article = types.Article{ 145 | Title: node.ShowName, 146 | Link: node.Link, 147 | } 148 | article.PublishTime = time.Unix(node.ModTime, 0).Format("2006-01-02 15:04:05") 149 | 150 | reg := regexp.MustCompile(`/(.+)/`) 151 | article.Category = reg.FindStringSubmatch(node.Link)[1] 152 | // 读取文件 153 | content, err := os.ReadFile(node.Path) 154 | if err != nil { 155 | log.Println(err) 156 | } 157 | unsafe := blackfriday.Run(content) 158 | html := bluemonday.UGCPolicy().SanitizeBytes(unsafe) 159 | doc, err := goquery.NewDocumentFromReader(bytes.NewReader(html)) 160 | if err != nil { 161 | log.Println(err) 162 | } 163 | plist := doc.Find("p").Text() 164 | if strings.Contains(plist, "[toc]") { 165 | plist = strings.ReplaceAll(plist, "[toc]", "") 166 | } 167 | 168 | runeList := []rune(plist) 169 | if len(runeList) > 150 { 170 | plist = string(runeList[:150]) 171 | } 172 | article.Preview = plist + "..." 173 | 174 | return article 175 | } 176 | -------------------------------------------------------------------------------- /web/assets/js/jquery.slimscroll.min.js: -------------------------------------------------------------------------------- 1 | /*! Copyright (c) 2011 Piotr Rochala (http://rocha.la) 2 | * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 3 | * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. 4 | * 5 | * Version: 1.3.8 6 | * 7 | */ 8 | (function(e){e.fn.extend({slimScroll:function(f){var a=e.extend({width:"auto",height:"250px",size:"7px",color:"#000",position:"right",distance:"1px",start:"top",opacity:.4,alwaysVisible:!1,disableFadeOut:!1,railVisible:!1,railColor:"#333",railOpacity:.2,railDraggable:!0,railClass:"slimScrollRail",barClass:"slimScrollBar",wrapperClass:"slimScrollDiv",allowPageScroll:!1,wheelStep:20,touchScrollStep:200,borderRadius:"7px",railBorderRadius:"7px"},f);this.each(function(){function v(d){if(r){d=d||window.event; 9 | var c=0;d.wheelDelta&&(c=-d.wheelDelta/120);d.detail&&(c=d.detail/3);e(d.target||d.srcTarget||d.srcElement).closest("."+a.wrapperClass).is(b.parent())&&n(c,!0);d.preventDefault&&!k&&d.preventDefault();k||(d.returnValue=!1)}}function n(d,g,e){k=!1;var f=b.outerHeight()-c.outerHeight();g&&(g=parseInt(c.css("top"))+d*parseInt(a.wheelStep)/100*c.outerHeight(),g=Math.min(Math.max(g,0),f),g=0=b.outerHeight()?k=!0:(c.stop(!0, 11 | !0).fadeIn("fast"),a.railVisible&&m.stop(!0,!0).fadeIn("fast"))}function p(){a.alwaysVisible||(B=setTimeout(function(){a.disableFadeOut&&r||y||z||(c.fadeOut("slow"),m.fadeOut("slow"))},1E3))}var r,y,z,B,A,u,l,C,k=!1,b=e(this);if(b.parent().hasClass(a.wrapperClass)){var q=b.scrollTop(),c=b.siblings("."+a.barClass),m=b.siblings("."+a.railClass);x();if(e.isPlainObject(f)){if("height"in f&&"auto"==f.height){b.parent().css("height","auto");b.css("height","auto");var h=b.parent().parent().height();b.parent().css("height", 12 | h);b.css("height",h)}else"height"in f&&(h=f.height,b.parent().css("height",h),b.css("height",h));if("scrollTo"in f)q=parseInt(a.scrollTo);else if("scrollBy"in f)q+=parseInt(a.scrollBy);else if("destroy"in f){c.remove();m.remove();b.unwrap();return}n(q,!1,!0)}}else if(!(e.isPlainObject(f)&&"destroy"in f)){a.height="auto"==a.height?b.parent().height():a.height;q=e("
").addClass(a.wrapperClass).css({position:"relative",overflow:"hidden",width:a.width,height:a.height});b.css({overflow:"hidden", 13 | width:a.width,height:a.height});var m=e("
").addClass(a.railClass).css({width:a.size,height:"100%",position:"absolute",top:0,display:a.alwaysVisible&&a.railVisible?"block":"none","border-radius":a.railBorderRadius,background:a.railColor,opacity:a.railOpacity,zIndex:90}),c=e("
").addClass(a.barClass).css({background:a.color,width:a.size,position:"absolute",top:0,opacity:a.opacity,display:a.alwaysVisible?"block":"none","border-radius":a.borderRadius,BorderRadius:a.borderRadius,MozBorderRadius:a.borderRadius, 14 | WebkitBorderRadius:a.borderRadius,zIndex:99}),h="right"==a.position?{right:a.distance}:{left:a.distance};m.css(h);c.css(h);b.wrap(q);b.parent().append(c);b.parent().append(m);a.railDraggable&&c.bind("mousedown",function(a){var b=e(document);z=!0;t=parseFloat(c.css("top"));pageY=a.pageY;b.bind("mousemove.slimscroll",function(a){currTop=t+a.pageY-pageY;c.css("top",currTop);n(0,c.position().top,!1)});b.bind("mouseup.slimscroll",function(a){z=!1;p();b.unbind(".slimscroll")});return!1}).bind("selectstart.slimscroll", 15 | function(a){a.stopPropagation();a.preventDefault();return!1});m.hover(function(){w()},function(){p()});c.hover(function(){y=!0},function(){y=!1});b.hover(function(){r=!0;w();p()},function(){r=!1;p()});b.bind("touchstart",function(a,b){a.originalEvent.touches.length&&(A=a.originalEvent.touches[0].pageY)});b.bind("touchmove",function(b){k||b.originalEvent.preventDefault();b.originalEvent.touches.length&&(n((A-b.originalEvent.touches[0].pageY)/a.touchScrollStep,!0),A=b.originalEvent.touches[0].pageY)}); 16 | x();"bottom"===a.start?(c.css({top:b.outerHeight()-c.outerHeight()}),n(0,!0)):"top"!==a.start&&(n(e(a.start).position().top,null,!0),a.alwaysVisible||c.hide());window.addEventListener?(this.addEventListener("DOMMouseScroll",v,!1),this.addEventListener("mousewheel",v,!1)):document.attachEvent("onmousewheel",v)}});return this}});e.fn.extend({slimscroll:e.fn.slimScroll})})(jQuery); 17 | -------------------------------------------------------------------------------- /package.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash -x 2 | 3 | # 生成压缩包 xx.tar.gz或xx.zip 4 | # 使用 . ./package.sh -a amd664 -p linux -v v2.0.0 5 | 6 | # 任何命令返回非0值退出 7 | set -o errexit 8 | # 使用未定义的变量退出 9 | set -o nounset 10 | # 管道中任一命令执行失败退出 11 | set -o pipefail 12 | 13 | eval $(go env) 14 | 15 | # 二进制文件名 16 | BINARY_NAME='' 17 | # main函数所在文件 18 | MAIN_FILE="" 19 | 20 | # 提取git最新tag作为应用版本 21 | VERSION='' 22 | # 最新git commit id 23 | GIT_COMMIT_ID='' 24 | 25 | # 外部输入的系统 26 | INPUT_OS=() 27 | # 外部输入的架构 28 | INPUT_ARCH=() 29 | # 未指定OS,默认值 30 | DEFAULT_OS=${GOHOSTOS} 31 | # 未指定ARCH,默认值 32 | DEFAULT_ARCH=${GOHOSTARCH} 33 | # 支持的系统 34 | SUPPORT_OS=(linux darwin windows) 35 | # 支持的架构 36 | SUPPORT_ARCH=(386 amd64 arm64) 37 | 38 | # 编译参数 39 | LDFLAGS='' 40 | # 需要打包的文件 41 | INCLUDE_FILE=() 42 | # 打包文件生成目录 43 | PACKAGE_DIR='' 44 | # 编译文件生成目录 45 | BUILD_DIR='' 46 | 47 | # 获取git 最新tag name 48 | git_latest_tag() { 49 | local COMMIT_ID="" 50 | local TAG_NAME="" 51 | COMMIT_ID=`git rev-list --tags --max-count=1` 52 | TAG_NAME=`git describe --tags "${COMMIT_ID}"` 53 | 54 | echo ${TAG_NAME} 55 | } 56 | 57 | # 获取git 最新commit id 58 | git_latest_commit() { 59 | echo "$(git rev-parse --short HEAD)" 60 | } 61 | 62 | # 打印信息 63 | print_message() { 64 | echo "$1" 65 | } 66 | 67 | # 打印信息后推出 68 | print_message_and_exit() { 69 | if [[ -n $1 ]]; then 70 | print_message "$1" 71 | fi 72 | exit 1 73 | } 74 | 75 | # 设置系统、CPU架构 76 | set_os_arch() { 77 | if [[ ${#INPUT_OS[@]} = 0 ]];then 78 | INPUT_OS=("${DEFAULT_OS}") 79 | fi 80 | 81 | if [[ ${#INPUT_ARCH[@]} = 0 ]];then 82 | INPUT_ARCH=("${DEFAULT_ARCH}") 83 | fi 84 | 85 | for OS in "${INPUT_OS[@]}"; do 86 | if [[ ! "${SUPPORT_OS[*]}" =~ ${OS} ]]; then 87 | print_message_and_exit "不支持的系统${OS}" 88 | fi 89 | done 90 | 91 | for ARCH in "${INPUT_ARCH[@]}";do 92 | if [[ ! "${SUPPORT_ARCH[*]}" =~ ${ARCH} ]]; then 93 | print_message_and_exit "不支持的CPU架构${ARCH}" 94 | fi 95 | done 96 | } 97 | 98 | # 初始化 99 | init() { 100 | set_os_arch 101 | 102 | if [[ -z "${VERSION}" ]];then 103 | VERSION=`git_latest_tag` 104 | fi 105 | GIT_COMMIT_ID=`git_latest_commit` 106 | LDFLAGS="-w -X 'main.AppVersion=${VERSION}' -X 'main.BuildDate=`date '+%Y-%m-%d %H:%M:%S'`' -X 'main.GitCommit=${GIT_COMMIT_ID}'" 107 | 108 | PACKAGE_DIR=package 109 | BUILD_DIR=build 110 | 111 | if [[ -d ${BUILD_DIR} ]];then 112 | rm -rf ${BUILD_DIR} 113 | fi 114 | if [[ -d ${PACKAGE_DIR} ]];then 115 | rm -rf ${PACKAGE_DIR} 116 | fi 117 | 118 | mkdir -p ${BUILD_DIR} 119 | mkdir -p ${PACKAGE_DIR} 120 | } 121 | 122 | # 编译 123 | build() { 124 | local FILENAME='' 125 | for OS in "${INPUT_OS[@]}";do 126 | for ARCH in "${INPUT_ARCH[@]}";do 127 | if [[ "${OS}" = "windows" ]];then 128 | FILENAME=${BINARY_NAME}.exe 129 | else 130 | FILENAME=${BINARY_NAME} 131 | fi 132 | env CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH} go build -ldflags "${LDFLAGS}" -trimpath -o ${BUILD_DIR}/${BINARY_NAME}-${OS}-${ARCH}/${FILENAME} ${MAIN_FILE} 133 | done 134 | done 135 | } 136 | 137 | # 打包 138 | package_binary() { 139 | cd ${BUILD_DIR} 140 | 141 | for OS in "${INPUT_OS[@]}";do 142 | for ARCH in "${INPUT_ARCH[@]}";do 143 | package_file ${BINARY_NAME}-${OS}-${ARCH} 144 | if [[ "${OS}" = "windows" ]];then 145 | zip -rq ../${PACKAGE_DIR}/${BINARY_NAME}-${VERSION}-${OS}-${ARCH}.zip ${BINARY_NAME}-${OS}-${ARCH} 146 | else 147 | tar czf ../${PACKAGE_DIR}/${BINARY_NAME}-${VERSION}-${OS}-${ARCH}.tar.gz ${BINARY_NAME}-${OS}-${ARCH} 148 | fi 149 | done 150 | done 151 | 152 | cd ${OLDPWD} 153 | } 154 | 155 | # 打包文件 156 | package_file() { 157 | if [[ "${#INCLUDE_FILE[@]}" = "0" ]];then 158 | return 159 | fi 160 | for item in "${INCLUDE_FILE[@]}"; do 161 | cp -r ../${item} $1 162 | done 163 | } 164 | 165 | # 清理 166 | clean() { 167 | if [[ -d ${BUILD_DIR} ]];then 168 | rm -rf ${BUILD_DIR} 169 | fi 170 | } 171 | 172 | # 运行 173 | run() { 174 | echo "开始初始化" 175 | init 176 | echo "开始编译" 177 | build 178 | echo "开始打包" 179 | package_binary 180 | } 181 | 182 | package_mdbook() { 183 | BINARY_NAME='markdown-book' 184 | MAIN_FILE="./main.go" 185 | 186 | run 187 | } 188 | 189 | # p 平台 linux darwin windows 190 | # a 架构 386 amd64 191 | # v 版本号 默认取git最新tag 192 | while getopts "p:a:v:" OPT; 193 | do 194 | case ${OPT} in 195 | p) IPS=',' read -r -a INPUT_OS <<< "${OPTARG}" 196 | ;; 197 | a) IPS=',' read -r -a INPUT_ARCH <<< "${OPTARG}" 198 | ;; 199 | v) VERSION=$OPTARG 200 | ;; 201 | *) 202 | ;; 203 | esac 204 | done 205 | 206 | package_mdbook 207 | 208 | -------------------------------------------------------------------------------- /internal/app/githubReader.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | "io" 8 | "log" 9 | "net/http" 10 | "strings" 11 | 12 | "github.com/PuerkitoBio/goquery" 13 | "github.com/google/go-github/github" 14 | "github.com/hedongshu/go-md-book/internal/types" 15 | "github.com/hedongshu/go-md-book/internal/utils" 16 | "github.com/kataras/iris/v12" 17 | "github.com/microcosm-cc/bluemonday" 18 | "github.com/russross/blackfriday/v2" 19 | ) 20 | 21 | var ( 22 | client *github.Client 23 | ) 24 | 25 | func GetAllMarkDownsFromGithub() { 26 | fmt.Println("GetAllMarkDownsFromGithub start") 27 | 28 | ctx := context.Background() 29 | client = github.NewClient(nil) 30 | 31 | opt := github.RepositoryContentGetOptions{ 32 | Ref: "main", 33 | } 34 | // 获取分类 35 | _, directoryContent, _, err := client.Repositories.GetContents(ctx, GithubStr.Owner, GithubStr.Repo, "/", &opt) 36 | 37 | if err != nil { 38 | log.Println(err) 39 | return 40 | } 41 | 42 | TreeArticles := make([]types.TreeArticle, 0) 43 | Categories := make([]string, 0) 44 | Articles := make([]types.Article, 0) 45 | 46 | for _, item := range directoryContent { 47 | if strings.HasPrefix(item.GetName(), "_") { 48 | continue 49 | } 50 | 51 | if item.GetType() == "dir" { 52 | Categories = append(Categories, item.GetName()) 53 | 54 | theList := getArticlesFromGithub(item, item.GetName()) 55 | Articles = append(Articles, theList...) 56 | 57 | TreeArticles = append(TreeArticles, types.TreeArticle{ 58 | CategorieName: item.GetName(), 59 | List: theList, 60 | }) 61 | } 62 | } 63 | 64 | GlobleDatas.Articles = Articles 65 | GlobleDatas.Categories = Categories 66 | GlobleDatas.TreeArticles = TreeArticles 67 | } 68 | 69 | func getArticlesFromGithub(content *github.RepositoryContent, category string) []types.Article { 70 | list := make([]types.Article, 0) 71 | 72 | if content.GetType() == "file" { 73 | title := content.GetName() 74 | i := strings.LastIndex(title, ".") 75 | if i != -1 { 76 | title = title[:i] 77 | } 78 | 79 | info := types.Article{ 80 | Title: title, 81 | Category: category, 82 | PublishTime: "", 83 | Link: "/" + category + "/" + title, 84 | } 85 | downUrl := content.GetDownloadURL() 86 | resp, err := http.Get(downUrl) 87 | if err != nil { 88 | log.Fatal(err) 89 | } 90 | // 读取返回结果 91 | body, _ := io.ReadAll(resp.Body) 92 | unsafe := blackfriday.Run(body) 93 | html := bluemonday.UGCPolicy().SanitizeBytes(unsafe) 94 | doc, err := goquery.NewDocumentFromReader(bytes.NewReader(html)) 95 | if err != nil { 96 | log.Println(err) 97 | } 98 | plist := doc.Find("p").Text() 99 | if strings.Contains(plist, "[toc]") { 100 | plist = strings.ReplaceAll(plist, "[toc]", "") 101 | } 102 | runeList := []rune(plist) 103 | if len(runeList) > 150 { 104 | plist = string(runeList[:150]) 105 | } 106 | info.Preview = plist + "..." 107 | 108 | list = append(list, info) 109 | } 110 | 111 | if content.GetType() == "dir" { 112 | ctx := context.Background() 113 | opt := github.RepositoryContentGetOptions{ 114 | Ref: "main", 115 | } 116 | // 获取文章列表 117 | _, posts, _, err := client.Repositories.GetContents(ctx, GithubStr.Owner, GithubStr.Repo, "/"+content.GetName(), &opt) 118 | if err != nil { 119 | return list 120 | } 121 | 122 | for _, post := range posts { 123 | newitem := getArticlesFromGithub(post, content.GetName()) 124 | list = append(list, newitem...) 125 | } 126 | 127 | } 128 | 129 | return list 130 | } 131 | 132 | func Github_HomeHandler(ctx iris.Context) { 133 | if err := ctx.View("home.html"); err != nil { 134 | log.Println(err) 135 | } 136 | } 137 | 138 | func Github_ArticleHandler(ctx iris.Context) { 139 | f := ctx.Params().Get("f") 140 | // 设置 Gitalk ID 141 | Gitalk.Id = utils.MD5(f) 142 | ctx.ViewData("Gitalk", Gitalk) 143 | ctx.ViewData("ActiveNav", f) 144 | 145 | if utils.IsInSlice(IgnoreFile, f) { 146 | return 147 | } 148 | 149 | opt := github.RepositoryContentGetOptions{ 150 | Ref: "main", 151 | } 152 | // 获取markdwon文件 153 | fileContent, _, _, err := client.Repositories.GetContents(ctx, GithubStr.Owner, GithubStr.Repo, "/"+f+".md", &opt) 154 | 155 | if err != nil { 156 | ctx.StatusCode(500) 157 | ctx.Application().Logger().Errorf("ReadFile Error '%s', Path is %s", f, ctx.Path()) 158 | return 159 | } 160 | 161 | c, _ := fileContent.GetContent() 162 | ctx.ViewData("Article", utils.MdToHtml([]byte(c), TocPrefix)) 163 | 164 | ctx.View("article.html") 165 | 166 | } 167 | 168 | func Github_CategoriesHandler(ctx iris.Context) { 169 | categorie := ctx.Params().Get("f") 170 | showAll := false 171 | 172 | if categorie == "" { 173 | showAll = true 174 | } else { 175 | 176 | for _, item := range GlobleDatas.TreeArticles { 177 | if item.CategorieName == categorie { 178 | ctx.ViewData("theList", item.List) 179 | } 180 | } 181 | } 182 | 183 | ctx.ViewData("ShowAll", showAll) 184 | ctx.ViewData("Categorie", categorie) 185 | 186 | ctx.View("categories.html") 187 | } 188 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/hedongshu/go-md-book/internal/app" 7 | "github.com/hedongshu/go-md-book/internal/utils" 8 | "github.com/urfave/cli/v2" 9 | "github.com/urfave/cli/v2/altsrc" 10 | ) 11 | 12 | //go:generate go-bindata -fs -o internal/bindata/views/views.go -pkg=views -prefix=web/views ./web/views/... 13 | //go:generate go-bindata -fs -o internal/bindata/assets/assets.go -pkg=assets -prefix=web/assets ./web/assets/... 14 | 15 | var ( 16 | origin = "file" 17 | MdDir = "md/" 18 | Title = "Blog Title" 19 | Title2 = "Blog Title2" 20 | AppVersion = "1.0.0" 21 | BuildDate, GitCommit string 22 | ) 23 | 24 | // web服务器默认端口 25 | const DefaultPort = 5006 26 | 27 | func main() { 28 | cliApp := cli.NewApp() 29 | cliApp.Name = "markdown-book" 30 | cliApp.Usage = "Markdown Book App" 31 | cliApp.Version, _ = utils.FormatAppVersion(AppVersion, GitCommit, BuildDate) 32 | cliApp.Commands = getCommands() 33 | cliApp.Flags = append(cliApp.Flags, []cli.Flag{}...) 34 | cliApp.Run(os.Args) 35 | } 36 | 37 | func getCommands() []*cli.Command { 38 | web := webCommand() 39 | 40 | return []*cli.Command{web} 41 | } 42 | 43 | func webCommand() *cli.Command { 44 | commonFlags := []cli.Flag{ 45 | &cli.StringFlag{ 46 | Name: "config", 47 | Value: "", 48 | Usage: "Load configuration from `FILE`, default is empty", 49 | }, 50 | altsrc.NewStringFlag(&cli.StringFlag{ 51 | Name: "origin", 52 | Aliases: []string{"o"}, 53 | Value: origin, 54 | Usage: "article origin, file or github", 55 | }), 56 | altsrc.NewStringFlag(&cli.StringFlag{ 57 | Name: "dir", 58 | Aliases: []string{"d"}, 59 | Value: MdDir, 60 | Usage: "Markdown files dir", 61 | }), 62 | altsrc.NewStringFlag(&cli.StringFlag{ 63 | Name: "title", 64 | Aliases: []string{"t"}, 65 | Value: Title, 66 | Usage: "Blog title", 67 | }), 68 | altsrc.NewStringFlag(&cli.StringFlag{ 69 | Name: "title2", 70 | Aliases: []string{"t2"}, 71 | Value: Title2, 72 | Usage: "Blog title2", 73 | }), 74 | altsrc.NewIntFlag(&cli.IntFlag{ 75 | Name: "port", 76 | Aliases: []string{"p"}, 77 | Value: DefaultPort, 78 | Usage: "Bind port", 79 | }), 80 | altsrc.NewStringFlag(&cli.StringFlag{ 81 | Name: "env", 82 | Aliases: []string{"e"}, 83 | Value: "prod", 84 | Usage: "Runtime environment, dev|test|prod", 85 | }), 86 | altsrc.NewIntFlag(&cli.IntFlag{ 87 | Name: "cache", 88 | Aliases: []string{"c"}, 89 | Value: 3, 90 | Usage: "The cache time unit is minutes, this parameter takes effect in the prod environment", 91 | }), 92 | } 93 | 94 | githubFlags := []cli.Flag{ 95 | altsrc.NewStringFlag(&cli.StringFlag{ 96 | Name: "github.owner", 97 | Usage: "set up github owner", 98 | }), 99 | altsrc.NewStringFlag(&cli.StringFlag{ 100 | Name: "github.repo", 101 | Usage: "set up github Repo", 102 | }), 103 | } 104 | flags := append(commonFlags, githubFlags...) 105 | 106 | gitalkFlags := []cli.Flag{ 107 | altsrc.NewStringFlag(&cli.StringFlag{ 108 | Name: "gitalk.client-id", 109 | Usage: "Set up Gitalk ClientId, default is empty", 110 | }), 111 | altsrc.NewStringFlag(&cli.StringFlag{ 112 | Name: "gitalk.client-secret", 113 | Usage: "Set up Gitalk ClientSecret, default is empty", 114 | }), 115 | altsrc.NewStringFlag(&cli.StringFlag{ 116 | Name: "gitalk.repo", 117 | Usage: "Set up Gitalk Repo, default is empty", 118 | }), 119 | altsrc.NewStringFlag(&cli.StringFlag{ 120 | Name: "gitalk.owner", 121 | Usage: "Set up Gitalk Repo, default is empty", 122 | }), 123 | altsrc.NewStringSliceFlag(&cli.StringSliceFlag{ 124 | Name: "gitalk.admin", 125 | Usage: "Set up Gitalk Admin, default is `[gitalk.owner]`", 126 | }), 127 | altsrc.NewStringSliceFlag(&cli.StringSliceFlag{ 128 | Name: "gitalk.labels", 129 | Usage: "Set up Gitalk Admin, default is `[\"gitalk\"]`", 130 | }), 131 | } 132 | flags = append(flags, gitalkFlags...) 133 | 134 | analyzerFlags := []cli.Flag{ 135 | altsrc.NewStringFlag(&cli.StringFlag{ 136 | Name: "analyzer-baidu", 137 | Aliases: []string{"ab"}, 138 | Value: "", 139 | Usage: "Set up Baidu Analyzer, default is empty", 140 | }), 141 | altsrc.NewStringFlag(&cli.StringFlag{ 142 | Name: "analyzer-google", 143 | Aliases: []string{"ag"}, 144 | Value: "", 145 | Usage: "Set up Google Analyzer, default is empty", 146 | }), 147 | } 148 | flags = append(flags, analyzerFlags...) 149 | 150 | ignoreFlags := []cli.Flag{ 151 | altsrc.NewStringSliceFlag(&cli.StringSliceFlag{ 152 | Name: "ignore-file", 153 | Usage: "Set up ignore file, eg: demo.md", 154 | }), 155 | altsrc.NewStringSliceFlag(&cli.StringSliceFlag{ 156 | Name: "ignore-path", 157 | Usage: "Set up ignore path, eg: demo", 158 | }), 159 | } 160 | flags = append(flags, ignoreFlags...) 161 | 162 | web := cli.Command{ 163 | Name: "web", 164 | Usage: "Run blog web server", 165 | Action: app.RunWeb, 166 | Flags: flags, 167 | Before: altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("config")), 168 | } 169 | 170 | return &web 171 | } 172 | -------------------------------------------------------------------------------- /web/assets/css/animation.css: -------------------------------------------------------------------------------- 1 | @keyframes fadein { 2 | from { 3 | opacity: 0; 4 | } 5 | to { 6 | opacity: 1; 7 | } 8 | } 9 | /* Firefox < 16 */ 10 | @-moz-keyframes fadein { 11 | from { 12 | opacity: 0; 13 | } 14 | to { 15 | opacity: 1; 16 | } 17 | } 18 | /* Safari, Chrome and Opera > 12.1 */ 19 | @-webkit-keyframes fadein { 20 | from { 21 | opacity: 0; 22 | } 23 | to { 24 | opacity: 1; 25 | } 26 | } 27 | /* Internet Explorer */ 28 | @-ms-keyframes fadein { 29 | from { 30 | opacity: 0; 31 | } 32 | to { 33 | opacity: 1; 34 | } 35 | } 36 | /* Opera < 12.1 */ 37 | @-o-keyframes fadein { 38 | from { 39 | opacity: 0; 40 | } 41 | to { 42 | opacity: 1; 43 | } 44 | } 45 | @keyframes fadein-right { 46 | from { 47 | opacity: 0; 48 | transform: translateX(20px); 49 | } 50 | to { 51 | opacity: 1; 52 | transform: translateX(0px); 53 | } 54 | } 55 | /* Firefox < 16 */ 56 | @-moz-keyframes fadein-right { 57 | from { 58 | opacity: 0; 59 | transform: translateX(20px); 60 | } 61 | to { 62 | opacity: 1; 63 | transform: translateX(0px); 64 | } 65 | } 66 | /* Safari, Chrome and Opera > 12.1 */ 67 | @-webkit-keyframes fadein-right { 68 | from { 69 | opacity: 0; 70 | transform: translateX(20px); 71 | } 72 | to { 73 | opacity: 1; 74 | transform: translateX(0px); 75 | } 76 | } 77 | /* Internet Explorer */ 78 | @-ms-keyframes fadein-right { 79 | from { 80 | opacity: 0; 81 | transform: translateX(20px); 82 | } 83 | to { 84 | opacity: 1; 85 | transform: translateX(0px); 86 | } 87 | } 88 | /* Opera < 12.1 */ 89 | @-o-keyframes fadein-right { 90 | from { 91 | opacity: 0; 92 | transform: translateX(20px); 93 | } 94 | to { 95 | opacity: 1; 96 | transform: translateX(0px); 97 | } 98 | } 99 | @keyframes fadein-top { 100 | from { 101 | opacity: 0; 102 | transform: translateY(-20px); 103 | } 104 | to { 105 | opacity: 1; 106 | transform: translateY(0px); 107 | } 108 | } 109 | /* Firefox < 16 */ 110 | @-moz-keyframes fadein-top { 111 | from { 112 | opacity: 0; 113 | transform: translateY(-20px); 114 | } 115 | to { 116 | opacity: 1; 117 | transform: translateY(0px); 118 | } 119 | } 120 | /* Safari, Chrome and Opera > 12.1 */ 121 | @-webkit-keyframes fadein-top { 122 | from { 123 | opacity: 0; 124 | transform: translateY(-20px); 125 | } 126 | to { 127 | opacity: 1; 128 | transform: translateY(0px); 129 | } 130 | } 131 | /* Internet Explorer */ 132 | @-ms-keyframes fadein-top { 133 | from { 134 | opacity: 0; 135 | transform: translateY(-20px); 136 | } 137 | to { 138 | opacity: 1; 139 | transform: translateY(0px); 140 | } 141 | } 142 | /* Opera < 12.1 */ 143 | @-o-keyframes fadein-top { 144 | from { 145 | opacity: 0; 146 | transform: translateY(-20px); 147 | } 148 | to { 149 | opacity: 1; 150 | transform: translateY(0px); 151 | } 152 | } 153 | @keyframes swift-down { 154 | from { 155 | transform: translateY(0%); 156 | } 157 | to { 158 | transform: translateY(20%); 159 | } 160 | } 161 | /* Firefox < 16 */ 162 | @-moz-keyframes swift-down { 163 | from { 164 | transform: translateY(0%); 165 | } 166 | to { 167 | transform: translateY(20%); 168 | } 169 | } 170 | /* Safari, Chrome and Opera > 12.1 */ 171 | @-webkit-keyframes swift-down { 172 | from { 173 | transform: translateY(0%); 174 | } 175 | to { 176 | transform: translateY(20%); 177 | } 178 | } 179 | /* Internet Explorer */ 180 | @-ms-keyframes swift-down { 181 | from { 182 | transform: translateY(0%); 183 | } 184 | to { 185 | transform: translateY(20%); 186 | } 187 | } 188 | /* Opera < 12.1 */ 189 | @-o-keyframes swift-down { 190 | from { 191 | transform: translateY(0%); 192 | } 193 | to { 194 | transform: translateY(20%); 195 | } 196 | } 197 | .invisible { 198 | opacity: 0; 199 | } 200 | 201 | .fadeIn { 202 | -webkit-animation: fadein 1s ease-out; /* Safari, Chrome and Opera > 12.1 */ 203 | -moz-animation: fadein 1s ease-out; /* Firefox < 16 */ 204 | -ms-animation: fadein 1s ease-out; /* Internet Explorer */ 205 | -o-animation: fadein 1s ease-out; /* Opera < 12.1 */ 206 | animation: fadein 1s ease-out; 207 | } 208 | 209 | .fadeInRight { 210 | -webkit-animation: fadein-right 1s ease-out; /* Safari, Chrome and Opera > 12.1 */ 211 | -moz-animation: fadein-right 1s ease-out; /* Firefox < 16 */ 212 | -ms-animation: fadein-right 1s ease-out; /* Internet Explorer */ 213 | -o-animation: fadein-right 1s ease-out; /* Opera < 12.1 */ 214 | animation: fadein-right 1s ease-out; 215 | } 216 | 217 | .fadeInTop { 218 | -webkit-animation: fadein-top 1s ease-out; /* Safari, Chrome and Opera > 12.1 */ 219 | -moz-animation: fadein-top 1s ease-out; /* Firefox < 16 */ 220 | -ms-animation: fadein-top 1s ease-out; /* Internet Explorer */ 221 | -o-animation: fadein-top 1s ease-out; /* Opera < 12.1 */ 222 | animation: fadein-top 1s ease-out; 223 | } 224 | 225 | .swiftDown { 226 | -webkit-animation: swift-down 1s ease-out; /* Safari, Chrome and Opera > 12.1 */ 227 | -moz-animation: swift-down 1s ease-out; /* Firefox < 16 */ 228 | -ms-animation: swift-down 1s ease-out; /* Internet Explorer */ 229 | -o-animation: swift-down 1s ease-out; /* Opera < 12.1 */ 230 | animation: swift-down 1s ease-out; 231 | } 232 | 233 | .swiftDownReversed { 234 | -webkit-animation: swift-down 1s reverse ease-out; /* Safari, Chrome and Opera > 12.1 */ 235 | -moz-animation: swift-down 1s reverse ease-out; /* Firefox < 16 */ 236 | -ms-animation: swift-down 1s reverse ease-out; /* Internet Explorer */ 237 | -o-animation: swift-down 1s reverse ease-out; /* Opera < 12.1 */ 238 | animation: swift-down 1s reverse ease-out; 239 | } 240 | 241 | /*# sourceMappingURL=animation.css.map */ 242 | -------------------------------------------------------------------------------- /internal/app/app.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "path/filepath" 8 | "strconv" 9 | "strings" 10 | "time" 11 | 12 | "github.com/hedongshu/go-md-book/internal/api" 13 | "github.com/hedongshu/go-md-book/internal/bindata/assets" 14 | "github.com/hedongshu/go-md-book/internal/bindata/views" 15 | "github.com/hedongshu/go-md-book/internal/types" 16 | "github.com/kataras/iris/v12" 17 | "github.com/kataras/iris/v12/middleware/accesslog" 18 | "github.com/urfave/cli/v2" 19 | ) 20 | 21 | var ( 22 | Origin string 23 | MdDir string 24 | Env string 25 | Title string 26 | Title2 string 27 | LayoutFile = "layouts/layout.html" 28 | LogsDir = "cache/logs/" 29 | TocPrefix = "[toc]" 30 | IgnoreFile = []string{`favicon.ico`, `.DS_Store`, `.gitignore`, `README.md`} 31 | IgnorePath = []string{`.git`} 32 | Cache time.Duration = 3 33 | Analyzer types.Analyzer 34 | Gitalk types.Gitalk 35 | GithubStr types.GithubStr 36 | ) 37 | 38 | var GlobleDatas types.GlobleData 39 | 40 | // web服务器默认端口 41 | const DefaultPort = 5006 42 | 43 | func RunWeb(ctx *cli.Context) error { 44 | initParams(ctx) 45 | 46 | app := iris.New() 47 | 48 | setLog(app) 49 | 50 | tmpl := iris.HTML(views.AssetFile(), ".html").Reload(true) 51 | app.RegisterView(tmpl) 52 | tmpl.AddFunc("getArticlesLen", func(articles []types.Article) int { 53 | return len(articles) 54 | }) 55 | tmpl.AddFunc("isHomeNavActive", func(url string, name string) string { 56 | class := "" 57 | 58 | if strings.TrimSpace(url) == strings.TrimSpace(name) { 59 | class = "current" 60 | } 61 | 62 | return class 63 | }) 64 | app.OnErrorCode(iris.StatusNotFound, api.NotFound) 65 | app.OnErrorCode(iris.StatusInternalServerError, api.InternalServerError) 66 | app.Favicon("./favicon.ico") 67 | app.HandleDir("/static", assets.AssetFile()) 68 | 69 | if Origin == "file" { 70 | app.Use(func(ctx iris.Context) { 71 | 72 | CurrentPath := ctx.Path() 73 | 74 | GetAllMarkDownsFromFile() 75 | 76 | ctx.ViewData("CurrentPath", CurrentPath) 77 | ctx.ViewData("Analyzer", Analyzer) 78 | ctx.ViewData("Title", Title) 79 | ctx.ViewData("Title2", Title2) 80 | 81 | ctx.ViewData("Categories", GlobleDatas.Categories) 82 | ctx.ViewData("Articles", GlobleDatas.Articles) 83 | ctx.ViewData("TreeArticles", GlobleDatas.TreeArticles) 84 | 85 | ctx.ViewLayout(LayoutFile) 86 | 87 | ctx.Next() 88 | }) 89 | 90 | app.Get("/", iris.Cache(Cache), HomeHandler) 91 | app.Get("/article/{f:path}", iris.Cache(Cache), ArticleHandler) 92 | app.Get("/categories", iris.Cache(Cache), CategoriesHandler) 93 | app.Get("/categories/{f:path}", iris.Cache(Cache), CategoriesHandler) 94 | } 95 | if Origin == "github" { 96 | fmt.Println("origin is github") 97 | GetAllMarkDownsFromGithub() 98 | 99 | app.Use(func(ctx iris.Context) { 100 | 101 | CurrentPath := ctx.Path() 102 | 103 | ctx.ViewData("CurrentPath", CurrentPath) 104 | ctx.ViewData("Analyzer", Analyzer) 105 | ctx.ViewData("Title", Title) 106 | ctx.ViewData("Title2", Title2) 107 | ctx.ViewData("Articles", GlobleDatas.Articles) 108 | ctx.ViewData("Categories", GlobleDatas.Categories) 109 | ctx.ViewData("TreeArticles", GlobleDatas.TreeArticles) 110 | ctx.ViewLayout(LayoutFile) 111 | 112 | ctx.Next() 113 | }) 114 | 115 | app.Get("/", iris.Cache(Cache), Github_HomeHandler) 116 | app.Get("/article/{f:path}", iris.Cache(Cache), Github_ArticleHandler) 117 | app.Get("/categories", iris.Cache(Cache), Github_CategoriesHandler) 118 | app.Get("/categories/{f:path}", iris.Cache(Cache), Github_CategoriesHandler) 119 | 120 | } 121 | 122 | app.Get("/update", func(ctx iris.Context) { 123 | if Origin == "file" { 124 | ctx.HTML("

当前不是github模式

") 125 | return 126 | } 127 | if Origin == "github" { 128 | oldLen := len(GlobleDatas.Articles) 129 | GetAllMarkDownsFromGithub() 130 | nowLen := len(GlobleDatas.Articles) 131 | 132 | ctx.HTML("

更新完成

更新前 %d 篇文章, 更新后 %d 篇文章

回到首页

", oldLen, nowLen) 133 | return 134 | } 135 | }) 136 | 137 | app.Run(iris.Addr(":" + strconv.Itoa(parsePort(ctx)))) 138 | 139 | return nil 140 | } 141 | 142 | func initParams(ctx *cli.Context) { 143 | // 设置文件来源 144 | Origin = ctx.String("origin") 145 | 146 | if Origin == "github" { 147 | GithubStr.Owner = ctx.String("github.owner") 148 | GithubStr.Repo = ctx.String("github.repo") 149 | } 150 | 151 | MdDir = ctx.String("dir") 152 | if strings.TrimSpace(MdDir) == "" { 153 | log.Panic("Markdown files folder cannot be empty") 154 | } 155 | MdDir, _ = filepath.Abs(MdDir) 156 | 157 | Env = ctx.String("env") 158 | Title = ctx.String("title") 159 | Title2 = ctx.String("title2") 160 | 161 | _cache := ctx.Int("cache") 162 | 163 | Cache = time.Minute * time.Duration(_cache) 164 | if Env == "dev" { 165 | Cache = time.Minute * 0 166 | } 167 | 168 | // 设置分析器 169 | Analyzer.SetAnalyzer(ctx.String("analyzer-baidu"), ctx.String("analyzer-google")) 170 | 171 | // 设置Gitalk 172 | Gitalk.SetGitalk(ctx.String("gitalk.client-id"), ctx.String("gitalk.client-secret"), ctx.String("gitalk.repo"), ctx.String("gitalk.owner"), ctx.StringSlice("gitalk.admin"), ctx.StringSlice("gitalk.labels")) 173 | 174 | // 忽略文件 175 | IgnoreFile = append(IgnoreFile, ctx.StringSlice("ignore-file")...) 176 | IgnorePath = append(IgnorePath, ctx.StringSlice("ignore-path")...) 177 | } 178 | 179 | func setLog(app *iris.Application) { 180 | os.MkdirAll(LogsDir, 0777) 181 | f, _ := os.OpenFile(LogsDir+"access-"+time.Now().Format("20060102")+".log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600) 182 | 183 | if Env == "prod" { 184 | app.Logger().SetOutput(f) 185 | } else { 186 | app.Logger().SetLevel("debug") 187 | app.Logger().Debugf(`Log level set to "debug"`) 188 | } 189 | 190 | // Close the file on shutdown. 191 | app.ConfigureHost(func(su *iris.Supervisor) { 192 | su.RegisterOnShutdown(func() { 193 | f.Close() 194 | }) 195 | }) 196 | 197 | ac := accesslog.New(f) 198 | ac.AddOutput(app.Logger().Printer) 199 | app.UseRouter(ac.Handler) 200 | app.Logger().Debugf("Using <%s> to log requests", f.Name()) 201 | } 202 | 203 | func parsePort(ctx *cli.Context) int { 204 | port := DefaultPort 205 | if ctx.IsSet("port") { 206 | port = ctx.Int("port") 207 | } 208 | if port <= 0 || port >= 65535 { 209 | port = DefaultPort 210 | } 211 | 212 | return port 213 | } 214 | -------------------------------------------------------------------------------- /web/views/layouts/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ .Title }} 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {{ if .Gitalk.ClientID }} 31 | 32 | {{end}} 33 | 34 | {{if .Analyzer.Baidu}} 35 | 36 | 45 | {{end}} 46 | 47 | {{if .Analyzer.Google}} 48 | 49 | 50 | 57 | {{end}} 58 | 59 | 60 | 61 | 62 | 63 |
64 |
65 | 100 | 101 | 102 | 103 | 104 |
105 |
106 |
107 | 108 | 109 | {{ yield }} 110 | 111 | 119 |
120 |
121 | 128 |
129 |
130 |
131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | {{ if .Gitalk.ClientID }} 142 | 143 | 156 | {{end}} 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /web/assets/js/jquery-migrate-1.2.1.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery Migrate v1.2.1 | (c) 2005, 2013 jQuery Foundation, Inc. and other contributors | jquery.org/license */ 2 | jQuery.migrateMute===void 0&&(jQuery.migrateMute=!0),function(e,t,n){function r(n){var r=t.console;i[n]||(i[n]=!0,e.migrateWarnings.push(n),r&&r.warn&&!e.migrateMute&&(r.warn("JQMIGRATE: "+n),e.migrateTrace&&r.trace&&r.trace()))}function a(t,a,i,o){if(Object.defineProperty)try{return Object.defineProperty(t,a,{configurable:!0,enumerable:!0,get:function(){return r(o),i},set:function(e){r(o),i=e}}),n}catch(s){}e._definePropertyBroken=!0,t[a]=i}var i={};e.migrateWarnings=[],!e.migrateMute&&t.console&&t.console.log&&t.console.log("JQMIGRATE: Logging is active"),e.migrateTrace===n&&(e.migrateTrace=!0),e.migrateReset=function(){i={},e.migrateWarnings.length=0},"BackCompat"===document.compatMode&&r("jQuery is not compatible with Quirks Mode");var o=e("",{size:1}).attr("size")&&e.attrFn,s=e.attr,u=e.attrHooks.value&&e.attrHooks.value.get||function(){return null},c=e.attrHooks.value&&e.attrHooks.value.set||function(){return n},l=/^(?:input|button)$/i,d=/^[238]$/,p=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,f=/^(?:checked|selected)$/i;a(e,"attrFn",o||{},"jQuery.attrFn is deprecated"),e.attr=function(t,a,i,u){var c=a.toLowerCase(),g=t&&t.nodeType;return u&&(4>s.length&&r("jQuery.fn.attr( props, pass ) is deprecated"),t&&!d.test(g)&&(o?a in o:e.isFunction(e.fn[a])))?e(t)[a](i):("type"===a&&i!==n&&l.test(t.nodeName)&&t.parentNode&&r("Can't change the 'type' of an input or button in IE 6/7/8"),!e.attrHooks[c]&&p.test(c)&&(e.attrHooks[c]={get:function(t,r){var a,i=e.prop(t,r);return i===!0||"boolean"!=typeof i&&(a=t.getAttributeNode(r))&&a.nodeValue!==!1?r.toLowerCase():n},set:function(t,n,r){var a;return n===!1?e.removeAttr(t,r):(a=e.propFix[r]||r,a in t&&(t[a]=!0),t.setAttribute(r,r.toLowerCase())),r}},f.test(c)&&r("jQuery.fn.attr('"+c+"') may use property instead of attribute")),s.call(e,t,a,i))},e.attrHooks.value={get:function(e,t){var n=(e.nodeName||"").toLowerCase();return"button"===n?u.apply(this,arguments):("input"!==n&&"option"!==n&&r("jQuery.fn.attr('value') no longer gets properties"),t in e?e.value:null)},set:function(e,t){var a=(e.nodeName||"").toLowerCase();return"button"===a?c.apply(this,arguments):("input"!==a&&"option"!==a&&r("jQuery.fn.attr('value', val) no longer sets properties"),e.value=t,n)}};var g,h,v=e.fn.init,m=e.parseJSON,y=/^([^<]*)(<[\w\W]+>)([^>]*)$/;e.fn.init=function(t,n,a){var i;return t&&"string"==typeof t&&!e.isPlainObject(n)&&(i=y.exec(e.trim(t)))&&i[0]&&("<"!==t.charAt(0)&&r("$(html) HTML strings must start with '<' character"),i[3]&&r("$(html) HTML text after last tag is ignored"),"#"===i[0].charAt(0)&&(r("HTML string cannot start with a '#' character"),e.error("JQMIGRATE: Invalid selector string (XSS)")),n&&n.context&&(n=n.context),e.parseHTML)?v.call(this,e.parseHTML(i[2],n,!0),n,a):v.apply(this,arguments)},e.fn.init.prototype=e.fn,e.parseJSON=function(e){return e||null===e?m.apply(this,arguments):(r("jQuery.parseJSON requires a valid JSON string"),null)},e.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||0>e.indexOf("compatible")&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e.browser||(g=e.uaMatch(navigator.userAgent),h={},g.browser&&(h[g.browser]=!0,h.version=g.version),h.chrome?h.webkit=!0:h.webkit&&(h.safari=!0),e.browser=h),a(e,"browser",e.browser,"jQuery.browser is deprecated"),e.sub=function(){function t(e,n){return new t.fn.init(e,n)}e.extend(!0,t,this),t.superclass=this,t.fn=t.prototype=this(),t.fn.constructor=t,t.sub=this.sub,t.fn.init=function(r,a){return a&&a instanceof e&&!(a instanceof t)&&(a=t(a)),e.fn.init.call(this,r,a,n)},t.fn.init.prototype=t.fn;var n=t(document);return r("jQuery.sub() is deprecated"),t},e.ajaxSetup({converters:{"text json":e.parseJSON}});var b=e.fn.data;e.fn.data=function(t){var a,i,o=this[0];return!o||"events"!==t||1!==arguments.length||(a=e.data(o,t),i=e._data(o,t),a!==n&&a!==i||i===n)?b.apply(this,arguments):(r("Use of jQuery.fn.data('events') is deprecated"),i)};var j=/\/(java|ecma)script/i,w=e.fn.andSelf||e.fn.addBack;e.fn.andSelf=function(){return r("jQuery.fn.andSelf() replaced by jQuery.fn.addBack()"),w.apply(this,arguments)},e.clean||(e.clean=function(t,a,i,o){a=a||document,a=!a.nodeType&&a[0]||a,a=a.ownerDocument||a,r("jQuery.clean() is deprecated");var s,u,c,l,d=[];if(e.merge(d,e.buildFragment(t,a).childNodes),i)for(c=function(e){return!e.type||j.test(e.type)?o?o.push(e.parentNode?e.parentNode.removeChild(e):e):i.appendChild(e):n},s=0;null!=(u=d[s]);s++)e.nodeName(u,"script")&&c(u)||(i.appendChild(u),u.getElementsByTagName!==n&&(l=e.grep(e.merge([],u.getElementsByTagName("script")),c),d.splice.apply(d,[s+1,0].concat(l)),s+=l.length));return d});var Q=e.event.add,x=e.event.remove,k=e.event.trigger,N=e.fn.toggle,T=e.fn.live,M=e.fn.die,S="ajaxStart|ajaxStop|ajaxSend|ajaxComplete|ajaxError|ajaxSuccess",C=RegExp("\\b(?:"+S+")\\b"),H=/(?:^|\s)hover(\.\S+|)\b/,A=function(t){return"string"!=typeof t||e.event.special.hover?t:(H.test(t)&&r("'hover' pseudo-event is deprecated, use 'mouseenter mouseleave'"),t&&t.replace(H,"mouseenter$1 mouseleave$1"))};e.event.props&&"attrChange"!==e.event.props[0]&&e.event.props.unshift("attrChange","attrName","relatedNode","srcElement"),e.event.dispatch&&a(e.event,"handle",e.event.dispatch,"jQuery.event.handle is undocumented and deprecated"),e.event.add=function(e,t,n,a,i){e!==document&&C.test(t)&&r("AJAX events should be attached to document: "+t),Q.call(this,e,A(t||""),n,a,i)},e.event.remove=function(e,t,n,r,a){x.call(this,e,A(t)||"",n,r,a)},e.fn.error=function(){var e=Array.prototype.slice.call(arguments,0);return r("jQuery.fn.error() is deprecated"),e.splice(0,0,"error"),arguments.length?this.bind.apply(this,e):(this.triggerHandler.apply(this,e),this)},e.fn.toggle=function(t,n){if(!e.isFunction(t)||!e.isFunction(n))return N.apply(this,arguments);r("jQuery.fn.toggle(handler, handler...) is deprecated");var a=arguments,i=t.guid||e.guid++,o=0,s=function(n){var r=(e._data(this,"lastToggle"+t.guid)||0)%o;return e._data(this,"lastToggle"+t.guid,r+1),n.preventDefault(),a[r].apply(this,arguments)||!1};for(s.guid=i;a.length>o;)a[o++].guid=i;return this.click(s)},e.fn.live=function(t,n,a){return r("jQuery.fn.live() is deprecated"),T?T.apply(this,arguments):(e(this.context).on(t,this.selector,n,a),this)},e.fn.die=function(t,n){return r("jQuery.fn.die() is deprecated"),M?M.apply(this,arguments):(e(this.context).off(t,this.selector||"**",n),this)},e.event.trigger=function(e,t,n,a){return n||C.test(e)||r("Global events are undocumented and deprecated"),k.call(this,e,t,n||document,a)},e.each(S.split("|"),function(t,n){e.event.special[n]={setup:function(){var t=this;return t!==document&&(e.event.add(document,n+"."+e.guid,function(){e.event.trigger(n,null,t,!0)}),e._data(this,n,e.guid++)),!1},teardown:function(){return this!==document&&e.event.remove(document,n+"."+e._data(this,n)),!1}}})}(jQuery,window); -------------------------------------------------------------------------------- /web/assets/js/jquery.simplyscroll.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * simplyScroll 2 - a scroll-tastic jQuery plugin 3 | * 4 | * http://logicbox.net/jquery/simplyscroll/ 5 | * 6 | * Copyright (c) 2009-2012 Will Kelly - http://logicbox.net 7 | * 8 | * Dual licensed under the MIT and GPL licenses. 9 | * 10 | * Version: 2.0.5 Last revised: 10/05/2012 11 | * 12 | */ 13 | (function(c,j,i){c.fn.simplyScroll=function(a){return this.each(function(){new c.simplyScroll(this,a)})};var h={customClass:"simply-scroll",frameRate:24,speed:1,orientation:"horizontal",auto:!0,autoMode:"loop",manualMode:"end",direction:"forwards",pauseOnHover:!0,pauseOnTouch:!0,pauseButton:!1,startOnLoad:!1};c.simplyScroll=function(a,b){var g=this;this.o=c.extend({},h,b||{});this.isAuto=!1!==this.o.auto&&null!==this.o.autoMode.match(/^loop|bounce$/);this.isRTL=(this.isHorizontal=null!==this.o.orientation.match(/^horizontal|vertical$/)&& 14 | this.o.orientation==h.orientation)&&"rtl"==c("html").attr("dir");this.isForwards=!this.isAuto||this.isAuto&&(null!==this.o.direction.match(/^forwards|backwards$/)&&this.o.direction==h.direction)&&!this.isRTL;this.isLoop=this.isAuto&&"loop"==this.o.autoMode||!this.isAuto&&"loop"==this.o.manualMode;this.events=(this.supportsTouch="createTouch"in document)?{start:"touchstart MozTouchDown",move:"touchmove MozTouchMove",end:"touchend touchcancel MozTouchRelease"}:{start:"mouseenter",end:"mouseleave"}; 15 | this.$list=c(a);var d=this.$list.children();this.$list.addClass("simply-scroll-list").wrap('
').parent().wrap('
');this.isAuto?this.o.pauseButton&&(this.$list.parent().parent().prepend('
'),this.o.pauseOnHover=!1):this.$list.parent().parent().prepend('
').prepend('
'); 16 | if(1").parent().addClass("simply-scroll-list"),this.isHorizontal?this.$list.children().css({"float":"left",width:e+"px"}):this.$list.children().css({height:e+"px"}))}this.o.startOnLoad?c(j).load(function(){g.init()}):this.init()};c.simplyScroll.fn= 17 | c.simplyScroll.prototype={};c.simplyScroll.fn.extend=c.simplyScroll.extend=c.extend;c.simplyScroll.fn.extend({init:function(){this.$items=this.$list.children();this.$clip=this.$list.parent();this.$container=this.$clip.parent();this.$btnBack=c(".simply-scroll-back",this.$container);this.$btnForward=c(".simply-scroll-forward",this.$container);this.isHorizontal?(this.itemMax=this.$items.eq(0).outerWidth(!0),this.clipMax=this.$clip.width(),this.dimension="width",this.moveBackClass="simply-scroll-btn-left", 18 | this.moveForwardClass="simply-scroll-btn-right",this.scrollPos="Left"):(this.itemMax=this.$items.eq(0).outerHeight(!0),this.clipMax=this.$clip.height(),this.dimension="height",this.moveBackClass="simply-scroll-btn-up",this.moveForwardClass="simply-scroll-btn-down",this.scrollPos="Top");this.posMin=0;this.posMax=this.$items.length*this.itemMax;var a=Math.ceil(this.clipMax/this.itemMax);if(this.isAuto&&"loop"==this.o.autoMode)this.$list.css(this.dimension,this.posMax+this.itemMax*a+"px"),this.posMax+= 19 | this.clipMax-this.o.speed,this.isForwards?(this.$items.slice(0,a).clone(!0).appendTo(this.$list),this.resetPosition=0):(this.$items.slice(-a).clone(!0).prependTo(this.$list),this.resetPosition=this.$items.length*this.itemMax,this.isRTL&&(this.$clip[0].dir="ltr",this.$items.css("float","right")));else if(!this.isAuto&&"loop"==this.o.manualMode){this.posMax+=this.itemMax*a;this.$list.css(this.dimension,this.posMax+this.itemMax*a+"px");this.posMax+=this.clipMax-this.o.speed;this.$items.slice(0,a).clone(!0).appendTo(this.$list); 20 | this.$items.slice(-a).clone(!0).prependTo(this.$list);this.resetPositionForwards=this.resetPosition=a*this.itemMax;this.resetPositionBackwards=this.$items.length*this.itemMax;var b=this;this.$btnBack.bind(this.events.start,function(){b.isForwards=false;b.resetPosition=b.resetPositionBackwards});this.$btnForward.bind(this.events.start,function(){b.isForwards=true;b.resetPosition=b.resetPositionForwards})}else this.$list.css(this.dimension,this.posMax+"px"),this.isForwards?this.resetPosition=0:(this.resetPosition= 21 | this.$items.length*this.itemMax,this.isRTL&&(this.$clip[0].dir="ltr",this.$items.css("float","right")));this.resetPos();this.interval=null;this.intervalDelay=Math.floor(1E3/this.o.frameRate);if(this.isAuto||"end"!=this.o.manualMode)for(;0!==this.itemMax%this.o.speed;)if(this.o.speed--,0===this.o.speed){this.o.speed=1;break}b=this;this.trigger=null;this.funcMoveBack=function(a){a!==i&&a.preventDefault();b.trigger=!b.isAuto&&b.o.manualMode=="end"?this:null;b.isAuto?b.isForwards?b.moveBack():b.moveForward(): 22 | b.moveBack()};this.funcMoveForward=function(a){a!==i&&a.preventDefault();b.trigger=!b.isAuto&&b.o.manualMode=="end"?this:null;b.isAuto?b.isForwards?b.moveForward():b.moveBack():b.moveForward()};this.funcMovePause=function(){b.movePause()};this.funcMoveStop=function(){b.moveStop()};this.funcMoveResume=function(){b.moveResume()};if(this.isAuto){this.paused=!1;var g=function(){if(b.paused===false){b.paused=true;b.funcMovePause()}else{b.paused=false;b.funcMoveResume()}return b.paused};this.supportsTouch&& 23 | this.$items.find("a").length&&(this.supportsTouch=!1);if(this.isAuto&&this.o.pauseOnHover&&!this.supportsTouch)this.$clip.bind(this.events.start,this.funcMovePause).bind(this.events.end,this.funcMoveResume);else if(this.isAuto&&this.o.pauseOnTouch&&!this.o.pauseButton&&this.supportsTouch){var d,f;this.$clip.bind(this.events.start,function(a){g();var c=a.originalEvent.touches[0];d=b.isHorizontal?c.pageX:c.pageY;f=b.$clip[0]["scroll"+b.scrollPos];a.stopPropagation();a.preventDefault()}).bind(this.events.move, 24 | function(a){a.stopPropagation();a.preventDefault();a=a.originalEvent.touches[0];a=d-(b.isHorizontal?a.pageX:a.pageY)+f;if(a<0)a=0;else if(a>b.posMax)a=b.posMax;b.$clip[0]["scroll"+b.scrollPos]=a;b.funcMovePause();b.paused=true})}else this.o.pauseButton&&(this.$btnPause=c(".simply-scroll-btn-pause",this.$container).bind("click",function(a){a.preventDefault();g()?c(this).addClass("active"):c(this).removeClass("active")}));this.funcMoveForward()}else this.$btnBack.addClass("simply-scroll-btn "+this.moveBackClass).bind(this.events.start, 25 | this.funcMoveBack).bind(this.events.end,this.funcMoveStop),this.$btnForward.addClass("simply-scroll-btn "+this.moveForwardClass).bind(this.events.start,this.funcMoveForward).bind(this.events.end,this.funcMoveStop),"end"==this.o.manualMode&&(!this.isRTL?this.$btnBack.addClass("disabled"):this.$btnForward.addClass("disabled"))},moveForward:function(){var a=this;this.movement="forward";null!==this.trigger&&this.$btnBack.removeClass("disabled");a.interval=setInterval(function(){a.$clip[0]["scroll"+a.scrollPos]< 26 | a.posMax-a.clipMax?a.$clip[0]["scroll"+a.scrollPos]+=a.o.speed:a.isLoop?a.resetPos():a.moveStop(a.movement)},a.intervalDelay)},moveBack:function(){var a=this;this.movement="back";null!==this.trigger&&this.$btnForward.removeClass("disabled");a.interval=setInterval(function(){a.$clip[0]["scroll"+a.scrollPos]>a.posMin?a.$clip[0]["scroll"+a.scrollPos]-=a.o.speed:a.isLoop?a.resetPos():a.moveStop(a.movement)},a.intervalDelay)},movePause:function(){clearInterval(this.interval)},moveStop:function(a){this.movePause(); 27 | null!==this.trigger&&("undefined"!==typeof a&&c(this.trigger).addClass("disabled"),this.trigger=null);this.isAuto&&"bounce"==this.o.autoMode&&("forward"==a?this.moveBack():this.moveForward())},moveResume:function(){"forward"==this.movement?this.moveForward():this.moveBack()},resetPos:function(){this.$clip[0]["scroll"+this.scrollPos]=this.resetPosition}})})(jQuery,window); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [go-md-book](https://github.com/hedongshu/go-md-book) 2 | 3 | > 这是一款可以快捷的把 md 文件发布成 web 页面 的程序 4 | > ![light](https://github.com/hedongshu/go-markdown-book/blob/main/light.jpg?raw=true) > ![dark](https://github.com/hedongshu/go-markdown-book/blob/main/dark.jpg?raw=true) 5 | 6 | ## 支持平台 7 | 8 | > Windows、Linux、Mac OS 9 | 10 | ## 安装 11 | 12 | 1. 下载 [release](https://github.com/hedongshu/go-md-book/releases) 13 | 14 | 2. 解压 15 | 16 | ``` 17 | tar zxf markdown-book-V1.0.0-darwin-amd64.tar.gz 18 | ``` 19 | 20 | 3. 创建 markdown-book 文件目录 21 | 22 | ``` 23 | cd markdown-book-linux-amd64 24 | mkdir md && mkdir md/技术 md/生活 25 | echo "### Hello World" > .md/技术/主页.md 26 | echo "### Hello World" > .md/生活/主页.md 27 | ``` 28 | 29 | 4. 运行 30 | 31 | ``` 32 | ./markdown-book web 33 | ``` 34 | 35 | 5. 访问 http://127.0.0.1:5006,查看效果 36 | 37 | ## 升级 38 | 39 | 1. 下载最新版 [release](https://github.com/hedongshu/go-md-book/releases) 40 | 41 | 2. 停止程序,解压替换 `markdown-book` 42 | 43 | 3. 重新启动程序 44 | 45 | ## 使用 46 | 47 | ### 命令 48 | 49 | - markdown-book 50 | - -h 查看版本 51 | - web 运行博客服务 52 | - markdown-book web 53 | - --config FILE 加载配置文件, 54 | - --origin value, -o value 指定数据源,可选:github,file,默认:file 55 | - --dir value, -d value 指定 markdown 文件夹,默认:./md/ 56 | - --title value, -t value web 服务标题,默认:"Blog Title" 57 | - --title2 value, -t2 value web 副标题,默认:"Blog Title2" 58 | - --port value, -p value web 服务端口,默认:5006 59 | - --env value, -e value 运行环境, 可选:dev,test,prod,默认:"prod" 60 | - --cache value, -c value 设置页面缓存时间,单位分钟,默认 3 分钟 61 | - --github.owner 设置 GitHub 数据源的用户名 62 | - --github.repo 设置 GitHub 数据源的仓库名 63 | - --analyzer-baidu value 设置百度分析统计器 64 | - --analyzer-google value 设置谷歌分析统计器 65 | - --gitalk.client-id value 设置 Gitalk ClientId, 默认为空 66 | - --gitalk.client-secret value 设置 Gitalk ClientSecret, 默认为空 67 | - --gitalk.repo value 设置 Gitalk Repo, 默认为空 68 | - --gitalk.owner value 设置 Gitalk Owner, 默认为空 69 | - --gitalk.admin 设置 Gitalk Admin, 默认为数组 [gitalk.owner] 70 | - --gitalk.labels 设置 Gitalk Admin, 默认为数组 ["gitalk"] 71 | - --ignore-file value 设置忽略文件, eg: demo.md 72 | - --ignore-path value 设置忽略文件夹, eg: demo 73 | - -h 查看版本 74 | 75 | ### 运行参数 76 | 77 | > 支持从配置文件读取配置项,不过运行时指定参数优先于配置文件,配置内容参考 `config/config.yml.tmp` 78 | 79 | ### 配置文件 80 | 81 | 1. 新建配置文件 `config/config.yml` 82 | 83 | 2. 启动时加载配置文件 84 | 85 | - 二进制文件 86 | 87 | ``` 88 | ./markdown-book web --config ./config/config.yml 89 | ``` 90 | 91 | ### 设置数据源 92 | 93 | > 支持使用 github 或者本地的文件夹作为文章的数据源,设置 origin 为 `github` or file` 94 | 95 | #### 使用 GitHub 为数据源 96 | 97 | ```yaml 98 | origin: github 99 | github: 100 | owner: "github 用户名" 101 | repo: "github 仓库名" 102 | ``` 103 | 104 | 假如你有一个 github 的仓库,里面存了你的 markdown 文档,你只需要把用户名和仓库名填到配置文件里。 105 | 使用配置启动之后,会自动读取一次你的 github 仓库,把所有的文档加载到 go 里,后续如果你有 md 文件的改动,只需要访问一次 106 | 107 | > http://127.0.0.1:5006/update 108 | > 就可以完成数据的更新 109 | 110 | - Docker 111 | 112 | ``` 113 | docker run -dit --rm --name=markdown-book \ 114 | -p 5006:5006 \ 115 | -v $(pwd)/md:/md -v $(pwd)/cache:/cache -v $(pwd)/config:/config \ 116 | hedongshu/markdown-book:latest --config ./config/config.yml 117 | ``` 118 | 119 | ### 评论插件 120 | 121 | > 评论插件使用的是 **Gitalk**,在使用前请阅读插件使用说明 [English](https://github.com/gitalk/gitalk/blob/master/readme.md) | [中文](https://github.com/gitalk/gitalk/blob/master/readme-cn.md) 122 | 123 | #### 新增 `gitalk` 配置项,启动时加载配置文件即可 124 | 125 | ```yaml 126 | gitalk: 127 | client-id: "你的 github oauth app client-id,必填。 如: ad549a9d085d7f5736d3" 128 | client-secret: "你的 github oauth app client-secret,必填。 如: 510d1a6bb875fd5031f0d613cd606b1d" 129 | repo: "你准备用于评论的项目名称,必填。 如: blog-issue" 130 | owner: "你的Github账号,必填。" 131 | admin: 132 | - "你的Github账号" 133 | labels: 134 | - "自定义issue标签,如: gitalk" 135 | ``` 136 | 137 | ### 分析统计器 138 | 139 | #### 百度 140 | 141 | ##### 1. 访问 https://tongji.baidu.com 创建站点,获取官方代码中的参数 `0952befd5b7da358ad12fae3437515b1` 142 | 143 | ```html 144 | 153 | ``` 154 | 155 | ##### 2. 配置 156 | 157 | ```shell 158 | ./markdown-book web --analyzer-baidu 0952befd5b7da358ad12fae3437515b1 159 | ``` 160 | 161 | #### 谷歌 162 | 163 | ##### 1. 访问 https://analytics.google.com 创建站点,获取官方代码中的参数 `G-MYSMYSMYS` 164 | 165 | ```html 166 | 170 | 179 | ``` 180 | 181 | ##### 2. 配置 182 | 183 | ```shell 184 | ./markdown-book web --analyzer-google G-MYSMYSMYS 185 | ``` 186 | 187 | ### 标题栏图标 188 | 189 | > 默认读取与程序运行同一级目录的 **favicon.ico** 文件 190 | 191 | 194 | 195 | ## 开发 196 | 197 | 1. 安装 `Golang` 开发环境 198 | 199 | 2. Fork [源码](https://github.com/gaowei-space/gocron) 200 | 201 | 3. 启动 web 服务 202 | 203 | 运行之后访问地址 [http://localhost:5006](http://localhost:5006),API 请求会转发给 `markdown-book` 程序 204 | 205 | ``` 206 | make run 207 | ``` 208 | 209 | 4. 编译 210 | 211 | 在 **bin** 目录生成当前系统的压缩包,如:markdown-book-v1.1.0-darwin-amd64.tar 212 | 213 | ``` 214 | make 215 | ``` 216 | 217 | 5. 打包 218 | 219 | 在 **package** 目录生成当前系统的压缩包,如:markdown-book-v1.1.0-darwin-amd64.tar 220 | 221 | ``` 222 | make package 223 | ``` 224 | 225 | 6. 生成 Windows、Linux、Mac 的压缩包 226 | 227 | 在 **package** 生成压缩包,如:markdown-book-v1.1.0-darwin-amd64.tar markdown-book-v1.1.0-linux-amd64.tar.gz markdown-book-v1.1.0-windows-amd64.zip 228 | 229 | ``` 230 | make package-all 231 | ``` 232 | 233 | ### Docker 234 | 235 | 7. 下载 236 | 237 | ``` 238 | docker pull hedongshu/markdown-book:latest 239 | ``` 240 | 241 | 2. 启动 242 | 243 | - 线上环境 244 | 245 | ``` 246 | docker run -dit --rm --name=markdown-book \ 247 | -p 5006:5006 \ 248 | -v $(pwd)/md:/md -v $(pwd)/cache:/cache \ 249 | hedongshu/markdown-book:latest 250 | ``` 251 | 252 | - 开发环境 253 | 254 | ``` 255 | docker run -dit --rm --name=markdown-book \ 256 | -p 5006:5006 \ 257 | -v $(pwd)/md:/md -v $(pwd)/cache:/cache \ 258 | hedongshu/markdown-book:latest \ 259 | -e dev 260 | ``` 261 | 262 | 3. 访问 http://127.0.0.1:5006,查看效果 263 | 264 | 4. 其他用法 265 | 266 | ``` 267 | # 查看帮助 268 | docker run -dit --rm --name=markdown-book \ 269 | -p 5006:5006 \ 270 | -v $(pwd)/md:/md -v $(pwd)/cache:/cache \ 271 | hedongshu/markdown-book:latest -h 272 | 273 | 274 | # 设置 title 275 | docker run -dit --rm --name=markdown-book \ 276 | -p 5006:5006 \ 277 | -v $(pwd)/md:/md -v $(pwd)/cache:/cache \ 278 | hedongshu/markdown-book:latest \ 279 | -t "TechMan'Blog" 280 | 281 | 282 | # 设置 谷歌统计 283 | docker run -dit --rm --name=markdown-book \ 284 | -p 5006:5006 \ 285 | -v $(pwd)/md:/md -v $(pwd)/cache:/cache \ 286 | hedongshu/markdown-book:latest \ 287 | -t "TechMan'Blog" \ 288 | --analyzer-google "De44AJSLDdda" 289 | ``` 290 | 291 | ## 部署 292 | 293 | > Nginx 反向代理配置文件参考 294 | 295 | #### HTTP 协议 296 | 297 | ``` 298 | server { 299 | listen 80; 300 | listen [::]:80; 301 | server_name yourhost.com; 302 | 303 | location / { 304 | proxy_pass http://127.0.0.1:5006; 305 | proxy_set_header Host $host; 306 | proxy_set_header X-Real-IP $remote_addr; 307 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 308 | } 309 | } 310 | ``` 311 | 312 | #### HTTPS 协议(80 端口自动跳转至 443) 313 | 314 | ``` 315 | server { 316 | listen 80; 317 | listen [::]:80; 318 | server_name yourhost.com; 319 | 320 | location / { 321 | rewrite ^ https://$host$request_uri? permanent; 322 | } 323 | } 324 | 325 | server { 326 | listen 443 ssl; 327 | server_name yourhost.com; 328 | access_log /var/log/nginx/markdown-book.access.log main; 329 | 330 | 331 | #证书文件名称 332 | ssl_certificate /etc/nginx/certs/yourhost.com_bundle.crt; 333 | #私钥文件名称 334 | ssl_certificate_key /etc/ngpackinx/certs/yourhost.com.key; 335 | ssl_session_timeout 5m; 336 | ssl_protocols TLSv1.2 TLSv1.3; 337 | ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; 338 | ssl_prefer_server_ciphers on; 339 | 340 | location / { 341 | proxy_pass http://127.0.0.1:5006; 342 | proxy_set_header Host $host; 343 | proxy_set_header X-Real-IP $remote_addr; 344 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 345 | } 346 | } 347 | ``` 348 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= 2 | github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 3 | github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c= 4 | github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= 5 | github.com/CloudyKit/jet/v6 v6.1.0 h1:hvO96X345XagdH1fAoBjpBYG4a1ghhL/QzalkduPuXk= 6 | github.com/CloudyKit/jet/v6 v6.1.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4= 7 | github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc= 8 | github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U= 9 | github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI= 10 | github.com/Shopify/goreferrer v0.0.0-20210630161223-536fa16abd6f h1:XeOBnoBP7K19tMBEKeUo1NOxOO+h5FFi2HGzQvvkb44= 11 | github.com/Shopify/goreferrer v0.0.0-20210630161223-536fa16abd6f/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= 12 | github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= 13 | github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= 14 | github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= 15 | github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= 16 | github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= 17 | github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= 18 | github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= 19 | github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= 20 | github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= 21 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 22 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 23 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 24 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 25 | github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE= 26 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 27 | github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o= 28 | github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= 29 | github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= 30 | github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= 31 | github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw= 32 | github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= 33 | github.com/fsnotify/fsnotify v1.5.3/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= 34 | github.com/goccy/go-json v0.9.8-0.20220506185958-23bd66f4c0d5 h1:aeyOtISssR4sP36FAC9LV96PQqxzcbhz54EWv9U+ZGc= 35 | github.com/goccy/go-json v0.9.8-0.20220506185958-23bd66f4c0d5/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 36 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 37 | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 38 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 39 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 40 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 41 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 42 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 43 | github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= 44 | github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= 45 | github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= 46 | github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 47 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 48 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 49 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 50 | github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= 51 | github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= 52 | github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= 53 | github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= 54 | github.com/iris-contrib/httpexpect/v2 v2.3.1 h1:A69ilxKGW1jDRKK5UAhjTL4uJYh3RjD4qzt9vNZ7fpY= 55 | github.com/iris-contrib/jade v1.1.4 h1:WoYdfyJFfZIUgqNAeOyRfTNQZOksSlZ6+FnXR3AEpX0= 56 | github.com/iris-contrib/jade v1.1.4/go.mod h1:EDqR+ur9piDl6DUgs6qRrlfzmlx/D5UybogqrXvJTBE= 57 | github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw= 58 | github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA= 59 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 60 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 61 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 62 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 63 | github.com/kataras/blocks v0.0.5 h1:jFrsHEDfXZhHTbhkNWgMgpfEQNj1Bwr1IYEYZ9Xxoxg= 64 | github.com/kataras/blocks v0.0.5/go.mod h1:kcJIuvuA8QmGKFLHIZHdCAPCjcE85IhttzXd6W+ayfE= 65 | github.com/kataras/golog v0.1.7 h1:0TY5tHn5L5DlRIikepcaRR/6oInIr9AiWsxzt0vvlBE= 66 | github.com/kataras/golog v0.1.7/go.mod h1:jOSQ+C5fUqsNSwurB/oAHq1IFSb0KI3l6GMa7xB6dZA= 67 | github.com/kataras/iris/v12 v12.2.0-beta2 h1:pWKzWKYMm8hz9x7fpzl1QD8Ip0pACs2tfm7P9FJ9AMY= 68 | github.com/kataras/iris/v12 v12.2.0-beta2/go.mod h1:cBLFT5zArhTQikV76/hyK3gZzDPF2uumNwH86dj+XC4= 69 | github.com/kataras/pio v0.0.10 h1:b0qtPUqOpM2O+bqa5wr2O6dN4cQNwSmFd6HQqgVae0g= 70 | github.com/kataras/pio v0.0.10/go.mod h1:gS3ui9xSD+lAUpbYnjOGiQyY7sUMJO+EHpiRzhtZ5no= 71 | github.com/kataras/sitemap v0.0.5 h1:4HCONX5RLgVy6G4RkYOV3vKNcma9p236LdGOipJsaFE= 72 | github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= 73 | github.com/kataras/tunnel v0.0.3 h1:+8eHXujPD3wLnqTbYtPGa/3/Jc+Eq+bsPwEGTeFBB00= 74 | github.com/kataras/tunnel v0.0.3/go.mod h1:VOlCoaUE5zN1buE+yAjWCkjfQ9hxGuhomKLsjei/5Zs= 75 | github.com/klauspost/compress v1.15.3 h1:wmfu2iqj9q22SyMINp1uQ8C2/V4M1phJdmH9fG4nba0= 76 | github.com/klauspost/compress v1.15.3/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= 77 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 78 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 79 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 80 | github.com/mailgun/raymond/v2 v2.0.46 h1:aOYHhvTpF5USySJ0o7cpPno/Uh2I5qg2115K25A+Ft4= 81 | github.com/mailgun/raymond/v2 v2.0.46/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18= 82 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 83 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 84 | github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= 85 | github.com/microcosm-cc/bluemonday v1.0.21 h1:dNH3e4PSyE4vNX+KlRGHT5KrSvjeUkoNPwEORjffHJg= 86 | github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM= 87 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 88 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 89 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 90 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 91 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 92 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 93 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 94 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 95 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 96 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 97 | github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk= 98 | github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= 99 | github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= 100 | github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= 101 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 102 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 103 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 104 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 105 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 106 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 107 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 108 | github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= 109 | github.com/tdewolff/minify/v2 v2.11.2 h1:PpaPWhNlMVjkAKaOj0bbPv6KCVnrm8jbVwG7OtSdAqw= 110 | github.com/tdewolff/minify/v2 v2.11.2/go.mod h1:NxozhBtgUVypPLzQdV96wkIu9J9vAiVmBcKhfC2zMfg= 111 | github.com/tdewolff/parse/v2 v2.5.29 h1:Uf0OtZL9YaUXTuHEOitdo9lD90P0XTwCjZi+KbGChuM= 112 | github.com/tdewolff/parse/v2 v2.5.29/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho= 113 | github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4= 114 | github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= 115 | github.com/urfave/cli/v2 v2.23.7 h1:YHDQ46s3VghFHFf1DdF+Sh7H4RqhcM+t0TmZRJx4oJY= 116 | github.com/urfave/cli/v2 v2.23.7/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= 117 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 118 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 119 | github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= 120 | github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= 121 | github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= 122 | github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= 123 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= 124 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= 125 | github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= 126 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= 127 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= 128 | github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY= 129 | github.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA= 130 | github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0= 131 | github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= 132 | github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= 133 | golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= 134 | golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= 135 | golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 136 | golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= 137 | golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= 138 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 139 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 140 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 141 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 142 | golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= 143 | golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 144 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 145 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 146 | golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= 147 | golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 148 | golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= 149 | golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 150 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 151 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 152 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 153 | google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= 154 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 155 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 156 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= 157 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 158 | gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= 159 | gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 160 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 161 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 162 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 163 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 164 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 165 | moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8= 166 | -------------------------------------------------------------------------------- /web/assets/css/style.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css?family=Source+Sans+Pro:100,300,400,600"); 2 | @keyframes fadein { 3 | from { 4 | opacity: 0; 5 | } 6 | to { 7 | opacity: 1; 8 | } 9 | } 10 | /* Firefox < 16 */ 11 | @-moz-keyframes fadein { 12 | from { 13 | opacity: 0; 14 | } 15 | to { 16 | opacity: 1; 17 | } 18 | } 19 | /* Safari, Chrome and Opera > 12.1 */ 20 | @-webkit-keyframes fadein { 21 | from { 22 | opacity: 0; 23 | } 24 | to { 25 | opacity: 1; 26 | } 27 | } 28 | /* Internet Explorer */ 29 | @-ms-keyframes fadein { 30 | from { 31 | opacity: 0; 32 | } 33 | to { 34 | opacity: 1; 35 | } 36 | } 37 | /* Opera < 12.1 */ 38 | @-o-keyframes fadein { 39 | from { 40 | opacity: 0; 41 | } 42 | to { 43 | opacity: 1; 44 | } 45 | } 46 | @keyframes fadein-right { 47 | from { 48 | opacity: 0; 49 | transform: translateX(20px); 50 | } 51 | to { 52 | opacity: 1; 53 | transform: translateX(0px); 54 | } 55 | } 56 | /* Firefox < 16 */ 57 | @-moz-keyframes fadein-right { 58 | from { 59 | opacity: 0; 60 | transform: translateX(20px); 61 | } 62 | to { 63 | opacity: 1; 64 | transform: translateX(0px); 65 | } 66 | } 67 | /* Safari, Chrome and Opera > 12.1 */ 68 | @-webkit-keyframes fadein-right { 69 | from { 70 | opacity: 0; 71 | transform: translateX(20px); 72 | } 73 | to { 74 | opacity: 1; 75 | transform: translateX(0px); 76 | } 77 | } 78 | /* Internet Explorer */ 79 | @-ms-keyframes fadein-right { 80 | from { 81 | opacity: 0; 82 | transform: translateX(20px); 83 | } 84 | to { 85 | opacity: 1; 86 | transform: translateX(0px); 87 | } 88 | } 89 | /* Opera < 12.1 */ 90 | @-o-keyframes fadein-right { 91 | from { 92 | opacity: 0; 93 | transform: translateX(20px); 94 | } 95 | to { 96 | opacity: 1; 97 | transform: translateX(0px); 98 | } 99 | } 100 | @keyframes fadein-top { 101 | from { 102 | opacity: 0; 103 | transform: translateY(-20px); 104 | } 105 | to { 106 | opacity: 1; 107 | transform: translateY(0px); 108 | } 109 | } 110 | /* Firefox < 16 */ 111 | @-moz-keyframes fadein-top { 112 | from { 113 | opacity: 0; 114 | transform: translateY(-20px); 115 | } 116 | to { 117 | opacity: 1; 118 | transform: translateY(0px); 119 | } 120 | } 121 | /* Safari, Chrome and Opera > 12.1 */ 122 | @-webkit-keyframes fadein-top { 123 | from { 124 | opacity: 0; 125 | transform: translateY(-20px); 126 | } 127 | to { 128 | opacity: 1; 129 | transform: translateY(0px); 130 | } 131 | } 132 | /* Internet Explorer */ 133 | @-ms-keyframes fadein-top { 134 | from { 135 | opacity: 0; 136 | transform: translateY(-20px); 137 | } 138 | to { 139 | opacity: 1; 140 | transform: translateY(0px); 141 | } 142 | } 143 | /* Opera < 12.1 */ 144 | @-o-keyframes fadein-top { 145 | from { 146 | opacity: 0; 147 | transform: translateY(-20px); 148 | } 149 | to { 150 | opacity: 1; 151 | transform: translateY(0px); 152 | } 153 | } 154 | @keyframes swift-down { 155 | from { 156 | transform: translateY(0%); 157 | } 158 | to { 159 | transform: translateY(20%); 160 | } 161 | } 162 | /* Firefox < 16 */ 163 | @-moz-keyframes swift-down { 164 | from { 165 | transform: translateY(0%); 166 | } 167 | to { 168 | transform: translateY(20%); 169 | } 170 | } 171 | /* Safari, Chrome and Opera > 12.1 */ 172 | @-webkit-keyframes swift-down { 173 | from { 174 | transform: translateY(0%); 175 | } 176 | to { 177 | transform: translateY(20%); 178 | } 179 | } 180 | /* Internet Explorer */ 181 | @-ms-keyframes swift-down { 182 | from { 183 | transform: translateY(0%); 184 | } 185 | to { 186 | transform: translateY(20%); 187 | } 188 | } 189 | /* Opera < 12.1 */ 190 | @-o-keyframes swift-down { 191 | from { 192 | transform: translateY(0%); 193 | } 194 | to { 195 | transform: translateY(20%); 196 | } 197 | } 198 | .invisible { 199 | opacity: 0; 200 | } 201 | 202 | .fadeIn { 203 | -webkit-animation: fadein 1s ease-out; /* Safari, Chrome and Opera > 12.1 */ 204 | -moz-animation: fadein 1s ease-out; /* Firefox < 16 */ 205 | -ms-animation: fadein 1s ease-out; /* Internet Explorer */ 206 | -o-animation: fadein 1s ease-out; /* Opera < 12.1 */ 207 | animation: fadein 1s ease-out; 208 | } 209 | 210 | .fadeInRight { 211 | -webkit-animation: fadein-right 1s ease-out; /* Safari, Chrome and Opera > 12.1 */ 212 | -moz-animation: fadein-right 1s ease-out; /* Firefox < 16 */ 213 | -ms-animation: fadein-right 1s ease-out; /* Internet Explorer */ 214 | -o-animation: fadein-right 1s ease-out; /* Opera < 12.1 */ 215 | animation: fadein-right 1s ease-out; 216 | } 217 | 218 | .fadeInTop { 219 | -webkit-animation: fadein-top 1s ease-out; /* Safari, Chrome and Opera > 12.1 */ 220 | -moz-animation: fadein-top 1s ease-out; /* Firefox < 16 */ 221 | -ms-animation: fadein-top 1s ease-out; /* Internet Explorer */ 222 | -o-animation: fadein-top 1s ease-out; /* Opera < 12.1 */ 223 | animation: fadein-top 1s ease-out; 224 | } 225 | 226 | .swiftDown { 227 | -webkit-animation: swift-down 1s ease-out; /* Safari, Chrome and Opera > 12.1 */ 228 | -moz-animation: swift-down 1s ease-out; /* Firefox < 16 */ 229 | -ms-animation: swift-down 1s ease-out; /* Internet Explorer */ 230 | -o-animation: swift-down 1s ease-out; /* Opera < 12.1 */ 231 | animation: swift-down 1s ease-out; 232 | } 233 | 234 | .swiftDownReversed { 235 | -webkit-animation: swift-down 1s reverse ease-out; /* Safari, Chrome and Opera > 12.1 */ 236 | -moz-animation: swift-down 1s reverse ease-out; /* Firefox < 16 */ 237 | -ms-animation: swift-down 1s reverse ease-out; /* Internet Explorer */ 238 | -o-animation: swift-down 1s reverse ease-out; /* Opera < 12.1 */ 239 | animation: swift-down 1s reverse ease-out; 240 | } 241 | 242 | html, 243 | body, 244 | span, 245 | p, 246 | h1, 247 | h2, 248 | h3, 249 | h4, 250 | h5, 251 | h6 { 252 | font-family: "Source Sans Pro", "Roboto", "Helvetica", "Helvetica Neue", "Source Han Sans SC", "Source Han Sans TC", "PingFang SC", "PingFang HK", "PingFang TC", sans-serif; 253 | color: #2e405b; 254 | } 255 | 256 | html { 257 | background: #ffffff; 258 | } 259 | 260 | .foreground { 261 | background: #2e405b !important; 262 | } 263 | 264 | .background { 265 | background: #ffffff !important; 266 | } 267 | 268 | .foreground-text * { 269 | color: #2e405b !important; 270 | } 271 | 272 | .background-text * { 273 | color: #ffffff !important; 274 | } 275 | 276 | h1 { 277 | font-size: 42px; 278 | } 279 | 280 | h2 { 281 | font-size: 36px; 282 | } 283 | 284 | h3 { 285 | font-size: 30px; 286 | } 287 | 288 | h4 { 289 | font-size: 24px; 290 | } 291 | 292 | h5 { 293 | font-size: 18px; 294 | } 295 | 296 | h6 { 297 | font-size: 14px; 298 | } 299 | 300 | body { 301 | font-size: 18px; 302 | line-height: 1.5em; 303 | background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACFJREFUeNpiZGBg4GegAsBlCD8TqSYNQg2Mo6FEBAAIMACdPABtrSW/IQAAAABJRU5ErkJggg=="); 304 | background-repeat: repeat; 305 | background-attachment: fixed; 306 | -webkit-font-smoothing: antialiased; 307 | -moz-osx-font-smoothing: grayscale; 308 | text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.04); 309 | min-height: 100vh; 310 | } 311 | 312 | .container { 313 | background-color: transparent; 314 | padding: 0; 315 | position: relative; 316 | } 317 | 318 | .full-width-container { 319 | width: 100vw; 320 | } 321 | 322 | .main-container { 323 | text-align: left; 324 | margin: 0; 325 | padding: 80px 40px; 326 | overflow: hidden; 327 | float: left; 328 | } 329 | .main-container :nth-child(1) { 330 | margin-top: 0; 331 | } 332 | 333 | .row { 334 | margin: 0; 335 | } 336 | 337 | .vertical-text { 338 | writing-mode: vertical-rl; 339 | } 340 | .vertical-text * { 341 | line-height: 1; 342 | text-align: left; 343 | } 344 | 345 | .side-container { 346 | height: 100vh; 347 | position: fixed; 348 | right: -100px; 349 | font-style: normal; 350 | font-size: 1em; 351 | margin: 0; 352 | transform: rotate(0deg); 353 | text-align: left; 354 | float: right; 355 | padding: 80px 20px; 356 | } 357 | 358 | .ar-year { 359 | margin-top: 5px; 360 | margin-bottom: 5px; 361 | } 362 | 363 | .date { 364 | margin-bottom: 0px; 365 | } 366 | 367 | .ar-date { 368 | font-size: 16px; 369 | margin-bottom: 5px; 370 | } 371 | 372 | .archive :first-child { 373 | padding-left: 0; 374 | } 375 | 376 | .archive ul { 377 | list-style-type: none; 378 | } 379 | 380 | .site-title { 381 | font-weight: bolder; 382 | font-style: normal; 383 | width: auto; 384 | height: auto; 385 | margin: 0 0 0 -2px; 386 | text-align: left; 387 | font-size: 0.5em; 388 | padding: 0 0 50px 0; 389 | border-left: solid #2e405b 2px; 390 | transition: padding 0.8s ease 0s, background 0.8s ease 0s; 391 | } 392 | .site-title:hover { 393 | padding: 15px 0 35px 0; 394 | color: #ffffff !important; 395 | background: #2e405b !important; 396 | border-left: solid #2e405b 2px; 397 | } 398 | .site-title:hover * { 399 | color: #ffffff !important; 400 | } 401 | .site-title :first-child { 402 | margin-right: 10px; 403 | } 404 | .site-title * { 405 | text-align: left; 406 | font-weight: bolder; 407 | margin-top: 0; 408 | margin-left: 10px; 409 | margin-right: 0; 410 | font-family: "HiraMinProN-W6", "Source Han Serif CN", "Source Han Serif SC", "Source Han Serif TC", serif; 411 | } 412 | 413 | .site-title-large { 414 | font-size: 50px; 415 | padding-top: 0; 416 | } 417 | 418 | .site-title-small { 419 | font-size: 30px; 420 | padding-top: 1px; 421 | } 422 | 423 | .site-title-links { 424 | font-weight: bold; 425 | font-style: normal; 426 | margin: 0; 427 | padding: 60px 0; 428 | text-align: left; 429 | position: fixed; 430 | bottom: 20px; 431 | } 432 | 433 | .site-title-links ul { 434 | list-style: none; 435 | padding: 0; 436 | } 437 | 438 | .site-title-links ul li { 439 | display: block; 440 | margin: 5px 0; 441 | font-size: 18px; 442 | } 443 | 444 | .site-title-links a { 445 | border-bottom: solid #2e405b 2px; 446 | } 447 | 448 | .no-margin-left { 449 | margin-left: 0; 450 | } 451 | 452 | .no-margin-right { 453 | margin-right: 0; 454 | } 455 | 456 | .no-margin-top { 457 | margin-top: 0; 458 | } 459 | 460 | .no-margin-bottom { 461 | margin-bottom: 0; 462 | } 463 | 464 | a { 465 | color: #2e405b; 466 | transition: color 0.3s ease 0s, background-color 0.3s ease 0s; 467 | -moz-transition: color 0.3s ease 0s, background-color 0.3s ease 0s; 468 | -o-transition: color 0.3s ease 0s, background-color 0.3s ease 0s; 469 | -webkit-transition: color 0.3s ease 0s, background-color 0.3s ease 0s; 470 | border-bottom: solid #2e405b 2px; 471 | padding: 0; 472 | cursor: pointer; 473 | cursor: hand; 474 | } 475 | a:hover, a:focus { 476 | text-decoration: none; 477 | } 478 | a:hover { 479 | color: #ffffff !important; 480 | background-color: #2e405b; 481 | border-bottom: solid transparent 2px; 482 | } 483 | a:focus { 484 | color: black; 485 | } 486 | 487 | .a-title { 488 | text-decoration: none; 489 | background-color: transparent; 490 | border-bottom: solid transparent 2px; 491 | } 492 | .a-title:hover, .a-title:focus { 493 | background-color: transparent; 494 | } 495 | 496 | .current { 497 | border-bottom: solid #2e405b 3px !important; 498 | } 499 | 500 | .a-mute { 501 | background-color: transparent; 502 | color: #2e405b !important; 503 | text-decoration: none; 504 | border-bottom: none; 505 | } 506 | .a-mute:hover, .a-mute:focus { 507 | background-color: transparent; 508 | color: #2e405b !important; 509 | text-decoration: none; 510 | border-bottom: none; 511 | } 512 | 513 | .soc { 514 | margin: 0 5px 0 0; 515 | font-size: 18px !important; 516 | } 517 | .soc a { 518 | border-bottom: solid transparent 2px; 519 | } 520 | .soc a:hover { 521 | color: #3f587d !important; 522 | background-color: transparent !important; 523 | text-decoration: none; 524 | border-bottom: solid rgba(0, 0, 0, 0) 2px; 525 | } 526 | .soc a:focus { 527 | color: #3f587d !important; 528 | } 529 | 530 | .meta-item { 531 | margin: 0 10px 0 0; 532 | } 533 | 534 | img { 535 | display: block; 536 | max-width: 100%; 537 | height: auto; 538 | } 539 | 540 | footer { 541 | margin-top: 10px; 542 | padding-top: 0; 543 | font-size: 12px; 544 | font-weight: bold; 545 | } 546 | 547 | footer * { 548 | margin: 0; 549 | } 550 | 551 | .post-container { 552 | margin: 0 0 30px 0; 553 | font-weight: normal; 554 | } 555 | .post-container .gist-file { 556 | border: none !important; 557 | } 558 | .post-container .gist-file .gist-data { 559 | background-color: rgba(0, 0, 0, 0) !important; 560 | } 561 | 562 | .post-title { 563 | font-size: 24px; 564 | font-weight: bold; 565 | margin: 3px 0; 566 | } 567 | 568 | .post-meta { 569 | font-size: 16px; 570 | margin: 10px 0; 571 | } 572 | 573 | .post-abstract { 574 | font-size: 18px; 575 | } 576 | 577 | li { 578 | margin: 5px 0; 579 | } 580 | 581 | figure { 582 | overflow: scroll; 583 | } 584 | 585 | figure > table { 586 | width: 100%; 587 | } 588 | 589 | pre * { 590 | font-family: Monaco, Menlo, "Source Code Pro", "Cascadia Code", Consolas, monospace; 591 | background: transparent !important; 592 | } 593 | 594 | figure .gutter pre { 595 | border-bottom-right-radius: 0; 596 | border-top-right-radius: 0; 597 | border-right: none; 598 | text-align: right; 599 | min-width: 3em; 600 | } 601 | figure .code { 602 | width: 99%; 603 | } 604 | figure .code pre { 605 | border-bottom-left-radius: 0; 606 | border-top-left-radius: 0; 607 | border-left: solid rgba(0, 0, 0, 0.05) 1px; 608 | margin-right: 1px; 609 | } 610 | 611 | .h4, 612 | .h5, 613 | .h6, 614 | h4, 615 | h5, 616 | h6 { 617 | margin-top: 20px; 618 | } 619 | 620 | @media (max-width: 768px) { 621 | body { 622 | line-height: normal; 623 | } 624 | h1 { 625 | font-size: 40px; 626 | } 627 | h2 { 628 | font-size: 34px; 629 | } 630 | h3 { 631 | font-size: 28px; 632 | } 633 | h4 { 634 | font-size: 22px; 635 | } 636 | h5 { 637 | font-size: 16px; 638 | } 639 | h6 { 640 | font-size: 14px; 641 | } 642 | .site-title-large { 643 | font-size: 38px; 644 | } 645 | .site-title-small { 646 | font-size: 22px; 647 | } 648 | .vertical-text { 649 | writing-mode: horizontal-tb; 650 | } 651 | .site-title { 652 | margin: 0; 653 | font-weight: bolder; 654 | font-style: normal; 655 | width: auto; 656 | height: auto; 657 | font-size: 0.5em; 658 | padding: 0; 659 | border-left: none; 660 | text-align: center; 661 | transition: none; 662 | } 663 | .site-title * { 664 | text-align: center; 665 | margin-left: 0 !important; 666 | margin-right: 0 !important; 667 | } 668 | .site-title:hover { 669 | color: #2e405b !important; 670 | background: transparent !important; 671 | padding: 0; 672 | border: none; 673 | } 674 | .site-title:hover * { 675 | color: #2e405b !important; 676 | background: transparent !important; 677 | } 678 | .main-container { 679 | text-align: left; 680 | height: auto; 681 | margin: 0; 682 | padding: 20px 30px; 683 | overflow: auto; 684 | } 685 | .side-container { 686 | padding: 40px 30px 0 30px; 687 | height: auto; 688 | position: relative; 689 | left: 0; 690 | } 691 | .site-title-links { 692 | position: unset; 693 | padding: 10px 0; 694 | margin: 10px 0; 695 | text-align: center; 696 | } 697 | .site-title-links ul li { 698 | display: inline; 699 | margin: 0 3px; 700 | font-size: 16px; 701 | } 702 | footer { 703 | padding-top: 30px; 704 | padding-bottom: 35px; 705 | } 706 | a { 707 | padding: 0; 708 | } 709 | .post-title { 710 | font-size: 20px; 711 | font-weight: bold; 712 | margin: 3px 0; 713 | } 714 | .post-meta { 715 | font-size: 12px; 716 | margin: 10px 0; 717 | } 718 | .post-abstract { 719 | font-size: 16px; 720 | } 721 | .soc { 722 | display: block !important; 723 | } 724 | .site-nav-footer { 725 | display: none !important; 726 | } 727 | .site-bottom-footer { 728 | display: block !important; 729 | } 730 | li.soc { 731 | margin-top: 5px !important; 732 | } 733 | } 734 | @media only screen and (max-height: 860px) { 735 | .site-title-large { 736 | font-size: 32px; 737 | } 738 | .site-title-small { 739 | font-size: 20px; 740 | } 741 | .site-title-links ul li { 742 | font-size: 14px; 743 | line-height: 1.3; 744 | } 745 | .site-nav-footer-br { 746 | display: none; 747 | } 748 | .side-container { 749 | padding-top: 40px; 750 | padding-bottom: 20px; 751 | } 752 | .main-container { 753 | padding-top: 40px; 754 | padding-bottom: 60px; 755 | } 756 | } 757 | @media only screen and (max-height: 600px) { 758 | body { 759 | line-height: normal; 760 | } 761 | h1 { 762 | font-size: 40px; 763 | } 764 | h2 { 765 | font-size: 34px; 766 | } 767 | h3 { 768 | font-size: 28px; 769 | } 770 | h4 { 771 | font-size: 22px; 772 | } 773 | h5 { 774 | font-size: 16px; 775 | } 776 | h6 { 777 | font-size: 14px; 778 | } 779 | .site-title-large { 780 | font-size: 38px; 781 | } 782 | .site-title-small { 783 | font-size: 22px; 784 | } 785 | .vertical-text { 786 | writing-mode: horizontal-tb; 787 | } 788 | .site-title { 789 | margin: 0; 790 | font-weight: bolder; 791 | font-style: normal; 792 | width: auto; 793 | height: auto; 794 | font-size: 0.5em; 795 | padding: 0; 796 | border-left: none; 797 | text-align: center; 798 | transition: none; 799 | } 800 | .site-title * { 801 | text-align: center; 802 | margin-left: 0 !important; 803 | margin-right: 0 !important; 804 | } 805 | .site-title:hover { 806 | color: #2e405b !important; 807 | background: transparent !important; 808 | padding: 0; 809 | border: none; 810 | } 811 | .site-title:hover * { 812 | color: #2e405b !important; 813 | background: transparent !important; 814 | } 815 | .main-container { 816 | text-align: left; 817 | height: auto; 818 | margin: 0; 819 | padding: 20px 30px; 820 | overflow: auto; 821 | } 822 | .side-container { 823 | padding: 40px 30px 0 30px; 824 | height: auto; 825 | position: relative; 826 | left: 0; 827 | } 828 | .site-title-links { 829 | position: unset; 830 | padding: 10px 0; 831 | margin: 10px 0; 832 | text-align: center; 833 | } 834 | .site-title-links ul li { 835 | display: inline; 836 | margin: 0 3px; 837 | font-size: 16px; 838 | } 839 | footer { 840 | padding-top: 30px; 841 | padding-bottom: 35px; 842 | } 843 | a { 844 | padding: 0; 845 | } 846 | .post-title { 847 | font-size: 20px; 848 | font-weight: bold; 849 | margin: 3px 0; 850 | } 851 | .post-meta { 852 | font-size: 12px; 853 | margin: 10px 0; 854 | } 855 | .post-abstract { 856 | font-size: 16px; 857 | } 858 | .soc { 859 | display: block !important; 860 | } 861 | .site-nav-footer { 862 | display: none !important; 863 | } 864 | .site-bottom-footer { 865 | display: block !important; 866 | } 867 | } 868 | 869 | /*# sourceMappingURL=style.css.map */ 870 | -------------------------------------------------------------------------------- /web/assets/css/style-dark.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css?family=Source+Sans+Pro:100,300,400,600"); 2 | @keyframes fadein { 3 | from { 4 | opacity: 0; 5 | } 6 | to { 7 | opacity: 1; 8 | } 9 | } 10 | /* Firefox < 16 */ 11 | @-moz-keyframes fadein { 12 | from { 13 | opacity: 0; 14 | } 15 | to { 16 | opacity: 1; 17 | } 18 | } 19 | /* Safari, Chrome and Opera > 12.1 */ 20 | @-webkit-keyframes fadein { 21 | from { 22 | opacity: 0; 23 | } 24 | to { 25 | opacity: 1; 26 | } 27 | } 28 | /* Internet Explorer */ 29 | @-ms-keyframes fadein { 30 | from { 31 | opacity: 0; 32 | } 33 | to { 34 | opacity: 1; 35 | } 36 | } 37 | /* Opera < 12.1 */ 38 | @-o-keyframes fadein { 39 | from { 40 | opacity: 0; 41 | } 42 | to { 43 | opacity: 1; 44 | } 45 | } 46 | @keyframes fadein-right { 47 | from { 48 | opacity: 0; 49 | transform: translateX(20px); 50 | } 51 | to { 52 | opacity: 1; 53 | transform: translateX(0px); 54 | } 55 | } 56 | /* Firefox < 16 */ 57 | @-moz-keyframes fadein-right { 58 | from { 59 | opacity: 0; 60 | transform: translateX(20px); 61 | } 62 | to { 63 | opacity: 1; 64 | transform: translateX(0px); 65 | } 66 | } 67 | /* Safari, Chrome and Opera > 12.1 */ 68 | @-webkit-keyframes fadein-right { 69 | from { 70 | opacity: 0; 71 | transform: translateX(20px); 72 | } 73 | to { 74 | opacity: 1; 75 | transform: translateX(0px); 76 | } 77 | } 78 | /* Internet Explorer */ 79 | @-ms-keyframes fadein-right { 80 | from { 81 | opacity: 0; 82 | transform: translateX(20px); 83 | } 84 | to { 85 | opacity: 1; 86 | transform: translateX(0px); 87 | } 88 | } 89 | /* Opera < 12.1 */ 90 | @-o-keyframes fadein-right { 91 | from { 92 | opacity: 0; 93 | transform: translateX(20px); 94 | } 95 | to { 96 | opacity: 1; 97 | transform: translateX(0px); 98 | } 99 | } 100 | @keyframes fadein-top { 101 | from { 102 | opacity: 0; 103 | transform: translateY(-20px); 104 | } 105 | to { 106 | opacity: 1; 107 | transform: translateY(0px); 108 | } 109 | } 110 | /* Firefox < 16 */ 111 | @-moz-keyframes fadein-top { 112 | from { 113 | opacity: 0; 114 | transform: translateY(-20px); 115 | } 116 | to { 117 | opacity: 1; 118 | transform: translateY(0px); 119 | } 120 | } 121 | /* Safari, Chrome and Opera > 12.1 */ 122 | @-webkit-keyframes fadein-top { 123 | from { 124 | opacity: 0; 125 | transform: translateY(-20px); 126 | } 127 | to { 128 | opacity: 1; 129 | transform: translateY(0px); 130 | } 131 | } 132 | /* Internet Explorer */ 133 | @-ms-keyframes fadein-top { 134 | from { 135 | opacity: 0; 136 | transform: translateY(-20px); 137 | } 138 | to { 139 | opacity: 1; 140 | transform: translateY(0px); 141 | } 142 | } 143 | /* Opera < 12.1 */ 144 | @-o-keyframes fadein-top { 145 | from { 146 | opacity: 0; 147 | transform: translateY(-20px); 148 | } 149 | to { 150 | opacity: 1; 151 | transform: translateY(0px); 152 | } 153 | } 154 | @keyframes swift-down { 155 | from { 156 | transform: translateY(0%); 157 | } 158 | to { 159 | transform: translateY(20%); 160 | } 161 | } 162 | /* Firefox < 16 */ 163 | @-moz-keyframes swift-down { 164 | from { 165 | transform: translateY(0%); 166 | } 167 | to { 168 | transform: translateY(20%); 169 | } 170 | } 171 | /* Safari, Chrome and Opera > 12.1 */ 172 | @-webkit-keyframes swift-down { 173 | from { 174 | transform: translateY(0%); 175 | } 176 | to { 177 | transform: translateY(20%); 178 | } 179 | } 180 | /* Internet Explorer */ 181 | @-ms-keyframes swift-down { 182 | from { 183 | transform: translateY(0%); 184 | } 185 | to { 186 | transform: translateY(20%); 187 | } 188 | } 189 | /* Opera < 12.1 */ 190 | @-o-keyframes swift-down { 191 | from { 192 | transform: translateY(0%); 193 | } 194 | to { 195 | transform: translateY(20%); 196 | } 197 | } 198 | .invisible { 199 | opacity: 0; 200 | } 201 | 202 | .fadeIn { 203 | -webkit-animation: fadein 1s ease-out; /* Safari, Chrome and Opera > 12.1 */ 204 | -moz-animation: fadein 1s ease-out; /* Firefox < 16 */ 205 | -ms-animation: fadein 1s ease-out; /* Internet Explorer */ 206 | -o-animation: fadein 1s ease-out; /* Opera < 12.1 */ 207 | animation: fadein 1s ease-out; 208 | } 209 | 210 | .fadeInRight { 211 | -webkit-animation: fadein-right 1s ease-out; /* Safari, Chrome and Opera > 12.1 */ 212 | -moz-animation: fadein-right 1s ease-out; /* Firefox < 16 */ 213 | -ms-animation: fadein-right 1s ease-out; /* Internet Explorer */ 214 | -o-animation: fadein-right 1s ease-out; /* Opera < 12.1 */ 215 | animation: fadein-right 1s ease-out; 216 | } 217 | 218 | .fadeInTop { 219 | -webkit-animation: fadein-top 1s ease-out; /* Safari, Chrome and Opera > 12.1 */ 220 | -moz-animation: fadein-top 1s ease-out; /* Firefox < 16 */ 221 | -ms-animation: fadein-top 1s ease-out; /* Internet Explorer */ 222 | -o-animation: fadein-top 1s ease-out; /* Opera < 12.1 */ 223 | animation: fadein-top 1s ease-out; 224 | } 225 | 226 | .swiftDown { 227 | -webkit-animation: swift-down 1s ease-out; /* Safari, Chrome and Opera > 12.1 */ 228 | -moz-animation: swift-down 1s ease-out; /* Firefox < 16 */ 229 | -ms-animation: swift-down 1s ease-out; /* Internet Explorer */ 230 | -o-animation: swift-down 1s ease-out; /* Opera < 12.1 */ 231 | animation: swift-down 1s ease-out; 232 | } 233 | 234 | .swiftDownReversed { 235 | -webkit-animation: swift-down 1s reverse ease-out; /* Safari, Chrome and Opera > 12.1 */ 236 | -moz-animation: swift-down 1s reverse ease-out; /* Firefox < 16 */ 237 | -ms-animation: swift-down 1s reverse ease-out; /* Internet Explorer */ 238 | -o-animation: swift-down 1s reverse ease-out; /* Opera < 12.1 */ 239 | animation: swift-down 1s reverse ease-out; 240 | } 241 | 242 | html, 243 | body, 244 | span, 245 | p, 246 | h1, 247 | h2, 248 | h3, 249 | h4, 250 | h5, 251 | h6 { 252 | font-family: "Source Sans Pro", "Roboto", "Helvetica", "Helvetica Neue", "Source Han Sans SC", "Source Han Sans TC", "PingFang SC", "PingFang HK", "PingFang TC", sans-serif; 253 | color: #ffffff; 254 | } 255 | 256 | html { 257 | background: #2e405b; 258 | } 259 | 260 | .foreground { 261 | background: #ffffff !important; 262 | } 263 | 264 | .background { 265 | background: #2e405b !important; 266 | } 267 | 268 | .foreground-text * { 269 | color: #ffffff !important; 270 | } 271 | 272 | .background-text * { 273 | color: #2e405b !important; 274 | } 275 | 276 | h1 { 277 | font-size: 42px; 278 | } 279 | 280 | h2 { 281 | font-size: 36px; 282 | } 283 | 284 | h3 { 285 | font-size: 30px; 286 | } 287 | 288 | h4 { 289 | font-size: 24px; 290 | } 291 | 292 | h5 { 293 | font-size: 18px; 294 | } 295 | 296 | h6 { 297 | font-size: 14px; 298 | } 299 | 300 | body { 301 | font-size: 18px; 302 | line-height: 1.5em; 303 | background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi/P//Py8DpQCXISBxJlING4QaGEdDiQgAEGAAg8sST3LGZYoAAAAASUVORK5CYII="); 304 | background-repeat: repeat; 305 | background-attachment: fixed; 306 | -webkit-font-smoothing: antialiased; 307 | -moz-osx-font-smoothing: grayscale; 308 | text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.04); 309 | min-height: 100vh; 310 | } 311 | 312 | .container { 313 | background-color: transparent; 314 | padding: 0; 315 | position: relative; 316 | } 317 | 318 | .full-width-container { 319 | width: 100vw; 320 | } 321 | 322 | .main-container { 323 | text-align: left; 324 | margin: 0; 325 | padding: 80px 40px; 326 | overflow: hidden; 327 | float: left; 328 | } 329 | .main-container :nth-child(1) { 330 | margin-top: 0; 331 | } 332 | 333 | .row { 334 | margin: 0; 335 | } 336 | 337 | .vertical-text { 338 | writing-mode: vertical-rl; 339 | } 340 | .vertical-text * { 341 | line-height: 1; 342 | text-align: left; 343 | } 344 | 345 | .side-container { 346 | height: 100vh; 347 | position: fixed; 348 | right: -100px; 349 | font-style: normal; 350 | font-size: 1em; 351 | margin: 0; 352 | transform: rotate(0deg); 353 | text-align: left; 354 | float: right; 355 | padding: 80px 20px; 356 | } 357 | 358 | .ar-year { 359 | margin-top: 5px; 360 | margin-bottom: 5px; 361 | } 362 | 363 | .date { 364 | margin-bottom: 0px; 365 | } 366 | 367 | .ar-date { 368 | font-size: 16px; 369 | margin-bottom: 5px; 370 | } 371 | 372 | .archive :first-child { 373 | padding-left: 0; 374 | } 375 | 376 | .archive ul { 377 | list-style-type: none; 378 | } 379 | 380 | .site-title { 381 | font-weight: bolder; 382 | font-style: normal; 383 | width: auto; 384 | height: auto; 385 | margin: 0 0 0 -2px; 386 | text-align: left; 387 | font-size: 0.5em; 388 | padding: 0 0 50px 0; 389 | border-left: solid #ffffff 2px; 390 | transition: padding 0.8s ease 0s, background 0.8s ease 0s; 391 | } 392 | .site-title:hover { 393 | padding: 15px 0 35px 0; 394 | color: #2e405b !important; 395 | background: #ffffff !important; 396 | border-left: solid #ffffff 2px; 397 | } 398 | .site-title:hover * { 399 | color: #2e405b !important; 400 | } 401 | .site-title :first-child { 402 | margin-right: 10px; 403 | } 404 | .site-title * { 405 | text-align: left; 406 | font-weight: bolder; 407 | margin-top: 0; 408 | margin-left: 10px; 409 | margin-right: 0; 410 | font-family: "HiraMinProN-W6", "Source Han Serif CN", "Source Han Serif SC", "Source Han Serif TC", serif; 411 | } 412 | 413 | .site-title-large { 414 | font-size: 50px; 415 | padding-top: 0; 416 | } 417 | 418 | .site-title-small { 419 | font-size: 30px; 420 | padding-top: 1px; 421 | } 422 | 423 | .site-title-links { 424 | font-weight: bold; 425 | font-style: normal; 426 | margin: 0; 427 | padding: 60px 0; 428 | text-align: left; 429 | position: fixed; 430 | bottom: 20px; 431 | } 432 | 433 | .site-title-links ul { 434 | list-style: none; 435 | padding: 0; 436 | } 437 | 438 | .site-title-links ul li { 439 | display: block; 440 | margin: 5px 0; 441 | font-size: 18px; 442 | } 443 | 444 | .site-title-links a { 445 | border-bottom: solid #ffffff 2px; 446 | } 447 | 448 | .no-margin-left { 449 | margin-left: 0; 450 | } 451 | 452 | .no-margin-right { 453 | margin-right: 0; 454 | } 455 | 456 | .no-margin-top { 457 | margin-top: 0; 458 | } 459 | 460 | .no-margin-bottom { 461 | margin-bottom: 0; 462 | } 463 | 464 | a { 465 | color: #ffffff; 466 | transition: color 0.3s ease 0s, background-color 0.3s ease 0s; 467 | -moz-transition: color 0.3s ease 0s, background-color 0.3s ease 0s; 468 | -o-transition: color 0.3s ease 0s, background-color 0.3s ease 0s; 469 | -webkit-transition: color 0.3s ease 0s, background-color 0.3s ease 0s; 470 | border-bottom: solid #ffffff 2px; 471 | padding: 0; 472 | cursor: pointer; 473 | cursor: hand; 474 | } 475 | a:hover, a:focus { 476 | text-decoration: none; 477 | } 478 | a:hover { 479 | color: #2e405b !important; 480 | background-color: #ffffff; 481 | border-bottom: solid transparent 2px; 482 | } 483 | a:focus { 484 | color: #b3b3b3; 485 | } 486 | 487 | .a-title { 488 | text-decoration: none; 489 | background-color: transparent; 490 | border-bottom: solid transparent 2px; 491 | } 492 | .a-title:hover, .a-title:focus { 493 | background-color: transparent; 494 | } 495 | 496 | .current { 497 | border-bottom: solid #ffffff 3px !important; 498 | } 499 | 500 | .a-mute { 501 | background-color: transparent; 502 | color: #ffffff !important; 503 | text-decoration: none; 504 | border-bottom: none; 505 | } 506 | .a-mute:hover, .a-mute:focus { 507 | background-color: transparent; 508 | color: #ffffff !important; 509 | text-decoration: none; 510 | border-bottom: none; 511 | } 512 | 513 | .soc { 514 | margin: 0 5px 0 0; 515 | font-size: 18px !important; 516 | } 517 | .soc a { 518 | border-bottom: solid transparent 2px; 519 | } 520 | .soc a:hover { 521 | color: #e6e6e6 !important; 522 | background-color: transparent !important; 523 | text-decoration: none; 524 | border-bottom: solid rgba(0, 0, 0, 0) 2px; 525 | } 526 | .soc a:focus { 527 | color: #e6e6e6 !important; 528 | } 529 | 530 | .meta-item { 531 | margin: 0 10px 0 0; 532 | } 533 | 534 | img { 535 | display: block; 536 | max-width: 100%; 537 | height: auto; 538 | } 539 | 540 | footer { 541 | margin-top: 10px; 542 | padding-top: 0; 543 | font-size: 12px; 544 | font-weight: bold; 545 | } 546 | 547 | footer * { 548 | margin: 0; 549 | } 550 | 551 | .post-container { 552 | margin: 0 0 30px 0; 553 | font-weight: normal; 554 | } 555 | .post-container .gist-file { 556 | border: none !important; 557 | } 558 | .post-container .gist-file .gist-data { 559 | background-color: rgba(0, 0, 0, 0) !important; 560 | } 561 | 562 | .post-title { 563 | font-size: 24px; 564 | font-weight: bold; 565 | margin: 3px 0; 566 | } 567 | 568 | .post-meta { 569 | font-size: 16px; 570 | margin: 10px 0; 571 | } 572 | 573 | .post-abstract { 574 | font-size: 18px; 575 | } 576 | 577 | li { 578 | margin: 5px 0; 579 | } 580 | 581 | figure { 582 | overflow: scroll; 583 | } 584 | 585 | figure > table { 586 | width: 100%; 587 | } 588 | 589 | pre * { 590 | font-family: Monaco, Menlo, "Source Code Pro", "Cascadia Code", Consolas, monospace; 591 | background: transparent !important; 592 | } 593 | 594 | figure .gutter pre { 595 | border-bottom-right-radius: 0; 596 | border-top-right-radius: 0; 597 | border-right: none; 598 | text-align: right; 599 | min-width: 3em; 600 | } 601 | figure .code { 602 | width: 99%; 603 | } 604 | figure .code pre { 605 | border-bottom-left-radius: 0; 606 | border-top-left-radius: 0; 607 | border-left: solid rgba(0, 0, 0, 0.05) 1px; 608 | margin-right: 1px; 609 | } 610 | 611 | .h4, 612 | .h5, 613 | .h6, 614 | h4, 615 | h5, 616 | h6 { 617 | margin-top: 20px; 618 | } 619 | 620 | @media (max-width: 768px) { 621 | body { 622 | line-height: normal; 623 | } 624 | h1 { 625 | font-size: 40px; 626 | } 627 | h2 { 628 | font-size: 34px; 629 | } 630 | h3 { 631 | font-size: 28px; 632 | } 633 | h4 { 634 | font-size: 22px; 635 | } 636 | h5 { 637 | font-size: 16px; 638 | } 639 | h6 { 640 | font-size: 14px; 641 | } 642 | .site-title-large { 643 | font-size: 38px; 644 | } 645 | .site-title-small { 646 | font-size: 22px; 647 | } 648 | .vertical-text { 649 | writing-mode: horizontal-tb; 650 | } 651 | .site-title { 652 | margin: 0; 653 | font-weight: bolder; 654 | font-style: normal; 655 | width: auto; 656 | height: auto; 657 | font-size: 0.5em; 658 | padding: 0; 659 | border-left: none; 660 | text-align: center; 661 | transition: none; 662 | } 663 | .site-title * { 664 | text-align: center; 665 | margin-left: 0 !important; 666 | margin-right: 0 !important; 667 | } 668 | .site-title:hover { 669 | color: #ffffff !important; 670 | background: transparent !important; 671 | padding: 0; 672 | border: none; 673 | } 674 | .site-title:hover * { 675 | color: #ffffff !important; 676 | background: transparent !important; 677 | } 678 | .main-container { 679 | text-align: left; 680 | height: auto; 681 | margin: 0; 682 | padding: 20px 30px; 683 | overflow: auto; 684 | } 685 | .side-container { 686 | padding: 40px 30px 0 30px; 687 | height: auto; 688 | position: relative; 689 | left: 0; 690 | } 691 | .site-title-links { 692 | position: unset; 693 | padding: 10px 0; 694 | margin: 10px 0; 695 | text-align: center; 696 | } 697 | .site-title-links ul li { 698 | display: inline; 699 | margin: 0 3px; 700 | font-size: 16px; 701 | } 702 | footer { 703 | padding-top: 30px; 704 | padding-bottom: 35px; 705 | } 706 | a { 707 | padding: 0; 708 | } 709 | .post-title { 710 | font-size: 20px; 711 | font-weight: bold; 712 | margin: 3px 0; 713 | } 714 | .post-meta { 715 | font-size: 12px; 716 | margin: 10px 0; 717 | } 718 | .post-abstract { 719 | font-size: 16px; 720 | } 721 | .soc { 722 | display: block !important; 723 | } 724 | .site-nav-footer { 725 | display: none !important; 726 | } 727 | .site-bottom-footer { 728 | display: block !important; 729 | } 730 | li.soc { 731 | margin-top: 5px !important; 732 | } 733 | } 734 | @media only screen and (max-height: 860px) { 735 | .site-title-large { 736 | font-size: 32px; 737 | } 738 | .site-title-small { 739 | font-size: 20px; 740 | } 741 | .site-title-links ul li { 742 | font-size: 14px; 743 | line-height: 1.3; 744 | } 745 | .site-nav-footer-br { 746 | display: none; 747 | } 748 | .side-container { 749 | padding-top: 40px; 750 | padding-bottom: 20px; 751 | } 752 | .main-container { 753 | padding-top: 40px; 754 | padding-bottom: 60px; 755 | } 756 | } 757 | @media only screen and (max-height: 600px) { 758 | body { 759 | line-height: normal; 760 | } 761 | h1 { 762 | font-size: 40px; 763 | } 764 | h2 { 765 | font-size: 34px; 766 | } 767 | h3 { 768 | font-size: 28px; 769 | } 770 | h4 { 771 | font-size: 22px; 772 | } 773 | h5 { 774 | font-size: 16px; 775 | } 776 | h6 { 777 | font-size: 14px; 778 | } 779 | .site-title-large { 780 | font-size: 38px; 781 | } 782 | .site-title-small { 783 | font-size: 22px; 784 | } 785 | .vertical-text { 786 | writing-mode: horizontal-tb; 787 | } 788 | .site-title { 789 | margin: 0; 790 | font-weight: bolder; 791 | font-style: normal; 792 | width: auto; 793 | height: auto; 794 | font-size: 0.5em; 795 | padding: 0; 796 | border-left: none; 797 | text-align: center; 798 | transition: none; 799 | } 800 | .site-title * { 801 | text-align: center; 802 | margin-left: 0 !important; 803 | margin-right: 0 !important; 804 | } 805 | .site-title:hover { 806 | color: #ffffff !important; 807 | background: transparent !important; 808 | padding: 0; 809 | border: none; 810 | } 811 | .site-title:hover * { 812 | color: #ffffff !important; 813 | background: transparent !important; 814 | } 815 | .main-container { 816 | text-align: left; 817 | height: auto; 818 | margin: 0; 819 | padding: 20px 30px; 820 | overflow: auto; 821 | } 822 | .side-container { 823 | padding: 40px 30px 0 30px; 824 | height: auto; 825 | position: relative; 826 | left: 0; 827 | } 828 | .site-title-links { 829 | position: unset; 830 | padding: 10px 0; 831 | margin: 10px 0; 832 | text-align: center; 833 | } 834 | .site-title-links ul li { 835 | display: inline; 836 | margin: 0 3px; 837 | font-size: 16px; 838 | } 839 | footer { 840 | padding-top: 30px; 841 | padding-bottom: 35px; 842 | } 843 | a { 844 | padding: 0; 845 | } 846 | .post-title { 847 | font-size: 20px; 848 | font-weight: bold; 849 | margin: 3px 0; 850 | } 851 | .post-meta { 852 | font-size: 12px; 853 | margin: 10px 0; 854 | } 855 | .post-abstract { 856 | font-size: 16px; 857 | } 858 | .soc { 859 | display: block !important; 860 | } 861 | .site-nav-footer { 862 | display: none !important; 863 | } 864 | .site-bottom-footer { 865 | display: block !important; 866 | } 867 | } 868 | 869 | /*# sourceMappingURL=style-dark.css.map */ 870 | -------------------------------------------------------------------------------- /internal/bindata/views/views.go: -------------------------------------------------------------------------------- 1 | // Code generated by go-bindata. (@generated) DO NOT EDIT. 2 | 3 | //Package views generated by go-bindata.// sources: 4 | // web/views/article.html 5 | // web/views/categories.html 6 | // web/views/errors/404.html 7 | // web/views/errors/500.html 8 | // web/views/home.html 9 | // web/views/layouts/layout.html 10 | package views 11 | 12 | import ( 13 | "bytes" 14 | "compress/gzip" 15 | "fmt" 16 | "net/http" 17 | "io" 18 | "io/ioutil" 19 | "os" 20 | "path/filepath" 21 | "strings" 22 | "time" 23 | ) 24 | 25 | func bindataRead(data []byte, name string) ([]byte, error) { 26 | gz, err := gzip.NewReader(bytes.NewBuffer(data)) 27 | if err != nil { 28 | return nil, fmt.Errorf("read %q: %v", name, err) 29 | } 30 | 31 | var buf bytes.Buffer 32 | _, err = io.Copy(&buf, gz) 33 | clErr := gz.Close() 34 | 35 | if err != nil { 36 | return nil, fmt.Errorf("read %q: %v", name, err) 37 | } 38 | if clErr != nil { 39 | return nil, err 40 | } 41 | 42 | return buf.Bytes(), nil 43 | } 44 | 45 | type asset struct { 46 | bytes []byte 47 | info os.FileInfo 48 | } 49 | 50 | type bindataFileInfo struct { 51 | name string 52 | size int64 53 | mode os.FileMode 54 | modTime time.Time 55 | } 56 | 57 | // Name return file name 58 | func (fi bindataFileInfo) Name() string { 59 | return fi.name 60 | } 61 | 62 | // Size return file size 63 | func (fi bindataFileInfo) Size() int64 { 64 | return fi.size 65 | } 66 | 67 | // Mode return file mode 68 | func (fi bindataFileInfo) Mode() os.FileMode { 69 | return fi.mode 70 | } 71 | 72 | // ModTime return file modify time 73 | func (fi bindataFileInfo) ModTime() time.Time { 74 | return fi.modTime 75 | } 76 | 77 | // IsDir return file whether a directory 78 | func (fi bindataFileInfo) IsDir() bool { 79 | return fi.mode&os.ModeDir != 0 80 | } 81 | 82 | // Sys return file is sys mode 83 | func (fi bindataFileInfo) Sys() interface{} { 84 | return nil 85 | } 86 | 87 | 88 | type assetFile struct { 89 | *bytes.Reader 90 | name string 91 | childInfos []os.FileInfo 92 | childInfoOffset int 93 | } 94 | 95 | type assetOperator struct{} 96 | 97 | // Open implement http.FileSystem interface 98 | func (f *assetOperator) Open(name string) (http.File, error) { 99 | var err error 100 | if len(name) > 0 && name[0] == '/' { 101 | name = name[1:] 102 | } 103 | content, err := Asset(name) 104 | if err == nil { 105 | return &assetFile{name: name, Reader: bytes.NewReader(content)}, nil 106 | } 107 | children, err := AssetDir(name) 108 | if err == nil { 109 | childInfos := make([]os.FileInfo, 0, len(children)) 110 | for _, child := range children { 111 | childPath := filepath.Join(name, child) 112 | info, errInfo := AssetInfo(filepath.Join(name, child)) 113 | if errInfo == nil { 114 | childInfos = append(childInfos, info) 115 | } else { 116 | childInfos = append(childInfos, newDirFileInfo(childPath)) 117 | } 118 | } 119 | return &assetFile{name: name, childInfos: childInfos}, nil 120 | } else { 121 | // If the error is not found, return an error that will 122 | // result in a 404 error. Otherwise the server returns 123 | // a 500 error for files not found. 124 | if strings.Contains(err.Error(), "not found") { 125 | return nil, os.ErrNotExist 126 | } 127 | return nil, err 128 | } 129 | } 130 | 131 | // Close no need do anything 132 | func (f *assetFile) Close() error { 133 | return nil 134 | } 135 | 136 | // Readdir read dir's children file info 137 | func (f *assetFile) Readdir(count int) ([]os.FileInfo, error) { 138 | if len(f.childInfos) == 0 { 139 | return nil, os.ErrNotExist 140 | } 141 | if count <= 0 { 142 | return f.childInfos, nil 143 | } 144 | if f.childInfoOffset+count > len(f.childInfos) { 145 | count = len(f.childInfos) - f.childInfoOffset 146 | } 147 | offset := f.childInfoOffset 148 | f.childInfoOffset += count 149 | return f.childInfos[offset : offset+count], nil 150 | } 151 | 152 | // Stat read file info from asset item 153 | func (f *assetFile) Stat() (os.FileInfo, error) { 154 | if len(f.childInfos) != 0 { 155 | return newDirFileInfo(f.name), nil 156 | } 157 | return AssetInfo(f.name) 158 | } 159 | 160 | // newDirFileInfo return default dir file info 161 | func newDirFileInfo(name string) os.FileInfo { 162 | return &bindataFileInfo{ 163 | name: name, 164 | size: 0, 165 | mode: os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir 166 | modTime: time.Time{}} 167 | } 168 | 169 | // AssetFile return a http.FileSystem instance that data backend by asset 170 | func AssetFile() http.FileSystem { 171 | return &assetOperator{} 172 | } 173 | 174 | var _articleHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb2\x49\xc9\x2c\x53\x48\xce\x49\x2c\x2e\xb6\x55\x2a\xc8\x2f\x2e\xd1\x2d\x48\x4c\x4f\x55\xb2\xe3\x52\x50\xa8\xae\xd6\x73\x2c\x2a\xc9\x4c\xce\x49\xad\xad\xe5\xb2\xd1\x4f\xc9\x2c\xb3\xe3\xe2\x02\x2b\xcf\x4c\xb1\x55\x4a\xcf\x2c\x49\xcc\xc9\x56\xb2\x83\x4a\x00\x02\x00\x00\xff\xff\x68\x41\x05\xf7\x47\x00\x00\x00") 175 | 176 | func articleHtmlBytes() ([]byte, error) { 177 | return bindataRead( 178 | _articleHtml, 179 | "article.html", 180 | ) 181 | } 182 | 183 | func articleHtml() (*asset, error) { 184 | bytes, err := articleHtmlBytes() 185 | if err != nil { 186 | return nil, err 187 | } 188 | 189 | info := bindataFileInfo{name: "article.html", size: 71, mode: os.FileMode(420), modTime: time.Unix(1673419221, 0)} 190 | a := &asset{bytes: bytes, info: info} 191 | return a, nil 192 | } 193 | 194 | var _categoriesHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x92\x41\x6a\xeb\x30\x10\x86\xf7\x39\xc5\xe0\x7d\xac\x0b\x28\x86\xf0\xde\xe6\x41\x78\x3c\x78\xb9\xc0\xd4\x9e\xd8\x43\x65\xd9\x58\x4a\xb2\x10\xda\xb6\xbd\x4d\xe9\xba\xe7\x29\xb9\x46\xb1\x2c\x05\x27\x6d\xa1\x85\xd2\x9d\x46\xfa\xc7\xf3\xff\xf3\xd9\x39\xde\x41\xfe\xbf\xe9\x8e\x6b\xa5\xbc\x5f\xc8\x8a\x0f\x50\x2a\x34\x66\x95\xe1\x50\x36\x7c\x20\x40\xcd\x2d\x5a\xaa\x60\x87\x15\xfd\xd1\xbf\xbb\xa3\xce\x8a\x05\x80\xec\x93\xb2\xef\x8c\x5d\x5a\xb6\x8a\xb2\xe2\xe5\xe1\xee\xf4\xf4\x0c\x52\xf4\x41\xb3\x57\x49\xa4\xd8\x58\xd6\x75\x68\x05\x70\x6e\x40\x5d\x13\xe4\xdb\x81\x68\x3d\x58\x2e\x15\x19\xef\xc3\x9b\x54\x7c\xd5\xb4\x64\x4b\x6d\xec\x04\x98\x9b\x4c\x82\xd1\xc2\x59\xf0\x91\x37\x89\xd0\x0c\xb4\x5b\x65\xa2\x44\x4b\x75\x37\x30\x19\xe1\x5c\xfe\x2b\x55\x7f\xb1\x25\xef\x33\x08\xfa\x55\xf6\xce\x53\xf1\xf6\x4e\x0a\x9c\x0d\x9e\x72\xc7\xc2\xf4\xa8\x93\x91\x0a\x2d\x41\x4b\x16\x63\x18\xe7\x6a\xb2\x29\xf9\x86\x34\xe4\x1b\x36\xd6\x7b\x38\x3d\xde\x4b\x31\x76\x9e\xf3\x8a\x8a\x0f\x53\x21\x85\xe2\xb4\x40\xd2\x55\x58\x98\x14\x7b\x55\x2c\xa2\xc8\x39\x52\x86\xbe\x93\xe4\x3c\xf0\x18\xf6\x93\x5c\x6d\x43\x53\x9e\x9f\x42\x8a\xd3\x2a\x9d\xcb\x37\xac\x6f\x2f\x29\x6e\xc7\x53\xa4\x17\xcf\x23\xb5\x4b\x58\x33\x0b\x71\x42\x4b\xb3\xf9\x57\x38\x71\x58\x8e\x44\xc3\x27\xff\xed\x6f\x14\x9b\x66\xcb\xd3\xef\x30\x67\x77\x41\xef\xcb\x28\xc3\xf5\x6b\x00\x00\x00\xff\xff\xea\x97\xce\x28\xa4\x03\x00\x00") 195 | 196 | func categoriesHtmlBytes() ([]byte, error) { 197 | return bindataRead( 198 | _categoriesHtml, 199 | "categories.html", 200 | ) 201 | } 202 | 203 | func categoriesHtml() (*asset, error) { 204 | bytes, err := categoriesHtmlBytes() 205 | if err != nil { 206 | return nil, err 207 | } 208 | 209 | info := bindataFileInfo{name: "categories.html", size: 932, mode: os.FileMode(420), modTime: time.Unix(1673409500, 0)} 210 | a := &asset{bytes: bytes, info: info} 211 | return a, nil 212 | } 213 | 214 | var _errors404Html = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x5c\x90\xc1\x6a\xc3\x30\x0c\x86\xef\x79\x0a\xcd\xe7\x79\x69\xa0\x87\x1d\xec\xc0\xe8\x5a\xd8\x65\xdb\xa1\x85\xed\xe8\xda\x3f\xb5\xc0\xb1\xb3\x54\x4d\xd9\xdb\x8f\x34\x1d\x74\x3b\x09\x7d\xfa\x3f\x81\x64\xee\x9e\xdf\x56\xdb\xcf\xf7\x35\x45\xe9\x52\x5b\x99\xa9\x50\x72\xf9\x60\x15\xb2\x9a\x00\x5c\x68\x2b\x22\x22\xd3\x41\x1c\xf9\xe8\x86\x23\xc4\xaa\xdd\x76\xa3\x1f\xd5\xed\x28\x8a\xf4\x1a\x5f\x27\x1e\xad\xfa\xd0\xbb\x27\xbd\x2a\x5d\xef\x84\xf7\x09\x8a\x7c\xc9\x82\x2c\x56\xbd\xac\x2d\xc2\x01\x7f\xcc\xec\x3a\x58\x35\x32\xce\x7d\x19\xe4\x26\x7c\xe6\x20\xd1\x06\x8c\xec\xa1\x2f\xcd\x3d\x71\x66\x61\x97\xf4\xd1\xbb\x04\xdb\x3c\x2c\x7e\x57\x09\x4b\x42\xbb\x5c\x2c\xe9\xb5\x08\x6d\xca\x29\x07\x53\xcf\xb0\x32\xf5\x7c\x88\xd9\x97\xf0\x7d\xcd\xc7\xa6\x35\x1e\x59\x30\xfc\x97\xae\xd4\xd4\xb1\x99\xd4\xd9\x31\xf5\xe5\x47\x3f\x01\x00\x00\xff\xff\xf0\x70\x71\x97\x33\x01\x00\x00") 215 | 216 | func errors404HtmlBytes() ([]byte, error) { 217 | return bindataRead( 218 | _errors404Html, 219 | "errors/404.html", 220 | ) 221 | } 222 | 223 | func errors404Html() (*asset, error) { 224 | bytes, err := errors404HtmlBytes() 225 | if err != nil { 226 | return nil, err 227 | } 228 | 229 | info := bindataFileInfo{name: "errors/404.html", size: 307, mode: os.FileMode(420), modTime: time.Unix(1672904546, 0)} 230 | a := &asset{bytes: bytes, info: info} 231 | return a, nil 232 | } 233 | 234 | var _errors500Html = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x90\xc1\x4e\xc3\x30\x0c\x86\xef\x79\x0a\x93\xf3\x42\xd9\x8d\x43\x52\x09\x8d\x21\x71\x1a\x87\x4d\x82\xa3\xd7\x5a\x8d\xa5\xd4\x29\xad\xb7\x6a\x6f\x8f\xba\x0c\x69\xdc\x38\x25\xbf\xfc\xf9\xb3\x6c\xff\xf0\xba\xdb\xec\xbf\x3e\xb6\x10\xb5\x4f\xb5\xf1\xcb\x03\x09\xa5\x0b\x96\xc4\xd6\xc6\xf8\x48\xd8\xd6\x06\x00\xc0\xf7\xa4\x08\x4d\xc4\x71\x22\x0d\xf6\xb0\x7f\x73\xcf\xf6\xbe\x14\x55\x07\x47\xdf\x27\x3e\x07\xfb\xe9\x0e\x2f\x6e\x93\xfb\x01\x95\x8f\x89\x2c\x34\x59\x94\x44\x83\x7d\xdf\x06\x6a\x3b\xfa\xd3\x29\xd8\x53\xb0\x67\xa6\x79\xc8\xa3\xde\xc1\x33\xb7\x1a\x43\x4b\x67\x6e\xc8\x5d\xc3\x0a\x58\x58\x19\x93\x9b\x1a\x4c\x14\xd6\x8f\x4f\xbf\x2a\x65\x4d\x54\xef\x4e\xc3\x04\x53\xee\x49\x23\x4b\x07\x33\x89\xc2\x3c\x66\xe9\x56\xa0\xe3\x05\xb0\x43\x16\x5f\x15\xd6\xf8\xaa\xec\x67\xfc\x31\xb7\x97\x9b\x27\xae\xcb\xe7\x1a\x1a\x12\xa5\xf1\x9f\xd6\x1b\x5c\x34\xd5\xe2\xf1\x55\x11\x2f\x93\x96\x13\xff\x04\x00\x00\xff\xff\x89\x94\x40\x58\x72\x01\x00\x00") 235 | 236 | func errors500HtmlBytes() ([]byte, error) { 237 | return bindataRead( 238 | _errors500Html, 239 | "errors/500.html", 240 | ) 241 | } 242 | 243 | func errors500Html() (*asset, error) { 244 | bytes, err := errors500HtmlBytes() 245 | if err != nil { 246 | return nil, err 247 | } 248 | 249 | info := bindataFileInfo{name: "errors/500.html", size: 370, mode: os.FileMode(420), modTime: time.Unix(1672904546, 0)} 250 | a := &asset{bytes: bytes, info: info} 251 | return a, nil 252 | } 253 | 254 | var _homeHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\x90\x31\x4e\xc4\x30\x10\x45\xfb\x9c\x62\x64\xea\x8d\x2f\xe0\xb5\x84\x68\x29\xb6\xd8\x0b\xcc\x26\xc3\xee\x88\xc4\x89\xec\x21\x08\x8d\x5c\xd1\x83\xb8\x0d\xe2\x3c\xec\x3d\x50\x12\x82\x88\x94\xd2\x7e\xfe\x4f\xff\x5b\x35\x62\x38\x13\x94\xb7\x51\xb8\x6a\x28\xe5\x5c\xb8\x9a\x07\xa8\x1a\x4c\x69\x6f\xfa\x2e\xc9\xae\xea\x82\x20\x07\x8a\xc6\x17\x00\xae\x5f\x41\x61\x69\xc8\x78\x87\x70\x89\xf4\xb0\x37\x16\x67\x93\x6a\x79\xcf\xe1\x31\x67\xe3\x55\xcb\xe3\xf8\x2a\x67\x67\xd1\x3b\xdb\x6f\x68\x5a\x12\x9c\xf4\x00\x2e\xf5\x18\x16\x58\xa3\x10\x8c\x70\xc7\x42\xad\xf1\xd7\xf7\x8f\xeb\xe7\xeb\xf7\xd7\x1b\xa8\x96\x87\xa7\x53\xc3\xe9\x72\xe4\x76\x72\x8f\xb9\x0d\xc5\xbf\xf4\x04\x01\x1c\x2e\x0c\x77\x82\x67\xb3\x54\xaf\x50\xe8\xdc\x45\xa6\x64\x55\xcb\xbb\xf9\xf4\x92\xb3\x35\x30\xcd\xdc\x9b\xd5\xb5\xf1\x37\xbf\x42\x80\x15\x18\x67\xce\x3d\xfe\x3a\x6d\xaf\xc6\x53\x92\x88\x95\x4c\x7f\x74\x88\x34\x30\x3d\x8f\xf1\xde\x17\xce\xd6\x3c\xf8\x42\x95\x42\x9d\x73\xf1\x13\x00\x00\xff\xff\x76\x3d\x73\x03\xa9\x01\x00\x00") 255 | 256 | func homeHtmlBytes() ([]byte, error) { 257 | return bindataRead( 258 | _homeHtml, 259 | "home.html", 260 | ) 261 | } 262 | 263 | func homeHtml() (*asset, error) { 264 | bytes, err := homeHtmlBytes() 265 | if err != nil { 266 | return nil, err 267 | } 268 | 269 | info := bindataFileInfo{name: "home.html", size: 425, mode: os.FileMode(420), modTime: time.Unix(1673409500, 0)} 270 | a := &asset{bytes: bytes, info: info} 271 | return a, nil 272 | } 273 | 274 | var _layoutsLayoutHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x58\x4b\x8f\xdc\xc6\x11\xbe\xcf\xaf\x28\xf3\xb2\xbb\xc8\x92\xdc\x07\x10\x24\xda\xe1\x18\x7a\xc1\x16\x10\x4b\x82\xb4\x01\x12\x08\x82\x50\x43\xd6\x90\xbd\x4b\x76\x33\xdd\x3d\x33\x3b\x1e\x0f\x90\x4b\x82\x20\xbe\xe7\xae\x4b\x80\x00\xbe\xf8\x20\xc0\x40\x6e\xf9\x2d\x36\x0c\xfd\x8b\xa0\xbb\x49\x0e\x5f\x2b\xaf\x65\xc0\x27\xf6\xa3\x1e\x5f\x57\x57\x7f\xd5\xec\xe9\x27\x8f\x9e\x3d\xbc\xfc\xf3\xf3\xc7\x90\xe9\x22\x9f\x4d\xa6\xe6\x03\x39\xf2\x34\xf2\x88\x7b\xb3\xc9\x64\x9a\x11\x26\xb3\x09\xc0\xb4\x20\x8d\x10\x67\x28\x15\xe9\xc8\x5b\xea\x85\xff\x3b\x6f\x3f\xc1\xb1\xa0\xc8\xfb\x93\xff\xc7\xfb\xfe\x43\x51\x94\xa8\xd9\x3c\x27\x0f\x62\xc1\x35\x71\x1d\x79\x4f\x1e\x47\x94\xa4\x34\xd0\xc0\xa5\xce\x84\x74\xc3\x9a\xe9\x9c\x66\xdb\x2d\x04\x97\xa6\x05\xbb\xdd\x34\x74\x63\x93\x49\x4f\x2d\x21\x15\x4b\x56\x6a\x26\x78\xcb\xc9\x7d\x78\x90\x8b\x14\x9e\x8b\x35\x49\x4a\xe0\xc1\x06\x3e\xa7\x1b\x31\xf0\x79\x4d\x9b\xb5\x90\x89\x6a\x69\xb6\x64\x9a\xb1\x35\x4b\x74\x16\x25\xb4\x62\x31\xf9\xb6\x73\x0c\x8c\x33\xcd\x30\xf7\x55\x8c\x39\x45\xa7\xc1\xc9\x31\x14\x78\xc3\x8a\x65\xd1\x1e\x5a\x2a\x92\xb6\x8f\xf3\x9c\xa2\x13\xaf\xf2\xbb\x62\xb4\x2e\x85\xd4\x63\xce\x36\xa4\x6a\x31\x2c\xcb\x9c\xfc\x42\xcc\x59\x4e\xfe\x9a\xe6\x3e\x96\xa5\x1f\x63\x69\x8c\x8d\xa9\xce\x73\x8c\xaf\x3f\xa8\xac\x34\xea\xa5\xf2\xe7\x28\x7d\xa5\x37\xe3\x56\x34\xe5\x54\x66\x82\x53\xc4\x45\x6d\x6c\x21\x64\x81\xda\x4f\x48\x53\x6c\x43\xdd\x8f\xa4\x24\x9e\x90\x24\xd9\x8a\xe4\x9a\xe6\xd7\xac\x5a\x62\xce\xf8\x35\x48\xca\x23\x0f\x73\x4d\x92\xa3\x26\x0f\xf4\xa6\xac\x70\xb2\x18\x8d\xd5\x10\xb5\x28\x7e\x73\x53\xe4\x1e\xd8\xdd\x8e\xbc\xfb\x97\xcf\xbe\x80\xd3\xe0\xc4\x83\x4c\xd2\x22\xf2\xac\x44\x60\x24\xfa\x00\x52\xe2\x24\x51\x8b\x36\x02\xb3\xe7\xf0\xdb\xe0\x3c\x38\x31\x09\x0c\x30\xfd\xc4\xf7\x21\x56\x0a\x7c\xbf\x87\xca\x06\x43\x65\x44\xba\xf1\x64\x42\xc5\xe2\x30\x56\x2a\x9c\x0b\xa1\x95\x96\x58\x06\x05\xe3\x41\xac\x54\x7f\x51\x1f\x56\x5f\x08\xae\x7d\x5c\x93\x12\x05\x7d\x9c\x85\x52\x32\x55\xec\xd5\xcc\x32\xee\xac\x6b\x67\xfd\x04\xe5\xb5\x33\xd0\x59\x3c\x4b\x22\x4f\x67\x54\x90\x6f\xe6\xee\x6c\xae\x82\x62\x0f\xe3\x76\x0b\x6c\x01\xc1\x67\x4c\x63\x7e\x1d\x3c\xcc\x19\x71\xfd\xe4\x11\xec\x76\x3f\x67\x81\xa9\xd5\xae\x3e\xcd\x42\xb7\x5b\xe2\xc9\x6e\xe7\xbc\x18\x27\xf7\x39\xe6\x9b\x2f\x49\x06\x0f\x90\x25\x4b\xe7\xc2\x04\xc3\x76\x01\xcd\xac\x66\x71\xb3\xbf\x8e\x19\x4c\x13\x60\x85\x12\xde\x64\x85\x86\xc8\x7d\xbe\xfa\x0a\x5e\xbd\xbe\xb0\x53\x87\x8b\x25\xb7\x59\x0d\x87\x47\xb0\xb5\x43\x4e\x3e\x2b\x20\x82\x44\xc4\xcb\x82\xb8\x0e\x62\x49\xa8\xe9\x71\x4e\xa6\x77\xe8\x39\xe3\xde\xd1\x45\xa5\x90\x15\x81\x92\x31\x44\xe0\x65\x5a\x97\xea\x5e\x18\x66\x45\x30\x37\xc0\x82\x58\x14\xa6\x73\xa5\x3e\xdd\x6e\x07\x6b\xf0\x2e\x5a\x1e\x55\xdb\x61\x4a\xba\xf2\xa6\x1e\x6c\x2e\x31\x7d\x8a\x05\xed\xfd\xbe\x3a\x79\x5d\x6b\xaa\xa0\x44\x49\x5c\x3f\x15\x09\x05\x8c\x2b\x92\xfa\x01\x2d\x84\xa4\xc3\xac\x38\x06\x55\x41\xdc\x1d\x1d\xda\xd6\x34\xdc\xc7\xe5\xf6\x08\x7f\x26\x44\x9a\xd3\x3e\xc4\xae\x0f\x1a\x53\x38\x4c\x35\xa6\xc1\x95\x3a\xea\xc6\x19\x50\x6d\x78\x0c\x4a\xc6\x51\x13\x82\xf5\x7a\x1d\xa4\x56\x53\x63\x5a\x20\xc7\x94\xa4\x0d\x87\x31\x11\x5e\xa9\x4f\x59\x12\xb5\x63\x52\x7b\xf5\x66\x6d\x94\x9d\x8d\x5c\x33\x9e\x88\x75\x90\xa0\xc6\x3f\xe0\x86\x24\x44\xc3\xa1\xd6\xe6\x36\x7b\x6b\x3c\x9a\xfd\x85\x46\x2c\x28\x97\x2a\x3b\x44\x99\xda\x70\xab\xa3\x0b\xd8\x59\x15\x2b\x79\x70\xa5\x0e\x8e\x81\xd3\x1a\x1e\xa1\xa6\xc3\xa3\xa3\x8b\x49\x6b\x32\x16\x7c\xc1\xd2\x83\x63\x38\x18\x43\x7f\x70\x7b\x9c\x27\xd3\xd0\xd5\xd1\xc9\x74\x2e\x92\x8d\x5d\x5d\xc2\x56\x10\xe7\xa8\x54\xe4\x19\xea\x42\xc6\x0d\x8f\x9a\xa3\xa9\x34\x56\x95\xb2\x2b\x26\xc5\xba\x1a\xed\xab\xe7\xbe\x2a\xfc\x73\x30\x8d\x1b\xe5\x9f\x9e\x81\x62\x09\xf9\x8d\x59\x58\x60\x42\x4f\xf8\x0b\x96\x66\xba\x72\x61\xe6\xe7\x28\x1b\x7b\x5d\x8b\x2b\x92\x9a\xc5\x98\xfb\x9a\x6e\x34\x28\xa6\xc9\xb7\xd4\xdc\x12\x07\x98\x66\xe7\xb5\xfc\x5e\xc2\x57\x05\xe6\x86\xc9\x71\xce\x78\x42\x37\x91\xe7\x9f\x7a\xb3\x29\xd6\x92\x58\x19\xaa\xd9\xc0\xdb\xd7\xfb\x33\x5b\xf0\x71\x36\x0d\xb3\xf3\xae\x9f\xd3\x11\x3f\x39\xca\x94\x3e\xca\xcf\xde\xcd\xe9\x6c\xb2\x5f\x7e\x98\xb0\xd5\x6c\x3a\x97\x4d\x0c\x98\x32\x77\x18\x3f\x4f\xa1\x6e\x16\x49\xd3\x54\xc5\x2d\xa1\x6b\x43\x64\xfc\x5a\xd5\xf1\xd6\xe4\x73\x5c\x75\x03\xb8\xcc\xdb\x5d\x4b\x9c\xdd\x01\x73\x08\x4a\xe4\xb5\x6d\x93\x43\x75\x92\xd4\x6d\x29\x4c\xc5\xe4\xb8\x62\x29\x36\x35\xba\x67\xa3\x89\x0a\xc0\x95\xf2\x1d\xf3\xa3\x3b\x1f\xed\x4e\x1d\xac\x2b\x5c\xa1\x4b\xe2\x7b\x17\xde\x6c\xca\x6a\xed\x05\xc2\x02\x7d\xb5\xe4\xbe\x30\x27\x95\xcd\x4c\x18\xfb\x78\x43\x03\xb8\xb7\xac\xb0\xbf\x2e\xb3\xd0\x3d\xaa\xed\x96\xa9\xcf\x45\x41\x4f\x71\x75\x3f\xd6\x6c\x45\x10\x3c\x5c\x4a\x43\x6d\xcf\x51\x67\xe0\x41\xe8\xc1\x6e\xd7\xda\xca\xf7\xff\xfe\xd7\xfb\xb7\xef\xdc\x26\xfe\x52\xd3\x31\x6a\x4a\x85\x64\xe6\xfe\xd5\x72\xd2\x1a\x9e\xfd\xf0\x8f\xbf\xff\xf8\xed\x7f\x6f\x71\x67\x58\x72\xbb\x05\x89\x3c\x35\xc6\x1b\x35\x57\x0a\x07\xb8\x9c\x79\x97\x8d\x4d\x22\xf6\x8d\x56\xbc\x61\x89\x76\xe8\x6d\x9a\x37\x3b\xa2\x44\xec\xed\xad\xd6\xf4\x9b\x32\x9d\x2d\xe7\x96\x70\x0b\x41\x9b\x25\x9a\x63\x22\x53\x73\x6d\x7f\x33\xcf\x91\x5f\x57\x25\x9f\x0b\x51\x9a\xfb\x13\x70\x21\x69\x41\xd2\x5c\xe4\x06\xc9\x83\x92\xa1\x9f\xe3\xdc\x28\x7c\x66\x0d\x0f\x53\x22\xad\xc6\xa1\x4e\x8a\x01\x24\xbd\x66\x5a\x57\x45\xe0\xf1\x4a\x24\x5f\x38\x5c\x03\x6f\x77\xc6\xd9\xc1\x75\xe9\xac\x0f\x81\xe9\x7a\xa2\x85\x6c\xe0\x73\x8f\xf4\x5e\x58\x05\xcc\xe2\x6c\x2e\x9d\x1f\x89\xea\xc5\xcb\x97\x06\xd1\xc0\x1f\x74\x31\x4a\x7b\x3d\x6b\xf0\x99\x64\xa8\x2a\xec\xfe\xfc\x74\x89\xa2\xc3\xd4\x3f\xc1\x52\x50\x33\x8f\xbf\x10\xc2\xc5\x68\x4f\x71\xbd\x39\x7f\x2e\x7b\xec\x31\x75\x13\x83\x43\x5e\xce\xfe\xf7\x1f\x38\x3b\x39\x3b\x37\xdc\xd2\x8f\x4e\x95\xe2\x2e\x4a\x5e\xb5\xaa\x72\xc4\xc6\xed\xca\x23\x99\x9c\x51\x22\x78\xaa\xb2\x65\x98\x0a\xbf\x40\x79\x9d\x88\x35\xf7\xe7\x42\x5c\x37\x9e\x1e\x91\x62\x29\x1f\x21\x25\x80\xf9\xe6\x67\x39\x53\xcb\x82\x15\x78\xcd\xb4\x08\x33\xba\x11\x15\x65\xea\x4d\x29\x52\x89\x65\xb6\x69\x5c\x5e\x36\x43\xa3\x5c\x58\xf6\x89\x70\x18\x4f\x57\x77\x26\xa3\xdd\xaa\xe3\x6e\xdc\xfd\x9d\xaf\xaa\xfe\xef\x5b\x55\xbf\x40\xc6\x07\x55\xff\x52\x94\xae\x62\x74\x67\x6f\x29\x5f\xb8\xd4\xa2\x34\xf7\x35\xf6\x25\xbd\x31\x8d\x37\xe4\x2e\xa3\xde\x6d\x19\x58\xfd\x77\x79\x63\x54\xf5\xc2\xfe\x1f\x9a\x22\x03\xb1\xa3\x5d\xd0\x54\x94\x39\x6a\x82\x8c\x24\xf5\x12\xdd\xfe\x56\x6c\x18\xe5\x09\xb8\xdb\x69\xcf\x5c\xdb\x6d\x89\x29\xe3\xb6\xe2\x01\xb7\x09\x91\x32\x93\x0e\x5a\x8b\x62\x50\x02\xa7\x65\x03\x36\x27\x94\x0b\x76\xe3\xdd\x96\x92\x3f\x7e\xf3\x0d\x9c\xc2\xfb\xb7\xef\x20\x84\x1f\xfe\xf6\x2d\x9c\xd9\xf6\x70\x2b\xad\x74\xa7\x38\x97\x92\xa0\xc4\x74\xbe\xd4\x5a\x70\x35\xe4\x22\xe4\xa9\xf9\x23\x13\x4b\x7b\x5c\x69\xa1\x5d\x0d\x35\x79\x39\xa8\xe1\x4d\xa9\x1b\x21\x8f\x2e\x25\x7f\xff\xdd\x3f\xbf\xff\xee\xaf\xef\xdf\xbe\xf3\x66\x4d\xd3\x9d\x38\x5b\x87\x2d\xc2\x19\xb4\x7b\x35\x2a\x6e\xae\x76\x1d\xc0\x23\x40\x46\xfd\x57\xe0\x4c\x76\x84\x67\xa1\xd7\xc3\xf3\xf5\x1e\xcf\xd7\x7b\x3c\xf0\xc1\x70\x48\x7b\x33\xad\xee\x14\x0e\xea\x78\xc0\x87\x27\xca\x9c\x90\x01\x5f\xde\x7e\xa4\xc6\xd9\xf3\x46\x39\x9e\x74\xf9\xd3\x50\x65\xdb\xe6\x18\x13\xfe\x72\x1e\xfc\x35\x59\xf0\xd7\xe1\xc0\xde\x12\x87\x94\x37\xc6\x70\x9d\x66\xcd\x7a\xfb\x5f\x4c\xfb\x73\x59\x3f\x1c\x5c\xa9\xf0\xea\x2f\x4b\x92\x1b\xff\x3c\x38\x0d\x4e\xec\xd3\xca\x95\x1a\xfd\x75\x1c\x28\x76\x9f\x74\xee\xaa\x55\xb9\x2b\x58\x2a\x51\x93\x7f\x1a\x9c\x05\xa7\x1f\x61\x20\xc0\xb2\x24\x94\x77\xd6\x72\xcf\x3f\x77\x95\xde\x6f\xcc\x9d\x55\x4c\x41\xe8\x09\xff\xe4\xd3\xce\x2d\xa6\xaa\x27\x9c\x0f\x04\xc5\x6d\x72\x2c\xb8\xd2\xe0\xa4\x21\xb2\x3f\xda\xce\xcf\x61\xfd\x02\x13\x57\xfe\xee\xd9\x9f\xec\x1e\x88\xdd\xee\xe0\xb8\x23\xf7\x92\x62\x49\x7a\x44\xd6\x4d\xb4\xe4\x25\x95\xa2\x23\xf7\x82\x4a\xd1\x9a\x17\x6b\x4e\xb2\x23\xf0\xcc\x8c\xb4\x24\x30\x29\x18\xbf\x07\xaf\x46\x44\x5e\xd7\x32\x2c\xe9\x98\x78\x92\xb4\xf4\x2d\x43\x2a\x63\xc0\xad\xff\xe0\x75\xf5\x40\x53\x3d\x31\x38\x15\xf7\xa4\x7a\x58\xcb\x1c\x7d\xe0\x49\xc1\x3d\x25\x4c\xa6\xa1\x7b\xbc\xff\x7f\x00\x00\x00\xff\xff\xd3\x10\x57\x08\xcd\x17\x00\x00") 275 | 276 | func layoutsLayoutHtmlBytes() ([]byte, error) { 277 | return bindataRead( 278 | _layoutsLayoutHtml, 279 | "layouts/layout.html", 280 | ) 281 | } 282 | 283 | func layoutsLayoutHtml() (*asset, error) { 284 | bytes, err := layoutsLayoutHtmlBytes() 285 | if err != nil { 286 | return nil, err 287 | } 288 | 289 | info := bindataFileInfo{name: "layouts/layout.html", size: 6093, mode: os.FileMode(420), modTime: time.Unix(1673409500, 0)} 290 | a := &asset{bytes: bytes, info: info} 291 | return a, nil 292 | } 293 | 294 | // Asset loads and returns the asset for the given name. 295 | // It returns an error if the asset could not be found or 296 | // could not be loaded. 297 | func Asset(name string) ([]byte, error) { 298 | cannonicalName := strings.Replace(name, "\\", "/", -1) 299 | if f, ok := _bindata[cannonicalName]; ok { 300 | a, err := f() 301 | if err != nil { 302 | return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) 303 | } 304 | return a.bytes, nil 305 | } 306 | return nil, fmt.Errorf("Asset %s not found", name) 307 | } 308 | 309 | // MustAsset is like Asset but panics when Asset would return an error. 310 | // It simplifies safe initialization of global variables. 311 | func MustAsset(name string) []byte { 312 | a, err := Asset(name) 313 | if err != nil { 314 | panic("asset: Asset(" + name + "): " + err.Error()) 315 | } 316 | 317 | return a 318 | } 319 | 320 | // AssetInfo loads and returns the asset info for the given name. 321 | // It returns an error if the asset could not be found or 322 | // could not be loaded. 323 | func AssetInfo(name string) (os.FileInfo, error) { 324 | cannonicalName := strings.Replace(name, "\\", "/", -1) 325 | if f, ok := _bindata[cannonicalName]; ok { 326 | a, err := f() 327 | if err != nil { 328 | return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) 329 | } 330 | return a.info, nil 331 | } 332 | return nil, fmt.Errorf("AssetInfo %s not found", name) 333 | } 334 | 335 | // AssetNames returns the names of the assets. 336 | func AssetNames() []string { 337 | names := make([]string, 0, len(_bindata)) 338 | for name := range _bindata { 339 | names = append(names, name) 340 | } 341 | return names 342 | } 343 | 344 | // _bindata is a table, holding each asset generator, mapped to its name. 345 | var _bindata = map[string]func() (*asset, error){ 346 | "article.html": articleHtml, 347 | "categories.html": categoriesHtml, 348 | "errors/404.html": errors404Html, 349 | "errors/500.html": errors500Html, 350 | "home.html": homeHtml, 351 | "layouts/layout.html": layoutsLayoutHtml, 352 | } 353 | 354 | // AssetDir returns the file names below a certain 355 | // directory embedded in the file by go-bindata. 356 | // For example if you run go-bindata on data/... and data contains the 357 | // following hierarchy: 358 | // data/ 359 | // foo.txt 360 | // img/ 361 | // a.png 362 | // b.png 363 | // then AssetDir("data") would return []string{"foo.txt", "img"} 364 | // AssetDir("data/img") would return []string{"a.png", "b.png"} 365 | // AssetDir("foo.txt") and AssetDir("notexist") would return an error 366 | // AssetDir("") will return []string{"data"}. 367 | func AssetDir(name string) ([]string, error) { 368 | node := _bintree 369 | if len(name) != 0 { 370 | cannonicalName := strings.Replace(name, "\\", "/", -1) 371 | pathList := strings.Split(cannonicalName, "/") 372 | for _, p := range pathList { 373 | node = node.Children[p] 374 | if node == nil { 375 | return nil, fmt.Errorf("Asset %s not found", name) 376 | } 377 | } 378 | } 379 | if node.Func != nil { 380 | return nil, fmt.Errorf("Asset %s not found", name) 381 | } 382 | rv := make([]string, 0, len(node.Children)) 383 | for childName := range node.Children { 384 | rv = append(rv, childName) 385 | } 386 | return rv, nil 387 | } 388 | 389 | type bintree struct { 390 | Func func() (*asset, error) 391 | Children map[string]*bintree 392 | } 393 | 394 | var _bintree = &bintree{nil, map[string]*bintree{ 395 | "article.html": &bintree{articleHtml, map[string]*bintree{}}, 396 | "categories.html": &bintree{categoriesHtml, map[string]*bintree{}}, 397 | "errors": &bintree{nil, map[string]*bintree{ 398 | "404.html": &bintree{errors404Html, map[string]*bintree{}}, 399 | "500.html": &bintree{errors500Html, map[string]*bintree{}}, 400 | }}, 401 | "home.html": &bintree{homeHtml, map[string]*bintree{}}, 402 | "layouts": &bintree{nil, map[string]*bintree{ 403 | "layout.html": &bintree{layoutsLayoutHtml, map[string]*bintree{}}, 404 | }}, 405 | }} 406 | 407 | // RestoreAsset restores an asset under the given directory 408 | func RestoreAsset(dir, name string) error { 409 | data, err := Asset(name) 410 | if err != nil { 411 | return err 412 | } 413 | info, err := AssetInfo(name) 414 | if err != nil { 415 | return err 416 | } 417 | err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) 418 | if err != nil { 419 | return err 420 | } 421 | err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) 422 | if err != nil { 423 | return err 424 | } 425 | err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) 426 | if err != nil { 427 | return err 428 | } 429 | return nil 430 | } 431 | 432 | // RestoreAssets restores an asset under the given directory recursively 433 | func RestoreAssets(dir, name string) error { 434 | children, err := AssetDir(name) 435 | // File 436 | if err != nil { 437 | return RestoreAsset(dir, name) 438 | } 439 | // Dir 440 | for _, child := range children { 441 | err = RestoreAssets(dir, filepath.Join(name, child)) 442 | if err != nil { 443 | return err 444 | } 445 | } 446 | return nil 447 | } 448 | 449 | func _filePath(dir, name string) string { 450 | cannonicalName := strings.Replace(name, "\\", "/", -1) 451 | return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) 452 | } 453 | -------------------------------------------------------------------------------- /web/assets/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.7 (http://getbootstrap.com) 3 | * Copyright 2011-2016 Twitter, Inc. 4 | * Licensed under the MIT license 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(a>this.$items.length-1||a<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.7",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.7",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);if(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),!c.isInStateTrue())return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null,a.$element=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.7",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.7",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); --------------------------------------------------------------------------------