Compare commits

...

No commits in common. "v5.0.0-rc1" and "3.x" have entirely different histories.

584 changed files with 35749 additions and 25689 deletions

View File

@ -1,8 +0,0 @@
> 1%
last 2 versions
not IE 11
Android >= 6
Firefox >= 69
Chrome >= 64
iOS >= 9
not dead

View File

@ -1,18 +0,0 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
[*.{js,ts,json,vue,php}]
charset = utf-8
indent_style = space
indent_size = 2
[/node_modules/**]
charset = unset
end_of_line = unset
insert_final_newline = unset
trim_trailing_whitespace = unset
indent_style = unset
indent_size = unset

View File

@ -1 +0,0 @@
src/locales/*.json

View File

@ -1,44 +0,0 @@
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'plugin:vue/essential',
'eslint:recommended',
'@vue/typescript/recommended',
'@vue/prettier',
'@vue/prettier/@typescript-eslint',
],
parserOptions: {
ecmaVersion: 12,
parser: '@typescript-eslint/parser',
sourceType: 'module',
},
plugins: ['vue', '@typescript-eslint', 'file-progress', 'formatjs'],
rules: {
'formatjs/no-offset': 'error',
'file-progress/activate': 1,
indent: ['error', 2, { SwitchCase: 1 }],
'linebreak-style': ['error', 'unix'],
'array-element-newline': ['error', { multiline: true, minItems: 3 }],
quotes: ['error', 'single'],
semi: ['error', 'never'],
'@typescript-eslint/no-explicit-any': 0,
'@typescript-eslint/ban-types': 0,
'vue/no-multiple-template-root': 0,
'prettier/prettier': [
'error',
{
endOfLine: 'lf',
},
],
},
globals: {
Pagination: 'readonly',
WPPostAbstract: 'readonly',
Post: 'readonly',
PostListData: 'readonly',
PostStore: 'readonly',
},
}

14
.gitattributes vendored
View File

@ -1,14 +1,2 @@
# Set the default behavior, in case people don't have core.autocrlf set.
# Auto detect text files and perform LF normalization
* text=auto
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.c text
*.h text
# Declare files that will always have LF line endings on checkout.
*.sln text eol=lf
# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary

View File

@ -0,0 +1,27 @@
---
name: 报告 bug
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
<!--
如果这是您第一次在GitHub上发 issue请阅读【提问的智慧】https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/master/README-zh_CN.md
-->
**描述问题**
**bug 重现步骤**
**截图(如有)**
**系统信息**
- (前端)操作系统: [e.g. Windows 10, Android 9.0]
- 浏览器: [e.g. chrome, safari]
- PHP 版本
- WordPress 版本
- Sakura 主题版本
**补充信息**

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -1,24 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
interval: 'daily'
# Maintain dependencies for npm
- package-ecosystem: 'npm'
directory: '/'
schedule:
interval: 'daily'
# Maintain dependencies for Composer
- package-ecosystem: 'composer'
directory: '/'
schedule:
interval: 'daily'

16
.gitignore vendored
View File

@ -1,16 +0,0 @@
node_modules
dist
dist-ssr
*.local
# node
yarn-error.log
package-lock.json
composer.phar
# private config
.env.development
# mac
.DS_Store
# jest
coverage
# release
sakura-next.zip

1
.npmrc
View File

@ -1 +0,0 @@
engine-strict=true

View File

@ -1,8 +0,0 @@
module.exports = ({ env, options }) => ({
...options,
plugins: [
require('autoprefixer')({}),
// require('flex-gap-polyfill')() // bugly with vite
// require('stylelint')({}), //
],
})

View File

@ -1 +0,0 @@
src/locales/*.json

View File

@ -1,50 +0,0 @@
module.exports = {
// 行宽 default:80
printWidth: 100,
// tab 宽度 default:2
tabWidth: 2,
// 使用 tab 键 default:false
useTabs: false,
// 语句行末是否添加分号 default:true
semi: false,
// 是否使用单引号 default:false
singleQuote: true,
// 对象需要引号在加 default:"as-needed"
quoteProps: 'as-needed',
// jsx单引号 default:false
jsxSingleQuote: false,
// 最后一个对象元素加逗号 default:"es5"
trailingComma: 'es5',
// 在对象字面量声明所使用的的花括号后({)和前(})输出空格 default:true
bracketSpacing: true,
// 将 > 多行 JSX 元素放在最后一行的末尾而不是单独放在下一行不适用于自闭元素。default:false
jsxBracketSameLine: false,
// (x) => {} 是否要有小括号 default:"always"
arrowParens: 'always',
// default:0
rangeStart: 0,
// default:Infinity
rangeEnd: Infinity,
// default:false
insertPragma: false,
// default:false
requirePragma: false,
// 不包装 markdown text default:"preserve"
proseWrap: 'never',
// HTML空白敏感性 default:"css"
htmlWhitespaceSensitivity: 'strict',
// 在 *.vue 文件中 Script 和 Style 标签内的代码是否缩进 default:false
vueIndentScriptAndStyle: false,
// 末尾换行符 default:"lf"
endOfLine: 'lf',
// default:"auto"
embeddedLanguageFormatting: 'auto',
overrides: [
{
files: '*.md',
options: {
tabWidth: 2,
},
},
],
}

View File

@ -1 +0,0 @@
node_modules

View File

@ -1,10 +0,0 @@
{
"plugins": [
"stylelint-scss"
],
"extends": "stylelint-config-standard",
"rules": {
"rule-empty-line-before": "never",
"indentation": 2
}
}

View File

@ -1,13 +0,0 @@
{
"recommendations": [
"bmewburn.vscode-intelephense-client",
"dbaeumer.vscode-eslint",
"mhutchie.git-graph",
"donjayamanne.githistory",
"eamodio.gitlens",
"esbenp.prettier-vscode",
"octref.vetur",
"editorconfig.editorconfig",
"neilbrayfield.php-docblocker"
]
}

132
.vscode/settings.json vendored
View File

@ -1,132 +0,0 @@
{
"stylelint.enable": false,
"php-docblocker.returnGap": true,
"php-docblocker.qualifyClassNames": true,
"php-docblocker.author": {
"name": "mashirozx",
"email": "moezhx@outlook.com"
},
"editor.formatOnSave": true,
"emmet.extensionsPath": [
".vscode/"
],
"emmet.triggerExpansionOnTab": true,
"emmet.showExpandedAbbreviation": "always",
"emmet.includeLanguages": {
"vue": "html",
// "twig": "html"
},
"prettier.requireConfig": true,
"prettier.configPath": ".prettierrc.js",
"prettier.semi": true,
"files.associations": {
"*.json": "jsonc",
// "*.html": "twig"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript|react]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript|react]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[less]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[php]": {
"editor.defaultFormatter": "bmewburn.vscode-intelephense-client"
},
"[twig]": {
"editor.defaultFormatter": "mblode.twig-language-2"
},
"intelephense.telemetry.enabled": false,
"intelephense.format.enable": true,
"intelephense.completion.triggerParameterHints": true,
"intelephense.completion.insertUseDeclaration": true,
"intelephense.files.maxSize": 3000000,
"intelephense.stubs": [
"apache",
"bcmath",
"bz2",
"calendar",
"com_dotnet",
"Core",
"ctype",
"curl",
"date",
"dba",
"dom",
"enchant",
"exif",
"FFI",
"fileinfo",
"filter",
"fpm",
"ftp",
"gd",
"gettext",
"gmp",
"hash",
"iconv",
"imap",
"intl",
"json",
"ldap",
"libxml",
"mbstring",
"meta",
"mysqli",
"oci8",
"odbc",
"openssl",
"pcntl",
"pcre",
"PDO",
"pdo_ibm",
"pdo_mysql",
"pdo_pgsql",
"pdo_sqlite",
"pgsql",
"Phar",
"posix",
"pspell",
"readline",
"Reflection",
"session",
"shmop",
"SimpleXML",
"snmp",
"soap",
"sockets",
"sodium",
"SPL",
"sqlite3",
"standard",
"superglobals",
"sysvmsg",
"sysvsem",
"sysvshm",
"tidy",
"tokenizer",
"xml",
"xmlreader",
"xmlrpc",
"xmlwriter",
"xsl",
"Zend OPcache",
"zip",
"zlib",
"wordpress"
]
}

12
.vscode/snippets.json vendored
View File

@ -1,12 +0,0 @@
{
"html": {
"snippets": {
"divc": "div[class=${1}]",
"view": "div[class=${1}]",
"text": "span[class=\"text\"]",
"image": "Image[src=${1} placeholder=${2} :avatar=\"false\" alt=${2} :draggable=\"false\"]",
"icon": "UiIcon[name=${1} :width=\"100%\" :height=\"100%\"]",
"slot": "slot[name=${1}]"
}
}
}

59
404.php 100644
View File

@ -0,0 +1,59 @@
<?php
/**
* The template for displaying 404 pages (not found).
*
* @link https://codex.wordpress.org/Creating_an_Error_404_Page
*
* @package Akina
*/
?>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo( 'charset' ); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="<?php echo akina_option('favicon_link', ''); ?>"/>
<title itemprop="name"><?php global $page, $paged;wp_title( '-', true, 'right' );
bloginfo( 'name' );$site_description = get_bloginfo( 'description', 'display' );
if ( $site_description && ( is_home() || is_front_page() ) ) echo " - $site_description";if ( $paged >= 2 || $page >= 2 ) echo ' - ' . sprintf( __( 'page %s'), max( $paged, $page ) );/*第 %s 页*/?>
</title>
<link type="text/css" media="all" href="https://cdn.jsdelivr.net/gh/moezx/cdn@3.2.2/css/lib.css" rel="stylesheet" />
<?php wp_head(); ?>
<script>
var the_url=window.location.href;
var the_dom="<?php echo str_replace("http://", "", str_replace("https://", "", get_site_url())); ?>";
var no_report = false;
if (the_dom!= '2heng.xin') {
no_report = true;
}
var the_ua=navigator.userAgent;
var the_ref=document.referrer;
function httpGet(theUrl) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", theUrl, false ); // false for synchronous request
xmlHttp.send( null );
return xmlHttp.responseText;
}
var report_url = "https://api.mashiro.top/count/404/?" + "url="+the_url+"&ua="+the_ua+"&ref="+the_ref;
if (!no_report) httpGet(report_url);
</script>
</head>
<body <?php body_class(); ?>>
<section class="error-404 not-found">
<div class="error-img">
<div class="anim-icon" id="404" style="height: 66%;"></div>
</div>
<div class="err-button back">
<a id="golast" href=javascript:history.go(-1);><?php _e('return to previous page','sakura');/*返回上一页*/?></a>
<a id="gohome" href="<?php bloginfo('url');?>"><?php _e('return to home page','sakura');/*返回主页*/?></a>
</div>
<div style="display:block; width:284px;margin: auto;">
<p style="margin-bottom: 1em;margin-top: 1.5em;text-align: center;font-size: 15px;"><?php _e('Don\'t worry, search in site?','sakura');/*别急,试试站内搜索?*/?></p>
<form class="s-search" method="get" action="/" role="search">
<i class="iconfont icon-search" style="bottom: 8px;left: 12px;"></i>
<input class="text-input" style="padding: 8px 20px 8px 46px;" type="search" name="s" placeholder="<?php _e('Search...', 'akina') ?>" required>
</form>
</div>
</section>
<script src="https://cdn.jsdelivr.net/gh/moezx/cdn@3.1.5/js/other/404.min.js" type="text/javascript"></script>
</body>

339
LICENSE 100644
View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

47
README-en.md 100644
View File

@ -0,0 +1,47 @@
Sakura🌸: A Wonderful WordPress Theme
===
[中文](README.md) | English
![Sakura](screenshot.jpg)
![PHP version](https://img.shields.io/badge/PHP-7.1+-4F5B93.svg?style=flat-square&logo=php)
![WP version](https://img.shields.io/badge/WordPress-5.3-0073aa.svg?style=flat-square&logo=wordpress)
[![GitHub release](https://img.shields.io/github/release/mashirozx/Sakura.svg?style=flat-square)](https://github.com/mashirozx/Sakura/releases/latest)
[![Github commits (since latest release)](https://img.shields.io/github/commits-since/mashirozx/Sakura/latest/dev.svg?style=flat-square)](https://github.com/mashirozx/Sakura/commits/dev)
[![](https://data.jsdelivr.com/v1/package/gh/moezx/cdn/badge)](https://www.jsdelivr.com/package/gh/moezx/cdn)
Modified based on theme [Akina (by Fuzzz)](http://www.akina.pw/themeakina) and [Siren (by Louie)](https://github.com/louie-senpai/Siren).
The rebuild version is on the way :)
### Install
#### By GIT
```bash
cd /wp-content/themes/
git clone https://github.com/mashirozx/Sakura.git
```
#### By FTP
**Rename the theme folder name as `Sakura`** if you download form the green [Clone or download] button on this page. Make sure the path to the theme root is /wp-content/themes/Sakura/`.
### Configuration
Config your theme at `Mune-Appearance-Sakura Options`. Please turn on `Use js and css file of the theme locally` under CDN tag if you want to modify local SCC an Javascript files.
### Update
```bash
cd /wp-content/themes/Sakura
git fetch
git pull
```
[Learn more info about the theme (Chinese)](https://2heng.xin/theme-sakura/)
Enjoy it!
### Donate
[WeChat Pay](https://view.moezx.cc/images/2018/05/28/WeChanQR.png) | [AliPay](https://view.moezx.cc/images/2018/05/28/AliPayQR.jpg) | [PayPal](https://paypal.me/mashirozx)
### Need a server?
[![](https://www.vultr.com/media/banners/banner_728x90.png)](https://www.vultr.com/?ref=7674346)

View File

@ -1,27 +1,34 @@
# sakura-next
Sakura🌸: 樱花庄的白猫博客主题
===
## Requirement
中文 | [English](README-en.md)
- PHP=7.4
- WordPress>=5.6.0
- Node.js>=14.17.1
- yarn>=1.22.10
![Sakura](screenshot.jpg)
## Commands
![PHP version](https://img.shields.io/badge/PHP-7.1+-4F5B93.svg?style=flat-square&logo=php)
![WP version](https://img.shields.io/badge/WordPress-5.3-0073aa.svg?style=flat-square&logo=wordpress)
[![GitHub release](https://img.shields.io/github/v/release/mashirozx/Sakura.svg?style=flat-square&logo=github)](https://github.com/mashirozx/Sakura/releases/latest)
[![Github commits (since latest release)](https://img.shields.io/github/commits-since/mashirozx/Sakura/latest/dev.svg?style=flat-square&logo=git&color=important)](https://github.com/mashirozx/Sakura/commits/dev)
[![](https://data.jsdelivr.com/v1/package/gh/moezx/cdn/badge)](https://www.jsdelivr.com/package/gh/moezx/cdn)
```sh
composer install # install php dependencies
yarn install # install node dependencies
yarn dev # run dev server
yarn build # build assets
yarn test # Jest test
yarn lint # eslint
yarn format # auto format
yarn i18n # export i18n variables
yarn rsync # sync backend app with server, see docs/dev.md
yarn composer # run `composer install` on remote server
yarn icon # generate svg icon component
yarn remote-download:geoip2 # download GeoIP db on remote server
yarn local-download:geoip2 # download GeoIP db locally
yarn options # export node side admin options.ts to php side options.json
```
在 Louie 基于 Fuzzz 的 [Akina](http://www.akina.pw/themeakina) 主题修改的主题 [Siren](https://github.com/louie-senpai/Siren) 基础上三次修改 =.=
两位前辈做得已经很棒了,或许我所做的只是把他们的代码弄得凌乱不堪吧 :)
特别感谢 [@Spirit](https://github.com/spirit1431007) 对本项目的贡献!
注意:建议 `git clone` 下载([简易 Git 使用指南](https://github.com/mashirozx/Sakura/wiki/Git-%E4%B8%8B%E8%BD%BD%E3%80%81%E6%9B%B4%E6%96%B0%E6%8C%87%E5%8D%97));如果选择下载压缩包,**解压后记得把文件夹名改回 `Sakura`,也即保证主题路径为 `/wp-content/themes/Sakura/`**;主题设置在 `菜单-外观-Sakura 主题设置`DIY 的时候建议采用[子主题](https://github.com/mashirozx/Sakura/tree/child) 并勾选 `Sakura 主题设置-CDN-本地调用主题 js、css 文件`;请留意主题说明里的其他注意事项。
主题使用说明见:<https://2heng.xin/theme-sakura/>
本仓库国内镜像:<https://git.moezx.cc/mirrors/sakura>
主题交流群860262481
希望你喜欢!
### 打赏支持~
[微信支付](https://view.moezx.cc/images/2018/05/28/WeChanQR.png) | [支付宝](https://view.moezx.cc/images/2018/05/28/AliPayQR.jpg) | [PayPal](https://paypal.me/mashirozx)
### 需要服务器吗?
[![](https://www.vultr.com/media/banners/banner_728x90.png)](https://www.vultr.com/?ref=7674346)

2
app/.gitignore vendored
View File

@ -1,2 +0,0 @@
vendor
cache

View File

@ -1,2 +0,0 @@
main/
admin/

View File

View File

@ -1,291 +0,0 @@
{
"basic.site.title": {
"namespace": "basic.site.title",
"public": true,
"title": "Site title",
"desc": "The site title",
"type": "string",
"default": "Theme Sakura"
},
"basic.site.logo": {
"namespace": "basic.site.logo",
"public": true,
"title": "Site logo",
"desc": "The site's Logo image, will display on navigation bar.",
"type": "mediaPicker",
"default": [
{
"id": 0,
"url": "https://v3.vuejs.org/logo.png"
}
],
"binds": {
"title": "Select image for site logo.",
"button": "Use this image",
"type": "image",
"multiple": false
}
},
"homepage.slogan": {
"namespace": "homepage.slogan",
"public": true,
"title": "Slogan",
"desc": "The slogan text (with typewriter effect), recommend 10-20 characters.",
"type": "string",
"default": "Hello World!"
},
"homepage.quote": {
"namespace": "homepage.quote",
"public": true,
"title": "Quote",
"desc": "The quote text (behinds the slogan).",
"type": "longString",
"default": "The most beautiful things in the world cannot be seen or even touched. \nThey must be felt with the heart."
},
"homepage.signature": {
"namespace": "homepage.signature",
"public": true,
"title": "Signature",
"desc": "The signature text (follows the quote).",
"type": "string",
"default": "—Helen Keller"
},
"homepage.cover.image": {
"namespace": "homepage.cover.image",
"public": true,
"title": "Cover image",
"desc": "Homepage cover image.",
"type": "mediaPicker",
"default": [
{
"id": 0,
"url": "https://view.moezx.cc/images/2021/06/19/ca4748651c3c67e7e4c29c34fb13bc33.jpg"
},
{
"id": 0,
"url": "https://view.moezx.cc/images/2021/07/21/c21fcdbf4cf09674537d928884863ecc.jpg"
}
],
"binds": {
"title": "Select image for homepage cover.",
"button": "Use this image",
"type": "image",
"multiple": true
}
},
"social.github": {
"namespace": "social.github",
"public": true,
"title": "Github username",
"desc": "Your '<a href=\"https://github.com\" target=\"_blank\">Github</a>' username",
"type": "string",
"default": ""
},
"social.gitlab": {
"namespace": "social.gitlab",
"public": true,
"title": "Gitlab username",
"desc": "Your '<a href=\"https://gitlab.com\" target=\"_blank\">Gitlab</a>' username",
"type": "string",
"default": ""
},
"social.twitter": {
"namespace": "social.twitter",
"public": true,
"title": "Twitter username",
"desc": "Your '<a href=\"https://twitter.com\" target=\"_blank\">Twitter</a>' username",
"type": "string",
"default": ""
},
"social.weibo": {
"namespace": "social.weibo",
"public": true,
"title": "Weibo username",
"desc": "Your '<a href=\"https://weibo.com\" target=\"_blank\">Weibo</a>' username",
"type": "string",
"default": ""
},
"social.facebook": {
"namespace": "social.facebook",
"public": true,
"title": "Facebook username",
"desc": "Your '<a href=\"https://facebook.com\" target=\"_blank\">Facebook</a>' username",
"type": "string",
"default": ""
},
"social.stackoverflow": {
"namespace": "social.stackoverflow",
"public": true,
"title": "Stackoverflow username",
"desc": "Your '<a href=\"https://stackoverflow.com\" target=\"_blank\">Stackoverflow</a>' username",
"type": "string",
"default": ""
},
"thirdParty.reCaptcha.enable": {
"namespace": "thirdParty.reCaptcha.enable",
"public": true,
"title": "Enable reCAPTCHA",
"desc": "Use reCAPTCHA for anti-spam check.",
"type": "switcher",
"default": false,
"binds": {
"positiveLabel": "Enabled",
"negativeLabel": "Disabled",
"disabled": false
}
},
"thirdParty.reCaptcha.version": {
"namespace": "thirdParty.reCaptcha.version",
"public": true,
"title": "reCAPTCHA version",
"desc": "Register your reCAPTCHA app '<a href=\"https://www.google.com/recaptcha/about/\" target=\"_blank\">here</a>', and choose a version.",
"type": "choose",
"default": null,
"binds": {
"options": [
{
"label": "reCAPTCHA version 3",
"disabled": false
},
{
"label": "reCAPTCHA version 2",
"disabled": false
}
]
}
},
"thirdParty.reCaptcha.siteKey": {
"namespace": "thirdParty.reCaptcha.siteKey",
"public": true,
"title": "reCAPTCHA site key",
"type": "string",
"default": ""
},
"thirdParty.reCaptcha.secretKey": {
"namespace": "thirdParty.reCaptcha.secretKey",
"public": false,
"title": "reCAPTCHA secret key",
"type": "string",
"default": ""
},
"other.hello": {
"namespace": "other.hello",
"public": true,
"title": "Hello world",
"type": "string",
"default": "world"
},
"demo.string": {
"namespace": "demo.string",
"public": true,
"title": "String",
"desc": "One line string input.",
"type": "string",
"default": "Hello world!"
},
"demo.longString": {
"namespace": "demo.longString",
"public": true,
"title": "Long string",
"desc": "Textarea for long string input.",
"type": "longString",
"default": "\"It is the unknown we fear when we look upon death and darkness, nothing more.\"\n-- Albus Dumbledore"
},
"demo.switcher": {
"namespace": "demo.switcher",
"public": true,
"title": "Switcher",
"type": "switcher",
"desc": "True/False switcher.",
"default": true,
"binds": {
"positiveLabel": "current on",
"negativeLabel": "current off",
"disabled": false
}
},
"demo.choose": {
"namespace": "demo.choose",
"public": true,
"title": "Choose",
"desc": "Choose one from options.",
"type": "choose",
"default": null,
"binds": {
"options": [
{
"label": "op 1",
"disabled": false
},
{
"label": "op 2",
"disabled": false
},
{
"label": "op 3",
"disabled": false
},
{
"label": "op 4",
"disabled": true
}
]
}
},
"demo.selection": {
"namespace": "demo.selection",
"public": true,
"title": "Selection",
"desc": "Selection multiple items from options. max: {0: no limit, >0: limit}",
"type": "selection",
"default": [
true,
false,
true
],
"binds": {
"options": [
{
"label": "op 1",
"disabled": false
},
{
"label": "op 2",
"disabled": false
},
{
"label": "op 3",
"disabled": false
},
{
"label": "op 4",
"disabled": true
}
],
"max": 2
}
},
"demo.mediaPicker": {
"namespace": "demo.mediaPicker",
"public": true,
"title": "Media picker",
"desc": "<code>type=\"image\"|\"video\"|\"audio?\"</code>, the object must include id, id=0 for remote media.",
"type": "mediaPicker",
"default": [
{
"id": 0,
"url": "https://view.moezx.cc/images/2021/07/02/d5ab73174d18652d890e2f4d1b9bef8f.gif"
},
{
"id": 0,
"url": "https://view.moezx.cc/images/2021/07/02/a90553bf5b67770e87a89b2ce204eaa7.gif"
}
],
"binds": {
"title": "Select Media",
"button": "Use this media",
"type": "image",
"multiple": true
}
}
}

View File

@ -1,47 +0,0 @@
<?php
namespace Sakura\Controllers;
use Sakura\Controllers\UserController;
use Sakura\Controllers\AvatarController;
use Sakura\Lib\Exception;
class AuthorController extends UserController
{
/**
* Get author meta by ID with mutiple fields
*
* @param integer $author_id
* @param array $fields
*
* @return void
*/
public static function get_author_meta_fields(int $author_id, array $fields)
{
$author_meta = [];
foreach ($fields as $field) {
$meta = get_the_author_meta($field, $author_id);
if (isset($meta)) {
$author_meta[$field] = $meta;
} else {
throw new Exception("No such author or field: \$id={$author_id}, \$fields={$field}");
}
}
return $author_meta;
}
/**
* Get usefull meta fileds of author
*
* @param integer $author_id
*
* @return void
*/
public static function get_author_meta(int $author_id)
{
$fields = ['description', 'display_name', 'nickname', 'user_level', 'user_nicename', 'user_status', 'user_url'];
$meta = self::get_author_meta_fields($author_id, $fields);
$meta['avatar'] = AvatarController::get_avatar($author_id);
return $meta;
}
}

View File

@ -1,32 +0,0 @@
<?php
namespace Sakura\Controllers;
use Sakura\Controllers\BaseController;
class AvatarController extends BaseController
{
/**
* Get avatar set of all sizes
*
* @param mixed (int|string) $id_or_email
* @return string
*/
public static function get_avatar($id_or_email)
{
// TODO: use standard 24/48/96
$sizes = [
// 'small' => 24,
// 'normal' => 48,
// 'large' => 96,
'24' => 24,
'48' => 48,
'96' => 96
];
$avatar_array = [];
foreach ($sizes as $key => $value) {
$avatar_array[$key] = get_avatar_url($id_or_email, ['size' => $value, 'default' => 'avatar_default']);
}
return $avatar_array;
}
}

View File

@ -1,37 +0,0 @@
<?php
namespace Sakura\Controllers;
use WP_REST_Controller;
/**
* The controller abstract base
* @since 1.0.0
* @license GPLv3
* @author mashirozx <moezhx@outlook.com>
*/
class BaseController extends WP_REST_Controller
{
public static $version = SAKURA_VERSION;
public static $text_domain = SAKURA_TEXT_DOMAIN;
/**
* The rest API request parameters
* @since 0.0.1
* @var WP_REST_Request
*/
protected $request;
/**
* Response status code
* @since 0.0.1
* @var WP_REST_Request
*/
protected $code = 200;
protected function request_parser(\WP_REST_Request $request)
{
$this->request = $request;
}
}

View File

@ -1,13 +0,0 @@
<?php
namespace Sakura\Controllers;
use Sakura\Controllers\TermController;
class CategoryController extends TermController
{
public static function get_the_category(int $post_id)
{
return get_the_category($post_id);
}
}

View File

@ -1,108 +0,0 @@
<?php
namespace Sakura\Controllers;
use Sakura\Lib\Exception;
use Sakura\Controllers\BaseController;
use Sakura\Models\CommentModel;
use Sakura\Controllers\AvatarController;
use WP_REST_Request;
use WP_REST_Response;
class CommentController extends BaseController
{
/**
* Set comment ancestor_id from meta data when init
* @return void
*/
public static function init_comment_ancestor_meta()
{
$res = [];
$comments = get_comments();
foreach ($comments as $comment) {
$comment_ID = $comment->comment_ID;
// delete_comment_meta($comment_ID, self::$ancestor_id_meta_key);
$ancestor_id = CommentModel::get_comment_ancestor_meta($comment_ID);
// $ancestor_id = $ancestor_id == false ? false : intval($ancestor_id);
// array_push($res, [$comment_ID, $ancestor_id]);
if ($ancestor_id === 0) {
array_push($res, "[KEEPT] {$comment_ID}:{$ancestor_id} -> {$comment_ID}:{$ancestor_id}");
} elseif (empty($ancestor_id)) {
$meta = CommentModel::set_comment_ancestor_meta($comment_ID);
array_push($res, "[CREATE] {$comment_ID}:{$ancestor_id} -> {$comment_ID}:{$meta}");
} elseif (empty(CommentModel::get_comments([$ancestor_id]))) {
$meta = CommentModel::update_comment_ancestor_meta($comment_ID);
array_push($res, "[UPDATE] {$comment_ID}:{$ancestor_id} -> {$comment_ID}:{$meta}");
} else {
array_push($res, "[KEEPT] {$comment_ID}:{$ancestor_id} -> {$comment_ID}:{$ancestor_id}");
}
}
return $res;
}
/**
* Set comment ancestor_id from meta data when init
* @return void
*/
public static function init_comment_user_agent_info_meta()
{
$res = [];
$comments = get_comments();
foreach ($comments as $comment) {
$comment_ID = $comment->comment_ID;
// delete_comment_meta($comment_ID, self::$ancestor_id_meta_key);
$user_agent_info = CommentModel::get_comment_user_agent_info_meta($comment_ID);
if (empty($user_agent_info)) {
$meta = CommentModel::set_comment_user_agent_info_meta($comment_ID);
array_push($res, ['type' => 'CREATE', 'ID' => $comment_ID, 'ua' => $meta]);
} else {
$meta = CommentModel::update_comment_user_agent_info_meta($comment_ID);
array_push($res, ['type' => 'UPDATE', 'ID' => $comment_ID, 'ua' => $meta]);
}
}
return $res;
}
/**
* Get comment's all children/descendant
*
* @param integer $comment_ID
* @param integer $per_page
* @param integer $page
* @param integer $offset
* @param string $order (string) How to order retrieved comments. Accepts 'ASC', 'DESC'. Default: 'DESC'.
*
* @return array
*/
public static function get_comment_children(int $comment_ID, int $per_page, int $page, int $offset, string $order)
{
return CommentModel::get_comments_with_public_fields([
'meta_key' => CommentModel::$ancestor_id_meta_key,
'meta_value' => $comment_ID,
'number' => $per_page,
'paged' => $page,
'offset' => $offset,
// (string|array) Comment status or array of statuses. To use 'meta_value' or 'meta_value_num', $meta_key must also be defined. To sort by a specific $meta_query clause, use that clause's array key. Accepts 'comment_agent', 'comment_approved', 'comment_author', 'comment_author_email', 'comment_author_IP', 'comment_author_url', 'comment_content', 'comment_date', 'comment_date_gmt', 'comment_ID', 'comment_karma', 'comment_parent', 'comment_post_ID', 'comment_type', 'user_id', 'comment__in', 'meta_value', 'meta_value_num', the value of $meta_key, and the array keys of $meta_query. Also accepts false, an empty array, or 'none' to disable ORDER BY clause. Default: 'comment_date_gmt'.
// TODO: order by 'like'
'orderby' => 'comment_date_gmt',
'order' => $order,
]);
}
public static function get_comment_meta_fields(int $comment_ID)
{
return CommentModel::get_comment_meta_fields($comment_ID);
}
public static function get_comment_plain(int $comment_ID)
{
$comment = CommentModel::get_comment($comment_ID);
return $comment->comment_content;
}
public static function rest_api_comment_content_filter(array $comment)
{
$comment['content']['plain'] = self::get_comment_plain($comment['id']);
return $comment['content'];
}
}

View File

@ -1,109 +0,0 @@
<?php
namespace Sakura\Controllers;
use WP_REST_Response;
use WP_REST_Request;
use WP_Rewrite;
class InitStateController extends BaseController
{
public function show(WP_REST_Request $request)
{
$this->request_parser($request);
$data = $this->get_initial_state();
$response = new WP_REST_Response($data);
$response->set_status($this->code);
return $response;
}
public function get_initial_state()
{
return array(
'api_base' => get_rest_url(),
'root' => esc_url_raw(rest_url()),
'nonce' => wp_create_nonce('wp_rest'),
'routing' => $this->get_routing(),
'site_info' => $this->get_site_info(),
'menus' => (new MenuController)->get_menus(),
// 'rewrite_rules' => (new \WP_Rewrite())->rewrite_rules(),
'index' => (new WP_Rewrite())->index,
'config' => (new OptionController)->get_public_display_options(),
// 'recaptcha_site_key' => sakura_options('thirdParty.reCaptcha.siteKey', ''), // use thirdParty.reCaptcha.siteKey
);
}
public static function get_routing()
{
$routing = array(
'category_base' => get_option('category_base'),
'page_on_front' => null,
'page_for_posts' => null,
'permalink_structure' => get_option('permalink_structure'),
'show_on_front' => get_option('show_on_front'),
'tag_base' => get_option('tag_base'),
'url' => get_bloginfo('url')
);
if ($routing['show_on_front'] === 'page') {
$front_page_id = get_option('page_on_front');
$posts_page_id = get_option('page_for_posts');
if ($front_page_id) {
$front_page = get_post($front_page_id);
$routing['page_on_front'] = $front_page->post_name;
}
if ($posts_page_id) {
$posts_page = get_post($posts_page_id);
$routing['page_for_posts'] = $posts_page->post_name;
}
}
return $routing;
}
public static function get_site_info()
{
return array(
'woordpress_version' => get_bloginfo('version'),
'sakura_version' => self::$version,
'sakura_text_domain' => self::$text_domain,
'description' => get_bloginfo('description'),
'docTitle' => '',
'loading' => false,
'logo' => get_theme_mod('custom_logo'),
'name' => get_bloginfo('name'),
'posts_per_page' => get_option('posts_per_page'),
'url' => get_bloginfo('url')
);
}
// TODO: need auth first
public function init_ancestor_meta_show(WP_REST_Request $request)
{
$this->request_parser($request);
$data = CommentController::init_comment_ancestor_meta();
$response = new WP_REST_Response($data);
$response->set_status($this->code);
return $response;
}
public function init_user_agent_info_meta_show(WP_REST_Request $request)
{
$this->request_parser($request);
$data = CommentController::init_comment_user_agent_info_meta();
$response = new WP_REST_Response($data);
$response->set_status($this->code);
return $response;
}
}

View File

@ -1,46 +0,0 @@
<?php
namespace Sakura\Controllers;
use Sakura\Controllers\BaseController;
use Sakura\Lib\Exception;
class MediaController extends BaseController
{
/**
* Markup for wp_upload_dir, drop sensitive fs info
*
* @return string ie. "http://wp.moezx.cc/wp-content/uploads"
*/
public static function get_upload_baseurl()
{
$dir = wp_upload_dir();
return $dir['baseurl'];
}
/**
* Get attachment metadata by attachment_id, also returns the resources' url
*
* @param integer $attachment_id
* @return mixed (array | false) return false if attachment_id is invalid (in rest API post endpoint, WP will return attachment_id as 0, which means there is no attachment)
*/
public static function get_attachment_metadata(int $attachment_id)
{
$metadata = wp_get_attachment_metadata($attachment_id);
if (!$metadata) {
// throw new Exception("Invalid file name. \$attachment_id=$attachment_id");
return false;
}
$file = $metadata['file'];
$file_name = basename($file);
$subdir = str_replace($file_name, '', $file);
$url_prefix = Self::get_upload_baseurl() . '/' . $subdir;
$metadata['url'] = $url_prefix . $file_name;
foreach ($metadata['sizes'] as $size => $meta) {
$metadata['sizes'][$size]['url'] = $url_prefix . $meta['file'];
}
return $metadata;
}
}

View File

@ -1,68 +0,0 @@
<?php
namespace Sakura\Controllers;
use Sakura\Helpers\CustomMenuMetaFieldsHelper;
class MenuController extends BaseController
{
public function show(\WP_REST_Request $request)
{
$this->request_parser($request);
$location = isset($_GET['location']) ?? $_GET['location'];
$data = !$location ? $this->get_menus() : $this->get_menu($location);
$response = new \WP_REST_Response($data);
$response->set_status($this->code);
return $response;
}
public function get_menu_location(string $location_name)
{
$locations = \get_nav_menu_locations();
return $locations[$location_name];
}
public function get_menu($location)
{
$id = $this->get_menu_location($location);
if (!$id) {
return [];
}
$menu_items = \wp_get_nav_menu_items($id, array());
$fitered_menu_items = array();
foreach ($menu_items as $menu_item) {
$fitered_menu_item = array(
"id" => $menu_item->ID,
'title' => $menu_item->title,
'url' => $menu_item->url,
'type' => $menu_item->type,
'parent' => intval($menu_item->menu_item_parent),
);
foreach (array_keys(CustomMenuMetaFieldsHelper::fileds()) as $name) {
$fitered_menu_item[$name] = get_post_meta($menu_item->ID, "_custom_menu_meta_{$name}", true);
}
array_push($fitered_menu_items, $fitered_menu_item);
}
return $fitered_menu_items;
}
public function get_menus()
{
$menus = array();
// $locations is an array where ([NAME] = MENU_ID);
$locations = get_nav_menu_locations();
foreach (array_keys($locations) as $location) {
$menu = $this->get_menu($location);
$menus[$location] = $menu;
}
return $menus;
}
}

View File

@ -1,203 +0,0 @@
<?php
namespace Sakura\Controllers;
use WP_REST_Server;
use WP_REST_Request;
use WP_Error;
use Sakura\Models\OptionModel;
class OptionController extends BaseController
{
/**
* Constructor.
*
* @since 5.0.0
*/
public function __construct()
{
$this->namespace = 'sakura/v1';
$this->rest_base = 'config';
}
/**
* Registers the routes for comments.
*
* @since 5.0.0
*
* @see register_rest_route()
*/
public function register_routes()
{
register_rest_route(
$this->namespace,
'/' . $this->rest_base,
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'get_public_config'),
'permission_callback' => array($this, 'get_public_config_permissions_check'),
// 'args' => $this->get_collection_params(),
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array($this, 'update_config'),
'permission_callback' => array($this, 'update_config_permissions_check'),
// 'args' => $this->get_endpoint_args_for_item_schema(WP_REST_Server::CREATABLE),
),
// 'schema' => array($this, 'get_public_item_schema'),
)
);
}
public function get_public_config(WP_REST_Request $request)
{
return $this->get_public_display_options();
}
public function get_public_config_permissions_check(WP_REST_Request $request)
{
return true;
}
public function get_config(WP_REST_Request $request)
{
$config = (array) OptionModel::get($this->rest_base);
if (!$config) {
return new WP_Error(
'no_such_option',
__('Maybe you should save the configuration bufore using it.', self::$text_domain),
array('status' => 500)
);
} else {
return $config;
}
}
public function get_config_permissions_check(WP_REST_Request $request)
{
return true;
}
public function update_config(WP_REST_Request $request)
{
$db = (array) $this->get_config($request);
$cache = $db;
$json = (array) self::json_validate($request->get_body());
$hasNoDiff = true;
foreach ($json as $key => $value) {
if (array_key_exists($key, $cache)) {
$nv = json_encode($value);
$ov = json_encode($cache[$key]);
if ($hasNoDiff) $hasNoDiff = $nv === $ov;
} else {
if ($hasNoDiff) $hasNoDiff = false;
}
$db[$key] = $value;
}
if ($hasNoDiff) {
return [
'code' => 'save_config_succeed',
'message' => __('Configuration is already up to date.', self::$text_domain),
];
}
$config = OptionModel::update($this->rest_base, $db);
if (!$config) {
return new WP_Error(
'save_config_failure',
__('Unable to save the configuration.', self::$text_domain),
array('status' => 500)
);
} else {
return [
'code' => 'save_config_succeed',
'message' => __('Configuration saved successfully.', self::$text_domain),
];
}
}
public function update_config_permissions_check(WP_REST_Request $request)
{
return true;
}
// public function inite_theme()
// {
// $config = OptionModel::create($this->rest_base, (array)[]);
// }
public static function json_validate(string $string)
{
$json = json_decode($string);
return $json;
}
public function set_key_value(string $key, $value)
{
$json = (array) OptionModel::get($this->rest_base);
if (!$json) {
return new WP_Error(
'no_such_option',
__('Maybe you should save the configuration bufore using it.', self::$text_domain),
array('status' => 500)
);
}
$json[$key] = $value;
$config = OptionModel::update($this->rest_base, $json);
$config = $config ? $config : OptionModel::create($this->rest_base, $json);
return $config;
}
public function sakura_options(string $namespace, $default)
{
$config = (array) OptionModel::get($this->rest_base);
if (array_key_exists($namespace, $config)) {
return $config[$namespace];
} else {
$this->set_key_value($namespace, $default);
return $default;
}
// translators: %s: $namespace */
// throw new Exception(
// sprintf(__("No existing database saving value or default value for option '%s'.", self::$text_domain), $namespace)
// );
}
public static function get_option_json()
{
$options = file_get_contents(__DIR__ . "/../configs/options.json");
return json_decode($options, true);
}
public function get_public_display_options()
{
$output = [];
$defaults = (array) self::get_option_json();
// return $defaults;
foreach ($defaults as $key => $value) {
if ($value['public']) {
$output[$value['namespace']] = $this->sakura_options($value['namespace'], $value['default']);
}
}
return $output;
}
/**
* Use in admin page only
* @return array
*/
public function get_all_options()
{
$output = [];
$defaults = (array) self::get_option_json();
// return $defaults;
foreach ($defaults as $key => $value) {
$output[$value['namespace']] = $this->sakura_options($value['namespace'], $value['default']);
}
return $output;
}
}

View File

@ -1,9 +0,0 @@
<?php
namespace Sakura\Controllers;
use Sakura\Controllers\PostController;
class PageController extends PostController
{
}

View File

@ -1,118 +0,0 @@
<?php
namespace Sakura\Controllers;
use Sakura\Controllers\BaseController;
use Sakura\Lib\Exception;
use DOMDocument;
class PostController extends BaseController
{
/**
* Get post comment count
*
* @param integer $post_id
* @return number
*/
public static function get_comments_number(int $post_id)
{
return intval(get_comments_number($post_id));
}
public static function get_pagination_info($request)
{
$the_query = new \WP_Query($_GET);
$total_page = $the_query->post_count;
return $the_query;
}
/**
* Get original post expert
*
* @param integer $post_id
* @return string
*/
public static function get_post_excerpt(int $post_id)
{
$post = get_post($post_id);
if ($post) {
// throw new Exception('whoop');
return $post->post_excerpt;
} else {
throw new Exception("No such post \$id={$post_id}!");
}
}
/**
* Get post original Markdown content
* based on https://wordpress.org/plugins/wp-githuber-md/
*
* @param integer $post_id
* @return mixed string | null
*/
public static function get_post_markdown(int $post_id)
{
$post = get_post($post_id);
if ($post) {
// won't check if post_content_filtered is empty, will check it in js
// TODO: global check if markdown available.
if (property_exists($post, 'post_content_filtered')) {
return html_entity_decode($post->post_content_filtered, ENT_QUOTES);
} else {
return null;
}
}
}
/**
* Content filter
*
* @param object $post $post obj in register_rest_field
* @return object $post content modified
*/
public static function rest_api_post_content_filter(array $post)
{
$post['content']['markdown'] = self::get_post_markdown($post['id']);
return $post['content'];
}
public static function rest_api_post_excerpt_filter($post)
{
$post['excerpt']['plain'] = self::get_post_excerpt($post['id']);
return $post['excerpt'];
}
public static function get_post_views($post_id)
{
if (false) {
// if (!function_exists('wp_statistics_pages')) {
// throw new Exception(__('Please install pulgin WP-Statistics (https://wordpress.org/plugins/wp-statistics)', SAKURA_TEXT_DOMAIN));
// } else {
// return intval(wp_statistics_pages('total', 'uri', $post_id));
// }
} else {
$views = get_post_meta($post_id, 'views', true);
if (!$views) {
return 0;
} else {
return intval($views);
}
}
}
public static function get_post_word_count($post_id)
{
$post = get_post($post_id);
if ($post) {
// return $post->post_content;
$doc = new DOMDocument();
$internalErrors = libxml_use_internal_errors(true);
$doc->loadHTML('<?xml encoding="utf-8" ?>' . $post->post_content);
libxml_use_internal_errors($internalErrors);
$string = $doc->textContent;
$string = preg_replace('/\s+/', '', $string);
return strlen($string);
}
return NAN;
}
}

View File

@ -1,13 +0,0 @@
<?php
namespace Sakura\Controllers;
use Sakura\Controllers\TermController;
class TagController extends TermController
{
public static function get_the_tags(int $post_id)
{
return get_the_tags($post_id);
}
}

View File

@ -1,16 +0,0 @@
<?php
namespace Sakura\Controllers;
use Sakura\Controllers\BaseController;
/**
* Base controller for WP_Term
*/
class TermController extends BaseController
{
public static function get_terms(int $post_id)
{
return get_terms($post_id);
}
}

View File

@ -1,9 +0,0 @@
<?php
namespace Sakura\Controllers;
use Sakura\Controllers\BaseController;
class ThemeController extends BaseController
{
}

View File

@ -1,39 +0,0 @@
<?php
namespace Sakura\Controllers;
use Sakura\Controllers\BaseController;
use Sakura\Controllers\AvatarController;
class UserController extends BaseController
{
/**
* Warning: be carefull to use this, use UserController::get_user_public_meta
* instead if not necessary
*
* @param integer $user_id
* @param string $key
* @param boolean $single
*
* @return mixed array | false
*/
public static function get_user_meta(int $user_id, string $key = '', bool $single = false)
{
return get_user_meta($user_id, $key, $single);
}
public static function get_user_public_meta(int $user_id)
{
$white_list = ['nickname', 'first_name', 'last_name', 'description', 'wp_user_level'];
$meta = self::get_user_meta($user_id);
$public_meta = [];
foreach ($white_list as $key) {
$public_meta[$key] = $meta[$key];
}
$public_meta['avatar'] = AvatarController::get_avatar($user_id);
return $public_meta;
}
}

View File

@ -1,22 +0,0 @@
<?php
/**
* The template for displaying the footer
*
* Contains the closing of the #content div and all content after.
*
* @link https://developer.wordpress.org/themes/basics/template-files/#template-partials
*
* @package WordPress
* @subpackage Twenty_Twenty_One
* @since 1.0.0
*/
?>
</div><!-- #app -->
<?php wp_footer(); ?>
</body>
</html>

View File

@ -1,29 +0,0 @@
<?php
// namespace Sakura;
define('SAKURA_VERSION', wp_get_theme()->get('Version'));
define('SAKURA_TEXT_DOMAIN', wp_get_theme()->get('TextDomain'));
define('SAKURA_DEVEPLOMENT', false);
define('SAKURA_DEVEPLOMENT_HOST', 'http://127.0.0.1:9000');
// PHP loaders
require_once(__DIR__ . '/loader.php');
new Sakura\Helpers\SetupHelper();
new Sakura\Helpers\WhoopsHelper();
new Sakura\Helpers\ViteHelper();
new Sakura\Helpers\AdminPageHelper();
new Sakura\Helpers\CustomMenuMetaFieldsHelper();
new Sakura\Helpers\CommentHelper();
new Sakura\Helpers\PostQueryHelper('post');
new Sakura\Routers\ApiRouter();
new Sakura\Routers\PagesRouter();
function sakura_options(string $namespace, $default)
{
$CF = new Sakura\Controllers\OptionController();
return $CF->sakura_options($namespace, $default);
}

View File

@ -1,40 +0,0 @@
<?php
/**
* The header.
*
* This is the template that displays all of the <head> section and everything up until main.
*
* @link https://developer.wordpress.org/themes/basics/template-files/#template-partials
*
* @package WordPress
* @subpackage Twenty_Twenty_One
* @since 1.0.0
*/
// namespace Sakura;
// use Sakura\Utils;
// $template = new Helpers\TemplateHelper();
// $params = [
// 'language_attributes' => Utils\echo_interceptor('language_attributes'),
// 'bloginfo' => Utils\echo_interceptor('bloginfo', 'charset'),
// 'wp_head' => Utils\echo_interceptor('wp_head')
// ];
// echo $template->render('header.twig', $params);
?>
<!doctype html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<?php wp_head(); ?>
</head>
<body>
<div id="app" class="container">
<h1>Vite is loading</h1>
<p>we will render basic content with PHP here in terms of better SEO.</p>

View File

@ -1,104 +0,0 @@
<?php
namespace Sakura\Helpers;
use Sakura\Helpers\ViteHelper;
use Sakura\Controllers\InitStateController;
use Sakura\Controllers\OptionController;
class AdminPageHelper extends ViteHelper
{
public $page_title;
public $menu_title;
public $menu_slug;
public function __construct()
{
$this->page_title = __('Sakura theme options', self::$text_domain);
$this->menu_title = __('Sakura Options', self::$text_domain);
$this->menu_slug = __('sakura_options', self::$text_domain);
add_action('admin_menu', [$this, 'add_theme_page']);
add_action('admin_enqueue_scripts', [$this, 'enqueue_scripts']);
}
public function add_theme_page()
{
add_theme_page($this->page_title, $this->menu_title, 'edit_theme_options', $this->menu_slug, function () {
$template = new TemplateHelper();
echo $template->load('admin-page-helper.twig')->renderBlock('admin_app', []);
});
}
public function enqueue_scripts($hook)
{
if ("appearance_page_{$this->menu_slug}" === $hook) {
$this->enqueue_common_scripts();
if (SAKURA_DEVEPLOMENT) {
$this->enqueue_development_scripts();
} else {
$this->enqueue_production_scripts();
}
}
}
public function enqueue_development_scripts()
{
wp_enqueue_script('[type:module]vite-client', self::$development_host . '/@vite/client', array(), null, false);
wp_enqueue_script('[type:module]dev-main', self::$development_host . '/src/admin/main.ts', array(), null, true);
wp_localize_script('[type:module]dev-main', 'AdminColors', $this->get_admin_color_css());
wp_localize_script('[type:module]dev-main', 'InitState', (new InitStateController())->get_initial_state());
wp_localize_script('[type:module]dev-main', 'SakuraOptions', ['data' => (new OptionController())->get_all_options()]);
}
public function enqueue_production_scripts()
{
$entry_key = 'src/admin/main.ts';
$assets_base_path = get_template_directory_uri() . '/assets/admin/';
$manifest = self::get_manifest_file('admin');
// <script type="module" crossorigin src="http://localhost:9000/assets/index.36b06f45.js"></script>
wp_enqueue_script('[type:module]chunk-entrance.js', $assets_base_path . $manifest[$entry_key]['file'], array(), null, false);
wp_localize_script('[type:module]chunk-entrance.js', 'AdminColors', $this->get_admin_color_css());
wp_localize_script('[type:module]chunk-entrance.js', 'InitState', (new InitStateController())->get_initial_state());
wp_localize_script('[type:module]chunk-entrance.js', 'SakuraOptions', ['data' => (new OptionController())->get_all_options()]);
// <link rel="modulepreload" href="http://localhost:9000/assets/vendor.b3a324ba.js">
foreach ($manifest[$entry_key]['imports'] as $index => $import) {
wp_enqueue_style("[ref:modulepreload]chunk-vendors{$import}", $assets_base_path . $manifest[$import]['file']);
if (empty($manifest[$import]['css'])) {
continue;
}
foreach ($manifest[$import]['css'] as $css_index => $css_path) {
wp_enqueue_style("sakura-chunk-{$import}-{$css_index}.css", $assets_base_path . $css_path);
}
}
// <link rel="stylesheet" href="http://localhost:9000/assets/index.2c78c25a.css">
foreach ($manifest[$entry_key]['css'] as $index => $path) {
wp_enqueue_style("sakura-chunk-{$index}.css", $assets_base_path . $path);
}
}
public function enqueue_common_scripts()
{
wp_enqueue_media();
wp_enqueue_style('style.css', get_template_directory_uri() . '/style.css');
wp_enqueue_style('fontawesome-free', 'https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.3/css/all.min.css');
}
public function get_admin_color_css()
{
global $_wp_admin_css_colors;
$theme = (array) $_wp_admin_css_colors[get_user_option('admin_color')];
return $theme;
}
}

View File

@ -1,86 +0,0 @@
<?php
namespace Sakura\Helpers;
use Sakura\Controllers\CommentController;
use Sakura\Models\CommentModel;
use WP_Comment;
use WP_Theme;
class CommentHelper
{
public function __construct()
{
add_action('comment_post', [$this, 'create_comment_actions'], 1, 3);
add_action('edit_comment', [$this, 'update_comment_actions'], 1, 2);
add_action('delete_comment', [$this, 'delete_comment_actions'], 1, 2);
// TODO: not sure if it's ok to handle a lot of comments? If not ok, provide only the manual method
// add_action('after_switch_theme', [$this, 'after_switch_theme_actions'], 1, 2);
// deprecated, extends class-wp-rest-comments-controller instaed.
// add_filter('rest_allow_anonymous_comments', '__return_true');
}
/**
* Actions after create comment
*
* @param integer $comment_ID
* @param int|string $comment_approved
* @param array $commentdata
*
* @return void
*/
public static function create_comment_actions(int $comment_ID, $comment_approved, array $commentdata)
{
CommentModel::set_comment_ancestor_meta($comment_ID);
}
public static function update_comment_actions(int $comment_ID, array $data)
{
CommentModel::update_comment_ancestor_meta($comment_ID);
}
public static function delete_comment_actions(int $comment_ID, WP_Comment $comment)
{
// upgrade children/descendant comments' ancestor meta (ancestor means the parent/ancestor whose parent_id === 0)
if (CommentModel::get_comment_ancestor_meta($comment_ID) == 0) {
$descendant_comments = get_comments([
'meta_key' => CommentModel::$ancestor_id_meta_key,
'meta_value' => $comment_ID,
]);
$child_comments = get_comments([
'parent' => $comment_ID,
]);
// set children's parent to 0
foreach ($child_comments as $child_comment) {
// https://developer.wordpress.org/reference/functions/wp_insert_comment/
wp_update_comment([
'comment_ID' => $child_comment->comment_ID,
'comment_parent' => 0,
], false);
}
// update ancestor for descendant
foreach ($descendant_comments as $descendant_comment) {
CommentModel::update_comment_ancestor_meta($descendant_comment->comment_ID);
}
}
}
public static function after_switch_theme_actions(string $old_name, WP_Theme $old_theme)
{
CommentController::init_comment_ancestor_meta();
}
public static function filter_rest_allow_anonymous_comments($false, $request)
{
// return [
// 'code' => 123
// ];
throw new \Exception("opps");
// return true;
}
}

View File

@ -1,83 +0,0 @@
<?php
namespace Sakura\Helpers;
use Exception;
/**
* Add custom fields to menu item
* https://www.kathyisawesome.com/add-custom-fields-to-wordpress-menu-items/
*/
class CustomMenuMetaFieldsHelper
{
public static function fileds()
{
return [
'icon' => [
'label' => __('Icon', SAKURA_TEXT_DOMAIN),
'desc' => __('Menu item icon', SAKURA_TEXT_DOMAIN),
],
'class' => [
'label' => __('Class', SAKURA_TEXT_DOMAIN),
'desc' => __('Menu item class class <code>Array&lt;string&gt;</code>', SAKURA_TEXT_DOMAIN),
],
];
}
public static function is_key_in_fields(string $key)
{
return array_key_exists($key, self::fileds());
}
public function __construct()
{
add_action('wp_nav_menu_item_custom_fields', [$this, 'add_fileds'], 10, 2);
add_action('wp_update_nav_menu_item', [$this, 'update_fileds'], 10, 2);
}
public function add_fileds($item_id, $item)
{
$template = new TemplateHelper();
echo $template->load('custom-menu-meta-fields-helper.twig')->renderBlock('id_input', ['item_id' => $item_id,]);
foreach ($this->fileds() as $key => $value) {
wp_nonce_field("custom_menu_meta_{$key}_nonce", "_custom_menu_meta_{$key}_nonce_name");
$custom_menu_meta = get_post_meta($item_id, "_custom_menu_meta_{$key}", true);
$label = $value['label'];
$desc = $value['desc'];
$esc_attr_custom_menu_meta = esc_attr($custom_menu_meta);
echo $template->load('custom-menu-meta-fields-helper.twig')->renderBlock('input_field', [
'item_id' => $item_id,
'key' => $key,
'label' => $label,
'esc_attr_custom_menu_meta' => $esc_attr_custom_menu_meta,
'desc' => $desc
]);
}
}
/**
* admain-ajax filter
*/
public function update_fileds($menu_id, $menu_item_db_id)
{
// throw new Exception("Debug $menu_id");
foreach ($this->fileds() as $key => $value) {
if (!isset($_POST["_custom_menu_meta_{$key}_nonce_name"])) {
// when first add a menu item, this can be empty (undefined)
return $menu_id;
} else if (!wp_verify_nonce($_POST["_custom_menu_meta_{$key}_nonce_name"], "custom_menu_meta_{$key}_nonce")) {
throw new Exception("Invalid `_custom_menu_meta_{$key}_nonce_name` in {$_POST}, \$menu_id={$menu_id}");
return $menu_id;
}
if (isset($_POST["custom_menu_meta_{$key}"][$menu_item_db_id])) {
$sanitized_data = sanitize_text_field($_POST["custom_menu_meta_{$key}"][$menu_item_db_id]);
update_post_meta($menu_item_db_id, "_custom_menu_meta_{$key}", $sanitized_data);
} else {
delete_post_meta($menu_item_db_id, "_custom_menu_meta_{$key}");
}
}
}
}

View File

@ -1,51 +0,0 @@
<?php
namespace Sakura\Helpers;
class PostQueryHelper
{
/**
* Post type.
*
* @since 5.0.0
* @var string
*/
protected $post_type;
public function __construct(string $post_type)
{
$this->post_type = $post_type;
add_filter("rest_{$post_type}_query", [$this, 'filter_rest_post_query'], 10, 2);
}
/**
* Filter rest posts by category slug
*
* @param array $args
* @param WP_Rest_Rquest $request
* @return array $args
*/
public function filter_rest_post_query($args, $request)
{
$args['tax_query'] = [];
$taxonomies = wp_list_filter(get_object_taxonomies($this->post_type, 'objects'), array('show_in_rest' => true));
foreach ($taxonomies as $taxonomy) {
$base = !empty($taxonomy->rest_base) ? $taxonomy->rest_base : $taxonomy->name;
if (!isset($request["{$base}_slug"])) {
continue;
}
array_push($args['tax_query'], [
'taxonomy' => $taxonomy->name,
'field' => 'slug',
'terms' => explode(',', $request["{$base}_slug"]),
'include_children' => true,
'operator' => 'IN',
]);
}
return $args;
}
}

View File

@ -1,58 +0,0 @@
<?php
namespace Sakura\Helpers;
/**
* @deprecated use apply_filters( "rest_{$this->post_type}_query", array $args, WP_REST_Request $request ) or filter instead
*/
class RestApiFilterHelper
{
public function __construct()
{
add_action('rest_api_init', [$this, 'rest_api_filter_add_filters']);
}
/**
* Add the necessary filter to each post type
**/
public function rest_api_filter_add_filters()
{
foreach (get_post_types(array('show_in_rest' => true), 'objects') as $post_type) {
add_filter('rest_' . $post_type->name . '_query', [$this, 'rest_api_filter_add_filter_param'], 10, 2);
}
}
/**
* Add the filter parameter
*
* @param array $args The query arguments.
* @param WP_REST_Request $request Full details about the request.
* @return array $args.
**/
public function rest_api_filter_add_filter_param($args, $request)
{
// Bail out if no filter parameter is set.
if (empty($request['filter']) || !is_array($request['filter'])) {
return $args;
}
$filter = $request['filter'];
if (isset($filter['posts_per_page']) && ((int) $filter['posts_per_page'] >= 1 && (int) $filter['posts_per_page'] <= 100)) {
$args['posts_per_page'] = $filter['posts_per_page'];
}
global $wp;
$vars = apply_filters('rest_query_vars', $wp->public_query_vars);
// Allow valid meta query vars.
$vars = array_unique(array_merge($vars, array('meta_query', 'meta_key', 'meta_value', 'meta_compare')));
foreach ($vars as $var) {
if (isset($filter[$var])) {
$args[$var] = $filter[$var];
}
}
return $args;
}
}

View File

@ -1,93 +0,0 @@
<?php
namespace Sakura\Helpers;
// use Sakura\Controllers\OptionController;
class SetupHelper
{
public function __construct()
{
add_action('after_setup_theme', [$this, 'setup']);
// Disable WP Admin Bar
add_filter('show_admin_bar', '__return_false');
// TODO: enable this if http?
// add_filter('wp_is_application_passwords_available', '__return_true');
// Allow rest API CORS.
add_action('rest_api_init', [$this, 'wp_rest_allow_all_cors'], 15);
// post excerpt more context
add_filter('excerpt_more', [$this, 'changes_post_excerpt_more']);
// post excerpt length
add_filter('excerpt_length', [$this, 'changes_post_excerpt_length'], 10);
// count post views
add_action('get_header', [$this, 'set_post_views']);
// Inite config options
// won't need anymore with options?
// add_action('after_switch_theme', [new OptionController(), 'inite_theme'], 1, 2);
}
public function setup()
{
add_theme_support('title-tag');
add_theme_support('post-thumbnails');
add_theme_support('custom-logo', array(
'height' => 160,
'width' => 160,
));
register_nav_menus($this->menu_locations());
}
public static function menu_locations()
{
return [
'header_menu' => esc_html('Header Menu (show on page header)', SAKURA_TEXT_DOMAIN),
];
}
public static function wp_rest_allow_all_cors()
{
// Remove the default filter.
remove_filter('rest_pre_serve_request', 'rest_send_cors_headers');
// Add a Custom filter.
add_filter('rest_pre_serve_request', function ($value) {
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE');
header('Access-Control-Allow-Credentials: true');
return $value;
});
}
public function changes_post_excerpt_more(string $more)
{
return ' ...';
}
public function changes_post_excerpt_length(int $length)
{
return 120;
}
/**
* Post view times counter
*
* @return void
*/
public function set_post_views()
{
if (is_singular()) {
global $post;
$post_id = intval($post->ID);
if ($post_id) {
$views = (int) get_post_meta($post_id, 'views', true);
if (!update_post_meta($post_id, 'views', ($views + 1))) {
add_post_meta($post_id, 'views', 1, true);
}
}
}
}
}

View File

@ -1,58 +0,0 @@
<?php
namespace Sakura\Helpers;
use Twig\Loader\FilesystemLoader;
use Twig\Environment;
use Twig\TwigFunction;
/**
* Twig engine template loader markup
* @since 0.0.1
* @license MIT
* @author mashirozx <moezhx@outlook.com>
*/
class TemplateHelper
{
public $loader;
public $twig;
public $template;
public function __construct()
{
$this->loader = new FilesystemLoader(array_map(function ($path) {
return __DIR__ . '/' . $path;
}, $this->loader_path_array()));
$this->twig = new Environment($this->loader);
}
public static function loader_path_array()
{
return [
'../views/helpers',
'../views'
];
}
public function load($template_name)
{
$this->template = $this->twig->load($template_name);
return $this->template;
}
public function render(...$params)
{
return $this->twig->render(...$params);
}
public function addFunction($function_names)
{
if (is_array($function_names)) {
foreach ($function_names as $function_name) {
$this->twig->addFunction(new TwigFunction($function_name, $function_name));
}
} elseif (is_string($function_names)) {
$this->twig->addFunction(new TwigFunction($function_names, $function_names));
}
}
}

View File

@ -1,98 +0,0 @@
<?php
namespace Sakura\Helpers;
use Sakura\Lib\BaseClass;
use Sakura\Controllers\InitStateController;
class ViteHelper extends BaseClass
{
public static $development_host = SAKURA_DEVEPLOMENT_HOST;
function __construct()
{
add_action('wp_enqueue_scripts', [$this, 'enqueue_common_scripts']);
if (SAKURA_DEVEPLOMENT) {
add_action('wp_enqueue_scripts', [$this, 'enqueue_development_scripts']);
} else {
add_action('wp_enqueue_scripts', [$this, 'enqueue_production_scripts']);
}
// add tag filters
add_filter('script_loader_tag', [$this, 'script_tag_filter'], 10, 3);
add_filter('style_loader_tag', [$this, 'style_tag_filter'], 10, 3);
}
public function enqueue_development_scripts()
{
wp_enqueue_script('[type:module]vite-client', self::$development_host . '/@vite/client', array(), null, false);
wp_enqueue_script('[type:module]dev-main', self::$development_host . '/src/main.ts', array(), null, true);
wp_localize_script('[type:module]dev-main', 'InitState', (new InitStateController())->get_initial_state());
}
public function enqueue_production_scripts()
{
$entry_key = 'src/main.ts';
$assets_base_path = get_template_directory_uri() . '/assets/main/';
$manifest = $this->get_manifest_file('main');
// <script type="module" crossorigin src="http://localhost:9000/assets/index.36b06f45.js"></script>
wp_enqueue_script('[type:module]chunk-entrance.js', $assets_base_path . $manifest[$entry_key]['file'], array(), null, false);
wp_localize_script('[type:module]chunk-entrance.js', 'InitState', (new InitStateController())->get_initial_state());
// <link rel="modulepreload" href="http://localhost:9000/assets/vendor.b3a324ba.js">
foreach ($manifest[$entry_key]['imports'] as $index => $import) {
wp_enqueue_style("[ref:modulepreload]chunk-vendors{$import}", $assets_base_path . $manifest[$import]['file']);
if (empty($manifest[$import]['css'])) {
continue;
}
foreach ($manifest[$import]['css'] as $css_index => $css_path) {
wp_enqueue_style("sakura-chunk-{$import}-{$css_index}.css", $assets_base_path . $css_path);
}
}
// <link rel="stylesheet" href="http://localhost:9000/assets/index.2c78c25a.css">
foreach ($manifest[$entry_key]['css'] as $index => $path) {
wp_enqueue_style("sakura-chunk-{$index}.css", $assets_base_path . $path);
}
}
public function enqueue_common_scripts()
{
wp_enqueue_style('style.css', get_template_directory_uri() . '/style.css');
wp_enqueue_style('fontawesome-free.css', 'https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.3/css/all.min.css');
wp_enqueue_style('normalize.css', 'https://cdn.jsdelivr.net/npm/normalize.css/normalize.css');
wp_enqueue_script('recaptcha', 'https://www.recaptcha.net/recaptcha/api.js', array(), false, true);
}
public static function script_tag_filter($tag, $handle, $src)
{
if (preg_match('/^\[([^:]*)\:([^\]]*)\]/', $handle)) {
preg_match('/^\[([^:]*)\:([^\]]*)\]/', $handle, $matches, PREG_OFFSET_CAPTURE);
$template = new TemplateHelper();
$tag = $template->load('vite-require-helper.twig')->renderBlock('script', ['key' => $matches[1][0], 'value' => $matches[2][0], 'src' => esc_url($src)]);
}
return $tag;
}
public static function style_tag_filter($tag, $handle, $src)
{
if (preg_match('/^\[([^:]*)\:([^\]]*)\]/', $handle)) {
preg_match('/^\[([^:]*)\:([^\]]*)\]/', $handle, $matches, PREG_OFFSET_CAPTURE);
$template = new TemplateHelper();
$tag = $template->load('vite-require-helper.twig')->renderBlock('style', ['key' => $matches[1][0], 'value' => $matches[2][0], 'href' => esc_url($src)]);
}
return $tag;
}
public static function get_manifest_file(string $namespace)
{
$manifest = file_get_contents(__DIR__ . "/../assets/{$namespace}/manifest.json");
return json_decode($manifest, true);
}
}

View File

@ -1,36 +0,0 @@
<?php
namespace Sakura\Helpers;
use Whoops\Handler\PrettyPageHandler;
use Whoops\Handler\JsonResponseHandler;
use Whoops\Handler\PlainTextHandler;
use Whoops\Util\Misc;
use Whoops\Run;
class WhoopsHelper
{
public function __construct()
{
if (self::is_debug()) {
$whoops = new Run();
// TODO: not working??
if (wp_is_json_request()) {
$whoops->pushHandler(new JsonResponseHandler);
} elseif (Misc::isCommandLine()) {
$whoops->pushHandler(new PlainTextHandler);
} else {
$whoops->pushHandler(new PrettyPageHandler);
}
$whoops->register();
}
}
/**
* @return bool
*/
public static function is_debug()
{
return defined('WP_DEBUG') && WP_DEBUG;
}
}

View File

@ -1,30 +0,0 @@
<?php
/**
* The main template file
*
* This is the most generic template file in a WordPress theme
* and one of the two required files for a theme (the other being style.css).
* It is used to display a page when nothing more specific matches a query.
* E.g., it puts together the home page when no home.php file exists.
*
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/
*
* @package WordPress
* @subpackage Twenty_Twenty_One
* @since 1.0.0
*/
get_header();
if (have_posts()) {
// Load posts loop.
while (have_posts()) {
the_post();
}
// Previous/next page navigation.
}
get_footer();

View File

@ -1,9 +0,0 @@
<?php
namespace Sakura\Lib;
class BaseClass
{
public static $version = SAKURA_VERSION;
public static $text_domain = SAKURA_TEXT_DOMAIN;
}

View File

@ -1,141 +0,0 @@
<?php
namespace Sakura\Lib;
use WP_REST_Comments_Controller;
use WP_Error;
use WP_REST_Request;
class ClassWpRestCommentsController extends WP_REST_Comments_Controller
{
/**
* Checks if a given request has access to create a comment.
*
* wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php
*
* Source: https://git.io/JcSan
* Modify based on commit 278843f
*
* @since 4.7.0
*
* @param WP_REST_Request $request Full details about the request.
* @return true|WP_Error True if the request has access to create items, error object otherwise.
*/
public function create_item_permissions_check($request)
{
if (!is_user_logged_in()) {
if (get_option('comment_registration')) {
return new WP_Error(
'rest_comment_login_required',
__('Sorry, you must be logged in to comment.'),
array('status' => 401)
);
}
/**
* Filters whether comments can be created via the REST API without authentication.
*
* Enables creating comments for anonymous users.
*
* @since 4.7.0
*
* @param bool $allow_anonymous Whether to allow anonymous comments to
* be created. Default `false`.
* @param WP_REST_Request $request Request used to generate the
* response.
*/
// $allow_anonymous = apply_filters( 'rest_allow_anonymous_comments', false, $request );
$allow_anonymous = true;
if (!$allow_anonymous) {
return new WP_Error(
'rest_comment_login_required',
__('Sorry, you must be logged in to comment.'),
array('status' => 401)
);
}
}
// Limit who can set comment `author`, `author_ip` or `status` to anything other than the default.
if (isset($request['author']) && get_current_user_id() !== $request['author'] && !current_user_can('moderate_comments')) {
return new WP_Error(
'rest_comment_invalid_author',
/* translators: %s: Request parameter. */
sprintf(__("Sorry, you are not allowed to edit '%s' for comments."), 'author'),
array('status' => rest_authorization_required_code())
);
}
if (isset($request['author_ip']) && !current_user_can('moderate_comments')) {
if (empty($_SERVER['REMOTE_ADDR']) || $request['author_ip'] !== $_SERVER['REMOTE_ADDR']) {
return new WP_Error(
'rest_comment_invalid_author_ip',
/* translators: %s: Request parameter. */
sprintf(__("Sorry, you are not allowed to edit '%s' for comments."), 'author_ip'),
array('status' => rest_authorization_required_code())
);
}
}
if (isset($request['status']) && !current_user_can('moderate_comments')) {
return new WP_Error(
'rest_comment_invalid_status',
/* translators: %s: Request parameter. */
sprintf(__("Sorry, you are not allowed to edit '%s' for comments."), 'status'),
array('status' => rest_authorization_required_code())
);
}
if (empty($request['post'])) {
return new WP_Error(
'rest_comment_invalid_post_id',
__('Sorry, you are not allowed to create this comment without a post.'),
array('status' => 403)
);
}
$post = get_post((int) $request['post']);
if (!$post) {
return new WP_Error(
'rest_comment_invalid_post_id',
__('Sorry, you are not allowed to create this comment without a post.'),
array('status' => 403)
);
}
if ('draft' === $post->post_status) {
return new WP_Error(
'rest_comment_draft_post',
__('Sorry, you are not allowed to create a comment on this post.'),
array('status' => 403)
);
}
if ('trash' === $post->post_status) {
return new WP_Error(
'rest_comment_trash_post',
__('Sorry, you are not allowed to create a comment on this post.'),
array('status' => 403)
);
}
if (!$this->check_read_post_permission($post, $request)) {
return new WP_Error(
'rest_cannot_read_post',
__('Sorry, you are not allowed to read the post for this comment.'),
array('status' => rest_authorization_required_code())
);
}
if (!comments_open($post->ID)) {
return new WP_Error(
'rest_comment_closed',
__('Sorry, comments are closed for this item.'),
array('status' => 403)
);
}
return true;
}
}

View File

@ -1,357 +0,0 @@
<?php
namespace Sakura\Lib;
use WP_REST_Posts_Controller;
use WP_REST_Request;
use WP_Error;
use WP_Query;
/**
* @deprecated use apply_filters( "rest_{$this->post_type}_query", array $args, WP_REST_Request $request ) instead
*/
class ClassWpRestPostsController extends WP_REST_Posts_Controller
{
/**
* Retrieves a collection of posts.
* Source: https://github.com/WordPress/wordpress-develop/blob/master/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php
* Memo: only change $registered = $this->get_collection_params_mod();
* Based on commit 5383af8
*
* @since 4.7.0
*
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function get_items($request)
{
// Ensure a search string is set in case the orderby is set to 'relevance'.
if (!empty($request['orderby']) && 'relevance' === $request['orderby'] && empty($request['search'])) {
return new WP_Error(
'rest_no_search_term_defined',
__('You need to define a search term to order by relevance.'),
array('status' => 400)
);
}
// Ensure an include parameter is set in case the orderby is set to 'include'.
if (!empty($request['orderby']) && 'include' === $request['orderby'] && empty($request['include'])) {
return new WP_Error(
'rest_orderby_include_missing_include',
__('You need to define an include parameter to order by include.'),
array('status' => 400)
);
}
// Retrieve the list of registered collection query parameters.
$registered = $this->get_collection_params_mod();
$args = array();
/*
* This array defines mappings between public API query parameters whose
* values are accepted as-passed, and their internal WP_Query parameter
* name equivalents (some are the same). Only values which are also
* present in $registered will be set.
*/
$parameter_mappings = array(
'author' => 'author__in',
'author_exclude' => 'author__not_in',
'exclude' => 'post__not_in',
'include' => 'post__in',
'menu_order' => 'menu_order',
'offset' => 'offset',
'order' => 'order',
'orderby' => 'orderby',
'page' => 'paged',
'parent' => 'post_parent__in',
'parent_exclude' => 'post_parent__not_in',
'search' => 's',
'slug' => 'post_name__in',
'status' => 'post_status',
);
/*
* For each known parameter which is both registered and present in the request,
* set the parameter's value on the query $args.
*/
foreach ($parameter_mappings as $api_param => $wp_param) {
if (isset($registered[$api_param], $request[$api_param])) {
$args[$wp_param] = $request[$api_param];
}
}
// Check for & assign any parameters which require special handling or setting.
$args['date_query'] = array();
if (isset($registered['before'], $request['before'])) {
$args['date_query'][] = array(
'before' => $request['before'],
'column' => 'post_date',
);
}
if (isset($registered['modified_before'], $request['modified_before'])) {
$args['date_query'][] = array(
'before' => $request['modified_before'],
'column' => 'post_modified',
);
}
if (isset($registered['after'], $request['after'])) {
$args['date_query'][] = array(
'after' => $request['after'],
'column' => 'post_date',
);
}
if (isset($registered['modified_after'], $request['modified_after'])) {
$args['date_query'][] = array(
'after' => $request['modified_after'],
'column' => 'post_modified',
);
}
// Ensure our per_page parameter overrides any provided posts_per_page filter.
if (isset($registered['per_page'])) {
$args['posts_per_page'] = $request['per_page'];
}
if (isset($registered['sticky'], $request['sticky'])) {
$sticky_posts = get_option('sticky_posts', array());
if (!is_array($sticky_posts)) {
$sticky_posts = array();
}
if ($request['sticky']) {
/*
* As post__in will be used to only get sticky posts,
* we have to support the case where post__in was already
* specified.
*/
$args['post__in'] = $args['post__in'] ? array_intersect($sticky_posts, $args['post__in']) : $sticky_posts;
/*
* If we intersected, but there are no post IDs in common,
* WP_Query won't return "no posts" for post__in = array()
* so we have to fake it a bit.
*/
if (!$args['post__in']) {
$args['post__in'] = array(0);
}
} elseif ($sticky_posts) {
/*
* As post___not_in will be used to only get posts that
* are not sticky, we have to support the case where post__not_in
* was already specified.
*/
$args['post__not_in'] = array_merge($args['post__not_in'], $sticky_posts);
}
}
$args = $this->prepare_tax_query($args, $request);
// Force the post_type argument, since it's not a user input variable.
$args['post_type'] = $this->post_type;
/**
* Filters WP_Query arguments when querying posts via the REST API.
*
* The dynamic portion of the hook name, `$this->post_type`, refers to the post type slug.
*
* Possible hook names include:
*
* - `rest_post_query`
* - `rest_page_query`
* - `rest_attachment_query`
*
* Enables adding extra arguments or setting defaults for a post collection request.
*
* @since 4.7.0
* @since 5.7.0 Moved after the `tax_query` query arg is generated.
*
* @link https://developer.wordpress.org/reference/classes/wp_query/
*
* @param array $args Array of arguments for WP_Query.
* @param WP_REST_Request $request The REST API request.
*/
$args = apply_filters("rest_{$this->post_type}_query", $args, $request);
$query_args = $this->prepare_items_query($args, $request);
$posts_query = new WP_Query();
$query_result = $posts_query->query($query_args);
// Allow access to all password protected posts if the context is edit.
if ('edit' === $request['context']) {
add_filter('post_password_required', array($this, 'check_password_required'), 10, 2);
}
$posts = array();
foreach ($query_result as $post) {
if (!$this->check_read_permission($post)) {
continue;
}
$data = $this->prepare_item_for_response($post, $request);
$posts[] = $this->prepare_response_for_collection($data);
}
// Reset filter.
if ('edit' === $request['context']) {
remove_filter('post_password_required', array($this, 'check_password_required'));
}
$page = (int) $query_args['paged'];
$total_posts = $posts_query->found_posts;
if ($total_posts < 1) {
// Out-of-bounds, run the query again without LIMIT for total count.
unset($query_args['paged']);
$count_query = new WP_Query();
$count_query->query($query_args);
$total_posts = $count_query->found_posts;
}
$max_pages = ceil($total_posts / (int) $posts_query->query_vars['posts_per_page']);
if ($page > $max_pages && $total_posts > 0) {
return new WP_Error(
'rest_post_invalid_page_number',
__('The page number requested is larger than the number of pages available.'),
array('status' => 400)
);
}
$response = rest_ensure_response($posts);
$response->header('X-WP-Total', (int) $total_posts);
$response->header('X-WP-TotalPages', (int) $max_pages);
$request_params = $request->get_query_params();
$base = add_query_arg(urlencode_deep($request_params), rest_url(sprintf('%s/%s', $this->namespace, $this->rest_base)));
if ($page > 1) {
$prev_page = $page - 1;
if ($prev_page > $max_pages) {
$prev_page = $max_pages;
}
$prev_link = add_query_arg('page', $prev_page, $base);
$response->link_header('prev', $prev_link);
}
if ($max_pages > $page) {
$next_page = $page + 1;
$next_link = add_query_arg('page', $next_page, $base);
$response->link_header('next', $next_link);
}
return $response;
}
/**
* Prepares the 'tax_query' for a collection of posts.
*
* @since 5.7.0
*
* @param array $args WP_Query arguments.
* @param WP_REST_Request $request Full details about the request.
* @return array Updated query arguments.
*/
private function prepare_tax_query(array $args, WP_REST_Request $request)
{
$relation = $request['tax_relation'];
if ($relation) {
$args['tax_query'] = array('relation' => $relation);
}
$taxonomies = wp_list_filter(
get_object_taxonomies($this->post_type, 'objects'),
array('show_in_rest' => true)
);
foreach ($taxonomies as $taxonomy) {
$base = !empty($taxonomy->rest_base) ? $taxonomy->rest_base : $taxonomy->name;
$tax_include = $request[$base];
$tax_exclude = $request[$base . '_exclude'];
if ($tax_include) {
$terms = array();
$include_children = false;
$operator = 'IN';
if (rest_is_array($tax_include)) {
$terms = $tax_include;
} elseif (rest_is_object($tax_include)) {
$terms = empty($tax_include['terms']) ? array() : $tax_include['terms'];
$include_children = !empty($tax_include['include_children']);
if (isset($tax_include['operator']) && 'AND' === $tax_include['operator']) {
$operator = 'AND';
}
}
if ($terms) {
$args['tax_query'][] = array(
'taxonomy' => $taxonomy->name,
'field' => 'slug', // 'term_id',
'terms' => $terms,
'include_children' => $include_children,
'operator' => $operator,
);
}
}
if ($tax_exclude) {
$terms = array();
$include_children = false;
if (rest_is_array($tax_exclude)) {
$terms = $tax_exclude;
} elseif (rest_is_object($tax_exclude)) {
$terms = empty($tax_exclude['terms']) ? array() : $tax_exclude['terms'];
$include_children = !empty($tax_exclude['include_children']);
}
if ($terms) {
$args['tax_query'][] = array(
'taxonomy' => $taxonomy->name,
'field' => 'term_id',
'terms' => $terms,
'include_children' => $include_children,
'operator' => 'NOT IN',
);
}
}
}
return $args;
}
public function get_public_item_schema_mod()
{
$schema = $this->get_public_item_schema();
$schema['properties']['categories']['items']['type'] = [
"string",
"integer"
];
return $schema;
}
public function get_collection_params_mod()
{
$params = $this->get_collection_params();
$new = [
"title" => "Term Slug",
"description" => "Match terms with the listed Slug.",
"type" => "array",
"items" => [
"type" => "string"
]
];
$params['categories']['oneOf'][0] = $new;
return $params;
}
}

View File

@ -1,15 +0,0 @@
<?php
// memo: https://learnku.com/articles/5657/laravel-exceptions-exception-and-error-handling
namespace Sakura\Lib;
use Exception as BaseException;
class Exception extends BaseException
{
public function __construct($message, $code = 0)
{
parent::__construct($message, $code);
}
}

View File

@ -1,35 +0,0 @@
<?php
/*------------------------------------*\
Auto loaders
\*------------------------------------*/
// Composer autoload
require_once(__DIR__ . '/vendor/autoload.php');
// Autoload namespace Sakura
spl_autoload_register(function ($class_name) {
$namespaces = explode('\\', $class_name);
$namespaces_length = count($namespaces);
if ($namespaces[0] !== 'Sakura') {
// new Exception("No such class '{$class_name}'");
return;
}
$path = __DIR__;
$index = 1;
foreach ($namespaces as $namespace) {
if ($index === 1) {
$path .= '';
} elseif ($index < $namespaces_length) {
$path .= '/' . strtolower($namespace);
} else {
$path .= '/' . strtolower(preg_replace('%([a-z])([A-Z])%', '\1-\2', $namespace));
}
$index++;
}
// TODO: check if file exists before require
require_once $path . '.php';
});

View File

@ -1,9 +0,0 @@
<?php
namespace Sakura\Models;
use Sakura\Lib\BaseClass;
class BaseModel extends BaseClass
{
}

View File

@ -1,231 +0,0 @@
<?php
namespace Sakura\Models;
use Sakura\Lib\Exception;
use Sakura\Models\BaseModel;
use Sakura\Utils\UaParser;
use Sakura\Utils\IpParser;
use Sakura\Controllers\AvatarController;
class CommentModel extends BaseModel
{
public static $ancestor_id_meta_key = 'ancestor_id';
public static $user_agent_info_meta_key = 'user_agent_info';
public static $user_location_meta_key = 'user_location';
public static function get_comments(array $comment_IDs)
{
return get_comments(['comment__in' => $comment_IDs]);
}
public static function get_comment(int $comment_ID)
{
return get_comment($comment_ID);
}
public static function get_comment_childern(int $comment_ID)
{
return get_comments(['parent' => $comment_ID]);
}
public static function get_comments_of_post(int $post_id)
{
return get_comments(['post_id' => $post_id]);
}
public static function get_comment_parent_id(int $comment_ID)
{
$comment = self::get_comments([$comment_ID]);
return intval($comment[0]->comment_parent);
}
public static function get_comment_ancestor_id(int $comment_ID, $target = true)
{
$parent_id = self::get_comment_parent_id($comment_ID);
if ($parent_id > 0) {
return self::get_comment_ancestor_id($parent_id, false);
} else {
return $target ? $parent_id : $comment_ID;
}
}
public static function set_comment_ancestor_meta(int $comment_ID)
{
$ancestor_id = self::get_comment_ancestor_id($comment_ID);
add_comment_meta($comment_ID, self::$ancestor_id_meta_key, $ancestor_id, true);
return $ancestor_id;
}
public static function update_comment_ancestor_meta(int $comment_ID)
{
$ancestor_id = self::get_comment_ancestor_id($comment_ID);
update_comment_meta($comment_ID, self::$ancestor_id_meta_key, $ancestor_id);
return $ancestor_id;
}
/**
* Get ancestor id from meta
*
* @param integer $comment_ID
* @param boolean $display If ture, will throw error if meta not exist. If flase, will return NULL if not exist.
* @return void
*/
public static function get_comment_ancestor_meta(int $comment_ID, $display = false)
{
$ancestor_id = get_comment_meta($comment_ID, self::$ancestor_id_meta_key, true);
if ($ancestor_id == '0') {
return 0;
} elseif (!$ancestor_id) {
if ($display) {
throw new Exception("Please init ancestor_id first");
}
return NULL;
} else {
return intval($ancestor_id);
}
}
public static function set_comment_user_agent_info_meta(int $comment_ID)
{
$user_agent = self::get_comment($comment_ID)->comment_agent;
$parser = new UaParser($user_agent);
$user_agent_info = $parser->get_public_display_content();
add_comment_meta($comment_ID, self::$user_agent_info_meta_key, $user_agent_info, true);
return $user_agent_info;
}
public static function update_comment_user_agent_info_meta(int $comment_ID)
{
$user_agent = self::get_comment($comment_ID)->comment_agent;
$parser = new UaParser($user_agent);
$user_agent_info = $parser->get_public_display_content();
update_comment_meta($comment_ID, self::$user_agent_info_meta_key, $user_agent_info);
return $user_agent_info;
}
/**
* Get user agent info from meta
*
* @param integer $comment_ID
* @param boolean $display If ture, will throw error if meta not exist. If flase, will return [] if not exist.
* @return void
*/
public static function get_comment_user_agent_info_meta(int $comment_ID, $display = false)
{
$user_agent_info = get_comment_meta($comment_ID, self::$user_agent_info_meta_key, false);
if (empty($user_agent_info) && $display) {
throw new Exception("Please init user_agent_info first");
}
return $user_agent_info;
}
public static function set_comment_user_location_meta(int $comment_ID)
{
$comment_author_IP = self::get_comment($comment_ID)->comment_author_IP;
$location = IpParser::get_location($comment_author_IP);
add_comment_meta($comment_ID, self::$user_location_meta_key, $location, true);
return $location;
}
public static function update_comment_user_location_meta(int $comment_ID)
{
$comment_author_IP = self::get_comment($comment_ID)->comment_author_IP;
$location = IpParser::get_location($comment_author_IP);
update_comment_meta($comment_ID, self::$user_location_meta_key, $location);
return $location;
}
/**
* Get user location from meta
*
* @param integer $comment_ID
* @param boolean $display If ture, will throw error if meta not exist. If flase, will return false if not exist.
* @return void
*/
public static function get_comment_user_location_meta(int $comment_ID, $display = false)
{
$location = get_comment_meta($comment_ID, self::$user_location_meta_key, true);
if (empty($location) && $display) {
throw new Exception("Please init user_location first");
}
return $location;
}
/**
* Get comment public display meta
*
* @param array string|array $args Optional. Array or string of arguments. See WP_Comment_Query::__construct() for information on accepted arguments. Default empty.
* @return void
*/
public static function get_comments_with_public_fields(array $args)
{
$comments = get_comments($args);
$output_comments = [];
foreach ($comments as $comment) {
$output_comment = [
'id' => intval($comment->comment_ID),
'post' => intval($comment->comment_post_ID),
'parent' => intval($comment->comment_parent),
'author' => intval($comment->user_id),
'author_name' => $comment->comment_author,
// 'comment_author_email' => $comment->comment_author_email,
'author_url' => $comment->comment_author_url,
// 'comment_author_IP' => $comment->comment_author_IP,
'date' => $comment->comment_date,
'date_gmt' => $comment->comment_date_gmt,
'content' => [
'rendered' => '', // TODO
'plain' => $comment->comment_content
],
'link' => '', // TODO: get_comment_link(),
// 'comment_karma' => $comment->comment_karma,
'status' => $comment->comment_approved === '1' ? 'approved' : '', // TODO
// 'comment_agent' => $comment->comment_agent,
'type' => $comment->comment_type,
'author_avatar_urls' => AvatarController::get_avatar($comment->comment_author_email),
];
$output_comment['meta_fields'] = self::get_comment_meta_fields($comment->comment_ID);
array_push($output_comments, $output_comment);
}
return $output_comments;
}
/**
* Add fields to input array $comment and return it
*
* @param integer $comment_ID
*
* @return void
*/
public static function get_comment_meta_fields(int $comment_ID)
{
$meta = [];
// $ancestor_id = self::get_comment_ancestor_meta($comment_ID);
// if ($ancestor_id === NULL) {
// $ancestor_id = self::set_comment_ancestor_meta($comment_ID);
// }
// $meta['ancestor_id'] = $ancestor_id;
$user_agent_info = self::get_comment_user_agent_info_meta($comment_ID);
if (empty($user_agent_info)) {
$user_agent_info = self::set_comment_user_agent_info_meta($comment_ID);
}
$meta['user_agent_info'] = $user_agent_info;
$user_location = self::get_comment_user_location_meta($comment_ID);
// TODO: not fully tested if '' or false or NULL
// TODO: language option
if ($user_location === '') {
$user_location = self::set_comment_user_location_meta($comment_ID);
}
$meta['user_location'] = $user_location;
return $meta;
}
}

View File

@ -1,33 +0,0 @@
<?php
namespace Sakura\Models;
class OptionModel extends BaseModel
{
public static $namespace = 'sakura_options';
public static function the_key(string $key)
{
return self::$namespace . "_{$key}";
}
public static function create(string $key, $value)
{
return add_option(self::the_key($key), $value);
}
public static function get(string $key)
{
return get_option(self::the_key($key));
}
public static function update(string $key, $value)
{
return update_option(self::the_key($key), $value);
}
public static function delete(string $key)
{
return delete_option(self::the_key($key));
}
}

View File

@ -1,365 +0,0 @@
<?php
namespace Sakura\Routers;
use WP_REST_Controller;
use WP_REST_Server;
use Sakura\Controllers\OptionController;
use Sakura\Controllers\InitStateController;
use Sakura\Controllers\MenuController;
use Sakura\Controllers\PostController;
use Sakura\Controllers\AuthorController;
use Sakura\Controllers\MediaController;
use Sakura\Controllers\CategoryController;
use Sakura\Controllers\TagController;
use Sakura\Controllers\CommentController;
use Sakura\Lib\ClassWpRestCommentsController;
use Sakura\Lib\ClassWpRestPostsController;
class ApiRouter extends WP_REST_Controller
{
public function __construct()
{
$this->namespace = 'sakura/v1';
$this->register_rest_routes();
$this->register_rest_fields();
}
/**
* Add routers
* @since 1.0.0
* @license MIT
* @author mashirozx <moezhx@outlook.com>
*/
public function register_rest_routes()
{
// add options support
add_action('rest_api_init', [new OptionController(), 'register_routes']);
add_action('rest_api_init', function () {
// theme's initial states
register_rest_route(
$this->namespace,
'/init-state',
[
'methods' => WP_REST_Server::READABLE,
'callback' => [new InitStateController(), 'show'],
'permission_callback' => function () {
return true;
}
]
);
// get menu items
register_rest_route(
$this->namespace,
'/menu',
[
'methods' => WP_REST_Server::READABLE,
'callback' => [new MenuController(), 'show'],
'permission_callback' => function () {
return true;
}
]
);
// initial comment ancestor meta
// TODO: AUTH
register_rest_route(
$this->namespace,
'/init-comments-ancestor-meta',
[
'methods' => WP_REST_Server::READABLE,
'callback' => [new InitStateController(), 'init_ancestor_meta_show'],
'permission_callback' => function () {
return true;
}
]
);
// initial comment ua info meta
// TODO: AUTH
register_rest_route(
$this->namespace,
'/init-comments-user-agent-info-meta',
[
'methods' => WP_REST_Server::READABLE,
'callback' => [new InitStateController(), 'init_user_agent_info_meta_show'],
'permission_callback' => function () {
return true;
}
]
);
$rest_comments_controller = new ClassWpRestCommentsController();
// custom create comment
register_rest_route(
$this->namespace,
'/comments',
[
'methods' => WP_REST_Server::CREATABLE,
'callback' => [$rest_comments_controller, 'create_item'],
'permission_callback' => [$rest_comments_controller, 'create_item_permissions_check'],
]
);
// custom edit comment
register_rest_route(
$this->namespace,
'/comments' . '/(?P<id>[\d]+)',
array(
'args' => array(
'id' => array(
'description' => __('Unique identifier for the comment.'),
'type' => 'integer',
),
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => [$rest_comments_controller, 'update_item'],
'permission_callback' => [$rest_comments_controller, 'update_item_permissions_check'],
'args' => $this->get_endpoint_args_for_item_schema(WP_REST_Server::EDITABLE),
),
array(
'methods' => WP_REST_Server::DELETABLE,
'callback' => [$rest_comments_controller, 'delete_item'],
'permission_callback' => [$rest_comments_controller, 'delete_item_permissions_check'],
'args' => array(
'force' => array(
'type' => 'boolean',
'default' => false,
'description' => __('Whether to bypass Trash and force deletion.'),
),
'password' => array(
'description' => __('The password for the parent post of the comment (if the post is password protected).'),
'type' => 'string',
),
),
),
'schema' => array($this, 'get_public_item_schema'),
)
);
// @deprecated using PostQueryHelper instaed
// $rest_posts_controller = new ClassWpRestPostsController('post');
// custom get posts by category slugs
// register_rest_route(
// $this->namespace,
// '/posts',
// array(
// array(
// 'methods' => WP_REST_Server::READABLE,
// 'callback' => array($rest_posts_controller, 'get_items'),
// 'permission_callback' => array($rest_posts_controller, 'get_items_permissions_check'),
// 'args' => $rest_posts_controller->get_collection_params_mod(),
// ),
// array(
// 'methods' => WP_REST_Server::CREATABLE,
// 'callback' => array($rest_posts_controller, 'create_item'),
// 'permission_callback' => array($rest_posts_controller, 'create_item_permissions_check'),
// 'args' => $rest_posts_controller->get_endpoint_args_for_item_schema(WP_REST_Server::CREATABLE),
// ),
// 'schema' => array($rest_posts_controller, 'get_public_item_schema_mod'),
// )
// );
});
}
/**
* Add fields to existing endpoint
* @since 1.0.0
* @license MIT
* @author mashirozx <moezhx@outlook.com>
*/
public function register_rest_fields()
{
// add fields to /posts & /pages endpoint
add_action('rest_api_init', function () {
$this->register_wp_post_rest_fields(['post', 'page']);
});
// add fields to /comments endpoint
add_action('rest_api_init', function () {
$this->register_wp_comment_rest_fields(['comment']);
});
}
/**
* Common method of adding rest api fields to WP_POST output
*
* @param array $object_type
* @return void
*/
public function register_wp_post_rest_fields(array $object_type)
{
/**
* Add comment_count field to $post
*/
register_rest_field(
$object_type,
'comment_count',
[
'get_callback' => function ($post, $attr, $request, $object_type) {
return PostController::get_comments_number($post['id']);
},
'update_callback' => null,
'schema' => null
]
);
register_rest_field(
$object_type,
'view_count',
[
'get_callback' => function ($post, $attr, $request, $object_type) {
return PostController::get_post_views($post['id']);
},
'update_callback' => null,
'schema' => null
]
);
register_rest_field(
$object_type,
'words_count',
[
'get_callback' => function ($post, $attr, $request, $object_type) {
return PostController::get_post_word_count($post['id']);
},
'update_callback' => null,
'schema' => null
]
);
/**
* Add markdown field to $post['content]
*/
register_rest_field(
$object_type,
'content',
[
'get_callback' => function ($post, $attr, $request, $object_type) {
return PostController::rest_api_post_content_filter($post);
},
'update_callback' => null,
'schema' => null
]
);
/**
* Add plain field to $post['excerpt]
*/
register_rest_field(
$object_type,
'excerpt',
[
'get_callback' => function ($post, $attr, $request, $object_type) {
return PostController::rest_api_post_excerpt_filter($post);
},
'update_callback' => null,
'schema' => null
]
);
register_rest_field(
$object_type,
'author_meta',
[
'get_callback' => function ($post, $attr, $request, $object_type) {
return AuthorController::get_author_meta($post['author']);
},
'update_callback' => null,
'schema' => null
]
);
register_rest_field(
$object_type,
'featured_media_meta',
[
'get_callback' => function ($post, $attr, $request, $object_type) {
return MediaController::get_attachment_metadata($post['featured_media']);
},
'update_callback' => null,
'schema' => null
]
);
register_rest_field(
$object_type,
'categories_meta',
[
'get_callback' => function ($post, $attr, $request, $object_type) {
return CategoryController::get_the_category($post['id']);
},
'update_callback' => null,
'schema' => null
]
);
register_rest_field(
$object_type,
'tags_meta',
[
'get_callback' => function ($post, $attr, $request, $object_type) {
return TagController::get_the_tags($post['id']);
},
'update_callback' => null,
'schema' => null
]
);
// end of public func
}
/**
* Common method of adding rest api fields to WP_COMMENT output
*
* @param array $object_type
* @return void
*/
public static function register_wp_comment_rest_fields(array $object_type)
{
/**
* Add markdown field to $post['content]
*/
register_rest_field(
$object_type,
'content',
[
'get_callback' => function ($comment, $attr, $request, $object_type) {
return CommentController::rest_api_comment_content_filter($comment);
},
'update_callback' => null,
'schema' => null
]
);
// get the custom meta fields
register_rest_field(
$object_type,
'meta_fields',
[
'get_callback' => function ($comment, $attr, $request, $object_type) {
return CommentController::get_comment_meta_fields($comment['id']);
},
'update_callback' => null,
'schema' => null
]
);
// get comment children preview
// register_rest_field(
// $object_type,
// 'children_preview',
// [
// 'get_callback' => function ($comment, $attr, $request, $object_type) {
// return CommentController::get_comment_children($comment['id'], 3, 1, 0, 'DESC');
// },
// 'update_callback' => null,
// 'schema' => null
// ]
// );
}
}

View File

@ -1,27 +0,0 @@
<?php
namespace Sakura\Routers;
class PagesRouter
{
public function __construct()
{
$this->add_rewrite_rules();
}
public function add_rewrite_rules()
{
// This is the front end auth page. Add this rewrite rule
// to avoid 404 message when auth page first load, so the
// response from PHP can be a blank page.
// TODO: blank
add_action('init', function () {
add_rewrite_rule('sakura/auth$', 'index.php', 'top');
});
// TODO: Docker health check route
// add_action('init', function () {
// add_rewrite_rule('health$', 'index.php', 'top');
// });
}
}

View File

@ -1,10 +0,0 @@
<?php
namespace Sakura\Services;
class PostCommentService
{
// public function create_comment(){
// wp_insert_comment()
// }
}

View File

@ -1,12 +0,0 @@
/*
Theme Name: sakura-next
Author: Mashiro
Author URI: https://github.com/mashirozx
Description: Next generation Sakura theme.
Version: 5.0.0
Text Domain: sakura-next
*/
.grecaptcha-badge {
opacity: 0;
}

View File

@ -1,145 +0,0 @@
<?php
namespace Sakura\Utils;
use Sakura\Lib\Exception;
use GeoIp2\Database\Reader;
use Ritaswc\ZxIPAddress\IPTool;
class IpParser
{
public static $version = SAKURA_VERSION;
public static $text_domain = SAKURA_TEXT_DOMAIN;
public static function get_location(string $ip)
{
try {
// return self::get_ip_location_geoip2_string($ip);
return self::get_ip_location_qqwary_string($ip);
} catch (Exception $e) {
return self::message()['unknown_error'] . " [current: {$ip}]";
}
}
public static function get_ip_location_qqwary_string(string $ip)
{
return preg_replace('/\s+/', ' ', self::get_ip_location_qqwary($ip)['disp']);
}
public static function get_ip_location_geoip2_string(string $ip)
{
// de en es fr ja pt-BR ru zh-CN
$lang = "zh-CN";
if (self::is_private_ip($ip)) {
return [self::message()['private']];
}
$db = self::get_ip_location_geoip2($ip);
$city = self::auto_select_geoip2_lang($lang, $db->city->names);
$subdivisions = self::auto_select_geoip2_lang($lang, $db->subdivisions[0]->names);
$country = self::auto_select_geoip2_lang($lang, $db->country->names);
$array = [$city, $subdivisions, $country];
if ($lang === 'zh-CN' || 'ja') {
$array = array_reverse($array);
}
return join(" ", array_filter($array));
}
private static function auto_select_geoip2_lang(string $default, array $names)
{
return $names[$default] ?? $names['en'] ?? $names['zh-CN'] ?? $names['de'] ?? $names['es'] ?? $names['fr'] ?? $names['ja'] ?? $names['ru'] ?? $names['pt-BR'] ?? '';
}
public static function message()
{
return [
'private' => __("Intranet", self::$text_domain),
'nomatch' => __("Earth", self::$text_domain),
'unknown_error' => __("IP parser handling error.", self::$text_domain),
];
}
// https://github.com/maxmind/GeoIP2-php#readme
public static function get_ip_location_geoip2(string $ip)
{
if (self::is_private_ip($ip)) {
throw new Exception("GeoIP2 doesn't support private IP range. [current: {$ip}]");
}
$reader = new Reader(__DIR__ . '/../cache/GeoLite2-City.mmdb');
$record = $reader->city($ip);
return $record;
}
public static function get_ip_location_qqwary(string $ip)
{
return IPTool::query($ip);
}
static public function is_ip($ip = NULL): bool
{
return filter_var(
$ip,
FILTER_VALIDATE_IP,
FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6
) === $ip ? TRUE : FALSE;
}
static public function is_ipv4($ip = NULL): bool
{
return filter_var(
$ip,
FILTER_VALIDATE_IP,
FILTER_FLAG_IPV4
) === $ip ? TRUE : FALSE;
}
static public function is_ipv6($ip = NULL): bool
{
return filter_var(
$ip,
FILTER_VALIDATE_IP,
FILTER_FLAG_IPV6
) === $ip ? TRUE : FALSE;
}
static public function is_public_ip($ip = NULL): bool
{
return filter_var(
$ip,
FILTER_VALIDATE_IP,
FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
) === $ip ? TRUE : FALSE;
}
static public function is_public_ipv4($ip = NULL): bool
{
return filter_var(
$ip,
FILTER_VALIDATE_IP,
FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
) === $ip ? TRUE : FALSE;
}
static public function is_public_ipv6($ip = NULL): bool
{
return filter_var(
$ip,
FILTER_VALIDATE_IP,
FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
) === $ip ? TRUE : FALSE;
}
static public function is_private_ip($ip = NULL): bool
{
return self::is_ip($ip) && !self::is_public_ip($ip);
}
static public function is_private_ipv4($ip = NULL): bool
{
return self::is_ipv4($ip) && !self::is_public_ipv4($ip);
}
static public function is_private_ipv6($ip = NULL): bool
{
return self::is_ipv6($ip) && !self::is_public_ipv6($ip);
}
}

View File

@ -1,60 +0,0 @@
<?php
namespace Sakura\Utils;
use Rogervila\ArrayDiffMultidimensional;
class Tools
{
public static function echo_interceptor(callable $callback, ...$args)
{
ob_start();
call_user_func($callback, ...$args);
$output = ob_get_contents();
ob_end_clean();
return $output;
}
public static function get_text_from_dom($node, $text)
{
if (!is_null($node->childNodes)) {
foreach ($node->childNodes as $node) {
$text = self::get_text_from_dom($node, $text);
}
} else {
return $text . $node->textContent . ' ';
}
return $text;
}
/**
* https://stackoverflow.com/a/3877494/8083009
*
* @param array $aArray1
* @param array $aArray2
*
* @return array
*/
public static function array_recursive_diff(array $aArray1, array $aArray2)
{
$aReturn = array();
foreach ($aArray1 as $mKey => $mValue) {
if (array_key_exists($mKey, $aArray2)) {
if (is_array($mValue)) {
$aRecursiveDiff = self::array_recursive_diff($mValue, $aArray2[$mKey]);
if (count($aRecursiveDiff)) {
$aReturn[$mKey] = $aRecursiveDiff;
}
} else {
if ($mValue != $aArray2[$mKey]) {
$aReturn[$mKey] = $mValue;
}
}
} else {
$aReturn[$mKey] = $mValue;
}
}
return $aReturn;
}
}

View File

@ -1,76 +0,0 @@
<?php
namespace Sakura\Utils;
use WhichBrowser\Parser;
class UaParser
{
private $result;
public function __construct(string $ua)
{
$this->result = new Parser($ua);
}
public function get_browser_name()
{
return $this->result->browser->getName();
}
public function get_browser_version()
{
return $this->result->browser->getVersion();
}
public function get_engine_name()
{
return $this->result->engine->getName();
}
public function get_engine_version()
{
return $this->result->engine->getVersion();
}
public function get_os_name()
{
return $this->result->os->getName();
}
public function get_os_version()
{
return $this->result->os->getVersion();
}
public function get_manufacturer()
{
return $this->result->device->getManufacturer();
}
public function get_model()
{
return $this->result->device->getModel();
}
public function get_device_type()
{
// TODO: does this need any exception handling?
return $this->result->device->type;
}
public function get_public_display_content()
{
return [
'os_name' => $this->get_os_name(),
'os_version' => $this->get_os_version(),
'browser_name' => $this->get_browser_name(),
'browser_version' => $this->get_browser_version(),
'engine_name' => $this->get_engine_name(),
'engine_version' => $this->get_engine_version(),
'manufacturer' => $this->get_manufacturer(),
'model' => $this->get_model(),
'device_type' => $this->get_device_type(),
];
}
}

View File

@ -1,5 +0,0 @@
<head>
<meta charset="{{bloginfo}}"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
{{wp_head}}
</head>

View File

@ -1,5 +0,0 @@
{% block admin_app %}
<div id="app" class="sakura-options-page__app" style="{{scheme}}">
Loading
</div>
{% endblock %}

View File

@ -1,14 +0,0 @@
{% block id_input %}
<input type="hidden" class="nav-menu-id" value="{{item_id}}"/>
{% endblock %}
{% block input_field %}
<p class="field-custom-{{key}} description description-wide">
<label for="custom-menu-meta-{{key}}-{{item_id}}">
{{label}}
<br>
<input type="text" name="custom_menu_meta_{{key}}[{{item_id}}]" id="custom-menu-meta-{{key}}-{{item_id}}" class="widefat edit-menu-item-custom-menu-meta-{{key}}" value="{{esc_attr_custom_menu_meta}}"/>
</label>
{{desc}}
</p>
{% endblock %}

View File

@ -1,7 +0,0 @@
{% block style %}
<link {{key}}="{{value}}" href="{{href}}">
{% endblock %}
{% block script %}
<script {{key}}="{{value}}" crossorigin src="{{src}}"></script>
{% endblock %}

65
archive.php 100644
View File

@ -0,0 +1,65 @@
<?php
/**
* The template for displaying archive pages.
*
* @link https://codex.wordpress.org/Template_Hierarchy
*
* @package Akina
*/
get_header(); ?>
<div id="primary" class="content-area">
<main id="main" class="site-main" role="main">
<?php
if ( have_posts() ) : ?>
<?php if(akina_option('patternimg') || !z_taxonomy_image_url()) { ?>
<header class="page-header">
<h1 class="cat-title"><?php single_cat_title('', true); ?></h1>
<span class="cat-des">
<?php
if(category_description() != ""){
echo "" . category_description();
}
?>
</span>
</header><!-- .page-header -->
<?php } // page-header ?>
<?php
/* Start the Loop */
while ( have_posts() ) : the_post();
/*
* 图片展示分类
*/
if ( akina_option('image_category') && is_category(explode(',',akina_option('image_category'))) ){
get_template_part( 'tpl/content', 'category' );
} else {
get_template_part( 'tpl/content', get_post_format() );
}
endwhile;
?>
<div class="clearer"></div>
<?php else :
get_template_part( 'tpl/content', 'none' );
endif; ?>
</main><!-- #main -->
<?php if ( akina_option('pagenav_style') == 'ajax') { ?>
<div id="pagination" <?php if(akina_option('image_category') && is_category(explode(',',akina_option('image_category')))) echo 'class="pagination-archive"'; ?>><?php next_posts_link(' Previous'); ?></div>
<div id="add_post"><span id="add_post_time" style="visibility: hidden;" title="<?php echo akina_option('auto_load_post',''); ?>" ></span></div>
<?php }else{ ?>
<nav class="navigator">
<?php previous_posts_link('<i class="iconfont icon-back"></i>') ?><?php next_posts_link('<i class="iconfont icon-right"></i>') ?>
</nav>
<?php } ?>
</div><!-- #primary -->
<?php
get_footer();

86
author.php 100644
View File

@ -0,0 +1,86 @@
<?php
get_header();
?>
<div class="author_info">
<div class="avatar">
<img src="<?php echo get_avatar_profile_url(); ?>" itemprop="image" alt="<?php the_author(); ?>" height="70" width="70">
</div>
<div class="author-center">
<h3><?php the_author() ?></h3>
<div class="description"><?php echo get_the_author_meta( 'description' ) ? get_the_author_meta( 'description' ) : akina_option('admin_des', 'Carpe Diem and Do what I like'); ?></div>
</div>
</div>
<style type="text/css">
.author_info{
margin-top: 50px;
overflow: hidden;
padding: 40px 0;
position: relative;
border-bottom: 1px solid #eee;
font-family: miranafont,"Hiragino Sans GB",STXihei,"Microsoft YaHei",SimSun,sans-serif;
}
.author_info .avatar{
float: left;
margin-right: 12px;
margin-left: 8px;
}
.author_info .avatar img{
border-radius: 100%;
border: 2px solid #fff;
background: #fff;
vertical-align: middle;
}
.author_info .author-center{
line-height: 28px;
padding-top: 9px;
}
.author_info .author-center h3{
font-weight: 700;
font-size: 20px;
line-height: 1.2;
margin-bottom: 5px;
display: inline;
}
.author-description {
font-size: 14px;
color: rgba(0,0,0,.4);
line-height: 1.2;
}
</style>
<div id="primary" class="content-area">
<main id="main" class="site-main" role="main">
<?php
if ( have_posts() ) :
/* Start the Loop */
while ( have_posts() ) : the_post();
/*
* Include the Post-Format-specific template for the content.
* If you want to override this in a child theme, then include a file
* called content-___.php (where ___ is the Post Format name) and that will be used instead.
*/
get_template_part( 'tpl/content', get_post_format() );
endwhile;
?>
<div class="clearer"></div>
<?php else :
get_template_part( 'tpl/content', 'none' );
endif; ?>
</main><!-- #main -->
<?php if ( akina_option('pagenav_style') == 'ajax') { ?>
<div id="pagination"><?php next_posts_link(' Previous'); ?></div>
<div id="add_post"><span id="add_post_time" style="visibility: hidden;" title="<?php echo akina_option('auto_load_post',''); ?>" ></span></div>
<?php }else{ ?>
<nav class="navigator">
<?php previous_posts_link('<i class="iconfont icon-back"></i>') ?><?php next_posts_link('<i class="iconfont icon-right"></i>') ?>
</nav>
<?php } ?>
</div><!-- #primary -->
<?php
get_footer();

17
cdn/css/lib.css 100755

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,6 @@
/*!
* baguetteBox.js
* @author feimosi
* @version 1.11.0
* @url https://github.com/feimosi/baguetteBox.js
*/#baguetteBox-overlay{display:none;opacity:0;position:fixed;overflow:hidden;top:0;left:0;width:100%;height:100%;z-index:1000000;background-color:#222;background-color:rgba(0,0,0,.8);-webkit-transition:opacity .5s ease;transition:opacity .5s ease}#baguetteBox-overlay.visible{opacity:1}#baguetteBox-overlay .full-image{display:inline-block;position:relative;width:100%;height:100%;text-align:center}#baguetteBox-overlay .full-image figure{display:inline;margin:0;height:100%}#baguetteBox-overlay .full-image img{display:inline-block;width:auto;height:auto;max-height:100%;max-width:100%;vertical-align:middle;-webkit-box-shadow:0 0 8px rgba(0,0,0,.6);-moz-box-shadow:0 0 8px rgba(0,0,0,.6);box-shadow:0 0 8px rgba(0,0,0,.6)}#baguetteBox-overlay .full-image figcaption{display:block;position:absolute;bottom:0;width:100%;text-align:center;line-height:1.8;white-space:normal;color:#ccc;background-color:#000;background-color:rgba(0,0,0,.6);font-family:sans-serif}#baguetteBox-overlay .full-image:before{content:"";display:inline-block;height:50%;width:1px;margin-right:-1px}#baguetteBox-slider{position:absolute;left:0;top:0;height:100%;width:100%;white-space:nowrap;-webkit-transition:left .4s ease,-webkit-transform .4s ease;transition:left .4s ease,-webkit-transform .4s ease;transition:left .4s ease,transform .4s ease;transition:left .4s ease,transform .4s ease,-webkit-transform .4s ease,-moz-transform .4s ease}#baguetteBox-slider.bounce-from-right{-webkit-animation:bounceFromRight .4s ease-out;animation:bounceFromRight .4s ease-out}#baguetteBox-slider.bounce-from-left{-webkit-animation:bounceFromLeft .4s ease-out;animation:bounceFromLeft .4s ease-out}@-webkit-keyframes bounceFromRight{0%,100%{margin-left:0}50%{margin-left:-30px}}@keyframes bounceFromRight{0%,100%{margin-left:0}50%{margin-left:-30px}}@-webkit-keyframes bounceFromLeft{0%,100%{margin-left:0}50%{margin-left:30px}}@keyframes bounceFromLeft{0%,100%{margin-left:0}50%{margin-left:30px}}.baguetteBox-button#next-button,.baguetteBox-button#previous-button{top:50%;top:calc(50% - 30px);width:44px;height:60px}.baguetteBox-button{position:absolute;cursor:pointer;outline:0;padding:0;margin:0;border:0;-moz-border-radius:15%;border-radius:15%;background-color:#323232;background-color:rgba(50,50,50,.5);color:#ddd;font:1.6em sans-serif;-webkit-transition:background-color .4s ease;transition:background-color .4s ease}.baguetteBox-button:focus,.baguetteBox-button:hover{background-color:rgba(50,50,50,.9)}.baguetteBox-button#next-button{right:2%}.baguetteBox-button#previous-button{left:2%}.baguetteBox-button#close-button{top:20px;right:2%;right:calc(2% + 6px);width:30px;height:30px}.baguetteBox-button svg{position:absolute;left:0;top:0}.baguetteBox-spinner{width:40px;height:40px;display:inline-block;position:absolute;top:50%;left:50%;margin-top:-20px;margin-left:-20px}.baguetteBox-double-bounce1,.baguetteBox-double-bounce2{width:100%;height:100%;-moz-border-radius:50%;border-radius:50%;background-color:#fff;opacity:.6;position:absolute;top:0;left:0;-webkit-animation:bounce 2s infinite ease-in-out;animation:bounce 2s infinite ease-in-out}.baguetteBox-double-bounce2{-webkit-animation-delay:-1s;animation-delay:-1s}@-webkit-keyframes bounce{0%,100%{-webkit-transform:scale(0);transform:scale(0)}50%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounce{0%,100%{-webkit-transform:scale(0);-moz-transform:scale(0);transform:scale(0)}50%{-webkit-transform:scale(1);-moz-transform:scale(1);transform:scale(1)}}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,270 @@
@font-face {
font-family:"socialshare";src:url("https://cdn.jsdelivr.net/gh/moezx/share.js@1.0.3/src/fonts/iconfont.eot");
src:url("https://cdn.jsdelivr.net/gh/moezx/share.js@1.0.3/src/fonts/iconfont.eot?#iefix") format("embedded-opentype"),url("https://cdn.jsdelivr.net/gh/moezx/share.js@1.0.3/src/fonts/iconfont.woff") format("woff"),url("https://cdn.jsdelivr.net/gh/moezx/share.js@1.0.3/src/fonts/iconfont.ttf") format("truetype"),url("https://cdn.jsdelivr.net/gh/moezx/share.js@1.0.3/src/fonts/iconfont.svg#iconfont") format("svg")
}
.social-share {
display: inline-block;
font-family: "socialshare" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-webkit-text-stroke-width: 0.2px;
-moz-osx-font-smoothing: grayscale
}
.social-share * {
font-family: "socialshare" !important
}
.social-share .icon-tencent:before {
content: "\f07a"
}
.social-share .icon-qq:before {
content: "\f11a"
}
.social-share .icon-weibo:before {
content: "\f12a"
}
.social-share .icon-wechat:before {
content: "\f09a"
}
.social-share .icon-douban:before {
content: "\f10a"
}
.social-share .icon-heart:before {
content: "\f20a"
}
.social-share .icon-like:before {
content: "\f00a"
}
.social-share .icon-qzone:before {
content: "\f08a"
}
.social-share .icon-linkedin:before {
content: "\f01a"
}
.social-share .icon-diandian:before {
content: "\f05a"
}
.social-share .icon-facebook:before {
content: "\f03a"
}
.social-share .icon-google:before {
content: "\f04a"
}
.social-share .icon-twitter:before {
content: "\f06a"
}
.social-share a {
position: relative;
text-decoration: none;
margin: 0 4px;
display: inline-block;
outline: none
}
.social-share .social-share-icon {
position: relative;
display: inline-block;
width: 16px;
height: 16px;
font-size: 10px;
border-radius: 50%;
line-height: 16px;
border: 1px solid #666;
color: #666;
text-align: center;
vertical-align: middle;
transition: background 0.6s ease-out 0s;
}
.social-share .social-share-icon:hover {
background: #666;
color: #fff
}
.social-share .icon-weibo {
color: #ff763b;
border-color: #ff763b
}
.social-share .icon-weibo:hover {
background: #ff763b
}
.social-share .icon-tencent {
color: #56b6e7;
border-color: #56b6e7
}
.social-share .icon-tencent:hover {
background: #56b6e7
}
.social-share .icon-qq {
color: #56b6e7;
border-color: #56b6e7
}
.social-share .icon-qq:hover {
background: #56b6e7
}
.social-share .icon-qzone {
color: #FDBE3D;
border-color: #FDBE3D
}
.social-share .icon-qzone:hover {
background: #FDBE3D
}
.social-share .icon-douban {
color: #33b045;
border-color: #33b045
}
.social-share .icon-douban:hover {
background: #33b045
}
.social-share .icon-linkedin {
color: #0077B5;
border-color: #0077B5
}
.social-share .icon-linkedin:hover {
background: #0077B5
}
.social-share .icon-facebook {
color: #44619D;
border-color: #44619D
}
.social-share .icon-facebook:hover {
background: #44619D
}
.social-share .icon-google {
color: #db4437;
border-color: #db4437
}
.social-share .icon-google:hover {
background: #db4437
}
.social-share .icon-twitter {
color: #55acee;
border-color: #55acee
}
.social-share .icon-twitter:hover {
background: #55acee
}
.social-share .icon-diandian {
color: #307DCA;
border-color: #307DCA
}
.social-share .icon-diandian:hover {
background: #307DCA
}
.social-share .icon-wechat {
position: relative;
color: #7bc549;
border-color: #7bc549
}
.social-share .icon-wechat:hover {
background: #7bc549
}
.social-share .icon-wechat .wechat-qrcode {
display: none;
border: 1px solid #eee;
position: absolute;
z-index: 9;
top: -205px;
left: -84px;
width: 200px;
height: 192px;
color: #666;
font-size: 12px;
text-align: center;
background-color: #fff;
box-shadow: 0 2px 10px #aaa;
transition: all 200ms;
-webkit-tansition: all 350ms;
-moz-transition: all 350ms
}
.social-share .icon-wechat .wechat-qrcode.bottom {
top: 40px;
left: -84px
}
.social-share .icon-wechat .wechat-qrcode.bottom:after {
display: none
}
.social-share .icon-wechat .wechat-qrcode h4 {
font-weight: normal;
height: 26px;
line-height: 26px;
font-size: 12px;
background-color: #f3f3f3;
margin: 0;
padding: 0;
color: #777
}
.social-share .icon-wechat .wechat-qrcode .qrcode {
width: 105px;
margin: 10px auto
}
.social-share .icon-wechat .wechat-qrcode .qrcode table {
margin: 0 !important
}
.social-share .icon-wechat .wechat-qrcode .help p {
font-weight: normal;
line-height: 16px;
padding: 0;
margin: 0
}
.social-share .icon-wechat .wechat-qrcode:after {
content: '';
position: absolute;
left: 50%;
margin-left: -6px;
bottom: -13px;
width: 0;
height: 0;
border-width: 8px 6px 6px 6px;
border-style: solid;
border-color: #fff transparent transparent transparent
}
.social-share .icon-wechat:hover .wechat-qrcode {
display: block
}

View File

@ -0,0 +1,86 @@
/* Fix: Aplayer beautify*/
.aplayer {
background: rgba(255, 255, 255, .3);
font-family: 'Noto Serif CJK SC', 'Noto Serif CJK', 'Source Han Serif SC', 'Source Han Serif', 'source-han-serif-sc', 'PT Serif', 'SongTi SC', 'MicroSoft Yahei', Georgia, serif;
}
.aplayer .aplayer-lrc::before {
background: rgba(255, 255, 255, 0);
filter: none
}
.aplayer .aplayer-lrc::after {
background: rgba(255, 255, 255, 0);
filter: none
}
.aplayer {
box-shadow: 0 0 0 1px #eee;
border-radius: 3px;
margin: 15px 0
}
.aplayer .aplayer-info {
padding: 9px 7px 0 10px
}
.aplayer .aplayer-info .aplayer-controller .aplayer-bar-wrap {
margin-left: 1px;
padding-right: 3px
}
.aplayer .aplayer-info .aplayer-controller .aplayer-bar-wrap .aplayer-bar,
.aplayer .aplayer-info .aplayer-controller .aplayer-bar-wrap .aplayer-bar .aplayer-loaded,
.aplayer .aplayer-info .aplayer-controller .aplayer-bar-wrap .aplayer-bar .aplayer-played {
height: 6px;
border-radius: 10px
}
.aplayer .aplayer-info .aplayer-controller .aplayer-bar-wrap .aplayer-bar .aplayer-played .aplayer-thumb {
height: 12px;
width: 12px;
right: 3px;
box-shadow: 0 0 5px 0 rgba(0, 0, 0, .18);
transition: all .35s
}
.aplayer .aplayer-info .aplayer-controller .aplayer-volume-wrap .aplayer-volume-bar-wrap .aplayer-volume-bar,
.aplayer .aplayer-info .aplayer-controller .aplayer-volume-wrap .aplayer-volume-bar-wrap .aplayer-volume-bar .aplayer-volume {
border-radius: 10px;
width: 6px
}
.aplayer .aplayer-info .aplayer-controller .aplayer-volume-wrap .aplayer-volume-bar-wrap .aplayer-volume-bar {
right: 10.5px
}
.aplayer .aplayer-list ol li {
border-top: none
}
.aplayer .aplayer-list ol li.aplayer-list-light {
background: #f8f8f8
}
#aplayer-float {
background: rgba(255, 255, 255, 1);
margin: 0;
}
.ap-hover {
left: -66px !important;
}
.ap-hover:hover {
left: 0px !important;
}
#aplayer-float .aplayer-lrc-current {
color: orange;
font-size: 15px;
font-weight: bold;
}
.aplayer-lrc {
pointer-events: none;
}

59
cdn/do.py 100644
View File

@ -0,0 +1,59 @@
# coding: utf-8
'''
Created on Apr 12, 2018
Update on 2018-04-13
@author: Mashiro @ https://2heng.xin
Desc: Auto compress & minfy JavaScript codes and CSS style sheet
'''
import os
from os import listdir
from os.path import isfile, join
from jsmin import jsmin
from csscompressor import compress
import time
import codecs
localtime = time.asctime( time.localtime(time.time()) )
print (localtime)
pathJS = 'js/src/'
pathJSroot = 'js/'
pathCSS = 'css/src/'
pathCSSroot = 'css/'
jsfiles = [f for f in listdir(pathJS) if isfile(join(pathJS, f))]
cssfiles = [f for f in listdir(pathCSS) if isfile(join(pathCSS, f))]
strJS = '/*! Generate by Mashiro. ' + localtime + '*/'
strCSS = '/*! Generate by Mashiro. ' + localtime + '*/'
for f in jsfiles:
with codecs.open(pathJS + f, 'r', encoding='utf-8') as file:
data = file.read()
strJS = strJS + data
print(f)
JSminified = jsmin(strJS)
with codecs.open(pathJSroot + "lib.js", "w", encoding='utf-8') as text_file:
print(JSminified, file=text_file)
print('------------------JS Done------------------')
for f in cssfiles:
with codecs.open(pathCSS + f, 'r', encoding='utf-8') as file:
data = file.read()
strCSS = strCSS + data
print(f)
CSSminified = compress(strCSS)
with codecs.open(pathCSSroot + "lib.css", "w", encoding='utf-8') as text_file:
print(CSSminified, file=text_file)
print('------------------CSS Done------------------')
key = input('Press any key to quit')
quit()

45
cdn/js/lib.js 100755

File diff suppressed because one or more lines are too long

2
cdn/js/src/01.jquery.min.js vendored 100755

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
/*! line-number */
!function(n,e){"use strict";function t(){var n=e.createElement("style");n.type="text/css",n.innerHTML=h(".{0}{border-collapse:collapse}.{0} td{padding:0}.{1}:before{content:attr({2})}",[f,m,j]),e.getElementsByTagName("head")[0].appendChild(n)}function r(t){"complete"===e.readyState?l(t):n.addEventListener("DOMContentLoaded",function(){l(t)})}function l(t){try{var r=e.querySelectorAll("code.hljs");for(var l in r)r.hasOwnProperty(l)&&i(r[l],t)}catch(o){n.console.error("LineNumbers error: ",o)}}function i(n,e){if("object"==typeof n){e=e||{singleLine:!1};var t=e.singleLine?0:1;u(function(){s(n),n.innerHTML=o(n.innerHTML,t)})}}function o(n,e){var t=c(n);if(""===t[t.length-1].trim()&&t.pop(),t.length>e){for(var r="",l=0,i=t.length;l<i;l++)r+=h('<tr><td class="{0}"><div class="{1} {2}" {3}="{5}"></div></td><td class="{4}"><div class="{1}">{6}</div></td></tr>',[v,g,m,j,p,l+1,t[l].length>0?t[l]:" "]);return h('<table class="{0}">{1}</table>',[f,r])}return n}function s(n){var e=n.childNodes;for(var t in e)if(e.hasOwnProperty(t)){var r=e[t];d(r.textContent)>0&&(r.childNodes.length>0?s(r):a(r.parentNode))}}function a(n){var e=n.className;if(/hljs-/.test(e)){for(var t=c(n.innerHTML),r=0,l="";r<t.length;r++)l+=h('<span class="{0}">{1}</span>\n',[e,t[r]]);n.innerHTML=l.trim()}}function c(n){return 0===n.length?[]:n.split(L)}function d(n){return(n.trim().match(L)||[]).length}function u(e){n.setTimeout(e,0)}function h(n,e){return n.replace(/\{(\d+)\}/g,function(n,t){return e[t]?e[t]:n})}var f="hljs-ln",g="hljs-ln-line",p="hljs-ln-code",v="hljs-ln-numbers",m="hljs-ln-n",j="data-line-number",L=/\r\n|\r|\n/g;n.hljs?(n.hljs.initLineNumbersOnLoad=r,n.hljs.lineNumbersBlock=i,t()):n.console.error("highlight.js not detected!")}(window,document);

1
cdn/js/src/05.nprogress.min.js vendored 100644
View File

@ -0,0 +1 @@
!function(n,e){"function"==typeof define&&define.amd?define(e):"object"==typeof exports?module.exports=e():n.NProgress=e()}(this,function(){function n(n,e,t){return e>n?e:n>t?t:n}function e(n){return 100*(-1+n)}function t(n,t,r){var i;return i="translate3d"===c.positionUsing?{transform:"translate3d("+e(n)+"%,0,0)"}:"translate"===c.positionUsing?{transform:"translate("+e(n)+"%,0)"}:{"margin-left":e(n)+"%"},i.transition="all "+t+"ms "+r,i}function r(n,e){var t="string"==typeof n?n:o(n);return t.indexOf(" "+e+" ")>=0}function i(n,e){var t=o(n),i=t+e;r(t,e)||(n.className=i.substring(1))}function s(n,e){var t,i=o(n);r(n,e)&&(t=i.replace(" "+e+" "," "),n.className=t.substring(1,t.length-1))}function o(n){return(" "+(n.className||"")+" ").replace(/\s+/gi," ")}function a(n){n&&n.parentNode&&n.parentNode.removeChild(n)}var u={};u.version="0.2.0";var c=u.settings={minimum:.08,easing:"ease",positionUsing:"",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,showSpinner:!0,barSelector:'[role="bar"]',spinnerSelector:'[role="spinner"]',parent:"body",template:'<div class="bar" role="bar"><div class="peg"></div></div><div class="spinner" role="spinner"><div class="spinner-icon"></div></div>'};u.configure=function(n){var e,t;for(e in n)t=n[e],void 0!==t&&n.hasOwnProperty(e)&&(c[e]=t);return this},u.status=null,u.set=function(e){var r=u.isStarted();e=n(e,c.minimum,1),u.status=1===e?null:e;var i=u.render(!r),s=i.querySelector(c.barSelector),o=c.speed,a=c.easing;return i.offsetWidth,l(function(n){""===c.positionUsing&&(c.positionUsing=u.getPositioningCSS()),f(s,t(e,o,a)),1===e?(f(i,{transition:"none",opacity:1}),i.offsetWidth,setTimeout(function(){f(i,{transition:"all "+o+"ms linear",opacity:0}),setTimeout(function(){u.remove(),n()},o)},o)):setTimeout(n,o)}),this},u.isStarted=function(){return"number"==typeof u.status},u.start=function(){u.status||u.set(0);var n=function(){setTimeout(function(){u.status&&(u.trickle(),n())},c.trickleSpeed)};return c.trickle&&n(),this},u.done=function(n){return n||u.status?u.inc(.3+.5*Math.random()).set(1):this},u.inc=function(e){var t=u.status;return t?("number"!=typeof e&&(e=(1-t)*n(Math.random()*t,.1,.95)),t=n(t+e,0,.994),u.set(t)):u.start()},u.trickle=function(){return u.inc(Math.random()*c.trickleRate)},function(){var n=0,e=0;u.promise=function(t){return t&&"resolved"!==t.state()?(0===e&&u.start(),n++,e++,t.always(function(){e--,0===e?(n=0,u.done()):u.set((n-e)/n)}),this):this}}(),u.render=function(n){if(u.isRendered())return document.getElementById("nprogress");i(document.documentElement,"nprogress-busy");var t=document.createElement("div");t.id="nprogress",t.innerHTML=c.template;var r,s=t.querySelector(c.barSelector),o=n?"-100":e(u.status||0),l=document.querySelector(c.parent);return f(s,{transition:"all 0 linear",transform:"translate3d("+o+"%,0,0)"}),c.showSpinner||(r=t.querySelector(c.spinnerSelector),r&&a(r)),l!=document.body&&i(l,"nprogress-custom-parent"),l.appendChild(t),t},u.remove=function(){s(document.documentElement,"nprogress-busy"),s(document.querySelector(c.parent),"nprogress-custom-parent");var n=document.getElementById("nprogress");n&&a(n)},u.isRendered=function(){return!!document.getElementById("nprogress")},u.getPositioningCSS=function(){var n=document.body.style,e="WebkitTransform"in n?"Webkit":"MozTransform"in n?"Moz":"msTransform"in n?"ms":"OTransform"in n?"O":"";return e+"Perspective"in n?"translate3d":e+"Transform"in n?"translate":"margin"};var l=function(){function n(){var t=e.shift();t&&t(n)}var e=[];return function(t){e.push(t),1==e.length&&n()}}(),f=function(){function n(n){return n.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(n,e){return e.toUpperCase()})}function e(n){var e=document.body.style;if(n in e)return n;for(var t,r=i.length,s=n.charAt(0).toUpperCase()+n.slice(1);r--;)if(t=i[r]+s,t in e)return t;return n}function t(t){return t=n(t),s[t]||(s[t]=e(t))}function r(n,e,r){e=t(e),n.style[e]=r}var i=["Webkit","O","Moz","ms"],s={};return function(n,e){var t,i,s=arguments;if(2==s.length)for(t in e)i=e[t],void 0!==i&&e.hasOwnProperty(t)&&r(n,t,i);else r(n,s[1],s[2])}}();return u});

View File

@ -0,0 +1,324 @@
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["POWERMODE"] = factory();
else
root["POWERMODE"] = factory();
})(this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
'use strict';
var canvas = document.createElement('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.cssText = 'position:fixed;top:0;left:0;pointer-events:none;z-index:999999';
window.addEventListener('resize', function () {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
});
document.body.appendChild(canvas);
var context = canvas.getContext('2d');
var particles = [];
var particlePointer = 0;
var rendering = false;
POWERMODE.shake = true;
function getRandom(min, max) {
return Math.random() * (max - min) + min;
}
function getColor(el) {
if (POWERMODE.colorful) {
var u = getRandom(0, 360);
return 'hsla(' + getRandom(u - 10, u + 10) + ', 100%, ' + getRandom(50, 80) + '%, ' + 1 + ')';
} else {
return window.getComputedStyle(el).color;
}
}
function getCaret() {
var el = document.activeElement;
var bcr;
if (el.tagName === 'TEXTAREA' ||
(el.tagName === 'INPUT' && el.getAttribute('type') === 'text')) {
var offset = __webpack_require__(1)(el, el.selectionEnd);
bcr = el.getBoundingClientRect();
return {
x: offset.left + bcr.left,
y: offset.top + bcr.top,
color: getColor(el)
};
}
var selection = window.getSelection();
if (selection.rangeCount) {
var range = selection.getRangeAt(0);
var startNode = range.startContainer;
if (startNode.nodeType === document.TEXT_NODE) {
startNode = startNode.parentNode;
}
bcr = range.getBoundingClientRect();
return {
x: bcr.left,
y: bcr.top,
color: getColor(startNode)
};
}
return { x: 0, y: 0, color: 'transparent' };
}
function createParticle(x, y, color) {
return {
x: x,
y: y,
alpha: 1,
color: color,
velocity: {
x: -1 + Math.random() * 2,
y: -3.5 + Math.random() * 2
}
};
}
function POWERMODE() {
{ // spawn particles
var caret = getCaret();
var numParticles = 5 + Math.round(Math.random() * 10);
while (numParticles--) {
particles[particlePointer] = createParticle(caret.x, caret.y, caret.color);
particlePointer = (particlePointer + 1) % 500;
}
}
{ // shake screen
if (POWERMODE.shake) {
var intensity = 1 + 2 * Math.random();
var x = intensity * (Math.random() > 0.5 ? -1 : 1);
var y = intensity * (Math.random() > 0.5 ? -1 : 1);
document.body.style.marginLeft = x + 'px';
document.body.style.marginTop = y + 'px';
setTimeout(function() {
document.body.style.marginLeft = '';
document.body.style.marginTop = '';
}, 75);
}
}
if(!rendering){
requestAnimationFrame(loop);
}
};
POWERMODE.colorful = false;
function loop() {
rendering = true;
context.clearRect(0, 0, canvas.width, canvas.height);
var rendered = false;
var rect = canvas.getBoundingClientRect();
for (var i = 0; i < particles.length; ++i) {
var particle = particles[i];
if (particle.alpha <= 0.1) continue;
particle.velocity.y += 0.075;
particle.x += particle.velocity.x;
particle.y += particle.velocity.y;
particle.alpha *= 0.96;
context.globalAlpha = particle.alpha;
context.fillStyle = particle.color;
context.fillRect(
Math.round(particle.x - 1.5) - rect.left,
Math.round(particle.y - 1.5) - rect.top,
3, 3
);
rendered = true;
}
if(rendered){
requestAnimationFrame(loop);
}else{
rendering = false;
}
}
module.exports = POWERMODE;
/***/ }),
/* 1 */
/***/ (function(module, exports) {
/* jshint browser: true */
(function () {
// The properties that we copy into a mirrored div.
// Note that some browsers, such as Firefox,
// do not concatenate properties, i.e. padding-top, bottom etc. -> padding,
// so we have to do every single property specifically.
var properties = [
'direction', // RTL support
'boxSizing',
'width', // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does
'height',
'overflowX',
'overflowY', // copy the scrollbar for IE
'borderTopWidth',
'borderRightWidth',
'borderBottomWidth',
'borderLeftWidth',
'borderStyle',
'paddingTop',
'paddingRight',
'paddingBottom',
'paddingLeft',
// https://developer.mozilla.org/en-US/docs/Web/CSS/font
'fontStyle',
'fontVariant',
'fontWeight',
'fontStretch',
'fontSize',
'fontSizeAdjust',
'lineHeight',
'fontFamily',
'textAlign',
'textTransform',
'textIndent',
'textDecoration', // might not make a difference, but better be safe
'letterSpacing',
'wordSpacing',
'tabSize',
'MozTabSize'
];
var isFirefox = window.mozInnerScreenX != null;
function getCaretCoordinates(element, position, options) {
var debug = options && options.debug || false;
if (debug) {
var el = document.querySelector('#input-textarea-caret-position-mirror-div');
if ( el ) { el.parentNode.removeChild(el); }
}
// mirrored div
var div = document.createElement('div');
div.id = 'input-textarea-caret-position-mirror-div';
document.body.appendChild(div);
var style = div.style;
var computed = window.getComputedStyle? getComputedStyle(element) : element.currentStyle; // currentStyle for IE < 9
// default textarea styles
style.whiteSpace = 'pre-wrap';
if (element.nodeName !== 'INPUT')
style.wordWrap = 'break-word'; // only for textarea-s
// position off-screen
style.position = 'absolute'; // required to return coordinates properly
if (!debug)
style.visibility = 'hidden'; // not 'display: none' because we want rendering
// transfer the element's properties to the div
properties.forEach(function (prop) {
style[prop] = computed[prop];
});
if (isFirefox) {
// Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275
if (element.scrollHeight > parseInt(computed.height))
style.overflowY = 'scroll';
} else {
style.overflow = 'hidden'; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll'
}
div.textContent = element.value.substring(0, position);
// the second special handling for input type="text" vs textarea: spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037
if (element.nodeName === 'INPUT')
div.textContent = div.textContent.replace(/\s/g, "\u00a0");
var span = document.createElement('span');
// Wrapping must be replicated *exactly*, including when a long word gets
// onto the next line, with whitespace at the end of the line before (#7).
// The *only* reliable way to do that is to copy the *entire* rest of the
// textarea's content into the <span> created at the caret position.
// for inputs, just '.' would be enough, but why bother?
span.textContent = element.value.substring(position) || '.'; // || because a completely empty faux span doesn't render at all
div.appendChild(span);
var coordinates = {
top: span.offsetTop + parseInt(computed['borderTopWidth']),
left: span.offsetLeft + parseInt(computed['borderLeftWidth'])
};
if (debug) {
span.style.backgroundColor = '#aaa';
} else {
document.body.removeChild(div);
}
return coordinates;
}
if (typeof module != "undefined" && typeof module.exports != "undefined") {
module.exports = getCaretCoordinates;
} else {
window.getCaretCoordinates = getCaretCoordinates;
}
}());
/***/ })
/******/ ])
});
;

2
cdn/js/src/07.APlayer.min.js vendored 100644

File diff suppressed because one or more lines are too long

2
cdn/js/src/08.lazyload.min.js vendored 100644
View File

@ -0,0 +1,2 @@
/*! Lazy Load 2.0.0-beta.2 - MIT license - Copyright 2007-2017 Mika Tuupola */
!function(t,e){"object"==typeof exports?module.exports=e(t):"function"==typeof define&&define.amd?define([],e(t)):t.LazyLoad=e(t)}("undefined"!=typeof global?global:this.window||this.global,function(t){"use strict";function e(t,e){this.settings=r(s,e||{}),this.images=t||document.querySelectorAll(this.settings.selector),this.observer=null,this.init()}const s={src:"data-src",srcset:"data-srcset",selector:".lazyload"},r=function(){let t={},e=!1,s=0,o=arguments.length;"[object Boolean]"===Object.prototype.toString.call(arguments[0])&&(e=arguments[0],s++);for(;s<o;s++)!function(s){for(let o in s)Object.prototype.hasOwnProperty.call(s,o)&&(e&&"[object Object]"===Object.prototype.toString.call(s[o])?t[o]=r(!0,t[o],s[o]):t[o]=s[o])}(arguments[s]);return t};if(e.prototype={init:function(){if(!t.IntersectionObserver)return void this.loadImages();let e=this,s={root:null,rootMargin:"0px",threshold:[0]};this.observer=new IntersectionObserver(function(t){t.forEach(function(t){if(t.isIntersecting){e.observer.unobserve(t.target);let s=t.target.getAttribute(e.settings.src),r=t.target.getAttribute(e.settings.srcset);"img"===t.target.tagName.toLowerCase()?(s&&(t.target.src=s),r&&(t.target.srcset=r)):t.target.style.backgroundImage="url("+s+")"}})},s),this.images.forEach(function(t){e.observer.observe(t)})},loadAndDestroy:function(){this.settings&&(this.loadImages(),this.destroy())},loadImages:function(){if(!this.settings)return;let t=this;this.images.forEach(function(e){let s=e.getAttribute(t.settings.src),r=e.getAttribute(t.settings.srcset);"img"===e.tagName.toLowerCase()?(s&&(e.src=s),r&&(e.srcset=r)):e.style.backgroundImage="url("+s+")"})},destroy:function(){this.settings&&(this.observer.disconnect(),this.settings=null)}},t.lazyload=function(t,s){return new e(t,s)},t.jQuery){const s=t.jQuery;s.fn.lazyload=function(t){return t=t||{},t.attribute=t.attribute||"data-src",new e(s.makeArray(this),t),this}}return e});

View File

@ -0,0 +1,16 @@
// Gravatar
function get_gravatar(email, size) {
// MD5 (Message-Digest Algorithm) by WebToolkit
//
var MD5=function(s){function L(k,d){return(k<<d)|(k>>>(32-d))}function K(G,k){var I,d,F,H,x;F=(G&2147483648);H=(k&2147483648);I=(G&1073741824);d=(k&1073741824);x=(G&1073741823)+(k&1073741823);if(I&d){return(x^2147483648^F^H)}if(I|d){if(x&1073741824){return(x^3221225472^F^H)}else{return(x^1073741824^F^H)}}else{return(x^F^H)}}function r(d,F,k){return(d&F)|((~d)&k)}function q(d,F,k){return(d&k)|(F&(~k))}function p(d,F,k){return(d^F^k)}function n(d,F,k){return(F^(d|(~k)))}function u(G,F,aa,Z,k,H,I){G=K(G,K(K(r(F,aa,Z),k),I));return K(L(G,H),F)}function f(G,F,aa,Z,k,H,I){G=K(G,K(K(q(F,aa,Z),k),I));return K(L(G,H),F)}function D(G,F,aa,Z,k,H,I){G=K(G,K(K(p(F,aa,Z),k),I));return K(L(G,H),F)}function t(G,F,aa,Z,k,H,I){G=K(G,K(K(n(F,aa,Z),k),I));return K(L(G,H),F)}function e(G){var Z;var F=G.length;var x=F+8;var k=(x-(x%64))/64;var I=(k+1)*16;var aa=Array(I-1);var d=0;var H=0;while(H<F){Z=(H-(H%4))/4;d=(H%4)*8;aa[Z]=(aa[Z]|(G.charCodeAt(H)<<d));H++}Z=(H-(H%4))/4;d=(H%4)*8;aa[Z]=aa[Z]|(128<<d);aa[I-2]=F<<3;aa[I-1]=F>>>29;return aa}function B(x){var k="",F="",G,d;for(d=0;d<=3;d++){G=(x>>>(d*8))&255;F="0"+G.toString(16);k=k+F.substr(F.length-2,2)}return k}function J(k){k=k.replace(/rn/g,"n");var d="";for(var F=0;F<k.length;F++){var x=k.charCodeAt(F);if(x<128){d+=String.fromCharCode(x)}else{if((x>127)&&(x<2048)){d+=String.fromCharCode((x>>6)|192);d+=String.fromCharCode((x&63)|128)}else{d+=String.fromCharCode((x>>12)|224);d+=String.fromCharCode(((x>>6)&63)|128);d+=String.fromCharCode((x&63)|128)}}}return d}var C=Array();var P,h,E,v,g,Y,X,W,V;var S=7,Q=12,N=17,M=22;var A=5,z=9,y=14,w=20;var o=4,m=11,l=16,j=23;var U=6,T=10,R=15,O=21;s=J(s);C=e(s);Y=1732584193;X=4023233417;W=2562383102;V=271733878;for(P=0;P<C.length;P+=16){h=Y;E=X;v=W;g=V;Y=u(Y,X,W,V,C[P+0],S,3614090360);V=u(V,Y,X,W,C[P+1],Q,3905402710);W=u(W,V,Y,X,C[P+2],N,606105819);X=u(X,W,V,Y,C[P+3],M,3250441966);Y=u(Y,X,W,V,C[P+4],S,4118548399);V=u(V,Y,X,W,C[P+5],Q,1200080426);W=u(W,V,Y,X,C[P+6],N,2821735955);X=u(X,W,V,Y,C[P+7],M,4249261313);Y=u(Y,X,W,V,C[P+8],S,1770035416);V=u(V,Y,X,W,C[P+9],Q,2336552879);W=u(W,V,Y,X,C[P+10],N,4294925233);X=u(X,W,V,Y,C[P+11],M,2304563134);Y=u(Y,X,W,V,C[P+12],S,1804603682);V=u(V,Y,X,W,C[P+13],Q,4254626195);W=u(W,V,Y,X,C[P+14],N,2792965006);X=u(X,W,V,Y,C[P+15],M,1236535329);Y=f(Y,X,W,V,C[P+1],A,4129170786);V=f(V,Y,X,W,C[P+6],z,3225465664);W=f(W,V,Y,X,C[P+11],y,643717713);X=f(X,W,V,Y,C[P+0],w,3921069994);Y=f(Y,X,W,V,C[P+5],A,3593408605);V=f(V,Y,X,W,C[P+10],z,38016083);W=f(W,V,Y,X,C[P+15],y,3634488961);X=f(X,W,V,Y,C[P+4],w,3889429448);Y=f(Y,X,W,V,C[P+9],A,568446438);V=f(V,Y,X,W,C[P+14],z,3275163606);W=f(W,V,Y,X,C[P+3],y,4107603335);X=f(X,W,V,Y,C[P+8],w,1163531501);Y=f(Y,X,W,V,C[P+13],A,2850285829);V=f(V,Y,X,W,C[P+2],z,4243563512);W=f(W,V,Y,X,C[P+7],y,1735328473);X=f(X,W,V,Y,C[P+12],w,2368359562);Y=D(Y,X,W,V,C[P+5],o,4294588738);V=D(V,Y,X,W,C[P+8],m,2272392833);W=D(W,V,Y,X,C[P+11],l,1839030562);X=D(X,W,V,Y,C[P+14],j,4259657740);Y=D(Y,X,W,V,C[P+1],o,2763975236);V=D(V,Y,X,W,C[P+4],m,1272893353);W=D(W,V,Y,X,C[P+7],l,4139469664);X=D(X,W,V,Y,C[P+10],j,3200236656);Y=D(Y,X,W,V,C[P+13],o,681279174);V=D(V,Y,X,W,C[P+0],m,3936430074);W=D(W,V,Y,X,C[P+3],l,3572445317);X=D(X,W,V,Y,C[P+6],j,76029189);Y=D(Y,X,W,V,C[P+9],o,3654602809);V=D(V,Y,X,W,C[P+12],m,3873151461);W=D(W,V,Y,X,C[P+15],l,530742520);X=D(X,W,V,Y,C[P+2],j,3299628645);Y=t(Y,X,W,V,C[P+0],U,4096336452);V=t(V,Y,X,W,C[P+7],T,1126891415);W=t(W,V,Y,X,C[P+14],R,2878612391);X=t(X,W,V,Y,C[P+5],O,4237533241);Y=t(Y,X,W,V,C[P+12],U,1700485571);V=t(V,Y,X,W,C[P+3],T,2399980690);W=t(W,V,Y,X,C[P+10],R,4293915773);X=t(X,W,V,Y,C[P+1],O,2240044497);Y=t(Y,X,W,V,C[P+8],U,1873313359);V=t(V,Y,X,W,C[P+15],T,4264355552);W=t(W,V,Y,X,C[P+6],R,2734768916);X=t(X,W,V,Y,C[P+13],O,1309151649);Y=t(Y,X,W,V,C[P+4],U,4149444226);V=t(V,Y,X,W,C[P+11],T,3174756917);W=t(W,V,Y,X,C[P+2],R,718787259);X=t(X,W,V,Y,C[P+9],O,3951481745);Y=K(Y,h);X=K(X,E);W=K(W,v);V=K(V,g)}var i=B(Y)+B(X)+B(W)+B(V);return i.toLowerCase()};
var size = size || 80;
return 'https://' + Poi.gravatar_url + '/' + MD5(email) + '.jpg?s=' + size + '&d=mm';
}
// sync css
!function(e){"use strict";var n=function(n,t,o){var l,r=e.document,i=r.createElement("link");if(t)l=t;else{var a=(r.body||r.getElementsByTagName("head")[0]).childNodes;l=a[a.length-1]}var d=r.styleSheets;i.rel="stylesheet",i.href=n,i.media="only x",l.parentNode.insertBefore(i,t?l:l.nextSibling);var f=function(e){for(var n=i.href,t=d.length;t--;)if(d[t].href===n)return e();setTimeout(function(){f(e)})};return i.onloadcssdefined=f,f(function(){i.media=o||"all"}),i};"undefined"!=typeof module?module.exports=n:e.loadCSS=n}("undefined"!=typeof global?global:this);

1
cdn/js/src/12.tocbot.min.js vendored 100644

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

7
cdn/js/src/18.clipboard.min.js vendored 100644

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,929 @@
.entry-content blockquote:before,.entry-content blockquote:after {
display: none
}
.entry-content {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
line-height: 1.5;
color: #24292e;
/*font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";*/
font-size: 16px;
line-height: 1.5;
word-wrap: break-word;
}
.entry-content .pl-c {
color: #6a737d;
}
.entry-content .pl-c1,
.entry-content .pl-s .pl-v {
color: #005cc5;
}
.entry-content .pl-e,
.entry-content .pl-en {
color: #6f42c1;
}
.entry-content .pl-smi,
.entry-content .pl-s .pl-s1 {
color: #24292e;
}
.entry-content .pl-ent {
color: #22863a;
}
.entry-content .pl-k {
color: #d73a49;
}
.entry-content .pl-s,
.entry-content .pl-pds,
.entry-content .pl-s .pl-pse .pl-s1,
.entry-content .pl-sr,
.entry-content .pl-sr .pl-cce,
.entry-content .pl-sr .pl-sre,
.entry-content .pl-sr .pl-sra {
color: #032f62;
}
.entry-content .pl-v,
.entry-content .pl-smw {
color: #e36209;
}
.entry-content .pl-bu {
color: #b31d28;
}
.entry-content .pl-ii {
color: #fafbfc;
background-color: #b31d28;
}
.entry-content .pl-c2 {
color: #fafbfc;
background-color: #d73a49;
}
.entry-content .pl-c2::before {
content: "^M";
}
.entry-content .pl-sr .pl-cce {
font-weight: bold;
color: #22863a;
}
.entry-content .pl-ml {
color: #735c0f;
}
.entry-content .pl-mh,
.entry-content .pl-mh .pl-en,
.entry-content .pl-ms {
font-weight: bold;
color: #005cc5;
}
.entry-content .pl-mi {
font-style: italic;
color: #24292e;
}
.entry-content .pl-mb {
font-weight: bold;
color: #24292e;
}
.entry-content .pl-md {
color: #b31d28;
background-color: #ffeef0;
}
.entry-content .pl-mi1 {
color: #22863a;
background-color: #f0fff4;
}
.entry-content .pl-mc {
color: #e36209;
background-color: #ffebda;
}
.entry-content .pl-mi2 {
color: #f6f8fa;
background-color: #005cc5;
}
.entry-content .pl-mdr {
font-weight: bold;
color: #6f42c1;
}
.entry-content .pl-ba {
color: #586069;
}
.entry-content .pl-sg {
color: #959da5;
}
.entry-content .pl-corl {
text-decoration: underline;
color: #032f62;
}
.entry-content .octicon {
display: inline-block;
vertical-align: text-top;
fill: currentColor;
}
.entry-content a {
background-color: transparent;
}
.entry-content a:active,
.entry-content a:hover {
outline-width: 0;
}
.entry-content strong {
font-weight: inherit;
}
.entry-content strong {
font-weight: bolder;
}
.entry-content h1 {
font-size: 2em;
margin: 0.67em 0;
}
.entry-content img {
border-style: none;
}
.entry-content code,
.entry-content kbd {
font-family: monospace, monospace;
font-size: 1em;
}
.entry-content hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
.entry-content input {
font: inherit;
margin: 0;
}
.entry-content input {
overflow: visible;
}
.entry-content [type="checkbox"] {
box-sizing: border-box;
padding: 0;
}
.entry-content * {
box-sizing: border-box;
}
.entry-content input {
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
.entry-content a {
color: #0366d6;
text-decoration: none;
}
.entry-content a:hover {
text-decoration: underline;
}
.entry-content strong {
font-weight: 600;
}
.entry-content hr {
height: 0;
margin: 15px 0;
overflow: hidden;
background: transparent;
border: 0;
border-bottom: 1px solid #dfe2e5;
}
.entry-content hr::before {
display: table;
content: "";
}
.entry-content hr::after {
display: table;
clear: both;
content: "";
}
.entry-content table {
border-spacing: 0;
border-collapse: collapse;
}
.entry-content td,
.entry-content th {
padding: 0;
}
.entry-content h1,
.entry-content h2,
.entry-content h3,
.entry-content h4,
.entry-content h5,
.entry-content h6 {
margin-top: 0;
margin-bottom: 0;
}
.entry-content h1 {
font-size: 32px;
font-weight: 600;
}
.entry-content h2 {
font-size: 24px;
font-weight: 600;
}
.entry-content h3 {
font-size: 20px;
font-weight: 600;
}
.entry-content h4 {
font-size: 16px;
font-weight: 600;
}
.entry-content h5 {
font-size: 14px;
font-weight: 600;
}
.entry-content h6 {
font-size: 12px;
font-weight: 600;
}
.entry-content p {
margin-top: 0;
margin-bottom: 10px;
}
.entry-content blockquote {
margin: 0;
}
.entry-content ul,
.entry-content ol {
padding-left: 0;
margin-top: 0;
margin-bottom: 0;
}
.entry-content ol ol,
.entry-content ul ol {
list-style-type: lower-roman;
}
.entry-content ul ul ol,
.entry-content ul ol ol,
.entry-content ol ul ol,
.entry-content ol ol ol {
list-style-type: lower-alpha;
}
.entry-content dd {
margin-left: 0;
}
.entry-content code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 12px;
}
.entry-content .octicon {
vertical-align: text-bottom;
}
.entry-content .pl-0 {
padding-left: 0 !important;
}
.entry-content .pl-1 {
padding-left: 4px !important;
}
.entry-content .pl-2 {
padding-left: 8px !important;
}
.entry-content .pl-3 {
padding-left: 16px !important;
}
.entry-content .pl-4 {
padding-left: 24px !important;
}
.entry-content .pl-5 {
padding-left: 32px !important;
}
.entry-content .pl-6 {
padding-left: 40px !important;
}
.entry-content::before {
display: table;
content: "";
}
.entry-content::after {
display: table;
clear: both;
content: "";
}
.entry-content>*:first-child {
margin-top: 0 !important;
}
.entry-content>*:last-child {
margin-bottom: 0 !important;
}
.entry-content a:not([href]) {
color: inherit;
text-decoration: none;
}
.entry-content .anchor {
float: left;
padding-right: 4px;
margin-left: -20px;
line-height: 1;
}
.entry-content .anchor:focus {
outline: none;
}
.entry-content p,
.entry-content blockquote,
.entry-content ul,
.entry-content ol,
.entry-content dl,
.entry-content table {
margin-top: 0;
margin-bottom: 16px;
}
.entry-content hr {
height: 0.25em;
padding: 0;
margin: 24px 0;
background-color: #e1e4e8;
border: 0;
}
.entry-content blockquote {
padding: 0 1em;
color: #6a737d;
border-left: 0.25em solid #dfe2e5;
}
.entry-content blockquote>:first-child {
margin-top: 0;
}
.entry-content blockquote>:last-child {
margin-bottom: 0;
}
.entry-content kbd {
display: inline-block;
padding: 3px 5px;
font-size: 11px;
line-height: 10px;
color: #444d56;
vertical-align: middle;
background-color: #fafbfc;
border: solid 1px #c6cbd1;
border-bottom-color: #959da5;
border-radius: 3px;
box-shadow: inset 0 -1px 0 #959da5;
}
.entry-content h1,
.entry-content h2,
.entry-content h3,
.entry-content h4,
.entry-content h5,
.entry-content h6 {
margin-top: 24px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.25;
}
.entry-content h1 .octicon-link,
.entry-content h2 .octicon-link,
.entry-content h3 .octicon-link,
.entry-content h4 .octicon-link,
.entry-content h5 .octicon-link,
.entry-content h6 .octicon-link {
color: #1b1f23;
vertical-align: middle;
visibility: hidden;
}
.entry-content h1:hover .anchor,
.entry-content h2:hover .anchor,
.entry-content h3:hover .anchor,
.entry-content h4:hover .anchor,
.entry-content h5:hover .anchor,
.entry-content h6:hover .anchor {
text-decoration: none;
}
.entry-content h1:hover .anchor .octicon-link,
.entry-content h2:hover .anchor .octicon-link,
.entry-content h3:hover .anchor .octicon-link,
.entry-content h4:hover .anchor .octicon-link,
.entry-content h5:hover .anchor .octicon-link,
.entry-content h6:hover .anchor .octicon-link {
visibility: visible;
}
.entry-content h1 {
padding-bottom: 0.3em;
font-size: 2em;
border-bottom: 1px solid #eaecef;
}
.entry-content h2 {
padding-bottom: 0.3em;
font-size: 1.5em;
border-bottom: 1px solid #eaecef;
}
.entry-content h3 {
font-size: 1.25em;
}
.entry-content h4 {
font-size: 1em;
}
.entry-content h5 {
font-size: 0.875em;
}
.entry-content h6 {
font-size: 0.85em;
color: #6a737d;
}
.entry-content ul,
.entry-content ol {
padding-left: 2em;
}
.entry-content ul ul,
.entry-content ul ol,
.entry-content ol ol,
.entry-content ol ul {
margin-top: 0;
margin-bottom: 0;
}
.entry-content li {
word-wrap: break-all;
}
.entry-content li>p {
margin-top: 16px;
}
.entry-content li+li {
margin-top: 0.25em;
}
.entry-content dl {
padding: 0;
}
.entry-content dl dt {
padding: 0;
margin-top: 16px;
font-size: 1em;
font-style: italic;
font-weight: 600;
}
.entry-content dl dd {
padding: 0 16px;
margin-bottom: 16px;
}
.entry-content table {
display: block;
width: 100%;
overflow: auto;
}
.entry-content table th {
font-weight: 600;
}
.entry-content table th,
.entry-content table td {
padding: 6px 13px;
border: 1px solid #dfe2e5;
}
.entry-content table tr {
background-color: #fff;
border-top: 1px solid #c6cbd1;
}
.entry-content table tr:nth-child(2n) {
background-color: #f6f8fa;
}
.entry-content img {
max-width: 100%;
box-sizing: content-box;
background-color: #fff;
}
.entry-content img[align=right] {
padding-left: 20px;
}
.entry-content img[align=left] {
padding-right: 20px;
}
.entry-content code {
padding: 0.2em 0.4em;
margin: 0;
font-size: 85%;
background-color: rgba(27,31,35,0.05);
border-radius: 3px;
}
.entry-content .full-commit .btn-outline:not(:disabled):hover {
color: #005cc5;
border-color: #005cc5;
}
.entry-content kbd {
display: inline-block;
padding: 3px 5px;
font: 11px "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
line-height: 10px;
color: #444d56;
vertical-align: middle;
background-color: #fafbfc;
border: solid 1px #d1d5da;
border-bottom-color: #c6cbd1;
border-radius: 3px;
box-shadow: inset 0 -1px 0 #c6cbd1;
}
.entry-content :checked+.radio-label {
position: relative;
z-index: 1;
border-color: #0366d6;
}
.entry-content .task-list-item {
list-style-type: none;
}
.entry-content .task-list-item+.task-list-item {
margin-top: 3px;
}
.entry-content .task-list-item input {
margin: 0 0.2em 0.25em -1.6em;
vertical-align: middle;
}
.entry-content hr {
border-bottom-color: #eee;
}
/*fix mac code*/
.entry-content pre.highlight-wrap {
position: relative;
background: #21252b;
border-radius: 5px;
font: 15px/22px "Microsoft YaHei", Arial, Sans-Serif;
line-height: 1.6;
margin-bottom: 1.6em;
max-width: 100%;
overflow: auto;
text-shadow: none;
color: #000;
padding-top: 30px;
box-shadow: 0 10px 30px 0 rgba(0, 0, 0, .4)
}
.entry-content pre.highlight-wrap:before {
content: " ";
position: absolute;
-webkit-border-radius: 50%;
border-radius: 50%;
background: #fc625d;
width: 12px;
height: 12px;
left: 12px;
margin-top: -18px;
-webkit-box-shadow: 20px 0 #fdbc40, 40px 0 #35cd4b;
box-shadow: 20px 0 #fdbc40, 40px 0 #35cd4b;
z-index: 2
}
.entry-content pre.highlight-wrap .copy-code {
color: #fff;
position: absolute;
float: right;
right: 10px;
top: 0;
padding-top: 4px;
padding-right: 2px;
z-index: 2;
font-size: 16px;
}
.entry-content pre.highlight-wrap .copy-code:hover {
color: rgba(255,255,255,.5);
}
.entry-content pre .hljs-ln-line span::-moz-selection,
.entry-content pre .hljs-ln-line::-moz-selection {
background: #fff;
color: #21252b
}
.entry-content pre .hljs-ln-line span::selection,
.entry-content pre .hljs-ln-line::selection {
background: #fff;
color: #21252b
}
.entry-content pre.highlight-wrap code::-webkit-scrollbar {
height: 10x !important;
width: 10x !important;
background-color: #1d1f21 !important
}
.entry-content pre.highlight-wrap code::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3) !important;
border-radius: 10px !important;
background-color: #21252b !important
}
.entry-content pre.highlight-wrap code::-webkit-scrollbar-thumb {
border-radius: 10px !important;
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .5) !important;
background-color: #21252b !important
}
.entry-content pre table.hljs-ln::-webkit-scrollbar {
height: 10x !important;
width: 10x !important;
background-color: #1d1f21 !important;
}
.entry-content pre table.hljs-ln::-webkit-scrollbar-track {
background-color: #41454a;
}
.entry-content pre table.hljs-ln::-webkit-scrollbar-thumb {
border-radius: 10px !important;
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .5) !important;
background-color: #21252b !important;
}
.entry-content pre.highlight-wrap code {
background: #1d1f21;
color: #fff;
word-break: break-word;
font-family: 'Source Code Pro', monospace, Helvetica, Tahoma, Arial, STXihei, "STHeiti Light", "Microsoft YaHei", sans-serif;
padding: 2px;
text-shadow: none;
border-radius: 0 0 5px 5px;
cursor: url(https://cdn.jsdelivr.net/gh/moezx/cdn@3.1.9/img/Sakura/cursor/texto.cur), auto
}
.entry-content pre.highlight-wrap code[data-rel]:before {
color: #fff;
content: attr(data-rel);
height: 30px;
line-height: 30px;
background: #21252b;
font-size: 16px;
position: absolute;
margin-top: -30px;
left: 0;
width: 100%;
font-family: Ubuntu, sans-serif;
font-weight: 700;
padding: 0 80px;
text-indent: 15px;
text-align: center;
float: left;
z-index: 1;
border-radius: 5px 5px 0 0;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
pointer-events: none
}
.entry-content pre .hljs {
display: block;
overflow-x: auto;
padding: .5em;
color: #abb2bf;
background: rgba(254, 250, 199, .5);
border-radius: 10px
}
.entry-content pre .hljs-comment,
.entry-content pre .hljs-quote {
color: #888f96;
font-style: italic
}
.entry-content pre .hljs-doctag,
.entry-content pre .hljs-formula,
.entry-content pre .hljs-keyword {
color: #c678dd
}
.entry-content pre .hljs-deletion,
.entry-content pre .hljs-name,
.entry-content pre .hljs-section,
.entry-content pre .hljs-selector-tag,
.entry-content pre .hljs-subst {
color: #e06c75
}
.entry-content pre .hljs-literal {
color: #56b6c2
}
.entry-content pre .hljs-addition,
.entry-content pre .hljs-attribute,
.entry-content pre .hljs-meta-string,
.entry-content pre .hljs-regexp,
.entry-content pre .hljs-string {
color: #98c379
}
.entry-content pre .hljs-built_in,
.entry-content pre .hljs-class .hljs-title {
color: #e6c07b
}
.entry-content pre .hljs-attr,
.entry-content pre .hljs-number,
.entry-content pre .hljs-selector-attr,
.entry-content pre .hljs-selector-class,
.entry-content pre .hljs-selector-pseudo,
.entry-content pre .hljs-template-variable,
.entry-content pre .hljs-type,
.entry-content pre .hljs-variable {
color: #d19a66
}
.entry-content pre .hljs-bullet,
.entry-content pre .hljs-link,
.entry-content pre .hljs-meta,
.entry-content pre .hljs-selector-id,
.entry-content pre .hljs-symbol,
.entry-content pre .hljs-title {
color: #61aeee
}
.entry-content pre .hljs-emphasis {
font-style: italic
}
.entry-content pre .hljs-strong {
font-weight: 700
}
.entry-content pre .hljs-link {
text-decoration: underline
}
.entry-content pre .hljs-ln {
margin: 6px 0 0 0 !important
}
.entry-content pre td .hljs-ln-numbers {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
text-align: center;
color: #888f96;
background: #1d1f21;
font-family: 'Source Code Pro', monospace, Helvetica, Tahoma, Arial, STXihei, "STHeiti Light", "Microsoft YaHei", sans-serif;
vertical-align: top;
position: absolute;
left: 0;
width: 30px
}
.entry-content pre .hljs-ln-code .hljs-ln-line {
margin-left: 22px;
}
.entry-content pre .hljs-ln-code .hljs-ln-line:hover {
background-color: rgba(255, 255, 255, .1)
}
.entry-content pre .hljs-ln{border-collapse:collapse}
.entry-content pre .hljs-ln td{padding:0}
.entry-content pre .hljs-ln-n:before{content:attr(data-line-number)}
/*fix*/
.entry-content pre table tr {
background-color: transparent;
border-top: none;
}
.entry-content pre table th, .entry-content pre table td {
border: none;
}
.entry-content pre table tr {
background-color: transparent;
border-top: 0;
}
.entry-content pre table tr:nth-child(2n) {
background-color: transparent;
}
.code-block-fullscreen {
position: fixed !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
min-width: 100% !important;
z-index: 9999999 !important;
margin: 0 !important;
animation: elastic 1s !important
}
.code-block-fullscreen code {
--widthA: 100%;
--widthB: calc(var(--widthA) - 30px);
height: var(--widthB) !important;
min-height: 99% !important;
overflow-y: hidden !important;
overflow-x: auto !important;
height: auto !important
}
.code-block-fullscreen-html-scroll {
overflow: hidden !important
}
/*other fix*/
.entry-content ol,.entry-content ul {
margin: auto !important;
}

View File

@ -0,0 +1,399 @@
.entry-content blockquote:before,.entry-content blockquote:after {
display: block
}
.entry-content blockquote blockquote:before,.entry-content blockquote blockquote:after {
display: none
}
.entry-content .begin,
.single-begin {
float: left;
font-size: 3.6em;
line-height: 1em;
margin-right: 3px;
margin-top: 2px;
font-weight: 700
}
@media screen and (max-width:860px) {
.entry-content .begin,
.single-begin {
margin-top: 6px
}
}
.entry-content ul {
list-style: disc;
border: 1px dashed #E4E4E4;
padding: 15px 10px 15px 50px;
color: #616161;
margin-left: 0;
border-radius: 10px
}
.entry-content ul ul {
border: none;
padding: 15px 10px 15px 30px;
}
.entry-content ol {
list-style: decimal;
border: 1px dashed #E4E4E4;
padding: 15px 10px 15px 50px;
color: #616161;
margin-left: 0;
border-radius: 10px
}
.entry-content ol li,
.entry-content ul li {
padding: 8px 0
}
.entry-content {
position: relative
}
.entry-content h3, .entry-content h4, .entry-content h5 {
padding-left: 16px;
}
.entry-content h3 {
padding-bottom: 8px;
border-bottom: 1px dashed #ddd;
color: #737373
}
.entry-content h3:after {
content: "#";
position: absolute;
left: 0;
color: #FF6D6D
}
.entry-content h4:after {
content: ">";
position: absolute;
left: 0;
color: #FF6D6D
}
.entry-content h5:after {
content: "?";
position: absolute;
left: 0;
color: #FF6D6D
}
.entry-content code {
background: rgba(254, 250, 199, 1);
color: #E67474;
word-break: break-word;
font-family: 'Source Code Pro', monospace, Helvetica, Tahoma, Arial, STXihei, "STHeiti Light", "Microsoft YaHei", sans-serif;
padding: 2px;
text-shadow: none;
border-radius: 5px
}
.entry-content a {
color: #e67474;
text-decoration: underline dotted rgba(0, 0, 0, .1)
}
.entry-content a:hover {
color: orange;
text-decoration: underline orange
}
h1.entry-title {
font-size: 24px;
font-weight: 300
}
.entry-content p {
color: #797979
}
.entry-content p {
line-height: 30px
}
.entry-content hr {
margin-top: 40px;
margin-bottom: 40px;
display: block;
border: 0;
text-align: center;
background: 0 0
}
.entry-content hr:before {
content: '...';
display: inline-block;
margin-left: .6em;
color: rgba(0, 0, 0, .8);
position: relative;
top: -30px;
font-size: 28px;
letter-spacing: .6em
}
.entry-content table {
border-collapse: collapse;
width: 100%;
border-radius: 5px
}
.entry-content th, .entry-content td {
padding: 8px;
}
.entry-content tr:nth-child(even){background-color: #f2f2f2}
body.dark .entry-content tr:nth-child(even){background-color: unset}
.entry-content th {
color: white;
}
/*fix code block*/
.entry-content pre th, .entry-content pre td {
padding: 0;
}
.entry-content pre tr:nth-child(even){
background-color: transparent
}
.entry-content pre th {
background-color: transparent !important;
}
.highlight-wrap:before {
content: " ";
position: absolute;
-webkit-border-radius: 50%;
border-radius: 50%;
background: #fc625d;
width: 12px;
height: 12px;
left: 12px;
margin-top: -18px;
-webkit-box-shadow: 20px 0 #fdbc40, 40px 0 #35cd4b;
box-shadow: 20px 0 #fdbc40, 40px 0 #35cd4b;
z-index: 2
}
.highlight-wrap {
position: relative;
background: #21252b;
border-radius: 5px;
font: 15px/22px "Microsoft YaHei", Arial, Sans-Serif;
line-height: 1.6;
margin-bottom: 1.6em;
max-width: 100%;
overflow: auto;
text-shadow: none;
color: #000;
padding-top: 30px;
box-shadow: 0 10px 30px 0 rgba(0, 0, 0, .4)
}
.highlight-wrap .copy-code {
color: #fff;
position: absolute;
float: right;
right: 10px;
top: 0;
padding-top: 4px;
padding-right: 2px;
z-index: 2;
font-size: 16px;
}
.highlight-wrap .copy-code:hover {
color: rgba(255,255,255,.5);
}
.hljs-ln-line span::-moz-selection,
.hljs-ln-line::-moz-selection {
background: #fff;
color: #21252b
}
.hljs-ln-line span::selection,
.hljs-ln-line::selection {
background: #fff;
color: #21252b
}
.highlight-wrap code {
background: #1d1f21;
color: #fff;
word-break: break-word;
font-family: 'Source Code Pro', monospace, Helvetica, Tahoma, Arial, STXihei, "STHeiti Light", "Microsoft YaHei", sans-serif;
padding: 2px;
text-shadow: none;
border-radius: 0 0 5px 5px;
cursor: url(https://cdn.jsdelivr.net/gh/moezx/cdn@3.1.9/img/Sakura/cursor/texto.cur), auto
}
.highlight-wrap code[data-rel]:before {
color: #fff;
content: attr(data-rel);
height: 30px;
line-height: 30px;
background: #21252b;
font-size: 16px;
position: absolute;
margin-top: -30px;
left: 0;
width: 100%;
font-family: Ubuntu, sans-serif;
font-weight: 700;
padding: 0 80px;
text-indent: 15px;
text-align: center;
float: left;
z-index: 1;
border-radius: 5px 5px 0 0;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
pointer-events: none
}
.hljs {
display: block;
overflow-x: auto;
padding: .5em;
color: #abb2bf;
background: rgba(254, 250, 199, .5);
border-radius: 10px
}
.hljs-comment,
.hljs-quote {
color: #888f96;
font-style: italic
}
.hljs-doctag,
.hljs-formula,
.hljs-keyword {
color: #c678dd
}
.hljs-deletion,
.hljs-name,
.hljs-section,
.hljs-selector-tag,
.hljs-subst {
color: #e06c75
}
.hljs-literal {
color: #56b6c2
}
.hljs-addition,
.hljs-attribute,
.hljs-meta-string,
.hljs-regexp,
.hljs-string {
color: #98c379
}
.hljs-built_in,
.hljs-class .hljs-title {
color: #e6c07b
}
.hljs-attr,
.hljs-number,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-pseudo,
.hljs-template-variable,
.hljs-type,
.hljs-variable {
color: #d19a66
}
.hljs-bullet,
.hljs-link,
.hljs-meta,
.hljs-selector-id,
.hljs-symbol,
.hljs-title {
color: #61aeee
}
.hljs-emphasis {
font-style: italic
}
.hljs-strong {
font-weight: 700
}
.hljs-link {
text-decoration: underline
}
.hljs-ln {
margin: 6px 0 0 0 !important
}
td.hljs-ln-numbers {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
text-align: center;
color: #888f96;
background: #1d1f21;
font-family: 'Source Code Pro', monospace, Helvetica, Tahoma, Arial, STXihei, "STHeiti Light", "Microsoft YaHei", sans-serif;
vertical-align: top;
position: absolute;
left: 0;
width: 30px
}
.hljs-ln-code .hljs-ln-line {
margin-left: 25px;
padding-left: 5px
}
.hljs-ln-code .hljs-ln-line:hover {
background-color: rgba(255, 255, 255, .1)
}
.code-block-fullscreen {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
min-width: 100%;
z-index: 9999999;
margin: 0;
animation: elastic 1s
}
.code-block-fullscreen code {
--widthA: 100%;
--widthB: calc(var(--widthA) - 30px);
height: var(--widthB);
min-height: 99%;
overflow-y: hidden;
overflow-x: auto;
height: auto
}
.code-block-fullscreen-html-scroll {
overflow: hidden
}

Some files were not shown because too many files have changed in this diff Show More