├── CNAME
├── loading.gif
├── css
├── line.png
├── style.css
├── highlight.css
└── GitHub2.css
├── js
├── .DS_Store
├── director.min.js
├── marked.min.js
├── underscore-min.js
└── jquery-2.0.3.min.js
├── config.js
├── index.html
├── README.md
└── app.js
/CNAME:
--------------------------------------------------------------------------------
1 | wuhao.pub
2 |
--------------------------------------------------------------------------------
/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuhaoworld/github-issues-blog/HEAD/loading.gif
--------------------------------------------------------------------------------
/css/line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuhaoworld/github-issues-blog/HEAD/css/line.png
--------------------------------------------------------------------------------
/js/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuhaoworld/github-issues-blog/HEAD/js/.DS_Store
--------------------------------------------------------------------------------
/config.js:
--------------------------------------------------------------------------------
1 | var _config = {
2 | blog_name : '用于演示的博客', // 博客名称
3 | owner : 'lifesinger', // github 用户名
4 | repo : 'lifesinger.github.com',// github 中对应项目名
5 | duoshuo_id : 'hello1234', // 在第三方评论插件多说申请站点的子域名
6 | // access_token : 'abcde'+'fghijk', // 请求量大时需要在 github 后台单独设置一个读取公开库的 token, 注意将token 拆成两个字符串,否则会被系统自动删除掉
7 | per_page : '15' // 默认一页显示几篇文章
8 | }
9 |
10 | var duoshuoQuery = {short_name:_config['duoshuo_id']};
--------------------------------------------------------------------------------
/css/style.css:
--------------------------------------------------------------------------------
1 | body{
2 | padding: 0px 10px 30px 10px;
3 | }
4 | .blog_title{
5 | font-size: 26px;
6 | color: #333;
7 | text-decoration: none;
8 | }
9 | .blog_title:hover{
10 | color:#21759b;
11 | text-decoration: none;
12 | }
13 | h2{
14 | background-color: #eee;
15 | padding-left: 8px;
16 | margin-left: -8px;
17 | border-radius: 2px;
18 | }
19 | .avatar{
20 | display: inline;
21 | margin: 0px 0px;
22 | }
23 | .create_at{
24 | color: #999;
25 | display: block;
26 | margin: 15px 0px;
27 | font-size: 12px;
28 | }
29 |
30 | .loading{
31 | border: none;
32 | box-shadow:none;
33 | width: 32px;
34 | height: 32px;
35 | margin: 70px auto;
36 | }
37 | .prev{
38 | float: left;
39 | }
40 | .next{
41 | float: right;
42 | }
43 |
44 | @media only screen and (min-width: 600px) {
45 | #container {
46 | border: 1px solid #ccc;
47 | box-shadow: 2px -2px 10px #ddd;
48 | padding: 30px 60px 40px 60px;
49 | }
50 | }
51 |
52 | #header{
53 | margin-bottom: 20px;
54 | }
55 | .postlist{
56 | margin: 20px 0px;
57 | list-style: none;
58 | }
59 | ul{
60 | margin-top: 30px;
61 | }
62 | .postlist{
63 | padding: 0px;
64 | }
65 | .postlist a:hover{
66 | text-decoration: none;
67 | background-image: url(line.png);
68 | background-repeat: repeat-x;
69 | background-position: left bottom;
70 | padding-bottom: 4px;
71 | }
72 | img{
73 | clear: both;
74 | display: block;
75 | margin: 30px auto;
76 | }
77 | .datetime{
78 | float: right;
79 | color: #999;
80 | font-family: 'Open Sans', sans-serif
81 | }
82 | .ds-social-links{
83 | width: 300px !important; /* 修正多说在 iphone5 宽度下带来的横向滚动条的问题*/
84 | }
85 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 用于演示的博客
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
22 |

23 |
24 |
37 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 基于 Github issues 的单页面静态博客
2 |
3 | 玉伯的博客(https://github.com/lifesinger/lifesinger.github.com/issues )让我第一次知道 github issues 还可以这样用 ,作者发了很多干货技术文章,让我不由得感叹 ,文章不在于形式,也不在于写在哪里,只要是好文,总不会被埋没。
4 |
5 | 即便如此,很多人仍然希望能有一个独立域名、可以自由修改主题的博客。Wordpress 、Typecho 太重,还要买 VPS、部署服务器环境、安装插件、主题,太折腾人,于是我想,完全可以利用 Github 提供的 API 来实现一个只有一个静态页面的博客,具体思路如下:
6 |
7 | 1. 作者在 Github issues 上写文章(写 issues)
8 | 2. 博客页面通过 JS Ajax 请求 Github API 来获取文章内容,进行页面的渲染
9 | 3. 通过社会化评论插件实现评论功能
10 |
11 | 于是花了几天时间实现了这个设想, DEMO:http://wuhao.pub/
12 |
13 | 博客的 demo 内容是读取的玉伯博客的 issues。
14 |
15 | ## 1. 部署方法(方案1)
16 |
17 | 1. fork 本项目,然后再新建一个用于存放 blog(issues)的 repo。 (fork 的项目是没有 issue 的,所以得新建个项目)
18 | 2. 修改 gh-pages 分支下根目录的 config.js,填写好对应的博客名称,你自己的 github 用户名、对应项目名、多说 ID,保存。多说账号在这里申请http://duoshuo.com/
19 |
20 |
21 | var _config = {
22 | blog_name : '用于演示的博客', // 博客名称
23 | owner: 'lifesinger', // github 用户名
24 | repo: 'lifesinger.github.com',// 用于存放 blog(issues)的项目名
25 | duoshuo_id : 'hello1234', // 在第三方评论插件多说申请站点的子域名
26 | // access_token: '', // 请求量大时需要在 github 后台单独设置一个读取公开库的 token
27 | per_page: '15' // 默认一页显示几篇文章
28 | }
29 |
30 |
31 | 保存后即可通过 `http://用户名.github.io/github-issues-blog` 访问
32 |
33 | 注意:至少得有一次提交,github 的 pages 功能才会生效,直接 fork,没有任何修改是不行的。
34 |
35 | 如果你想绑定独立域名,修改根目录的 CNAME 文件,将其中的网址修改为你的域名,并把你的域名 CNAME 到 `用户名.github.io` 即可
36 |
37 | ## 2. 部署方法(方案2)
38 |
39 | 1.克隆本项目,修改根目录的 config.js
40 |
41 | var _config = {
42 | blog_name : '用于演示的博客', // 博客名称
43 | owner: 'lifesinger', // github 用户名
44 | repo: 'lifesinger.github.com',// github 中对应项目名
45 | duoshuo_id : 'hello1234', // 在第三方评论插件多说申请站点的子域名
46 | // access_token: '', // 请求量大时需要在 github 后台单独设置一个读取公开库的 token
47 | per_page: '15' // 默认一页显示几篇文章
48 | }
49 |
50 | 2.填写好对应的博客名称,你自己的 github 用户名、对应项目名和多说 ID,保存。多说账号在这里申请http://duoshuo.com/
51 | 3.将所有文件上传到一个静态空间,打开首页即可看到效果。
52 |
53 | 接下来就是在对应的 repo 的 issues 下写文章了!
54 |
55 | ## 3. 提高 API 访问次数的配额
56 |
57 | 默认情况下你是用匿名权限访问 github 接口的, github 的访问限制是一个小时最多 60 次请求,这显然是不够的,如何提高限制呢?
58 |
59 | 1. 到个人设置下的 Personal access tokens 页(https://github.com/settings/tokens ),如下图,点击右上角的 Generate new token
60 |
61 | 
62 |
63 | 2. 填写名称,只勾选 `public_repo`,然后保存,github 会生成一个可访问你公开项目的 access_token,将它填入到配置文件的 access_token 的值中,并取消注释。
64 | 
65 |
66 | 3. 打开 `app.js`,取消掉第 17 行和 88 行的注释,保存后重新上传即可
67 |
68 | data:{
69 | // access_token:_config['access_token']
70 | },
71 |
--------------------------------------------------------------------------------
/css/highlight.css:
--------------------------------------------------------------------------------
1 | /*
2 | Date: 17.V.2011
3 | Author: pumbur
4 | */
5 |
6 | .hljs {
7 | display: block;
8 | overflow-x: auto;
9 | padding: 0.5em;
10 | background: #222;
11 | -webkit-text-size-adjust: none;
12 | }
13 |
14 | .profile .hljs-header *,
15 | .ini .hljs-title,
16 | .nginx .hljs-title {
17 | color: #fff;
18 | }
19 |
20 | .hljs-comment,
21 | .hljs-preprocessor,
22 | .hljs-preprocessor .hljs-title,
23 | .hljs-pragma,
24 | .hljs-shebang,
25 | .profile .hljs-summary,
26 | .diff,
27 | .hljs-pi,
28 | .hljs-doctype,
29 | .hljs-tag,
30 | .css .hljs-rule,
31 | .tex .hljs-special {
32 | color: #444;
33 | }
34 |
35 | .hljs-string,
36 | .hljs-symbol,
37 | .diff .hljs-change,
38 | .hljs-regexp,
39 | .xml .hljs-attribute,
40 | .smalltalk .hljs-char,
41 | .xml .hljs-value,
42 | .ini .hljs-value,
43 | .clojure .hljs-attribute,
44 | .coffeescript .hljs-attribute {
45 | color: #ffcc33;
46 | }
47 |
48 | .hljs-number,
49 | .hljs-addition {
50 | color: #00cc66;
51 | }
52 |
53 | .hljs-built_in,
54 | .hljs-literal,
55 | .hljs-type,
56 | .hljs-typename,
57 | .go .hljs-constant,
58 | .ini .hljs-keyword,
59 | .lua .hljs-title,
60 | .perl .hljs-variable,
61 | .php .hljs-variable,
62 | .mel .hljs-variable,
63 | .django .hljs-variable,
64 | .css .funtion,
65 | .smalltalk .method,
66 | .hljs-hexcolor,
67 | .hljs-important,
68 | .hljs-flow,
69 | .hljs-inheritance,
70 | .hljs-name,
71 | .parser3 .hljs-variable {
72 | color: #32aaee;
73 | }
74 |
75 | .hljs-keyword,
76 | .hljs-tag .hljs-title,
77 | .css .hljs-tag,
78 | .css .hljs-class,
79 | .css .hljs-id,
80 | .css .hljs-pseudo,
81 | .css .hljs-attr_selector,
82 | .hljs-winutils,
83 | .tex .hljs-command,
84 | .hljs-request,
85 | .hljs-status {
86 | color: #6644aa;
87 | }
88 |
89 | .hljs-title,
90 | .ruby .hljs-constant,
91 | .vala .hljs-constant,
92 | .hljs-parent,
93 | .hljs-deletion,
94 | .hljs-template_tag,
95 | .css .hljs-keyword,
96 | .objectivec .hljs-class .hljs-id,
97 | .smalltalk .hljs-class,
98 | .lisp .hljs-keyword,
99 | .apache .hljs-tag,
100 | .nginx .hljs-variable,
101 | .hljs-envvar,
102 | .bash .hljs-variable,
103 | .go .hljs-built_in,
104 | .vbscript .hljs-built_in,
105 | .lua .hljs-built_in,
106 | .rsl .hljs-built_in,
107 | .tail,
108 | .avrasm .hljs-label,
109 | .tex .hljs-formula,
110 | .tex .hljs-formula * {
111 | color: #bb1166;
112 | }
113 |
114 | .hljs-doctag,
115 | .profile .hljs-header,
116 | .ini .hljs-title,
117 | .apache .hljs-tag,
118 | .parser3 .hljs-title {
119 | font-weight: bold;
120 | }
121 |
122 | .coffeescript .javascript,
123 | .javascript .xml,
124 | .tex .hljs-formula,
125 | .xml .javascript,
126 | .xml .vbscript,
127 | .xml .css,
128 | .xml .hljs-cdata {
129 | opacity: 0.6;
130 | }
131 |
132 | .hljs,
133 | .hljs-subst,
134 | .diff .hljs-chunk,
135 | .css .hljs-value,
136 | .css .hljs-attribute {
137 | color: #aaa;
138 | }
139 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | Ractive.DEBUG = false;
2 | function index(page){
3 | var page = parseInt(page) || 1;
4 | window._G = window._G || {post: {}, postList: {}};
5 | $('title').html(_config['blog_name']);
6 | if(_G.postList[page] != undefined){
7 | $('#container').html(_G.postList[page]);
8 | return;
9 | }
10 |
11 | $.ajax({
12 | url:"https://api.github.com/repos/"+_config['owner']+"/"+_config['repo']+"/issues",
13 | data:{
14 | filter : 'created',
15 | page : page,
16 | // access_token : _config['access_token'],
17 | per_page : _config['per_page']
18 | },
19 | beforeSend:function(){
20 | $('#container').html('
');
21 | },
22 | success:function(data, textStatus, jqXHR){
23 | var link = jqXHR.getResponseHeader("Link") || "";
24 | var next = false;
25 | var prev = false;
26 | if(link.indexOf('rel="next"') > 0){
27 | next = true;
28 | }
29 | if(link.indexOf('rel="prev"') > 0){
30 | prev = true;
31 | }
32 | var ractive = new Ractive({
33 | template : '#listTpl',
34 | data : {
35 | posts : data,
36 | next : next,
37 | prev : prev,
38 | page : page
39 | }
40 | });
41 | window._G.postList[page] = ractive.toHTML();
42 | $('#container').html(window._G.postList[page]);
43 |
44 | //将文章列表的信息存到全局变量中,避免重复请求
45 | for(i in data){
46 | var ractive = new Ractive({
47 | template : '#detailTpl',
48 | data : {post: data[i]}
49 | });
50 | window._G.post[data[i].number] = {};
51 | window._G.post[data[i].number].body = ractive.toHTML();
52 |
53 | var title = data[i].title + " | " + _config['blog_name'];
54 | window._G.post[data[i].number].title = title;
55 | }
56 | }
57 | });
58 | }
59 |
60 | function highlight(){
61 | $('pre code').each(function(i, block) {
62 | hljs.highlightBlock(block);
63 | });
64 | }
65 |
66 | // 动态加载多说评论框的函数
67 | function toggleDuoshuoComments(container, id){
68 | var el = document.createElement('div');
69 | var url = window.location.href;
70 | el.setAttribute('data-thread-key', id);
71 | el.setAttribute('data-url', url);
72 | DUOSHUO.EmbedThread(el);
73 | jQuery(container).append(el);
74 | }
75 |
76 | function detail(id){
77 | if(!window._G){
78 | window._G = {post: {}, postList: {}};
79 | window._G.post[id] = {};
80 | }
81 |
82 | if(_G.post[id].body != undefined){
83 | $('#container').html(_G.post[id].body);
84 | $('title').html(_G.post[id].title);
85 | toggleDuoshuoComments('#container', id);
86 | highlight();
87 | return;
88 | }
89 | $.ajax({
90 | url:"https://api.github.com/repos/"+_config['owner']+"/"+_config['repo']+"/issues/" + id,
91 | data:{
92 | // access_token:_config['access_token']
93 | },
94 | beforeSend:function(){
95 | $('#container').html('
');
96 | },
97 | success:function(data){
98 | var ractive = new Ractive({
99 | el: "#container",
100 | template: '#detailTpl',
101 | data: {post: data}
102 | });
103 |
104 | $('title').html(data.title + " | " + _config['blog_name']);
105 | toggleDuoshuoComments('#container', id);
106 | highlight();
107 | }
108 | });
109 |
110 | }
111 |
112 | var helpers = Ractive.defaults.data;
113 | helpers.markdown2HTML = function(content){
114 | return marked(content);
115 | }
116 | helpers.formatTime = function(time){
117 | return time.substr(0,10);
118 | }
119 |
120 | var routes = {
121 | '/': index,
122 | 'p:page': index,
123 | 'post/:postId': detail
124 | };
125 | var router = Router(routes);
126 | router.init('/');
--------------------------------------------------------------------------------
/js/director.min.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | //
4 | // Generated on Tue Dec 16 2014 12:13:47 GMT+0100 (CET) by Charlie Robbins, Paolo Fragomeni & the Contributors (Using Codesurgeon).
5 | // Version 1.2.6
6 | //
7 | (function(a){function k(a,b,c,d){var e=0,f=0,g=0,c=(c||"(").toString(),d=(d||")").toString(),h;for(h=0;hi.indexOf(d,e)||~i.indexOf(c,e)&&!~i.indexOf(d,e)||!~i.indexOf(c,e)&&~i.indexOf(d,e)){f=i.indexOf(c,e),g=i.indexOf(d,e);if(~f&&!~g||!~f&&~g){var j=a.slice(0,(h||1)+1).join(b);a=[j].concat(a.slice((h||1)+1))}e=(g>f?g:f)+1,h=0}else e=0}return a}function j(a,b){var c,d=0,e="";while(c=a.substr(d).match(/[^\w\d\- %@&]*\*[^\w\d\- %@&]*/))d=c.index+c[0].length,c[0]=c[0].replace(/^\*/,"([_.()!\\ %@&a-zA-Z0-9-]+)"),e+=a.substr(0,c.index)+c[0];a=e+=a.substr(d);var f=a.match(/:([^\/]+)/ig),g,h;if(f){h=f.length;for(var j=0;j7))this.history===!0?setTimeout(function(){window.onpopstate=d},500):window.onhashchange=d,this.mode="modern";else{var f=document.createElement("iframe");f.id="state-frame",f.style.display="none",document.body.appendChild(f),this.writeFrame(""),"onpropertychange"in document&&"attachEvent"in document&&document.attachEvent("onpropertychange",function(){event.propertyName==="location"&&c.check()}),window.setInterval(function(){c.check()},50),this.onHashChanged=d,this.mode="legacy"}e.listeners.push(a);return this.mode},destroy:function(a){if(!!e&&!!e.listeners){var b=e.listeners;for(var c=b.length-1;c>=0;c--)b[c]===a&&b.splice(c,1)}},setHash:function(a){this.mode==="legacy"&&this.writeFrame(a),this.history===!0?(window.history.pushState({},document.title,a),this.fire()):b.hash=a[0]==="/"?a:"/"+a;return this},writeFrame:function(a){var b=document.getElementById("state-frame"),c=b.contentDocument||b.contentWindow.document;c.open(),c.write("