├── .gitignore
├── LICENSE
├── README.md
├── _config.template.yml
├── _source
└── about
│ └── index.md
├── languages
├── default.yml
├── en.yml
├── ja.yml
├── zh-CN.yml
└── zh-TW.yml
├── layout
├── _layout.swig
├── _partial
│ ├── _feature
│ │ ├── balance.swig
│ │ ├── banderole.swig
│ │ └── common.swig
│ ├── _footer
│ │ ├── contact.swig
│ │ └── copyright.swig
│ ├── _head
│ │ ├── link.swig
│ │ └── meta.swig
│ ├── _header
│ │ ├── logo.swig
│ │ ├── mobile.swig
│ │ └── nav.swig
│ ├── _post
│ │ ├── comments.swig
│ │ ├── copyright.swig
│ │ ├── reward.swig
│ │ └── toc.swig
│ ├── archive.swig
│ ├── footer.swig
│ ├── friendlink.swig
│ ├── head.swig
│ ├── header.swig
│ ├── main.swig
│ ├── pagenum.swig
│ ├── post.swig
│ └── tagcloud.swig
├── _script
│ ├── _analytics
│ │ ├── baidu.swig
│ │ ├── busuanzi.swig
│ │ └── google.swig
│ ├── _comment
│ │ ├── changyan.swig
│ │ ├── disqus.swig
│ │ ├── gitalk.swig
│ │ ├── livere.swig
│ │ └── waline.swig
│ ├── _plugin
│ │ └── search.swig
│ ├── backtop.swig
│ ├── config.swig
│ └── script.swig
├── archive.swig
├── index.swig
├── page.swig
└── post.swig
├── package.json
├── screenshots
├── nlvi.png
└── style.png
└── source
├── assets
├── apple-launch-1125x2436.png
└── apple-touch-startup-image-2048x1496.png
├── font
├── allura
│ └── allura.ttf
└── iconfont
│ ├── iconfont.eot
│ ├── iconfont.svg
│ ├── iconfont.ttf
│ ├── iconfont.woff
│ └── iconfont.woff2
├── script
├── _src
│ ├── .babelrc
│ ├── package.json
│ ├── rollup.config.js
│ └── src
│ │ ├── balance.js
│ │ ├── banderole.js
│ │ ├── base.js
│ │ └── search.js
├── bootstarp.js
├── lib
│ ├── lightbox
│ │ ├── css
│ │ │ └── lightbox.min.css
│ │ ├── images
│ │ │ ├── close.png
│ │ │ ├── loading.gif
│ │ │ ├── next.png
│ │ │ └── prev.png
│ │ └── js
│ │ │ └── lightbox.min.js
│ └── pjax
│ │ └── pjax.js
└── scheme
│ ├── balance.js
│ └── banderole.js
├── style
├── _common
│ ├── _utils
│ │ ├── device.styl
│ │ ├── fonts.styl
│ │ └── iconfont.styl
│ ├── animation.styl
│ ├── func.styl
│ ├── global.styl
│ ├── normalize.styl
│ └── variable.styl
├── _custom
│ └── custom.styl
├── _partial
│ ├── _code
│ │ ├── frame.styl
│ │ ├── highlight.styl
│ │ └── index.styl
│ ├── _common
│ │ ├── about.styl
│ │ ├── archive.styl
│ │ ├── friendlink.styl
│ │ ├── header.styl
│ │ └── post.styl
│ ├── _page
│ │ └── paginator.styl
│ ├── _post
│ │ ├── copyright.styl
│ │ ├── footnote.styl
│ │ └── reward.styl
│ ├── _schemes
│ │ ├── _balance
│ │ │ ├── _post
│ │ │ │ └── toc.styl
│ │ │ ├── _utils
│ │ │ │ ├── backtop.styl
│ │ │ │ ├── search.styl
│ │ │ │ └── tagcloud.styl
│ │ │ ├── container.styl
│ │ │ ├── header.styl
│ │ │ └── index.styl
│ │ ├── _banderole
│ │ │ ├── _post
│ │ │ │ └── toc.styl
│ │ │ ├── _utils
│ │ │ │ ├── backtop.styl
│ │ │ │ ├── search.styl
│ │ │ │ └── tagcloud.styl
│ │ │ ├── container.styl
│ │ │ ├── header.styl
│ │ │ └── index.styl
│ │ └── common.styl
│ └── footer.styl
└── style.styl
└── syuanpi
└── syuanpi.min.css
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | .DS_Store
6 | /node_modules
7 | /.vscode
8 |
9 | # Runtime data
10 | pids
11 | *.pid
12 | *.seed
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # node-waf configuration
27 | .lock-wscript
28 |
29 | # Compiled binary addons (http://nodejs.org/api/addons.html)
30 | build/Release
31 |
32 | # Dependency directories
33 | node_modules
34 | jspm_packages
35 |
36 | # Optional npm cache directory
37 | .npm
38 |
39 | # Optional REPL history
40 | .node_repl_history
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017-2019 Co1MugX
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # hexo-theme-Nlvi
2 |
3 | > A simple theme named Nlvi for hexo.
4 |
5 | 在线预览 · [Banderole][1] · [Balance][2]
6 |
7 | [主题文档|Theme Wiki][3]
8 |
9 | 
10 |
11 | 
12 |
13 | (前排提醒老粉:你们熟悉的Nlvi现已归入 1.x 分支与 2.x 分支)
14 |
15 | ## 特点 Special
16 |
17 | - 使用了动画库 [Syuanpi.css][4]
18 | - 使用了 Lightbox
19 | - 自定义样式
20 | - 文章打赏(二维码)
21 | - 版权说明
22 | - 主题:常规颜色、桜、初音绿、雪初音蓝。自定义颜色(建议明度不要太高)
23 |
24 | ## 语言 Language
25 |
26 | - 简体中文
27 | - 正體中文(你可以告知我哪裡出問題,多謝。)
28 | - English (Please tell me all errors if you mind. Thank you very much.)
29 | - 日本語 「テーマはすべてのエラーを教えてください、ありがとうございました。」
30 |
31 | ## 下载 Download
32 |
33 | 你有三种方式下载到Nlvi
34 |
35 | 1. `git clone https://github.com/ColMugX/hexo-theme-Nlvi.git themes/Nlvi`
36 |
37 | 2. `Download ZIP`
38 |
39 | 3. `Open in Desktop`
40 |
41 | ## 安装 Install
42 |
43 | 1. 在 Hexo 所在目录中执行以下命令。
44 |
45 | ```bash
46 | npm i hexo-renderer-swig --save
47 | ```
48 |
49 | 2. 放到你的 Hexo 项目的`themes`文件夹里面,修改你的 Hexo 项目的 `_config.yml` 里面的`theme`如下。
50 |
51 | ```yaml
52 | # Extensions
53 | ## Plugins: https://hexo.io/plugins/
54 | ## Themes: https://hexo.io/themes/
55 | theme: Nlvi
56 | ```
57 |
58 | ## 配置 Setting
59 |
60 | 须将`_source`文件夹里面的`about`目录拷贝到项目中的`source`目录中,**这点很重要!**
61 |
62 | ### 支持 Support
63 |
64 | {Issue, Email, Zhihu?}
65 |
66 | [1]: https://colmugx.github.io/banderole/
67 | [2]: https://colmugx.github.io/balance/
68 | [3]: https://github.com/ColMugX/hexo-theme-Nlvi/wiki
69 | [4]: https://github.com/colmugx/syuanpi.css
70 |
--------------------------------------------------------------------------------
/_config.template.yml:
--------------------------------------------------------------------------------
1 | # ===========================================
2 | # Information Settings
3 | # ===========================================
4 |
5 | #navigation
6 | menu:
7 | article: /
8 | archives: /archives
9 | tags: /tags
10 | about: /about
11 | # rss: /atom.xml
12 |
13 | # favicon
14 | favicon:
15 |
16 | # ===========================================
17 | # Color&Schemes Setting
18 | # ===========================================
19 |
20 | ## Common
21 |
22 | # Navigator Language
23 | local-nav: false
24 |
25 | # Mobile-Header
26 | # light | dark
27 | mobile-header: light
28 |
29 | # Scheme
30 | # banderole | balance
31 | scheme: banderole
32 |
33 | # Header Style
34 | # line | block
35 | header-style: block
36 |
37 | # theme-style
38 | # default: dark | green | blue | purple
39 | # miku | sakura | snowmiku
40 | theme-style: sakura
41 |
42 | # ===========================================
43 | # Article Setting
44 | # ===========================================
45 |
46 | # Navigator in post
47 | toc: true
48 |
49 | # Use copyright infomation
50 | copyright:
51 | enable: true
52 | # license:
53 |
54 | #Reword button or message(fill your QRCode of any Pay.)
55 | reward:
56 | enable: false
57 | # message:
58 | qrcode:
59 | # alipay:
60 | # url:
61 | # message:
62 | # wechat:
63 | # url:
64 | # message:
65 | # paypal:
66 | # url:
67 | # message:
68 |
69 | # ===========================================
70 | # Social Contact
71 | # ===========================================
72 |
73 | # Comments // Please only switch one on.
74 | comments:
75 | enable: false
76 |
77 | # disqus:
78 | # livere_datauid:
79 |
80 | # changyan:
81 | # appid:
82 | # appkey:
83 |
84 | # gitalk:
85 | # owner:
86 | # repo:
87 | # client_id:
88 | # client_secret:
89 | # admin:
90 |
91 | # waline:
92 | # serverURL:
93 | # path: window.location.pathname
94 | # meta: []
95 | # lang: en
96 | # requiredMeta: ['nick', 'mail']
97 | # emoji: ['https://cdn.jsdelivr.net/gh/walinejs/emojis/weibo']
98 | # dark: html[data-user-color-scheme="dark"]
99 | # wordLimit: 0
100 | # pageSize: 10
101 |
102 | # sub //Please just fill out ID.
103 | sub:
104 | facebook:
105 | twitter:
106 | instagram:
107 | weibo:
108 | zhihu:
109 | github:
110 | linkedin:
111 | douban:
112 | medium:
113 | yuque:
114 |
115 | #Friend link
116 | # friendLink:
117 |
118 | # ===========================================
119 | # Analytics
120 | # ===========================================
121 |
122 | analytics:
123 | google:
124 | baidu:
125 |
126 | #visit_count(Powered by busuanzi on http://busuanzi.ibruce.info/)
127 | visit_count: false
128 |
129 | # ===========================================
130 | # Theme Settings
131 | # ===========================================
132 |
133 | # pjax
134 | pjax: false
135 |
136 | # mathjax
137 | mathjax: false
138 | mathjax3: false
139 |
140 | # lightbox
141 | lightbox: true
142 |
143 | # lazyload
144 | lazyload: false
145 |
146 | # animate (Powered by Syuanpi on https://colmugx.github.io/Syuanpi.css)
147 | animate: true
148 |
149 | # Created Time
150 | since: 2016
151 |
152 | # plugin
153 | # plugins:
154 |
155 | # about
156 | # self:
157 |
158 | # ===========================================
159 | # Search Settings
160 | # ===========================================
161 |
162 | # Search
163 | search:
164 | enable: true
165 | label: 搜索一下?
166 |
167 | # ===========================================
168 | # Documents Styles
169 | # ===========================================
170 |
171 | # article-fonts
172 | # default | Simsun | YaHei | Hel | Custom
173 | fonts-style: default
174 |
175 | # code-fonts
176 | # default | Custom
177 | code-style: default
178 |
179 | # ===========================================
180 | # CDN Path
181 | # ===========================================
182 |
183 | # cdn:
184 | # mathjax:
185 | # polyfill:
186 | # mathjax3:
187 | # lazyload:
188 |
189 | # ===========================================
190 | # WebApp Settings (iOS)
191 | # ===========================================
192 |
193 |
194 | # ===========================================
195 | # Version
196 | # ===========================================
197 | version: 3.0
198 |
199 | #theme_author: Co1MugX
200 |
--------------------------------------------------------------------------------
/_source/about/index.md:
--------------------------------------------------------------------------------
1 | title: About
2 | type: "about"
3 | comments: false
4 | ---
--------------------------------------------------------------------------------
/languages/default.yml:
--------------------------------------------------------------------------------
1 | nav:
2 | article: Articles
3 | archives: Archives
4 | tags: Tags
5 | categories: Categories
6 | about: About
7 | menu: Menu
8 | search: Search
9 | rss: RSS
10 |
11 | page:
12 | prev: Prev
13 | next: Next
14 | comment: Comment
15 | share: Share
16 | hide: Hide
17 | readmore: Read More...
18 | para: '%d Posts In Total'
19 | now: now in
20 | reward: reward
21 | index: Index
22 |
23 | copyright:
24 | author: Author
25 | url: Url
26 | origin: Origin
27 | default: "All rights reserved. Please contact the author for authorization to reprint."
28 |
29 | friendlink: Friends
30 |
31 | #footer
32 | come: Powered by
33 | theme: Theme
34 |
--------------------------------------------------------------------------------
/languages/en.yml:
--------------------------------------------------------------------------------
1 | nav:
2 | article: Articles
3 | archives: Archives
4 | tags: Tags
5 | categories: Categories
6 | about: About
7 | menu: Menu
8 | search: Search
9 | rss: RSS
10 |
11 | page:
12 | prev: Prev
13 | next: Next
14 | comment: Comment
15 | share: Share
16 | hide: Hide
17 | readmore: Read More...
18 | para: '%d Posts In Total'
19 | now: now in
20 | reward: reward
21 | index: Index
22 |
23 | copyright:
24 | author: Author
25 | url: Url
26 | origin: Origin
27 | default: "All rights reserved. Please contact the author for authorization to reprint."
28 |
29 | friendlink: Friends
30 |
31 | #footer
32 | come: Powered by
33 | theme: Theme
34 |
--------------------------------------------------------------------------------
/languages/ja.yml:
--------------------------------------------------------------------------------
1 | nav:
2 | article: キジ
3 | archives: アーカイブ
4 | tags: タグ
5 | categories: カテゴリ
6 | about: ついて
7 | menu: メニュー
8 | search: サーチ
9 | rss: 購読
10 | page:
11 | prev: 前のページ
12 | next: 後のページ
13 | comment: コメント
14 | share: シェア
15 | hide: 隠す
16 | readmore: もっと見る...
17 | para: '%d Posts'
18 | now: 今
19 | reward: 褒賞
20 | index: 目次
21 |
22 | copyright:
23 | author: 著者
24 | url: URL
25 | origin: Origin
26 | default: "All rights reserved. Please contact the author for authorization to reprint."
27 |
28 | friendlink: 私の友達
29 |
30 | #footer
31 | come: Powered by
32 | theme: Theme
33 |
--------------------------------------------------------------------------------
/languages/zh-CN.yml:
--------------------------------------------------------------------------------
1 | nav:
2 | article: 文章
3 | archives: 归档
4 | tags: 标签
5 | categories: 分类
6 | about: 关于
7 | menu: 菜单
8 | search: 搜索
9 | rss: 订阅
10 |
11 | page:
12 | prev: 上一页
13 | next: 下一页
14 | comment: 留言
15 | share: 分享
16 | hide: 隐藏
17 | readmore: 继续阅读
18 | para: 一共有 %d 篇文章
19 | now: 正在查看
20 | reward: 赞赏
21 | index: 文章导航
22 |
23 | copyright:
24 | author: 作者
25 | url: 地址
26 | origin: 来源
27 | default: 著作权归作者所有,转载请联系作者获得授权。
28 |
29 | friendlink: 朋友列表
30 |
31 | #footer
32 | come: 动力来源于
33 | theme: 主题
34 |
--------------------------------------------------------------------------------
/languages/zh-TW.yml:
--------------------------------------------------------------------------------
1 | nav:
2 | article: 記事
3 | archives: 歸檔
4 | tags: 標籤
5 | categories: 分類
6 | about: 關於
7 | menu: 菜單
8 | search: 搜尋
9 | rss: 訂閱
10 |
11 | page:
12 | prev: 上一頁
13 | next: 下一頁
14 | comment: 留言
15 | share: 分享
16 | hide: 藏起
17 | excerpt_link: 接著讀…
18 | para: 共計 %d 篇文章
19 | now: 在看標籤
20 | reward: 打賞
21 | index: 文章目錄
22 |
23 | copyright:
24 | author: 作家
25 | url: 網路地址
26 | origin: 目標
27 | default: 著作權歸作者所有,轉載請聯絡作者獲得授權。
28 |
29 | friendlink: 好友連結
30 |
31 | #footer
32 | come: 動力來自
33 | theme: 樣式
34 |
--------------------------------------------------------------------------------
/layout/_layout.swig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {% include "_partial/head.swig" %}
5 | {% block title %}{% endblock %}
6 |
7 |
8 |
9 | {% include "_partial/header.swig" %}
10 |
11 |
12 |
13 | {% block main %} {% endblock %}
14 |
15 |
16 | {% include "./_partial/footer.swig" %}
17 |
18 | {% include "./_partial/tagcloud.swig" %}
19 |
20 | {% include "_partial/_feature/common.swig" %}
21 |
22 |
23 |
--------------------------------------------------------------------------------
/layout/_partial/_feature/balance.swig:
--------------------------------------------------------------------------------
1 | {% if is_post() %}
2 | {% include "../_post/toc.swig" %}
3 | {% endif %}
4 |
--------------------------------------------------------------------------------
/layout/_partial/_feature/banderole.swig:
--------------------------------------------------------------------------------
1 | {% if is_post() %}
2 | {% include "../_post/toc.swig" %}
3 | {% endif %}
--------------------------------------------------------------------------------
/layout/_partial/_feature/common.swig:
--------------------------------------------------------------------------------
1 | {% include "../../_script/backtop.swig" %}
2 | {% include "../../_script/_plugin/search.swig" %}
3 | {% include "../../_script/script.swig" %}
4 |
--------------------------------------------------------------------------------
/layout/_partial/_footer/contact.swig:
--------------------------------------------------------------------------------
1 |
18 |
--------------------------------------------------------------------------------
/layout/_partial/_footer/copyright.swig:
--------------------------------------------------------------------------------
1 |
2 |
3 | ©
4 | {{ theme.since }} ~ {{ date(Date.now(), 'YYYY') }}
5 | ❤
6 | {{ config.author }}
7 |
8 |
9 |
10 | {{ __('come') }}
11 | Hexo
12 |
13 |
14 | {{ __('theme') }}
15 | Nlvi
16 |
17 |
18 | {% if theme.visit_count %}
19 |
20 |
21 |
22 |
23 |
24 |
25 | {% endif %}
26 |
27 |
--------------------------------------------------------------------------------
/layout/_partial/_head/link.swig:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ css('style/style') }}
4 | {% include "../../_script/config.swig" %}
5 |
6 | {# lightbox #}
7 | {% if theme.lightbox %}
8 | {{ css('script/lib/lightbox/css/lightbox.min') }}
9 | {% endif %}
10 |
11 | {# animation #}
12 | {% if theme.animate %}
13 | {{ css('syuanpi/syuanpi.min') }}
14 | {% endif %}
15 |
16 | {# favicon #}
17 | {% if theme.favicon %}
18 |
19 | {% endif %}
20 |
21 | {# rss #}
22 | {% if theme.menu.rss %}
23 |
24 | {% endif %}
25 |
26 | {# any analytics #}
27 | {% include "../../_script/_analytics/google.swig" %}
28 | {% include "../../_script/_analytics/baidu.swig" %}
29 | {% include "../../_script/_analytics/busuanzi.swig" %}
30 |
--------------------------------------------------------------------------------
/layout/_partial/_head/meta.swig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | {% if theme.favicon %}
12 |
13 |
14 | {% endif %}
15 | {% if config.subtitle %}
16 |
17 | {% endif %}
18 | {% if config.description %}
19 |
20 | {% endif %}
21 | {% if config.keywords %}
22 |
23 | {% endif %}
24 | {% if page.tags and page.tags.length %}
25 |
26 | {% endif %}
27 |
--------------------------------------------------------------------------------
/layout/_partial/_header/logo.swig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {% if config.subtitle %}
5 |
{{config.subtitle}}
6 | {% endif %}
7 |
8 |
9 |
--------------------------------------------------------------------------------
/layout/_partial/_header/mobile.swig:
--------------------------------------------------------------------------------
1 |
29 |
--------------------------------------------------------------------------------
/layout/_partial/_header/nav.swig:
--------------------------------------------------------------------------------
1 |
39 |
--------------------------------------------------------------------------------
/layout/_partial/_post/comments.swig:
--------------------------------------------------------------------------------
1 | {% if theme.comments.enable or page.comments %}
2 |
3 |
10 | {% endif %}
11 |
--------------------------------------------------------------------------------
/layout/_partial/_post/copyright.swig:
--------------------------------------------------------------------------------
1 | {% if theme.copyright.enable %}
2 |
3 |
4 |
5 | {{ __('copyright.author') }} :
6 | {{ config.author }}
7 |
8 |
12 |
16 |
17 | {% set license = page.license || theme.copyright.license || __('copyright.default') %}
18 | {{ license }}
19 |
20 |
21 | {% endif %}
--------------------------------------------------------------------------------
/layout/_partial/_post/reward.swig:
--------------------------------------------------------------------------------
1 | {% if theme.reward.enable %}
2 |
3 |
{{ theme.reward.message or __('page.reward') }}
4 |
5 | {% for name, value in theme.reward.qrcode %}
6 |
7 | {% if theme.lightbox %}
8 |
9 |
10 |
11 | {% else %}
12 |
 }})
13 | {% endif %}
14 |
15 | {% endfor %}
16 |
17 |
18 | {% endif %}
19 |
--------------------------------------------------------------------------------
/layout/_partial/_post/toc.swig:
--------------------------------------------------------------------------------
1 | {% if theme.toc %}
2 |
8 | {% endif %}
9 |
--------------------------------------------------------------------------------
/layout/_partial/archive.swig:
--------------------------------------------------------------------------------
1 | {% macro render() %}
2 |
3 | {% if page.archive %}
4 |
5 |
{{ __('page.para', site.posts.length) }}
6 | {% elif page.category %}
7 |
{{ __('page.now') }}
8 |
— {{ page.category }}
9 | {% endif %}
10 | {% set year %}
11 | {% for post in page.posts %}
12 | {% if page.archive %}
13 | {% set post.year = date(post.date, 'YYYY') %}
14 | {% if post.year !== year %}
15 | {% if year !== null %}
16 |
17 | {% endif %}
18 | {% set year = post.year %}
19 |
20 |
21 |
22 | {% endif %}
23 | {% else %}
24 |
25 | {% endif %}
26 |
27 |
28 | {% if post.excerpt %}
29 | {{ post.excerpt }}
30 | {% else %}
31 | {{ post.content }}
32 | {% endif %}
33 |
34 |
35 |
38 |
39 | {{ post.title }}
40 |
41 |
42 |
43 | {% endfor %}
44 | {% if page.posts.length %}
45 |
46 | {% endif %}
47 |
48 | {% include "pagenum.swig" %}
49 | {% endmacro %}
50 |
--------------------------------------------------------------------------------
/layout/_partial/footer.swig:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/layout/_partial/friendlink.swig:
--------------------------------------------------------------------------------
1 | {% if theme.friendLink %}
2 |
3 |
{{ __('friendlink') }}
4 |
5 | {% for name, url in theme.friendLink %}
6 |
11 | {% endfor %}
12 |
13 |
14 | {% endif %}
--------------------------------------------------------------------------------
/layout/_partial/head.swig:
--------------------------------------------------------------------------------
1 | {% include "./_head/meta.swig" %}
2 | {% include "./_head/link.swig" %}
3 |
9 |
--------------------------------------------------------------------------------
/layout/_partial/header.swig:
--------------------------------------------------------------------------------
1 |
7 | {% include "_header/mobile.swig" %}
8 | {% if theme.scheme == 'balance' %}
9 | {% include "./_feature/balance.swig" %}
10 | {% endif %}
11 |
--------------------------------------------------------------------------------
/layout/_partial/main.swig:
--------------------------------------------------------------------------------
1 | {% import "post.swig" as postPage %}
2 |
3 |
4 | {% for post in page.posts %}
5 | {{ postPage.render(post) }}
6 | {% endfor %}
7 |
8 |
--------------------------------------------------------------------------------
/layout/_partial/pagenum.swig:
--------------------------------------------------------------------------------
1 | {% if not is_post() %}
2 |
9 | {% else %}
10 |
26 | {% endif %}
27 |
--------------------------------------------------------------------------------
/layout/_partial/post.swig:
--------------------------------------------------------------------------------
1 | {% macro render(post) %}
2 | {% if !config.mathjax && !config.mathjax3 && page.mathjax %}
3 |
4 |
5 | {% endif %}
6 |
10 |
32 |
33 | {% if is_post() %}
34 | {{ post.content }}
35 | {% else %}
36 | {% if post.excerpt %}
37 | {{ post.excerpt }}
38 | {% else %}
39 | {{ post.content }}
40 | {% endif %}
41 | {% endif %}
42 | {% if post.excerpt and is_home() %}
43 |
44 |
45 | {{ __('page.readmore') }}
46 |
47 |
48 | {% endif %}
49 |
50 | {% if post.tags and post.tags.length %}
51 |
52 | {% for tag in post.tags %}
53 |
{{ tag.name }}
54 | {% endfor %}
55 |
56 | {% endif %}
57 | {% if is_post() %}
58 | {% include "_post/reward.swig" %}
59 | {% include "_post/copyright.swig" %}
60 | {% endif %}
61 |
62 | {% if is_post() %}
63 | {% include "pagenum.swig" %}
64 | {% include "_post/comments.swig" %}
65 |
66 | {% endif %}
67 | {% if theme.scheme == 'banderole' %}
68 | {% include "./_feature/banderole.swig" %}
69 | {% endif %}
70 | {% endmacro %}
71 |
--------------------------------------------------------------------------------
/layout/_partial/tagcloud.swig:
--------------------------------------------------------------------------------
1 |
2 |
3 | {% for tag in site.tags %}
4 |
5 |
6 |
7 | {% endfor %}
8 |
9 | {% for tag in site.tags %}
10 |
11 |
{{ tag.name }}
12 | {% for post in tag.posts %}
13 |
19 | {% endfor %}
20 |
21 | {% endfor %}
22 |
23 |
--------------------------------------------------------------------------------
/layout/_script/_analytics/baidu.swig:
--------------------------------------------------------------------------------
1 | {% if theme.analytics.baidu %}
2 |
11 | {% endif %}
--------------------------------------------------------------------------------
/layout/_script/_analytics/busuanzi.swig:
--------------------------------------------------------------------------------
1 | {% if theme.visit_count %}
2 |
3 | {% endif %}
4 |
--------------------------------------------------------------------------------
/layout/_script/_analytics/google.swig:
--------------------------------------------------------------------------------
1 | {% if theme.analytics.google %}
2 |
3 |
4 |
11 |
12 | {% endif %}
13 |
--------------------------------------------------------------------------------
/layout/_script/_comment/changyan.swig:
--------------------------------------------------------------------------------
1 | {% if theme.comments.changyan %}
2 |
3 |
4 |
11 | {% endif %}
--------------------------------------------------------------------------------
/layout/_script/_comment/disqus.swig:
--------------------------------------------------------------------------------
1 | {% if theme.comments.disqus %}
2 |
3 |
6 |
17 | {% endif %}
18 |
--------------------------------------------------------------------------------
/layout/_script/_comment/gitalk.swig:
--------------------------------------------------------------------------------
1 | {% if theme.comments.gitalk %}
2 |
3 |
4 |
5 |
6 |
7 |
20 | {% endif %}
21 |
--------------------------------------------------------------------------------
/layout/_script/_comment/livere.swig:
--------------------------------------------------------------------------------
1 | {% if theme.comments.livere_datauid %}
2 |
3 |
4 |
5 |
15 | {% endif %}
16 |
--------------------------------------------------------------------------------
/layout/_script/_comment/waline.swig:
--------------------------------------------------------------------------------
1 | {% if theme.comments.waline %}
2 |
3 |
4 |
5 |
6 |
22 | {% endif %}
23 |
--------------------------------------------------------------------------------
/layout/_script/_plugin/search.swig:
--------------------------------------------------------------------------------
1 | {% if theme.search.enable %}
2 |
8 | {% endif %}
9 |
--------------------------------------------------------------------------------
/layout/_script/backtop.swig:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1%
4 |
--------------------------------------------------------------------------------
/layout/_script/config.swig:
--------------------------------------------------------------------------------
1 |
19 |
--------------------------------------------------------------------------------
/layout/_script/script.swig:
--------------------------------------------------------------------------------
1 | {{ js('https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js') }}
2 |
3 | {% set cdn_script_mathjax = theme.cdn.mathjax || "https://cdn.jsdelivr.net/npm/mathjax@2.7.7/unpacked/MathJax.js?config=TeX-AMS-MML_HTMLorMML" %}
4 | {% set cdn_script_polyfill = theme.cdn.polyfill || "https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=es6" %}
5 | {% set cdn_script_mathjax3 = theme.cdn.mathjax3 || "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js" %}
6 | {% set cdn_script_lazyload = theme.cdn.lazyload || "https://cdn.jsdelivr.net/npm/lazysizes@5.1.1/lazysizes.min.js" %}
7 |
8 | {% if theme.lightbox %}
9 |
10 |
11 | {% endif %}
12 |
13 | {% if theme.mathjax %}
14 |
15 |
16 | {% endif %}
17 |
18 | {% if theme.mathjax3 %}
19 |
20 |
27 |
28 | {% endif %}
29 |
30 | {% if theme.lazyload %}
31 |
32 | {% endif %}
33 |
34 | {% if theme.pjax %}
35 | {{ js('script/lib/pjax/pjax') }}
36 | {% endif %}
37 |
38 | {% if theme.scheme == 'banderole' %}
39 | {{ js('script/scheme/banderole') }}
40 | {% else %}
41 | {{ js('script/scheme/balance') }}
42 | {% endif %}
43 |
44 | {{ js('script/bootstarp') }}
45 |
46 | {% if is_post() %}
47 |
60 | {% endif %}
61 |
--------------------------------------------------------------------------------
/layout/archive.swig:
--------------------------------------------------------------------------------
1 | {% extends "_layout.swig" %}
2 | {% import "_partial/archive.swig" as archivet %}
3 |
4 | {% block title %}
5 | {% if page.archive %}
6 | {{ __('nav.archives') }} · {{ config.title }}
7 | {% elif page.tag %}
8 | {{ page.tag }} · {{ config.title }}
9 | {% endif %}
10 | {% endblock %}
11 |
12 | {% block main %}
13 | {{ archivet.render() }}
14 | {% endblock %}
--------------------------------------------------------------------------------
/layout/index.swig:
--------------------------------------------------------------------------------
1 | {% extends "_layout.swig" %}
2 |
3 | {% block title %}
4 | {{config.title}}
5 | {% if config.subtitle %}
6 | | {{ config.subtitle }}
7 | {% endif %}
8 | {% endblock %}
9 |
10 |
11 | {% block main %}
12 | {% include "_partial/main.swig" %}
13 | {% include "_partial/pagenum.swig" %}
14 | {% endblock %}
15 |
16 |
--------------------------------------------------------------------------------
/layout/page.swig:
--------------------------------------------------------------------------------
1 | {% extends "_layout.swig" %}
2 | {% block title %}
3 | {{config.title}}
4 | {% if config.subtitle %}
5 | | {{ config.subtitle }}
6 | {% endif %}
7 | {% endblock %}
8 |
9 | {% block main %}
10 | {% if page.type === 'about' %}
11 |
12 |
{{ __('nav.about') }}
13 |
14 | {% if theme.self %}
15 | {{ theme.self }}
16 | {% else %}
17 | {{ page.content }}
18 | {% endif %}
19 |
20 |
21 | {% include "_partial/friendlink.swig" %}
22 | {% include "_partial/_post/comments.swig" %}
23 | {% else %}
24 | {{ page.content }}
25 | {% endif %}
26 | {% endblock %}
27 |
--------------------------------------------------------------------------------
/layout/post.swig:
--------------------------------------------------------------------------------
1 | {% extends "_layout.swig" %}
2 | {% import '_partial/post.swig' as postPage %}
3 |
4 | {% block title %} {{ page.title }} · {{ config.title }} {% endblock %}
5 |
6 | {% block main %}
7 | {{ postPage.render(page) }}
8 | {% endblock %}
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hexo-theme-nlvi",
3 | "version": "2.0.1",
4 | "description": "A simple theme named Nlvi for hexo.",
5 | "main": "index.js",
6 | "scripts": {
7 |
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/ColMugX/hexo-theme-Nlvi.git"
12 | },
13 | "keywords": [
14 | "Hexo",
15 | "Nlvi"
16 | ],
17 | "author": "ColMugX
",
18 | "license": "MIT",
19 | "bugs": {
20 | "url": "https://github.com/ColMugX/hexo-theme-Nlvi/issues"
21 | },
22 | "homepage": "https://github.com/ColMugX/hexo-theme-Nlvi#readme"
23 | }
24 |
--------------------------------------------------------------------------------
/screenshots/nlvi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colmugx/hexo-theme-Nlvi/257bf09d3c3500437b9684e3032f469d847925a0/screenshots/nlvi.png
--------------------------------------------------------------------------------
/screenshots/style.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colmugx/hexo-theme-Nlvi/257bf09d3c3500437b9684e3032f469d847925a0/screenshots/style.png
--------------------------------------------------------------------------------
/source/assets/apple-launch-1125x2436.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colmugx/hexo-theme-Nlvi/257bf09d3c3500437b9684e3032f469d847925a0/source/assets/apple-launch-1125x2436.png
--------------------------------------------------------------------------------
/source/assets/apple-touch-startup-image-2048x1496.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colmugx/hexo-theme-Nlvi/257bf09d3c3500437b9684e3032f469d847925a0/source/assets/apple-touch-startup-image-2048x1496.png
--------------------------------------------------------------------------------
/source/font/allura/allura.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colmugx/hexo-theme-Nlvi/257bf09d3c3500437b9684e3032f469d847925a0/source/font/allura/allura.ttf
--------------------------------------------------------------------------------
/source/font/iconfont/iconfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colmugx/hexo-theme-Nlvi/257bf09d3c3500437b9684e3032f469d847925a0/source/font/iconfont/iconfont.eot
--------------------------------------------------------------------------------
/source/font/iconfont/iconfont.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
105 |
--------------------------------------------------------------------------------
/source/font/iconfont/iconfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colmugx/hexo-theme-Nlvi/257bf09d3c3500437b9684e3032f469d847925a0/source/font/iconfont/iconfont.ttf
--------------------------------------------------------------------------------
/source/font/iconfont/iconfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colmugx/hexo-theme-Nlvi/257bf09d3c3500437b9684e3032f469d847925a0/source/font/iconfont/iconfont.woff
--------------------------------------------------------------------------------
/source/font/iconfont/iconfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colmugx/hexo-theme-Nlvi/257bf09d3c3500437b9684e3032f469d847925a0/source/font/iconfont/iconfont.woff2
--------------------------------------------------------------------------------
/source/script/_src/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env"],
3 | "plugins": ["@babel/plugin-external-helpers"]
4 | }
5 |
6 |
--------------------------------------------------------------------------------
/source/script/_src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nlvi-src",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "rollup -c -w",
8 | "build": "rollup -c"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "@babel/core": "^7.2.2",
15 | "@babel/plugin-external-helpers": "^7.2.0",
16 | "@babel/preset-env": "^7.2.3",
17 | "rollup": "^1.1.0",
18 | "rollup-plugin-babel": "^4.3.0",
19 | "rollup-plugin-commonjs": "^9.2.0",
20 | "rollup-plugin-node-resolve": "^4.0.0",
21 | "rollup-plugin-replace": "^2.1.0",
22 | "rollup-plugin-uglify": "^6.0.0",
23 | "uglify-es": "^3.3.9"
24 | },
25 | "dependencies": {
26 | "rxjs": "^6.3.3"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/source/script/_src/rollup.config.js:
--------------------------------------------------------------------------------
1 | import babel from 'rollup-plugin-babel'
2 | import resolve from 'rollup-plugin-node-resolve'
3 | import commonjs from 'rollup-plugin-commonjs'
4 | import replace from 'rollup-plugin-replace'
5 | import { uglify } from 'rollup-plugin-uglify'
6 | import { minify } from 'uglify-es'
7 |
8 | export default ['banderole', 'balance'].map(name => ({
9 | input: `src/${name}.js`,
10 | plugins: [
11 | babel({
12 | exclude: 'node_modules/**'
13 | }),
14 | resolve(),
15 | commonjs(),
16 | uglify({
17 | compress: {
18 | pure_getters: true
19 | },
20 | output: {
21 | comments: false
22 | },
23 | }, minify)
24 | ],
25 | output: [
26 | {
27 | file: `../scheme/${name}.js`,
28 | name: 'Nlvi',
29 | format: 'umd'
30 | }
31 | ]
32 | }))
33 |
--------------------------------------------------------------------------------
/source/script/_src/src/balance.js:
--------------------------------------------------------------------------------
1 | import Base from './base'
2 |
3 | export default class Balance extends Base {
4 | constructor (config) {
5 | super(config)
6 | this.utils = Base.utils
7 | }
8 |
9 | back2top() {
10 | const backtop = this.utils('cls', '#backtop')
11 | backtop.opreate('melt', 'remove')
12 | this.scrollArr.push((sct) => {
13 | this.updateRound(sct)
14 | })
15 | super.back2top()
16 | }
17 |
18 | switchToc() {
19 | if (!this.theme.toc) false
20 | const utils = this.utils
21 | const $header = utils('cls', '#header')
22 | const $toc = utils('cls', '.post-toc')
23 | $('#switch-toc').on('click', e => {
24 | e.stopPropagation()
25 | $header.opreate('show_toc')
26 | $toc.opreate('hide', 'remove')
27 | })
28 | $('#header').on('click', () => {
29 | if ($header.exist('show_toc')) {
30 | $header.opreate('show_toc', 'remove')
31 | $toc.opreate('hide')
32 | }
33 | })
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/source/script/_src/src/banderole.js:
--------------------------------------------------------------------------------
1 | import Base from './base'
2 | import { fromEvent } from 'rxjs'
3 | import { throttleTime, map } from 'rxjs/operators'
4 |
5 | export default class Banderole extends Base {
6 | constructor (config) {
7 | super(config)
8 | this.utils = Base.utils
9 | }
10 |
11 | pushHeader() {
12 | super.pushHeader()
13 | if (!this.utils('iss').banderole()) return
14 | const $header = this.utils('cls', '#header')
15 | fromEvent(window, 'wheel').pipe(
16 | throttleTime(500),
17 | map(({ deltaY }) => deltaY > 0)
18 | ).subscribe(v => $header.opreate('header-hide', v ? 'add' : 'remove'))
19 | this.scrollArr.push(sct => {
20 | if (sct > 50) {
21 | $header.opreate('header-scroll', 'add')
22 | } else {
23 | $header.opreate('header-scroll', 'remove')
24 | }
25 | })
26 | }
27 |
28 | back2top() {
29 | const backtop = this.utils('cls', '#backtop')
30 | this.scrollArr.push((sct) => {
31 | if (sct > 110) {
32 | backtop.opreate('clarity', 'add')
33 | backtop.opreate('melt', 'remove')
34 | } else {
35 | backtop.opreate('melt')
36 | backtop.opreate('clarity', 'remove')
37 | }
38 | this.updateRound(sct)
39 | })
40 | super.back2top()
41 | }
42 |
43 | switchToc() {
44 | if (!this.theme.toc) false
45 | const utils = this.utils
46 | const $inner = utils('cls', '.toc-inner')
47 | const $title = utils('cls', '.post-toc .title')
48 | const $container = utils('cls', '.container-inner')
49 |
50 | $('.post-toc .title').on('click', () => {
51 | if ($title.exist('show')) {
52 | $title.opreate('show', 'remove')
53 | $inner.opreate('show', 'remove')
54 | $container.opreate('has_toc', 'remove')
55 | } else {
56 | $title.opreate('show')
57 | $inner.opreate('show')
58 | $container.opreate('has_toc')
59 | }
60 | })
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/source/script/_src/src/base.js:
--------------------------------------------------------------------------------
1 | import { fromEvent, from, zip } from 'rxjs'
2 | import { map, switchMap, filter } from 'rxjs/operators'
3 | import genSearch from './search'
4 |
5 | class Base {
6 |
7 | constructor(config) {
8 | this.config = config
9 | this.theme = config.theme
10 | this.scrollArr = []
11 | }
12 |
13 | init() {
14 | const utils = Base.utils
15 | const fns = {
16 | smoothScroll() {
17 | $('.toc-link').on('click', function () {
18 | $('html, body').animate({
19 | scrollTop: $($.attr(this, 'href')).offset().top - 200
20 | })
21 | })
22 | },
23 | picPos() {
24 | const _this = this
25 | $('.post-content').each(function () {
26 | $(this).find('img').each(function () {
27 | $(this).parent('p').css('text-align', 'center')
28 | let imgHead = `
${imgHead} alt="${this.alt}">`)
33 | })
34 | })
35 | },
36 | showComments() {
37 | $('#com-switch').on('click', () => {
38 | if (utils('iss', '#post-comments').display()) {
39 | $('#post-comments').css('display', 'block').addClass('syuanpi fadeInDown')
40 | $(this).removeClass('syuanpi').css('transform', 'rotate(180deg)')
41 | } else {
42 | $(this).addClass('syuanpi').css('transform', '')
43 | utils('cls', '#post-comments').opreate('fadeInDown', 'remove')
44 | utils('ani', '#post-comments').end('fadeOutUp', function () {
45 | $(this).css('display', 'none')
46 | })
47 | }
48 | })
49 | }
50 | }
51 | Base.opScroll(this.scrollArr)
52 | return Object.values(fns).forEach(fn => fn.call(this))
53 | }
54 |
55 | back2top() {
56 | $('.toTop').on('click', function () {
57 | $('html, body').animate({
58 | scrollTop: 0
59 | })
60 | })
61 | }
62 |
63 | pushHeader() {
64 | const $header = this.utils('cls', '#mobile-header')
65 | this.scrollArr.push(sct => {
66 | if (sct > 5) {
67 | $header.opreate('header-scroll', 'add')
68 | } else {
69 | $header.opreate('header-scroll', 'remove')
70 | }
71 | })
72 | }
73 |
74 | updateRound(sct) {
75 | const scrollPercentRounded = Math.floor(
76 | sct
77 | / ($(document).height() - $(window).height())
78 | * 100
79 | )
80 | $('#scrollpercent').html(scrollPercentRounded)
81 | }
82 |
83 | showToc() {
84 | const utils = Base.utils
85 | const $toclink = $('.toc-link')
86 | const $headerlink = $('.headerlink')
87 | this.scrollArr.push(function (sct) {
88 | const headerlinkTop = $.map($headerlink, function (link) {
89 | return $(link).offset().top
90 | })
91 | $('.title-link a').each(function () {
92 | const ele = utils('cls', this)
93 | sct >= 0 && sct < 230
94 | ? ele.opreate('active')
95 | : ele.opreate('active', 'remove')
96 | })
97 | for (let i = 0; i < $toclink.length; i++) {
98 | const
99 | isLastOne = i + 1 === $toclink.length,
100 | currentTop = headerlinkTop[i],
101 | nextTop = isLastOne
102 | ? Infinity
103 | : headerlinkTop[i + 1],
104 | $tl = utils('cls', $toclink[i])
105 | currentTop < sct + 210 && sct + 210 <= nextTop
106 | ? $tl.opreate('active')
107 | : $tl.opreate('active', 'remove')
108 | }
109 | })
110 | }
111 |
112 | titleStatus() {
113 | const title = document.title
114 | var tme
115 | document.addEventListener('visibilitychange', function () {
116 | const sct = Math.floor($(window).scrollTop() / ($(document).height() - $(window).height()) * 100)
117 | if ($(document).height() - $(window).height() === 0) sct = 100
118 | if (document.hidden) {
119 | clearTimeout(tme)
120 | document.title = 'Read ' + sct + '% · ' + title
121 | } else {
122 | document.title = 'Welcome Back · ' + title
123 | tme = setTimeout(function () {
124 | document.title = title
125 | }, 3000)
126 | }
127 | })
128 | }
129 |
130 | showReward() {
131 | if (!this.theme.reward) return
132 | const utils = Base.utils
133 | const $btn = utils('ani', '#reward-btn')
134 | $('#reward-btn').click(() => {
135 | if (utils('iss', '#reward-wrapper').display()) {
136 | $('#reward-wrapper').css('display', 'flex')
137 | $btn.end('clarity')
138 | } else {
139 | $btn.end('melt', () => {
140 | $('#reward-wrapper').hide()
141 | })
142 | }
143 | })
144 | }
145 |
146 | listenExit(elm, fn) {
147 | fromEvent(elm, 'keydown').pipe(
148 | filter(e => e.keyCode === 27)
149 | ).subscribe(() => fn())
150 | }
151 |
152 | depth(open, close) {
153 | const utils = this.utils
154 | const $container = utils('cls', 'body')
155 | const $containerInner = utils('cls', '.container-inner')
156 | if ($container.exist('under')) {
157 | $container.opreate('under', 'remove')
158 | $containerInner.opreate('under', 'remove')
159 | close.call(this)
160 | } else {
161 | $container.opreate('under', 'add')
162 | $containerInner.opreate('under', 'add')
163 | open.call(this)
164 | }
165 | }
166 |
167 | tagcloud() {
168 | const utils = this.utils
169 | const $tag = $('#tags')
170 | const $tagcloud = utils('cls', '#tagcloud')
171 | const $tagcloudAni = utils('ani', '#tagcloud')
172 | const $search = utils('cls', '#search')
173 | const $searchAni = utils('ani', '#search')
174 | const closeFrame = () => {
175 | $tagcloud.opreate('shuttleIn', 'remove')
176 | $tagcloudAni.end('zoomOut', () => {
177 | $tagcloud.opreate('syuanpi show', 'remove')
178 | })
179 | }
180 | const switchShow = () => {
181 | this.depth(() => $tagcloud.opreate('syuanpi shuttleIn show'), closeFrame)
182 | }
183 | this.listenExit($tag, switchShow)
184 | this.listenExit(document.getElementsByClassName('tagcloud-taglist'), switchShow)
185 | $tag.on('click', () => {
186 | if ($search.exist('show')) {
187 | $tagcloud.opreate('syuanpi shuttleIn show')
188 | $search.opreate('shuttleIn', 'remove')
189 | $searchAni.end('zoomOut', () => {
190 | $search.opreate('syuanpi show', 'remove')
191 | })
192 | return
193 | }
194 | switchShow()
195 | })
196 | $('#tagcloud').on('click', e => {
197 | e.stopPropagation()
198 | if (e.target.tagName === 'DIV') {
199 | this.depth(() => $tagcloud.opreate('syuanpi shuttleIn show'), closeFrame)
200 | }
201 | })
202 | const tags$ = fromEvent(document.querySelectorAll('.tagcloud-tag button'), 'click').pipe(
203 | map(({ target }) => target)
204 | )
205 | const postlist$ = from(document.querySelectorAll('.tagcloud-postlist'))
206 | const cleanlist$ = postlist$.pipe(map(dom => dom.classList.remove('active')))
207 | const click$ = tags$.pipe(switchMap(() => cleanlist$))
208 | zip(click$, tags$).pipe(
209 | map(([_, dom]) => dom),
210 | switchMap(v => postlist$.pipe(
211 | filter(dom => dom.firstElementChild.innerHTML.trim() === v.innerHTML.trim())
212 | ))
213 | ).subscribe(v => v.classList.add('active'))
214 | }
215 |
216 | search() {
217 | if (!this.theme.search) return
218 | $('body').append(``)
219 | const utils = this.utils
220 | const $searchbtn = $('#search-btn')
221 | const $result = $('#search-result')
222 | const $search = utils('cls', '#search')
223 | const $searchAni = utils('ani', '#search')
224 | const $tagcloud = utils('cls', '#tagcloud')
225 | const $tagcloudAni = utils('ani', '#tagcloud')
226 | const closeFrame = () => {
227 | $search.opreate('shuttleIn', 'remove')
228 | $searchAni.end('zoomOut', () => {
229 | $search.opreate('syuanpi show', 'remove')
230 | })
231 | }
232 | const switchShow = () => {
233 | this.depth(() => $search.opreate('syuanpi shuttleIn show'), closeFrame)
234 | }
235 | this.listenExit(document.getElementById('search'), switchShow)
236 | $searchbtn.on('click', () => {
237 | if ($tagcloud.exist('show')) {
238 | $search.opreate('syuanpi shuttleIn show')
239 | $tagcloud.opreate('shuttleIn', 'remove')
240 | $tagcloudAni.end('zoomOut', () => {
241 | $tagcloud.opreate('syuanpi show', 'remove')
242 | })
243 | return
244 | }
245 | switchShow()
246 | })
247 | $('#search').on('click', e => {
248 | e.stopPropagation()
249 | if(e.target.tagName === 'DIV') {
250 | this.depth(() => $search.opreate('syuanpi shuttleIn show'), closeFrame)
251 | }
252 | })
253 | genSearch(`${this.config.baseUrl}search.xml`, 'search-input')
254 | .subscribe(vals => {
255 | const list = body => ``
256 | const item = ({ url, title, content }) => `
257 |
258 | ${title}
259 | ${content}
260 |
261 | `
262 | const output = vals.map(item)
263 | $result.html(list(output.join('')))
264 | })
265 | }
266 |
267 | headerMenu() {
268 | const utils = this.utils
269 | const $mobileMenu = utils('cls', '.mobile-header-body')
270 | const $haderline = utils('cls', '.header-menu-line')
271 | const $mtag = $('#mobile-tags')
272 | const $tagcloud = utils('cls', '#tagcloud')
273 | $mtag.on('click', () => {
274 | $mobileMenu.opreate('show', 'remove')
275 | $haderline.opreate('show', 'remove')
276 | $tagcloud.opreate('syuanpi shuttleIn show')
277 | })
278 | $('#mobile-left').on('click', () => {
279 | this.depth(() => {
280 | $mobileMenu.opreate('show')
281 | $haderline.opreate('show')
282 | }, () => {
283 | $mobileMenu.opreate('show', 'remove')
284 | $haderline.opreate('show', 'remove')
285 | })
286 | })
287 | }
288 |
289 | pjax() {
290 | if (!this.theme.pjax) return
291 | const utils = this.utils
292 | const $container = utils('cls', '.container-inner')
293 | const $header = utils('cls', '.header')
294 | const $headerWrapper = utils('cls', '.header-wrapper')
295 | $(document).pjax('.container-inner a', '.container-inner', { fragment: 'container-inner' })
296 | $(document).on('pjax:send', function () {
297 | $container.opreate('syuanpi fadeOutLeftShort')
298 | $headerWrapper.opreate('syuanpi fadeOutLeftShort')
299 | $header.opreate('melt')
300 | })
301 | }
302 |
303 | bootstarp() {
304 | this.showToc()
305 | this.back2top()
306 | this.switchToc()
307 | this.titleStatus()
308 | this.init()
309 | this.pushHeader()
310 | this.tagcloud()
311 | this.search()
312 | this.showReward()
313 | this.headerMenu()
314 | this.pjax()
315 | }
316 |
317 | static utils(g, e) {
318 | const cls = ele => ({
319 | opreate(cls, opt) {
320 | return opt === 'remove'
321 | ? $(ele).removeClass(cls)
322 | : $(ele).addClass(cls)
323 | },
324 | exist(cls) {
325 | return $(ele).hasClass(cls)
326 | }
327 | })
328 | const iss = ele => ({
329 | banderole: () => {
330 | return this.theme.scheme === 'banderole'
331 | },
332 | balance: () => {
333 | return this.theme.scheme === 'balance'
334 | },
335 | display() {
336 | return $(ele).css('display') === 'none'
337 | }
338 | })
339 | const ani = ele => ({
340 | close() {
341 | return cls.opreate('.syuanpi', 'syuanpi', 'remove')
342 | },
343 | end(ani, fn) {
344 | $(ele)
345 | .addClass(ani)
346 | .one('webkitAnimationEnd AnimationEnd', function () {
347 | $(ele).removeClass(ani)
348 | fn && fn.call(null, ele)
349 | })
350 | }
351 | })
352 | return { cls, iss, ani }[g](e)
353 | }
354 |
355 | static opScroll(fns) {
356 | const scroll$ = fromEvent(window, 'scroll')
357 | .pipe(
358 | map(v => v.target.scrollingElement.scrollTop)
359 | )
360 | fns.length && scroll$.subscribe(next => fns.forEach(fn => fn(next)))
361 | }
362 | }
363 |
364 |
365 | export default Base
366 |
--------------------------------------------------------------------------------
/source/script/_src/src/search.js:
--------------------------------------------------------------------------------
1 | import { fromEvent, zip, from } from 'rxjs'
2 | import { ajax } from 'rxjs/ajax'
3 | import { map, debounceTime, withLatestFrom } from 'rxjs/operators'
4 |
5 | export default function(path, inputId) {
6 | const result$ = ajax({
7 | url: path,
8 | responseType: 'xml'
9 | }).pipe(
10 | map(({ response }) => response),
11 | map(res => res.querySelectorAll('entry')),
12 | map(res => [...res].map(v => ({
13 | title: v.getElementsByTagName('title')[0].textContent,
14 | url: v.getElementsByTagName('url')[0].textContent,
15 | content: v.getElementsByTagName('content')[0].textContent,
16 | })))
17 | )
18 |
19 | const input$ = fromEvent(document.getElementById(inputId), 'input').pipe(
20 | debounceTime(500),
21 | map(({ target }) => target.value.trim())
22 | )
23 |
24 | const novalue$ = input$.pipe(
25 | map(v => !!v)
26 | )
27 |
28 | const output$ = input$.pipe(
29 | withLatestFrom(result$),
30 | map(
31 | ([keyword, results]) => results
32 | .filter(({ title, content }) => title.indexOf(keyword) >= 0 || content.indexOf(keyword) >= 0)
33 | .map(obj => {
34 | const reg = new RegExp(`(${keyword})`, 'gi')
35 | const title = obj.title.replace(reg, '$1')
36 | let content = obj.content.replace(/<[^>]+>/g, "")
37 | const index = content.indexOf(keyword)
38 |
39 | content = content
40 | .slice(index < 20 ? 0 : index - 20, index < 0 ? 100 : index + 80)
41 | .replace(reg, '$1')
42 | return { ...obj, title, content }
43 | })
44 | ),
45 | )
46 |
47 | return zip(novalue$, output$).pipe(
48 | map(([exist, values]) => exist ? values : [])
49 | )
50 | }
51 |
--------------------------------------------------------------------------------
/source/script/bootstarp.js:
--------------------------------------------------------------------------------
1 | document.addEventListener('DOMContentLoaded', function() {
2 | document.body.addEventListener('touchstart', function () {});
3 | var app = new Nlvi(nlviconfig);
4 | app.bootstarp();
5 | });
6 | $(document).ready(function() {
7 | $('.container-inner').show();
8 | $('.logo-inner').show();
9 | });
10 |
--------------------------------------------------------------------------------
/source/script/lib/lightbox/css/lightbox.min.css:
--------------------------------------------------------------------------------
1 | .lb-loader,.lightbox{text-align:center;line-height:0}.lb-dataContainer:after,.lb-outerContainer:after{content:"";clear:both}html.lb-disable-scrolling{overflow:hidden;position:fixed;height:100vh;width:100vw}.lightboxOverlay{position:absolute;top:0;left:0;z-index:9999;background-color:#000;filter:alpha(Opacity=80);opacity:.8;display:none}.lightbox{position:absolute;left:0;width:100%;z-index:10000;font-weight:400}.lightbox .lb-image{display:block;height:auto;max-width:inherit;max-height:none;border-radius:3px;border:4px solid #fff}.lightbox a img{border:none}.lb-outerContainer{position:relative;width:250px;height:250px;margin:0 auto;border-radius:4px;background-color:#fff}.lb-loader,.lb-nav{position:absolute;left:0}.lb-outerContainer:after{display:table}.lb-loader{top:43%;height:25%;width:100%}.lb-cancel{display:block;width:32px;height:32px;margin:0 auto;background:url(../images/loading.gif) no-repeat}.lb-nav{top:0;height:100%;width:100%;z-index:10}.lb-container>.nav{left:0}.lb-nav a{outline:0;background-image:url(data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==)}.lb-next,.lb-prev{height:100%;cursor:pointer;display:block}.lb-nav a.lb-prev{width:34%;left:0;float:left;background:url(../images/prev.png) left 48% no-repeat;filter:alpha(Opacity=0);opacity:0;-webkit-transition:opacity .6s;-moz-transition:opacity .6s;-o-transition:opacity .6s;transition:opacity .6s}.lb-nav a.lb-prev:hover{filter:alpha(Opacity=100);opacity:1}.lb-nav a.lb-next{width:64%;right:0;float:right;background:url(../images/next.png) right 48% no-repeat;filter:alpha(Opacity=0);opacity:0;-webkit-transition:opacity .6s;-moz-transition:opacity .6s;-o-transition:opacity .6s;transition:opacity .6s}.lb-nav a.lb-next:hover{filter:alpha(Opacity=100);opacity:1}.lb-dataContainer{margin:0 auto;padding-top:5px;width:100%;border-bottom-left-radius:4px;border-bottom-right-radius:4px}.lb-dataContainer:after{display:table}.lb-data{padding:0 4px;color:#ccc}.lb-data .lb-details{width:85%;float:left;text-align:left;line-height:1.1em}.lb-data .lb-caption{font-size:13px;font-weight:700;line-height:1em}.lb-data .lb-caption a{color:#4ae}.lb-data .lb-number{display:block;clear:left;padding-bottom:1em;font-size:12px;color:#999}.lb-data .lb-close{display:block;float:right;width:30px;height:30px;background:url(../images/close.png) top right no-repeat;text-align:right;outline:0;filter:alpha(Opacity=70);opacity:.7;-webkit-transition:opacity .2s;-moz-transition:opacity .2s;-o-transition:opacity .2s;transition:opacity .2s}.lb-data .lb-close:hover{cursor:pointer;filter:alpha(Opacity=100);opacity:1}
2 |
--------------------------------------------------------------------------------
/source/script/lib/lightbox/images/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colmugx/hexo-theme-Nlvi/257bf09d3c3500437b9684e3032f469d847925a0/source/script/lib/lightbox/images/close.png
--------------------------------------------------------------------------------
/source/script/lib/lightbox/images/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colmugx/hexo-theme-Nlvi/257bf09d3c3500437b9684e3032f469d847925a0/source/script/lib/lightbox/images/loading.gif
--------------------------------------------------------------------------------
/source/script/lib/lightbox/images/next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colmugx/hexo-theme-Nlvi/257bf09d3c3500437b9684e3032f469d847925a0/source/script/lib/lightbox/images/next.png
--------------------------------------------------------------------------------
/source/script/lib/lightbox/images/prev.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colmugx/hexo-theme-Nlvi/257bf09d3c3500437b9684e3032f469d847925a0/source/script/lib/lightbox/images/prev.png
--------------------------------------------------------------------------------
/source/script/lib/lightbox/js/lightbox.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Lightbox v2.10.0
3 | * by Lokesh Dhakar
4 | *
5 | * More info:
6 | * http://lokeshdhakar.com/projects/lightbox2/
7 | *
8 | * Copyright 2007, 2018 Lokesh Dhakar
9 | * Released under the MIT license
10 | * https://github.com/lokesh/lightbox2/blob/master/LICENSE
11 | *
12 | * @preserve
13 | */
14 | !function(a,b){"function"==typeof define&&define.amd?define(["jquery"],b):"object"==typeof exports?module.exports=b(require("jquery")):a.lightbox=b(a.jQuery)}(this,function(a){function b(b){this.album=[],this.currentImageIndex=void 0,this.init(),this.options=a.extend({},this.constructor.defaults),this.option(b)}return b.defaults={albumLabel:"Image %1 of %2",alwaysShowNavOnTouchDevices:!1,fadeDuration:600,fitImagesInViewport:!0,imageFadeDuration:600,positionFromTop:50,resizeDuration:700,showImageNumberLabel:!0,wrapAround:!1,disableScrolling:!1,sanitizeTitle:!1},b.prototype.option=function(b){a.extend(this.options,b)},b.prototype.imageCountLabel=function(a,b){return this.options.albumLabel.replace(/%1/g,a).replace(/%2/g,b)},b.prototype.init=function(){var b=this;a(document).ready(function(){b.enable(),b.build()})},b.prototype.enable=function(){var b=this;a("body").on("click","a[rel^=lightbox], area[rel^=lightbox], a[data-lightbox], area[data-lightbox]",function(c){return b.start(a(c.currentTarget)),!1})},b.prototype.build=function(){if(!(a("#lightbox").length>0)){var b=this;a('').appendTo(a("body")),this.$lightbox=a("#lightbox"),this.$overlay=a("#lightboxOverlay"),this.$outerContainer=this.$lightbox.find(".lb-outerContainer"),this.$container=this.$lightbox.find(".lb-container"),this.$image=this.$lightbox.find(".lb-image"),this.$nav=this.$lightbox.find(".lb-nav"),this.containerPadding={top:parseInt(this.$container.css("padding-top"),10),right:parseInt(this.$container.css("padding-right"),10),bottom:parseInt(this.$container.css("padding-bottom"),10),left:parseInt(this.$container.css("padding-left"),10)},this.imageBorderWidth={top:parseInt(this.$image.css("border-top-width"),10),right:parseInt(this.$image.css("border-right-width"),10),bottom:parseInt(this.$image.css("border-bottom-width"),10),left:parseInt(this.$image.css("border-left-width"),10)},this.$overlay.hide().on("click",function(){return b.end(),!1}),this.$lightbox.hide().on("click",function(c){return"lightbox"===a(c.target).attr("id")&&b.end(),!1}),this.$outerContainer.on("click",function(c){return"lightbox"===a(c.target).attr("id")&&b.end(),!1}),this.$lightbox.find(".lb-prev").on("click",function(){return 0===b.currentImageIndex?b.changeImage(b.album.length-1):b.changeImage(b.currentImageIndex-1),!1}),this.$lightbox.find(".lb-next").on("click",function(){return b.currentImageIndex===b.album.length-1?b.changeImage(0):b.changeImage(b.currentImageIndex+1),!1}),this.$nav.on("mousedown",function(a){3===a.which&&(b.$nav.css("pointer-events","none"),b.$lightbox.one("contextmenu",function(){setTimeout(function(){this.$nav.css("pointer-events","auto")}.bind(b),0)}))}),this.$lightbox.find(".lb-loader, .lb-close").on("click",function(){return b.end(),!1})}},b.prototype.start=function(b){function c(a){d.album.push({alt:a.attr("data-alt"),link:a.attr("href"),title:a.attr("data-title")||a.attr("title")})}var d=this,e=a(window);e.on("resize",a.proxy(this.sizeOverlay,this)),a("select, object, embed").css({visibility:"hidden"}),this.sizeOverlay(),this.album=[];var f,g=0,h=b.attr("data-lightbox");if(h){f=a(b.prop("tagName")+'[data-lightbox="'+h+'"]');for(var i=0;ii||e.height>h)&&(e.width/i>e.height/h?(g=i,f=parseInt(e.height/(e.width/g),10),d.width(g),d.height(f)):(f=h,g=parseInt(e.width/(e.height/f),10),d.width(g),d.height(f)))),c.sizeContainer(d.width(),d.height())},e.src=this.album[b].link,this.currentImageIndex=b},b.prototype.sizeOverlay=function(){this.$overlay.width(a(document).width()).height(a(document).height())},b.prototype.sizeContainer=function(a,b){function c(){d.$lightbox.find(".lb-dataContainer").width(g),d.$lightbox.find(".lb-prevLink").height(h),d.$lightbox.find(".lb-nextLink").height(h),d.showImage()}var d=this,e=this.$outerContainer.outerWidth(),f=this.$outerContainer.outerHeight(),g=a+this.containerPadding.left+this.containerPadding.right+this.imageBorderWidth.left+this.imageBorderWidth.right,h=b+this.containerPadding.top+this.containerPadding.bottom+this.imageBorderWidth.top+this.imageBorderWidth.bottom;e!==g||f!==h?this.$outerContainer.animate({width:g,height:h},this.options.resizeDuration,"swing",function(){c()}):c()},b.prototype.showImage=function(){this.$lightbox.find(".lb-loader").stop(!0).hide(),this.$lightbox.find(".lb-image").fadeIn(this.options.imageFadeDuration),this.updateNav(),this.updateDetails(),this.preloadNeighboringImages(),this.enableKeyboardNav()},b.prototype.updateNav=function(){var a=!1;try{document.createEvent("TouchEvent"),a=!!this.options.alwaysShowNavOnTouchDevices}catch(a){}this.$lightbox.find(".lb-nav").show(),this.album.length>1&&(this.options.wrapAround?(a&&this.$lightbox.find(".lb-prev, .lb-next").css("opacity","1"),this.$lightbox.find(".lb-prev, .lb-next").show()):(this.currentImageIndex>0&&(this.$lightbox.find(".lb-prev").show(),a&&this.$lightbox.find(".lb-prev").css("opacity","1")),this.currentImageIndex1&&this.options.showImageNumberLabel){var d=this.imageCountLabel(this.currentImageIndex+1,this.album.length);this.$lightbox.find(".lb-number").text(d).fadeIn("fast")}else this.$lightbox.find(".lb-number").hide();this.$outerContainer.removeClass("animating"),this.$lightbox.find(".lb-dataContainer").fadeIn(this.options.resizeDuration,function(){return b.sizeOverlay()})},b.prototype.preloadNeighboringImages=function(){if(this.album.length>this.currentImageIndex+1){(new Image).src=this.album[this.currentImageIndex+1].link}if(this.currentImageIndex>0){(new Image).src=this.album[this.currentImageIndex-1].link}},b.prototype.enableKeyboardNav=function(){a(document).on("keyup.keyboard",a.proxy(this.keyboardAction,this))},b.prototype.disableKeyboardNav=function(){a(document).off(".keyboard")},b.prototype.keyboardAction=function(a){var b=a.keyCode,c=String.fromCharCode(b).toLowerCase();27===b||c.match(/x|o|c/)?this.end():"p"===c||37===b?0!==this.currentImageIndex?this.changeImage(this.currentImageIndex-1):this.options.wrapAround&&this.album.length>1&&this.changeImage(this.album.length-1):"n"!==c&&39!==b||(this.currentImageIndex!==this.album.length-1?this.changeImage(this.currentImageIndex+1):this.options.wrapAround&&this.album.length>1&&this.changeImage(0))},b.prototype.end=function(){this.disableKeyboardNav(),a(window).off("resize",this.sizeOverlay),this.$lightbox.fadeOut(this.options.fadeDuration),this.$overlay.fadeOut(this.options.fadeDuration),a("select, object, embed").css({visibility:"visible"}),this.options.disableScrolling&&a("html").removeClass("lb-disable-scrolling")},new b});
15 | //# sourceMappingURL=lightbox.min.map
16 |
--------------------------------------------------------------------------------
/source/script/lib/pjax/pjax.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012, Chris Wanstrath
3 | * Released under the MIT License
4 | * https://github.com/defunkt/jquery-pjax
5 | */
6 |
7 | (function($){
8 |
9 | // When called on a container with a selector, fetches the href with
10 | // ajax into the container or with the data-pjax attribute on the link
11 | // itself.
12 | //
13 | // Tries to make sure the back button and ctrl+click work the way
14 | // you'd expect.
15 | //
16 | // Exported as $.fn.pjax
17 | //
18 | // Accepts a jQuery ajax options object that may include these
19 | // pjax specific options:
20 | //
21 | //
22 | // container - String selector for the element where to place the response body.
23 | // push - Whether to pushState the URL. Defaults to true (of course).
24 | // replace - Want to use replaceState instead? That's cool.
25 | //
26 | // For convenience the second parameter can be either the container or
27 | // the options object.
28 | //
29 | // Returns the jQuery object
30 | function fnPjax(selector, container, options) {
31 | options = optionsFor(container, options)
32 | return this.on('click.pjax', selector, function(event) {
33 | var opts = options
34 | if (!opts.container) {
35 | opts = $.extend({}, options)
36 | opts.container = $(this).attr('data-pjax')
37 | }
38 | handleClick(event, opts)
39 | })
40 | }
41 |
42 | // Public: pjax on click handler
43 | //
44 | // Exported as $.pjax.click.
45 | //
46 | // event - "click" jQuery.Event
47 | // options - pjax options
48 | //
49 | // Examples
50 | //
51 | // $(document).on('click', 'a', $.pjax.click)
52 | // // is the same as
53 | // $(document).pjax('a')
54 | //
55 | // Returns nothing.
56 | function handleClick(event, container, options) {
57 | options = optionsFor(container, options)
58 |
59 | var link = event.currentTarget
60 | var $link = $(link)
61 |
62 | if (link.tagName.toUpperCase() !== 'A')
63 | throw "$.fn.pjax or $.pjax.click requires an anchor element"
64 |
65 | // Middle click, cmd click, and ctrl click should open
66 | // links in a new tab as normal.
67 | if ( event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey )
68 | return
69 |
70 | // Ignore cross origin links
71 | if ( location.protocol !== link.protocol || location.hostname !== link.hostname )
72 | return
73 |
74 | // Ignore case when a hash is being tacked on the current URL
75 | if ( link.href.indexOf('#') > -1 && stripHash(link) == stripHash(location) )
76 | return
77 |
78 | // Ignore event with default prevented
79 | if (event.isDefaultPrevented())
80 | return
81 |
82 | var defaults = {
83 | url: link.href,
84 | container: $link.attr('data-pjax'),
85 | target: link
86 | }
87 |
88 | var opts = $.extend({}, defaults, options)
89 | var clickEvent = $.Event('pjax:click')
90 | $link.trigger(clickEvent, [opts])
91 |
92 | if (!clickEvent.isDefaultPrevented()) {
93 | pjax(opts)
94 | event.preventDefault()
95 | $link.trigger('pjax:clicked', [opts])
96 | }
97 | }
98 |
99 | // Public: pjax on form submit handler
100 | //
101 | // Exported as $.pjax.submit
102 | //
103 | // event - "click" jQuery.Event
104 | // options - pjax options
105 | //
106 | // Examples
107 | //
108 | // $(document).on('submit', 'form', function(event) {
109 | // $.pjax.submit(event, '[data-pjax-container]')
110 | // })
111 | //
112 | // Returns nothing.
113 | function handleSubmit(event, container, options) {
114 | options = optionsFor(container, options)
115 |
116 | var form = event.currentTarget
117 | var $form = $(form)
118 |
119 | if (form.tagName.toUpperCase() !== 'FORM')
120 | throw "$.pjax.submit requires a form element"
121 |
122 | var defaults = {
123 | type: ($form.attr('method') || 'GET').toUpperCase(),
124 | url: $form.attr('action'),
125 | container: $form.attr('data-pjax'),
126 | target: form
127 | }
128 |
129 | if (defaults.type !== 'GET' && window.FormData !== undefined) {
130 | defaults.data = new FormData(form)
131 | defaults.processData = false
132 | defaults.contentType = false
133 | } else {
134 | // Can't handle file uploads, exit
135 | if ($form.find(':file').length) {
136 | return
137 | }
138 |
139 | // Fallback to manually serializing the fields
140 | defaults.data = $form.serializeArray()
141 | }
142 |
143 | pjax($.extend({}, defaults, options))
144 |
145 | event.preventDefault()
146 | }
147 |
148 | // Loads a URL with ajax, puts the response body inside a container,
149 | // then pushState()'s the loaded URL.
150 | //
151 | // Works just like $.ajax in that it accepts a jQuery ajax
152 | // settings object (with keys like url, type, data, etc).
153 | //
154 | // Accepts these extra keys:
155 | //
156 | // container - String selector for where to stick the response body.
157 | // push - Whether to pushState the URL. Defaults to true (of course).
158 | // replace - Want to use replaceState instead? That's cool.
159 | //
160 | // Use it just like $.ajax:
161 | //
162 | // var xhr = $.pjax({ url: this.href, container: '#main' })
163 | // console.log( xhr.readyState )
164 | //
165 | // Returns whatever $.ajax returns.
166 | function pjax(options) {
167 | options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options)
168 |
169 | if ($.isFunction(options.url)) {
170 | options.url = options.url()
171 | }
172 |
173 | var hash = parseURL(options.url).hash
174 |
175 | var containerType = $.type(options.container)
176 | if (containerType !== 'string') {
177 | throw "expected string value for 'container' option; got " + containerType
178 | }
179 | var context = options.context = $(options.container)
180 | if (!context.length) {
181 | throw "the container selector '" + options.container + "' did not match anything"
182 | }
183 |
184 | // We want the browser to maintain two separate internal caches: one
185 | // for pjax'd partial page loads and one for normal page loads.
186 | // Without adding this secret parameter, some browsers will often
187 | // confuse the two.
188 | if (!options.data) options.data = {}
189 | if ($.isArray(options.data)) {
190 | options.data.push({name: '_pjax', value: options.container})
191 | } else {
192 | options.data._pjax = options.container
193 | }
194 |
195 | function fire(type, args, props) {
196 | if (!props) props = {}
197 | props.relatedTarget = options.target
198 | var event = $.Event(type, props)
199 | context.trigger(event, args)
200 | return !event.isDefaultPrevented()
201 | }
202 |
203 | var timeoutTimer
204 |
205 | options.beforeSend = function(xhr, settings) {
206 | // No timeout for non-GET requests
207 | // Its not safe to request the resource again with a fallback method.
208 | if (settings.type !== 'GET') {
209 | settings.timeout = 0
210 | }
211 |
212 | xhr.setRequestHeader('X-PJAX', 'true')
213 | xhr.setRequestHeader('X-PJAX-Container', options.container)
214 |
215 | if (!fire('pjax:beforeSend', [xhr, settings]))
216 | return false
217 |
218 | if (settings.timeout > 0) {
219 | timeoutTimer = setTimeout(function() {
220 | if (fire('pjax:timeout', [xhr, options]))
221 | xhr.abort('timeout')
222 | }, settings.timeout)
223 |
224 | // Clear timeout setting so jquerys internal timeout isn't invoked
225 | settings.timeout = 0
226 | }
227 |
228 | var url = parseURL(settings.url)
229 | if (hash) url.hash = hash
230 | options.requestUrl = stripInternalParams(url)
231 | }
232 |
233 | options.complete = function(xhr, textStatus) {
234 | if (timeoutTimer)
235 | clearTimeout(timeoutTimer)
236 |
237 | fire('pjax:complete', [xhr, textStatus, options])
238 |
239 | fire('pjax:end', [xhr, options])
240 | }
241 |
242 | options.error = function(xhr, textStatus, errorThrown) {
243 | var container = extractContainer("", xhr, options)
244 |
245 | var allowed = fire('pjax:error', [xhr, textStatus, errorThrown, options])
246 | if (options.type == 'GET' && textStatus !== 'abort' && allowed) {
247 | locationReplace(container.url)
248 | }
249 | }
250 |
251 | options.success = function(data, status, xhr) {
252 | var previousState = pjax.state
253 |
254 | // If $.pjax.defaults.version is a function, invoke it first.
255 | // Otherwise it can be a static string.
256 | var currentVersion = typeof $.pjax.defaults.version === 'function' ?
257 | $.pjax.defaults.version() :
258 | $.pjax.defaults.version
259 |
260 | var latestVersion = xhr.getResponseHeader('X-PJAX-Version')
261 |
262 | var container = extractContainer(data, xhr, options)
263 |
264 | var url = parseURL(container.url)
265 | if (hash) {
266 | url.hash = hash
267 | container.url = url.href
268 | }
269 |
270 | // If there is a layout version mismatch, hard load the new url
271 | if (currentVersion && latestVersion && currentVersion !== latestVersion) {
272 | locationReplace(container.url)
273 | return
274 | }
275 |
276 | // If the new response is missing a body, hard load the page
277 | if (!container.contents) {
278 | locationReplace(container.url)
279 | return
280 | }
281 |
282 | pjax.state = {
283 | id: options.id || uniqueId(),
284 | url: container.url,
285 | title: container.title,
286 | container: options.container,
287 | fragment: options.fragment,
288 | timeout: options.timeout
289 | }
290 |
291 | if (options.push || options.replace) {
292 | window.history.replaceState(pjax.state, container.title, container.url)
293 | }
294 |
295 | // Only blur the focus if the focused element is within the container.
296 | var blurFocus = $.contains(context, document.activeElement)
297 |
298 | // Clear out any focused controls before inserting new page contents.
299 | if (blurFocus) {
300 | try {
301 | document.activeElement.blur()
302 | } catch (e) { /* ignore */ }
303 | }
304 |
305 | if (container.title) document.title = container.title
306 |
307 | fire('pjax:beforeReplace', [container.contents, options], {
308 | state: pjax.state,
309 | previousState: previousState
310 | })
311 | context.html(container.contents)
312 |
313 | // FF bug: Won't autofocus fields that are inserted via JS.
314 | // This behavior is incorrect. So if theres no current focus, autofocus
315 | // the last field.
316 | //
317 | // http://www.w3.org/html/wg/drafts/html/master/forms.html
318 | var autofocusEl = context.find('input[autofocus], textarea[autofocus]').last()[0]
319 | if (autofocusEl && document.activeElement !== autofocusEl) {
320 | autofocusEl.focus()
321 | }
322 |
323 | executeScriptTags(container.scripts)
324 |
325 | var scrollTo = options.scrollTo
326 |
327 | // Ensure browser scrolls to the element referenced by the URL anchor
328 | if (hash) {
329 | var name = decodeURIComponent(hash.slice(1))
330 | var target = document.getElementById(name) || document.getElementsByName(name)[0]
331 | if (target) scrollTo = $(target).offset().top
332 | }
333 |
334 | if (typeof scrollTo == 'number') $(window).scrollTop(scrollTo)
335 |
336 | fire('pjax:success', [data, status, xhr, options])
337 | }
338 |
339 |
340 | // Initialize pjax.state for the initial page load. Assume we're
341 | // using the container and options of the link we're loading for the
342 | // back button to the initial page. This ensures good back button
343 | // behavior.
344 | if (!pjax.state) {
345 | pjax.state = {
346 | id: uniqueId(),
347 | url: window.location.href,
348 | title: document.title,
349 | container: options.container,
350 | fragment: options.fragment,
351 | timeout: options.timeout
352 | }
353 | window.history.replaceState(pjax.state, document.title)
354 | }
355 |
356 | // Cancel the current request if we're already pjaxing
357 | abortXHR(pjax.xhr)
358 |
359 | pjax.options = options
360 | var xhr = pjax.xhr = $.ajax(options)
361 |
362 | if (xhr.readyState > 0) {
363 | if (options.push && !options.replace) {
364 | // Cache current container element before replacing it
365 | cachePush(pjax.state.id, [options.container, cloneContents(context)])
366 |
367 | window.history.pushState(null, "", options.requestUrl)
368 | }
369 |
370 | fire('pjax:start', [xhr, options])
371 | fire('pjax:send', [xhr, options])
372 | }
373 |
374 | return pjax.xhr
375 | }
376 |
377 | // Public: Reload current page with pjax.
378 | //
379 | // Returns whatever $.pjax returns.
380 | function pjaxReload(container, options) {
381 | var defaults = {
382 | url: window.location.href,
383 | push: false,
384 | replace: true,
385 | scrollTo: false
386 | }
387 |
388 | return pjax($.extend(defaults, optionsFor(container, options)))
389 | }
390 |
391 | // Internal: Hard replace current state with url.
392 | //
393 | // Work for around WebKit
394 | // https://bugs.webkit.org/show_bug.cgi?id=93506
395 | //
396 | // Returns nothing.
397 | function locationReplace(url) {
398 | window.history.replaceState(null, "", pjax.state.url)
399 | window.location.replace(url)
400 | }
401 |
402 |
403 | var initialPop = true
404 | var initialURL = window.location.href
405 | var initialState = window.history.state
406 |
407 | // Initialize $.pjax.state if possible
408 | // Happens when reloading a page and coming forward from a different
409 | // session history.
410 | if (initialState && initialState.container) {
411 | pjax.state = initialState
412 | }
413 |
414 | // Non-webkit browsers don't fire an initial popstate event
415 | if ('state' in window.history) {
416 | initialPop = false
417 | }
418 |
419 | // popstate handler takes care of the back and forward buttons
420 | //
421 | // You probably shouldn't use pjax on pages with other pushState
422 | // stuff yet.
423 | function onPjaxPopstate(event) {
424 |
425 | // Hitting back or forward should override any pending PJAX request.
426 | if (!initialPop) {
427 | abortXHR(pjax.xhr)
428 | }
429 |
430 | var previousState = pjax.state
431 | var state = event.state
432 | var direction
433 |
434 | if (state && state.container) {
435 | // When coming forward from a separate history session, will get an
436 | // initial pop with a state we are already at. Skip reloading the current
437 | // page.
438 | if (initialPop && initialURL == state.url) return
439 |
440 | if (previousState) {
441 | // If popping back to the same state, just skip.
442 | // Could be clicking back from hashchange rather than a pushState.
443 | if (previousState.id === state.id) return
444 |
445 | // Since state IDs always increase, we can deduce the navigation direction
446 | direction = previousState.id < state.id ? 'forward' : 'back'
447 | }
448 |
449 | var cache = cacheMapping[state.id] || []
450 | var containerSelector = cache[0] || state.container
451 | var container = $(containerSelector), contents = cache[1]
452 |
453 | if (container.length) {
454 | if (previousState) {
455 | // Cache current container before replacement and inform the
456 | // cache which direction the history shifted.
457 | cachePop(direction, previousState.id, [containerSelector, cloneContents(container)])
458 | }
459 |
460 | var popstateEvent = $.Event('pjax:popstate', {
461 | state: state,
462 | direction: direction
463 | })
464 | container.trigger(popstateEvent)
465 |
466 | var options = {
467 | id: state.id,
468 | url: state.url,
469 | container: containerSelector,
470 | push: false,
471 | fragment: state.fragment,
472 | timeout: state.timeout,
473 | scrollTo: false
474 | }
475 |
476 | if (contents) {
477 | container.trigger('pjax:start', [null, options])
478 |
479 | pjax.state = state
480 | if (state.title) document.title = state.title
481 | var beforeReplaceEvent = $.Event('pjax:beforeReplace', {
482 | state: state,
483 | previousState: previousState
484 | })
485 | container.trigger(beforeReplaceEvent, [contents, options])
486 | container.html(contents)
487 |
488 | container.trigger('pjax:end', [null, options])
489 | } else {
490 | pjax(options)
491 | }
492 |
493 | // Force reflow/relayout before the browser tries to restore the
494 | // scroll position.
495 | container[0].offsetHeight // eslint-disable-line no-unused-expressions
496 | } else {
497 | locationReplace(location.href)
498 | }
499 | }
500 | initialPop = false
501 | }
502 |
503 | // Fallback version of main pjax function for browsers that don't
504 | // support pushState.
505 | //
506 | // Returns nothing since it retriggers a hard form submission.
507 | function fallbackPjax(options) {
508 | var url = $.isFunction(options.url) ? options.url() : options.url,
509 | method = options.type ? options.type.toUpperCase() : 'GET'
510 |
511 | var form = $('