├── LICENSE
├── README.md
├── archive.php
├── assets
├── css
│ ├── main.css
│ └── main.min.css
└── js
│ ├── argon.min.js
│ ├── bbrender.js
│ ├── jquery.pjax.js
│ └── progress.js
├── comments.php
├── footer.php
├── functions.php
├── header.php
├── images
├── Loading.gif
├── avatar.png
└── logo.png
├── index.php
├── page.php
├── post.php
└── screenshot.jpg
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 trinitrotofu
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 | # Bubble
2 |
3 | Typecho 清新风格响应式主题。
4 |
5 | 化繁为简,如沐清风。
6 |
7 | Demo: [https://typecho.tntofu.com/](https://typecho.tntofu.com/)
8 |
9 | 如果觉得本主题还不错的话可以给个 star 哦,这是对开源主题作者们最大的鼓励。
10 |
11 | # 特性
12 |
13 | + 清新的界面:大气简洁的页面布局,采用 argon design system,元素间隔恰到好处
14 | + 人性化的设计:登陆后显示后台管理按钮、醒目的文字、方便操作的按钮摆放,以及页面自适应
15 | + 流畅的用户体验:支持全站 pjax、ajax 实现评论无刷新,采用 jsDelivr CDN 全球加速主题大部分静态资源加载,减小服务器压力
16 | + 短代码功能:使用主题特殊 HTML 标签实现更丰富的功能,如高亮代码框以及友链卡片
17 | + 自定义壁纸:首页固定壁纸、其他页面随机壁纸,彰显个性爱好
18 | + 代码高亮:自带 prism.js 代码高亮,主题丰富,解析迅速
19 | + 数学公式:自带 KaTeX 数学公式渲染,相比 MathJax 更为轻量、快速
20 | + 自定义样式:支持用户添加 css 样式,随意调整主题外观
21 | + 文件轻量:主题`.zip`格式安装包(去掉`.git`文件夹后)大小不到 250 KB,简洁而不简单
22 |
23 | ~~我真佩服自己写文案的水平。~~
24 |
25 | # 使用
26 | ## 安装
27 |
28 | 在 [Releases](https://github.com/trinitrotofu/Bubble/releases) 下载 zip 源码,解压后移动到 Typecho 主题目录。
29 |
30 | ## 设置
31 |
32 | 主题设置页面位置:Typecho 后台->控制台->外观->设置外观。
33 |
34 | ## 更新
35 |
36 | 主题有自动检查更新功能,并且能够一键安装更新。
37 |
38 | 自动更新按钮设置在主题设置页面中。
39 |
40 | 自动更新将从 jsdelivr 的 CDN 上下载最新版本的主题以替换旧版本主题。
41 |
42 | 你也可以手动更新,手动更新步骤为:删除旧版本,并安装新版本。
43 |
44 | 请注意,由于 Typecho 本身设计的原因,主题更新后可能会添加新的设置项,如果不禁用主题并重新启用(通过启用其他主题),新设置项将被留空并可能导致 Bug,所以请在更新后重启主题,或者请仔细检查是否有留空的设置项(这里主要指选择型设置项,文本类设置项似乎没这个问题)。
45 |
46 | ### 站点副标题
47 |
48 | 该项用以设定首页的标题栏中站点标题后显示的文字。
49 |
50 | ### 站点 LOGO
51 |
52 | 该项应为一个图片的 URL 地址,将显示在浏览器顶部标签标题左边。
53 |
54 | 请勿使用相对地址。
55 |
56 | ### 站点头像
57 |
58 | 该项应为一个图片的 URL 地址,将显示在首页大标题上方。
59 |
60 | 请勿使用相对地址。
61 |
62 | ### 首页背景图像
63 |
64 | 该项应为一个图片的 URL 地址,用以设定网站首页背景图片,留空则使用默认紫色渐变背景。
65 |
66 | 请勿使用相对地址。
67 |
68 | ### 随机背景图像地址
69 |
70 | 该项应为一个或多个图片的 URL 地址,用以设定网站文章页、独立页面以及其他页面的头图,设定后将从给定的图片中随机抽取一个显示,留空则使用默认紫色渐变背景。
71 |
72 | URL 之间用换行隔开,即每行一个 URL,**请勿包含多余字符**。
73 |
74 | 请勿使用相对地址。
75 |
76 | ### 背景气泡
77 |
78 | 该项用以选择是否在首页以及文章页顶部背景处显示半透明气泡。
79 |
80 | ### 页脚左下角文字
81 |
82 | 该项用以设定页脚左下角的说明文字,如 Copyright 和 备案信息。
83 |
84 | 可添加 HTML 代码以实现更丰富的功能,如跳转链接等。
85 |
86 | ### 页脚小工具
87 |
88 | 该项用以选择是否在页面底部显示“最新评论”、“最新文章”和“近期归档”。
89 |
90 | ### 自定义 css
91 |
92 | 该项用以提供自定义 css 接口,用户可以填入自己需要的 css 代码来改变主题外观,例如更改字体大小。
93 |
94 | 自定义 css 的生效不需要清空缓存。
95 |
96 | ### 全站 pjax 模式
97 |
98 | 该项用以选择是否启用全站 pjax 模式提升用户访问体验。
99 |
100 | 请注意:开启该功能后可能会导致站点一些功能不正常,例如如果您未使用主题自带数学公式渲染,而是选择使用其他插件实现该功能,则会导致无刷新打开页面时数学公式插件不工作,因此请仔细检查后决定是否开启该模式。
101 |
102 | 如果您发现部分功能确实出现了问题,而您又希望开启该模式,则请查看“pjax 回调代码”一项的说明。
103 |
104 | ### pjax 回调代码
105 |
106 | 该项用以设定 pjax 渲染完毕后需执行的 JS 代码,以此解决上一项中提到的插件不工作之类的问题。
107 |
108 | 例如您有一个叫做`myFunction()`的函数在其他插件中调用了,但您发现它在全站 pjax 模式下不工作,则您应该在该项中填入以下内容:
109 |
110 | ```js
111 | myFunction();
112 | ```
113 |
114 | 那么在 pjax 执行完毕之后会调用`myFunction()`,问题就解决了。
115 |
116 | ### katex 数学公式渲染
117 |
118 | 该项用以选择是否启用 katex 数学公式渲染。
119 |
120 | ### prism.js 代码高亮
121 |
122 | 该项用以选择是否启用 prism.js 代码高亮。
123 |
124 | ### prism.js 行号显示
125 |
126 | 该项用以选择代码高亮是否显示行号。
127 |
128 | ### prism.js 高亮主题
129 |
130 | 该项用以选择 prism.js 代码高亮的主题配色。
131 |
132 | ### TOC 文章目录
133 |
134 | 该项用以选择是否启用 TOC 文章目录功能。
135 |
136 | 启用后将在文章页右侧显示一个可展开和关闭的 TOC 目录。
137 |
138 | ### TOC 目录展开状态
139 |
140 | 该项用以选择 TOC 目录栏是否默认展开。
141 |
142 | ### 评论缩进风格
143 |
144 | 该项用以选择评论缩进风格。
145 |
146 | Typecho风格:每一层回复都会缩进一次,和Typecho默认主题一致,看起来像下面这样。
147 |
148 | ```
149 | a: ---
150 | b: ---
151 | a: ---
152 | b: ---
153 | ```
154 |
155 | Bubble风格:会在必要的时候合并评论到同一层里,方便阅读,看起来像下面这样。
156 |
157 | ```
158 | a: ---
159 | b: ---
160 | a: ---
161 | b: ---
162 | ```
163 |
164 | ### 被回复人的昵称显示
165 |
166 | 该项用以选择是否在评论人的昵称后面显示被回复人的昵称
167 |
168 | 如果显示:`aa 回复 bb · 2021-05-14 22:16`,如果不显示:`aa · 2021-05-14 22:16`
169 |
170 | ## 短代码
171 |
172 | *注意:若无法正常使用此功能,请尝试关闭第三方插件,如第三方文章编辑器。*
173 |
174 | ### 高亮代码框
175 |
176 | 标签名:
177 |
178 | + `bbcode`:高亮代码框标签
179 |
180 | 语法:
181 |
182 | ```html
183 | 代码框内容
184 | ```
185 |
186 | 示例:
187 |
188 | ```html
189 | 这是绿色高亮代码框,用以显示推荐信息
190 | 这是红色高亮代码框,用以显示警告信息
191 | 这是橙色高亮代码框,用以显示注意信息
192 | 这是灰色高亮代码框,用以显示引用信息
193 | 这是蓝绿色高亮代码框,用以显示高亮信息
194 | 这是深蓝色高亮代码框,用以显示高亮信息
195 | 这是紫色高亮代码框,用以显示高亮信息
196 | ```
197 |
198 | 效果:
199 |
200 | 
201 |
202 | ### 友链
203 |
204 | 标签名:
205 |
206 | + `bblist`:友链列表标签
207 | + `bblink`:友链项标签
208 |
209 | 语法:
210 |
211 | ```html
212 |
213 | 友链名称
214 |
215 | ```
216 |
217 | 示例:
218 |
219 | ```html
220 |
221 | 豆腐蒸锅
222 | Rorical
223 |
224 | ```
225 |
226 | 效果:
227 |
228 | 
229 |
230 | # 截图
231 |
232 | 
233 |
234 | # License
235 |
236 | Open sourced under the MIT license.
237 |
238 | # 其他
239 |
240 | + 主题由[三硝基豆腐](https://github.com/trinitrotofu)、[Rorical](https://github.com/Liupaperbox)和[Totorato](https://github.com/totorato)三人共同完成
241 | + 基于[Argon Design System](https://www.creative-tim.com/product/argon-design-system)
242 | + 评论模块中部分代码参照了 Typecho 官方主题 Default
243 | + Rorical 的 Rorical 主题(同样基于 argon design system):[https://github.com/Liupaperbox/Rorical](https://github.com/Rorical/Rorical)
244 | + Totorator 的修改版 Bubble 主题:[https://github.com/totorato/Bubble](https://github.com/totorato/Bubble)
245 |
246 | # 更新历史
247 |
248 | ## 4.0.1
249 |
250 | + 修复了副标题显示不正确的问题(于是 4.0.0 只存活了不到一个小时)
251 |
252 | ## 4.0.0
253 |
254 | + 更改页面布局为卡片式布局
255 | + 新增自动更新功能
256 | + 新增 TOC 目录功能
257 | + 新增 viewer.js 图片查看器(点击放大)
258 | + 新增回到顶部按钮
259 | + 新增副标题设置功能
260 |
261 |
262 | ## 3.0.3
263 |
264 | + 优化 pjax 进度条显示
265 | + 添加图片圆角
266 | + 修改高分辨率内容卡片边距
267 | + 美化搜索框
268 |
269 | ## 3.0.2
270 |
271 | + 调整首页壁纸全屏显示以美化高分屏显示效果
272 | + 首页翻页自动滚动至文章列表
273 |
274 | ## 3.0.1
275 |
276 | + 修复评论卡托条问题
277 | + 修复左上角标题颜色问题
278 |
279 | ## 3.0.0
280 |
281 | + 新增 pjax 支持、ajax 评论无刷新及其部分细节优化
282 | + 新增 KaTeX 数学公式渲染
283 | + 新增 prism.js 代码高亮
284 | + 新增短代码功能(高亮代码框以及友链功能)
285 | + 使用 jsdelivr CDN 加速静态文件加载
286 | + 更新部分页面布局
287 | + 文章列表采用分页导航栏代替翻页按钮
288 | + 修正评论区头像被挤压的问题
289 | + 修正评论卡由于采用 flex 而高度塌缩的问题
290 |
291 | 注:此次更新主要由 Rorical 贡献代码,十分感谢
292 |
293 | ## 2.3.0
294 |
295 | + 支持自定义首页背景图像和其他页面标题栏背景图像随机显示
296 | + 调整页面按钮样式
297 | + 评论区布局调整
298 | + 新增首页头像旋转特效
299 | + 调整修复部分页面布局以及样式错误
300 | + 更新截图
301 |
302 | ## 2.2.2
303 |
304 | + 调整内容页宽度
305 | + 支持自定义页脚说明文字
306 |
307 | ## 2.2.1
308 |
309 | + 修复分类显示 Bug(issue #2)
310 | + 规整代码
311 |
312 | ## 2.2
313 |
314 | + 优化内容宽度设定
315 | + 更新换行规则,避免某单词或网址过长导致
316 |
317 | ## 2.1
318 |
319 | + 页脚显示“最新评论”、“最新文章”和“文章归档”
320 | + 新增后下角快捷编辑按钮(需登录后显示),方便快速进入后台、编辑文章
321 |
322 | ## 2.0
323 |
324 | + 采用卡片式页面,提升美观性
325 | + 美化顶部导航栏样式
326 | + 新增后台设置 Logo 和 首页头像功能
327 | + 美化文章列表和文章阅读页中文章状态标签(如分类、作者等)
328 | + 美化评论区显示,添加博主标志,调整头像大小,提升布局美观性,解决评论卡片无法显示于子评论下方的问题,适配移动端,提升阅读舒适度
329 | + 采用 font awesome 代替 nucleo 图标集,丰富图标种类
330 | + 美化加密文章密码表单
331 | + 调整隐藏/加密文章的评论卡显示机制
332 | + 调整页面 title 显示机制
333 |
334 | ## 2.0 之前
335 |
336 | 我不记得了,也懒得看
337 |
--------------------------------------------------------------------------------
/archive.php:
--------------------------------------------------------------------------------
1 | need('header.php');
4 | ?>
5 |
6 |
7 |
8 | options->randomImage), $this->options->bubbleShow); ?>
9 |
10 |
11 |
12 |
13 | archiveTitle(array(
14 | 'category'=>_t('%s'),
15 | 'search'=>_t('%s的搜索结果'),
16 | 'tag' =>_t('%s'),
17 | 'author'=>_t('%s的文章')
18 | ), '');
19 | ?>
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | have()): ?>
29 |
30 | next()): ?>
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
这里空空如也
41 |
42 |
不如换个地方看看吧?
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | need('footer.php'); ?>
--------------------------------------------------------------------------------
/assets/js/argon.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 |
3 | =========================================================
4 | * Argon Design System - v1.1.1
5 | =========================================================
6 |
7 | * Product Page: https://www.creative-tim.com/product/argon-design-system
8 | * Copyright 2020 Creative Tim (https://www.creative-tim.com)
9 | * Licensed under MIT (https://github.com/creativetimofficial/argon-design-system/blob/master/LICENSE.md)
10 |
11 | * Coded by www.creative-tim.com
12 |
13 | =========================================================
14 |
15 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
16 |
17 | */
18 | "use strict";$(document).ready(function(){$(".navbar-main .collapse").on("hide.bs.collapse",function(){$(this).addClass("collapsing-out")}),$(".navbar-main .collapse").on("hidden.bs.collapse",function(){$(this).removeClass("collapsing-out")}),$(".navbar-main .dropdown").on("hide.bs.dropdown",function(){var e=$(this).find(".dropdown-menu");e.addClass("close"),setTimeout(function(){e.removeClass("close")},200)}),$(".headroom")[0]&&new Headroom(document.querySelector("#navbar-main"),{offset:300,tolerance:{up:30,down:30}}).init();if($(".datepicker")[0]&&$(".datepicker").each(function(){$(".datepicker").datepicker({disableTouchKeyboard:!0,autoclose:!1})}),$('[data-toggle="tooltip"]').tooltip(),$('[data-toggle="popover"]').each(function(){var e="";$(this).data("color")&&(e="popover-"+$(this).data("color")),$(this).popover({trigger:"focus",template:''})}),$(".form-control").on("focus blur",function(e){$(this).parents(".form-group").toggleClass("focused","focus"===e.type||0';
6 | switch (tp) {
7 | case 'success':
8 | res += '';
9 | break;
10 | case 'danger':
11 | res += '';
12 | break;
13 | case 'warning':
14 | res += '';
15 | break;
16 | case 'secondary':
17 | res += '';
18 | break;
19 | default:
20 | res += '';
21 | break;
22 | }
23 | res += '' + obj[i].innerHTML + '';
24 | obj[i].innerHTML = res;
25 | }
26 | }
27 |
28 | function parseBblink() {
29 | let obj = document.getElementsByTagName('bblink');
30 | for (let i = 0; i < obj.length; i++) {
31 | obj[i].setAttribute('class', 'card shadow card-lift--hover friend-card');
32 | let ico = obj[i].getAttribute('icon');
33 | let lnk = obj[i].getAttribute('link');
34 | let des = obj[i].getAttribute('des');
35 | let res = ''
38 | res += obj[i].innerHTML + '
' + des + ' ';
39 | obj[i].innerHTML = res;
40 | }
41 | obj = document.getElementsByTagName('bblist');
42 | for (let i = 0; i < obj.length; i++)
43 | obj[i].innerHTML = '' + obj[i].innerHTML + '
';
44 | }
--------------------------------------------------------------------------------
/assets/js/jquery.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 = $('
18 |
19 |
20 |
83 | options->Pjax) _e(''); ?>
84 |
85 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
131 |
132 |
133 |
202 | options->Pjax): ?>
203 |
204 |
205 |
237 |
238 |
239 |
240 | options->katex): ?>
241 |
242 |
243 |
244 |
245 | options->prismjs): ?>
246 |
247 |
248 |
249 |
250 |
251 | options->prismLine): ?>
252 |
253 |
254 |
255 |
256 |
269 |
278 |
279 | footer(); ?>
280 |
281 |
--------------------------------------------------------------------------------
/functions.php:
--------------------------------------------------------------------------------
1 | to($themes);
5 | foreach ($themes -> stack as $key => $value){
6 | if($value["activated"]==1){
7 | break;
8 | }
9 | }
10 |
11 | if(!file_exists("themeupdater.php")){
12 | $updater = fopen("themeupdater.php", "w");
13 | $txt = '
14 |
15 |
16 | Updater
17 |
18 |
47 |
48 |
49 |
50 | $value){
99 | $filecontent = getRequest("https://cdn.jsdelivr.net/gh/trinitrotofu/Bubble@" . $version . "/" .$value["name"]);
100 | if (!file_exists(dirname($dir.$value["name"]))){
101 | mkdir(dirname($dir.$value["name"]),0755,true);
102 | }
103 | $fileobj = fopen($dir.$value["name"], "w");
104 | fwrite($fileobj, $filecontent);
105 | fclose($fileobj);
106 | }
107 |
108 | echo "主题更新成功!即将返回主题页面。";
109 | echo \'\';
110 | @unlink ("themeupdater.php");
111 | }catch(Exception $e){
112 | echo "更新失败!请查看错误信息或者手动更新。
";
113 | echo $e;
114 | }
115 | ?>
116 |
117 |
118 | ';
119 | fwrite($updater, $txt);
120 | fclose($updater);
121 | }
122 |
123 | echo '';
138 |
139 | echo
140 | '
141 |
142 |
143 | -
144 |
147 |
148 | -
149 |
150 | 正在检查更新...
151 |
152 |
153 | -
154 |
156 |
157 |
158 | ';
180 |
181 | $subtitle = new Typecho_Widget_Helper_Form_Element_Text('subtitle', NULL, '', _t('站点副标题'), _t('在这里填入站点副标题,以在网站标题后显示'));
182 | $form->addInput($subtitle);
183 | $logoUrl = new Typecho_Widget_Helper_Form_Element_Text('logoUrl', NULL, '', _t('站点 LOGO 地址'), _t('在这里填入一个图片 URL 地址,以在网站标题前加上一个 LOGO'));
184 | $form->addInput($logoUrl);
185 | $avatarUrl = new Typecho_Widget_Helper_Form_Element_Text('avatarUrl', NULL, '', _t('站点头像地址'), _t('在这里填入一个图片 URL 地址,以在网站首页上加上一个头像'));
186 | $form->addInput($avatarUrl);
187 | $indexImage = new Typecho_Widget_Helper_Form_Element_Text('indexImage', NULL, '', _t('首页背景图像地址'), _t('在这里填入一个图片 URL 地址, 以设定网站首页背景图片,留空则使用默认紫色渐变背景'));
188 | $form->addInput($indexImage);
189 | $randomImage = new Typecho_Widget_Helper_Form_Element_Textarea('randomImage', NULL, '', _t('随机背景图像地址'), _t('在这里填入一个或多个图片 URL 地址,每行一个,请勿包含多余字符,以设定网站文章页、独立页面以及其他页面的头图,设定后将随机显示,留空则使用默认紫色渐变背景'));
190 | $form->addInput($randomImage);
191 | $bubbleShow = new Typecho_Widget_Helper_Form_Element_Radio('bubbleShow', array('0' => _t('不显示'), '1' => _t('显示')), '1', _t('背景气泡'), _t('选择是否在首页以及文章页顶部背景处显示半透明气泡'));
192 | $form->addInput($bubbleShow);
193 | $footerText = new Typecho_Widget_Helper_Form_Element_Text('footerText', NULL, 'Powered by | Theme by ', _t('页脚左下角文字'), _t('在这里填入页脚左下角的说明文字,如 Copyright 和 备案信息,可添加 HTML 标签'));
194 | $form->addInput($footerText);
195 | $footerWidget = new Typecho_Widget_Helper_Form_Element_Radio('footerWidget', array('0' => _t('不显示'), '1' => _t('显示')), '1', _t('页脚小工具'), _t('选择是否在页面底部显示“最新评论”、“最新文章”等栏目'));
196 | $form->addInput($footerWidget);
197 | $customCss = new Typecho_Widget_Helper_Form_Element_Textarea('customCss', NULL, '', _t('自定义 css'), _t('在这里填入所需要的 css,以实现自定义页面样式,如调整字体大小等'));
198 | $form->addInput($customCss);
199 | $viewerEnable = new Typecho_Widget_Helper_Form_Element_Radio('viewerEnable', array('0' => _t('关闭'), '1' => _t('打开'),), '1', _t('开启 viewer.js 图片查看器(点击放大)'), _t('选择是否启用 viewer.js 图片查看器'));
200 | $form->addInput($viewerEnable);
201 | $Pjax = new Typecho_Widget_Helper_Form_Element_Radio('Pjax', array('0' => _t('关闭'), '1' => _t('打开')), '1', _t('开启全站 pjax 模式'), _t('选择是否启用全站 pjax 模式提升用户访问体验。注意:启用该项可能带来页面加载问题,请仔细阅读主题说明文档。'));
202 | $form->addInput($Pjax);
203 | $pjaxcomp = new Typecho_Widget_Helper_Form_Element_Textarea('pjaxcomp', NULL, '', _t('pjax 回调代码'), _t('在这里填入 pjax 渲染完毕后需执行的 JS 代码,具体使用方法请仔细阅读主题说明文档'));
204 | $form->addInput($pjaxcomp);
205 | $katex = new Typecho_Widget_Helper_Form_Element_Radio('katex', array('0' => _t('关闭'), '1' => _t('打开')), '0', _t('开启 katex 数学公式渲染'), _t('选择是否启用 katex 数学公式渲染'));
206 | $form->addInput($katex);
207 | $prismjs = new Typecho_Widget_Helper_Form_Element_Radio('prismjs', array('0' => _t('关闭'), '1' => _t('打开')), '0', _t('开启 prism.js 代码高亮'), _t('选择是否启用 prism.js 代码高亮'));
208 | $form->addInput($prismjs);
209 | $prismLine = new Typecho_Widget_Helper_Form_Element_Radio('prismLine', array('0' => _t('关闭'), '1' => _t('打开')), '0', _t('开启 prism.js 行号显示'), _t('选择是否显示 prism.js 代码高亮左侧行号'));
210 | $form->addInput($prismLine);
211 | $prismTheme = new Typecho_Widget_Helper_Form_Element_Select('prismTheme',
212 | array('prism' => _t('default'),
213 | 'prism-coy' => _t('coy'),
214 | 'prism-dark' => _t('dark'),
215 | 'prism-funky' => _t('funky'),
216 | 'prism-okaidia' => _t('okaidia'),
217 | 'prism-solarizedlight' => _t('solarizedlight'),
218 | 'prism-tomorrow' => _t('tomorrow'),
219 | 'prism-twilight' => _t('twilight')
220 | ),
221 | 'prism', _t('prism.js 高亮主题'), _t('选择 prism.js 代码高亮的主题配色'));
222 | $form->addInput($prismTheme);
223 | $toc = new Typecho_Widget_Helper_Form_Element_Radio('toc',
224 | array('0' => _t('关闭'),
225 | '1' => _t('打开'),
226 | ),
227 | '1', _t('开启 TOC 文章目录功能'), _t('选择是否开启 TOC 文章目录功能'));
228 | $form->addInput($toc);
229 | $toc_enable = new Typecho_Widget_Helper_Form_Element_Radio('toc_enable',
230 | array('0' => _t('关闭'),
231 | '1' => _t('展开'),
232 | ),
233 | '0', _t('默认 TOC 目录展开状态'), _t('选择打开文章时 TOC 目录的展开状态'));
234 | $form->addInput($toc_enable);
235 | $comment_indent_style = new Typecho_Widget_Helper_Form_Element_Radio('comment_indent_style',
236 | array('native' => _t('Typecho风格'),
237 | 'bubble' => _t('Bubble风格'),
238 | ),
239 | 'bubble', _t('评论缩进风格'), _t('选择评论缩进的风格,Typecho风格会为每一层回复进行缩进,Bubble风格会在需要的时候合并评论到同一层里,方便阅读'));
240 | $form->addInput($comment_indent_style);
241 | $comment_object_nick = new Typecho_Widget_Helper_Form_Element_Radio('comment_object_nick',
242 | array('0' => _t('不显示'),
243 | '1' => _t('显示'),
244 | ),
245 | '1', _t('被回复人的昵称显示'), _t('选择是否显示被回复人的昵称,显示"aa 回复 bb",或者只显示"aa"'));
246 | $form->addInput($comment_object_nick);
247 |
248 | $header_links_html = '
249 |
254 |
324 |
325 |
326 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 | 编辑在顶部显示的链接';
346 | $headerLinks = new Typecho_Widget_Helper_Form_Element_Text('headerLinks', NULL, '', _t('顶部跳转链接'), $header_links_html);
347 | $form->addInput($headerLinks);
348 | }
349 |
350 | function printCategory($that, $icon = 0) { ?>
351 |
352 |
353 | categories as $categories): ?>
354 |
355 |
356 |
357 |
360 |
361 |
362 | tags) > 0): ?>
363 | tags as $tags): ?>
364 |
365 |
366 |
367 | 无标签
368 |
369 |
370 |
373 | fields->pic){ ?>
374 |
375 |
376 |
377 |
392 |
393 |
394 |
395 |
410 |
411 |
412 | getTotal() > $that->parameter->pageSize) { ?>
416 |
417 |
418 |
419 |
420 |
421 | ');
429 | if ($show)
430 | _e('
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 | ');
440 | _e('');
441 | }
442 |
443 | function getRandomImage($str)
444 | {
445 | if ($str == '') return '';
446 | $arr = explode(PHP_EOL, $str);
447 | return $arr[rand(0, sizeof($arr) - 1)];
448 | }
449 |
450 | function clear_urlcan($url)
451 | {
452 | $rstr='';
453 | $tmparr=parse_url($url);
454 | $rstr=empty($tmparr['scheme'])?'http://':$tmparr['scheme'].'://';
455 | $rstr.=$tmparr['host'].$tmparr['path'];
456 | return $rstr;
457 | }
458 |
459 | function createCatalog($obj) {
460 | global $catalog;
461 | global $catalog_count;
462 | $catalog = array();
463 | $catalog_count = 0;
464 | $obj = preg_replace_callback('/(.*?)<\/h\1>/i', function($obj) {
465 | global $catalog;
466 | global $catalog_count;
467 | $catalog_count ++;
468 | $catalog[] = array('text' => trim(strip_tags($obj[3])), 'depth' => $obj[1], 'count' => $catalog_count);
469 | return ''.$obj[3].'';
470 | }, $obj);
471 | return $obj;
472 | }
473 |
474 | function getCatalog() {
475 | global $catalog;
476 | $index = '';
477 | if ($catalog) {
478 | $index = ''."\n";
479 | $prev_depth = '';
480 | $to_depth = 0;
481 | foreach($catalog as $catalog_item) {
482 | $catalog_depth = $catalog_item['depth'];
483 | if ($prev_depth) {
484 | if ($catalog_depth == $prev_depth) {
485 | $index .= ''."\n";
486 | } elseif ($catalog_depth > $prev_depth) {
487 | $to_depth++;
488 | $index .= ''."\n";
489 | } else {
490 | $to_depth2 = ($to_depth > ($prev_depth - $catalog_depth)) ? ($prev_depth - $catalog_depth) : $to_depth;
491 | if ($to_depth2) {
492 | for ($i=0; $i<$to_depth2; $i++) {
493 | $index .= ''."\n".'
'."\n";
494 | $to_depth--;
495 | }
496 | }
497 | $index .= '';
498 | }
499 | }
500 | $index .= '- '.$catalog_item['text'].'';
501 | $prev_depth = $catalog_item['depth'];
502 | }
503 | for ($i=0; $i<=$to_depth; $i++) {
504 | $index .= '
'."\n".'
'."\n";
505 | }
506 | }
507 | echo $index;
508 | }
509 |
510 | function getCommentLineInDb($coid, $depth=3) { // 3 for getting this comment, the parent and the grandparent by default
511 | $db = Typecho_Db::get();
512 | $commentLine = [];
513 | while((count($commentLine) < $depth) and (isset($coid) and 0 != $coid)) {
514 | $row = $db->fetchRow($db->select()->from('table.comments')->where('coid = ? ', $coid));
515 | if(empty($row)) break;
516 | array_push($commentLine, $row);
517 | $coid = $row['parent'];
518 | }
519 | return $commentLine;
520 | }
521 |
522 | function shouldCommentIndent($comment, &$comment_line=NULL) {
523 | $commentIndentStyle = Helper::options()->comment_indent_style;
524 | $commentLine = getCommentLineInDb($comment->coid);
525 | $isTopLevel = count($commentLine) == 1;
526 | $thisAuthor = $comment->author;
527 | $parentAuthor = count($commentLine) >= 2? $commentLine[1]['author']:NULL;
528 | $grandparentAuthor = count($commentLine) >= 3? $commentLine[2]['author']:NULL;
529 | $indent = false;
530 | if($commentIndentStyle==NULL || $commentIndentStyle=='bubble') {
531 | $indent = !$isTopLevel; // 顶层回复不需要缩进,非顶层回复才需要缩进
532 | // 有父评论和爷评论
533 | if($parentAuthor && $grandparentAuthor) {
534 | // 本评论和爷评论或者父评论是同一人发布时,不需要缩进
535 | $indent &= !($thisAuthor == $parentAuthor || $thisAuthor == $grandparentAuthor);
536 | }
537 | } else {
538 | $indent = true;
539 | }
540 | if($comment_line!==NULL) {
541 | $comment_line = $commentLine;
542 | }
543 | return $indent;
544 | }
545 |
546 | function themeInit($archive) {
547 |
548 | }
549 |
550 | function themeFields($layout) {
551 | $logoUrl = new Typecho_Widget_Helper_Form_Element_Text('pic', NULL, NULL, _t('文章头图地址'), _t('在这里填入一个图片URL地址, 就可以让文章加上头图'));
552 | $layout->addItem($logoUrl);
553 | }
--------------------------------------------------------------------------------
/header.php:
--------------------------------------------------------------------------------
1 | is('single')) {
3 | if ($this->options->toc) {
4 | $this->content = createCatalog($this->content);
5 | }
6 | $this->content = preg_replace('/
/i',"
",$this->content);
7 | }
8 | ?>
9 |
10 |
11 |
12 |
13 |
14 |
15 | archiveTitle(array(
16 | 'category' => _t('%s 下的文章'),
17 | 'search' => _t('包含关键字 %s 的文章'),
18 | 'tag' => _t('标签 %s 下的文章'),
19 | 'author' => _t('%s 的文章')
20 | ), '', ' - '); ?>options->title();
22 | if ($this->is('index') && $this->options->subtitle != '') echo " - {$this->options->subtitle}";
23 | ?>
24 |
25 |
26 | options->logoUrl();
31 | }
32 | ?>" rel="icon" type="image/png">
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | " rel="stylesheet">
42 |
43 |
44 | options->katex): ?>
45 |
46 |
47 |
48 |
49 | options->prismjs): ?>
50 |
51 |
52 | options->prismLine): ?>
53 |
54 |
55 |
56 |
57 |
58 | options->viewerEnable): ?>
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | options->customCss): ?>
67 |
68 |
69 |
70 |
71 | options->viewerEnable): ?>
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | options->Pjax=="1"): ?>
83 | header('commentReply=&antiSpam='); ?>
84 |
85 | header('commentReply='); ?>
86 |
87 |
88 |
89 |
147 | options->Pjax) _e(''); ?>
--------------------------------------------------------------------------------
/images/Loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trinitrotofu/Bubble/e5ac03db4176914736939b56f6cf8679ce4303b9/images/Loading.gif
--------------------------------------------------------------------------------
/images/avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trinitrotofu/Bubble/e5ac03db4176914736939b56f6cf8679ce4303b9/images/avatar.png
--------------------------------------------------------------------------------
/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trinitrotofu/Bubble/e5ac03db4176914736939b56f6cf8679ce4303b9/images/logo.png
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 | need('header.php');
13 | ?>
14 |
15 |
16 |
17 | options->indexImage, $this->options->bubbleShow); ?>
18 |
19 |
20 |
21 |
22 |
23 |
 {
25 | $this->options->themeUrl()
options->avatarUrl();
28 | }
29 | ?>" class="index-avatar">
30 |
31 |
options->title() ?>
32 |
33 |
options->description() ?>
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | next()): ?>
44 |
45 |
46 |
47 |
48 |
49 |
50 | _currentPage>1) echo("") ?>
51 | need('footer.php'); ?>
52 |
--------------------------------------------------------------------------------
/page.php:
--------------------------------------------------------------------------------
1 | need('header.php');
4 | ?>
5 |
6 |
7 |
8 |
9 | fields->pic?$this->fields->pic:getRandomImage($this->options->randomImage)), $this->options->bubbleShow); ?>
10 |
11 |
12 |
13 |
title() ?>
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | content();
27 | ?>
28 |
29 |
30 |
31 |
32 |
33 | hidden && $this->allow('comment')): ?>
34 |
35 | need('comments.php'); ?>
36 |
37 |
38 |
39 |
40 | need('footer.php'); ?>
--------------------------------------------------------------------------------
/post.php:
--------------------------------------------------------------------------------
1 | need('header.php');
4 | ?>
5 |
6 |
7 |
8 | options->toc) : ?>
9 |
10 |
11 |
12 |
13 |
14 |
文章目录
15 |
16 |
17 |
18 |
19 |
20 |
67 |
68 |
69 | fields->pic ? $this->fields->pic : getRandomImage($this->options->randomImage)), $this->options->bubbleShow); ?>
70 |
71 |
72 |
73 |
title() ?>
74 |
75 |
76 |
于 由 author(); ?> 发布
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | hidden){ ?>
89 |
142 |
143 | content();
145 | ?>
146 |
147 |
148 | - 分类:
149 | - 标签:
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 | hidden && $this->allow('comment')) $this->need('comments.php'); ?>
159 |
160 |
161 |
162 | need('footer.php'); ?>
--------------------------------------------------------------------------------
/screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trinitrotofu/Bubble/e5ac03db4176914736939b56f6cf8679ce4303b9/screenshot.jpg
--------------------------------------------------------------------------------