syj注释 #2

Merged
pqnvcz97o merged 4 commits from xjf_branch into develop 5 months ago

8
.idea/.gitignore vendored

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.12" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12" project-jdk-type="Python SDK" />
</project>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/zyd2025.iml" filepath="$PROJECT_DIR$/.idea/zyd2025.iml" />
</modules>
</component>
</project>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="FacetManager">
<facet type="django" name="Django">
<configuration>
<option name="rootFolder" value="$MODULE_DIR$/src" />
<option name="settingsModule" value="settings.py" />
<option name="manageScript" value="$MODULE_DIR$/src/manage.py" />
<option name="environment" value="&lt;map/&gt;" />
<option name="doNotUseTestRunner" value="false" />
<option name="trackFilePattern" value="migrations" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="jdk" jdkName="Python 3.12" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
<component name="TemplatesService">
<option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
<option name="TEMPLATE_FOLDERS">
<list>
<option value="$MODULE_DIR$/src/templates" />
</list>
</option>
</component>
</module>

@ -146,7 +146,7 @@ python manage.py runserver
## 🙏 鸣谢
特别感谢 **JetBrains** 为本项目提供的免费开源许可证。gs ls syj143 zyd164
特别感谢 **JetBrains** 为本项目提供的免费开源许可证。gs ls syj zyd164
<p align="center">
<a href="https://www.jetbrains.com/?from=DjangoBlog">

@ -0,0 +1,688 @@
/*
*
*/
/* Google 图标样式 */
.icon-sn-google {
background-position: 0 -28px;
}
.icon-sn-bg-google {
background-color: #4285f4;
background-position: 0 0;
}
.fa-sn-google {
color: #4285f4;
}
/* GitHub 图标样式 */
.icon-sn-github {
background-position: -28px -28px;
}
.icon-sn-bg-github {
background-color: #333;
background-position: -28px 0;
}
.fa-sn-github {
color: #333;
}
/* 微博图标样式 */
.icon-sn-weibo {
background-position: -56px -28px;
}
.icon-sn-bg-weibo {
background-color: #e90d24;
background-position: -56px 0;
}
.fa-sn-weibo {
color: #e90d24;
}
/* QQ 图标样式 */
.icon-sn-qq {
background-position: -84px -28px;
}
.icon-sn-bg-qq {
background-color: #0098e6;
background-position: -84px 0;
}
.fa-sn-qq {
color: #0098e6;
}
/* Twitter 图标样式 */
.icon-sn-twitter {
background-position: -112px -28px;
}
.icon-sn-bg-twitter {
background-color: #50abf1;
background-position: -112px 0;
}
.fa-sn-twitter {
color: #50abf1;
}
/* Facebook 图标样式 */
.icon-sn-facebook {
background-position: -140px -28px;
}
.icon-sn-bg-facebook {
background-color: #4862a3;
background-position: -140px 0;
}
.fa-sn-facebook {
color: #4862a3;
}
/* 人人网图标样式 */
.icon-sn-renren {
background-position: -168px -28px;
}
.icon-sn-bg-renren {
background-color: #197bc8;
background-position: -168px 0;
}
.fa-sn-renren {
color: #197bc8;
}
/* 腾讯微博图标样式 */
.icon-sn-tqq {
background-position: -196px -28px;
}
.icon-sn-bg-tqq {
background-color: #1f9ed2;
background-position: -196px 0;
}
.fa-sn-tqq {
color: #1f9ed2;
}
/* 豆瓣图标样式 */
.icon-sn-douban {
background-position: -224px -28px;
}
.icon-sn-bg-douban {
background-color: #279738;
background-position: -224px 0;
}
.fa-sn-douban {
color: #279738;
}
/* 微信图标样式 */
.icon-sn-weixin {
background-position: -252px -28px;
}
.icon-sn-bg-weixin {
background-color: #00b500;
background-position: -252px 0;
}
.fa-sn-weixin {
color: #00b500;
}
/* 虚线图标样式 */
.icon-sn-dotted {
background-position: -280px -28px;
}
.icon-sn-bg-dotted {
background-color: #eee;
background-position: -280px 0;
}
.fa-sn-dotted {
color: #eee;
}
/* 站点图标样式 */
.icon-sn-site {
background-position: -308px -28px;
}
.icon-sn-bg-site {
background-color: #00b500;
background-position: -308px 0;
}
.fa-sn-site {
color: #00b500;
}
/* LinkedIn 图标样式 */
.icon-sn-linkedin {
background-position: -336px -28px;
}
.icon-sn-bg-linkedin {
background-color: #0077b9;
background-position: -336px 0;
}
.fa-sn-linkedin {
color: #0077b9;
}
/* 通用图标样式 */
[class*=icon-sn-] {
display: inline-block;
background-image: url('/static/blog/img/icon-sn.svg?56272f05e520');
background-repeat: no-repeat;
width: 28px;
height: 28px;
vertical-align: middle;
background-size: auto 56px;
}
[class*=icon-sn-]:hover {
opacity: .8;
filter: alpha(opacity=80);
}
/*
*
*/
/* Google 按钮样式 */
.btn-sn-google {
background: #4285f4;
}
.btn-sn-google:active,
.btn-sn-google:focus,
.btn-sn-google:hover {
background: #2a75f3;
}
/* GitHub 按钮样式 */
.btn-sn-github {
background: #333;
}
.btn-sn-github:active,
.btn-sn-github:focus,
.btn-sn-github:hover {
background: #262626;
}
/* 微博按钮样式 */
.btn-sn-weibo {
background: #e90d24;
}
.btn-sn-weibo:active,
.btn-sn-weibo:focus,
.btn-sn-weibo:hover {
background: #d10c20;
}
/* QQ 按钮样式 */
.btn-sn-qq {
background: #0098e6;
}
.btn-sn-qq:active,
.btn-sn-qq:focus,
.btn-sn-qq:hover {
background: #0087cd;
}
/* Twitter 按钮样式 */
.btn-sn-twitter {
background: #50abf1;
}
.btn-sn-twitter:active,
.btn-sn-twitter:focus,
.btn-sn-twitter:hover {
background: #38a0ef;
}
/* Facebook 按钮样式 */
.btn-sn-facebook {
background: #4862a3;
}
.btn-sn-facebook:active,
.btn-sn-facebook:focus,
.btn-sn-facebook:hover {
background: #405791;
}
/* 人人网按钮样式 */
.btn-sn-renren {
background: #197bc8;
}
.btn-sn-renren:active,
.btn-sn-renren:focus,
.btn-sn-renren:hover {
background: #166db1;
}
/* 腾讯微博按钮样式 */
.btn-sn-tqq {
background: #1f9ed2;
}
.btn-sn-tqq:active,
.btn-sn-tqq:focus,
.btn-sn-tqq:hover {
background: #1c8dbc;
}
/* 豆瓣按钮样式 */
.btn-sn-douban {
background: #279738;
}
.btn-sn-douban:active,
.btn-sn-douban:focus,
.btn-sn-douban:hover {
background: #228330;
}
/* 微信按钮样式 */
.btn-sn-weixin {
background: #00b500;
}
.btn-sn-weixin:active,
.btn-sn-weixin:focus,
.btn-sn-weixin:hover {
background: #009c00;
}
/* 虚线按钮样式 */
.btn-sn-dotted {
background: #eee;
}
.btn-sn-dotted:active,
.btn-sn-dotted:focus,
.btn-sn-dotted:hover {
background: #e1e1e1;
}
/* 站点按钮样式 */
.btn-sn-site {
background: #00b500;
}
.btn-sn-site:active,
.btn-sn-site:focus,
.btn-sn-site:hover {
background: #009c00;
}
/* LinkedIn 按钮样式 */
.btn-sn-linkedin {
background: #0077b9;
}
.btn-sn-linkedin:active,
.btn-sn-linkedin:focus,
.btn-sn-linkedin:hover {
background: #0067a0;
}
/* 通用按钮样式 */
[class*=btn-sn-],
[class*=btn-sn-]:active,
[class*=btn-sn-]:focus,
[class*=btn-sn-]:hover {
border: none;
color: #fff;
}
.btn-sn-more {
padding: 0;
}
.btn-sn-more,
.btn-sn-more:active,
.btn-sn-more:hover {
box-shadow: none;
}
[class*=btn-sn-] [class*=icon-sn-] {
background-color: transparent;
}
/*
*
*/
.codehilite .hll {
background-color: #ffffcc;
}
.codehilite {
background: #ffffff;
}
.codehilite .c {
color: #177500;
}
.codehilite .err {
color: #000000;
}
.codehilite .k {
color: #A90D91;
}
.codehilite .l {
color: #1C01CE;
}
.codehilite .n {
color: #000000;
}
.codehilite .o {
color: #000000;
}
.codehilite .ch {
color: #177500;
}
.codehilite .cm {
color: #177500;
}
.codehilite .cp {
color: #633820;
}
.codehilite .cpf {
color: #177500;
}
.codehilite .c1 {
color: #177500;
}
.codehilite .cs {
color: #177500;
}
.codehilite .kc {
color: #A90D91;
}
.codehilite .kd {
color: #A90D91;
}
.codehilite .kn {
color: #A90D91;
}
.codehilite .kp {
color: #A90D91;
}
.codehilite .kr {
color: #A90D91;
}
.codehilite .kt {
color: #A90D91;
}
.codehilite .ld {
color: #1C01CE;
}
.codehilite .m {
color: #1C01CE;
}
.codehilite .s {
color: #C41A16;
}
.codehilite .na {
color: #836C28;
}
.codehilite .nb {
color: #A90D91;
}
.codehilite .nc {
color: #3F6E75;
}
.codehilite .no {
color: #000000;
}
.codehilite .nd {
color: #000000;
}
.codehilite .ni {
color: #000000;
}
.codehilite .ne {
color: #000000;
}
.codehilite .nf {
color: #000000;
}
.codehilite .nl {
color: #000000;
}
.codehilite .nn {
color: #000000;
}
.codehilite .nx {
color: #000000;
}
.codehilite .py {
color: #000000;
}
.codehilite .nt {
color: #000000;
}
.codehilite .nv {
color: #000000;
}
.codehilite .ow {
color: #000000;
}
.codehilite .mb {
color: #1C01CE;
}
.codehilite .mf {
color: #1C01CE;
}
.codehilite .mh {
color: #1C01CE;
}
.codehilite .mi {
color: #1C01CE;
}
.codehilite .mo {
color: #1C01CE;
}
.codehilite .sb {
color: #C41A16;
}
.codehilite .sc {
color: #2300CE;
}
.codehilite .sd {
color: #C41A16;
}
.codehilite .s2 {
color: #C41A16;
}
.codehilite .se {
color: #C41A16;
}
.codehilite .sh {
color: #C41A16;
}
.codehilite .si {
color: #C41A16;
}
.codehilite .sx {
color: #C41A16;
}
.codehilite .sr {
color: #C41A16;
}
.codehilite .s1 {
color: #C41A16;
}
.codehilite .ss {
color: #C41A16;
}
.codehilite .bp {
color: #5B269A;
}
.codehilite .vc {
color: #000000;
}
.codehilite .vg {
color: #000000;
}
.codehilite .vi {
color: #000000;
}
.codehilite .il {
color: #1C01CE;
}
/*
* (NProgress)
*/
#nprogress {
pointer-events: none;
}
#nprogress .bar {
background: red;
position: fixed;
z-index: 1031;
top: 0;
left: 0;
width: 100%;
height: 2px;
}
#nprogress .peg {
display: block;
position: absolute;
right: 0px;
width: 100px;
height: 100%;
box-shadow: 0 0 10px #29d, 0 0 5px #29d;
opacity: 1.0;
-webkit-transform: rotate(3deg) translate(0px, -4px);
-ms-transform: rotate(3deg) translate(0px, -4px);
transform: rotate(3deg) translate(0px, -4px);
}
#nprogress .spinner {
display: block;
position: fixed;
z-index: 1031;
top: 15px;
right: 15px;
}
#nprogress .spinner-icon {
width: 18px;
height: 18px;
box-sizing: border-box;
border: solid 2px transparent;
border-top-color: red;
border-left-color: red;
border-radius: 50%;
-webkit-animation: nprogress-spinner 400ms linear infinite;
animation: nprogress-spinner 400ms linear infinite;
}
.nprogress-custom-parent {
overflow: hidden;
position: relative;
}
.nprogress-custom-parent #nprogress .spinner,
.nprogress-custom-parent #nprogress .bar {
position: absolute;
}
@-webkit-keyframes nprogress-spinner {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
@keyframes nprogress-spinner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

File diff suppressed because it is too large Load Diff

@ -1,9 +1,25 @@
/* 按钮样式 */
/* Button styles */
.button {
/* 移除边框 */
/* Remove border */
border: none;
/* 设置内边距 */
/* Set padding */
padding: 4px 80px;
/* 设置文本对齐方式 */
/* Set text alignment */
text-align: center;
/* 移除文本装饰 */
/* Remove text decoration */
text-decoration: none;
/* 设置为行内块元素 */
/* Set as inline-block element */
display: inline-block;
/* 设置字体大小 */
/* Set font size */
font-size: 16px;
/* 设置外边距 */
/* Set margin */
margin: 4px 2px;
}

@ -1,15 +1,35 @@
// 设置倒计时初始值为60秒
// Set initial countdown value to 60 seconds
let wait = 60;
// 倒计时函数,用于在发送验证码按钮上显示倒计时
// Countdown function to display countdown on the send verification code button
function time(o) {
// 如果倒计时结束
// If countdown is over
if (wait == 0) {
// 移除按钮的禁用状态
// Remove button disabled state
o.removeAttribute("disabled");
// 恢复按钮文本
// Restore button text
o.value = "获取验证码";
// 重置倒计时
// Reset countdown
wait = 60
return false
} else {
// 设置按钮为禁用状态
// Set button to disabled state
o.setAttribute("disabled", true);
// 更新按钮文本显示倒计时
// Update button text to show countdown
o.value = "重新发送(" + wait + ")";
// 倒计时减1
// Decrease countdown by 1
wait--;
// 1秒后递归调用此函数
// Recursively call this function after 1 second
setTimeout(function () {
time(o)
},
@ -17,31 +37,65 @@ function time(o) {
}
}
// 获取验证码按钮点击事件处理
// Verification code button click event handler
document.getElementById("btn").onclick = function () {
// 获取邮箱输入框元素
// Get email input element
let id_email = $("#id_email")
// 获取CSRF令牌
// Get CSRF token
let token = $("*[name='csrfmiddlewaretoken']").val()
// 获取当前按钮元素
// Get current button element
let ts = this
// 获取错误信息显示元素
// Get error message display element
let myErr = $("#myErr")
// 发送AJAX请求
// Send AJAX request
$.ajax(
{
// 请求URL
// Request URL
url: "/forget_password_code/",
// 请求类型
// Request type
type: "POST",
// 请求数据
// Request data
data: {
"email": id_email.val(),
"csrfmiddlewaretoken": token
},
// 请求成功回调函数
// Success callback function
success: function (result) {
// 如果返回结果不是"ok"
// If the returned result is not "ok"
if (result != "ok") {
// 移除旧的错误信息
// Remove old error message
myErr.remove()
// 在邮箱输入框后添加新的错误信息
// Add new error message after email input
id_email.after("<ul className='errorlist' id='myErr'><li>" + result + "</li></ul>")
return
}
// 移除错误信息
// Remove error message
myErr.remove()
// 启动倒计时
// Start countdown
time(ts)
},
// 请求失败回调函数
// Error callback function
error: function (e) {
// 弹出发送失败提示
// Show send failure alert
alert("发送失败,请重试")
}
}
);
}
}

@ -1,54 +1,68 @@
/* 管理界面自动补全样式 */
/* Admin autocomplete styles */
/* 选择器宽度设置 */
select.admin-autocomplete {
width: 20em;
}
/* 自动补全容器最小高度设置 */
.select2-container--admin-autocomplete.select2-container {
min-height: 30px;
}
/* 单选和多选容器最小高度设置 */
.select2-container--admin-autocomplete .select2-selection--single,
.select2-container--admin-autocomplete .select2-selection--multiple {
min-height: 30px;
padding: 0;
}
/* 聚焦或打开状态下的选择器边框颜色 */
.select2-container--admin-autocomplete.select2-container--focus .select2-selection,
.select2-container--admin-autocomplete.select2-container--open .select2-selection {
border-color: var(--body-quiet-color);
min-height: 30px;
}
/* 聚焦或打开状态下单选选择器内边距 */
.select2-container--admin-autocomplete.select2-container--focus .select2-selection.select2-selection--single,
.select2-container--admin-autocomplete.select2-container--open .select2-selection.select2-selection--single {
padding: 0;
}
/* 聚焦或打开状态下多选选择器内边距 */
.select2-container--admin-autocomplete.select2-container--focus .select2-selection.select2-selection--multiple,
.select2-container--admin-autocomplete.select2-container--open .select2-selection.select2-selection--multiple {
padding: 0;
}
/* 单选选择器背景、边框和圆角设置 */
.select2-container--admin-autocomplete .select2-selection--single {
background-color: var(--body-bg);
border: 1px solid var(--border-color);
border-radius: 4px;
}
/* 单选选择器渲染内容颜色和行高设置 */
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__rendered {
color: var(--body-fg);
line-height: 30px;
}
/* 单选选择器清除按钮光标和浮动设置 */
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
}
/* 单选选择器占位符颜色设置 */
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__placeholder {
color: var(--body-quiet-color);
}
/* 单选选择器箭头高度、位置和宽度设置 */
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__arrow {
height: 26px;
position: absolute;
@ -57,6 +71,7 @@ select.admin-autocomplete {
width: 20px;
}
/* 单选选择器箭头图标样式设置 */
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__arrow b {
border-color: #888 transparent transparent transparent;
border-style: solid;
@ -70,29 +85,35 @@ select.admin-autocomplete {
width: 0;
}
/* 从右到左语言环境下单选选择器清除按钮浮动设置 */
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--single .select2-selection__clear {
float: left;
}
/* 从右到左语言环境下单选选择器箭头位置设置 */
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--single .select2-selection__arrow {
left: 1px;
right: auto;
}
/* 禁用状态下单选选择器背景和光标设置 */
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--single {
background-color: var(--darkened-bg);
cursor: default;
}
/* 禁用状态下单选选择器清除按钮显示设置 */
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--single .select2-selection__clear {
display: none;
}
/* 打开状态下单选选择器箭头图标样式设置 */
.select2-container--admin-autocomplete.select2-container--open .select2-selection--single .select2-selection__arrow b {
border-color: transparent transparent #888 transparent;
border-width: 0 4px 5px 4px;
}
/* 多选选择器背景、边框、圆角和光标设置 */
.select2-container--admin-autocomplete .select2-selection--multiple {
background-color: var(--body-bg);
border: 1px solid var(--border-color);
@ -100,6 +121,7 @@ select.admin-autocomplete {
cursor: text;
}
/* 多选选择器渲染内容盒模型、列表样式、外边距、内边距和宽度设置 */
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__rendered {
box-sizing: border-box;
list-style: none;
@ -110,16 +132,19 @@ select.admin-autocomplete {
flex-wrap: wrap;
}
/* 多选选择器渲染内容列表项样式设置 */
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__rendered li {
list-style: none;
}
/* 多选选择器占位符颜色、上外边距和浮动设置 */
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__placeholder {
color: var(--body-quiet-color);
margin-top: 5px;
float: left;
}
/* 多选选择器清除按钮光标、浮动、字体粗细、外边距、位置和右边距设置 */
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__clear {
cursor: pointer;
float: right;
@ -129,6 +154,7 @@ select.admin-autocomplete {
right: 0;
}
/* 多选选择器选项背景、边框、圆角、光标、浮动、右边距、上外边距和内边距设置 */
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice {
background-color: var(--darkened-bg);
border: 1px solid var(--border-color);
@ -140,6 +166,7 @@ select.admin-autocomplete {
padding: 0 5px;
}
/* 多选选择器选项移除按钮颜色、光标、显示、字体粗细和右边距设置 */
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove {
color: var(--body-quiet-color);
cursor: pointer;
@ -148,52 +175,63 @@ select.admin-autocomplete {
margin-right: 2px;
}
/* 多选选择器选项移除按钮悬停状态颜色设置 */
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove:hover {
color: var(--body-fg);
}
/* 从右到左语言环境下多选选择器选项、占位符和内联搜索浮动设置 */
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-search--inline {
float: right;
}
/* 从右到左语言环境下多选选择器选项左边距和右边距设置 */
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
margin-left: 5px;
margin-right: auto;
}
/* 从右到左语言环境下多选选择器选项移除按钮左边距和右边距设置 */
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
margin-left: 2px;
margin-right: auto;
}
/* 聚焦状态下多选选择器边框设置 */
.select2-container--admin-autocomplete.select2-container--focus .select2-selection--multiple {
border: solid var(--body-quiet-color) 1px;
outline: 0;
}
/* 禁用状态下多选选择器背景和光标设置 */
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--multiple {
background-color: var(--darkened-bg);
cursor: default;
}
/* 禁用状态下多选选择器选项移除按钮显示设置 */
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection__choice__remove {
display: none;
}
/* 打开状态下选择器上边框圆角设置 */
.select2-container--admin-autocomplete.select2-container--open.select2-container--above .select2-selection--single, .select2-container--admin-autocomplete.select2-container--open.select2-container--above .select2-selection--multiple {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
/* 打开状态下选择器下边框圆角设置 */
.select2-container--admin-autocomplete.select2-container--open.select2-container--below .select2-selection--single, .select2-container--admin-autocomplete.select2-container--open.select2-container--below .select2-selection--multiple {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
/* 下拉搜索框背景设置 */
.select2-container--admin-autocomplete .select2-search--dropdown {
background: var(--darkened-bg);
}
/* 下拉搜索框字段背景、颜色、边框、圆角设置 */
.select2-container--admin-autocomplete .select2-search--dropdown .select2-search__field {
background: var(--body-bg);
color: var(--body-fg);
@ -201,6 +239,7 @@ select.admin-autocomplete {
border-radius: 4px;
}
/* 内联搜索框字段背景、颜色、边框、轮廓和外观设置 */
.select2-container--admin-autocomplete .select2-search--inline .select2-search__field {
background: transparent;
color: var(--body-fg);
@ -210,6 +249,7 @@ select.admin-autocomplete {
-webkit-appearance: textfield;
}
/* 结果选项最大高度、溢出和颜色设置 */
.select2-container--admin-autocomplete .select2-results > .select2-results__options {
max-height: 200px;
overflow-y: auto;
@ -217,63 +257,76 @@ select.admin-autocomplete {
background: var(--body-bg);
}
/* 分组结果选项内边距设置 */
.select2-container--admin-autocomplete .select2-results__option[role=group] {
padding: 0;
}
/* 禁用结果选项颜色设置 */
.select2-container--admin-autocomplete .select2-results__option[aria-disabled=true] {
color: var(--body-quiet-color);
}
/* 选中结果选项背景和颜色设置 */
.select2-container--admin-autocomplete .select2-results__option[aria-selected=true] {
background-color: var(--selected-bg);
color: var(--body-fg);
}
/* 结果选项嵌套内边距设置 */
.select2-container--admin-autocomplete .select2-results__option .select2-results__option {
padding-left: 1em;
}
/* 结果选项嵌套分组内边距设置 */
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__group {
padding-left: 0;
}
/* 结果选项嵌套第二层内边距和左边距设置 */
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option {
margin-left: -1em;
padding-left: 2em;
}
/* 结果选项嵌套第三层内边距和左边距设置 */
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -2em;
padding-left: 3em;
}
/* 结果选项嵌套第四层内边距和左边距设置 */
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -3em;
padding-left: 4em;
}
/* 结果选项嵌套第五层内边距和左边距设置 */
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -4em;
padding-left: 5em;
}
/* 结果选项嵌套第六层内边距和左边距设置 */
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -5em;
padding-left: 6em;
}
/* 高亮结果选项背景和颜色设置 */
.select2-container--admin-autocomplete .select2-results__option--highlighted[aria-selected] {
background-color: var(--primary);
color: var(--primary-fg);
}
/* 分组结果选项光标、显示、内边距设置 */
.select2-container--admin-autocomplete .select2-results__group {
cursor: default;
display: block;
padding: 6px;
}
/* 错误状态下选择器边框设置 */
.errors .select2-selection {
border: 1px solid var(--error-fg);
}
}

@ -1,7 +1,9 @@
/*
DJANGO
DJANGO Admin styles
*/
/* 变量定义 */
/* VARIABLE DEFINITIONS */
html[data-theme="light"],
:root {
@ -38,8 +40,8 @@ html[data-theme="light"],
--message-warning-bg: #ffc;
--message-error-bg: #ffefef;
--darkened-bg: #f8f8f8; /* A bit darker than --body-bg */
--selected-bg: #e4e4e4; /* E.g. selected table cells */
--darkened-bg: #f8f8f8; /* 比 --body-bg 稍暗一点 */
--selected-bg: #e4e4e4; /* 例如选中的表格单元格 */
--selected-row: #ffc;
--button-fg: #fff;
@ -102,6 +104,7 @@ body {
background: var(--body-bg);
}
/* 链接 */
/* LINKS */
a:link, a:visited {
@ -131,6 +134,7 @@ a.section:focus, a.section:hover {
text-decoration: underline;
}
/* 全局默认样式 */
/* GLOBAL DEFAULTS */
p, ol, ul, dl {
@ -260,6 +264,7 @@ hr {
line-height: 1px;
}
/* 文本样式和修饰符 */
/* TEXT STYLES & MODIFIERS */
.small {
@ -304,6 +309,7 @@ p img, h1 img, h2 img, h3 img, h4 img, td img {
display: none !important;
}
/* 表格 */
/* TABLES */
table {
@ -359,6 +365,7 @@ tr:nth-child(odd) + .row-form-errors .errorlist {
background: var(--darkened-bg);
}
/* 可排序表格 */
/* SORTABLE TABLES */
thead th {
@ -471,6 +478,7 @@ table thead th.sorted .sortoptions a.descending:hover {
background-position: 0 -70px;
}
/* 表单默认样式 */
/* FORM DEFAULTS */
input, textarea, select, .form-row p, form .button {
@ -490,8 +498,8 @@ textarea {
}
/*
Minifiers remove the default (text) "type" attribute from "input" HTML tags.
Add input:not([type]) to make the CSS stylesheet work the same.
"input" HTML"type"
input:not([type])使CSS
*/
input:not([type]), input[type=text], input[type=password], input[type=email],
input[type=url], input[type=number], input[type=tel], textarea, select,
@ -505,8 +513,8 @@ input[type=url], input[type=number], input[type=tel], textarea, select,
}
/*
Minifiers remove the default (text) "type" attribute from "input" HTML tags.
Add input:not([type]) to make the CSS stylesheet work the same.
"input" HTML"type"
input:not([type])使CSS
*/
input:not([type]):focus, input[type=text]:focus, input[type=password]:focus,
input[type=email]:focus, input[type=url]:focus, input[type=number]:focus,
@ -519,11 +527,12 @@ select {
}
select[multiple] {
/* Allow HTML size attribute to override the height in the rule above. */
/* 允许HTML size属性覆盖上面的height规则 */
height: auto;
min-height: 150px;
}
/* 表单按钮 */
/* FORM BUTTONS */
.button, input[type=submit], input[type=button], .submit-row input, a.button {
@ -569,6 +578,7 @@ input[type=button][disabled].default {
}
/* 模块 */
/* MODULES */
.module {
@ -615,6 +625,7 @@ input[type=button][disabled].default {
border-collapse: collapse;
}
/* 消息和错误 */
/* MESSAGES & ERRORS */
ul.messagelist {
@ -711,6 +722,7 @@ td ul.errorlist + input, td ul.errorlist + select, td ul.errorlist + textarea {
padding: 5px 0 0 12px;
}
/* 面包屑导航 */
/* BREADCRUMBS */
div.breadcrumbs {
@ -729,6 +741,7 @@ div.breadcrumbs a:focus, div.breadcrumbs a:hover {
color: var(--breadcrumbs-fg);
}
/* 操作图标 */
/* ACTION ICONS */
.viewlink, .inlineviewlink {
@ -757,14 +770,15 @@ div.breadcrumbs a:focus, div.breadcrumbs a:hover {
}
a.deletelink:link, a.deletelink:visited {
color: #CC3434; /* XXX Probably unused? */
color: #CC3434; /* XXX 可能未使用? */
}
a.deletelink:focus, a.deletelink:hover {
color: #993333; /* XXX Probably unused? */
color: #993333; /* XXX 可能未使用? */
text-decoration: none;
}
/* 对象工具 */
/* OBJECT TOOLS */
.object-tools {
@ -821,6 +835,7 @@ a.deletelink:focus, a.deletelink:hover {
background-image: url(../img/tooltag-add.svg);
}
/* 对象历史 */
/* OBJECT HISTORY */
#change-history table {
@ -838,6 +853,7 @@ a.deletelink:focus, a.deletelink:hover {
overflow: hidden;
}
/* 页面结构 */
/* PAGE STRUCTURE */
#container {
@ -900,6 +916,7 @@ a.deletelink:focus, a.deletelink:hover {
}
}
/* 列类型 */
/* COLUMN TYPES */
.colMS {
@ -924,6 +941,7 @@ a.deletelink:focus, a.deletelink:hover {
width: auto;
}
/* 头部 */
/* HEADER */
#header {
@ -1019,6 +1037,7 @@ a.deletelink:focus, a.deletelink:hover {
margin-bottom: 1px;
}
/* 侧边栏 */
/* SIDEBAR */
#content-related {
@ -1095,6 +1114,7 @@ a.deletelink:focus, a.deletelink:hover {
background: var(--close-button-hover-bg);
}
/* 弹窗 */
/* POPUP */
.popup #content {
padding: 20px;
@ -1108,6 +1128,7 @@ a.deletelink:focus, a.deletelink:hover {
padding: 10px 20px;
}
/* 分页器 */
/* PAGINATOR */
.paginator {
@ -1177,4 +1198,4 @@ a.deletelink:focus, a.deletelink:hover {
border: 0;
color: var(--body-fg);
background-color: var(--body-bg);
}
}

@ -1,3 +1,4 @@
/* 变更列表 */
/* CHANGELISTS */
#changelist {
@ -50,6 +51,7 @@
overflow: hidden;
}
/* 变更列表表格 */
/* CHANGELIST TABLES */
#changelist table thead th {
@ -71,6 +73,7 @@
color: var(--body-quiet-color);
}
/* 工具栏 */
/* TOOLBAR */
#toolbar {
@ -129,6 +132,7 @@
word-break: break-word;
}
/* 过滤器列 */
/* FILTER COLUMN */
#changelist-filter {
@ -227,6 +231,7 @@
border-bottom: 1px solid var(--hairline-color);
}
/* 日期钻取 */
/* DATE DRILLDOWN */
.change-list .toplinks {
@ -250,6 +255,7 @@
color: var(--link-hover-color);
}
/* 操作 */
/* ACTIONS */
.filtered .actions {
@ -261,6 +267,7 @@
vertical-align: baseline;
}
/* 一旦所有浏览器都支持:has()伪类可以移除tr.selected选择器和添加该类的JS代码 */
/* Once the :has() pseudo-class is supported by all browsers, the tr.selected
selector and the JS adding the class can be removed. */
#changelist tbody tr.selected {
@ -340,4 +347,4 @@
#changelist .actions .button:focus, #changelist .actions .button:hover {
border-color: var(--body-quiet-color);
}
}

@ -1,41 +1,93 @@
/* 深色模式媒体查询,当系统偏好设置为深色时应用 */
/* Dark mode media query, applied when system preference is set to dark */
@media (prefers-color-scheme: dark) {
:root {
/* 主色调 */
/* Primary color */
--primary: #264b5d;
/* 主要前景色 */
/* Primary foreground color */
--primary-fg: #f7f7f7;
/* 主体前景色 */
/* Body foreground color */
--body-fg: #eeeeee;
/* 主体背景色 */
/* Body background color */
--body-bg: #121212;
/* 主体安静色(较淡)*/
/* Body quiet color (lighter) */
--body-quiet-color: #d0d0d0;
/* 主体中等颜色 */
/* Body medium color */
--body-medium-color: #e0e0e0;
/* 主体响亮色(较亮)*/
/* Body loud color (brighter) */
--body-loud-color: #ffffff;
/* 面包屑链接颜色 */
/* Breadcrumbs link color */
--breadcrumbs-link-fg: #e0e0e0;
/* 面包屑背景色 */
/* Breadcrumbs background color */
--breadcrumbs-bg: var(--primary);
/* 链接颜色 */
/* Link color */
--link-fg: #81d4fa;
/* 链接悬停颜色 */
/* Link hover color */
--link-hover-color: #4ac1f7;
/* 链接选中颜色 */
/* Link selected color */
--link-selected-fg: #6f94c6;
/* 细线颜色 */
/* Hairline color */
--hairline-color: #272727;
/* 边框颜色 */
/* Border color */
--border-color: #353535;
/* 错误颜色 */
/* Error color */
--error-fg: #e35f5f;
/* 成功消息背景色 */
/* Success message background color */
--message-success-bg: #006b1b;
/* 警告消息背景色 */
/* Warning message background color */
--message-warning-bg: #583305;
/* 错误消息背景色 */
/* Error message background color */
--message-error-bg: #570808;
/* 深色背景 */
/* Darkened background */
--darkened-bg: #212121;
/* 选中背景色 */
/* Selected background color */
--selected-bg: #1b1b1b;
/* 选中行颜色 */
/* Selected row color */
--selected-row: #00363a;
/* 关闭按钮背景色 */
/* Close button background color */
--close-button-bg: #333333;
/* 关闭按钮悬停背景色 */
/* Close button hover background color */
--close-button-hover-bg: #666666;
/* 颜色方案设置为深色 */
/* Color scheme set to dark */
color-scheme: dark;
}
}
/* 当HTML元素的data-theme属性为"dark"时应用的样式 */
/* Styles applied when HTML element's data-theme attribute is "dark" */
html[data-theme="dark"] {
--primary: #264b5d;
--primary-fg: #f7f7f7;
@ -71,25 +123,51 @@ html[data-theme="dark"] {
color-scheme: dark;
}
/* THEME SWITCH */
/* 主题切换按钮样式 */
/* Theme switch button styles */
.theme-toggle {
/* 设置光标为指针 */
/* Set cursor to pointer */
cursor: pointer;
/* 移除边框 */
/* Remove border */
border: none;
/* 设置内边距为0 */
/* Set padding to 0 */
padding: 0;
/* 设置背景为透明 */
/* Set background to transparent */
background: transparent;
/* 设置垂直对齐方式 */
/* Set vertical alignment */
vertical-align: middle;
/* 设置左边距 */
/* Set left margin */
margin-inline-start: 5px;
/* 设置上边距 */
/* Set top margin */
margin-top: -1px;
}
/* 主题切换按钮中的SVG图标样式 */
/* SVG icon styles in theme toggle button */
.theme-toggle svg {
/* 设置垂直对齐方式 */
/* Set vertical alignment */
vertical-align: middle;
/* 设置高度 */
/* Set height */
height: 1.5rem;
/* 设置宽度 */
/* Set width */
width: 1.5rem;
/* 默认不显示 */
/* Hidden by default */
display: none;
}
/*
Fully hide screen reader text so we only show the one matching the current
theme.
*/
@ -97,19 +175,26 @@ theme.
display: none;
}
/* 当data-theme属性为"auto"时显示自动主题标签 */
/* Show auto theme label when data-theme attribute is "auto" */
html[data-theme="auto"] .theme-toggle .theme-label-when-auto {
display: block;
}
/* 当data-theme属性为"dark"时显示深色主题标签 */
/* Show dark theme label when data-theme attribute is "dark" */
html[data-theme="dark"] .theme-toggle .theme-label-when-dark {
display: block;
}
/* 当data-theme属性为"light"时显示浅色主题标签 */
/* Show light theme label when data-theme attribute is "light" */
html[data-theme="light"] .theme-toggle .theme-label-when-light {
display: block;
}
/* ICONS */
/* 图标样式 */
/* Icon styles */
.theme-toggle svg.theme-icon-when-auto,
.theme-toggle svg.theme-icon-when-dark,
.theme-toggle svg.theme-icon-when-light {
@ -117,14 +202,20 @@ html[data-theme="light"] .theme-toggle .theme-label-when-light {
color: var(--header-bg);
}
/* 当data-theme属性为"auto"时显示自动主题图标 */
/* Show auto theme icon when data-theme attribute is "auto" */
html[data-theme="auto"] .theme-toggle svg.theme-icon-when-auto {
display: block;
}
/* 当data-theme属性为"dark"时显示深色主题图标 */
/* Show dark theme icon when data-theme attribute is "dark" */
html[data-theme="dark"] .theme-toggle svg.theme-icon-when-dark {
display: block;
}
/* 当data-theme属性为"light"时显示浅色主题图标 */
/* Show light theme icon when data-theme attribute is "light" */
html[data-theme="light"] .theme-toggle svg.theme-icon-when-light {
display: block;
}
}

@ -1,3 +1,4 @@
/* 仪表板 */
/* DASHBOARD */
.dashboard td, .dashboard th {
word-break: break-word;
@ -16,6 +17,7 @@
padding-right: .6em;
}
/* 最近操作模块 */
/* RECENT ACTIONS MODULE */
.module ul.actionlist {
@ -26,4 +28,4 @@ ul.actionlist li {
list-style-type: none;
overflow: hidden;
text-overflow: ellipsis;
}
}

@ -1,5 +1,7 @@
/* 导入小部件样式 */
@import url('widgets.css');
/* 表单行 */
/* FORM ROWS */
.form-row {
@ -34,6 +36,7 @@ form .form-row p {
padding-bottom: 10px;
}
/* 表单标签 */
/* FORM LABELS */
label {
@ -46,6 +49,7 @@ label {
font-weight: bold;
}
/* 单选按钮 */
/* RADIO BUTTONS */
form div.radiolist div {
@ -75,6 +79,7 @@ form ul.inline li {
padding-right: 7px;
}
/* 字段集 */
/* FIELDSETS */
fieldset .fieldset-heading,
@ -89,6 +94,7 @@ fieldset .inline-heading,
color: var(--header-link-color);
}
/* 对齐的字段集 */
/* ALIGNED FIELDSETS */
.aligned label {
@ -199,6 +205,7 @@ fieldset .fieldBox {
margin-right: 20px;
}
/* 宽字段集 */
/* WIDE FIELDSETS */
.wide label {
@ -220,6 +227,7 @@ form div.help ul {
width: 450px;
}
/* 可折叠字段集 */
/* COLLAPSIBLE FIELDSETS */
.collapse summary .fieldset-heading,
@ -232,12 +240,14 @@ form div.help ul {
padding: 0;
}
/* 等宽字体文本区域 */
/* MONOSPACE TEXTAREAS */
fieldset.monospace textarea {
font-family: var(--font-family-monospace);
}
/* 提交行 */
/* SUBMIT ROW */
.submit-row {
@ -307,6 +317,7 @@ body.popup .submit-row {
text-decoration: none;
}
/* 自定义表单字段 */
/* CUSTOM FORM FIELDS */
.vSelectMultipleField {
@ -362,6 +373,7 @@ body.popup .submit-row {
width: 20em;
}
/* 行内组 */
/* INLINES */
.inline-group {
@ -471,6 +483,7 @@ body.popup .submit-row {
display: none;
}
/* 相关字段添加一个/查找 */
/* RELATED FIELD ADD ONE / LOOKUP */
.related-lookup {
@ -495,4 +508,4 @@ form .related-widget-wrapper ul {
.clearable-file-input input {
margin-top: 0;
}
}

@ -1,61 +1,89 @@
/* 登录表单样式 */
/* LOGIN FORM */
.login {
/* 设置背景色 */
background: var(--darkened-bg);
/* 设置高度为自动 */
height: auto;
}
.login #header {
/* 设置头部高度为自动 */
height: auto;
/* 设置内边距 */
padding: 15px 16px;
/* 设置内容居中对齐 */
justify-content: center;
}
.login #header h1 {
/* 设置标题字体大小 */
font-size: 1.125rem;
/* 设置外边距 */
margin: 0;
}
.login #header h1 a {
/* 设置链接颜色 */
color: var(--header-link-color);
}
.login #content {
/* 设置内容区内边距 */
padding: 20px;
}
.login #container {
/* 设置容器背景色 */
background: var(--body-bg);
/* 设置边框 */
border: 1px solid var(--hairline-color);
/* 设置圆角 */
border-radius: 4px;
/* 设置溢出隐藏 */
overflow: hidden;
/* 设置宽度 */
width: 28em;
/* 设置最小宽度 */
min-width: 300px;
/* 设置外边距居中 */
margin: 100px auto;
/* 设置高度为自动 */
height: auto;
}
.login .form-row {
/* 设置表单行内边距 */
padding: 4px 0;
}
.login .form-row label {
/* 设置标签为块级元素 */
display: block;
/* 设置行高 */
line-height: 2em;
}
.login .form-row #id_username, .login .form-row #id_password {
/* 设置内边距 */
padding: 8px;
/* 设置宽度为100% */
width: 100%;
/* 设置盒模型为border-box */
box-sizing: border-box;
}
.login .submit-row {
/* 设置提交行内边距 */
padding: 1em 0 0 0;
/* 设置外边距 */
margin: 0;
/* 设置文本居中对齐 */
text-align: center;
}
.login .password-reset-link {
/* 设置密码重置链接文本居中对齐 */
text-align: center;
}
}

@ -1,9 +1,13 @@
/* 粘性定位 - 使侧边栏在滚动时保持在顶部 */
/* sticky */
.sticky {
position: sticky;
top: 0;
max-height: 100vh;
}
/* 切换导航侧边栏按钮 */
/* 切换导航侧边栏按钮 */
.toggle-nav-sidebar {
z-index: 20;
left: 0;
@ -21,16 +25,19 @@
padding: 0;
}
/* 从右到左语言环境下的切换按钮边框调整 */
[dir="rtl"] .toggle-nav-sidebar {
border-left: 1px solid var(--hairline-color);
border-right: 0;
}
/* 切换按钮的悬停和焦点状态 */
.toggle-nav-sidebar:hover,
.toggle-nav-sidebar:focus {
background-color: var(--darkened-bg);
}
/* 导航侧边栏 */
#nav-sidebar {
z-index: 15;
flex: 0 0 275px;
@ -42,6 +49,7 @@
overflow: auto;
}
/* 从右到左语言环境下的导航侧边栏调整 */
[dir="rtl"] #nav-sidebar {
border-left: 1px solid var(--hairline-color);
border-right: 0;
@ -51,82 +59,99 @@
margin-right: -276px;
}
/* 切换导航侧边栏按钮的:before伪元素内容 */
.toggle-nav-sidebar::before {
content: '\00BB';
}
/* 主内容区域移动时切换按钮的:before伪元素内容 */
.main.shifted .toggle-nav-sidebar::before {
content: '\00AB';
}
/* 主内容区域的导航侧边栏可见性 */
.main > #nav-sidebar {
visibility: hidden;
}
/* 主内容区域移动时导航侧边栏的可见性 */
.main.shifted > #nav-sidebar {
margin-left: 0;
visibility: visible;
}
/* 从右到左语言环境下主内容区域移动时导航侧边栏的右边距 */
[dir="rtl"] .main.shifted > #nav-sidebar {
margin-right: 0;
}
/* 导航侧边栏模块标题的宽度和换行处理 */
#nav-sidebar .module th {
width: 100%;
overflow-wrap: anywhere;
}
/* 导航侧边栏模块标题和说明文字的内边距 */
#nav-sidebar .module th,
#nav-sidebar .module caption {
padding-left: 16px;
}
/* 导航侧边栏模块数据单元格的空白处理 */
#nav-sidebar .module td {
white-space: nowrap;
}
/* 从右到左语言环境下导航侧边栏模块标题和说明文字的内边距调整 */
[dir="rtl"] #nav-sidebar .module th,
[dir="rtl"] #nav-sidebar .module caption {
padding-left: 8px;
padding-right: 16px;
}
/* 当前应用的节链接样式 */
#nav-sidebar .current-app .section:link,
#nav-sidebar .current-app .section:visited {
color: var(--header-color);
font-weight: bold;
}
/* 当前模型的背景色 */
#nav-sidebar .current-model {
background: var(--selected-row);
}
/* 强制颜色模式下当前模型的背景色 */
@media (forced-colors: active) {
#nav-sidebar .current-model {
background-color: SelectedItem;
}
}
/* 主内容区域导航侧边栏和内容的最大宽度 */
.main > #nav-sidebar + .content {
max-width: calc(100% - 23px);
}
/* 主内容区域移动时导航侧边栏和内容的最大宽度 */
.main.shifted > #nav-sidebar + .content {
max-width: calc(100% - 299px);
}
/* 移动设备样式 - 隐藏导航侧边栏和切换按钮 */
@media (max-width: 767px) {
#nav-sidebar, #toggle-nav-sidebar {
display: none;
}
/* 移动设备上主内容区域的最大宽度 */
.main > #nav-sidebar + .content,
.main.shifted > #nav-sidebar + .content {
max-width: 100%;
}
}
/* 导航过滤器输入框样式 */
#nav-filter {
width: 100%;
box-sizing: border-box;
@ -137,14 +162,17 @@
color: var(--body-fg);
}
/* 导航过滤器输入框的焦点状态 */
#nav-filter:focus {
border-color: var(--body-quiet-color);
}
/* 导航过滤器无结果时的背景色 */
#nav-filter.no-results {
background: var(--message-error-bg);
}
/* 导航侧边栏表格宽度 */
#nav-sidebar table {
width: 100%;
}
}

@ -1,3 +1,4 @@
/* 平板设备 */
/* Tablets */
input[type="submit"], button {
@ -6,6 +7,7 @@ input[type="submit"], button {
}
@media (max-width: 1024px) {
/* 基础样式 */
/* Basic */
html {
@ -21,6 +23,7 @@ input[type="submit"], button {
font-size: 0.75rem;
}
/* 布局 */
/* Layout */
#container {
@ -35,6 +38,7 @@ input[type="submit"], button {
padding: 10px 30px;
}
/* 头部 */
/* Header */
#header {
@ -60,6 +64,7 @@ input[type="submit"], button {
line-height: 1.4;
}
/* 仪表板 */
/* Dashboard */
.dashboard #content {
@ -90,6 +95,7 @@ input[type="submit"], button {
font-size: 0.8125rem;
}
/* 变更列表 */
/* Changelist */
#toolbar {
@ -168,6 +174,7 @@ input[type="submit"], button {
border-top: none;
}
/* 表单 */
/* Forms */
label {
@ -175,8 +182,8 @@ input[type="submit"], button {
}
/*
Minifiers remove the default (text) "type" attribute from "input" HTML
tags. Add input:not([type]) to make the CSS stylesheet work the same.
"input" HTML"type"
input:not([type])使CSS
*/
.form-row input:not([type]),
.form-row input[type=text],
@ -242,6 +249,7 @@ input[type="submit"], button {
padding: 7px;
}
/* 选择器 */
/* Selector */
.selector {
@ -334,6 +342,7 @@ input[type="submit"], button {
overflow: auto;
}
/* 消息 */
/* Messages */
ul.messagelist li {
@ -349,6 +358,7 @@ input[type="submit"], button {
background-position: 30px 14px;
}
/* 登录 */
/* Login */
.login #header {
@ -371,6 +381,7 @@ input[type="submit"], button {
margin-top: 10px;
}
/* 文档 */
/* Docs */
.module table.xfull {
@ -382,9 +393,11 @@ input[type="submit"], button {
}
}
/* 移动设备 */
/* Mobile */
@media (max-width: 767px) {
/* 布局 */
/* Layout */
#header, #content {
@ -395,6 +408,7 @@ input[type="submit"], button {
padding: 10px 15px;
}
/* 仪表板 */
/* Dashboard */
.colMS, .colSM {
@ -415,6 +429,7 @@ input[type="submit"], button {
font-size: 1rem;
}
/* 变更列表 */
/* Changelist */
#changelist {
@ -465,6 +480,7 @@ input[type="submit"], button {
margin-left: 15px;
}
/* 表单 */
/* Forms */
.form-row {
@ -575,6 +591,7 @@ input[type="submit"], button {
margin-top: 5px;
}
/* 相关小部件 */
/* Related widget */
.related-widget-wrapper {
@ -600,6 +617,7 @@ input[type="submit"], button {
align-self: center;
}
/* 选择器 */
/* Selector */
.selector {
@ -643,6 +661,7 @@ input[type="submit"], button {
background-position: 0 -72px;
}
/* 行内组 */
/* Inlines */
.inline-group[data-inline-type="stacked"] .inline-related {
@ -714,6 +733,7 @@ input[type="submit"], button {
background-position: 8px 9px;
}
/* 提交行 */
/* Submit row */
.submit-row {
@ -736,6 +756,7 @@ input[type="submit"], button {
margin: 0;
}
/* 消息 */
/* Messages */
ul.messagelist li {
@ -751,12 +772,14 @@ input[type="submit"], button {
background-position: 15px 14px;
}
/* 分页器 */
/* Paginator */
.paginator .this-page, .paginator a:link, .paginator a:visited {
padding: 4px 10px;
}
/* 登录 */
/* Login */
body.login {
@ -810,6 +833,7 @@ input[type="submit"], button {
font-size: 0.8125rem;
}
/* 日历和时钟 */
/* Calendar and clock */
.calendarbox, .clockbox {
@ -884,6 +908,7 @@ input[type="submit"], button {
top: 10px;
}
/* 历史记录 */
/* History */
table#change-history tbody th, table#change-history tbody td {
@ -895,10 +920,11 @@ input[type="submit"], button {
width: auto;
}
/* 文档 */
/* Docs */
table.model tbody th, table.model tbody td {
font-size: 0.8125rem;
word-break: break-word;
}
}
}

@ -1,24 +1,30 @@
/* 平板设备样式 */
/* TABLETS */
@media (max-width: 1024px) {
/* 从右到左的语言布局调整 */
[dir="rtl"] .colMS {
margin-right: 0;
}
/* 用户工具栏文本对齐调整 */
[dir="rtl"] #user-tools {
text-align: right;
}
/* 变更列表操作标签内边距调整 */
[dir="rtl"] #changelist .actions label {
padding-left: 10px;
padding-right: 0;
}
/* 变更列表操作选择框边距调整 */
[dir="rtl"] #changelist .actions select {
margin-left: 0;
margin-right: 15px;
}
/* 过滤器相关元素的边距调整 */
[dir="rtl"] .change-list .filtered .results,
[dir="rtl"] .change-list .filtered .paginator,
[dir="rtl"] .filtered #toolbar,
@ -28,62 +34,76 @@
margin-left: 0;
}
/* 行内组添加行链接的内边距和背景位置调整 */
[dir="rtl"] .inline-group div.add-row a,
[dir="rtl"] .inline-group .tabular tr.add-row td a {
padding: 8px 26px 8px 10px;
background-position: calc(100% - 8px) 9px;
}
/* 对象工具列表项浮动方向调整 */
[dir="rtl"] .object-tools li {
float: right;
}
/* 对象工具相邻列表项的边距调整 */
[dir="rtl"] .object-tools li + li {
margin-left: 0;
margin-right: 15px;
}
/* 仪表板模块表格链接内边距调整 */
[dir="rtl"] .dashboard .module table td a {
padding-left: 0;
padding-right: 16px;
}
}
/* 移动设备样式 */
/* MOBILE */
@media (max-width: 767px) {
/* 相关查找和日期时间快捷方式的边距调整 */
[dir="rtl"] .aligned .related-lookup,
[dir="rtl"] .aligned .datetimeshortcuts {
margin-left: 0;
margin-right: 15px;
}
/* 列表的右边距调整 */
[dir="rtl"] .aligned ul,
[dir="rtl"] form .aligned ul.errorlist {
margin-right: 0;
}
/* 变更列表过滤器的边距调整 */
[dir="rtl"] #changelist-filter {
margin-left: 0;
margin-right: 0;
}
/* 复选框标签的内边距调整 */
[dir="rtl"] .aligned .vCheckboxLabel {
padding: 1px 5px 0 0;
}
/* 选择器移除按钮的背景位置调整 */
[dir="rtl"] .selector-remove {
background-position: 0 0;
}
/* 选择器移除按钮的焦点和悬停状态背景位置调整 */
[dir="rtl"] :enabled.selector-remove:focus, :enabled.selector-remove:hover {
background-position: 0 -24px;
}
/* 选择器添加按钮的背景位置调整 */
[dir="rtl"] .selector-add {
background-position: 0 -48px;
}
/* 选择器添加按钮的焦点和悬停状态背景位置调整 */
[dir="rtl"] :enabled.selector-add:focus, :enabled.selector-add:hover {
background-position: 0 -72px;
}
}
}

@ -1,91 +1,113 @@
/* 全局样式 - 适用于从右到左的语言 */
/* GLOBAL */
/* 表格标题文本对齐 */
th {
text-align: right;
}
/* 模块标题和说明文字对齐 */
.module h2, .module caption {
text-align: right;
}
/* 模块列表的左右边距调整 */
.module ul, .module ol {
margin-left: 0;
margin-right: 1.5em;
}
/* 查看、添加、变更、隐藏链接的内边距和背景位置调整 */
.viewlink, .addlink, .changelink, .hidelink {
padding-left: 0;
padding-right: 16px;
background-position: 100% 1px;
}
/* 删除链接的内边距和背景位置调整 */
.deletelink {
padding-left: 0;
padding-right: 16px;
background-position: 100% 1px;
}
/* 对象工具栏浮动方向 */
.object-tools {
float: left;
}
/* 表格首列的边框调整 */
thead th:first-child,
tfoot td:first-child {
border-left: none;
}
/* 布局相关样式 */
/* LAYOUT */
/* 用户工具栏位置和文本对齐调整 */
#user-tools {
right: auto;
left: 0;
text-align: left;
}
/* 面包屑导航文本对齐 */
div.breadcrumbs {
text-align: right;
}
/* 主内容区域浮动方向 */
#content-main {
float: right;
}
/* 相关内容区域浮动方向和边距调整 */
#content-related {
float: left;
margin-left: -300px;
margin-right: auto;
}
/* 主列和侧边栏布局调整 */
.colMS {
margin-left: 300px;
margin-right: 0;
}
/* 可排序表格样式 */
/* SORTABLE TABLES */
/* 排序选项浮动方向 */
table thead th.sorted .sortoptions {
float: left;
}
/* 已排序列文本内边距调整 */
thead th.sorted .text {
padding-right: 0;
padding-left: 42px;
}
/* 仪表板样式 */
/* dashboard styles */
/* 仪表板模块表格链接内边距调整 */
.dashboard .module table td a {
padding-left: .6em;
padding-right: 16px;
}
/* 变更列表样式 */
/* changelists styles */
/* 过滤变更列表的表格边框调整 */
.change-list .filtered table {
border-left: none;
border-right: 0px none;
}
/* 变更列表过滤器边框和边距调整 */
#changelist-filter {
border-left: none;
border-right: none;
@ -93,6 +115,7 @@ thead th.sorted .text {
margin-right: 30px;
}
/* 已选过滤条件的边框、内边距和边距调整 */
#changelist-filter li.selected {
border-left: none;
padding-left: 10px;
@ -102,40 +125,49 @@ thead th.sorted .text {
margin-right: -15px;
}
/* 变更列表表格首列的边框调整 */
#changelist table tbody td:first-child, #changelist table tbody th:first-child {
border-right: none;
border-left: none;
}
/* 分页器末页链接的边距调整 */
.paginator .end {
margin-left: 6px;
margin-right: 0;
}
/* 分页器输入框的边距调整 */
.paginator input {
margin-left: 0;
margin-right: auto;
}
/* 表单样式 */
/* FORMS */
/* 对齐标签的内边距调整 */
.aligned label {
padding: 0 0 3px 1em;
}
/* 删除链接的边距调整 */
.submit-row a.deletelink {
margin-left: 0;
margin-right: auto;
}
/* 日期和时间字段的边距调整 */
.vDateField, .vTimeField {
margin-left: 2px;
}
/* 对齐表单行输入框的边距调整 */
.aligned .form-row input {
margin-left: 5px;
}
/* 对齐列表的边距和内边距调整 */
form .aligned ul {
margin-right: 163px;
padding-right: 10px;
@ -143,12 +175,14 @@ form .aligned ul {
padding-left: 0;
}
/* 行内列表项的浮动方向和内边距调整 */
form ul.inline li {
float: right;
padding-right: 0;
padding-left: 7px;
}
/* 表单帮助文本的边距和内边距调整 */
form .aligned p.help,
form .aligned div.help {
margin-left: 0;
@ -156,6 +190,7 @@ form .aligned div.help {
padding-right: 10px;
}
/* 帮助文本列表、复选框行和时区警告的边距调整 */
form div.help ul,
form .aligned .checkbox-row + .help,
form .aligned p.date div.help.timezonewarning,
@ -165,6 +200,7 @@ form .aligned p.time div.help.timezonewarning {
padding-right: 0;
}
/* 宽表单帮助文本、错误列表和帮助文本的内边距调整 */
form .wide p.help,
form .wide ul.errorlist,
form .wide div.help {
@ -172,27 +208,33 @@ form .wide div.help {
padding-right: 50px;
}
/* 提交行文本对齐 */
.submit-row {
text-align: right;
}
/* 字段框的边距调整 */
fieldset .fieldBox {
margin-left: 20px;
margin-right: 0;
}
/* 错误列表项的背景位置和内边距调整 */
.errorlist li {
background-position: 100% 12px;
padding: 0;
}
/* 错误提示的背景位置和内边距调整 */
.errornote {
background-position: 100% 12px;
padding: 10px 12px;
}
/* 小部件样式 */
/* WIDGETS */
/* 日历导航上一个按钮的位置和背景调整 */
.calendarnav-previous {
top: 0;
left: auto;
@ -200,6 +242,7 @@ fieldset .fieldBox {
background: url(../img/calendar-icons.svg) 0 -15px no-repeat;
}
/* 日历导航下一个按钮的位置和背景调整 */
.calendarnav-next {
top: 0;
right: auto;
@ -207,70 +250,87 @@ fieldset .fieldBox {
background: url(../img/calendar-icons.svg) 0 0 no-repeat;
}
/* 日历标题和日历框标题文本对齐 */
.calendar caption, .calendarbox h2 {
text-align: center;
}
/* 选择器浮动方向 */
.selector {
float: right;
}
/* 选择器过滤器文本对齐 */
.selector .selector-filter {
text-align: right;
}
/* 选择器添加按钮的背景和背景大小调整 */
.selector-add {
background: url(../img/selector-icons.svg) 0 -96px no-repeat;
background-size: 24px auto;
}
/* 选择器添加按钮的焦点和悬停状态背景位置调整 */
:enabled.selector-add:focus, :enabled.selector-add:hover {
background-position: 0 -120px;
}
/* 选择器移除按钮的背景和背景大小调整 */
.selector-remove {
background: url(../img/selector-icons.svg) 0 -144px no-repeat;
background-size: 24px auto;
}
/* 选择器移除按钮的焦点和悬停状态背景位置调整 */
:enabled.selector-remove:focus, :enabled.selector-remove:hover {
background-position: 0 -168px;
}
/* 全选按钮的背景调整 */
.selector-chooseall {
background: url(../img/selector-icons.svg) right -128px no-repeat;
}
/* 全选按钮的焦点和悬停状态背景位置调整 */
:enabled.selector-chooseall:focus, :enabled.selector-chooseall:hover {
background-position: 100% -144px;
}
/* 全清按钮的背景调整 */
.selector-clearall {
background: url(../img/selector-icons.svg) 0 -160px no-repeat;
}
/* 全清按钮的焦点和悬停状态背景位置调整 */
:enabled.selector-clearall:focus, :enabled.selector-clearall:hover {
background-position: 0 -176px;
}
/* 行内删除链接浮动方向 */
.inline-deletelink {
float: left;
}
/* 日期时间表单行的溢出处理 */
form .form-row p.datetime {
overflow: hidden;
}
/* 相关小部件包装器的浮动方向 */
.related-widget-wrapper {
float: right;
}
/* 其他样式 */
/* MISC */
/* 行内相关标题和行内组标题文本对齐 */
.inline-related h2, .inline-group h2 {
text-align: right
}
/* 行内相关标题删除链接的内边距、位置和浮动方向调整 */
.inline-related h3 span.delete {
padding-right: 20px;
padding-left: inherit;
@ -279,15 +339,18 @@ form .form-row p.datetime {
float:left;
}
/* 行内相关标题删除链接标签的边距调整 */
.inline-related h3 span.delete label {
margin-left: inherit;
margin-right: 2px;
}
/* 行内组表格原始列段落的右边距调整 */
.inline-group .tabular td.original p {
right: 0;
}
/* 选择器选择器的边距调整 */
.selector .selector-chooser {
margin: 0;
}
}

@ -1,14 +1,17 @@
/* 当用户选择可用密码时隐藏警告字段 */
/* Hide warnings fields if usable password is selected */
form:has(#id_usable_password input[value="true"]:checked) .messagelist {
display: none;
}
/* 当用户选择不可用密码时隐藏密码字段 */
/* Hide password fields if unusable password is selected */
form:has(#id_usable_password input[value="false"]:checked) .field-password1,
form:has(#id_usable_password input[value="false"]:checked) .field-password2 {
display: none;
}
/* 选择适当的提交按钮 */
/* Select appropriate submit button */
form:has(#id_usable_password input[value="true"]:checked) input[type="submit"].unset-password {
display: none;
@ -16,4 +19,4 @@ form:has(#id_usable_password input[value="true"]:checked) input[type="submit"].u
form:has(#id_usable_password input[value="false"]:checked) input[type="submit"].set-password {
display: none;
}
}

File diff suppressed because one or more lines are too long

@ -1,3 +1,4 @@
/* 选择器(过滤器界面)*/
/* SELECTOR (FILTER INTERFACE) */
.selector {
@ -207,6 +208,7 @@
background-position: 0 -144px;
}
/* 堆叠选择器 */
/* STACKED SELECTORS */
.stacked {
@ -303,6 +305,7 @@
width: 1.125rem;
}
/* 日期和时间 */
/* DATE AND TIME */
p.datetime {
@ -381,6 +384,7 @@ p.url {
font-weight: normal;
}
/* 文件上传 */
/* FILE UPLOADS */
p.file-upload {
@ -407,6 +411,7 @@ span.clearable-file-input label {
float: none;
}
/* 日历和时钟 */
/* CALENDARS & CLOCKS */
.calendarbox, .clockbox {
@ -571,6 +576,7 @@ ul.timelist, .timelist li {
padding: 2px;
}
/* 行内编辑 */
/* EDIT INLINE */
.inline-deletelink {
@ -587,6 +593,7 @@ ul.timelist, .timelist li {
cursor: pointer;
}
/* 相关小部件包装器 */
/* RELATED WIDGET WRAPPER */
.related-widget-wrapper {
display: flex;
@ -606,8 +613,9 @@ ul.timelist, .timelist li {
filter: grayscale(0);
}
/* GIS地图 */
/* GIS MAPS */
.dj_map {
width: 600px;
height: 400px;
}
}

@ -1,66 +1,111 @@
'use strict';
{
const SelectBox = {
cache: {},
cache: {}, // 选择框缓存,用于存储选项数据
/**
* 初始化选择框缓存
* @param {string} id - 选择框元素的ID
*/
init: function(id) {
const box = document.getElementById(id);
SelectBox.cache[id] = [];
const cache = SelectBox.cache[id];
// 将选择框中的所有选项添加到缓存中
for (const node of box.options) {
cache.push({value: node.value, text: node.text, displayed: 1});
}
},
/**
* 从缓存重新显示HTML选择框
* @param {string} id - 选择框元素的ID
*/
redisplay: function(id) {
// Repopulate HTML select box from cache
// 从缓存重新填充HTML选择框
const box = document.getElementById(id);
const scroll_value_from_top = box.scrollTop;
box.innerHTML = '';
// 只显示标记为displayed的选项
for (const node of SelectBox.cache[id]) {
if (node.displayed) {
const new_option = new Option(node.text, node.value, false, false);
// Shows a tooltip when hovering over the option
// 鼠标悬停时显示提示工具
new_option.title = node.text;
box.appendChild(new_option);
}
}
box.scrollTop = scroll_value_from_top;
},
/**
* 根据文本过滤选择框中的选项
* @param {string} id - 选择框元素的ID
* @param {string} text - 过滤文本
*/
filter: function(id, text) {
// Redisplay the HTML select box, displaying only the choices containing ALL
// the words in text. (It's an AND search.)
// 重新显示HTML选择框仅显示包含文本中所有单词的选项AND搜索
const tokens = text.toLowerCase().split(/\s+/);
for (const node of SelectBox.cache[id]) {
node.displayed = 1;
const node_text = node.text.toLowerCase();
// 检查每个词是否都包含在选项文本中
for (const token of tokens) {
if (!node_text.includes(token)) {
node.displayed = 0;
break; // Once the first token isn't found we're done
break; // 一旦第一个词未找到就结束
}
}
}
SelectBox.redisplay(id);
},
/**
* 获取隐藏节点数量
* @param {string} id - 选择框元素的ID
* @returns {number} 隐藏节点的数量
*/
get_hidden_node_count(id) {
const cache = SelectBox.cache[id] || [];
return cache.filter(node => node.displayed === 0).length;
},
/**
* 从缓存中删除指定值的选项
* @param {string} id - 选择框元素的ID
* @param {string} value - 要删除的选项值
*/
delete_from_cache: function(id, value) {
let delete_index = null;
const cache = SelectBox.cache[id];
// 查找要删除的选项索引
for (const [i, node] of cache.entries()) {
if (node.value === value) {
delete_index = i;
break;
}
}
// 从缓存中删除选项
cache.splice(delete_index, 1);
},
/**
* 向缓存中添加选项
* @param {string} id - 选择框元素的ID
* @param {HTMLOptionElement} option - 要添加的选项元素
*/
add_to_cache: function(id, option) {
SelectBox.cache[id].push({value: option.value, text: option.text, displayed: 1});
},
/**
* 检查缓存中是否包含指定值的选项
* @param {string} id - 选择框元素的ID
* @param {string} value - 要检查的选项值
* @returns {boolean} 如果包含则返回true否则返回false
*/
cache_contains: function(id, value) {
// Check if an item is contained in the cache
// 检查项目是否包含在缓存中
for (const node of SelectBox.cache[id]) {
if (node.value === value) {
return true;
@ -68,30 +113,53 @@
}
return false;
},
/**
* 将选中的选项从一个选择框移动到另一个选择框
* @param {string} from - 源选择框ID
* @param {string} to - 目标选择框ID
*/
move: function(from, to) {
const from_box = document.getElementById(from);
// 遍历源选择框中的所有选项
for (const option of from_box.options) {
const option_value = option.value;
// 如果选项被选中且在缓存中存在,则移动它
if (option.selected && SelectBox.cache_contains(from, option_value)) {
SelectBox.add_to_cache(to, {value: option_value, text: option.text, displayed: 1});
SelectBox.delete_from_cache(from, option_value);
}
}
// 重新显示两个选择框
SelectBox.redisplay(from);
SelectBox.redisplay(to);
},
/**
* 将所有选项从一个选择框移动到另一个选择框
* @param {string} from - 源选择框ID
* @param {string} to - 目标选择框ID
*/
move_all: function(from, to) {
const from_box = document.getElementById(from);
// 遍历源选择框中的所有选项
for (const option of from_box.options) {
const option_value = option.value;
// 如果选项在缓存中存在,则移动它
if (SelectBox.cache_contains(from, option_value)) {
SelectBox.add_to_cache(to, {value: option_value, text: option.text, displayed: 1});
SelectBox.delete_from_cache(from, option_value);
}
}
// 重新显示两个选择框
SelectBox.redisplay(from);
SelectBox.redisplay(to);
},
/**
* 对选择框中的选项按文本进行排序
* @param {string} id - 选择框元素的ID
*/
sort: function(id) {
SelectBox.cache[id].sort(function(a, b) {
a = a.text.toLowerCase();
@ -105,12 +173,18 @@
return 0;
} );
},
/**
* 选中选择框中的所有选项
* @param {string} id - 选择框元素的ID
*/
select_all: function(id) {
const box = document.getElementById(id);
// 遍历所有选项并选中它们
for (const option of box.options) {
option.selected = true;
}
}
};
window.SelectBox = SelectBox;
}
}

@ -1,38 +1,42 @@
/*global SelectBox, gettext, ngettext, interpolate, quickElement, SelectFilter*/
/*
SelectFilter2 - Turns a multiple-select box into a filter interface.
SelectFilter2 - 将多选框转换为过滤器界面
Requires core.js and SelectBox.js.
需要 core.js SelectBox.js
*/
'use strict';
{
window.SelectFilter = {
/**
* 初始化选择过滤器
* @param {string} field_id - 字段ID
* @param {string} field_name - 字段名称
* @param {boolean} is_stacked - 是否堆叠显示
*/
init: function(field_id, field_name, is_stacked) {
if (field_id.match(/__prefix__/)) {
// Don't initialize on empty forms.
// 不要在空表单上初始化
return;
}
const from_box = document.getElementById(field_id);
from_box.id += '_from'; // change its ID
from_box.id += '_from'; // 更改ID
from_box.className = 'filtered';
from_box.setAttribute('aria-labelledby', field_id + '_from_title');
// 处理from_box的兄弟元素<p>
for (const p of from_box.parentNode.getElementsByTagName('p')) {
if (p.classList.contains("info")) {
// Remove <p class="info">, because it just gets in the way.
// 移除<p class="info">,因为它会妨碍布局
from_box.parentNode.removeChild(p);
} else if (p.classList.contains("help")) {
// Move help text up to the top so it isn't below the select
// boxes or wrapped off on the side to the right of the add
// button:
// 将帮助文本移到顶部,这样它就不会显示在选择框下方或添加按钮右侧
from_box.parentNode.insertBefore(p, from_box.parentNode.firstChild);
}
}
// <div class="selector"> or <div class="selector stacked">
// <div class="selector"> <div class="selector stacked">
const selector_div = quickElement('div', from_box.parentNode);
// Make sure the selector div is at the beginning so that the
// add link would be displayed to the right of the widget.
// 确保选择器div在开头这样添加链接会显示在控件的右侧
from_box.parentNode.prepend(selector_div);
selector_div.className = is_stacked ? 'selector stacked' : 'selector';
@ -50,6 +54,7 @@ Requires core.js and SelectBox.js.
'class', 'helptext'
);
// 创建过滤器输入框
const filter_p = quickElement('p', selector_available, '', 'id', field_id + '_filter');
filter_p.className = 'selector-filter';
@ -67,6 +72,7 @@ Requires core.js and SelectBox.js.
filter_input.id = field_id + '_input';
selector_available.appendChild(from_box);
// 创建"全选"按钮
const choose_all = quickElement(
'button',
selector_available,
@ -79,6 +85,7 @@ Requires core.js and SelectBox.js.
// <ul class="selector-chooser">
const selector_chooser = quickElement('ul', selector_div);
selector_chooser.className = 'selector-chooser';
// 创建"添加选中"按钮
const add_button = quickElement(
'button',
quickElement('li', selector_chooser),
@ -87,6 +94,7 @@ Requires core.js and SelectBox.js.
'class', 'selector-add',
'type', 'button'
);
// 创建"移除选中"按钮
const remove_button = quickElement(
'button',
quickElement('li', selector_chooser),
@ -110,6 +118,7 @@ Requires core.js and SelectBox.js.
'class', 'helptext'
);
// 创建选中项过滤器
const filter_selected_p = quickElement('p', selector_chosen, '', 'id', field_id + '_filter_selected');
filter_selected_p.className = 'selector-filter';
@ -126,6 +135,7 @@ Requires core.js and SelectBox.js.
const filter_selected_input = quickElement('input', filter_selected_p, '', 'type', 'text', 'placeholder', gettext("Filter"));
filter_selected_input.id = field_id + '_selected_input';
// 创建目标选择框
quickElement(
'select',
selector_chosen,
@ -137,9 +147,11 @@ Requires core.js and SelectBox.js.
'aria-labelledby', field_id + '_to_title',
'class', 'filtered'
);
// 创建警告页脚
const warning_footer = quickElement('div', selector_chosen, '', 'class', 'list-footer-display');
quickElement('span', warning_footer, '', 'id', field_id + '_list-footer-display-text');
quickElement('span', warning_footer, ' ' + gettext('(click to clear)'), 'class', 'list-footer-display__clear');
// 创建"全部移除"按钮
const clear_all = quickElement(
'button',
selector_chosen,
@ -151,7 +163,7 @@ Requires core.js and SelectBox.js.
from_box.name = from_box.name + '_old';
// Set up the JavaScript event handlers for the select box filter interface
// 设置选择框过滤器界面的JavaScript事件处理器
const move_selection = function(e, elem, move_func, from, to) {
if (!elem.hasAttribute('disabled')) {
move_func(from, to);
@ -161,24 +173,30 @@ Requires core.js and SelectBox.js.
}
e.preventDefault();
};
// 监听"全选"按钮点击事件
choose_all.addEventListener('click', function(e) {
move_selection(e, this, SelectBox.move_all, field_id + '_from', field_id + '_to');
});
// 监听"添加选中"按钮点击事件
add_button.addEventListener('click', function(e) {
move_selection(e, this, SelectBox.move, field_id + '_from', field_id + '_to');
});
// 监听"移除选中"按钮点击事件
remove_button.addEventListener('click', function(e) {
move_selection(e, this, SelectBox.move, field_id + '_to', field_id + '_from');
});
// 监听"全部移除"按钮点击事件
clear_all.addEventListener('click', function(e) {
move_selection(e, this, SelectBox.move_all, field_id + '_to', field_id + '_from');
});
// 监听警告页脚点击事件
warning_footer.addEventListener('click', function(e) {
filter_selected_input.value = '';
SelectBox.filter(field_id + '_to', '');
SelectFilter.refresh_filtered_warning(field_id);
SelectFilter.refresh_icons(field_id);
});
// 监听过滤输入框按键事件
filter_input.addEventListener('keypress', function(e) {
SelectFilter.filter_key_press(e, field_id, '_from', '_to');
});
@ -188,6 +206,7 @@ Requires core.js and SelectBox.js.
filter_input.addEventListener('keydown', function(e) {
SelectFilter.filter_key_down(e, field_id, '_from', '_to');
});
// 监听选中项过滤输入框按键事件
filter_selected_input.addEventListener('keypress', function(e) {
SelectFilter.filter_key_press(e, field_id, '_to', '_from');
});
@ -197,11 +216,13 @@ Requires core.js and SelectBox.js.
filter_selected_input.addEventListener('keydown', function(e) {
SelectFilter.filter_key_down(e, field_id, '_to', '_from');
});
// 监听选择框变更事件
selector_div.addEventListener('change', function(e) {
if (e.target.tagName === 'SELECT') {
SelectFilter.refresh_icons(field_id);
}
});
// 监听双击事件
selector_div.addEventListener('dblclick', function(e) {
if (e.target.tagName === 'OPTION') {
if (e.target.closest('select').id === field_id + '_to') {
@ -212,25 +233,38 @@ Requires core.js and SelectBox.js.
SelectFilter.refresh_icons(field_id);
}
});
// 监听表单提交事件
from_box.closest('form').addEventListener('submit', function() {
SelectBox.filter(field_id + '_to', '');
SelectBox.select_all(field_id + '_to');
});
// 初始化选择框
SelectBox.init(field_id + '_from');
SelectBox.init(field_id + '_to');
// Move selected from_box options to to_box
// 将选中的from_box选项移动到to_box
SelectBox.move(field_id + '_from', field_id + '_to');
// Initial icon refresh
// 初始刷新图标
SelectFilter.refresh_icons(field_id);
},
/**
* 检查是否有选项被选中
* @param {HTMLSelectElement} field - 选择框元素
* @returns {boolean} 如果有选项被选中返回true否则返回false
*/
any_selected: function(field) {
// Temporarily add the required attribute and check validity.
// 临时添加required属性并检查有效性
field.required = true;
const any_selected = field.checkValidity();
field.required = false;
return any_selected;
},
/**
* 刷新过滤警告信息
* @param {string} field_id - 字段ID
*/
refresh_filtered_warning: function(field_id) {
const count = SelectBox.get_hidden_node_count(field_id + '_to');
const selector = document.getElementById(field_id + '_selector_chosen');
@ -245,23 +279,41 @@ Requires core.js and SelectBox.js.
selector.className += ' selector-chosen--with-filtered';
}
},
/**
* 刷新过滤选择框
* @param {string} field_id - 字段ID
*/
refresh_filtered_selects: function(field_id) {
SelectBox.filter(field_id + '_from', document.getElementById(field_id + "_input").value);
SelectBox.filter(field_id + '_to', document.getElementById(field_id + "_selected_input").value);
},
/**
* 刷新图标状态
* @param {string} field_id - 字段ID
*/
refresh_icons: function(field_id) {
const from = document.getElementById(field_id + '_from');
const to = document.getElementById(field_id + '_to');
// Disabled if no items are selected.
// 如果没有选项被选中,则禁用按钮
document.getElementById(field_id + '_add').disabled = !SelectFilter.any_selected(from);
document.getElementById(field_id + '_remove').disabled = !SelectFilter.any_selected(to);
// Disabled if the corresponding box is empty.
// 如果对应的选择框为空,则禁用按钮
document.getElementById(field_id + '_add_all').disabled = !from.querySelector('option');
document.getElementById(field_id + '_remove_all').disabled = !to.querySelector('option');
},
/**
* 过滤器按键按下事件处理
* @param {KeyboardEvent} event - 键盘事件
* @param {string} field_id - 字段ID
* @param {string} source - 源选择框后缀
* @param {string} target - 目标选择框后缀
*/
filter_key_press: function(event, field_id, source, target) {
const source_box = document.getElementById(field_id + source);
// don't submit form if user pressed Enter
// 如果用户按下回车键,不要提交表单
if ((event.which && event.which === 13) || (event.keyCode && event.keyCode === 13)) {
source_box.selectedIndex = 0;
SelectBox.move(field_id + source, field_id + target);
@ -269,6 +321,14 @@ Requires core.js and SelectBox.js.
event.preventDefault();
}
},
/**
* 过滤器按键释放事件处理
* @param {KeyboardEvent} event - 键盘事件
* @param {string} field_id - 字段ID
* @param {string} source - 源选择框后缀
* @param {string} filter_input - 过滤输入框后缀
*/
filter_key_up: function(event, field_id, source, filter_input) {
const input = filter_input || '_input';
const source_box = document.getElementById(field_id + source);
@ -278,11 +338,19 @@ Requires core.js and SelectBox.js.
SelectFilter.refresh_filtered_warning(field_id);
SelectFilter.refresh_icons(field_id);
},
/**
* 过滤器按键按下事件处理
* @param {KeyboardEvent} event - 键盘事件
* @param {string} field_id - 字段ID
* @param {string} source - 源选择框后缀
* @param {string} target - 目标选择框后缀
*/
filter_key_down: function(event, field_id, source, target) {
const source_box = document.getElementById(field_id + source);
// right key (39) or left key (37)
// 右键(39)或左键(37)
const direction = source === '_from' ? 39 : 37;
// right arrow -- move across
// 右箭头 -- 移动到另一边
if ((event.which && event.which === direction) || (event.keyCode && event.keyCode === direction)) {
const old_index = source_box.selectedIndex;
SelectBox.move(field_id + source, field_id + target);
@ -291,21 +359,22 @@ Requires core.js and SelectBox.js.
source_box.selectedIndex = (old_index === source_box.length) ? source_box.length - 1 : old_index;
return;
}
// down arrow -- wrap around
// 下箭头 -- 循环选择
if ((event.which && event.which === 40) || (event.keyCode && event.keyCode === 40)) {
source_box.selectedIndex = (source_box.length === source_box.selectedIndex + 1) ? 0 : source_box.selectedIndex + 1;
}
// up arrow -- wrap around
// 上箭头 -- 循环选择
if ((event.which && event.which === 38) || (event.keyCode && event.keyCode === 38)) {
source_box.selectedIndex = (source_box.selectedIndex === 0) ? source_box.length - 1 : source_box.selectedIndex - 1;
}
}
};
// 页面加载完成后初始化所有选择过滤器
window.addEventListener('load', function(e) {
document.querySelectorAll('select.selectfilter, select.selectfilterstacked').forEach(function(el) {
const data = el.dataset;
SelectFilter.init(el.id, data.fieldName, parseInt(data.isStacked, 10));
});
});
}
}

@ -1,24 +1,40 @@
/*global gettext, interpolate, ngettext, Actions*/
'use strict';
{
/**
* 显示指定选择器的元素
* @param {string} selector - CSS选择器
*/
function show(selector) {
document.querySelectorAll(selector).forEach(function(el) {
el.classList.remove('hidden');
});
}
/**
* 隐藏指定选择器的元素
* @param {string} selector - CSS选择器
*/
function hide(selector) {
document.querySelectorAll(selector).forEach(function(el) {
el.classList.add('hidden');
});
}
/**
* 显示操作问题提示
* @param {Object} options - 配置选项
*/
function showQuestion(options) {
hide(options.acrossClears);
show(options.acrossQuestions);
hide(options.allContainer);
}
/**
* 显示清除跨页选择链接
* @param {Object} options - 配置选项
*/
function showClear(options) {
show(options.acrossClears);
hide(options.acrossQuestions);
@ -27,6 +43,10 @@
hide(options.counterContainer);
}
/**
* 重置操作状态
* @param {Object} options - 配置选项
*/
function reset(options) {
hide(options.acrossClears);
hide(options.acrossQuestions);
@ -34,6 +54,10 @@
show(options.counterContainer);
}
/**
* 清除跨页选择状态
* @param {Object} options - 配置选项
*/
function clearAcross(options) {
reset(options);
const acrossInputs = document.querySelectorAll(options.acrossInput);
@ -43,6 +67,12 @@
document.querySelector(options.actionContainer).classList.remove(options.selectedClass);
}
/**
* 选中/取消选中复选框
* @param {NodeList} actionCheckboxes - 操作复选框列表
* @param {Object} options - 配置选项
* @param {boolean} checked - 是否选中
*/
function checker(actionCheckboxes, options, checked) {
if (checked) {
showQuestion(options);
@ -55,20 +85,27 @@
});
}
/**
* 更新选中计数器
* @param {NodeList} actionCheckboxes - 操作复选框列表
* @param {Object} options - 配置选项
*/
function updateCounter(actionCheckboxes, options) {
// 计算选中的复选框数量
const sel = Array.from(actionCheckboxes).filter(function(el) {
return el.checked;
}).length;
const counter = document.querySelector(options.counterContainer);
// data-actions-icnt is defined in the generated HTML
// and contains the total amount of objects in the queryset
// data-actions-icnt在生成的HTML中定义包含查询集中的对象总数
const actions_icnt = Number(counter.dataset.actionsIcnt);
// 更新计数器文本
counter.textContent = interpolate(
ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), {
sel: sel,
cnt: actions_icnt
}, true);
const allToggle = document.getElementById(options.allToggleId);
// 如果所有复选框都被选中,则选中全选复选框
allToggle.checked = sel === actionCheckboxes.length;
if (allToggle.checked) {
showQuestion(options);
@ -77,23 +114,30 @@
}
}
// 默认配置选项
const defaults = {
actionContainer: "div.actions",
counterContainer: "span.action-counter",
allContainer: "div.actions span.all",
acrossInput: "div.actions input.select-across",
acrossQuestions: "div.actions span.question",
acrossClears: "div.actions span.clear",
allToggleId: "action-toggle",
selectedClass: "selected"
actionContainer: "div.actions", // 操作容器
counterContainer: "span.action-counter", // 计数器容器
allContainer: "div.actions span.all", // 全选容器
acrossInput: "div.actions input.select-across", // 跨页选择输入框
acrossQuestions: "div.actions span.question", // 跨页选择问题
acrossClears: "div.actions span.clear", // 跨页选择清除
allToggleId: "action-toggle", // 全选切换ID
selectedClass: "selected" // 选中项的CSS类
};
/**
* 初始化操作功能
* @param {NodeList} actionCheckboxes - 操作复选框列表
* @param {Object} options - 配置选项
*/
window.Actions = function(actionCheckboxes, options) {
options = Object.assign({}, defaults, options);
let list_editable_changed = false;
let lastChecked = null;
let shiftPressed = false;
let list_editable_changed = false; // 可编辑列表是否已更改
let lastChecked = null; // 最后选中的复选框
let shiftPressed = false; // Shift键是否按下
// 监听键盘事件记录Shift键状态
document.addEventListener('keydown', (event) => {
shiftPressed = event.shiftKey;
});
@ -102,11 +146,13 @@
shiftPressed = event.shiftKey;
});
// 监听全选复选框点击事件
document.getElementById(options.allToggleId).addEventListener('click', function(event) {
checker(actionCheckboxes, options, this.checked);
updateCounter(actionCheckboxes, options);
});
// 监听跨页选择问题链接点击事件
document.querySelectorAll(options.acrossQuestions + " a").forEach(function(el) {
el.addEventListener('click', function(event) {
event.preventDefault();
@ -118,6 +164,7 @@
});
});
// 监听跨页选择清除链接点击事件
document.querySelectorAll(options.acrossClears + " a").forEach(function(el) {
el.addEventListener('click', function(event) {
event.preventDefault();
@ -128,6 +175,12 @@
});
});
/**
* 获取受Shift键影响的复选框
* @param {HTMLInputElement} target - 目标复选框
* @param {boolean} withModifier - 是否按下修饰键
* @returns {Array} 受影响的复选框数组
*/
function affectedCheckboxes(target, withModifier) {
const multiSelect = (lastChecked && withModifier && lastChecked !== target);
if (!multiSelect) {
@ -142,6 +195,7 @@
return filtered;
};
// 监听结果列表中的变更事件
Array.from(document.getElementById('result_list').tBodies).forEach(function(el) {
el.addEventListener('change', function(event) {
const target = event.target;
@ -156,8 +210,10 @@
});
});
// 监听索引按钮点击事件
document.querySelector('#changelist-form button[name=index]').addEventListener('click', function(event) {
if (list_editable_changed) {
// 如果有未保存的更改,提示用户确认
const confirmed = confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."));
if (!confirmed) {
event.preventDefault();
@ -166,10 +222,11 @@
});
const el = document.querySelector('#changelist-form input[name=_save]');
// The button does not exist if no fields are editable.
// 如果存在保存按钮(当有可编辑字段时存在)
if (el) {
el.addEventListener('click', function(event) {
if (document.querySelector('[name=action]').value) {
// 根据是否有未保存的更改显示不同的提示信息
const text = list_editable_changed
? gettext("You have selected an action, but you havent saved your changes to individual fields yet. Please click OK to save. Youll need to re-run the action.")
: gettext("You have selected an action, and you havent made any changes on individual fields. Youre probably looking for the Go button rather than the Save button.");
@ -179,13 +236,11 @@
}
});
}
// Sync counter when navigating to the page, such as through the back
// button.
// 页面显示时同步计数器,例如通过后退按钮导航到页面时
window.addEventListener('pageshow', (event) => updateCounter(actionCheckboxes, options));
};
// Call function fn when the DOM is loaded and ready. If it is already
// loaded, call the function now.
// 页面加载完成后调用指定函数。如果页面已经加载完成,则立即调用该函数
// http://youmightnotneedjquery.com/#ready
function ready(fn) {
if (document.readyState !== 'loading') {
@ -195,10 +250,11 @@
}
}
// 页面加载完成后初始化操作功能
ready(function() {
const actionsEls = document.querySelectorAll('tr input.action-select');
if (actionsEls.length > 0) {
Actions(actionsEls);
}
});
}
}

@ -1,51 +1,60 @@
/*global Calendar, findPosX, findPosY, get_format, gettext, gettext_noop, interpolate, ngettext, quickElement*/
// Inserts shortcut buttons after all of the following:
// 在以下元素后插入快捷按钮:
// <input type="text" class="vDateField">
// <input type="text" class="vTimeField">
'use strict';
{
const DateTimeShortcuts = {
calendars: [],
calendarInputs: [],
clockInputs: [],
clockHours: {
calendars: [], // 日历数组
calendarInputs: [], // 日历输入框数组
clockInputs: [], // 时钟输入框数组
clockHours: { // 时钟快捷时间选项
default_: [
[gettext_noop('Now'), -1],
[gettext_noop('Midnight'), 0],
[gettext_noop('6 a.m.'), 6],
[gettext_noop('Noon'), 12],
[gettext_noop('6 p.m.'), 18]
[gettext_noop('Now'), -1], // 现在
[gettext_noop('Midnight'), 0], // 午夜
[gettext_noop('6 a.m.'), 6], // 早上6点
[gettext_noop('Noon'), 12], // 中午
[gettext_noop('6 p.m.'), 18] // 晚上6点
]
},
dismissClockFunc: [],
dismissCalendarFunc: [],
calendarDivName1: 'calendarbox', // name of calendar <div> that gets toggled
calendarDivName2: 'calendarin', // name of <div> that contains calendar
calendarLinkName: 'calendarlink', // name of the link that is used to toggle
clockDivName: 'clockbox', // name of clock <div> that gets toggled
clockLinkName: 'clocklink', // name of the link that is used to toggle
shortCutsClass: 'datetimeshortcuts', // class of the clock and cal shortcuts
timezoneWarningClass: 'timezonewarning', // class of the warning for timezone mismatch
timezoneOffset: 0,
dismissClockFunc: [], // 关闭时钟函数数组
dismissCalendarFunc: [], // 关闭日历函数数组
calendarDivName1: 'calendarbox', // 日历<div>的切换名称
calendarDivName2: 'calendarin', // 包含日历的<div>名称
calendarLinkName: 'calendarlink', // 用于切换的日历链接名称
clockDivName: 'clockbox', // 时钟<div>的切换名称
clockLinkName: 'clocklink', // 用于切换的时钟链接名称
shortCutsClass: 'datetimeshortcuts', // 时钟和日历快捷方式的类名
timezoneWarningClass: 'timezonewarning', // 时区不匹配警告的类名
timezoneOffset: 0, // 时区偏移量
/**
* 初始化函数
*/
init: function() {
// 获取服务器时区偏移量
const serverOffset = document.body.dataset.adminUtcOffset;
if (serverOffset) {
// 计算本地时区偏移量
const localOffset = new Date().getTimezoneOffset() * -60;
// 计算时区偏移差值
DateTimeShortcuts.timezoneOffset = localOffset - serverOffset;
}
// 遍历所有input元素
for (const inp of document.getElementsByTagName('input')) {
// 为时间输入框添加时钟控件
if (inp.type === 'text' && inp.classList.contains('vTimeField')) {
DateTimeShortcuts.addClock(inp);
DateTimeShortcuts.addTimezoneWarning(inp);
}
// 为日期输入框添加日历控件
else if (inp.type === 'text' && inp.classList.contains('vDateField')) {
DateTimeShortcuts.addCalendar(inp);
DateTimeShortcuts.addTimezoneWarning(inp);
}
}
},
// Return the current time while accounting for the server timezone.
// 返回当前时间,同时考虑服务器时区
now: function() {
const serverOffset = document.body.dataset.adminUtcOffset;
if (serverOffset) {
@ -57,23 +66,24 @@
return new Date();
}
},
// Add a warning when the time zone in the browser and backend do not match.
// 当浏览器和后端时区不匹配时添加警告
addTimezoneWarning: function(inp) {
const warningClass = DateTimeShortcuts.timezoneWarningClass;
let timezoneOffset = DateTimeShortcuts.timezoneOffset / 3600;
// Only warn if there is a time zone mismatch.
// 如果时区匹配,则不显示警告
if (!timezoneOffset) {
return;
}
// Check if warning is already there.
// 检查是否已存在警告
if (inp.parentNode.querySelectorAll('.' + warningClass).length) {
return;
}
let message;
if (timezoneOffset > 0) {
// 用户时区比服务器时区快
message = ngettext(
'Note: You are %s hour ahead of server time.',
'Note: You are %s hours ahead of server time.',
@ -81,6 +91,7 @@
);
}
else {
// 用户时区比服务器时区慢
timezoneOffset *= -1;
message = ngettext(
'Note: You are %s hour behind server time.',
@ -88,23 +99,26 @@
timezoneOffset
);
}
// 插入时区偏移量到消息中
message = interpolate(message, [timezoneOffset]);
// 创建警告元素
const warning = document.createElement('div');
warning.classList.add('help', warningClass);
warning.textContent = message;
inp.parentNode.appendChild(warning);
},
// Add clock widget to a given field
// 为指定字段添加时钟控件
addClock: function(inp) {
const num = DateTimeShortcuts.clockInputs.length;
DateTimeShortcuts.clockInputs[num] = inp;
DateTimeShortcuts.dismissClockFunc[num] = function() { DateTimeShortcuts.dismissClock(num); return true; };
// Shortcut links (clock icon and "Now" link)
// 快捷链接(时钟图标和"现在"链接)
const shortcuts_span = document.createElement('span');
shortcuts_span.className = DateTimeShortcuts.shortCutsClass;
inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
// 创建"现在"链接
const now_link = document.createElement('a');
now_link.href = "#";
now_link.textContent = gettext('Now');
@ -112,16 +126,18 @@
e.preventDefault();
DateTimeShortcuts.handleClockQuicklink(num, -1);
});
// 创建时钟链接
const clock_link = document.createElement('a');
clock_link.href = '#';
clock_link.id = DateTimeShortcuts.clockLinkName + num;
clock_link.addEventListener('click', function(e) {
e.preventDefault();
// avoid triggering the document click handler to dismiss the clock
// 避免触发文档点击处理程序关闭时钟
e.stopPropagation();
DateTimeShortcuts.openClock(num);
});
// 创建时钟图标
quickElement(
'span', clock_link, '',
'class', 'clock-icon',
@ -132,19 +148,19 @@
shortcuts_span.appendChild(document.createTextNode('\u00A0|\u00A0'));
shortcuts_span.appendChild(clock_link);
// Create clock link div
// 创建时钟链接div
//
// Markup looks like:
// 标记结构如下:
// <div id="clockbox1" class="clockbox module">
// <h2>Choose a time</h2>
// <h2>选择时间</h2>
// <ul class="timelist">
// <li><a href="#">Now</a></li>
// <li><a href="#">Midnight</a></li>
// <li><a href="#">6 a.m.</a></li>
// <li><a href="#">Noon</a></li>
// <li><a href="#">6 p.m.</a></li>
// <li><a href="#">现在</a></li>
// <li><a href="#">午夜</a></li>
// <li><a href="#">早上6点</a></li>
// <li><a href="#">中午</a></li>
// <li><a href="#">晚上6点</a></li>
// </ul>
// <p class="calendar-cancel"><a href="#">Cancel</a></p>
// <p class="calendar-cancel"><a href="#">取消</a></p>
// </div>
const clock_box = document.createElement('div');
@ -158,9 +174,9 @@
quickElement('h2', clock_box, gettext('Choose a time'));
const time_list = quickElement('ul', clock_box);
time_list.className = 'timelist';
// The list of choices can be overridden in JavaScript like this:
// 可以通过JavaScript覆盖选择列表例如:
// DateTimeShortcuts.clockHours.name = [['3 a.m.', 3]];
// where name is the name attribute of the <input>.
// 其中name是<input>元素的name属性
const name = typeof DateTimeShortcuts.clockHours[inp.name] === 'undefined' ? 'default_' : inp.name;
DateTimeShortcuts.clockHours[name].forEach(function(element) {
const time_link = quickElement('a', quickElement('li', time_list), gettext(element[0]), 'href', '#');
@ -170,6 +186,7 @@
});
});
// 创建取消按钮
const cancel_p = quickElement('p', clock_box);
cancel_p.className = 'calendar-cancel';
const cancel_link = quickElement('a', cancel_p, gettext('Cancel'), 'href', '#');
@ -178,61 +195,65 @@
DateTimeShortcuts.dismissClock(num);
});
// 监听键盘事件ESC键关闭弹窗
document.addEventListener('keyup', function(event) {
if (event.which === 27) {
// ESC key closes popup
// ESC键关闭弹窗
DateTimeShortcuts.dismissClock(num);
event.preventDefault();
}
});
},
// 打开时钟
openClock: function(num) {
const clock_box = document.getElementById(DateTimeShortcuts.clockDivName + num);
const clock_link = document.getElementById(DateTimeShortcuts.clockLinkName + num);
// Recalculate the clockbox position
// is it left-to-right or right-to-left layout ?
// 重新计算时钟框位置
// 判断是左到右还是右到左布局
if (window.getComputedStyle(document.body).direction !== 'rtl') {
clock_box.style.left = findPosX(clock_link) + 17 + 'px';
}
else {
// since style's width is in em, it'd be tough to calculate
// px value of it. let's use an estimated px for now
// 由于样式宽度是em单位难以计算像素值这里使用估算值
clock_box.style.left = findPosX(clock_link) - 110 + 'px';
}
clock_box.style.top = Math.max(0, findPosY(clock_link) - 30) + 'px';
// Show the clock box
// 显示时钟框
clock_box.style.display = 'block';
document.addEventListener('click', DateTimeShortcuts.dismissClockFunc[num]);
},
// 关闭时钟
dismissClock: function(num) {
document.getElementById(DateTimeShortcuts.clockDivName + num).style.display = 'none';
document.removeEventListener('click', DateTimeShortcuts.dismissClockFunc[num]);
},
// 处理时钟快捷链接
handleClockQuicklink: function(num, val) {
let d;
if (val === -1) {
d = DateTimeShortcuts.now();
d = DateTimeShortcuts.now(); // 当前时间
}
else {
d = new Date(1970, 1, 1, val, 0, 0, 0);
d = new Date(1970, 1, 1, val, 0, 0, 0); // 指定时间
}
DateTimeShortcuts.clockInputs[num].value = d.strftime(get_format('TIME_INPUT_FORMATS')[0]);
DateTimeShortcuts.clockInputs[num].focus();
DateTimeShortcuts.dismissClock(num);
},
// Add calendar widget to a given field.
// 为指定字段添加日历控件
addCalendar: function(inp) {
const num = DateTimeShortcuts.calendars.length;
DateTimeShortcuts.calendarInputs[num] = inp;
DateTimeShortcuts.dismissCalendarFunc[num] = function() { DateTimeShortcuts.dismissCalendar(num); return true; };
// Shortcut links (calendar icon and "Today" link)
// 快捷链接(日历图标和"今天"链接)
const shortcuts_span = document.createElement('span');
shortcuts_span.className = DateTimeShortcuts.shortCutsClass;
inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
// 创建"今天"链接
const today_link = document.createElement('a');
today_link.href = '#';
today_link.appendChild(document.createTextNode(gettext('Today')));
@ -240,15 +261,17 @@
e.preventDefault();
DateTimeShortcuts.handleCalendarQuickLink(num, 0);
});
// 创建日历链接
const cal_link = document.createElement('a');
cal_link.href = '#';
cal_link.id = DateTimeShortcuts.calendarLinkName + num;
cal_link.addEventListener('click', function(e) {
e.preventDefault();
// avoid triggering the document click handler to dismiss the calendar
// 避免触发文档点击处理程序关闭日历
e.stopPropagation();
DateTimeShortcuts.openCalendar(num);
});
// 创建日期图标
quickElement(
'span', cal_link, '',
'class', 'date-icon',
@ -259,9 +282,9 @@
shortcuts_span.appendChild(document.createTextNode('\u00A0|\u00A0'));
shortcuts_span.appendChild(cal_link);
// Create calendarbox div.
// 创建日历框div
//
// Markup looks like:
// 标记结构如下:
//
// <div id="calendarbox3" class="calendarbox module">
// <h2>
@ -272,9 +295,9 @@
// <!-- (cal) -->
// </div>
// <div class="calendar-shortcuts">
// <a href="#">Yesterday</a> | <a href="#">Today</a> | <a href="#">Tomorrow</a>
// <a href="#">昨天</a> | <a href="#">今天</a> | <a href="#">明天</a>
// </div>
// <p class="calendar-cancel"><a href="#">Cancel</a></p>
// <p class="calendar-cancel"><a href="#">取消</a></p>
// </div>
const cal_box = document.createElement('div');
cal_box.style.display = 'none';
@ -284,7 +307,7 @@
document.body.appendChild(cal_box);
cal_box.addEventListener('click', function(e) { e.stopPropagation(); });
// next-prev links
// 上一页-下一页链接
const cal_nav = quickElement('div', cal_box);
const cal_nav_prev = quickElement('a', cal_nav, '<', 'href', '#');
cal_nav_prev.className = 'calendarnav-previous';
@ -300,13 +323,13 @@
DateTimeShortcuts.drawNext(num);
});
// main box
// 主体框
const cal_main = quickElement('div', cal_box, '', 'id', DateTimeShortcuts.calendarDivName2 + num);
cal_main.className = 'calendar';
DateTimeShortcuts.calendars[num] = new Calendar(DateTimeShortcuts.calendarDivName2 + num, DateTimeShortcuts.handleCalendarCallback(num));
DateTimeShortcuts.calendars[num].drawCurrent();
// calendar shortcuts
// 日历快捷方式
const shortcuts = quickElement('div', cal_box);
shortcuts.className = 'calendar-shortcuts';
let day_link = quickElement('a', shortcuts, gettext('Yesterday'), 'href', '#');
@ -327,7 +350,7 @@
DateTimeShortcuts.handleCalendarQuickLink(num, +1);
});
// cancel bar
// 取消栏
const cancel_p = quickElement('p', cal_box);
cancel_p.className = 'calendar-cancel';
const cancel_link = quickElement('a', cancel_p, gettext('Cancel'), 'href', '#');
@ -335,21 +358,23 @@
e.preventDefault();
DateTimeShortcuts.dismissCalendar(num);
});
// 监听键盘事件ESC键关闭弹窗
document.addEventListener('keyup', function(event) {
if (event.which === 27) {
// ESC key closes popup
// ESC键关闭弹窗
DateTimeShortcuts.dismissCalendar(num);
event.preventDefault();
}
});
},
// 打开日历
openCalendar: function(num) {
const cal_box = document.getElementById(DateTimeShortcuts.calendarDivName1 + num);
const cal_link = document.getElementById(DateTimeShortcuts.calendarLinkName + num);
const inp = DateTimeShortcuts.calendarInputs[num];
// Determine if the current value in the input has a valid date.
// If so, draw the calendar with that date's year and month.
// 确定输入框中的当前值是否有有效日期
// 如果有,则使用该日期的年份和月份绘制日历
if (inp.value) {
const format = get_format('DATE_INPUT_FORMATS')[0];
const selected = inp.value.strptime(format);
@ -361,14 +386,13 @@
}
}
// Recalculate the clockbox position
// is it left-to-right or right-to-left layout ?
// 重新计算日历框位置
// 判断是左到右还是右到左布局
if (window.getComputedStyle(document.body).direction !== 'rtl') {
cal_box.style.left = findPosX(cal_link) + 17 + 'px';
}
else {
// since style's width is in em, it'd be tough to calculate
// px value of it. let's use an estimated px for now
// 由于样式宽度是em单位难以计算像素值这里使用估算值
cal_box.style.left = findPosX(cal_link) - 180 + 'px';
}
cal_box.style.top = Math.max(0, findPosY(cal_link) - 75) + 'px';
@ -376,16 +400,20 @@
cal_box.style.display = 'block';
document.addEventListener('click', DateTimeShortcuts.dismissCalendarFunc[num]);
},
// 关闭日历
dismissCalendar: function(num) {
document.getElementById(DateTimeShortcuts.calendarDivName1 + num).style.display = 'none';
document.removeEventListener('click', DateTimeShortcuts.dismissCalendarFunc[num]);
},
// 绘制上个月
drawPrev: function(num) {
DateTimeShortcuts.calendars[num].drawPreviousMonth();
},
// 绘制下个月
drawNext: function(num) {
DateTimeShortcuts.calendars[num].drawNextMonth();
},
// 处理日历回调函数
handleCalendarCallback: function(num) {
const format = get_format('DATE_INPUT_FORMATS')[0];
return function(y, m, d) {
@ -394,6 +422,7 @@
document.getElementById(DateTimeShortcuts.calendarDivName1 + num).style.display = 'none';
};
},
// 处理日历快捷链接
handleCalendarQuickLink: function(num, offset) {
const d = DateTimeShortcuts.now();
d.setDate(d.getDate() + offset);
@ -405,4 +434,4 @@
window.addEventListener('load', DateTimeShortcuts.init);
window.DateTimeShortcuts = DateTimeShortcuts;
}
}

@ -1,12 +1,14 @@
/*global SelectBox, interpolate*/
// Handles related-objects functionality: lookup link for raw_id_fields
// and Add Another links.
// 处理关联对象功能raw_id_fields的查找链接和"添加另一个"链接
'use strict';
{
const $ = django.jQuery;
let popupIndex = 0;
const relatedWindows = [];
const relatedWindows = []; // 关联窗口数组
/**
* 关闭所有子弹窗
*/
function dismissChildPopups() {
relatedWindows.forEach(function(win) {
if(!win.closed) {
@ -16,6 +18,9 @@
});
}
/**
* 设置弹窗索引
*/
function setPopupIndex() {
if(document.getElementsByName("_popup").length > 0) {
const index = window.name.lastIndexOf("__") + 2;
@ -25,82 +30,130 @@
}
}
/**
* 添加弹窗索引
* @param {string} name - 窗口名称
* @returns {string} 添加索引后的名称
*/
function addPopupIndex(name) {
return name + "__" + (popupIndex + 1);
}
/**
* 移除弹窗索引
* @param {string} name - 窗口名称
* @returns {string} 移除索引后的名称
*/
function removePopupIndex(name) {
return name.replace(new RegExp("__" + (popupIndex + 1) + "$"), '');
}
/**
* 显示管理弹窗
* @param {HTMLElement} triggeringLink - 触发链接元素
* @param {RegExp} name_regexp - 名称正则表达式
* @param {boolean} add_popup - 是否添加弹窗参数
* @returns {boolean} 总是返回false
*/
function showAdminPopup(triggeringLink, name_regexp, add_popup) {
const name = addPopupIndex(triggeringLink.id.replace(name_regexp, ''));
const href = new URL(triggeringLink.href);
const name = addPopupIndex(triggeringLink.id.replace(name_regexp, '')); // 生成窗口名称
const href = new URL(triggeringLink.href); // 创建URL对象
if (add_popup) {
href.searchParams.set('_popup', 1);
href.searchParams.set('_popup', 1); // 添加弹窗参数
}
// 打开新窗口
const win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
relatedWindows.push(win);
win.focus();
relatedWindows.push(win); // 将窗口添加到关联窗口数组
win.focus(); // 聚焦到新窗口
return false;
}
/**
* 显示关联对象查找弹窗
* @param {HTMLElement} triggeringLink - 触发链接元素
* @returns {boolean} 显示弹窗的结果
*/
function showRelatedObjectLookupPopup(triggeringLink) {
return showAdminPopup(triggeringLink, /^lookup_/, true);
}
/**
* 关闭关联查找弹窗
* @param {Window} win - 弹窗对象
* @param {string} chosenId - 选中的ID
*/
function dismissRelatedLookupPopup(win, chosenId) {
const name = removePopupIndex(win.name);
const elem = document.getElementById(name);
const name = removePopupIndex(win.name); // 移除索引后的名称
const elem = document.getElementById(name); // 获取对应元素
// 处理多对多字段
if (elem.classList.contains('vManyToManyRawIdAdminField') && elem.value) {
elem.value += ',' + chosenId;
} else {
elem.value = chosenId;
elem.value = chosenId; // 设置选中的ID
}
$(elem).trigger('change');
const index = relatedWindows.indexOf(win);
$(elem).trigger('change'); // 触发change事件
const index = relatedWindows.indexOf(win); // 查找窗口在数组中的索引
if (index > -1) {
relatedWindows.splice(index, 1);
relatedWindows.splice(index, 1); // 从数组中移除
}
win.close();
win.close(); // 关闭窗口
}
/**
* 显示关联对象弹窗
* @param {HTMLElement} triggeringLink - 触发链接元素
* @returns {boolean} 显示弹窗的结果
*/
function showRelatedObjectPopup(triggeringLink) {
return showAdminPopup(triggeringLink, /^(change|add|delete)_/, false);
}
/**
* 更新关联对象链接
* @param {HTMLElement} triggeringLink - 触发链接元素
*/
function updateRelatedObjectLinks(triggeringLink) {
const $this = $(triggeringLink);
// 获取相关的查看、修改、删除链接
const siblings = $this.nextAll('.view-related, .change-related, .delete-related');
if (!siblings.length) {
return;
}
const value = $this.val();
const value = $this.val(); // 获取当前值
if (value) {
// 有值时更新链接
siblings.each(function() {
const elm = $(this);
elm.attr('href', elm.attr('data-href-template').replace('__fk__', value));
elm.removeAttr('aria-disabled');
});
} else {
// 无值时禁用链接
siblings.removeAttr('href');
siblings.attr('aria-disabled', true);
}
}
/**
* 更新关联选择框选项
* @param {HTMLElement} currentSelect - 当前选择框
* @param {Window} win - 窗口对象
* @param {string} objId - 对象ID
* @param {string} newRepr - 新的表示
* @param {string} newId - 新的ID
* @param {Array} skipIds - 跳过的ID数组
*/
function updateRelatedSelectsOptions(currentSelect, win, objId, newRepr, newId, skipIds = []) {
// After create/edit a model from the options next to the current
// select (+ or :pencil:) update ForeignKey PK of the rest of selects
// in the page.
// 在从当前选择框旁边选项中创建/编辑模型后更新页面中其余选择框的ForeignKey PK
const path = win.location.pathname;
// Extract the model from the popup url '.../<model>/add/' or
// '.../<model>/<id>/change/' depending the action (add or change).
// 从弹窗URL中提取模型 '.../<model>/add/' 或 '.../<model>/<id>/change/' 取决于操作类型
const modelName = path.split('/')[path.split('/').length - (objId ? 4 : 3)];
// Select elements with a specific model reference and context of "available-source".
// 选择具有特定模型引用和"available-source"上下文的元素
const selectsRelated = document.querySelectorAll(`[data-model-ref="${modelName}"] [data-context="available-source"]`);
selectsRelated.forEach(function(select) {
// 跳过当前选择框和指定ID的选择框
if (currentSelect === select || skipIds && skipIds.includes(select.id)) {
return;
}
@ -108,9 +161,10 @@
let option = select.querySelector(`option[value="${objId}"]`);
if (!option) {
// 如果选项不存在,则创建新选项
option = new Option(newRepr, newId);
select.options.add(option);
// Update SelectBox cache for related fields.
// 更新关联字段的SelectBox缓存
if (window.SelectBox !== undefined && !SelectBox.cache[currentSelect.id]) {
SelectBox.add_to_cache(select.id, option);
SelectBox.redisplay(select.id);
@ -118,27 +172,36 @@
return;
}
// 更新选项的文本和值
option.textContent = newRepr;
option.value = newId;
});
}
/**
* 关闭添加关联对象弹窗
* @param {Window} win - 弹窗对象
* @param {string} newId - 新的ID
* @param {string} newRepr - 新的表示
*/
function dismissAddRelatedObjectPopup(win, newId, newRepr) {
const name = removePopupIndex(win.name);
const elem = document.getElementById(name);
const name = removePopupIndex(win.name); // 移除索引后的名称
const elem = document.getElementById(name); // 获取对应元素
if (elem) {
const elemName = elem.nodeName.toUpperCase();
const elemName = elem.nodeName.toUpperCase(); // 获取元素名称
if (elemName === 'SELECT') {
// 如果是选择框,添加新选项
elem.options[elem.options.length] = new Option(newRepr, newId, true, true);
updateRelatedSelectsOptions(elem, win, null, newRepr, newId);
} else if (elemName === 'INPUT') {
// 如果是输入框,处理多对多字段
if (elem.classList.contains('vManyToManyRawIdAdminField') && elem.value) {
elem.value += ',' + newId;
} else {
elem.value = newId;
}
}
// Trigger a change event to update related links if required.
// 触发change事件以更新相关链接
$(elem).trigger('change');
} else {
const toId = name + "_to";
@ -146,22 +209,31 @@
const o = new Option(newRepr, newId);
SelectBox.add_to_cache(toId, o);
SelectBox.redisplay(toId);
// 如果目标元素是选择框,则更新相关选择框选项
if (toElem && toElem.nodeName.toUpperCase() === 'SELECT') {
const skipIds = [name + "_from"];
updateRelatedSelectsOptions(toElem, win, null, newRepr, newId, skipIds);
}
}
const index = relatedWindows.indexOf(win);
const index = relatedWindows.indexOf(win); // 查找窗口在数组中的索引
if (index > -1) {
relatedWindows.splice(index, 1);
relatedWindows.splice(index, 1); // 从数组中移除
}
win.close();
win.close(); // 关闭窗口
}
/**
* 关闭修改关联对象弹窗
* @param {Window} win - 弹窗对象
* @param {string} objId - 对象ID
* @param {string} newRepr - 新的表示
* @param {string} newId - 新的ID
*/
function dismissChangeRelatedObjectPopup(win, objId, newRepr, newId) {
const id = removePopupIndex(win.name.replace(/^edit_/, ''));
const selectsSelector = interpolate('#%s, #%s_from, #%s_to', [id, id, id]);
const selects = $(selectsSelector);
const id = removePopupIndex(win.name.replace(/^edit_/, '')); // 移除索引后的ID
const selectsSelector = interpolate('#%s, #%s_from, #%s_to', [id, id, id]); // 选择器
const selects = $(selectsSelector); // 获取选择框元素
// 更新选项的文本和值
selects.find('option').each(function() {
if (this.value === objId) {
this.textContent = newRepr;
@ -169,35 +241,43 @@
}
}).trigger('change');
updateRelatedSelectsOptions(selects[0], win, objId, newRepr, newId);
// 更新Select2显示
selects.next().find('.select2-selection__rendered').each(function() {
// The element can have a clear button as a child.
// Use the lastChild to modify only the displayed value.
// 元素可能有清除按钮作为子元素
// 使用lastChild只修改显示的值
this.lastChild.textContent = newRepr;
this.title = newRepr;
});
const index = relatedWindows.indexOf(win);
const index = relatedWindows.indexOf(win); // 查找窗口在数组中的索引
if (index > -1) {
relatedWindows.splice(index, 1);
relatedWindows.splice(index, 1); // 从数组中移除
}
win.close();
win.close(); // 关闭窗口
}
/**
* 关闭删除关联对象弹窗
* @param {Window} win - 弹窗对象
* @param {string} objId - 对象ID
*/
function dismissDeleteRelatedObjectPopup(win, objId) {
const id = removePopupIndex(win.name.replace(/^delete_/, ''));
const selectsSelector = interpolate('#%s, #%s_from, #%s_to', [id, id, id]);
const selects = $(selectsSelector);
const id = removePopupIndex(win.name.replace(/^delete_/, '')); // 移除索引后的ID
const selectsSelector = interpolate('#%s, #%s_from, #%s_to', [id, id, id]); // 选择器
const selects = $(selectsSelector); // 获取选择框元素
// 移除对应的选项
selects.find('option').each(function() {
if (this.value === objId) {
$(this).remove();
}
}).trigger('change');
const index = relatedWindows.indexOf(win);
const index = relatedWindows.indexOf(win); // 查找窗口在数组中的索引
if (index > -1) {
relatedWindows.splice(index, 1);
relatedWindows.splice(index, 1); // 从数组中移除
}
win.close();
win.close(); // 关闭窗口
}
// 将函数暴露到全局作用域
window.showRelatedObjectLookupPopup = showRelatedObjectLookupPopup;
window.dismissRelatedLookupPopup = dismissRelatedLookupPopup;
window.showRelatedObjectPopup = showRelatedObjectPopup;
@ -208,20 +288,24 @@
window.dismissChildPopups = dismissChildPopups;
window.relatedWindows = relatedWindows;
// Kept for backward compatibility
// 为向后兼容保留
window.showAddAnotherPopup = showRelatedObjectPopup;
window.dismissAddAnotherPopup = dismissAddRelatedObjectPopup;
// 页面卸载时关闭所有子弹窗
window.addEventListener('unload', function(evt) {
window.dismissChildPopups();
});
// 文档加载完成后初始化
$(document).ready(function() {
setPopupIndex();
setPopupIndex(); // 设置弹窗索引
// 处理数据弹窗开启器点击事件
$("a[data-popup-opener]").on('click', function(event) {
event.preventDefault();
opener.dismissRelatedLookupPopup(window, $(this).data("popup-opener"));
});
// 处理相关控件包装链接点击事件
$('body').on('click', '.related-widget-wrapper-link[data-popup="yes"]', function(e) {
e.preventDefault();
if (this.href) {
@ -232,6 +316,7 @@
}
}
});
// 处理相关控件包装选择框变更事件
$('body').on('change', '.related-widget-wrapper select', function(e) {
const event = $.Event('django:update-related');
$(this).trigger(event);
@ -239,7 +324,9 @@
updateRelatedObjectLinks(this);
}
});
// 触发选择框变更事件
$('.related-widget-wrapper select').trigger('change');
// 处理相关查找点击事件
$('body').on('click', '.related-lookup', function(e) {
e.preventDefault();
const event = $.Event('django:lookup-related');
@ -249,4 +336,4 @@
}
});
});
}
}

@ -2,17 +2,25 @@
{
const $ = django.jQuery;
/**
* Django管理后台Select2插件
* 为选择框元素启用Select2功能并配置AJAX数据源
* @returns {jQuery} jQuery对象
*/
$.fn.djangoAdminSelect2 = function() {
// 遍历所有选中的元素
$.each(this, function(i, element) {
// 对每个元素应用select2插件
$(element).select2({
ajax: {
// 配置AJAX数据参数
data: (params) => {
return {
term: params.term,
page: params.page,
app_label: element.dataset.appLabel,
model_name: element.dataset.modelName,
field_name: element.dataset.fieldName
term: params.term, // 搜索关键词
page: params.page, // 分页页码
app_label: element.dataset.appLabel, // 应用标签
model_name: element.dataset.modelName, // 模型名称
field_name: element.dataset.fieldName // 字段名称
};
}
}
@ -21,13 +29,16 @@
return this;
};
// 文档加载完成后初始化
$(function() {
// Initialize all autocomplete widgets except the one in the template
// form used when a new formset is added.
// 初始化除模板表单外的所有自动完成控件
// 模板表单用于添加新表单集时使用
$('.admin-autocomplete').not('[name*=__prefix__]').djangoAdminSelect2();
});
// 监听表单集添加事件
document.addEventListener('formset:added', (event) => {
// 为新添加的表单中的自动完成控件初始化Select2
$(event.target).find('.admin-autocomplete').djangoAdminSelect2();
});
}
}

@ -1,12 +1,13 @@
/*global gettext, pgettext, get_format, quickElement, removeChildren*/
/*
calendar.js - Calendar functions by Adrian Holovaty
depends on core.js for utility functions like removeChildren or quickElement
calendar.js - 日历功能由Adrian Holovaty开发
依赖core.js中的工具函数如removeChildren或quickElement
*/
'use strict';
{
// CalendarNamespace -- Provides a collection of HTML calendar-related helper functions
// CalendarNamespace -- 提供HTML日历相关辅助函数的集合
const CalendarNamespace = {
// 一年中的月份名称
monthsOfYear: [
gettext('January'),
gettext('February'),
@ -21,6 +22,7 @@ depends on core.js for utility functions like removeChildren or quickElement
gettext('November'),
gettext('December')
],
// 月份名称缩写
monthsOfYearAbbrev: [
pgettext('abbrev. month January', 'Jan'),
pgettext('abbrev. month February', 'Feb'),
@ -35,6 +37,7 @@ depends on core.js for utility functions like removeChildren or quickElement
pgettext('abbrev. month November', 'Nov'),
pgettext('abbrev. month December', 'Dec')
],
// 一周中的天名称
daysOfWeek: [
gettext('Sunday'),
gettext('Monday'),
@ -44,6 +47,7 @@ depends on core.js for utility functions like removeChildren or quickElement
gettext('Friday'),
gettext('Saturday')
],
// 天名称缩写
daysOfWeekAbbrev: [
pgettext('abbrev. day Sunday', 'Sun'),
pgettext('abbrev. day Monday', 'Mon'),
@ -53,6 +57,7 @@ depends on core.js for utility functions like removeChildren or quickElement
pgettext('abbrev. day Friday', 'Fri'),
pgettext('abbrev. day Saturday', 'Sat')
],
// 天名称首字母
daysOfWeekInitial: [
pgettext('one letter Sunday', 'S'),
pgettext('one letter Monday', 'M'),
@ -62,18 +67,35 @@ depends on core.js for utility functions like removeChildren or quickElement
pgettext('one letter Friday', 'F'),
pgettext('one letter Saturday', 'S')
],
// 一周的第一天根据FIRST_DAY_OF_WEEK格式设置
firstDayOfWeek: parseInt(get_format('FIRST_DAY_OF_WEEK')),
/**
* 判断是否为闰年
* @param {number} year - 年份
* @returns {boolean} 如果是闰年返回true否则返回false
*/
isLeapYear: function(year) {
return (((year % 4) === 0) && ((year % 100) !== 0 ) || ((year % 400) === 0));
},
/**
* 获取指定月份的天数
* @param {number} month - 月份 (1-12)
* @param {number} year - 年份
* @returns {number} 该月的天数
*/
getDaysInMonth: function(month, year) {
let days;
// 大月(31天)
if (month === 1 || month === 3 || month === 5 || month === 7 || month === 8 || month === 10 || month === 12) {
days = 31;
}
// 小月(30天)
else if (month === 4 || month === 6 || month === 9 || month === 11) {
days = 30;
}
// 2月需要判断是否为闰年
else if (month === 2 && CalendarNamespace.isLeapYear(year)) {
days = 29;
}
@ -82,6 +104,15 @@ depends on core.js for utility functions like removeChildren or quickElement
}
return days;
},
/**
* 绘制日历
* @param {number} month - 月份 (1-12)
* @param {number} year - 年份 (1-9999)
* @param {string} div_id - 用于显示日历的div元素ID
* @param {Function} callback - 点击日期时调用的回调函数
* @param {Date} selected - 选中的日期
*/
draw: function(month, year, div_id, callback, selected) { // month = 1-12, year = 1-9999
const today = new Date();
const todayDay = today.getDate();
@ -89,17 +120,16 @@ depends on core.js for utility functions like removeChildren or quickElement
const todayYear = today.getFullYear();
let todayClass = '';
// Use UTC functions here because the date field does not contain time
// and using the UTC function variants prevent the local time offset
// from altering the date, specifically the day field. For example:
// 在这里使用UTC函数是因为日期字段不包含时间
// 使用UTC函数变体可以防止本地时间偏移改变日期特别是日期字段。
// 例如:
//
// ```
// var x = new Date('2013-10-02');
// var day = x.getDate();
// ```
//
// The day variable above will be 1 instead of 2 in, say, US Pacific time
// zone.
// 在美国太平洋时区上面的day变量将是1而不是2
let isSelectedMonth = false;
if (typeof selected !== 'undefined') {
isSelectedMonth = (selected.getUTCFullYear() === year && (selected.getUTCMonth() + 1) === month);
@ -108,29 +138,38 @@ depends on core.js for utility functions like removeChildren or quickElement
month = parseInt(month);
year = parseInt(year);
const calDiv = document.getElementById(div_id);
removeChildren(calDiv);
removeChildren(calDiv); // 清空容器元素
const calTable = document.createElement('table');
// 创建表格标题,显示月份和年份
quickElement('caption', calTable, CalendarNamespace.monthsOfYear[month - 1] + ' ' + year);
const tableBody = quickElement('tbody', calTable);
// Draw days-of-week header
// 绘制星期标题行
let tableRow = quickElement('tr', tableBody);
for (let i = 0; i < 7; i++) {
// 根据一周第一天的设置显示星期首字母
quickElement('th', tableRow, CalendarNamespace.daysOfWeekInitial[(i + CalendarNamespace.firstDayOfWeek) % 7]);
}
// 计算月份第一天之前的空白单元格数量
const startingPos = new Date(year, month - 1, 1 - CalendarNamespace.firstDayOfWeek).getDay();
const days = CalendarNamespace.getDaysInMonth(month, year);
let nonDayCell;
// Draw blanks before first of month
// 在月份第一天之前绘制空白单元格
tableRow = quickElement('tr', tableBody);
for (let i = 0; i < startingPos; i++) {
nonDayCell = quickElement('td', tableRow, ' ');
nonDayCell.className = "nonday";
}
/**
* 创建月份点击事件处理函数
* @param {number} y - 年份
* @param {number} m - 月份
* @returns {Function} 点击事件处理函数
*/
function calendarMonth(y, m) {
function onClick(e) {
e.preventDefault();
@ -139,19 +178,22 @@ depends on core.js for utility functions like removeChildren or quickElement
return onClick;
}
// Draw days of month
// 绘制月份中的每一天
let currentDay = 1;
for (let i = startingPos; currentDay <= days; i++) {
// 每7天开始新的一行星期日
if (i % 7 === 0 && currentDay !== 1) {
tableRow = quickElement('tr', tableBody);
}
// 如果是今天添加today类
if ((currentDay === todayDay) && (month === todayMonth) && (year === todayYear)) {
todayClass = 'today';
} else {
todayClass = '';
}
// use UTC function; see above for explanation.
// 使用UTC函数解释见上文
// 如果是选中的日期添加selected类
if (isSelectedMonth && currentDay === selected.getUTCDate()) {
if (todayClass !== '') {
todayClass += " ";
@ -159,13 +201,14 @@ depends on core.js for utility functions like removeChildren or quickElement
todayClass += "selected";
}
// 创建日期单元格和链接
const cell = quickElement('td', tableRow, '', 'class', todayClass);
const link = quickElement('a', cell, currentDay, 'href', '#');
link.addEventListener('click', calendarMonth(year, month));
currentDay++;
}
// Draw blanks after end of month (optional, but makes for valid code)
// 在月份结束后绘制空白单元格(可选,但使代码有效)
while (tableRow.childNodes.length < 7) {
nonDayCell = quickElement('td', tableRow, ' ');
nonDayCell.className = "nonday";
@ -175,13 +218,17 @@ depends on core.js for utility functions like removeChildren or quickElement
}
};
// Calendar -- A calendar instance
// Calendar -- 日历实例
/**
* 日历构造函数
* @param {string} div_id - 用于显示日历的元素ID
* @param {Function} callback - 点击日期时调用的回调函数
* @param {Date} selected - 选中的日期
*/
function Calendar(div_id, callback, selected) {
// div_id (string) is the ID of the element in which the calendar will
// be displayed
// callback (string) is the name of a JavaScript function that will be
// called with the parameters (year, month, day) when a day in the
// calendar is clicked
// div_id (string) 是用于显示日历的元素ID
// callback (string) 是一个JavaScript函数名称当点击日历中的日期时会调用该函数
// 该函数将接收参数 (year, month, day)
this.div_id = div_id;
this.callback = callback;
this.today = new Date();
@ -192,9 +239,19 @@ depends on core.js for utility functions like removeChildren or quickElement
}
}
Calendar.prototype = {
/**
* 绘制当前月份的日历
*/
drawCurrent: function() {
CalendarNamespace.draw(this.currentMonth, this.currentYear, this.div_id, this.callback, this.selected);
},
/**
* 绘制指定日期的日历
* @param {number} month - 月份
* @param {number} year - 年份
* @param {Date} selected - 选中的日期
*/
drawDate: function(month, year, selected) {
this.currentMonth = month;
this.currentYear = year;
@ -205,6 +262,10 @@ depends on core.js for utility functions like removeChildren or quickElement
this.drawCurrent();
},
/**
* 绘制上一个月的日历
*/
drawPreviousMonth: function() {
if (this.currentMonth === 1) {
this.currentMonth = 12;
@ -215,6 +276,10 @@ depends on core.js for utility functions like removeChildren or quickElement
}
this.drawCurrent();
},
/**
* 绘制下一个月的日历
*/
drawNextMonth: function() {
if (this.currentMonth === 12) {
this.currentMonth = 1;
@ -225,10 +290,18 @@ depends on core.js for utility functions like removeChildren or quickElement
}
this.drawCurrent();
},
/**
* 绘制上一年的日历
*/
drawPreviousYear: function() {
this.currentYear--;
this.drawCurrent();
},
/**
* 绘制下一年的日历
*/
drawNextYear: function() {
this.currentYear++;
this.drawCurrent();
@ -236,4 +309,4 @@ depends on core.js for utility functions like removeChildren or quickElement
};
window.Calendar = Calendar;
window.CalendarNamespace = CalendarNamespace;
}
}

@ -1,29 +1,36 @@
'use strict';
{
// Call function fn when the DOM is loaded and ready. If it is already
// loaded, call the function now.
// 页面加载完成后调用指定函数。如果页面已经加载完成,则立即调用该函数
// http://youmightnotneedjquery.com/#ready
function ready(fn) {
// 检查文档是否加载完成
if (document.readyState !== 'loading') {
fn();
} else {
// 如果文档未加载完成则在DOMContentLoaded事件中调用
document.addEventListener('DOMContentLoaded', fn);
}
}
ready(function() {
// 处理点击事件的函数
function handleClick(event) {
// 阻止默认行为
event.preventDefault();
// 获取URL参数
const params = new URLSearchParams(window.location.search);
// 如果URL中有_popup参数则关闭窗口(适用于弹窗情况)
if (params.has('_popup')) {
window.close(); // Close the popup.
window.close(); // 关闭弹窗
} else {
window.history.back(); // Otherwise, go back.
// 否则返回上一页
window.history.back(); // 返回上一页
}
}
// 为所有具有cancel-link类的元素添加点击事件监听器
document.querySelectorAll('.cancel-link').forEach(function(el) {
el.addEventListener('click', handleClick);
});
});
}
}

@ -1,16 +1,21 @@
'use strict';
{
// 定义表单输入元素标签数组
const inputTags = ['BUTTON', 'INPUT', 'SELECT', 'TEXTAREA'];
// 从页面中获取模型名称
const modelName = document.getElementById('django-admin-form-add-constants').dataset.modelName;
// 如果模型名称存在
if (modelName) {
// 根据模型名称获取表单元素
const form = document.getElementById(modelName + '_form');
// 遍历表单中的所有元素
for (const element of form.elements) {
// HTMLElement.offsetParent returns null when the element is not
// rendered.
// HTMLElement.offsetParent 在元素未渲染时返回 null
// 查找第一个可用的输入元素并设置焦点
if (inputTags.includes(element.tagName) && !element.disabled && element.offsetParent) {
element.focus();
break;
element.focus(); // 设置焦点到第一个可用的输入元素
break; // 找到后退出循环
}
}
}
}
}

@ -1,115 +1,217 @@
// Core JavaScript helper functions
// 核心JavaScript辅助函数
'use strict';
// quickElement(tagType, parentReference [, textInChildNode, attribute, attributeValue ...]);
/**
* 快速创建元素函数
* @param {string} tagType - 标签类型
* @param {HTMLElement} parentReference - 父元素引用
* @param {string} [textInChildNode] - 子节点中的文本可选
* @param {...string} [attribute] - 属性和属性值可选成对出现
* @returns {HTMLElement} 创建的元素
*
* 使用示例:
* quickElement('div', parentElement, 'Hello World', 'class', 'my-class', 'id', 'my-id')
*/
function quickElement() {
// 创建指定类型的元素
const obj = document.createElement(arguments[0]);
// 如果提供了文本内容,则添加文本节点
if (arguments[2]) {
const textNode = document.createTextNode(arguments[2]);
obj.appendChild(textNode);
}
// 处理属性参数从第4个参数开始每两个参数为一组属性名和属性值
const len = arguments.length;
for (let i = 3; i < len; i += 2) {
obj.setAttribute(arguments[i], arguments[i + 1]);
}
// 将创建的元素添加到父元素中
arguments[1].appendChild(obj);
return obj;
}
// "a" is reference to an object
/**
* 移除元素的所有子节点
* @param {HTMLElement} a - 需要清空子节点的元素
*/
function removeChildren(a) {
// 循环移除所有子节点
while (a.hasChildNodes()) {
a.removeChild(a.lastChild);
}
}
// ----------------------------------------------------------------------------
// Find-position functions by PPK
// See https://www.quirksmode.org/js/findpos.html
// 查找位置函数 by PPK
// 参见 https://www.quirksmode.org/js/findpos.html
// ----------------------------------------------------------------------------
/**
* 查找元素相对于文档的水平位置(X坐标)
* @param {HTMLElement} obj - 需要查找位置的元素
* @returns {number} 元素的X坐标
*/
function findPosX(obj) {
let curleft = 0;
// 如果元素有offsetParent属性则通过遍历offsetParent来计算位置
if (obj.offsetParent) {
while (obj.offsetParent) {
curleft += obj.offsetLeft - obj.scrollLeft;
obj = obj.offsetParent;
}
} else if (obj.x) {
// 如果元素有x属性则直接使用
curleft += obj.x;
}
return curleft;
}
/**
* 查找元素相对于文档的垂直位置(Y坐标)
* @param {HTMLElement} obj - 需要查找位置的元素
* @returns {number} 元素的Y坐标
*/
function findPosY(obj) {
let curtop = 0;
// 如果元素有offsetParent属性则通过遍历offsetParent来计算位置
if (obj.offsetParent) {
while (obj.offsetParent) {
curtop += obj.offsetTop - obj.scrollTop;
obj = obj.offsetParent;
}
} else if (obj.y) {
// 如果元素有y属性则直接使用
curtop += obj.y;
}
return curtop;
}
//-----------------------------------------------------------------------------
// Date object extensions
// Date对象扩展
// ----------------------------------------------------------------------------
{
/**
* 获取12小时制的小时数
* @returns {number} 12小时制的小时数(1-12)
*/
Date.prototype.getTwelveHours = function() {
return this.getHours() % 12 || 12;
};
/**
* 获取两位数的月份
* @returns {string} 两位数的月份(01-12)
*/
Date.prototype.getTwoDigitMonth = function() {
return (this.getMonth() < 9) ? '0' + (this.getMonth() + 1) : (this.getMonth() + 1);
};
/**
* 获取两位数的日期
* @returns {string} 两位数的日期(01-31)
*/
Date.prototype.getTwoDigitDate = function() {
return (this.getDate() < 10) ? '0' + this.getDate() : this.getDate();
};
/**
* 获取两位数的12小时制小时
* @returns {string} 两位数的12小时制小时(01-12)
*/
Date.prototype.getTwoDigitTwelveHour = function() {
return (this.getTwelveHours() < 10) ? '0' + this.getTwelveHours() : this.getTwelveHours();
};
/**
* 获取两位数的24小时制小时
* @returns {string} 两位数的24小时制小时(00-23)
*/
Date.prototype.getTwoDigitHour = function() {
return (this.getHours() < 10) ? '0' + this.getHours() : this.getHours();
};
/**
* 获取两位数的分钟
* @returns {string} 两位数的分钟(00-59)
*/
Date.prototype.getTwoDigitMinute = function() {
return (this.getMinutes() < 10) ? '0' + this.getMinutes() : this.getMinutes();
};
/**
* 获取两位数的秒
* @returns {string} 两位数的秒(00-59)
*/
Date.prototype.getTwoDigitSecond = function() {
return (this.getSeconds() < 10) ? '0' + this.getSeconds() : this.getSeconds();
};
/**
* 获取缩写的星期名称
* @returns {string} 缩写的星期名称
*/
Date.prototype.getAbbrevDayName = function() {
return typeof window.CalendarNamespace === "undefined"
? '0' + this.getDay()
: window.CalendarNamespace.daysOfWeekAbbrev[this.getDay()];
};
/**
* 获取完整的星期名称
* @returns {string} 完整的星期名称
*/
Date.prototype.getFullDayName = function() {
return typeof window.CalendarNamespace === "undefined"
? '0' + this.getDay()
: window.CalendarNamespace.daysOfWeek[this.getDay()];
};
/**
* 获取缩写的月份名称
* @returns {string} 缩写的月份名称
*/
Date.prototype.getAbbrevMonthName = function() {
return typeof window.CalendarNamespace === "undefined"
? this.getTwoDigitMonth()
: window.CalendarNamespace.monthsOfYearAbbrev[this.getMonth()];
};
/**
* 获取完整的月份名称
* @returns {string} 完整的月份名称
*/
Date.prototype.getFullMonthName = function() {
return typeof window.CalendarNamespace === "undefined"
? this.getTwoDigitMonth()
: window.CalendarNamespace.monthsOfYear[this.getMonth()];
};
/**
* 格式化日期时间字符串
* @param {string} format - 格式化字符串
* @returns {string} 格式化后的日期时间字符串
*
* 支持的格式化符号:
* %a - 缩写星期名称
* %A - 完整星期名称
* %b - 缩写月份名称
* %B - 完整月份名称
* %c - 日期和时间表示法
* %d - 两位数日期
* %H - 两位数24小时制小时
* %I - 两位数12小时制小时
* %m - 两位数月份
* %M - 两位数分钟
* %p - AM或PM
* %S - 两位数秒
* %w - 星期几(0-6, 0表示星期日)
* %x - 日期表示法
* %X - 时间表示法
* %y - 两位数年份
* %Y - 四位数年份
* %% - 百分号
*/
Date.prototype.strftime = function(format) {
// 定义格式化字段映射
const fields = {
a: this.getAbbrevDayName(),
A: this.getFullDayName(),
@ -131,6 +233,7 @@ function findPosY(obj) {
'%': '%'
};
let result = '', i = 0;
// 遍历格式字符串并替换相应的字段
while (i < format.length) {
if (format.charAt(i) === '%') {
result += fields[format.charAt(i + 1)];
@ -145,13 +248,20 @@ function findPosY(obj) {
};
// ----------------------------------------------------------------------------
// String object extensions
// String对象扩展
// ----------------------------------------------------------------------------
/**
* 解析日期时间字符串
* @param {string} format - 格式化字符串
* @returns {Date} 解析后的Date对象
*/
String.prototype.strptime = function(format) {
// 按分隔符分割格式字符串和日期字符串
const split_format = format.split(/[.\-/]/);
const date = this.split(/[.\-/]/);
let i = 0;
let day, month, year;
// 根据格式解析日期各部分
while (i < split_format.length) {
switch (split_format[i]) {
case "%d":
@ -164,9 +274,7 @@ function findPosY(obj) {
year = date[i];
break;
case "%y":
// A %y value in the range of [00, 68] is in the current
// century, while [69, 99] is in the previous century,
// according to the Open Group Specification.
// 根据Open Group规范[00, 68]范围内的%y值属于当前世纪[69, 99]范围内的属于上一个世纪
if (parseInt(date[i], 10) >= 69) {
year = date[i];
} else {
@ -176,9 +284,8 @@ function findPosY(obj) {
}
++i;
}
// Create Date object from UTC since the parsed value is supposed to be
// in UTC, not local time. Also, the calendar uses UTC functions for
// date extraction.
// 从UTC创建Date对象因为解析的值应该是UTC时间而不是本地时间
// 日历也使用UTC函数来提取日期
return new Date(Date.UTC(year, month, day));
};
}
}

@ -1,30 +1,38 @@
/**
* Persist changelist filters state (collapsed/expanded).
* 持久化变更列表过滤器状态折叠/展开
*/
'use strict';
{
// Init filters.
// 初始化过滤器状态
// 从sessionStorage中获取之前保存的过滤器状态
let filters = JSON.parse(sessionStorage.getItem('django.admin.filtersState'));
// 如果没有保存的过滤器状态,则初始化为空对象
if (!filters) {
filters = {};
}
// 遍历所有保存的过滤器状态,恢复对应的过滤器显示状态
Object.entries(filters).forEach(([key, value]) => {
// 根据过滤器标题查找对应的detail元素
const detailElement = document.querySelector(`[data-filter-title='${CSS.escape(key)}']`);
// Check if the filter is present, it could be from other view.
// 检查过滤器是否存在,因为它可能来自其他视图
if (detailElement) {
// 根据保存的状态设置open属性控制过滤器的展开或折叠
value ? detailElement.setAttribute('open', '') : detailElement.removeAttribute('open');
}
});
// Save filter state when clicks.
// 当点击过滤器时保存过滤器状态
const details = document.querySelectorAll('details');
details.forEach(detail => {
// 为每个details元素添加toggle事件监听器
detail.addEventListener('toggle', event => {
// 将当前过滤器的展开状态保存到filters对象中
filters[`${event.target.dataset.filterTitle}`] = detail.open;
// 将更新后的过滤器状态保存到sessionStorage中
sessionStorage.setItem('django.admin.filtersState', JSON.stringify(filters));
});
});
}
}

@ -1,27 +1,39 @@
/*global DateTimeShortcuts, SelectFilter*/
/**
* Django admin inlines
* Django管理后台内联表单
*
* Based on jQuery Formset 1.1
* 基于 jQuery Formset 1.1
* @author Stanislaus Madueke (stan DOT madueke AT gmail DOT com)
* @requires jQuery 1.2.6 or later
* @requires jQuery 1.2.6 或更高版本
*
* Copyright (c) 2009, Stanislaus Madueke
* All rights reserved.
* 版权所有 (c) 2009, Stanislaus Madueke
* 保留所有权利
*
* Spiced up with Code from Zain Memon's GSoC project 2009
* and modified for Django by Jannis Leidel, Travis Swicegood and Julien Phalip.
* 包含了Zain Memon的GSoC项目2009的代码
* 并由Jannis Leidel, Travis Swicegood和Julien Phalip为Django进行了修改
*
* Licensed under the New BSD License
* See: https://opensource.org/licenses/bsd-license.php
* 基于新BSD许可证授权
* 参见: https://opensource.org/licenses/bsd-license.php
*/
'use strict';
{
const $ = django.jQuery;
/**
* 表单集插件
* @param {Object} opts - 配置选项
* @returns {jQuery} jQuery对象
*/
$.fn.formset = function(opts) {
// 合并默认选项和用户选项
const options = $.extend({}, $.fn.formset.defaults, opts);
const $this = $(this);
const $parent = $this.parent();
/**
* 更新元素索引
* @param {HTMLElement} el - 元素
* @param {string} prefix - 前缀
* @param {number} ndx - 新索引
*/
const updateElementIndex = function(el, prefix, ndx) {
const id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))");
const replacement = prefix + "-" + ndx;
@ -35,6 +47,7 @@
el.name = el.name.replace(id_regex, replacement);
}
};
// 获取总表单数、最大表单数和最小表单数的输入框
const totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").prop("autocomplete", "off");
let nextIndex = parseInt(totalForms.val(), 10);
const maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").prop("autocomplete", "off");
@ -42,52 +55,60 @@
let addButton;
/**
* The "Add another MyModel" button below the inline forms.
* 添加内联添加按钮
* "添加另一个MyModel"按钮位于内联表单下方
*/
const addInlineAddButton = function() {
if (addButton === null) {
if ($this.prop("tagName") === "TR") {
// If forms are laid out as table rows, insert the
// "add" button in a new table row:
// 如果表单以表格行形式排列,在新行中插入"添加"按钮:
const numCols = $this.eq(-1).children().length;
$parent.append('<tr class="' + options.addCssClass + '"><td colspan="' + numCols + '"><a role="button" class="addlink" href="#">' + options.addText + "</a></tr>");
addButton = $parent.find("tr:last a");
} else {
// Otherwise, insert it immediately after the last form:
// 否则,将其插入到最后一个表单之后:
$this.filter(":last").after('<div class="' + options.addCssClass + '"><a role="button" class="addlink" href="#">' + options.addText + "</a></div>");
addButton = $this.filter(":last").next().find("a");
}
}
// 为添加按钮绑定点击事件处理器
addButton.on('click', addInlineClickHandler);
};
/**
* 内联添加按钮点击事件处理器
* @param {Event} e - 点击事件
*/
const addInlineClickHandler = function(e) {
e.preventDefault();
const template = $("#" + options.prefix + "-empty");
const row = template.clone(true);
// 移除空行CSS类添加表单CSS类设置ID
row.removeClass(options.emptyCssClass)
.addClass(options.formCssClass)
.attr("id", options.prefix + "-" + nextIndex);
addInlineDeleteButton(row);
// 更新新表单中所有元素的索引
row.find("*").each(function() {
updateElementIndex(this, options.prefix, totalForms.val());
});
// Insert the new form when it has been fully edited.
// 在完全编辑后插入新表单
row.insertBefore($(template));
// Update number of total forms.
// 更新总表单数
$(totalForms).val(parseInt(totalForms.val(), 10) + 1);
nextIndex += 1;
// Hide the add button if there's a limit and it's been reached.
// 如果达到限制数量,隐藏添加按钮
if ((maxForms.val() !== '') && (maxForms.val() - totalForms.val()) <= 0) {
addButton.parent().hide();
}
// Show the remove buttons if there are more than min_num.
// 如果表单数超过最小数量,显示删除按钮
toggleDeleteButtonVisibility(row.closest('.inline-group'));
// Pass the new form to the post-add callback, if provided.
// 如果提供了添加后的回调函数,则调用它
if (options.added) {
options.added(row);
}
// 触发formset:added自定义事件
row.get(0).dispatchEvent(new CustomEvent("formset:added", {
bubbles: true,
detail: {
@ -97,60 +118,61 @@
};
/**
* The "X" button that is part of every unsaved inline.
* (When saved, it is replaced with a "Delete" checkbox.)
* 为每个未保存的内联表单添加"X"按钮
* (保存后会被"删除"复选框替换)
* @param {jQuery} row - 表单行
*/
const addInlineDeleteButton = function(row) {
if (row.is("tr")) {
// If the forms are laid out in table rows, insert
// the remove button into the last table cell:
// 如果表单以表格行形式排列,将删除按钮插入到最后一个单元格:
row.children(":last").append('<div><a role="button" class="' + options.deleteCssClass + '" href="#">' + options.deleteText + "</a></div>");
} else if (row.is("ul") || row.is("ol")) {
// If they're laid out as an ordered/unordered list,
// insert an <li> after the last list item:
// 如果以有序/无序列表形式排列,在最后一个列表项后插入<li>:
row.append('<li><a role="button" class="' + options.deleteCssClass + '" href="#">' + options.deleteText + "</a></li>");
} else {
// Otherwise, just insert the remove button as the
// last child element of the form's container:
// 否则,将删除按钮作为表单容器的最后一个子元素插入:
row.children(":first").append('<span><a role="button" class="' + options.deleteCssClass + '" href="#">' + options.deleteText + "</a></span>");
}
// Add delete handler for each row.
// 为每一行添加删除事件处理器
row.find("a." + options.deleteCssClass).on('click', inlineDeleteHandler.bind(this));
};
/**
* 内联删除处理器
* @param {Event} e1 - 点击事件
*/
const inlineDeleteHandler = function(e1) {
e1.preventDefault();
const deleteButton = $(e1.target);
const row = deleteButton.closest('.' + options.formCssClass);
const inlineGroup = row.closest('.inline-group');
// Remove the parent form containing this button,
// and also remove the relevant row with non-field errors:
// 删除包含此按钮的父表单,以及包含非字段错误的相关行:
const prevRow = row.prev();
if (prevRow.length && prevRow.hasClass('row-form-errors')) {
prevRow.remove();
}
row.remove();
nextIndex -= 1;
// Pass the deleted form to the post-delete callback, if provided.
// 如果提供了删除后的回调函数,则调用它
if (options.removed) {
options.removed(row);
}
// 触发formset:removed自定义事件
document.dispatchEvent(new CustomEvent("formset:removed", {
detail: {
formsetName: options.prefix
}
}));
// Update the TOTAL_FORMS form count.
// 更新TOTAL_FORMS表单计数
const forms = $("." + options.formCssClass);
$("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length);
// Show add button again once below maximum number.
// 如果未达到最大数量,重新显示添加按钮
if ((maxForms.val() === '') || (maxForms.val() - forms.length) > 0) {
addButton.parent().show();
}
// Hide the remove buttons if at min_num.
// 如果达到最小数量,隐藏删除按钮
toggleDeleteButtonVisibility(inlineGroup);
// Also, update names and ids for all remaining form controls so
// they remain in sequence:
// 同时更新所有剩余表单控件的名称和ID使其保持顺序:
let i, formCount;
const updateElementCallback = function() {
updateElementIndex(this, options.prefix, i);
@ -161,6 +183,10 @@
}
};
/**
* 切换删除按钮可见性
* @param {jQuery} inlineGroup - 内联组
*/
const toggleDeleteButtonVisibility = function(inlineGroup) {
if ((minForms.val() !== '') && (minForms.val() - totalForms.val()) >= 0) {
inlineGroup.find('.inline-deletelink').hide();
@ -169,22 +195,23 @@
}
};
// 为每个表单添加CSS类
$this.each(function(i) {
$(this).not("." + options.emptyCssClass).addClass(options.formCssClass);
});
// Create the delete buttons for all unsaved inlines:
// 为所有未保存的内联表单创建删除按钮:
$this.filter('.' + options.formCssClass + ':not(.has_original):not(.' + options.emptyCssClass + ')').each(function() {
addInlineDeleteButton($(this));
});
toggleDeleteButtonVisibility($this);
// Create the add button, initially hidden.
// 创建添加按钮,初始隐藏
addButton = options.addButton;
addInlineAddButton();
// Show the add button if allowed to add more items.
// Note that max_num = None translates to a blank string.
// 如果允许添加更多项,则显示添加按钮
// 注意 max_num = None 会转换为空字符串
const showAddButton = maxForms.val() === '' || (maxForms.val() - totalForms.val()) > 0;
if ($this.length && showAddButton) {
addButton.parent().show();
@ -195,36 +222,48 @@
return this;
};
/* Setup plugin defaults */
/* 设置插件默认值 */
$.fn.formset.defaults = {
prefix: "form", // The form prefix for your django formset
addText: "add another", // Text for the add link
deleteText: "remove", // Text for the delete link
addCssClass: "add-row", // CSS class applied to the add link
deleteCssClass: "delete-row", // CSS class applied to the delete link
emptyCssClass: "empty-row", // CSS class applied to the empty row
formCssClass: "dynamic-form", // CSS class applied to each form in a formset
added: null, // Function called each time a new form is added
removed: null, // Function called each time a form is deleted
addButton: null // Existing add button to use
prefix: "form", // Django表单集的表单前缀
addText: "add another", // 添加链接的文本
deleteText: "remove", // 删除链接的文本
addCssClass: "add-row", // 应用于添加链接的CSS类
deleteCssClass: "delete-row", // 应用于删除链接的CSS类
emptyCssClass: "empty-row", // 应用于空行的CSS类
formCssClass: "dynamic-form", // 应用于表单集中的每个表单的CSS类
added: null, // 每次添加新表单时调用的函数
removed: null, // 每次删除表单时调用的函数
addButton: null // 要使用的现有添加按钮
};
// Tabular inlines ---------------------------------------------------------
// 表格内联 ---------------------------------------------------------
/**
* 表格形式的内联表单集
* @param {string} selector - 选择器
* @param {Object} options - 配置选项
* @returns {jQuery} jQuery对象
*/
$.fn.tabularFormset = function(selector, options) {
const $rows = $(this);
/**
* 重新初始化日期时间快捷方式
*/
const reinitDateTimeShortCuts = function() {
// Reinitialize the calendar and clock widgets by force
// 强制重新初始化日历和时钟控件
if (typeof DateTimeShortcuts !== "undefined") {
$(".datetimeshortcuts").remove();
DateTimeShortcuts.init();
}
};
/**
* 更新选择过滤器
*/
const updateSelectFilter = function() {
// If any SelectFilter widgets are a part of the new form,
// instantiate a new SelectFilter instance for it.
// 如果新表单中有SelectFilter控件
// 为其创建新的SelectFilter实例
if (typeof SelectFilter !== 'undefined') {
$('.selectfilter').each(function(index, value) {
SelectFilter.init(value.id, this.dataset.fieldName, false);
@ -235,6 +274,10 @@
}
};
/**
* 初始化预填充字段
* @param {jQuery} row - 表单行
*/
const initPrepopulatedFields = function(row) {
row.find('.prepopulated_field').each(function() {
const field = $(this),
@ -250,6 +293,7 @@
});
};
// 初始化表单集
$rows.formset({
prefix: options.prefix,
addText: options.addText,
@ -268,9 +312,19 @@
return $rows;
};
// Stacked inlines ---------------------------------------------------------
// 堆叠内联 ---------------------------------------------------------
/**
* 堆叠形式的内联表单集
* @param {string} selector - 选择器
* @param {Object} options - 配置选项
* @returns {jQuery} jQuery对象
*/
$.fn.stackedFormset = function(selector, options) {
const $rows = $(this);
/**
* 更新内联标签
* @param {jQuery} row - 表单行
*/
const updateInlineLabel = function(row) {
$(selector).find(".inline_label").each(function(i) {
const count = i + 1;
@ -278,16 +332,22 @@
});
};
/**
* 重新初始化日期时间快捷方式
*/
const reinitDateTimeShortCuts = function() {
// Reinitialize the calendar and clock widgets by force, yuck.
// 强制重新初始化日历和时钟控件
if (typeof DateTimeShortcuts !== "undefined") {
$(".datetimeshortcuts").remove();
DateTimeShortcuts.init();
}
};
/**
* 更新选择过滤器
*/
const updateSelectFilter = function() {
// If any SelectFilter widgets were added, instantiate a new instance.
// 如果添加了SelectFilter控件创建新实例
if (typeof SelectFilter !== "undefined") {
$(".selectfilter").each(function(index, value) {
SelectFilter.init(value.id, this.dataset.fieldName, false);
@ -298,6 +358,10 @@
}
};
/**
* 初始化预填充字段
* @param {jQuery} row - 表单行
*/
const initPrepopulatedFields = function(row) {
row.find('.prepopulated_field').each(function() {
const field = $(this),
@ -305,9 +369,9 @@
dependency_list = input.data('dependency_list') || [],
dependencies = [];
$.each(dependency_list, function(i, field_name) {
// Dependency in a fieldset.
// 字段集中的依赖项
let field_element = row.find('.form-row .field-' + field_name);
// Dependency without a fieldset.
// 没有字段集的依赖项
if (!field_element.length) {
field_element = row.find('.form-row.field-' + field_name);
}
@ -319,6 +383,7 @@
});
};
// 初始化表单集
$rows.formset({
prefix: options.prefix,
addText: options.addText,
@ -339,11 +404,13 @@
return $rows;
};
// 文档加载完成后初始化
$(document).ready(function() {
$(".js-inline-admin-formset").each(function() {
const data = $(this).data(),
inlineOptions = data.inlineFormset;
let selector;
// 根据内联类型初始化相应的表单集
switch(data.inlineType) {
case "stacked":
selector = inlineOptions.name + "-group .inline-related";
@ -356,4 +423,4 @@
}
});
});
}
}

@ -1,8 +1,8 @@
/*global jQuery:false*/
'use strict';
/* Puts the included jQuery into our own namespace using noConflict and passing
* it 'true'. This ensures that the included jQuery doesn't pollute the global
* namespace (i.e. this preserves pre-existing values for both window.$ and
* window.jQuery).
/* jQuery使noConflict'true'
* 这确保了引入的jQuery不会污染全局命名空间
* (即保留了window.$和window.jQuery的预先存在值)
*/
window.django = {jQuery: jQuery.noConflict(true)};
// 将jQuery设置为django对象的属性使用noConflict(true)解决命名冲突
window.django = {jQuery: jQuery.noConflict(true)};

@ -1,73 +1,100 @@
'use strict';
{
// 获取导航侧边栏切换按钮和相关元素
const toggleNavSidebar = document.getElementById('toggle-nav-sidebar');
if (toggleNavSidebar !== null) {
const navSidebar = document.getElementById('nav-sidebar');
const main = document.getElementById('main');
// 从localStorage获取导航侧边栏的开关状态
let navSidebarIsOpen = localStorage.getItem('django.admin.navSidebarIsOpen');
// 如果没有保存的状态,默认设为打开
if (navSidebarIsOpen === null) {
navSidebarIsOpen = 'true';
}
// 根据状态设置主内容区域的样式和侧边栏的展开属性
main.classList.toggle('shifted', navSidebarIsOpen === 'true');
navSidebar.setAttribute('aria-expanded', navSidebarIsOpen);
// 为切换按钮添加点击事件监听器
toggleNavSidebar.addEventListener('click', function() {
// 切换导航侧边栏的开关状态
if (navSidebarIsOpen === 'true') {
navSidebarIsOpen = 'false';
} else {
navSidebarIsOpen = 'true';
}
// 保存状态到localStorage
localStorage.setItem('django.admin.navSidebarIsOpen', navSidebarIsOpen);
// 切换主内容区域的样式
main.classList.toggle('shifted');
// 设置侧边栏的展开属性
navSidebar.setAttribute('aria-expanded', navSidebarIsOpen);
});
}
/**
* 初始化侧边栏快速过滤功能
* 允许用户通过输入关键字快速过滤导航菜单项
*/
function initSidebarQuickFilter() {
const options = [];
const options = []; // 存储导航菜单项的数组
const navSidebar = document.getElementById('nav-sidebar');
// 如果没有导航侧边栏,则直接返回
if (!navSidebar) {
return;
}
// 遍历所有导航链接将它们添加到options数组中
navSidebar.querySelectorAll('th[scope=row] a').forEach((container) => {
options.push({title: container.innerHTML, node: container});
});
/**
* 检查过滤值并更新显示
* @param {Event} event - 事件对象
*/
function checkValue(event) {
let filterValue = event.target.value;
// 如果有过滤值,则转换为小写以便进行不区分大小写的匹配
if (filterValue) {
filterValue = filterValue.toLowerCase();
}
// 如果按下Escape键则清空过滤器
if (event.key === 'Escape') {
filterValue = '';
event.target.value = ''; // clear input
event.target.value = ''; // 清空输入框
}
let matches = false;
// 遍历所有选项,根据过滤值显示或隐藏
for (const o of options) {
let displayValue = '';
if (filterValue) {
// 如果选项标题不包含过滤值,则隐藏该项
if (o.title.toLowerCase().indexOf(filterValue) === -1) {
displayValue = 'none';
} else {
matches = true;
matches = true; // 标记找到了匹配项
}
}
// show/hide parent <TR>
// 显示或隐藏父级<tr>元素
o.node.parentNode.parentNode.style.display = displayValue;
}
// 根据是否有匹配项设置输入框的样式
if (!filterValue || matches) {
event.target.classList.remove('no-results');
} else {
event.target.classList.add('no-results');
}
// 将过滤值保存到sessionStorage中
sessionStorage.setItem('django.admin.navSidebarFilterValue', filterValue);
}
// 获取导航过滤输入框并添加事件监听器
const nav = document.getElementById('nav-filter');
nav.addEventListener('change', checkValue, false);
nav.addEventListener('input', checkValue, false);
nav.addEventListener('keyup', checkValue, false);
nav.addEventListener('change', checkValue, false); // 变更事件
nav.addEventListener('input', checkValue, false); // 输入事件
nav.addEventListener('keyup', checkValue, false); // 键盘弹起事件
// 从sessionStorage获取之前保存的过滤值并应用
const storedValue = sessionStorage.getItem('django.admin.navSidebarFilterValue');
if (storedValue) {
nav.value = storedValue;
@ -76,4 +103,4 @@
}
window.initSidebarQuickFilter = initSidebarQuickFilter;
initSidebarQuickFilter();
}
}

@ -1,15 +1,21 @@
'use strict';
{
// 从页面中获取弹窗响应的初始化数据
const initData = JSON.parse(document.getElementById('django-admin-popup-response-constants').dataset.popupResponse);
// 根据不同的操作类型执行相应的处理函数
switch(initData.action) {
case 'change':
// 处理修改对象的弹窗响应
opener.dismissChangeRelatedObjectPopup(window, initData.value, initData.obj, initData.new_value);
break;
case 'delete':
// 处理删除对象的弹窗响应
opener.dismissDeleteRelatedObjectPopup(window, initData.value);
break;
default:
// 默认处理添加对象的弹窗响应
opener.dismissAddRelatedObjectPopup(window, initData.value, initData.obj);
break;
}
}
}

@ -2,42 +2,57 @@
'use strict';
{
const $ = django.jQuery;
/**
* 预填充插件
* 根据依赖字段的值填充选定字段生成URL友好的字符串
* @param {Array} dependencies - 依赖字段ID数组
* @param {number} maxLength - URLify字符串的最大长度
* @param {boolean} allowUnicode - URLify字符串是否支持Unicode
* @returns {jQuery} jQuery对象
*/
$.fn.prepopulate = function(dependencies, maxLength, allowUnicode) {
/*
Depends on urlify.js
Populates a selected field with the values of the dependent fields,
URLifies and shortens the string.
dependencies - array of dependent fields ids
maxLength - maximum length of the URLify'd string
allowUnicode - Unicode support of the URLify'd string
依赖 urlify.js
使用依赖字段的值填充选定字段生成URL友好的并截短的字符串
dependencies - 依赖字段ID数组
maxLength - URLify处理后字符串的最大长度
allowUnicode - URLify处理后的字符串是否支持Unicode
*/
return this.each(function() {
const prepopulatedField = $(this);
/**
* 填充字段值
*/
const populate = function() {
// Bail if the field's value has been changed by the user
// 如果用户已更改字段值,则不进行预填充
if (prepopulatedField.data('_changed')) {
return;
}
const values = [];
// 收集所有依赖字段的值
$.each(dependencies, function(i, field) {
field = $(field);
if (field.val().length > 0) {
values.push(field.val());
}
});
// 使用URLify函数处理连接后的值并设置到预填充字段中
prepopulatedField.val(URLify(values.join(' '), maxLength, allowUnicode));
};
// 初始化字段变更标记为false
prepopulatedField.data('_changed', false);
// 监听字段变更事件,标记字段已被用户更改
prepopulatedField.on('change', function() {
prepopulatedField.data('_changed', true);
});
// 如果预填充字段为空,则监听依赖字段的事件并进行填充
if (!prepopulatedField.val()) {
$(dependencies.join(',')).on('keyup change focus', populate);
}
});
};
}
}

@ -1,15 +1,21 @@
'use strict';
{
const $ = django.jQuery;
// 从页面常量中获取预填充字段的配置信息
const fields = $('#django-admin-prepopulated-fields-constants').data('prepopulatedFields');
// 遍历所有预填充字段并进行初始化
$.each(fields, function(index, field) {
// 为所有空表单中的预填充字段添加CSS类
$(
'.empty-form .form-row .field-' + field.name +
', .empty-form.form-row .field-' + field.name +
', .empty-form .form-row.field-' + field.name
).addClass('prepopulated_field');
// 为字段元素设置依赖列表数据并调用prepopulate插件进行初始化
$(field.id).data('dependency_list', field.dependency_list).prepopulate(
field.dependency_ids, field.maxLength, field.allowUnicode
);
});
}
}

@ -1,20 +1,32 @@
'use strict';
{
/**
* 设置主题模式
* @param {string} mode - 主题模式可选值: "light"(浅色), "dark"(深色), "auto"(自动)
*/
function setTheme(mode) {
// 验证主题模式参数是否有效
if (mode !== "light" && mode !== "dark" && mode !== "auto") {
console.error(`Got invalid theme mode: ${mode}. Resetting to auto.`);
mode = "auto";
}
// 设置文档根元素的主题属性
document.documentElement.dataset.theme = mode;
// 将主题选择保存到本地存储
localStorage.setItem("theme", mode);
}
/**
* 循环切换主题模式
*/
function cycleTheme() {
// 获取当前主题设置,默认为"auto"
const currentTheme = localStorage.getItem("theme") || "auto";
// 检查用户是否偏好深色主题
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
if (prefersDark) {
// Auto (dark) -> Light -> Dark
// 深色偏好下的主题切换顺序: 自动(深色) -> 浅色 -> 深色
if (currentTheme === "auto") {
setTheme("light");
} else if (currentTheme === "light") {
@ -23,7 +35,7 @@
setTheme("auto");
}
} else {
// Auto (light) -> Dark -> Light
// 浅色偏好下的主题切换顺序: 自动(浅色) -> 深色 -> 浅色
if (currentTheme === "auto") {
setTheme("dark");
} else if (currentTheme === "dark") {
@ -34,12 +46,16 @@
}
}
/**
* 初始化主题设置
*/
function initTheme() {
// set theme defined in localStorage if there is one, or fallback to auto mode
// 如果本地存储中有主题设置则使用,否则默认使用自动模式
const currentTheme = localStorage.getItem("theme");
currentTheme ? setTheme(currentTheme) : setTheme("auto");
}
// 页面加载完成后为所有主题切换按钮添加点击事件监听器
window.addEventListener('load', function(_) {
const buttons = document.getElementsByClassName("theme-toggle");
Array.from(buttons).forEach((btn) => {
@ -47,5 +63,6 @@
});
});
// 初始化主题
initTheme();
}
}

@ -1,29 +1,39 @@
"use strict";
// Fallback JS for browsers which do not support :has selector used in
// admin/css/unusable_password_fields.css
// Remove file once all supported browsers support :has selector
// 为不支持:has选择器的浏览器提供的后备JS方案
// 用于admin/css/unusable_password_fields.css
// 一旦所有支持的浏览器都支持:has选择器可以删除此文件
try {
// If browser does not support :has selector this will raise an error
// 如果浏览器不支持:has选择器这将引发错误
document.querySelector("form:has(input)");
} catch (error) {
console.log("Defaulting to javascript for usable password form management: " + error);
// JS replacement for unsupported :has selector
console.log("默认使用JavaScript管理可用密码表单: " + error);
// 不支持:has选择器时的JS替代方案
document.querySelectorAll('input[name="usable_password"]').forEach(option => {
// 为每个选项添加变更事件监听器
option.addEventListener('change', function() {
// 判断是否为可用密码
const usablePassword = (this.value === "true" ? this.checked : !this.checked);
// 获取提交按钮和警告信息元素
const submit1 = document.querySelector('input[type="submit"].set-password');
const submit2 = document.querySelector('input[type="submit"].unset-password');
const messages = document.querySelector('#id_unusable_warning');
// 根据密码可用性显示或隐藏密码输入字段
document.getElementById('id_password1').closest('.form-row').hidden = !usablePassword;
document.getElementById('id_password2').closest('.form-row').hidden = !usablePassword;
// 根据密码可用性显示或隐藏警告信息
if (messages) {
messages.hidden = usablePassword;
}
// 根据密码可用性显示或隐藏相应的提交按钮
if (submit1 && submit2) {
submit1.hidden = !usablePassword;
submit2.hidden = usablePassword;
}
});
// 触发初始变更事件
option.dispatchEvent(new Event('change'));
});
}
}

@ -1,6 +1,7 @@
/*global XRegExp*/
'use strict';
{
// 拉丁字符映射表
const LATIN_MAP = {
'À': 'A', 'Á': 'A', 'Â': 'A', 'Ã': 'A', 'Ä': 'A', 'Å': 'A', 'Æ': 'AE',
'Ç': 'C', 'È': 'E', 'É': 'E', 'Ê': 'E', 'Ë': 'E', 'Ì': 'I', 'Í': 'I',
@ -13,9 +14,13 @@
'ö': 'o', 'ő': 'o', 'ø': 'o', 'ù': 'u', 'ú': 'u', 'û': 'u', 'ü': 'u',
'ű': 'u', 'ý': 'y', 'þ': 'th', 'ÿ': 'y'
};
// 拉丁符号映射表
const LATIN_SYMBOLS_MAP = {
'©': '(c)'
};
// 希腊字符映射表
const GREEK_MAP = {
'α': 'a', 'β': 'b', 'γ': 'g', 'δ': 'd', 'ε': 'e', 'ζ': 'z', 'η': 'h',
'θ': '8', 'ι': 'i', 'κ': 'k', 'λ': 'l', 'μ': 'm', 'ν': 'n', 'ξ': '3',
@ -28,14 +33,20 @@
'Φ': 'F', 'Χ': 'X', 'Ψ': 'PS', 'Ω': 'W', 'Ά': 'A', 'Έ': 'E', 'Ί': 'I',
'Ό': 'O', 'Ύ': 'Y', 'Ή': 'H', 'Ώ': 'W', 'Ϊ': 'I', 'Ϋ': 'Y'
};
// 土耳其字符映射表
const TURKISH_MAP = {
'ş': 's', 'Ş': 'S', 'ı': 'i', 'İ': 'I', 'ç': 'c', 'Ç': 'C', 'ü': 'u',
'Ü': 'U', 'ö': 'o', 'Ö': 'O', 'ğ': 'g', 'Ğ': 'G'
};
// 罗马尼亚字符映射表
const ROMANIAN_MAP = {
'ă': 'a', 'î': 'i', 'ș': 's', 'ț': 't', 'â': 'a',
'Ă': 'A', 'Î': 'I', 'Ș': 'S', 'Ț': 'T', 'Â': 'A'
};
// 俄语字符映射表
const RUSSIAN_MAP = {
'а': 'a', 'б': 'b', 'в': 'v', 'г': 'g', 'д': 'd', 'е': 'e', 'ё': 'yo',
'ж': 'zh', 'з': 'z', 'и': 'i', 'й': 'j', 'к': 'k', 'л': 'l', 'м': 'm',
@ -48,15 +59,21 @@
'Ф': 'F', 'Х': 'H', 'Ц': 'C', 'Ч': 'Ch', 'Ш': 'Sh', 'Щ': 'Sh', 'Ъ': '',
'Ы': 'Y', 'Ь': '', 'Э': 'E', 'Ю': 'Yu', 'Я': 'Ya'
};
// 乌克兰字符映射表
const UKRAINIAN_MAP = {
'Є': 'Ye', 'І': 'I', 'Ї': 'Yi', 'Ґ': 'G', 'є': 'ye', 'і': 'i',
'ї': 'yi', 'ґ': 'g'
};
// 捷克字符映射表
const CZECH_MAP = {
'č': 'c', 'ď': 'd', 'ě': 'e', 'ň': 'n', 'ř': 'r', 'š': 's', 'ť': 't',
'ů': 'u', 'ž': 'z', 'Č': 'C', 'Ď': 'D', 'Ě': 'E', 'Ň': 'N', 'Ř': 'R',
'Š': 'S', 'Ť': 'T', 'Ů': 'U', 'Ž': 'Z'
};
// 斯洛伐克字符映射表
const SLOVAK_MAP = {
'á': 'a', 'ä': 'a', 'č': 'c', 'ď': 'd', 'é': 'e', 'í': 'i', 'ľ': 'l',
'ĺ': 'l', 'ň': 'n', 'ó': 'o', 'ô': 'o', 'ŕ': 'r', 'š': 's', 'ť': 't',
@ -65,39 +82,53 @@
'Ĺ': 'L', 'Ň': 'N', 'Ó': 'O', 'Ô': 'O', 'Ŕ': 'R', 'Š': 'S', 'Ť': 'T',
'Ú': 'U', 'Ý': 'Y', 'Ž': 'Z'
};
// 波兰字符映射表
const POLISH_MAP = {
'ą': 'a', 'ć': 'c', 'ę': 'e', 'ł': 'l', 'ń': 'n', 'ó': 'o', 'ś': 's',
'ź': 'z', 'ż': 'z',
'Ą': 'A', 'Ć': 'C', 'Ę': 'E', 'Ł': 'L', 'Ń': 'N', 'Ó': 'O', 'Ś': 'S',
'Ź': 'Z', 'Ż': 'Z'
};
// 拉脱维亚字符映射表
const LATVIAN_MAP = {
'ā': 'a', 'č': 'c', 'ē': 'e', 'ģ': 'g', 'ī': 'i', 'ķ': 'k', 'ļ': 'l',
'ņ': 'n', 'š': 's', 'ū': 'u', 'ž': 'z',
'Ā': 'A', 'Č': 'C', 'Ē': 'E', 'Ģ': 'G', 'Ī': 'I', 'Ķ': 'K', 'Ļ': 'L',
'Ņ': 'N', 'Š': 'S', 'Ū': 'U', 'Ž': 'Z'
};
// 阿拉伯字符映射表
const ARABIC_MAP = {
'أ': 'a', 'ب': 'b', 'ت': 't', 'ث': 'th', 'ج': 'g', 'ح': 'h', 'خ': 'kh', 'د': 'd',
'ذ': 'th', 'ر': 'r', 'ز': 'z', 'س': 's', 'ش': 'sh', 'ص': 's', 'ض': 'd', 'ط': 't',
'ظ': 'th', 'ع': 'aa', 'غ': 'gh', 'ف': 'f', 'ق': 'k', 'ك': 'k', 'ل': 'l', 'م': 'm',
'ن': 'n', 'ه': 'h', 'و': 'o', 'ي': 'y'
};
// 立陶宛字符映射表
const LITHUANIAN_MAP = {
'ą': 'a', 'č': 'c', 'ę': 'e', 'ė': 'e', 'į': 'i', 'š': 's', 'ų': 'u',
'ū': 'u', 'ž': 'z',
'Ą': 'A', 'Č': 'C', 'Ę': 'E', 'Ė': 'E', 'Į': 'I', 'Š': 'S', 'Ų': 'U',
'Ū': 'U', 'Ž': 'Z'
};
// 塞尔维亚字符映射表
const SERBIAN_MAP = {
'ђ': 'dj', 'ј': 'j', 'љ': 'lj', 'њ': 'nj', 'ћ': 'c', 'џ': 'dz',
'đ': 'dj', 'Ђ': 'Dj', 'Ј': 'j', 'Љ': 'Lj', 'Њ': 'Nj', 'Ћ': 'C',
'Џ': 'Dz', 'Đ': 'Dj'
};
// 阿塞拜疆字符映射表
const AZERBAIJANI_MAP = {
'ç': 'c', 'ə': 'e', 'ğ': 'g', 'ı': 'i', 'ö': 'o', 'ş': 's', 'ü': 'u',
'Ç': 'C', 'Ə': 'E', 'Ğ': 'G', 'İ': 'I', 'Ö': 'O', 'Ş': 'S', 'Ü': 'U'
};
// 格鲁吉亚字符映射表
const GEORGIAN_MAP = {
'ა': 'a', 'ბ': 'b', 'გ': 'g', 'დ': 'd', 'ე': 'e', 'ვ': 'v', 'ზ': 'z',
'თ': 't', 'ი': 'i', 'კ': 'k', 'ლ': 'l', 'მ': 'm', 'ნ': 'n', 'ო': 'o',
@ -106,6 +137,7 @@
'წ': 'w', 'ჭ': 'ch', 'ხ': 'x', 'ჯ': 'j', 'ჰ': 'h'
};
// 所有字符映射表的数组
const ALL_DOWNCODE_MAPS = [
LATIN_MAP,
LATIN_SYMBOLS_MAP,
@ -125,19 +157,30 @@
GEORGIAN_MAP
];
// 字符降码处理对象
const Downcoder = {
/**
* 初始化字符映射表
*/
'Initialize': function() {
if (Downcoder.map) { // already made
if (Downcoder.map) { // 如果已经初始化过,则直接返回
return;
}
Downcoder.map = {};
// 将所有映射表合并到一个对象中
for (const lookup of ALL_DOWNCODE_MAPS) {
Object.assign(Downcoder.map, lookup);
}
// 创建用于匹配所有映射字符的正则表达式
Downcoder.regex = new RegExp(Object.keys(Downcoder.map).join('|'), 'g');
}
};
/**
* 将特殊字符转换为普通字符
* @param {string} slug - 需要转换的字符串
* @returns {string} 转换后的字符串
*/
function downcode(slug) {
Downcoder.Initialize();
return slug.replace(Downcoder.regex, function(m) {
@ -146,24 +189,31 @@
}
/**
* 将字符串转换为URL友好的格式
* 例如将"Petty theft"转换为"petty-theft"
* @param {string} s - 输入字符串
* @param {number} num_chars - 最大字符数
* @param {boolean} allowUnicode - 是否允许Unicode字符
* @returns {string} URL友好的字符串
*/
function URLify(s, num_chars, allowUnicode) {
// changes, e.g., "Petty theft" to "petty-theft"
// 如果不允许Unicode字符则进行字符降码处理
if (!allowUnicode) {
s = downcode(s);
}
s = s.toLowerCase(); // convert to lowercase
// if downcode doesn't hit, the char will be stripped here
s = s.toLowerCase(); // 转换为小写
// 如果降码处理没有命中,字符将在这里被移除
if (allowUnicode) {
// Keep Unicode letters including both lowercase and uppercase
// characters, whitespace, and dash; remove other characters.
// 保留Unicode字母包括大小写字符、空白字符和连字符移除其他字符
s = XRegExp.replace(s, XRegExp('[^-_\\p{L}\\p{N}\\s]', 'g'), '');
} else {
s = s.replace(/[^-\w\s]/g, ''); // remove unneeded chars
s = s.replace(/[^-\w\s]/g, ''); // 移除不需要的字符
}
s = s.replace(/^\s+|\s+$/g, ''); // trim leading/trailing spaces
s = s.replace(/[-\s]+/g, '-'); // convert spaces to hyphens
s = s.substring(0, num_chars); // trim to first num_chars chars
return s.replace(/-+$/g, ''); // trim any trailing hyphens
s = s.replace(/^\s+|\s+$/g, ''); // 去除开头和结尾的空格
s = s.replace(/[-\s]+/g, '-'); // 将空格转换为连字符
s = s.substring(0, num_chars); // 截取指定长度的字符
return s.replace(/-+$/g, ''); // 去除末尾的连字符
}
window.URLify = URLify;
}
}

@ -1,3 +1,22 @@
/*! Select2 4.0.13 | https://github.com/select2/select2/blob/master/LICENSE.md */
// 简体中文国际化语言包
// 为Select2下拉选择框提供简体中文的用户界面文本翻译
!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/zh-CN",[],function(){return{errorLoading:function(){return"无法载入结果。"},inputTooLong:function(n){return"请删除"+(n.input.length-n.maximum)+"个字符"},inputTooShort:function(n){return"请再输入至少"+(n.minimum-n.input.length)+"个字符"},loadingMore:function(){return"载入更多结果…"},maximumSelected:function(n){return"最多只能选择"+n.maximum+"个项目"},noResults:function(){return"未找到结果"},searching:function(){return"搜索中…"},removeAllItems:function(){return"删除所有项目"}}}),n.define,n.require}();
!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/zh-CN",[],function(){return{
// 加载结果失败时的错误提示
errorLoading:function(){return"无法载入结果。"},
// 输入内容过长时的提示信息
inputTooLong:function(n){return"请删除"+(n.input.length-n.maximum)+"个字符"},
// 输入内容过短时的提示信息
inputTooShort:function(n){return"请再输入至少"+(n.minimum-n.input.length)+"个字符"},
// 加载更多结果时的提示信息
loadingMore:function(){return"载入更多结果…"},
// 超过最大选择数量时的提示信息
maximumSelected:function(n){return"最多只能选择"+n.maximum+"个项目"},
// 没有找到匹配结果时的提示信息
noResults:function(){return"未找到结果"},
// 搜索过程中的提示信息
searching:function(){return"搜索中…"},
// 删除所有选中项的提示信息
removeAllItems:function(){return"删除所有项目"}
}}),n.define,n.require}();

@ -1,3 +1,20 @@
/*! Select2 4.0.13 | https://github.com/select2/select2/blob/master/LICENSE.md */
// 繁体中文国际化语言包
// 为Select2下拉选择框提供繁体中文的用户界面文本翻译
!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/zh-TW",[],function(){return{inputTooLong:function(n){return"請刪掉"+(n.input.length-n.maximum)+"個字元"},inputTooShort:function(n){return"請再輸入"+(n.minimum-n.input.length)+"個字元"},loadingMore:function(){return"載入中…"},maximumSelected:function(n){return"你只能選擇最多"+n.maximum+"項"},noResults:function(){return"沒有找到相符的項目"},searching:function(){return"搜尋中…"},removeAllItems:function(){return"刪除所有項目"}}}),n.define,n.require}();
!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define("select2/i18n/zh-TW",[],function(){return{
// 输入内容过长时的提示信息
inputTooLong:function(n){return"請刪掉"+(n.input.length-n.maximum)+"個字元"},
// 输入内容过短时的提示信息
inputTooShort:function(n){return"請再輸入"+(n.minimum-n.input.length)+"個字元"},
// 加载更多结果时的提示信息
loadingMore:function(){return"載入中…"},
// 超过最大选择数量时的提示信息
maximumSelected:function(n){return"你只能選擇最多"+n.maximum+"項"},
// 没有找到匹配结果时的提示信息
noResults:function(){return"沒有找到相符的項目"},
// 搜索过程中的提示信息
searching:function(){return"搜尋中…"},
// 删除所有选中项的提示信息
removeAllItems:function(){return"刪除所有項目"}
}}),n.define,n.require}();

@ -5,18 +5,104 @@
* Released under the MIT license
* https://github.com/select2/select2/blob/master/LICENSE.md
*/
// Select2是一个基于jQuery的下拉选择框增强插件提供了更强大的功能和更好的用户体验
// 此文件是Select2的完整版本包含了所有功能实现
// almond.js - 一个轻量级的AMD模块加载器实现
// 用于在没有完整AMD加载器如RequireJS的环境中运行Select2
var S2 =(function () {
// 恢复Select2 AMD加载器使其可以被使用
// 主要用于语言文件中,因为在那些文件中加载器可能不存在
if (jQuery && jQuery.fn && jQuery.fn.select2 && jQuery.fn.select2.amd) {
var S2 = jQuery.fn.select2.amd;
}
var define, requirejs, require, requirejsLib;
if (typeof S2 === 'undefined') {
S2 = {};
}
define = S2.define = function (id, deps, factory) {
if (typeof id !== 'string') {
throw new Error('Module id must be a string');
}
if (deps.indexOf('exports') === -1) {
throw new Error('Module ' + id + ' must have an exports dependency');
}
if (deps.indexOf('module') === -1) {
throw new Error('Module ' + id + ' must have a module dependency');
}
if (S2.registry[id]) {
throw new Error('Module ' + id + ' is already defined');
}
S2.registry[id] = {
id: id,
deps: deps,
factory: factory
};
};
requirejs = S2.requirejs = S2.require = require = function (deps, callback) {
var module, dep, depModule, exports, callbackType,
modules = [],
i, j, len, args = [], arg;
for (i = 0, len = deps.length; i < len; i++) {
dep = deps[i];
if (dep === 'exports') {
exports = {};
args.push(exports);
} else if (dep === 'module') {
module = {
id: id,
uri: '',
exports: exports
};
args.push(module);
} else {
depModule = S2.registry[dep];
if (!depModule) {
throw new Error('Module ' + dep + ' is not defined');
}
modules.push(depModule);
args.push(depModule.exports);
}
}
callbackType = typeof callback;
if (callbackType === 'function') {
callback.apply(null, args);
} else if (callbackType === 'string') {
return eval(callback);
}
return exports;
};
define.amd = {
jQuery: true
};
S2.registry = {};
return S2;
})();
;(function (factory) {
// 检测模块加载器类型并进行相应处理
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
// AMD模块加载器支持
define(['jquery'], factory);
} else if (typeof module === 'object' && module.exports) {
// Node/CommonJS
// Node/CommonJS模块支持
module.exports = function (root, jQuery) {
if (jQuery === undefined) {
// require('jQuery') returns a factory that requires window to
// build a jQuery instance, we normalize how we use modules
// that require this pattern but the window provided is a noop
// if it's defined (how jquery works)
// 如果jQuery未定义则尝试通过require加载
if (typeof window !== 'undefined') {
jQuery = require('jquery');
}
@ -28,10 +114,12 @@
return jQuery;
};
} else {
// Browser globals
// 浏览器全局变量支持
factory(jQuery);
}
} (function (jQuery) {
// 这里是Select2的主要实现代码
// This is needed so we can catch the AMD loader configuration and use it
// The inner file should be wrapped (by `banner.start.js`) in a function that
// returns the AMD loader references.

File diff suppressed because one or more lines are too long

@ -1,5 +1,6 @@
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.XRegExp = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";
"use strict";
var _sliceInstanceProperty = require("@babel/runtime-corejs3/core-js-stable/instance/slice");
@ -24,13 +25,11 @@ exports["default"] = void 0;
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/slicedToArray"));
var _forEach = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/for-each"));
var _concat = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/concat"));
var _indexOf = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/index-of"));
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof _Symbol !== "undefined" && _getIteratorMethod(o) || o["@@iterator"]; if (!it) { if (_Array$isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof _Symbol !== "undefined" && _getIteratorMethod(o) || o["@@iterator"]; if (!it) { if (_Array$isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr =
method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
function _unsupportedIterableToArray(o, minLen) { var _context4; if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = _sliceInstanceProperty(_context4 = Object.prototype.toString.call(o)).call(_context4, 8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return _Array$from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
@ -40,41 +39,40 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
* <xregexp.com>
* Steven Levithan (c) 2008-present MIT License
*/
// XRegExp是一个增强的JavaScript正则表达式库
// 此文件是XRegExp的Unicode基础模块提供对Unicode匹配的支持
var _default = function _default(XRegExp) {
/**
* Adds base support for Unicode matching:
* - Adds syntax `\p{..}` for matching Unicode tokens. Tokens can be inverted using `\P{..}` or
* `\p{^..}`. Token names ignore case, spaces, hyphens, and underscores. You can omit the
* braces for token names that are a single letter (e.g. `\pL` or `PL`).
* - Adds flag A (astral), which enables 21-bit Unicode support.
* - Adds the `XRegExp.addUnicodeData` method used by other addons to provide character data.
* 添加对Unicode匹配的基础支持
* - 添加语法 `\p{..}` 用于匹配Unicode标记可以通过 `\P{..}` `\p{^..}` 反转标记
* 标记名称忽略大小写空格连字符和下划线对于单字母标记名称可以省略大括号例如 `\pL` `PL`
* - 添加标志Aastral启用21位Unicode支持
* - 添加 `XRegExp.addUnicodeData` 方法由其他插件用于提供字符数据
*
* Unicode Base relies on externally provided Unicode character data. Official addons are
* available to provide data for Unicode categories, scripts, and properties.
* Unicode Base依赖于外部提供的Unicode字符数据官方插件可用于提供Unicode类别脚本和属性的数据
*
* @requires XRegExp
*/
// ==--------------------------==
// Private stuff
// 私有内容
// ==--------------------------==
// Storage for Unicode data
// Unicode数据存储
var unicode = {};
var unicodeTypes = {}; // Reuse utils
var unicodeTypes = {}; // 重用工具函数
var dec = XRegExp._dec;
var hex = XRegExp._hex;
var pad4 = XRegExp._pad4; // Generates a token lookup name: lowercase, with hyphens, spaces, and underscores removed
var pad4 = XRegExp._pad4; // 生成标记查找名称:小写,去除连字符、空格和下划线
function normalize(name) {
return name.replace(/[- _]+/g, '').toLowerCase();
} // Gets the decimal code of a literal code unit, \xHH, \uHHHH, or a backslash-escaped literal
} // 获取文字代码单元、\xHH、\uHHHH或反斜杠转义文字的十进制代码
function charCode(chr) {
var esc = /^\\[xu](.+)/.exec(chr);
return esc ? dec(esc[1]) : chr.charCodeAt(chr[0] === '\\' ? 1 : 0);
} // Inverts a list of ordered BMP characters and ranges
} // 反转有序BMP字符和范围列表
function invertBmp(range) {
var output = '';
@ -102,14 +100,12 @@ var _default = function _default(XRegExp) {
}
return output;
} // Generates an inverted BMP range on first use
} // 首次使用时生成反转的BMP范围
function cacheInvertedBmp(slug) {
var prop = 'b!';
return unicode[slug][prop] || (unicode[slug][prop] = invertBmp(unicode[slug].bmp));
} // Combines and optionally negates BMP and astral data
} // 组合并可选地否定BMP和astral数据
function buildAstral(slug, isNegated) {
var item = unicode[slug];
@ -129,27 +125,25 @@ var _default = function _default(XRegExp) {
var _context2;
combined += (0, _concat["default"])(_context2 = "".concat(item.astral ? '|' : '', "[")).call(_context2, item.bmp, "]");
} // Astral Unicode tokens always match a code point, never a code unit
} // Astral Unicode标记始终匹配码点从不匹配码元
return isNegated ? "(?:(?!".concat(combined, ")(?:[\uD800-\uDBFF][\uDC00-\uDFFF]|[\0-\uFFFF]))") : "(?:".concat(combined, ")");
} // Builds a complete astral pattern on first use
} // 首次使用时构建完整的astral模式
function cacheAstral(slug, isNegated) {
var prop = isNegated ? 'a!' : 'a=';
return unicode[slug][prop] || (unicode[slug][prop] = buildAstral(slug, isNegated));
} // ==--------------------------==
// Core functionality
// 核心功能
// ==--------------------------==
/*
* Add astral mode (flag A) and Unicode token syntax: `\p{..}`, `\P{..}`, `\p{^..}`, `\pC`.
* 添加astral模式标志A和Unicode标记语法`\p{..}``\P{..}``\p{^..}``\pC`
*/
XRegExp.addToken( // Use `*` instead of `+` to avoid capturing `^` as the token name in `\p{^}`
/\\([pP])(?:{(\^?)(?:(\w+)=)?([^}]*)}|([A-Za-z]))/, function (match, scope, flags) {
XRegExp.addToken( // 使用`*`而不是`+`避免将`^`捕获为标记名称在`\p{^}`
/\\([pP])(?:{(\^?)(?:(\w+)=)?([^}]*)}|([A-Za-z]))/, function (match, scope, flags) {
var ERR_DOUBLE_NEG = 'Invalid double negation ';
var ERR_UNKNOWN_NAME = 'Unknown Unicode token ';
var ERR_UNKNOWN_REF = 'Unicode token missing data ';
@ -162,15 +156,13 @@ var _default = function _default(XRegExp) {
caretNegation = _match[2],
typePrefix = _match[3],
tokenName = _match[4],
tokenSingleCharName = _match[5]; // Negated via \P{..} or \p{^..}
tokenSingleCharName = _match[5]; // 通过\P{..}或\p{^..}否定
var isNegated = pPrefix === 'P' || !!caretNegation; // 通过标志A从BMP0-FFFF切换到astral0-10FFFF模式
var isNegated = pPrefix === 'P' || !!caretNegation; // Switch from BMP (0-FFFF) to astral (0-10FFFF) mode via flag A
var isAstralMode = (0, _indexOf["default"])(flags).call(flags, 'A') !== -1; // 标记查找名称。首先检查`tokenSingleCharName`以避免传递`undefined`
var isAstralMode = (0, _indexOf["default"])(flags).call(flags, 'A') !== -1; // Token lookup name. Check `tokenSingleCharName` first to avoid passing `undefined`
// via `\p{}`
var slug = normalize(tokenSingleCharName || tokenName); // Token data object
var slug = normalize(tokenSingleCharName || tokenName); // 标记数据对象
var item = unicode[slug];
@ -186,14 +178,14 @@ var _default = function _default(XRegExp) {
if (!(unicodeTypes[typePrefix] && unicodeTypes[typePrefix][slug])) {
throw new SyntaxError(ERR_UNKNOWN_NAME + fullToken);
}
} // Switch to the negated form of the referenced Unicode token
} // 切换到引用的Unicode标记的否定形式
if (item.inverseOf) {
slug = normalize(item.inverseOf);
if (!unicode.hasOwnProperty(slug)) {
var _context3;
var _context3;
throw new ReferenceError((0, _concat["default"])(_context3 = "".concat(ERR_UNKNOWN_REF + fullToken, " -> ")).call(_context3, item.inverseOf));
}
@ -221,29 +213,29 @@ var _default = function _default(XRegExp) {
leadChar: '\\'
});
/**
* Adds to the list of Unicode tokens that XRegExp regexes can match via `\p` or `\P`.
leadChar: '\\'
});
/**
* 添加到XRegExp正则表达式可通过`\p``\P`匹配的Unicode标记列表中
*
* @memberOf XRegExp
* @param {Array} data Objects with named character ranges. Each object may have properties
* `name`, `alias`, `isBmpLast`, `inverseOf`, `bmp`, and `astral`. All but `name` are
* optional, although one of `bmp` or `astral` is required (unless `inverseOf` is set). If
* `astral` is absent, the `bmp` data is used for BMP and astral modes. If `bmp` is absent,
* the name errors in BMP mode but works in astral mode. If both `bmp` and `astral` are
* provided, the `bmp` data only is used in BMP mode, and the combination of `bmp` and
* `astral` data is used in astral mode. `isBmpLast` is needed when a token matches orphan
* high surrogates *and* uses surrogate pairs to match astral code points. The `bmp` and
* `astral` data should be a combination of literal characters and `\xHH` or `\uHHHH` escape
* sequences, with hyphens to create ranges. Any regex metacharacters in the data should be
* escaped, apart from range-creating hyphens. The `astral` data can additionally use
* character classes and alternation, and should use surrogate pairs to represent astral code
* points. `inverseOf` can be used to avoid duplicating character data if a Unicode token is
* defined as the exact inverse of another token.
* @param {String} [typePrefix] Enables optionally using this type as a prefix for all of the
* provided Unicode tokens, e.g. if given `'Type'`, then `\p{TokenName}` can also be written
* as `\p{Type=TokenName}`.
* @param {Array} data 带有命名字符范围的对象每个对象可以有属性
* `name``alias``isBmpLast``inverseOf``bmp``astral`除了`name`之外都是
* 可选的尽管需要`bmp``astral`之一除非设置了`inverseOf`如果
* 缺少`astral`则在BMP和astral模式下都使用`bmp`数据如果缺少`bmp`
* 在BMP模式下名称会出错但在astral模式下有效如果同时提供了`bmp``astral`
* 在BMP模式下仅使用`bmp`数据在astral模式下结合使用`bmp``astral`数据当标记匹配孤儿
* 高代理项*并且*使用代理对匹配astral码点时需要`isBmpLast``bmp`
* `astral`数据应该是文字字符和`\xHH``\uHHHH`转义序列的组合
* 使用连字符创建范围数据中的任何正则元字符都应该被转义除了创建范围的连字符`astral`数据可以额外使用
* 字符类和替代并应使用代理对表示astral码点`inverseOf`可用于避免重复字符数据如果Unicode标记被定义为另一个标记的精确反向时使用
* @param {String} [typePrefix] 启用可选地使用此类型作为所有提供的Unicode标记的前缀例如如果给定`'Type'``\p{TokenName}`也可以写成`\p{Type=TokenName}`
* @example
*
* // Basic use
* // 基本使用
* @example
*
* // 基本使用
* XRegExp.addUnicodeData([{
* name: 'XDigit',
* alias: 'Hexadecimal',
@ -257,7 +249,7 @@ var _default = function _default(XRegExp) {
var ERR_NO_DATA = 'Unicode token has no character data ';
if (typePrefix) {
// Case sensitive to match ES2018
// 区分大小写以匹配ES2018
unicodeTypes[typePrefix] = {};
}
@ -271,6 +263,8 @@ var _default = function _default(XRegExp) {
if (!item.name) {
throw new Error(ERR_NO_NAME);
}
throw new Error(ERR_NO_NAME);
}
if (!(item.inverseOf || item.bmp || item.astral)) {
throw new Error(ERR_NO_DATA + item.name);
@ -291,8 +285,7 @@ var _default = function _default(XRegExp) {
unicodeTypes[typePrefix][normalizedAlias] = true;
}
}
} // Reset the pattern cache used by the `XRegExp` constructor, since the same pattern and
// flags might now produce different results
} // 重置由`XRegExp`构造函数使用的模式缓存,因为相同的模式和标志现在可能产生不同的结果
} catch (err) {
_iterator.e(err);
@ -305,23 +298,22 @@ var _default = function _default(XRegExp) {
/**
* @ignore
*
* Return a reference to the internal Unicode definition structure for the given Unicode
* Property if the given name is a legal Unicode Property for use in XRegExp `\p` or `\P` regex
* constructs.
};
/**
* @ignore
*
* 返回给定Unicode属性的内部Unicode定义结构的引用如果给定名称是XRegExp `\p``\P`正则表达式构造中合法的Unicode属性的话
*
* @memberOf XRegExp
* @param {String} name Name by which the Unicode Property may be recognized (case-insensitive),
* e.g. `'N'` or `'Number'`. The given name is matched against all registered Unicode
* Properties and Property Aliases.
* @returns {Object} Reference to definition structure when the name matches a Unicode Property.
* @param {String} name 可识别Unicode属性的名称不区分大小写例如`'N'``'Number'`给定名称与所有已注册的Unicode属性和属性别名匹配
* @returns {Object} 当名称匹配Unicode属性时返回定义结构的引用
*
* @note
* For more info on Unicode Properties, see also http://unicode.org/reports/tr18/#Categories.
* 更多关于Unicode属性的信息请参见 http://unicode.org/reports/tr18/#Categories.
*
* @note
* This method is *not* part of the officially documented API and may change or be removed in
* the future. It is meant for userland code that wishes to reuse the (large) internal Unicode
* structures set up by XRegExp.
* 此方法*不是*官方文档API的一部分将来可能会更改或被删除它适用于希望重用XRegExp设置的大量内部Unicode结构的用户代码
* structures set up by XRegExp.
*/
@ -356,11 +348,11 @@ var _categories = _interopRequireDefault(require("../../tools/output/categories"
*/
var _default = function _default(XRegExp) {
/**
* Adds support for Unicode's general categories. E.g., `\p{Lu}` or `\p{Uppercase Letter}`. See
* category descriptions in UAX #44 <http://unicode.org/reports/tr44/#GC_Values_Table>. Token
* names are case insensitive, and any spaces, hyphens, and underscores are ignored.
* 添加对Unicode通用类别的支持例如`\p{Lu}``\p{Uppercase Letter}`请参阅
* UAX #44 <http://unicode.org/reports/tr44/#GC_Values_Table>中的类别描述。标记
* 名称不区分大小写并忽略任何空格连字符和下划线
*
* Uses Unicode 14.0.0.
* 使用Unicode 14.0.0.
*
* @requires XRegExp, Unicode Base
*/
@ -383,6 +375,8 @@ var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequ
_Object$defineProperty(exports, "__esModule", {
value: true
});
value: true
});
exports["default"] = void 0;
@ -403,7 +397,6 @@ module.exports = exports.default;
var _sliceInstanceProperty2 = require("@babel/runtime-corejs3/core-js-stable/instance/slice");
var _Array$from = require("@babel/runtime-corejs3/core-js-stable/array/from");
var _Symbol = require("@babel/runtime-corejs3/core-js-stable/symbol");
var _getIteratorMethod = require("@babel/runtime-corejs3/core-js/get-iterator-method");
@ -427,7 +420,6 @@ var _flags = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stab
var _sort = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/sort"));
var _slice = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/slice"));
var _parseInt2 = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/parse-int"));
var _indexOf = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/index-of"));
@ -437,9 +429,8 @@ var _forEach = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-st
var _create = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/create"));
var _concat = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/concat"));
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof _Symbol !== "undefined" && _getIteratorMethod(o) || o["@@iterator"]; if (!it) { if (_Array$isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof _Symbol !== "undefined" && _getIteratorMethod(o) || o["@@iterator"]; if (!it) { if (_Array$isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr =
method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
function _unsupportedIterableToArray(o, minLen) { var _context9; if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = _sliceInstanceProperty2(_context9 = Object.prototype.toString.call(o)).call(_context9, 8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return _Array$from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
@ -451,29 +442,38 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
*/
/**
* XRegExp provides augmented, extensible regular expressions. You get additional regex syntax and
* flags, beyond what browsers support natively. XRegExp is also a regex utility belt with tools to
* make your client-side grepping simpler and more powerful, while freeing you from related
* cross-browser inconsistencies.
*/
/**
* XRegExp提供增强的可扩展的正则表达式您可以获得额外的正则表达式语法和标志超出浏览器原生支持的范围XRegExp还是一个正则表达式工具带使您的客户端搜索更简单更强大同时免受相关的跨浏览器不一致性的影响
*/
// ==--------------------------==
// Private stuff
// 私有内容
// ==--------------------------==
// Property name used for extended regex instance data
var REGEX_DATA = 'xregexp'; // Optional features that can be installed and uninstalled
// 用于扩展正则表达式实例数据的属性名
var REGEX_DATA = 'xregexp'; // 可安装和卸载的可选功能
var features = {
astral: false,
namespacing: true
}; // Storage for fixed/extended native methods
}; // 固定/扩展的原生方法存储
var fixed = {}; // 由`XRegExp.cache`缓存的正则表达式存储
var fixed = {}; // Storage for regexes cached by `XRegExp.cache`
var regexCache = {}; // 由`XRegExp`构造函数缓存的模式详细信息存储
var regexCache = {}; // Storage for pattern details cached by the `XRegExp` constructor
var patternCache = {}; // 由内部或`XRegExp.addToken`添加的正则表达式语法标记存储
var patternCache = {}; // Storage for regex syntax tokens added internally or by `XRegExp.addToken`
var tokens = []; // 标记作用域
var tokens = []; // Token scopes
var defaultScope = 'default';
var classScope = 'class'; // 匹配原生正则表达式语法的正则表达式,包括八进制
var nativeTokens = {
// 默认作用域中的任何原生多字符标记,或任何单个字符
'default': /\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9]\d*|x[\dA-Fa-f]{2}|u(?:[\dA-Fa-f]{4}|{[\dA-Fa-f]+})|c[A-Za-z]|[\s\S])|\(\?(?:[:=!]|<[=!])|[?*+]\?|{\d+(?:,\d*)?}\??|[\s\S]/,
// 字符类作用域中的任何原生多字符标记,或任何单个字符
'class': /\\(?:[0-3][0-7]{0,2}|[4-7][0-7]?|x[\dA-Fa-f]{2}|u(?:[\dA-Fa-f]{4}|{[\dA-Fa-f]+})|c[A-Za-z]|[\s\S])|[\s\S]/```
var defaultScope = 'default';
var classScope = 'class'; // Regexes that match native regex syntax, including octals

@ -1,58 +1,78 @@
/* 登录页面样式 */
body {
padding-top: 40px;
padding-bottom: 40px;
background-color: #fff;
padding-top: 40px; /* 顶部内边距 */
padding-bottom: 40px; /* 底部内边距 */
background-color: #fff; /* 背景颜色为白色 */
}
/* 登录表单样式 */
.form-signin {
max-width: 330px;
padding: 15px;
margin: 0 auto;
max-width: 330px; /* 最大宽度 */
padding: 15px; /* 内边距 */
margin: 0 auto; /* 居中显示 */
}
/* 登录表单标题样式 */
.form-signin-heading {
margin: 0 0 15px;
font-size: 18px;
font-weight: 400;
color: #555;
margin: 0 0 15px; /* 外边距 */
font-size: 18px; /* 字体大小 */
font-weight: 400; /* 字体粗细 */
color: #555; /* 字体颜色 */
}
/* 复选框样式 */
.form-signin .checkbox {
margin-bottom: 10px;
font-weight: normal;
margin-bottom: 10px; /* 底部外边距 */
font-weight: normal; /* 正常字体粗细 */
}
/* 表单控件通用样式 */
.form-signin .form-control {
position: relative;
height: auto;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 10px;
font-size: 16px;
position: relative; /* 相对定位 */
height: auto; /* 自动高度 */
-webkit-box-sizing: border-box; /* Webkit浏览器盒模型 */
-moz-box-sizing: border-box; /* Mozilla浏览器盒模型 */
box-sizing: border-box; /* 标准盒模型 */
padding: 10px; /* 内边距 */
font-size: 16px; /* 字体大小 */
}
/* 表单控件获得焦点时的样式 */
.form-signin .form-control:focus {
z-index: 2;
z-index: 2; /* 层级 */
}
/* 邮箱输入框样式 */
.form-signin input[type="email"] {
margin-bottom: 10px;
margin-bottom: 10px; /* 底部外边距 */
}
/* 密码输入框样式 */
.form-signin input[type="password"] {
margin-bottom: 10px;
margin-bottom: 10px; /* 底部外边距 */
}
/* 卡片容器样式 */
.card {
width: 304px;
padding: 20px 25px 30px;
margin: 0 auto 25px;
background-color: #f7f7f7;
border-radius: 2px;
-webkit-box-shadow: 0 2px 2px rgba(0, 0, 0, .3);
box-shadow: 0 2px 2px rgba(0, 0, 0, .3);
width: 304px; /* 宽度 */
padding: 20px 25px 30px; /* 内边距上20px左右25px下30px */
margin: 0 auto 25px; /* 居中显示底部外边距25px */
background-color: #f7f7f7; /* 背景颜色 */
border-radius: 2px; /* 圆角 */
-webkit-box-shadow: 0 2px 2px rgba(0, 0, 0, .3); /* Webkit浏览器阴影 */
box-shadow: 0 2px 2px rgba(0, 0, 0, .3); /* 标准阴影 */
}
/* 登录卡片样式 */
.card-signin {
width: 354px;
padding: 40px;
width: 354px; /* 宽度 */
padding: 40px; /* 内边距 */
}
/* 登录卡片中的头像图片样式 */
.card-signin .profile-img {
display: block;
width: 96px;
height: 96px;
margin: 0 auto 10px;
}
display: block; /* 显示为块级元素 */
width: 96px; /* 宽度 */
height: 96px; /* 高度 */
margin: 0 auto 10px; /* 居中显示底部外边距10px */
}

@ -1,13 +1,18 @@
/*
Styles for older IE versions (previous to IE9).
IEIE9
*/
/* 页面主体背景色 */
body {
background-color: #e6e6e6;
}
/* 自定义背景为空时的样式 */
body.custom-background-empty {
background-color: #fff;
}
/* 自定义背景为空或白色时站点样式 */
body.custom-background-empty .site,
body.custom-background-white .site {
box-shadow: none;
@ -15,14 +20,20 @@ body.custom-background-white .site {
margin-top: 0;
padding: 0;
}
/* 辅助文本和屏幕阅读器文本 */
.assistive-text,
.site .screen-reader-text {
clip: rect(1px 1px 1px 1px);
}
/* 全宽页面内容 */
.full-width .site-content {
float: none;
width: 100%;
}
/* 防止IE8中具有高度和宽度属性的完整尺寸和大尺寸图像被拉伸 */
img.size-full,
img.size-large,
img.header-image,
@ -30,17 +41,23 @@ img.wp-post-image,
img[class*="align"],
img[class*="wp-image-"],
img[class*="attachment-"] {
width: auto; /* Prevent stretching of full-size and large-size images with height and width attributes in IE8 */
width: auto; /* 防止在IE8中拉伸完整尺寸和大尺寸图像 */
}
/* 作者头像 */
.author-avatar {
float: left;
margin-top: 8px;
margin-top: 0.571428571rem;
}
/* 作者描述 */
.author-description {
float: right;
width: 80%;
}
/* 站点容器 */
.site {
box-shadow: 0 2px 6px rgba(100, 100, 100, 0.3);
margin: 48px auto;
@ -48,27 +65,39 @@ img[class*="attachment-"] {
overflow: hidden;
padding: 0 40px;
}
/* 站点内容 */
.site-content {
float: left;
width: 65.104166667%;
}
/* 不同页面模板的站点内容宽度 */
body.template-front-page .site-content,
body.attachment .site-content,
body.full-width .site-content {
width: 100%;
}
/* 小工具区域 */
.widget-area {
float: right;
width: 26.041666667%;
}
/* 站点头部标题 */
.site-header h1,
.site-header h2 {
text-align: left;
}
/* 站点头部一级标题 */
.site-header h1 {
font-size: 26px;
line-height: 1.846153846;
}
/* 主导航菜单 */
.main-navigation ul.nav-menu,
.main-navigation div.nav-menu > ul {
border-bottom: 1px solid #ededed;
@ -77,32 +106,46 @@ body.full-width .site-content {
text-align: left;
width: 100%;
}
/* 主导航菜单列表 */
.main-navigation ul {
margin: 0;
text-indent: 0;
}
/* 主导航菜单项和链接 */
.main-navigation li a,
.main-navigation li {
display: inline-block;
text-decoration: none;
}
/* IE7浏览器特殊处理 */
.ie7 .main-navigation li a,
.ie7 .main-navigation li {
display: inline;
}
/* 主导航菜单链接 */
.main-navigation li a {
border-bottom: 0;
color: #6a6a6a;
line-height: 3.692307692;
text-transform: uppercase;
}
/* 主导航菜单链接悬停效果 */
.main-navigation li a:hover {
color: #000;
}
/* 主导航菜单项 */
.main-navigation li {
margin: 0 40px 0 0;
position: relative;
}
/* 主导航菜单下拉列表 */
.main-navigation li ul {
margin: 0;
padding: 0;
@ -114,17 +157,23 @@ body.full-width .site-content {
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
}
/* IE7浏览器下拉列表特殊处理 */
.ie7 .main-navigation li ul {
clip: inherit;
display: none;
left: 0;
overflow: visible;
}
/* 多级下拉菜单 */
.main-navigation li ul ul,
.ie7 .main-navigation li ul ul {
top: 0;
left: 100%;
}
/* 下拉菜单显示 */
.main-navigation ul li:hover > ul,
.main-navigation ul li:focus > ul,
.main-navigation .focus > ul {
@ -134,10 +183,14 @@ body.full-width .site-content {
height: inherit;
width: inherit;
}
/* IE7浏览器下拉菜单显示特殊处理 */
.ie7 .main-navigation ul li:hover > ul,
.ie7 .main-navigation ul li:focus > ul {
display: block;
}
/* 下拉菜单项链接 */
.main-navigation li ul li a {
background: #efefef;
border-bottom: 1px solid #ededed;
@ -147,10 +200,14 @@ body.full-width .site-content {
padding: 8px 10px;
width: 180px;
}
/* 下拉菜单项链接悬停效果 */
.main-navigation li ul li a:hover {
background: #e3e3e3;
color: #444;
}
/* 当前菜单项样式 */
.main-navigation .current-menu-item > a,
.main-navigation .current-menu-ancestor > a,
.main-navigation .current_page_item > a,
@ -158,39 +215,58 @@ body.full-width .site-content {
color: #636363;
font-weight: bold;
}
/* 菜单切换按钮 */
.main-navigation .menu-toggle {
display: none;
}
/* 文章标题 */
.entry-header .entry-title {
font-size: 22px;
}
/* 评论表单输入框 */
#respond form input[type="text"] {
width: 46.333333333%;
}
/* 评论表单文本域 */
#respond form textarea.blog-textarea {
width: 79.666666667%;
}
/* 首页模板内容 */
.template-front-page .site-content,
.template-front-page article {
overflow: hidden;
}
/* 首页模板带缩略图文章 */
.template-front-page.has-post-thumbnail article {
float: left;
width: 47.916666667%;
}
/* 页面图像 */
.entry-page-image {
float: right;
margin-bottom: 0;
width: 47.916666667%;
}
/* IE Front Page Template Widget fix */
/* IE首页模板小工具修复 */
.template-front-page .widget-area {
clear: both;
}
/* 首页模板小工具 */
.template-front-page .widget {
width: 100% !important;
border: none;
}
/* 首页模板小工具区域 */
.template-front-page .widget-area .widget,
.template-front-page .first.front-widgets,
.template-front-page.two-sidebars .widget-area .front-widgets {
@ -198,10 +274,14 @@ body.full-width .site-content {
margin-bottom: 24px;
width: 51.875%;
}
/* 首页模板第二组小工具 */
.template-front-page .second.front-widgets,
.template-front-page .widget-area .widget:nth-child(odd) {
clear: right;
}
/* 首页模板第一组和第二组小工具 */
.template-front-page .first.front-widgets,
.template-front-page .second.front-widgets,
.template-front-page.two-sidebars .widget-area .front-widgets + .front-widgets {
@ -209,64 +289,88 @@ body.full-width .site-content {
margin: 0 0 24px;
width: 39.0625%;
}
/* 双侧边栏模板小工具 */
.template-front-page.two-sidebars .widget,
.template-front-page.two-sidebars .widget:nth-child(even) {
float: none;
width: auto;
}
/* add input font for <IE9 Password Box to make the bullets show up */
/* 为IE9之前的密码框添加字体以使圆点显示 */
input[type="password"] {
font-family: Helvetica, Arial, sans-serif;
}
/* RTL overrides for IE7 and IE8
/* IE7IE8RTL
-------------------------------------------------------------- */
/* RTL布局站点标题 */
.rtl .site-header h1,
.rtl .site-header h2 {
text-align: right;
}
/* RTL布局小工具区域和作者描述 */
.rtl .widget-area,
.rtl .author-description {
float: left;
}
/* RTL布局作者头像和站点内容 */
.rtl .author-avatar,
.rtl .site-content {
float: right;
}
/* RTL布局导航菜单 */
.rtl .main-navigation ul.nav-menu,
.rtl .main-navigation div.nav-menu > ul {
text-align: right;
}
/* RTL布局多级导航菜单项 */
.rtl .main-navigation ul li ul li,
.rtl .main-navigation ul li ul li ul li {
margin-left: 40px;
margin-right: auto;
}
/* RTL布局三级导航菜单 */
.rtl .main-navigation li ul ul {
position: absolute;
bottom: 0;
right: 100%;
z-index: 1;
}
/* IE7浏览器RTL布局三级导航菜单特殊处理 */
.ie7 .rtl .main-navigation li ul ul {
position: absolute;
bottom: 0;
right: 100%;
z-index: 1;
}
/* IE7浏览器RTL布局导航菜单项 */
.ie7 .rtl .main-navigation ul li {
z-index: 99;
}
/* IE7浏览器RTL布局下拉菜单 */
.ie7 .rtl .main-navigation li ul {
position: absolute;
bottom: 100%;
right: 0;
z-index: 1;
}
/* IE7浏览器RTL布局导航菜单项外边距 */
.ie7 .rtl .main-navigation li {
margin-right: auto;
margin-left: 40px;
}
/* IE7浏览器RTL布局四级导航菜单 */
.ie7 .rtl .main-navigation li ul ul ul {
position: relative;
z-index: 1;

@ -1,10 +1,11 @@
/* Make clicks pass-through */
/* 使点击事件穿透 */
#nprogress {
pointer-events: none;
}
/* 进度条样式 */
#nprogress .bar {
background: red;
background: red; /* 进度条颜色 */
position: fixed;
z-index: 1031;
@ -15,7 +16,7 @@
height: 2px;
}
/* Fancy blur effect */
/* 花哨的模糊效果 */
#nprogress .peg {
display: block;
position: absolute;
@ -30,7 +31,7 @@
transform: rotate(3deg) translate(0px, -4px);
}
/* Remove these to get rid of the spinner */
/* 移除这些来去掉旋转器 */
#nprogress .spinner {
display: block;
position: fixed;
@ -39,6 +40,7 @@
right: 15px;
}
/* 旋转器图标样式 */
#nprogress .spinner-icon {
width: 18px;
height: 18px;
@ -53,22 +55,26 @@
animation: nprogress-spinner 400ms linear infinite;
}
/* 自定义父容器样式 */
.nprogress-custom-parent {
overflow: hidden;
position: relative;
}
/* 自定义父容器中的旋转器和进度条样式 */
.nprogress-custom-parent #nprogress .spinner,
.nprogress-custom-parent #nprogress .bar {
position: absolute;
}
/* Webkit内核浏览器的旋转器动画 */
@-webkit-keyframes nprogress-spinner {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
/* 标准旋转器动画 */
@keyframes nprogress-spinner {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
}

@ -1,173 +1,212 @@
/* Google图标样式 */
.icon-sn-google {
background-position: 0 -28px;
}
/* Google背景图标样式 */
.icon-sn-bg-google {
background-color: #4285f4;
background-position: 0 0;
}
/* Google字体图标颜色 */
.fa-sn-google {
color: #4285f4;
}
/* GitHub图标样式 */
.icon-sn-github {
background-position: -28px -28px;
}
/* GitHub背景图标样式 */
.icon-sn-bg-github {
background-color: #333;
background-position: -28px 0;
}
/* GitHub字体图标颜色 */
.fa-sn-github {
color: #333;
}
/* 微博图标样式 */
.icon-sn-weibo {
background-position: -56px -28px;
}
/* 微博背景图标样式 */
.icon-sn-bg-weibo {
background-color: #e90d24;
background-position: -56px 0;
}
/* 微博字体图标颜色 */
.fa-sn-weibo {
color: #e90d24;
}
/* QQ图标样式 */
.icon-sn-qq {
background-position: -84px -28px;
}
/* QQ背景图标样式 */
.icon-sn-bg-qq {
background-color: #0098e6;
background-position: -84px 0;
}
/* QQ字体图标颜色 */
.fa-sn-qq {
color: #0098e6;
}
/* Twitter图标样式 */
.icon-sn-twitter {
background-position: -112px -28px;
}
/* Twitter背景图标样式 */
.icon-sn-bg-twitter {
background-color: #50abf1;
background-position: -112px 0;
}
/* Twitter字体图标颜色 */
.fa-sn-twitter {
color: #50abf1;
}
/* Facebook图标样式 */
.icon-sn-facebook {
background-position: -140px -28px;
}
/* Facebook背景图标样式 */
.icon-sn-bg-facebook {
background-color: #4862a3;
background-position: -140px 0;
}
/* Facebook字体图标颜色 */
.fa-sn-facebook {
color: #4862a3;
}
/* 人人网图标样式 */
.icon-sn-renren {
background-position: -168px -28px;
}
/* 人人网背景图标样式 */
.icon-sn-bg-renren {
background-color: #197bc8;
background-position: -168px 0;
}
/* 人人网字体图标颜色 */
.fa-sn-renren {
color: #197bc8;
}
/* 腾讯微博图标样式 */
.icon-sn-tqq {
background-position: -196px -28px;
}
/* 腾讯微博背景图标样式 */
.icon-sn-bg-tqq {
background-color: #1f9ed2;
background-position: -196px 0;
}
/* 腾讯微博字体图标颜色 */
.fa-sn-tqq {
color: #1f9ed2;
}
/* 豆瓣图标样式 */
.icon-sn-douban {
background-position: -224px -28px;
}
/* 豆瓣背景图标样式 */
.icon-sn-bg-douban {
background-color: #279738;
background-position: -224px 0;
}
/* 豆瓣字体图标颜色 */
.fa-sn-douban {
color: #279738;
}
/* 微信图标样式 */
.icon-sn-weixin {
background-position: -252px -28px;
}
/* 微信背景图标样式 */
.icon-sn-bg-weixin {
background-color: #00b500;
background-position: -252px 0;
}
/* 微信字体图标颜色 */
.fa-sn-weixin {
color: #00b500;
}
/* 虚线图标样式 */
.icon-sn-dotted {
background-position: -280px -28px;
}
/* 虚线背景图标样式 */
.icon-sn-bg-dotted {
background-color: #eee;
background-position: -280px 0;
}
/* 虚线字体图标颜色 */
.fa-sn-dotted {
color: #eee;
}
/* 网站图标样式 */
.icon-sn-site {
background-position: -308px -28px;
}
/* 网站背景图标样式 */
.icon-sn-bg-site {
background-color: #00b500;
background-position: -308px 0;
}
/* 网站字体图标颜色 */
.fa-sn-site {
color: #00b500;
}
/* LinkedIn图标样式 */
.icon-sn-linkedin {
background-position: -336px -28px;
}
/* LinkedIn背景图标样式 */
.icon-sn-bg-linkedin {
background-color: #0077b9;
background-position: -336px 0;
}
/* LinkedIn字体图标颜色 */
.fa-sn-linkedin {
color: #0077b9;
}
/* 社交网络图标通用样式 */
[class*=icon-sn-] {
display: inline-block;
background-image: url('../img/icon-sn.svg');
@ -178,128 +217,159 @@
background-size: auto 56px;
}
/* 社交网络图标悬停效果 */
[class*=icon-sn-]:hover {
opacity: .8;
filter: alpha(opacity=80);
}
/* Google按钮样式 */
.btn-sn-google {
background: #4285f4;
}
/* Google按钮激活、聚焦、悬停样式 */
.btn-sn-google:active, .btn-sn-google:focus, .btn-sn-google:hover {
background: #2a75f3;
}
/* GitHub按钮样式 */
.btn-sn-github {
background: #333;
}
/* GitHub按钮激活、聚焦、悬停样式 */
.btn-sn-github:active, .btn-sn-github:focus, .btn-sn-github:hover {
background: #262626;
}
/* 微博按钮样式 */
.btn-sn-weibo {
background: #e90d24;
}
/* 微博按钮激活、聚焦、悬停样式 */
.btn-sn-weibo:active, .btn-sn-weibo:focus, .btn-sn-weibo:hover {
background: #d10c20;
}
/* QQ按钮样式 */
.btn-sn-qq {
background: #0098e6;
}
/* QQ按钮激活、聚焦、悬停样式 */
.btn-sn-qq:active, .btn-sn-qq:focus, .btn-sn-qq:hover {
background: #0087cd;
}
/* Twitter按钮样式 */
.btn-sn-twitter {
background: #50abf1;
}
/* Twitter按钮激活、聚焦、悬停样式 */
.btn-sn-twitter:active, .btn-sn-twitter:focus, .btn-sn-twitter:hover {
background: #38a0ef;
}
/* Facebook按钮样式 */
.btn-sn-facebook {
background: #4862a3;
}
/* Facebook按钮激活、聚焦、悬停样式 */
.btn-sn-facebook:active, .btn-sn-facebook:focus, .btn-sn-facebook:hover {
background: #405791;
}
/* 人人网按钮样式 */
.btn-sn-renren {
background: #197bc8;
}
/* 人人网按钮激活、聚焦、悬停样式 */
.btn-sn-renren:active, .btn-sn-renren:focus, .btn-sn-renren:hover {
background: #166db1;
}
/* 腾讯微博按钮样式 */
.btn-sn-tqq {
background: #1f9ed2;
}
/* 腾讯微博按钮激活、聚焦、悬停样式 */
.btn-sn-tqq:active, .btn-sn-tqq:focus, .btn-sn-tqq:hover {
background: #1c8dbc;
}
/* 豆瓣按钮样式 */
.btn-sn-douban {
background: #279738;
}
/* 豆瓣按钮激活、聚焦、悬停样式 */
.btn-sn-douban:active, .btn-sn-douban:focus, .btn-sn-douban:hover {
background: #228330;
}
/* 微信按钮样式 */
.btn-sn-weixin {
background: #00b500;
}
/* 微信按钮激活、聚焦、悬停样式 */
.btn-sn-weixin:active, .btn-sn-weixin:focus, .btn-sn-weixin:hover {
background: #009c00;
}
/* 虚线按钮样式 */
.btn-sn-dotted {
background: #eee;
}
/* 虚线按钮激活、聚焦、悬停样式 */
.btn-sn-dotted:active, .btn-sn-dotted:focus, .btn-sn-dotted:hover {
background: #e1e1e1;
}
/* 网站按钮样式 */
.btn-sn-site {
background: #00b500;
}
/* 网站按钮激活、聚焦、悬停样式 */
.btn-sn-site:active, .btn-sn-site:focus, .btn-sn-site:hover {
background: #009c00;
}
/* LinkedIn按钮样式 */
.btn-sn-linkedin {
background: #0077b9;
}
/* LinkedIn按钮激活、聚焦、悬停样式 */
.btn-sn-linkedin:active, .btn-sn-linkedin:focus, .btn-sn-linkedin:hover {
background: #0067a0;
}
/* 社交网络按钮通用样式 */
[class*=btn-sn-], [class*=btn-sn-]:active, [class*=btn-sn-]:focus, [class*=btn-sn-]:hover {
border: none;
color: #fff;
}
/* 更多按钮样式 */
.btn-sn-more {
padding: 0;
}
/* 更多按钮及激活、悬停样式 */
.btn-sn-more, .btn-sn-more:active, .btn-sn-more:hover {
box-shadow: none;
}
/* 社交网络按钮内的图标样式 */
[class*=btn-sn-] [class*=icon-sn-] {
background-color: transparent;
}

@ -1,3 +1,6 @@
/*
*/
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
margin: 0;
padding: 0;
@ -6,20 +9,24 @@ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockq
vertical-align: baseline;
}
/* 页面主体行高 */
body {
line-height: 1;
}
/* 列表样式 */
ol,
ul {
list-style: none;
}
/* 引用样式 */
blockquote,
q {
quotes: none;
}
/* 引用前后内容 */
blockquote:before,
blockquote:after,
q:before,
@ -28,11 +35,13 @@ q:after {
content: none;
}
/* 表格样式 */
table {
border-collapse: collapse;
border-spacing: 0;
}
/* 表格标题和单元格 */
caption,
th,
td {
@ -40,6 +49,7 @@ td {
text-align: left;
}
/* 标题清除浮动 */
h1,
h2,
h3,
@ -49,6 +59,7 @@ h6 {
clear: both;
}
/* HTML基础样式 */
html {
overflow-y: scroll;
font-size: 100%;
@ -56,10 +67,12 @@ html {
-ms-text-size-adjust: 100%;
}
/* 焦点链接轮廓 */
a:focus {
outline: thin dotted;
}
/* HTML5元素显示 */
article,
aside,
details,
@ -73,25 +86,30 @@ section {
display: block;
}
/* 音频、画布、视频元素 */
audio,
canvas,
video {
display: inline-block;
}
/* 无控件音频元素 */
audio:not([controls]) {
display: none;
}
/* 删除线文本颜色 */
del {
color: #333;
}
/* 插入文本样式 */
ins {
background: #fff9c0;
text-decoration: none;
}
/* 分割线样式 */
hr {
background-color: #ccc;
border: 0;
@ -100,6 +118,7 @@ hr {
margin-bottom: 1.714285714rem;
}
/* 上下标样式 */
sub,
sup {
font-size: 75%;
@ -108,30 +127,35 @@ sup {
vertical-align: baseline;
}
/* 上标位置 */
sup {
top: -0.5em;
}
/* 下标位置 */
sub {
bottom: -0.25em;
}
/* 小号字体 */
small {
font-size: smaller;
}
/* 图片样式 */
img {
border: 0;
-ms-interpolation-mode: bicubic;
}
/* Clearing floats */
/* 清除浮动 */
.clear:after,
.wrapper:after,
.format-status .entry-header:after {
clear: both;
}
/* 清除浮动辅助 */
.clear:before,
.clear:after,
.wrapper:before,
@ -142,11 +166,10 @@ img {
content: "";
}
/* =Repeatable patterns
/* =
-------------------------------------------------------------- */
/* Small headers */
/* 小标题样式 */
.archive-title,
.page-title,
.widget-title,
@ -160,7 +183,7 @@ img {
color: #636363;
}
/* Shared Post Format styling */
/* 共享文章格式样式 */
article.format-quote footer.entry-meta,
article.format-link footer.entry-meta,
article.format-status footer.entry-meta {
@ -169,7 +192,7 @@ article.format-status footer.entry-meta {
line-height: 2.181818182;
}
/* Form fields, general styles first */
/* 表单字段通用样式 */
button,
input,
select,
@ -181,18 +204,20 @@ textarea {
padding: 0.428571429rem;
}
/* 按钮和输入框行高 */
button,
input {
line-height: normal;
}
/* 文本域样式 */
textarea {
font-size: 100%;
overflow: auto;
vertical-align: top;
}
/* Reset non-text input types */
/* 重置非文本输入类型 */
input[type="checkbox"],
input[type="radio"],
input[type="file"],
@ -204,7 +229,7 @@ input[type="color"] {
padding: 0;
}
/* Buttons */
/* 按钮样式 */
.menu-toggle,
input[type="submit"],
input[type="button"],
@ -230,6 +255,7 @@ article.post-password-required input[type=submit],
box-shadow: 0 1px 2px rgba(64, 64, 64, 0.1);
}
/* 菜单切换按钮、按钮和输入框 */
.menu-toggle,
button,
input[type="submit"],
@ -238,11 +264,13 @@ input[type="reset"] {
cursor: pointer;
}
/* 禁用按钮 */
button[disabled],
input[disabled] {
cursor: default;
}
/* 按钮悬停效果 */
.menu-toggle:hover,
.menu-toggle:focus,
button:hover,
@ -260,6 +288,7 @@ article.post-password-required input[type=submit]:hover {
background-image: linear-gradient(to bottom, #f9f9f9, #ebebeb);
}
/* 按钮激活状态 */
.menu-toggle:active,
.menu-toggle.toggled-on,
button:active,
@ -278,6 +307,7 @@ input[type="reset"]:active {
border-color: transparent;
}
/* 作者评论样式 */
.bypostauthor cite span {
color: #fff;
background-color: #21759b;
@ -288,28 +318,30 @@ input[type="reset"]:active {
padding: 0;
}
/* Responsive images */
/* 响应式图片 */
.entry-content img,
.comment-content img,
.widget img {
max-width: 100%; /* Fluid images for posts, comments, and widgets */
max-width: 100%; /* 文章、评论和小工具中的流体图像 */
}
/* 对齐图片 */
img[class*="align"],
img[class*="wp-image-"],
img[class*="attachment-"] {
height: auto; /* Make sure images with WordPress-added height and width attributes are scaled correctly */
height: auto; /* 确保带有WordPress添加的高度和宽度属性的图像正确缩放 */
}
/* 全尺寸和大尺寸图片 */
img.size-full,
img.size-large,
img.header-image,
img.wp-post-image {
max-width: 100%;
height: auto; /* Make sure images with WordPress-added height and width attributes are scaled correctly */
height: auto; /* 确保带有WordPress添加的高度和宽度属性的图像正确缩放 */
}
/* Make sure videos and embeds fit their containers */
/* 确保视频和嵌入内容适应其容器 */
embed,
iframe,
object,
@ -317,11 +349,12 @@ video {
max-width: 100%;
}
/* 推特嵌入内容 */
.entry-content .twitter-tweet-rendered {
max-width: 100% !important; /* Override the Twitter embed fixed width */
max-width: 100% !important; /* 覆盖推特嵌入的固定宽度 */
}
/* Images */
/* 图片对齐 */
.alignleft {
float: left;
}
@ -336,22 +369,25 @@ video {
margin-right: auto;
}
/* WordPress添加的图像样式 */
.entry-content img,
.comment-content img,
.widget img,
img.header-image,
.author-avatar img,
img.wp-post-image {
/* Add fancy borders to all WordPress-added images but not things like badges and icons and the like */
/* 为所有WordPress添加的图像添加精美边框但不包括徽章和图标等 */
border-radius: 3px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
}
/* 图片说明 */
.wp-caption {
max-width: 100%; /* Keep wide captions from overflowing their container. */
max-width: 100%; /* 防止宽说明超出其容器 */
padding: 4px;
}
/* 图片说明文本 */
.wp-caption .wp-caption-text,
.gallery-caption,
.entry-caption {
@ -362,6 +398,7 @@ img.wp-post-image {
color: #757575;
}
/* 表情和RSS小工具图片 */
img.wp-smiley,
.rsswidget img {
border: 0;
@ -372,6 +409,7 @@ img.wp-smiley,
padding: 0;
}
/* 相册项目 */
.entry-content dl.gallery-item {
margin: 0;
}
@ -389,11 +427,13 @@ img.wp-smiley,
display: inline;
}
/* 单列相册 */
.gallery-columns-1 .gallery-item a {
max-width: 100%;
width: auto;
}
/* 相册图标 */
.gallery .gallery-icon img {
height: auto;
max-width: 90%;
@ -404,13 +444,14 @@ img.wp-smiley,
padding: 3%;
}
/* Navigation */
/* 导航 */
.site-content nav {
clear: both;
line-height: 2;
overflow: hidden;
}
/* 上方导航 */
#nav-above {
padding: 24px 0;
padding: 1.714285714rem 0;
@ -424,6 +465,7 @@ img.wp-smiley,
display: block;
}
/* 上一页/下一页导航 */
.nav-previous,
.previous-image {
float: left;
@ -437,18 +479,20 @@ img.wp-smiley,
width: 50%;
}
/* 单篇文章导航和评论区域 */
.nav-single + .comments-area,
#comment-nav-above {
margin: 48px 0;
margin: 3.428571429rem 0;
}
/* Author profiles */
/* 作者档案 */
.author .archive-header {
margin-bottom: 24px;
margin-bottom: 1.714285714rem;
}
/* 作者信息 */
.author-info {
border-top: 1px solid #ededed;
margin: 24px 0;
@ -458,6 +502,7 @@ img.wp-smiley,
overflow: hidden;
}
/* 作者描述段落 */
.author-description p {
color: #757575;
font-size: 13px;
@ -465,21 +510,22 @@ img.wp-smiley,
line-height: 1.846153846;
}
/* 作者档案页面作者信息 */
.author.archive .author-info {
border-top: 0;
margin: 0 0 48px;
margin: 0 0 3.428571429rem;
}
/* 作者档案页面作者头像 */
.author.archive .author-avatar {
margin-top: 0;
}
/* =Basic structure
/* =
-------------------------------------------------------------- */
/* Body, links, basics */
/* 页面主体、链接基础样式 */
html {
font-size: 87.5%;
}
@ -492,10 +538,12 @@ body {
color: #444;
}
/* 自定义字体启用 */
body.custom-font-enabled {
font-family: "Open Sans", Helvetica, Arial, sans-serif;
}
/* 链接样式 */
a {
outline: none;
color: #21759b;
@ -505,7 +553,7 @@ a:hover {
color: #0f3647;
}
/* Assistive text */
/* 辅助文本 */
.assistive-text,
.site .screen-reader-text {
position: absolute !important;
@ -515,6 +563,7 @@ a:hover {
width: 1px;
}
/* 主导航辅助文本焦点状态 */
.main-navigation .assistive-text:focus,
.site .screen-reader-text:hover,
.site .screen-reader-text:active,
@ -532,37 +581,41 @@ a:hover {
top: 5px;
left: 5px;
width: auto;
z-index: 100000; /* Above WP toolbar */
z-index: 100000; /* 高于WP工具栏 */
}
/* Page structure */
/* 页面结构 */
.site {
padding: 0 24px;
padding: 0 1.714285714rem;
background-color: #fff;
}
/* 站点内容 */
.site-content {
margin: 24px 0 0;
margin: 1.714285714rem 0 0;
}
/* 小工具区域 */
.widget-area {
margin: 24px 0 0;
margin: 1.714285714rem 0 0;
}
/* Header */
/* 页头 */
.site-header {
padding: 24px 0;
padding: 1.714285714rem 0;
}
/* 站点头部标题 */
.site-header h1,
.site-header h2 {
text-align: center;
}
/* 站点头部标题链接 */
.site-header h1 a,
.site-header h2 a {
color: #515151;
@ -570,11 +623,13 @@ a:hover {
text-decoration: none;
}
/* 站点头部标题链接悬停 */
.site-header h1 a:hover,
.site-header h2 a:hover {
color: #21759b;
}
/* 站点头部一级标题 */
.site-header h1 {
font-size: 24px;
font-size: 1.714285714rem;
@ -583,6 +638,7 @@ a:hover {
margin-bottom: 1rem;
}
/* 站点头部二级标题 */
.site-header h2 {
font-weight: normal;
font-size: 13px;
@ -591,18 +647,20 @@ a:hover {
color: #757575;
}
/* 头部图像 */
.header-image {
margin-top: 24px;
margin-top: 1.714285714rem;
}
/* Navigation Menu */
/* 导航菜单 */
.main-navigation {
margin-top: 24px;
margin-top: 1.714285714rem;
text-align: center;
}
/* 导航菜单项 */
.main-navigation li {
margin-top: 24px;
margin-top: 1.714285714rem;
@ -611,6 +669,7 @@ a:hover {
line-height: 1.42857143;
}
/* 导航菜单链接 */
.main-navigation a {
color: #5e5e5e;
}
@ -620,23 +679,25 @@ a:hover {
color: #21759b;
}
/* 导航菜单列表 */
.main-navigation ul.nav-menu,
.main-navigation div.nav-menu > ul {
display: none;
}
/* 展开的导航菜单和菜单切换按钮 */
.main-navigation ul.nav-menu.toggled-on,
.menu-toggle {
display: inline-block;
}
/* Banner */
/* 横幅 */
section[role="banner"] {
margin-bottom: 48px;
margin-bottom: 3.428571429rem;
}
/* Sidebar */
/* 侧边栏 */
.widget-area .widget {
-webkit-hyphens: auto;
-moz-hyphens: auto;
@ -646,11 +707,13 @@ section[role="banner"] {
word-wrap: break-word;
}
/* 侧边栏小工具标题 */
.widget-area .widget h3 {
margin-bottom: 24px;
margin-bottom: 1.714285714rem;
}
/* 侧边栏小工具段落、列表项和文本小工具 */
.widget-area .widget p,
.widget-area .widget li,
.widget-area .widget .textwidget {
@ -659,11 +722,13 @@ section[role="banner"] {
line-height: 1.846153846;
}
/* 侧边栏小工具段落 */
.widget-area .widget p {
margin-bottom: 24px;
margin-bottom: 1.714285714rem;
}
/* 侧边栏文本小工具列表 */
.widget-area .textwidget ul,
.widget-area .textwidget ol {
list-style: disc outside;
@ -671,20 +736,24 @@ section[role="banner"] {
margin: 0 0 1.714285714rem;
}
/* 侧边栏文本小工具嵌套列表 */
.widget-area .textwidget li > ul,
.widget-area .textwidget li > ol {
margin-bottom: 0;
}
/* 侧边栏文本小工具有序列表 */
.widget-area .textwidget ol {
list-style: decimal;
}
/* 侧边栏文本小工具列表项 */
.widget-area .textwidget li {
margin-left: 36px;
margin-left: 2.571428571rem;
}
/* 侧边栏小工具链接 */
.widget-area .widget a {
color: #757575;
}
@ -693,15 +762,17 @@ section[role="banner"] {
color: #21759b;
}
/* 侧边栏小工具已访问链接 */
.widget-area .widget a:visited {
color: #9f9f9f;
}
/* 侧边栏搜索框 */
.widget-area #s {
width: 53.66666666666%; /* define a width to avoid dropping a wider submit button */
width: 53.66666666666%; /* 定义宽度以避免提交按钮换行 */
}
/* Footer */
/* 页脚 */
footer[role="contentinfo"] {
border-top: 1px solid #ededed;
clear: both;
@ -718,6 +789,7 @@ footer[role="contentinfo"] {
padding: 1.714285714rem 0;
}
/* 页脚链接 */
footer[role="contentinfo"] a {
color: #686868;
}
@ -726,6 +798,7 @@ footer[role="contentinfo"] a:hover {
color: #21759b;
}
/* 站点信息分隔符 */
.site-info span[role=separator] {
padding: 0 0.3em 0 0.6em;
}
@ -734,24 +807,27 @@ footer[role="contentinfo"] a:hover {
content: '\002f';
}
/* =Main content and comment content
/* =
-------------------------------------------------------------- */
/* 文章元数据 */
.entry-meta {
clear: both;
}
/* 文章头部 */
.entry-header {
margin-bottom: 24px;
margin-bottom: 1.714285714rem;
}
/* 文章特色图像 */
.entry-header img.wp-post-image {
margin-bottom: 24px;
margin-bottom: 1.714285714rem;
}
/* 文章标题 */
.entry-header .entry-title {
font-size: 20px;
font-size: 1.428571429rem;
@ -759,16 +835,19 @@ footer[role="contentinfo"] a:hover {
font-weight: normal;
}
/* 文章标题链接 */
.entry-header .entry-title a {
text-decoration: none;
}
/* 文章格式 */
.entry-header .entry-format {
margin-top: 24px;
margin-top: 1.714285714rem;
font-weight: normal;
}
/* 评论链接 */
.entry-header .comments-link {
margin-top: 24px;
margin-top: 1.714285714rem;
@ -778,6 +857,7 @@ footer[role="contentinfo"] a:hover {
color: #757575;
}
/* 评论链接和文章元数据链接 */
.comments-link a,
.entry-meta a {
color: #757575;
@ -788,6 +868,7 @@ footer[role="contentinfo"] a:hover {
color: #21759b;
}
/* 置顶文章 */
article.sticky .featured-post {
border-top: 4px double #ededed;
border-bottom: 4px double #ededed;
@ -800,12 +881,14 @@ article.sticky .featured-post {
text-align: center;
}
/* 文章内容、摘要和注册表单 */
.entry-content,
.entry-summary,
.mu_register {
line-height: 1.714285714;
}
/* 文章内容和评论内容标题 */
.entry-content h1,
.comment-content h1,
.entry-content h2,
@ -823,6 +906,7 @@ article.sticky .featured-post {
line-height: 1.714285714;
}
/* 一级标题 */
.entry-content h1,
.comment-content h1 {
font-size: 21px;
@ -830,6 +914,7 @@ article.sticky .featured-post {
line-height: 1.5;
}
/* 二级标题 */
.entry-content h2,
.comment-content h2,
.mu_register h2 {
@ -838,6 +923,7 @@ article.sticky .featured-post {
line-height: 1.6;
}
/* 三级标题 */
.entry-content h3,
.comment-content h3 {
font-size: 16px;
@ -845,6 +931,7 @@ article.sticky .featured-post {
line-height: 1.846153846;
}
/* 四级标题 */
.entry-content h4,
.comment-content h4 {
font-size: 14px;
@ -852,6 +939,7 @@ article.sticky .featured-post {
line-height: 1.846153846;
}
/* 五级标题 */
.entry-content h5,
.comment-content h5 {
font-size: 13px;
@ -859,6 +947,7 @@ article.sticky .featured-post {
line-height: 1.846153846;
}
/* 六级标题 */
.entry-content h6,
.comment-content h6 {
font-size: 12px;
@ -866,6 +955,7 @@ article.sticky .featured-post {
line-height: 1.846153846;
}
/* 段落 */
.entry-content p,
.entry-summary p,
.comment-content p,
@ -875,15 +965,18 @@ article.sticky .featured-post {
line-height: 1.714285714;
}
/* 已访问链接 */
.entry-content a:visited,
.comment-content a:visited {
color: #9f9f9f;
}
/* 阅读更多链接 */
.entry-content .more-link {
white-space: nowrap;
}
/* 列表 */
.entry-content ol,
.comment-content ol,
.entry-content ul,
@ -894,6 +987,7 @@ article.sticky .featured-post {
line-height: 1.714285714;
}
/* 嵌套列表 */
.entry-content ul ul,
.comment-content ul ul,
.entry-content ol ol,
@ -905,17 +999,20 @@ article.sticky .featured-post {
margin-bottom: 0;
}
/* 无序列表 */
.entry-content ul,
.comment-content ul,
.mu_register ul {
list-style: disc outside;
}
/* 有序列表 */
.entry-content ol,
.comment-content ol {
list-style: decimal outside;
}
/* 列表项 */
.entry-content li,
.comment-content li,
.mu_register li {
@ -923,6 +1020,7 @@ article.sticky .featured-post {
margin: 0 0 0 2.571428571rem;
}
/* 引用 */
.entry-content blockquote,
.comment-content blockquote {
margin-bottom: 24px;
@ -932,11 +1030,13 @@ article.sticky .featured-post {
font-style: italic;
}
/* 引用最后一段 */
.entry-content blockquote p:last-child,
.comment-content blockquote p:last-child {
margin-bottom: 0;
}
/* 代码 */
.entry-content code,
.comment-content code {
font-family: Consolas, Monaco, Lucida Console, monospace;
@ -945,6 +1045,7 @@ article.sticky .featured-post {
line-height: 2;
}
/* 代码块 */
.entry-content pre,
.comment-content pre {
border: 1px solid #ededed;
@ -960,11 +1061,13 @@ article.sticky .featured-post {
padding: 1.714285714rem;
}
/* 代码块中的代码 */
.entry-content pre code,
.comment-content pre code {
display: block;
}
/* 缩写、定义和首字母缩略词 */
.entry-content abbr,
.comment-content abbr,
.entry-content dfn,
@ -975,6 +1078,7 @@ article.sticky .featured-post {
cursor: help;
}
/* 地址 */
.entry-content address,
.comment-content address {
display: block;
@ -983,27 +1087,27 @@ article.sticky .featured-post {
margin: 0 0 1.714285714rem;
}
/* 左对齐图片和说明 */
img.alignleft,
.wp-caption.alignleft {
margin: 12px 24px 12px 0;
margin: 0.857142857rem 1.714285714rem 0.857142857rem 0;
}
/* 右对齐图片和说明 */
img.alignright,
.wp-caption.alignright {
margin: 12px 0 12px 24px;
margin: 0.857142857rem 0 0.857142857rem 1.714285714rem;
}
/* 居中图片和说明 */
img.aligncenter,
.wp-caption.aligncenter {
clear: both;
margin-top: 12px;
margin-top: 0.857142857rem;
margin-bottom: 12px;
margin-bottom: 0.857142857rem;
}
/* 嵌入内容 */
.entry-content embed,
.entry-content iframe,
.entry-content object,
@ -1012,18 +1116,21 @@ img.aligncenter,
margin-bottom: 1.714285714rem;
}
/* 定义列表 */
.entry-content dl,
.comment-content dl {
margin: 0 24px;
margin: 0 1.714285714rem;
}
/* 定义列表标题 */
.entry-content dt,
.comment-content dt {
font-weight: bold;
line-height: 1.714285714;
}
/* 定义列表描述 */
.entry-content dd,
.comment-content dd {
line-height: 1.714285714;
@ -1031,6 +1138,7 @@ img.aligncenter,
margin-bottom: 1.714285714rem;
}
/* 表格 */
.entry-content table,
.comment-content table {
border-bottom: 1px solid #ededed;
@ -1043,6 +1151,7 @@ img.aligncenter,
width: 100%;
}
/* 表格标题 */
.entry-content table caption,
.comment-content table caption {
font-size: 16px;
@ -1051,12 +1160,14 @@ img.aligncenter,
margin: 1.714285714rem 0;
}
/* 表格单元格 */
.entry-content td,
.comment-content td {
border-top: 1px solid #ededed;
padding: 6px 10px 6px 0;
}
/* 文章 */
.site-content article {
border-bottom: 4px double #ededed;
margin-bottom: 72px;
@ -1069,11 +1180,13 @@ img.aligncenter,
hyphens: auto;
}
/* 分页链接 */
.page-links {
clear: both;
line-height: 1.714285714;
}
/* 文章元数据 */
footer.entry-meta {
margin-top: 24px;
margin-top: 1.714285714rem;
@ -1083,10 +1196,12 @@ footer.entry-meta {
color: #757575;
}
/* 单个作者的文章元数据 */
.single-author .entry-meta .by-author {
display: none;
}
/* 注册表单标题 */
.mu_register h2 {
color: #757575;
font-weight: normal;
@ -1096,6 +1211,7 @@ footer.entry-meta {
/* =Archives
-------------------------------------------------------------- */
/* 归档和页面标题 */
.archive-header,
.page-header {
margin-bottom: 48px;
@ -1105,7 +1221,10 @@ footer.entry-meta {
border-bottom: 1px solid #ededed;
}
/* 归档元数据 */
.archive-meta {
}
color: #757575;
font-size: 12px;
font-size: 0.857142857rem;

@ -1,40 +1,62 @@
/**
* 博客前端交互功能
* Created by liangliang on 2016/11/20.
*/
/**
* 回复评论功能
* @param {string} parentid - 父评论ID
*/
function do_reply(parentid) {
console.log(parentid);
// 设置父评论ID
$("#id_parent_comment_id").val(parentid)
// 将评论表单移动到对应评论下
$("#commentform").appendTo($("#div-comment-" + parentid));
// 隐藏回复标题,显示取消回复按钮
$("#reply-title").hide();
$("#cancel_comment").show();
}
/**
* 取消回复功能
*/
function cancel_reply() {
// 显示回复标题,隐藏取消回复按钮
$("#reply-title").show();
$("#cancel_comment").hide();
// 清空父评论ID
$("#id_parent_comment_id").val('')
// 将评论表单移回原位置
$("#commentform").appendTo($("#respond"));
}
// 页面加载进度条控制
NProgress.start();
NProgress.set(0.4);
//Increment
// 增量更新进度条
var interval = setInterval(function () {
NProgress.inc();
}, 1000);
// 文档加载完成后停止进度条
$(document).ready(function () {
NProgress.done();
clearInterval(interval);
});
/** 侧边栏回到顶部 */
/** 侧边栏回到顶部功能 */
var rocket = $('#rocket');
// 监听窗口滚动事件,防抖处理
$(window).on('scroll', debounce(slideTopSet, 300));
/**
* 防抖函数
* @param {Function} func - 需要防抖的函数
* @param {number} wait - 等待时间毫秒
* @returns {Function} 防抖后的函数
*/
function debounce(func, wait) {
var timeout;
return function () {
@ -43,45 +65,54 @@ function debounce(func, wait) {
};
}
/**
* 设置回到顶部按钮显示状态
*/
function slideTopSet() {
var top = $(document).scrollTop();
if (top > 200) {
rocket.addClass('show');
rocket.addClass('show'); // 滚动超过200px时显示回到顶部按钮
} else {
rocket.removeClass('show');
rocket.removeClass('show'); // 否则隐藏
}
}
// 点击回到顶部按钮事件
$(document).on('click', '#rocket', function (event) {
rocket.addClass('move');
rocket.addClass('move'); // 添加动画类
// 页面平滑滚动到顶部
$('body, html').animate({
scrollTop: 0
}, 800);
});
// 动画结束事件处理
$(document).on('animationEnd', function () {
setTimeout(function () {
rocket.removeClass('move');
rocket.removeClass('move'); // 移除动画类
}, 400);
});
// Webkit内核浏览器动画结束事件处理
$(document).on('webkitAnimationEnd', function () {
setTimeout(function () {
rocket.removeClass('move');
rocket.removeClass('move'); // 移除动画类
}, 400);
});
// 页面加载完成后初始化评论回复功能
window.onload = function () {
var replyLinks = document.querySelectorAll(".comment-reply-link");
for (var i = 0; i < replyLinks.length; i++) {
replyLinks[i].onclick = function () {
var pk = this.getAttribute("data-pk");
do_reply(pk);
do_reply(pk); // 点击回复链接时调用回复功能
};
}
};
// 国际化语言切换功能(已注释)
// $(document).ready(function () {
// var form = $('#i18n-form');
// var selector = $('.i18n-select');

@ -1,6 +1,5 @@
/**
* Handles toggling the navigation menu for small screens and
* accessibility for submenu items.
* 处理小屏幕上的导航菜单切换和子菜单项的可访问性
*/
( function() {
var nav = document.getElementById( 'site-navigation' ), button, menu;
@ -14,17 +13,19 @@
return;
}
// Hide button if menu is missing or empty.
// 如果菜单不存在或为空则隐藏按钮
if ( ! menu || ! menu.childNodes.length ) {
button.style.display = 'none';
return;
}
// 按钮点击事件处理
button.onclick = function() {
if ( -1 === menu.className.indexOf( 'nav-menu' ) ) {
menu.className = 'nav-menu';
}
// 切换菜单展开/收起状态
if ( -1 !== button.className.indexOf( 'toggled-on' ) ) {
button.className = button.className.replace( ' toggled-on', '' );
menu.className = menu.className.replace( ' toggled-on', '' );
@ -35,12 +36,14 @@
};
} )();
// Better focus for hidden submenu items for accessibility.
// 为隐藏的子菜单项提供更好的焦点支持以提高可访问性
( function( $ ) {
// 为导航菜单中的链接添加焦点和失焦事件
$( '.main-navigation' ).find( 'a' ).on( 'focus.twentytwelve blur.twentytwelve', function() {
$( this ).parents( '.menu-item, .page_item' ).toggleClass( 'focus' );
} );
// 移动端触摸事件处理
if ( 'ontouchstart' in window ) {
$('body').on( 'touchstart.twentytwelve', '.menu-item-has-children > a, .page_item_has_children > a', function( e ) {
var el = $( this ).parent( 'li' );
@ -52,4 +55,4 @@
}
} );
}
} )( jQuery );
} )( jQuery );

@ -1,12 +1,17 @@
$(function () {
// 配置MathJax数学公式渲染器
MathJax.Hub.Config({
showProcessingMessages: false, //关闭js加载过程信息
messageStyle: "none", //不显示信息
extensions: ["tex2jax.js"], jax: ["input/TeX", "output/HTML-CSS"], displayAlign: "left", tex2jax: {
extensions: ["tex2jax.js"],
jax: ["input/TeX", "output/HTML-CSS"],
displayAlign: "left",
tex2jax: {
inlineMath: [["$", "$"]], //行内公式选择$
displayMath: [["$$", "$$"]], //段内公式选择$$
skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code', 'a'], //避开某些标签
}, "HTML-CSS": {
},
"HTML-CSS": {
availableFonts: ["STIX", "TeX"], //可选字体
showMathMenu: false //关闭右击菜单显示
}
@ -14,8 +19,6 @@ $(function () {
// 识别范围 => 文章内容、评论内容标签
const contentId = document.getElementById("content");
const commentId = document.getElementById("comments");
// 将文章内容和评论内容添加到MathJax渲染队列中
MathJax.Hub.Queue(["Typeset", MathJax.Hub, contentId, commentId]);
})

File diff suppressed because it is too large Load Diff

@ -11,6 +11,8 @@
*/
/*! prefixes.scss v0.1.0 | Author: Pandao | https://github.com/pandao/prefixes.scss | MIT license | Copyright (c) 2015 */
/* 定义editormd-logo字体 */
/* Define editormd-logo font */
@font-face {
font-family: 'editormd-logo';
src: url("../fonts/editormd-logo.eot?-5y8q6h");
@ -18,6 +20,9 @@
font-weight: normal;
font-style: normal;
}
/* editormd-logo样式类 */
/* editormd-logo style classes */
.editormd-logo,
.editormd-logo-1x,
.editormd-logo-2x,
@ -41,6 +46,9 @@
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* editormd-logo伪元素内容 */
/* editormd-logo pseudo-element content */
.editormd-logo:before,
.editormd-logo-1x:before,
.editormd-logo-2x:before,
@ -57,6 +65,8 @@
*/
}
/* 不同尺寸的editormd-logo */
/* Different sizes of editormd-logo */
.editormd-logo-1x {
font-size: 1em;
}
@ -93,6 +103,8 @@
font-size: 8em;
}
/* 彩色editormd-logo */
/* Colored editormd-logo */
.editormd-logo-color {
color: #2196F3;
}
}

@ -12,12 +12,13 @@
@charset "UTF-8";
/*! prefixes.scss v0.1.0 | Author: Pandao | https://github.com/pandao/prefixes.scss | MIT license | Copyright (c) 2015 */
/*!
* Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome
/*! Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/
/* FONT PATH
* -------------------------- */
/* 字体路径定义 */
/* Font path definition */
@font-face {
font-family: 'FontAwesome';
src: url("../fonts/fontawesome-webfont.eot?v=4.3.0");
@ -25,6 +26,9 @@
font-weight: normal;
font-style: normal;
}
/* FontAwesome基础样式 */
/* FontAwesome base styles */
.fa {
display: inline-block;
font: normal normal normal 14px/1 FontAwesome;
@ -35,7 +39,8 @@
transform: translate(0, 0);
}
/* makes the font 33% larger relative to the icon container */
/* 使字体相对图标容器大33% */
/* Makes the font 33% larger relative to the icon container */
.fa-lg {
font-size: 1.33333333em;
line-height: 0.75em;
@ -209,6 +214,7 @@
color: #ffffff;
}
/* Font Awesome使用Unicode私用区(PUA)确保屏幕阅读器不会读出随机字符 */
/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
readers do not read off random characters that represent icons */
.fa-glass:before {
@ -2362,6 +2368,8 @@
}
/*! prefixes.scss v0.1.0 | Author: Pandao | https://github.com/pandao/prefixes.scss | MIT license | Copyright (c) 2015 */
/* 定义editormd-logo字体 */
/* Define editormd-logo font */
@font-face {
font-family: 'editormd-logo';
src: url("../fonts/editormd-logo.eot?-5y8q6h");
@ -2369,6 +2377,9 @@
font-weight: normal;
font-style: normal;
}
/* editormd-logo样式类 */
/* editormd-logo style classes */
.editormd-logo,
.editormd-logo-1x,
.editormd-logo-2x,
@ -2392,6 +2403,7 @@
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.editormd-logo:before,
.editormd-logo-1x:before,
.editormd-logo-2x:before,
@ -2449,10 +2461,15 @@
}
/*! github-markdown-css | The MIT License (MIT) | Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com) | https://github.com/sindresorhus/github-markdown-css */
/* 定义octicons-anchor字体 */
/* Define octicons-anchor font */
@font-face {
font-family: octicons-anchor;
src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAYcAA0AAAAACjQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABMAAAABwAAAAca8vGTk9TLzIAAAFMAAAARAAAAFZG1VHVY21hcAAAAZAAAAA+AAABQgAP9AdjdnQgAAAB0AAAAAQAAAAEACICiGdhc3AAAAHUAAAACAAAAAj//wADZ2x5ZgAAAdwAAADRAAABEKyikaNoZWFkAAACsAAAAC0AAAA2AtXoA2hoZWEAAALgAAAAHAAAACQHngNFaG10eAAAAvwAAAAQAAAAEAwAACJsb2NhAAADDAAAAAoAAAAKALIAVG1heHAAAAMYAAAAHwAAACABEAB2bmFtZQAAAzgAAALBAAAFu3I9x/Nwb3N0AAAF/AAAAB0AAAAvaoFvbwAAAAEAAAAAzBdyYwAAAADP2IQvAAAAAM/bz7t4nGNgZGFgnMDAysDB1Ml0hoGBoR9CM75mMGLkYGBgYmBlZsAKAtJcUxgcPsR8iGF2+O/AEMPsznAYKMwIkgMA5REMOXicY2BgYGaAYBkGRgYQsAHyGMF8FgYFIM0ChED+h5j//yEk/3KoSgZGNgYYk4GRCUgwMaACRoZhDwCs7QgGAAAAIgKIAAAAAf//AAJ4nHWMMQrCQBBF/0zWrCCIKUQsTDCL2EXMohYGSSmorScInsRGL2DOYJe0Ntp7BK+gJ1BxF1stZvjz/v8DRghQzEc4kIgKwiAppcA9LtzKLSkdNhKFY3HF4lK69ExKslx7Xa+vPRVS43G98vG1DnkDMIBUgFN0MDXflU8tbaZOUkXUH0+U27RoRpOIyCKjbMCVejwypzJJG4jIwb43rfl6wbwanocrJm9XFYfskuVC5K/TPyczNU7b84CXcbxks1Un6H6tLH9vf2LRnn8Ax7A5WQAAAHicY2BkYGAA4teL1+yI57f5ysDNwgAC529f0kOmWRiYVgEpDgYmEA8AUzEKsQAAAHicY2BkYGB2+O/AEMPCAAJAkpEBFbAAADgKAe0EAAAiAAAAAAQAAAAEAAAAAAAAKgAqACoAiAAAeJxjYGRgYGBhsGFgYgABEMkFhAwM/xn0QAIAD6YBhwB4nI1Ty07cMBS9QwKlQapQW3VXySvEqDCZGbGaHULiIQ1FKgjWMxknMfLEke2A+IJu+wntrt/QbVf9gG75jK577Lg8K1qQPCfnnnt8fX1NRC/pmjrk/zprC+8D7tBy9DHgBXoWfQ44Av8t4Bj4Z8CLtBL9CniJluPXASf0Lm4CXqFX8Q84dOLnMB17N4c7tBo1AS/Qi+hTwBH4rwHHwN8DXqQ30XXAS7QaLwSc0Gn8NuAVWou/gFmnjLrEaEh9GmDdDGgL3B4JsrRPDU2hTOiMSuJUIdKQQayiAth69r6akSSFqIJuA19TrzCIaY8sIoxyrNIrL//pw7A2iMygkX5vDj+G+kuoLdX4GlGK/8Lnlz6/h9MpmoO9rafrz7ILXEHHaAx95s9lsI7AHNMBWEZHULnfAXwG9/ZqdzLI08iuwRloXE8kfhXYAvE23+23DU3t626rbs8/8adv+9DWknsHp3E17oCf+Z48rvEQNZ78paYM38qfk3v/u3l3u3GXN2Dmvmvpf1Srwk3pB/VSsp512bA/GG5i2WJ7wu430yQ5K3nFGiOqgtmSB5pJVSizwaacmUZzZhXLlZTq8qGGFY2YcSkqbth6aW1tRmlaCFs2016m5qn36SbJrqosG4uMV4aP2PHBmB3tjtmgN2izkGQyLWprekbIntJFing32a5rKWCN/SdSoga45EJykyQ7asZvHQ8PTm6cslIpwyeyjbVltNikc2HTR7YKh9LBl9DADC0U/jLcBZDKrMhUBfQBvXRzLtFtjU9eNHKin0x5InTqb8lNpfKv1s1xHzTXRqgKzek/mb7nB8RZTCDhGEX3kK/8Q75AmUM/eLkfA+0Hi908Kx4eNsMgudg5GLdRD7a84npi+YxNr5i5KIbW5izXas7cHXIMAau1OueZhfj+cOcP3P8MNIWLyYOBuxL6DRylJ4cAAAB4nGNgYoAALjDJyIAOWMCiTIxMLDmZedkABtIBygAAAA==) format("woff");
}
/* Markdown主体样式 */
/* Markdown body styles */
.markdown-body {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
@ -3110,6 +3127,8 @@
border-color: #4183c4;
}
/* Editor.md预览容器样式 */
/* Editor.md preview container styles */
.editormd-preview-container, .editormd-html-preview {
text-align: left;
font-size: 14px;
@ -3119,6 +3138,7 @@
width: 100%;
background-color: #fff;
}
.editormd-preview-container blockquote, .editormd-html-preview blockquote {
color: #666;
border-left: 4px solid #ddd;
@ -3127,19 +3147,23 @@
font-size: 14px;
font-style: italic;
}
.editormd-preview-container p code, .editormd-html-preview p code {
margin-left: 5px;
margin-right: 4px;
}
.editormd-preview-container abbr, .editormd-html-preview abbr {
background: #ffffdd;
}
.editormd-preview-container hr, .editormd-html-preview hr {
height: 1px;
border: none;
border-top: 1px solid #ddd;
background: none;
}
.editormd-preview-container code, .editormd-html-preview code {
border: 1px solid #ddd;
background: #f6f6f6;
@ -3147,6 +3171,7 @@
border-radius: 3px;
font-size: 14px;
}
.editormd-preview-container pre, .editormd-html-preview pre {
border: 1px solid #ddd;
background: #f6f6f6;
@ -3157,35 +3182,45 @@
-o-border-radius: 3px;
border-radius: 3px;
}
.editormd-preview-container pre code, .editormd-html-preview pre code {
padding: 0;
}
.editormd-preview-container pre, .editormd-preview-container code, .editormd-preview-container kbd, .editormd-html-preview pre, .editormd-html-preview code, .editormd-html-preview kbd {
font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace;
}
.editormd-preview-container table thead tr, .editormd-html-preview table thead tr {
background-color: #F8F8F8;
}
.editormd-preview-container p.editormd-tex, .editormd-html-preview p.editormd-tex {
text-align: center;
}
.editormd-preview-container span.editormd-tex, .editormd-html-preview span.editormd-tex {
margin: 0 5px;
}
.editormd-preview-container .emoji, .editormd-html-preview .emoji {
width: 24px;
height: 24px;
}
.editormd-preview-container .katex, .editormd-html-preview .katex {
font-size: 1.4em;
}
.editormd-preview-container .sequence-diagram, .editormd-preview-container .flowchart, .editormd-html-preview .sequence-diagram, .editormd-html-preview .flowchart {
margin: 0 auto;
text-align: center;
}
.editormd-preview-container .sequence-diagram svg, .editormd-preview-container .flowchart svg, .editormd-html-preview .sequence-diagram svg, .editormd-html-preview .flowchart svg {
margin: 0 auto;
}
.editormd-preview-container .sequence-diagram text, .editormd-preview-container .flowchart text, .editormd-html-preview .sequence-diagram text, .editormd-html-preview .flowchart text {
font-size: 15px !important;
font-family: "YaHei Consolas Hybrid", Consolas, "Microsoft YaHei", "Malgun Gothic", "Segoe UI", Helvetica, Arial !important;
@ -3193,38 +3228,47 @@
/*! Pretty printing styles. Used with prettify.js. */
/* SPAN elements with the classes below are added by prettyprint. */
/* 使用prettify.js的美化样式 */
/* 带有以下类的SPAN元素由prettyprint添加 */
.pln {
color: #000;
}
/* plain text */
/* 纯文本 */
@media screen {
.str {
color: #080;
}
/* string content */
/* 字符串内容 */
.kwd {
color: #008;
}
/* a keyword */
/* 关键字 */
.com {
color: #800;
}
/* a comment */
/* 注释 */
.typ {
color: #606;
}
/* a type name */
/* 类型名称 */
.lit {
color: #066;
}
/* a literal value */
/* 字面值 */
/* punctuation, lisp open bracket, lisp close bracket */
/* 标点符号Lisp开括号Lisp闭括号 */
.pun, .opn, .clo {
color: #660;
}
@ -3234,28 +3278,34 @@
}
/* a markup tag name */
/* 标记标签名称 */
.atn {
color: #606;
}
/* a markup attribute name */
/* 标记属性名称 */
.atv {
color: #080;
}
/* a markup attribute value */
/* 标记属性值 */
.dec, .var {
color: #606;
}
/* a declaration; a variable name */
/* 声明;变量名 */
.fun {
color: red;
}
/* a function name */
/* 函数名 */
}
/* Use higher contrast and text-weight for printable form. */
/* 为打印形式使用更高对比度和文字粗细 */
@media print, projection {
.str {
color: #060;
@ -3298,18 +3348,21 @@
}
}
/* Put a border around prettyprinted code snippets. */
/* 在美化代码片段周围添加边框 */
pre.prettyprint {
padding: 2px;
border: 1px solid #888;
}
/* Specify class=linenums on a pre to get line numbering */
/* 在pre元素上指定class=linenums以获取行号 */
ol.linenums {
margin-top: 0;
margin-bottom: 0;
}
/* IE indents via margin-left */
/* IE通过margin-left缩进 */
li.L0,
li.L1,
li.L2,
@ -3322,6 +3375,7 @@ li.L8 {
}
/* Alternate shading for lines */
/* 行的交替着色 */
li.L1,
li.L3,
li.L5,
@ -3336,13 +3390,16 @@ li.L9 {
white-space: pre-wrap;
word-wrap: break-word;
}
.editormd-preview-container ol.linenums, .editormd-html-preview ol.linenums {
color: #999;
padding-left: 2.5em;
}
.editormd-preview-container ol.linenums li, .editormd-html-preview ol.linenums li {
list-style-type: decimal;
}
.editormd-preview-container ol.linenums li code, .editormd-html-preview ol.linenums li code {
border: none;
background: none;
@ -3353,6 +3410,7 @@ li.L9 {
margin: 8px 0 12px 0;
display: inline-block;
}
.editormd-preview-container .editormd-toc-menu > .markdown-toc, .editormd-html-preview .editormd-toc-menu > .markdown-toc {
position: relative;
-webkit-border-radius: 4px;
@ -3364,6 +3422,7 @@ li.L9 {
display: inline-block;
font-size: 1em;
}
.editormd-preview-container .editormd-toc-menu > .markdown-toc > ul, .editormd-html-preview .editormd-toc-menu > .markdown-toc > ul {
width: 160%;
min-width: 180px;
@ -3391,6 +3450,7 @@ li.L9 {
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.2);
/* IE9+, News */
}
.editormd-preview-container .editormd-toc-menu > .markdown-toc > ul > li ul, .editormd-html-preview .editormd-toc-menu > .markdown-toc > ul > li ul {
width: 100%;
min-width: 180px;
@ -3403,6 +3463,7 @@ li.L9 {
-o-border-radius: 4px;
border-radius: 4px;
}
.editormd-preview-container .editormd-toc-menu > .markdown-toc > ul > li a, .editormd-html-preview .editormd-toc-menu > .markdown-toc > ul > li a {
color: #666;
padding: 6px 10px;
@ -3414,12 +3475,15 @@ li.L9 {
transition: background-color 500ms ease-out;
/* IE >9, FF >15, Opera >12.0 */
}
.editormd-preview-container .editormd-toc-menu > .markdown-toc > ul > li a:hover, .editormd-html-preview .editormd-toc-menu > .markdown-toc > ul > li a:hover {
background-color: #f6f6f6;
}
.editormd-preview-container .editormd-toc-menu > .markdown-toc li, .editormd-html-preview .editormd-toc-menu > .markdown-toc li {
position: relative;
}
.editormd-preview-container .editormd-toc-menu > .markdown-toc li > ul, .editormd-html-preview .editormd-toc-menu > .markdown-toc li > ul {
position: absolute;
top: 32px;
@ -3436,6 +3500,7 @@ li.L9 {
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.2);
/* IE9+, News */
}
.editormd-preview-container .editormd-toc-menu > .markdown-toc li > ul:before, .editormd-preview-container .editormd-toc-menu > .markdown-toc li > ul:after, .editormd-html-preview .editormd-toc-menu > .markdown-toc li > ul:before, .editormd-html-preview .editormd-toc-menu > .markdown-toc li > ul:after {
pointer-events: pointer-events;
position: absolute;
@ -3449,28 +3514,35 @@ li.L9 {
border-width: 0 6px 6px;
z-index: 10;
}
.editormd-preview-container .editormd-toc-menu > .markdown-toc li > ul:before, .editormd-html-preview .editormd-toc-menu > .markdown-toc li > ul:before {
border-bottom-color: #ccc;
}
.editormd-preview-container .editormd-toc-menu > .markdown-toc li > ul:after, .editormd-html-preview .editormd-toc-menu > .markdown-toc li > ul:after {
border-bottom-color: #ffffff;
top: -5px;
}
.editormd-preview-container .editormd-toc-menu ul, .editormd-html-preview .editormd-toc-menu ul {
list-style: none;
}
.editormd-preview-container .editormd-toc-menu a, .editormd-html-preview .editormd-toc-menu a {
text-decoration: none;
}
.editormd-preview-container .editormd-toc-menu h1, .editormd-html-preview .editormd-toc-menu h1 {
font-size: 16px;
padding: 5px 0 10px 10px;
line-height: 1;
border-bottom: 1px solid #eee;
}
.editormd-preview-container .editormd-toc-menu h1 .fa, .editormd-html-preview .editormd-toc-menu h1 .fa {
padding-left: 10px;
}
.editormd-preview-container .editormd-toc-menu .toc-menu-btn, .editormd-html-preview .editormd-toc-menu .toc-menu-btn {
color: #666;
min-width: 180px;
@ -3484,9 +3556,11 @@ li.L9 {
transition: background-color 500ms ease-out;
/* IE >9, FF >15, Opera >12.0 */
}
.editormd-preview-container .editormd-toc-menu .toc-menu-btn:hover, .editormd-html-preview .editormd-toc-menu .toc-menu-btn:hover {
background-color: #f6f6f6;
}
.editormd-preview-container .editormd-toc-menu .toc-menu-btn .fa, .editormd-html-preview .editormd-toc-menu .toc-menu-btn .fa {
float: right;
padding: 3px 0 0 10px;
@ -3496,6 +3570,7 @@ li.L9 {
.markdown-body .editormd-toc-menu ul {
padding-left: 0;
}
.markdown-body .highlight pre, .markdown-body pre {
line-height: 1.6;
}
@ -3513,9 +3588,11 @@ hr.editormd-page-break {
height: 0;
}
}
.editormd-html-preview textarea {
display: none;
}
.editormd-html-preview hr.editormd-page-break {
background: none;
border: none;
@ -3544,6 +3621,7 @@ hr.editormd-page-break {
transition: background-color 300ms ease-out;
/* IE >9, FF >15, Opera >12.0 */
}
.editormd-preview-close-btn:hover {
background-color: #999;
}
@ -3551,4 +3629,4 @@ hr.editormd-page-break {
.editormd-preview-active {
width: 100%;
padding: 40px;
}
}

@ -1,35 +1,44 @@
/* 全局重置样式 */
* {
padding: 0;
margin: 0;
}
/* 盒模型设置为border-box */
*, *:before, *:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td,hr,button,article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{
/* 重置常见元素的外边距和内边距 */
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td,hr,button,article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{
margin: 0;
padding: 0;
}
/* HTML5元素显示设置 */
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary {
display: block;
}
/* 音频、画布、视频元素显示设置 */
audio, canvas, video {
display: inline-block;
}
/* 图片样式 */
img {
border: none;
vertical-align: middle;
}
/* 列表样式 */
ul, ol {
/*list-style: none;*/
}
/* 清除浮动 */
.clear {
*zoom: 1; /* for IE 6/7 */
}
@ -47,6 +56,7 @@ ul, ol {
clear: both;
}
/* 页面主体样式 */
body {
font-size: 14px;
color: #666;
@ -55,29 +65,35 @@ body {
text-align: center;
}
/* 布局容器 */
#layout {
text-align: left;
}
/* 页面头部和按钮区域 */
#layout > header, .btns {
padding: 15px 0;
width: 90%;
margin: 0 auto;
}
/* 按钮区域 */
.btns {
padding-top: 0;
}
/* 按钮 */
.btns button {
padding: 2px 8px;
}
/* 页面标题 */
#layout > header > h1 {
font-size: 20px;
margin-bottom: 10px;
}
/* 按钮样式 */
.btns button, .btn {
padding: 8px 10px;
background: #fff;
@ -89,6 +105,7 @@ body {
transition: background 300ms ease-out;
}
/* 按钮悬停效果 */
.btns button:hover, .btn:hover {
background: #f6f6f6;
}

@ -11,125 +11,150 @@
(function() {
// 工厂函数,用于定义代码块对话框插件
// Factory function to define code block dialog plugin
var factory = function (exports) {
var cmEditor;
var pluginName = "code-block-dialog";
// CodeMirror编辑器实例
// CodeMirror editor instance
var cmEditor;
// 插件名称
// Plugin name
var pluginName = "code-block-dialog";
// for CodeBlock dialog select
var codeLanguages = exports.codeLanguages = {
asp : ["ASP", "vbscript"],
actionscript : ["ActionScript(3.0)/Flash/Flex", "clike"],
bash : ["Bash/Bat", "shell"],
css : ["CSS", "css"],
c : ["C", "clike"],
cpp : ["C++", "clike"],
csharp : ["C#", "clike"],
coffeescript : ["CoffeeScript", "coffeescript"],
d : ["D", "d"],
dart : ["Dart", "dart"],
delphi : ["Delphi/Pascal", "pascal"],
erlang : ["Erlang", "erlang"],
go : ["Golang", "go"],
groovy : ["Groovy", "groovy"],
html : ["HTML", "text/html"],
java : ["Java", "clike"],
json : ["JSON", "text/json"],
javascript : ["Javascript", "javascript"],
lua : ["Lua", "lua"],
less : ["LESS", "css"],
markdown : ["Markdown", "gfm"],
"objective-c" : ["Objective-C", "clike"],
php : ["PHP", "php"],
perl : ["Perl", "perl"],
python : ["Python", "python"],
r : ["R", "r"],
rst : ["reStructedText", "rst"],
ruby : ["Ruby", "ruby"],
sql : ["SQL", "sql"],
sass : ["SASS/SCSS", "sass"],
shell : ["Shell", "shell"],
scala : ["Scala", "clike"],
swift : ["Swift", "clike"],
vb : ["VB/VBScript", "vb"],
xml : ["XML", "text/xml"],
yaml : ["YAML", "yaml"]
};
exports.fn.codeBlockDialog = function() {
var _this = this;
var cm = this.cm;
var lang = this.lang;
var editor = this.editor;
var settings = this.settings;
var cursor = cm.getCursor();
var selection = cm.getSelection();
var classPrefix = this.classPrefix;
var dialogName = classPrefix + pluginName, dialog;
var dialogLang = lang.dialog.codeBlock;
cm.focus();
// 代码语言映射表,用于代码块对话框的选择列表
// Code language mapping table for code block dialog selection list
var codeLanguages = exports.codeLanguages = {
asp : ["ASP", "vbscript"], // ASP语言
actionscript : ["ActionScript(3.0)/Flash/Flex", "clike"], // ActionScript语言
bash : ["Bash/Bat", "shell"], // Bash/Bat脚本
css : ["CSS", "css"], // CSS样式表
c : ["C", "clike"], // C语言
cpp : ["C++", "clike"], // C++语言
csharp : ["C#", "clike"], // C#语言
coffeescript : ["CoffeeScript", "coffeescript"], // CoffeeScript语言
d : ["D", "d"], // D语言
dart : ["Dart", "dart"], // Dart语言
delphi : ["Delphi/Pascal", "pascal"], // Delphi/Pascal语言
erlang : ["Erlang", "erlang"], // Erlang语言
go : ["Golang", "go"], // Go语言
groovy : ["Groovy", "groovy"], // Groovy语言
html : ["HTML", "text/html"], // HTML标记语言
java : ["Java", "clike"], // Java语言
json : ["JSON", "text/json"], // JSON数据格式
javascript : ["Javascript", "javascript"], // JavaScript语言
lua : ["Lua", "lua"], // Lua语言
less : ["LESS", "css"], // LESS样式表
markdown : ["Markdown", "gfm"], // Markdown标记语言
"objective-c" : ["Objective-C", "clike"], // Objective-C语言
php : ["PHP", "php"], // PHP语言
perl : ["Perl", "perl"], // Perl语言
python : ["Python", "python"], // Python语言
r : ["R", "r"], // R语言
rst : ["reStructedText", "rst"], // reStructedText标记语言
ruby : ["Ruby", "ruby"], // Ruby语言
sql : ["SQL", "sql"], // SQL数据库查询语言
sass : ["SASS/SCSS", "sass"], // SASS/SCSS样式表
shell : ["Shell", "shell"], // Shell脚本
scala : ["Scala", "clike"], // Scala语言
swift : ["Swift", "clike"], // Swift语言
vb : ["VB/VBScript", "vb"], // VB/VBScript语言
xml : ["XML", "text/xml"], // XML标记语言
yaml : ["YAML", "yaml"] // YAML数据序列化标准
};
// 代码块对话框插件主函数
// Main function of code block dialog plugin
exports.fn.codeBlockDialog = function() {
// 获取当前编辑器实例的各种属性和方法
// Get various properties and methods of the current editor instance
var _this = this;
var cm = this.cm; // CodeMirror实例
var lang = this.lang; // 语言包
var editor = this.editor; // 编辑器DOM元素
var settings = this.settings; // 设置选项
var cursor = cm.getCursor(); // 获取光标位置
var selection = cm.getSelection(); // 获取选中文本
var classPrefix = this.classPrefix; // CSS类前缀
var dialogName = classPrefix + pluginName, dialog; // 对话框名称
var dialogLang = lang.dialog.codeBlock; // 对话框语言包
// 设置焦点到CodeMirror编辑器
// Set focus to CodeMirror editor
cm.focus();
// 如果对话框已存在,则显示并重置内容
// If the dialog already exists, show it and reset the content
if (editor.find("." + dialogName).length > 0)
{
dialog = editor.find("." + dialogName);
dialog.find("option:first").attr("selected", "selected");
dialog.find("textarea").val(selection);
this.dialogShowMask(dialog);
this.dialogLockScreen();
dialog.show();
this.dialogShowMask(dialog); // 显示对话框遮罩
this.dialogLockScreen(); // 锁定屏幕
dialog.show(); // 显示对话框
}
else
{
// 构建对话框HTML内容
// Build dialog HTML content
var dialogHTML = "<div class=\"" + classPrefix + "code-toolbar\">" +
dialogLang.selectLabel + "<select><option selected=\"selected\" value=\"\">" + dialogLang.selectDefaultText + "</option></select>" +
"</div>" +
"<textarea placeholder=\"coding now....\" style=\"display:none;\">" + selection + "</textarea>";
// 创建对话框
// Create dialog
dialog = this.createDialog({
name : dialogName,
title : dialogLang.title,
width : 780,
height : 565,
mask : settings.dialogShowMask,
drag : settings.dialogDraggable,
content : dialogHTML,
lockScreen : settings.dialogLockScreen,
maskStyle : {
opacity : settings.dialogMaskOpacity,
backgroundColor : settings.dialogMaskBgColor
name : dialogName, // 对话框名称
title : dialogLang.title, // 对话框标题
width : 780, // 对话框宽度
height : 565, // 对话框高度
mask : settings.dialogShowMask, // 是否显示遮罩
drag : settings.dialogDraggable, // 是否可拖拽
content : dialogHTML, // 对话框内容
lockScreen : settings.dialogLockScreen, // 是否锁定屏幕
maskStyle : { // 遮罩样式
opacity : settings.dialogMaskOpacity, // 遮罩透明度
backgroundColor : settings.dialogMaskBgColor // 遮罩背景色
},
buttons : {
enter : [lang.buttons.enter, function() {
var codeTexts = this.find("textarea").val();
var langName = this.find("select").val();
buttons : { // 对话框按钮
enter : [lang.buttons.enter, function() { // 确定按钮
var codeTexts = this.find("textarea").val(); // 获取代码文本
var langName = this.find("select").val(); // 获取选择的语言
// 检查是否选择了语言
if (langName === "")
{
alert(lang.dialog.codeBlock.unselectedLanguageAlert);
return false;
}
// 检查代码文本是否为空
if (codeTexts === "")
{
alert(lang.dialog.codeBlock.codeEmptyAlert);
return false;
}
// 处理语言名称
langName = (langName === "other") ? "" : langName;
// 插入代码块到编辑器
cm.replaceSelection(["```" + langName, codeTexts, "```"].join("\n"));
// 如果未指定语言,设置光标位置
if (langName === "") {
cm.setCursor(cursor.line, cursor.ch + 3);
}
// 隐藏对话框并解锁屏幕
this.hide().lockScreen(false).hideMask();
return false;
}],
cancel : [lang.buttons.cancel, function() {
cancel : [lang.buttons.cancel, function() { // 取消按钮
this.hide().lockScreen(false).hideMask();
return false;
@ -138,100 +163,112 @@
});
}
var langSelect = dialog.find("select");
if (langSelect.find("option").length === 1)
{
for (var key in codeLanguages)
{
var codeLang = codeLanguages[key];
langSelect.append("<option value=\"" + key + "\" mode=\"" + codeLang[1] + "\">" + codeLang[0] + "</option>");
}
langSelect.append("<option value=\"other\">" + dialogLang.otherLanguage + "</option>");
}
var mode = langSelect.find("option:selected").attr("mode");
var cmConfig = {
mode : (mode) ? mode : "text/html",
theme : settings.theme,
tabSize : 4,
autofocus : true,
autoCloseTags : true,
indentUnit : 4,
lineNumbers : true,
lineWrapping : true,
extraKeys : {"Ctrl-Q": function(cm){ cm.foldCode(cm.getCursor()); }},
foldGutter : true,
gutters : ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
matchBrackets : true,
indentWithTabs : true,
styleActiveLine : true,
styleSelectedText : true,
autoCloseBrackets : true,
showTrailingSpace : true,
highlightSelectionMatches : true
};
var textarea = dialog.find("textarea");
var cmObj = dialog.find(".CodeMirror");
if (dialog.find(".CodeMirror").length < 1)
{
cmEditor = exports.$CodeMirror.fromTextArea(textarea[0], cmConfig);
cmObj = dialog.find(".CodeMirror");
cmObj.css({
"float" : "none",
margin : "8px 0",
border : "1px solid #ddd",
fontSize : settings.fontSize,
width : "100%",
height : "390px"
});
cmEditor.on("change", function(cm) {
textarea.val(cm.getValue());
});
}
else
{
cmEditor.setValue(cm.getSelection());
}
langSelect.change(function(){
var _mode = $(this).find("option:selected").attr("mode");
cmEditor.setOption("mode", _mode);
});
};
};
// 获取语言选择下拉框
// Get language selection dropdown
var langSelect = dialog.find("select");
// 如果下拉框选项为空,则添加语言选项
if (langSelect.find("option").length === 1)
{
// 遍历代码语言映射表,添加选项
for (var key in codeLanguages)
{
var codeLang = codeLanguages[key];
langSelect.append("<option value=\"" + key + "\" mode=\"" + codeLang[1] + "\">" + codeLang[0] + "</option>");
}
// 添加"其他语言"选项
langSelect.append("<option value=\"other\">" + dialogLang.otherLanguage + "</option>");
}
// 获取选中语言的模式
var mode = langSelect.find("option:selected").attr("mode");
// CodeMirror配置
var cmConfig = {
mode : (mode) ? mode : "text/html", // 编辑器模式
theme : settings.theme, // 主题
tabSize : 4, // Tab大小
autofocus : true, // 自动聚焦
autoCloseTags : true, // 自动关闭标签
indentUnit : 4, // 缩进单位
lineNumbers : true, // 显示行号
lineWrapping : true, // 自动换行
extraKeys : {"Ctrl-Q": function(cm){ cm.foldCode(cm.getCursor()); }}, // 额外快捷键
foldGutter : true, // 代码折叠
gutters : ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], // 代码槽
matchBrackets : true, // 匹配括号
indentWithTabs : true, // 使用Tab缩进
styleActiveLine : true, // 高亮活动行
styleSelectedText : true, // 高亮选中文本
autoCloseBrackets : true, // 自动关闭括号
showTrailingSpace : true, // 显示尾随空格
highlightSelectionMatches : true // 高亮匹配选中
};
// 获取文本域和CodeMirror对象
var textarea = dialog.find("textarea");
var cmObj = dialog.find(".CodeMirror");
// 如果CodeMirror对象不存在则创建
if (dialog.find(".CodeMirror").length < 1)
{
cmEditor = exports.$CodeMirror.fromTextArea(textarea[0], cmConfig);
cmObj = dialog.find(".CodeMirror");
// 设置CodeMirror样式
cmObj.css({
"float" : "none",
margin : "8px 0",
border : "1px solid #ddd",
fontSize : settings.fontSize,
width : "100%",
height : "390px"
});
// 监听代码变化事件
cmEditor.on("change", function(cm) {
textarea.val(cm.getValue()); // 同步代码到文本域
});
}
else
{
// 设置CodeMirror值为选中文本
cmEditor.setValue(cm.getSelection());
}
// 监听语言选择变化事件
langSelect.change(function(){
var _mode = $(this).find("option:selected").attr("mode");
cmEditor.setOption("mode", _mode); // 设置CodeMirror模式
});
};
};
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
{
module.exports = factory;
}
else if (typeof define === "function") // AMD/CMD/Sea.js
else if (typeof define === "function") // AMD/CMD/Sea.js
{
if (define.amd) { // for Require.js
if (define.amd) { // for Require.js
define(["editormd"], function(editormd) {
define(["editormd"], function(editormd) {
factory(editormd);
});
} else { // for Sea.js
define(function(require) {
} else { // for Sea.js
define(function(require) {
var editormd = require("../../editormd");
factory(editormd);
});
}
}
else
{
}
}
else
{
factory(window.editormd);
}
}
})();
})();

@ -11,186 +11,212 @@
(function() {
var factory = function (exports) {
var $ = jQuery;
var pluginName = "emoji-dialog";
var emojiTabIndex = 0;
var emojiData = [];
// 工厂函数用于定义Emoji对话框插件
// Factory function to define Emoji dialog plugin
var factory = function (exports) {
// jQuery对象
var $ = jQuery;
// 插件名称
var pluginName = "emoji-dialog";
// Emoji选项卡索引
var emojiTabIndex = 0;
// Emoji数据
var emojiData = [];
// 已选择的Emoji
var selecteds = [];
var logoPrefix = "editormd-logo";
var logos = [
logoPrefix,
logoPrefix + "-1x",
logoPrefix + "-2x",
logoPrefix + "-3x",
logoPrefix + "-4x",
logoPrefix + "-5x",
logoPrefix + "-6x",
logoPrefix + "-7x",
logoPrefix + "-8x"
];
var langs = {
"zh-cn" : {
toolbar : {
emoji : "Emoji 表情"
},
dialog : {
emoji : {
title : "Emoji 表情"
}
}
},
"zh-tw" : {
toolbar : {
emoji : "Emoji 表情"
},
dialog : {
emoji : {
title : "Emoji 表情"
}
}
},
"en" : {
toolbar : {
emoji : "Emoji"
},
dialog : {
emoji : {
title : "Emoji"
}
}
},
"de" : {
toolbar : {
emoji : "Emoji"
},
dialog : {
emoji : {
title : "Emoji"
}
}
}
};
exports.fn.emojiDialog = function() {
var _this = this;
var cm = this.cm;
var settings = _this.settings;
// Logo前缀
var logoPrefix = "editormd-logo";
// Logo列表
var logos = [
logoPrefix,
logoPrefix + "-1x",
logoPrefix + "-2x",
logoPrefix + "-3x",
logoPrefix + "-4x",
logoPrefix + "-5x",
logoPrefix + "-6x",
logoPrefix + "-7x",
logoPrefix + "-8x"
];
// 多语言支持
var langs = {
"zh-cn" : {
toolbar : {
emoji : "Emoji 表情"
},
dialog : {
emoji : {
title : "Emoji 表情"
}
}
},
"zh-tw" : {
toolbar : {
emoji : "Emoji 表情"
},
dialog : {
emoji : {
title : "Emoji 表情"
}
}
},
"en" : {
toolbar : {
emoji : "Emoji"
},
dialog : {
emoji : {
title : "Emoji"
}
}
},
"de" : {
toolbar : {
emoji : "Emoji"
},
dialog : {
emoji : {
title : "Emoji"
}
}
}
};
// Emoji对话框插件主函数
exports.fn.emojiDialog = function() {
// 获取当前编辑器实例的各种属性和方法
var _this = this;
var cm = this.cm; // CodeMirror实例
var settings = _this.settings; // 设置选项
// 检查是否启用Emoji功能
if (!settings.emoji)
{
alert("settings.emoji == false");
return ;
}
var path = settings.pluginPath + pluginName + "/";
var editor = this.editor;
var cursor = cm.getCursor();
var selection = cm.getSelection();
var classPrefix = this.classPrefix;
$.extend(true, this.lang, langs[this.lang.name]);
this.setToolbar();
var lang = this.lang;
var dialogName = classPrefix + pluginName, dialog;
var dialogLang = lang.dialog.emoji;
var dialogContent = [
"<div class=\"" + classPrefix + "emoji-dialog-box\" style=\"width: 760px;height: 334px;margin-bottom: 8px;overflow: hidden;\">",
"<div class=\"" + classPrefix + "tab\"></div>",
"</div>",
].join("\n");
cm.focus();
if (editor.find("." + dialogName).length > 0)
{
// 获取插件路径
var path = settings.pluginPath + pluginName + "/";
var editor = this.editor; // 编辑器DOM元素
var cursor = cm.getCursor(); // 获取光标位置
var selection = cm.getSelection(); // 获取选中文本
var classPrefix = this.classPrefix; // CSS类前缀
// 扩展语言包并设置工具栏
$.extend(true, this.lang, langs[this.lang.name]);
this.setToolbar();
// 获取语言包
var lang = this.lang;
var dialogName = classPrefix + pluginName, dialog; // 对话框名称
var dialogLang = lang.dialog.emoji; // Emoji对话框语言包
// 构建对话框内容
var dialogContent = [
"<div class=\"" + classPrefix + "emoji-dialog-box\" style=\"width: 760px;height: 334px;margin-bottom: 8px;overflow: hidden;\">",
"<div class=\"" + classPrefix + "tab\"></div>",
"</div>",
].join("\n");
// 设置焦点到CodeMirror编辑器
cm.focus();
// 如果对话框已存在,则显示
if (editor.find("." + dialogName).length > 0)
{
dialog = editor.find("." + dialogName);
selecteds = [];
dialog.find("a").removeClass("selected");
this.dialogShowMask(dialog);
this.dialogLockScreen();
dialog.show();
}
else
{
dialog = this.createDialog({
name : dialogName,
title : dialogLang.title,
width : 800,
height : 475,
mask : settings.dialogShowMask,
drag : settings.dialogDraggable,
content : dialogContent,
lockScreen : settings.dialogLockScreen,
maskStyle : {
opacity : settings.dialogMaskOpacity,
backgroundColor : settings.dialogMaskBgColor
},
buttons : {
enter : [lang.buttons.enter, function() {
cm.replaceSelection(selecteds.join(" "));
this.hide().lockScreen(false).hideMask();
return false;
}],
cancel : [lang.buttons.cancel, function() {
this.hide().lockScreen(false).hideMask();
return false;
}]
}
});
}
var category = ["Github emoji", "Twemoji", "Font awesome", "Editor.md logo"];
var tab = dialog.find("." + classPrefix + "tab");
if (tab.html() === "")
{
var head = "<ul class=\"" + classPrefix + "tab-head\">";
for (var i = 0; i<4; i++) {
var active = (i === 0) ? " class=\"active\"" : "";
head += "<li" + active + "><a href=\"javascript:;\">" + category[i] + "</a></li>";
}
head += "</ul>";
tab.append(head);
var container = "<div class=\"" + classPrefix + "tab-container\">";
for (var x = 0; x < 4; x++)
selecteds = [];
dialog.find("a").removeClass("selected");
this.dialogShowMask(dialog); // 显示对话框遮罩
this.dialogLockScreen(); // 锁定屏幕
dialog.show(); // 显示对话框
}
else
{
// 创建对话框
dialog = this.createDialog({
name : dialogName, // 对话框名称
title : dialogLang.title, // 对话框标题
width : 800, // 对话框宽度
height : 475, // 对话框高度
mask : settings.dialogShowMask, // 是否显示遮罩
drag : settings.dialogDraggable, // 是否可拖拽
content : dialogContent, // 对话框内容
lockScreen : settings.dialogLockScreen, // 是否锁定屏幕
maskStyle : { // 遮罩样式
opacity : settings.dialogMaskOpacity, // 遮罩透明度
backgroundColor : settings.dialogMaskBgColor // 遮罩背景色
},
buttons : { // 对话框按钮
enter : [lang.buttons.enter, function() { // 确定按钮
cm.replaceSelection(selecteds.join(" ")); // 插入选中的Emoji
this.hide().lockScreen(false).hideMask(); // 隐藏对话框并解锁屏幕
return false;
}],
cancel : [lang.buttons.cancel, function() { // 取消按钮
this.hide().lockScreen(false).hideMask(); // 隐藏对话框并解锁屏幕
return false;
}]
}
});
}
// Emoji分类
var category = ["Github emoji", "Twemoji", "Font awesome", "Editor.md logo"];
var tab = dialog.find("." + classPrefix + "tab");
// 如果选项卡内容为空,则创建选项卡
if (tab.html() === "")
{
var head = "<ul class=\"" + classPrefix + "tab-head\">";
for (var i = 0; i<4; i++) {
var active = (i === 0) ? " class=\"active\"" : "";
head += "<li" + active + "><a href=\"javascript:;\">" + category[i] + "</a></li>";
}
head += "</ul>";
tab.append(head);
var container = "<div class=\"" + classPrefix + "tab-container\">";
for (var x = 0; x < 4; x++)
{
var display = (x === 0) ? "" : "display:none;";
container += "<div class=\"" + classPrefix + "tab-box\" style=\"height: 260px;overflow: hidden;overflow-y: auto;" + display + "\"></div>";
}
var display = (x === 0) ? "" : "display:none;";
container += "<div class=\"" + classPrefix + "tab-box\" style=\"height: 260px;overflow: hidden;overflow-y: auto;" + display + "\"></div>";
}
container += "</div>";
container += "</div>";
tab.append(container);
}
tab.append(container);
}
var tabBoxs = tab.find("." + classPrefix + "tab-box");
// 获取选项卡容器
var tabBoxs = tab.find("." + classPrefix + "tab-box");
var emojiCategories = ["github-emoji", "twemoji", "font-awesome", logoPrefix];
var drawTable = function() {
// 绘制Emoji表格
var drawTable = function() {
var cname = emojiCategories[emojiTabIndex];
var $data = emojiData[cname];
var $data = emojiData[cname];
var $tab = tabBoxs.eq(emojiTabIndex);
if ($tab.html() !== "") {
// 如果选项卡内容不为空,则直接返回
if ($tab.html() !== "") {
//console.log("break =>", cname);
return ;
}
// 分页处理
var pagination = function(data, type) {
var rowNumber = (type === "editormd-logo") ? "5" : 20;
var pageTotal = Math.ceil(data.length / rowNumber);
@ -208,6 +234,7 @@
{
var img = "", icon = "";
// 处理Github Emoji
if (type === "github-emoji")
{
var src = (emoji === "+1") ? "plus1" : emoji;
@ -218,17 +245,20 @@
img = "<img src=\"" + src + "\" width=\"24\" class=\"emoji\" title=\"&#58;" + emoji + "&#58;\" alt=\"&#58;" + emoji + "&#58;\" />";
row += "<a href=\"javascript:;\" value=\":" + emoji + ":\" title=\":" + emoji + ":\" class=\"" + classPrefix + "emoji-btn\">" + img + "</a>";
}
// 处理Twemoji
else if (type === "twemoji")
{
var twemojiSrc = exports.twemoji.path + emoji + exports.twemoji.ext;
img = "<img src=\"" + twemojiSrc + "\" width=\"24\" title=\"twemoji-" + emoji + "\" alt=\"twemoji-" + emoji + "\" class=\"emoji twemoji\" />";
row += "<a href=\"javascript:;\" value=\":tw-" + emoji + ":\" title=\":tw-" + emoji + ":\" class=\"" + classPrefix + "emoji-btn\">" + img + "</a>";
}
// 处理Font Awesome图标
else if (type === "font-awesome")
{
icon = "<i class=\"fa fa-" + emoji + " fa-emoji\" title=\"" + emoji + "\"></i>";
row += "<a href=\"javascript:;\" value=\":fa-" + emoji + ":\" title=\":fa-" + emoji + ":\" class=\"" + classPrefix + "emoji-btn\">" + icon + "</a>";
}
// 处理Editor.md Logo
else if (type === "editormd-logo")
{
icon = "<i class=\"" + emoji + "\" title=\"Editor.md logo (" + emoji + ")\"></i>";
@ -251,6 +281,7 @@
return table;
};
// 根据选项卡索引绘制不同内容
if (emojiTabIndex === 0)
{
for (var i = 0, len = $data.length; i < len; i++)
@ -265,73 +296,76 @@
$tab.append(pagination($data, cname));
}
$tab.find("." + classPrefix + "emoji-btn").bind(exports.mouseOrTouch("click", "touchend"), function() {
$(this).toggleClass("selected");
if ($(this).hasClass("selected"))
{
selecteds.push($(this).attr("value"));
}
});
};
if (emojiData.length < 1)
{
if (typeof dialog.loading === "function") {
// 绑定Emoji按钮点击事件
$tab.find("." + classPrefix + "emoji-btn").bind(exports.mouseOrTouch("click", "touchend"), function() {
$(this).toggleClass("selected");
if ($(this).hasClass("selected"))
{
selecteds.push($(this).attr("value"));
}
});
};
// 如果Emoji数据为空则从JSON文件加载
if (emojiData.length < 1)
{
if (typeof dialog.loading === "function") {
dialog.loading(true);
}
$.getJSON(path + "emoji.json?temp=" + Math.random(), function(json) {
$.getJSON(path + "emoji.json?temp=" + Math.random(), function(json) {
if (typeof dialog.loading === "function") {
if (typeof dialog.loading === "function") {
dialog.loading(false);
}
emojiData = json;
emojiData = json;
emojiData[logoPrefix] = logos;
drawTable();
});
}
else
{
drawTable();
}
tab.find("li").bind(exports.mouseOrTouch("click", "touchend"), function() {
var $this = $(this);
emojiTabIndex = $this.index();
$this.addClass("active").siblings().removeClass("active");
tabBoxs.eq(emojiTabIndex).show().siblings().hide();
drawTable();
});
};
};
drawTable();
});
}
else
{
drawTable();
}
// 绑定选项卡点击事件
tab.find("li").bind(exports.mouseOrTouch("click", "touchend"), function() {
var $this = $(this);
emojiTabIndex = $this.index();
$this.addClass("active").siblings().removeClass("active");
tabBoxs.eq(emojiTabIndex).show().siblings().hide();
drawTable();
});
};
};
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
{
module.exports = factory;
}
else if (typeof define === "function") // AMD/CMD/Sea.js
else if (typeof define === "function") // AMD/CMD/Sea.js
{
if (define.amd) { // for Require.js
if (define.amd) { // for Require.js
define(["editormd"], function(editormd) {
define(["editormd"], function(editormd) {
factory(editormd);
});
} else { // for Sea.js
define(function(require) {
} else { // for Sea.js
define(function(require) {
var editormd = require("../../editormd");
factory(editormd);
});
}
}
else
{
}
}
else
{
factory(window.editormd);
}
}
})();
})();

@ -11,159 +11,177 @@
(function() {
var factory = function (exports) {
var $ = jQuery;
var pluginName = "goto-line-dialog";
var langs = {
"zh-cn" : {
toolbar : {
"goto-line" : "跳转到行"
},
dialog : {
"goto-line" : {
title : "跳转到行",
label : "请输入行号",
error : "错误:"
}
}
},
"zh-tw" : {
toolbar : {
"goto-line" : "跳轉到行"
},
dialog : {
"goto-line" : {
title : "跳轉到行",
label : "請輸入行號",
error : "錯誤:"
}
}
},
"en" : {
toolbar : {
"goto-line" : "Goto line"
},
dialog : {
"goto-line" : {
title : "Goto line",
label : "Enter a line number, range ",
error : "Error: "
}
}
},
"de" : {
toolbar : {
"goto-line" : "Gehe zu Zeile"
},
dialog : {
"goto-line" : {
title : "Gehe zu Zeile",
label : "Eine Zeile oder Zeilenbereich eingeben",
error : "Fehler: "
}
}
}
};
exports.fn.gotoLineDialog = function() {
var _this = this;
var cm = this.cm;
var editor = this.editor;
var settings = this.settings;
var path = settings.pluginPath + pluginName +"/";
var classPrefix = this.classPrefix;
var dialogName = classPrefix + pluginName, dialog;
$.extend(true, this.lang, langs[this.lang.name]);
this.setToolbar();
var lang = this.lang;
var dialogLang = lang.dialog["goto-line"];
var lineCount = cm.lineCount();
dialogLang.error += dialogLang.label + " 1-" + lineCount;
if (editor.find("." + dialogName).length < 1)
{
var dialogContent = [
"<div class=\"editormd-form\" style=\"padding: 10px 0;\">",
"<p style=\"margin: 0;\">" + dialogLang.label + " 1-" + lineCount +"&nbsp;&nbsp;&nbsp;<input type=\"number\" class=\"number-input\" style=\"width: 60px;\" value=\"1\" max=\"" + lineCount + "\" min=\"1\" data-line-number /></p>",
"</div>"
].join("\n");
dialog = this.createDialog({
name : dialogName,
title : dialogLang.title,
width : 400,
height : 180,
mask : settings.dialogShowMask,
drag : settings.dialogDraggable,
content : dialogContent,
lockScreen : settings.dialogLockScreen,
maskStyle : {
opacity : settings.dialogMaskOpacity,
backgroundColor : settings.dialogMaskBgColor
},
buttons : {
enter : [lang.buttons.enter, function() {
var line = parseInt(this.find("[data-line-number]").val());
if (line < 1 || line > lineCount) {
alert(dialogLang.error);
return false;
}
_this.gotoLine(line);
// 工厂函数,用于定义跳转到行对话框插件
// Factory function to define goto line dialog plugin
var factory = function (exports) {
// jQuery对象
var $ = jQuery;
// 插件名称
var pluginName = "goto-line-dialog";
// 多语言支持
var langs = {
"zh-cn" : {
toolbar : {
"goto-line" : "跳转到行"
},
dialog : {
"goto-line" : {
title : "跳转到行",
label : "请输入行号",
error : "错误:"
}
}
},
"zh-tw" : {
toolbar : {
"goto-line" : "跳轉到行"
},
dialog : {
"goto-line" : {
title : "跳轉到行",
label : "請輸入行號",
error : "錯誤:"
}
}
},
"en" : {
toolbar : {
"goto-line" : "Goto line"
},
dialog : {
"goto-line" : {
title : "Goto line",
label : "Enter a line number, range ",
error : "Error: "
}
}
},
"de" : {
toolbar : {
"goto-line" : "Gehe zu Zeile"
},
dialog : {
"goto-line" : {
title : "Gehe zu Zeile",
label : "Eine Zeile oder Zeilenbereich eingeben",
error : "Fehler: "
}
}
}
};
// 跳转到行对话框插件主函数
exports.fn.gotoLineDialog = function() {
// 获取当前编辑器实例的各种属性和方法
var _this = this;
var cm = this.cm; // CodeMirror实例
var editor = this.editor; // 编辑器DOM元素
var settings = this.settings; // 设置选项
var path = settings.pluginPath + pluginName +"/"; // 插件路径
var classPrefix = this.classPrefix; // CSS类前缀
var dialogName = classPrefix + pluginName, dialog; // 对话框名称
// 扩展语言包并设置工具栏
$.extend(true, this.lang, langs[this.lang.name]);
this.setToolbar();
// 获取语言包
var lang = this.lang;
var dialogLang = lang.dialog["goto-line"];
var lineCount = cm.lineCount(); // 获取总行数
// 构造错误信息
dialogLang.error += dialogLang.label + " 1-" + lineCount;
// 如果对话框不存在,则创建
if (editor.find("." + dialogName).length < 1)
{
// 构建对话框内容
var dialogContent = [
"<div class=\"editormd-form\" style=\"padding: 10px 0;\">",
"<p style=\"margin: 0;\">" + dialogLang.label + " 1-" + lineCount +"&nbsp;&nbsp;&nbsp;<input type=\"number\" class=\"number-input\" style=\"width: 60px;\" value=\"1\" max=\"" + lineCount + "\" min=\"1\" data-line-number /></p>",
"</div>"
].join("\n");
// 创建对话框
dialog = this.createDialog({
name : dialogName, // 对话框名称
title : dialogLang.title, // 对话框标题
width : 400, // 对话框宽度
height : 180, // 对话框高度
mask : settings.dialogShowMask, // 是否显示遮罩
drag : settings.dialogDraggable, // 是否可拖拽
content : dialogContent, // 对话框内容
lockScreen : settings.dialogLockScreen, // 是否锁定屏幕
maskStyle : { // 遮罩样式
opacity : settings.dialogMaskOpacity, // 遮罩透明度
backgroundColor : settings.dialogMaskBgColor // 遮罩背景色
},
buttons : { // 对话框按钮
enter : [lang.buttons.enter, function() { // 确定按钮
var line = parseInt(this.find("[data-line-number]").val()); // 获取行号
// 检查行号是否有效
if (line < 1 || line > lineCount) {
alert(dialogLang.error);
return false;
}
// 跳转到指定行
_this.gotoLine(line);
// 隐藏对话框并解锁屏幕
this.hide().lockScreen(false).hideMask();
return false;
}],
cancel : [lang.buttons.cancel, function() {
cancel : [lang.buttons.cancel, function() { // 取消按钮
this.hide().lockScreen(false).hideMask();
return false;
}]
}
});
}
}
});
}
dialog = editor.find("." + dialogName);
// 获取对话框元素
dialog = editor.find("." + dialogName);
this.dialogShowMask(dialog);
this.dialogLockScreen();
dialog.show();
};
// 显示对话框
this.dialogShowMask(dialog);
this.dialogLockScreen();
dialog.show();
};
};
};
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
{
module.exports = factory;
}
else if (typeof define === "function") // AMD/CMD/Sea.js
else if (typeof define === "function") // AMD/CMD/Sea.js
{
if (define.amd) { // for Require.js
if (define.amd) { // for Require.js
define(["editormd"], function(editormd) {
define(["editormd"], function(editormd) {
factory(editormd);
});
} else { // for Sea.js
define(function(require) {
} else { // for Sea.js
define(function(require) {
var editormd = require("../../editormd");
factory(editormd);
});
}
}
else
{
}
}
else
{
factory(window.editormd);
}
}
})();
})();

@ -11,92 +11,106 @@
(function() {
var factory = function (exports) {
// 工厂函数,用于定义帮助对话框插件
// Factory function to define help dialog plugin
var factory = function (exports) {
var $ = jQuery;
var pluginName = "help-dialog";
// jQuery对象
var $ = jQuery;
// 插件名称
var pluginName = "help-dialog";
exports.fn.helpDialog = function() {
var _this = this;
var lang = this.lang;
var editor = this.editor;
var settings = this.settings;
var path = settings.pluginPath + pluginName + "/";
var classPrefix = this.classPrefix;
var dialogName = classPrefix + pluginName, dialog;
var dialogLang = lang.dialog.help;
// 帮助对话框插件主函数
exports.fn.helpDialog = function() {
// 获取当前编辑器实例的各种属性和方法
var _this = this;
var lang = this.lang; // 语言包
var editor = this.editor; // 编辑器DOM元素
var settings = this.settings; // 设置选项
var path = settings.pluginPath + pluginName + "/"; // 插件路径
var classPrefix = this.classPrefix; // CSS类前缀
var dialogName = classPrefix + pluginName, dialog; // 对话框名称
var dialogLang = lang.dialog.help; // 帮助对话框语言包
if (editor.find("." + dialogName).length < 1)
{
var dialogContent = "<div class=\"markdown-body\" style=\"font-family:微软雅黑, Helvetica, Tahoma, STXihei,Arial;height:390px;overflow:auto;font-size:14px;border-bottom:1px solid #ddd;padding:0 20px 20px 0;\"></div>";
// 如果对话框不存在,则创建
if (editor.find("." + dialogName).length < 1)
{
// 构建对话框内容
var dialogContent = "<div class=\"markdown-body\" style=\"font-family:微软雅黑, Helvetica, Tahoma, STXihei,Arial;height:390px;overflow:auto;font-size:14px;border-bottom:1px solid #ddd;padding:0 20px 20px 0;\"></div>";
dialog = this.createDialog({
name : dialogName,
title : dialogLang.title,
width : 840,
height : 540,
mask : settings.dialogShowMask,
drag : settings.dialogDraggable,
content : dialogContent,
lockScreen : settings.dialogLockScreen,
maskStyle : {
opacity : settings.dialogMaskOpacity,
backgroundColor : settings.dialogMaskBgColor
},
buttons : {
close : [lang.buttons.close, function() {
this.hide().lockScreen(false).hideMask();
return false;
}]
}
});
}
// 创建对话框
dialog = this.createDialog({
name : dialogName, // 对话框名称
title : dialogLang.title, // 对话框标题
width : 840, // 对话框宽度
height : 540, // 对话框高度
mask : settings.dialogShowMask, // 是否显示遮罩
drag : settings.dialogDraggable, // 是否可拖拽
content : dialogContent, // 对话框内容
lockScreen : settings.dialogLockScreen, // 是否锁定屏幕
maskStyle : { // 遮罩样式
opacity : settings.dialogMaskOpacity, // 遮罩透明度
backgroundColor : settings.dialogMaskBgColor // 遮罩背景色
},
buttons : { // 对话框按钮
close : [lang.buttons.close, function() { // 关闭按钮
this.hide().lockScreen(false).hideMask();
return false;
}]
}
});
}
dialog = editor.find("." + dialogName);
// 获取对话框元素
dialog = editor.find("." + dialogName);
this.dialogShowMask(dialog);
this.dialogLockScreen();
dialog.show();
// 显示对话框
this.dialogShowMask(dialog);
this.dialogLockScreen();
dialog.show();
var helpContent = dialog.find(".markdown-body");
// 获取帮助内容容器
var helpContent = dialog.find(".markdown-body");
if (helpContent.html() === "")
{
$.get(path + "help.md", function(text) {
var md = exports.$marked(text);
helpContent.html(md);
// 如果帮助内容为空则从help.md文件加载
if (helpContent.html() === "")
{
$.get(path + "help.md", function(text) {
var md = exports.$marked(text); // 使用marked解析Markdown
helpContent.html(md); // 设置帮助内容
// 设置链接在新窗口打开
helpContent.find("a").attr("target", "_blank");
});
}
};
});
}
};
};
};
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
{
module.exports = factory;
}
else if (typeof define === "function") // AMD/CMD/Sea.js
else if (typeof define === "function") // AMD/CMD/Sea.js
{
if (define.amd) { // for Require.js
if (define.amd) { // for Require.js
define(["editormd"], function(editormd) {
define(["editormd"], function(editormd) {
factory(editormd);
});
} else { // for Sea.js
define(function(require) {
} else { // for Sea.js
define(function(require) {
var editormd = require("../../editormd");
factory(editormd);
});
}
}
else
{
}
}
else
{
factory(window.editormd);
}
}
})();
})();

@ -11,163 +11,181 @@
(function() {
var factory = function (exports) {
var $ = jQuery;
var pluginName = "html-entities-dialog";
var selecteds = [];
var entitiesData = [];
exports.fn.htmlEntitiesDialog = function() {
var _this = this;
var cm = this.cm;
var lang = _this.lang;
var settings = _this.settings;
var path = settings.pluginPath + pluginName + "/";
var editor = this.editor;
var cursor = cm.getCursor();
var selection = cm.getSelection();
var classPrefix = _this.classPrefix;
var dialogName = classPrefix + "dialog-" + pluginName, dialog;
var dialogLang = lang.dialog.htmlEntities;
var dialogContent = [
'<div class="' + classPrefix + 'html-entities-box" style=\"width: 760px;height: 334px;margin-bottom: 8px;overflow: hidden;overflow-y: auto;\">',
'<div class="' + classPrefix + 'grid-table">',
'</div>',
'</div>',
].join("\r\n");
cm.focus();
if (editor.find("." + dialogName).length > 0)
{
// 工厂函数用于定义HTML实体对话框插件
// Factory function to define HTML entities dialog plugin
var factory = function (exports) {
// jQuery对象
var $ = jQuery;
// 插件名称
var pluginName = "html-entities-dialog";
// 已选择的实体
var selecteds = [];
// 实体数据
var entitiesData = [];
// HTML实体对话框插件主函数
exports.fn.htmlEntitiesDialog = function() {
// 获取当前编辑器实例的各种属性和方法
var _this = this;
var cm = this.cm; // CodeMirror实例
var lang = _this.lang; // 语言包
var settings = _this.settings; // 设置选项
var path = settings.pluginPath + pluginName + "/"; // 插件路径
var editor = this.editor; // 编辑器DOM元素
var cursor = cm.getCursor(); // 获取光标位置
var selection = cm.getSelection(); // 获取选中文本
var classPrefix = _this.classPrefix; // CSS类前缀
// 对话框名称和语言包
var dialogName = classPrefix + "dialog-" + pluginName, dialog;
var dialogLang = lang.dialog.htmlEntities;
// 构建对话框内容
var dialogContent = [
'<div class="' + classPrefix + 'html-entities-box" style=\"width: 760px;height: 334px;margin-bottom: 8px;overflow: hidden;overflow-y: auto;\">',
'<div class="' + classPrefix + 'grid-table">',
'</div>',
'</div>',
].join("\r\n");
// 设置焦点到CodeMirror编辑器
cm.focus();
// 如果对话框已存在,则显示
if (editor.find("." + dialogName).length > 0)
{
dialog = editor.find("." + dialogName);
selecteds = [];
dialog.find("a").removeClass("selected");
this.dialogShowMask(dialog);
this.dialogLockScreen();
dialog.show();
}
else
{
dialog = this.createDialog({
name : dialogName,
title : dialogLang.title,
width : 800,
height : 475,
mask : settings.dialogShowMask,
drag : settings.dialogDraggable,
content : dialogContent,
lockScreen : settings.dialogLockScreen,
maskStyle : {
opacity : settings.dialogMaskOpacity,
backgroundColor : settings.dialogMaskBgColor
},
buttons : {
enter : [lang.buttons.enter, function() {
cm.replaceSelection(selecteds.join(" "));
this.hide().lockScreen(false).hideMask();
return false;
}],
cancel : [lang.buttons.cancel, function() {
this.hide().lockScreen(false).hideMask();
return false;
}]
}
});
}
var table = dialog.find("." + classPrefix + "grid-table");
var drawTable = function() {
if (entitiesData.length < 1) return ;
var rowNumber = 20;
var pageTotal = Math.ceil(entitiesData.length / rowNumber);
table.html("");
for (var i = 0; i < pageTotal; i++)
{
var row = "<div class=\"" + classPrefix + "grid-table-row\">";
for (var x = 0; x < rowNumber; x++)
{
var entity = entitiesData[(i * rowNumber) + x];
if (typeof entity !== "undefined")
{
var name = entity.name.replace("&amp;", "&");
row += "<a href=\"javascript:;\" value=\"" + entity.name + "\" title=\"" + name + "\" class=\"" + classPrefix + "html-entity-btn\">" + name + "</a>";
}
}
row += "</div>";
table.append(row);
}
dialog.find("." + classPrefix + "html-entity-btn").bind(exports.mouseOrTouch("click", "touchend"), function() {
$(this).toggleClass("selected");
if ($(this).hasClass("selected"))
{
selecteds.push($(this).attr("value"));
}
});
};
if (entitiesData.length < 1)
{
if (typeof (dialog.loading) == "function") dialog.loading(true);
$.getJSON(path + pluginName.replace("-dialog", "") + ".json", function(json) {
if (typeof (dialog.loading) == "function") dialog.loading(false);
entitiesData = json;
drawTable();
});
}
else
{
drawTable();
}
};
};
selecteds = [];
dialog.find("a").removeClass("selected");
this.dialogShowMask(dialog); // 显示对话框遮罩
this.dialogLockScreen(); // 锁定屏幕
dialog.show(); // 显示对话框
}
else
{
// 创建对话框
dialog = this.createDialog({
name : dialogName, // 对话框名称
title : dialogLang.title, // 对话框标题
width : 800, // 对话框宽度
height : 475, // 对话框高度
mask : settings.dialogShowMask, // 是否显示遮罩
drag : settings.dialogDraggable, // 是否可拖拽
content : dialogContent, // 对话框内容
lockScreen : settings.dialogLockScreen, // 是否锁定屏幕
maskStyle : { // 遮罩样式
opacity : settings.dialogMaskOpacity, // 遮罩透明度
backgroundColor : settings.dialogMaskBgColor // 遮罩背景色
},
buttons : { // 对话框按钮
enter : [lang.buttons.enter, function() { // 确定按钮
cm.replaceSelection(selecteds.join(" ")); // 插入选中的实体
this.hide().lockScreen(false).hideMask(); // 隐藏对话框并解锁屏幕
return false;
}],
cancel : [lang.buttons.cancel, function() { // 取消按钮
this.hide().lockScreen(false).hideMask(); // 隐藏对话框并解锁屏幕
return false;
}]
}
});
}
// 获取表格容器
var table = dialog.find("." + classPrefix + "grid-table");
// 绘制表格
var drawTable = function() {
// 如果实体数据为空,则直接返回
if (entitiesData.length < 1) return ;
var rowNumber = 20;
var pageTotal = Math.ceil(entitiesData.length / rowNumber);
table.html("");
for (var i = 0; i < pageTotal; i++)
{
var row = "<div class=\"" + classPrefix + "grid-table-row\">";
for (var x = 0; x < rowNumber; x++)
{
var entity = entitiesData[(i * rowNumber) + x];
if (typeof entity !== "undefined")
{
var name = entity.name.replace("&amp;", "&");
row += "<a href=\"javascript:;\" value=\"" + entity.name + "\" title=\"" + name + "\" class=\"" + classPrefix + "html-entity-btn\">" + name + "</a>";
}
}
row += "</div>";
table.append(row);
}
// 绑定HTML实体按钮点击事件
dialog.find("." + classPrefix + "html-entity-btn").bind(exports.mouseOrTouch("click", "touchend"), function() {
$(this).toggleClass("selected");
if ($(this).hasClass("selected"))
{
selecteds.push($(this).attr("value"));
}
});
};
// 如果实体数据为空则从JSON文件加载
if (entitiesData.length < 1)
{
if (typeof (dialog.loading) == "function") dialog.loading(true);
$.getJSON(path + pluginName.replace("-dialog", "") + ".json", function(json) {
if (typeof (dialog.loading) == "function") dialog.loading(false);
entitiesData = json;
drawTable();
});
}
else
{
drawTable();
}
};
};
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
{
module.exports = factory;
}
else if (typeof define === "function") // AMD/CMD/Sea.js
else if (typeof define === "function") // AMD/CMD/Sea.js
{
if (define.amd) { // for Require.js
if (define.amd) { // for Require.js
define(["editormd"], function(editormd) {
define(["editormd"], function(editormd) {
factory(editormd);
});
} else { // for Sea.js
define(function(require) {
} else { // for Sea.js
define(function(require) {
var editormd = require("../../editormd");
factory(editormd);
});
}
}
else
{
}
}
else
{
factory(window.editormd);
}
}
})();
})();

@ -11,41 +11,59 @@
(function() {
// 工厂函数,用于定义图片对话框插件
// Factory function to define image dialog plugin
var factory = function (exports) {
var pluginName = "image-dialog";
// 插件名称
// Plugin name
var pluginName = "image-dialog";
exports.fn.imageDialog = function() {
// 图片对话框插件主函数
// Main function of image dialog plugin
exports.fn.imageDialog = function() {
// 获取当前编辑器实例的各种属性和方法
// Get various properties and methods of the current editor instance
var _this = this;
var cm = this.cm;
var lang = this.lang;
var editor = this.editor;
var settings = this.settings;
var cursor = cm.getCursor();
var selection = cm.getSelection();
var imageLang = lang.dialog.image;
var classPrefix = this.classPrefix;
var iframeName = classPrefix + "image-iframe";
var dialogName = classPrefix + pluginName, dialog;
cm.focus();
var cm = this.cm; // CodeMirror实例
var lang = this.lang; // 语言包
var editor = this.editor; // 编辑器DOM元素
var settings = this.settings; // 设置选项
var cursor = cm.getCursor(); // 获取光标位置
var selection = cm.getSelection(); // 获取选中文本
var imageLang = lang.dialog.image; // 图片对话框语言包
var classPrefix = this.classPrefix; // CSS类前缀
var iframeName = classPrefix + "image-iframe"; // iframe名称
var dialogName = classPrefix + pluginName, dialog; // 对话框名称
// 设置焦点到CodeMirror编辑器
// Set focus to CodeMirror editor
cm.focus();
// 显示/隐藏加载遮罩
// Show/hide loading mask
var loading = function(show) {
var _loading = dialog.find("." + classPrefix + "dialog-mask");
_loading[(show) ? "show" : "hide"]();
};
// 如果对话框不存在,则创建
// If the dialog does not exist, create it
if (editor.find("." + dialogName).length < 1)
{
// 生成唯一标识符
var guid = (new Date).getTime();
// 构造上传URL
var action = settings.imageUploadURL + (settings.imageUploadURL.indexOf("?") >= 0 ? "&" : "?") + "guid=" + guid;
// 处理跨域上传
if (settings.crossDomainUpload)
{
action += "&callback=" + settings.uploadCallbackURL + "&dialog_id=editormd-image-dialog-" + guid;
}
// 构建对话框内容
var dialogContent = ( (settings.imageUpload) ? "<form action=\"" + action +"\" target=\"" + iframeName + "\" method=\"post\" enctype=\"multipart/form-data\" class=\"" + classPrefix + "form\">" : "<div class=\"" + classPrefix + "form\">" ) +
( (settings.imageUpload) ? "<iframe name=\"" + iframeName + "\" id=\"" + iframeName + "\" guid=\"" + guid + "\"></iframe>" : "" ) +
"<label>" + imageLang.url + "</label>" +
@ -64,35 +82,37 @@
"<br/>" +
( (settings.imageUpload) ? "</form>" : "</div>");
//var imageFooterHTML = "<button class=\"" + classPrefix + "btn " + classPrefix + "image-manager-btn\" style=\"float:left;\">" + imageLang.managerButton + "</button>";
// 创建对话框
dialog = this.createDialog({
title : imageLang.title,
width : (settings.imageUpload) ? 465 : 380,
height : 254,
name : dialogName,
content : dialogContent,
mask : settings.dialogShowMask,
drag : settings.dialogDraggable,
lockScreen : settings.dialogLockScreen,
maskStyle : {
opacity : settings.dialogMaskOpacity,
backgroundColor : settings.dialogMaskBgColor
title : imageLang.title, // 对话框标题
width : (settings.imageUpload) ? 465 : 380, // 对话框宽度
height : 254, // 对话框高度
name : dialogName, // 对话框名称
content : dialogContent, // 对话框内容
mask : settings.dialogShowMask, // 是否显示遮罩
drag : settings.dialogDraggable, // 是否可拖拽
lockScreen : settings.dialogLockScreen, // 是否锁定屏幕
maskStyle : { // 遮罩样式
opacity : settings.dialogMaskOpacity, // 遮罩透明度
backgroundColor : settings.dialogMaskBgColor // 遮罩背景色
},
buttons : {
enter : [lang.buttons.enter, function() {
var url = this.find("[data-url]").val();
var alt = this.find("[data-alt]").val();
var link = this.find("[data-link]").val();
buttons : { // 对话框按钮
enter : [lang.buttons.enter, function() { // 确定按钮
var url = this.find("[data-url]").val(); // 获取图片URL
var alt = this.find("[data-alt]").val(); // 获取图片描述
var link = this.find("[data-link]").val(); // 获取链接地址
// 检查图片URL是否为空
if (url === "")
{
alert(imageLang.imageURLEmpty);
return false;
}
var altAttr = (alt !== "") ? " \"" + alt + "\"" : "";
// 构造alt属性
var altAttr = (alt !== "") ? " \"" + alt + "\"" : "";
// 根据是否包含链接构造Markdown图片格式
if (link === "" || link === "http://")
{
cm.replaceSelection("![" + alt + "](" + url + altAttr + ")");
@ -102,16 +122,18 @@
cm.replaceSelection("[![" + alt + "](" + url + altAttr + ")](" + link + altAttr + ")");
}
// 如果描述为空,设置光标位置
if (alt === "") {
cm.setCursor(cursor.line, cursor.ch + 2);
}
// 隐藏对话框并解锁屏幕
this.hide().lockScreen(false).hideMask();
return false;
}],
cancel : [lang.buttons.cancel, function() {
cancel : [lang.buttons.cancel, function() { // 取消按钮
this.hide().lockScreen(false).hideMask();
return false;
@ -119,47 +141,62 @@
}
});
// 设置对话框ID
dialog.attr("id", classPrefix + "image-dialog-" + guid);
if (!settings.imageUpload) {
// 如果不支持图片上传,直接返回
if (!settings.imageUpload) {
return ;
}
var fileInput = dialog.find("[name=\"" + classPrefix + "image-file\"]");
// 获取文件输入框
var fileInput = dialog.find("[name=\"" + classPrefix + "image-file\"]");
fileInput.bind("change", function() {
var fileName = fileInput.val();
var isImage = new RegExp("(\\.(" + settings.imageFormats.join("|") + "))$"); // /(\.(webp|jpg|jpeg|gif|bmp|png))$/
// 绑定文件选择变化事件
fileInput.bind("change", function() {
var fileName = fileInput.val();
// 检查文件扩展名是否为图片格式
var isImage = new RegExp("(\\.(" + settings.imageFormats.join("|") + "))$"); // /(\.(webp|jpg|jpeg|gif|bmp|png))$/
if (fileName === "")
{
alert(imageLang.uploadFileEmpty);
// 检查文件名是否为空
if (fileName === "")
{
alert(imageLang.uploadFileEmpty);
return false;
}
}
// 检查文件格式是否允许
if (!isImage.test(fileName))
{
alert(imageLang.formatNotAllowed + settings.imageFormats.join(", "));
{
alert(imageLang.formatNotAllowed + settings.imageFormats.join(", "));
return false;
}
}
// 显示加载遮罩
loading(true);
// 提交处理函数
var submitHandler = function() {
// 获取上传iframe
var uploadIframe = document.getElementById(iframeName);
// 绑定iframe加载完成事件
uploadIframe.onload = function() {
// 隐藏加载遮罩
loading(false);
// 获取iframe中的内容
var body = (uploadIframe.contentWindow ? uploadIframe.contentWindow : uploadIframe.contentDocument).document.body;
var json = (body.innerText) ? body.innerText : ( (body.textContent) ? body.textContent : null);
// 解析JSON响应
json = (typeof JSON.parse !== "undefined") ? JSON.parse(json) : eval("(" + json + ")");
// 处理非跨域上传响应
if(!settings.crossDomainUpload)
{
if (json.success === 1)
@ -176,46 +213,49 @@
};
};
// 绑定并触发提交按钮点击事件
dialog.find("[type=\"submit\"]").bind("click", submitHandler).trigger("click");
});
});
}
dialog = editor.find("." + dialogName);
dialog.find("[type=\"text\"]").val("");
dialog.find("[type=\"file\"]").val("");
dialog.find("[data-link]").val("http://");
// 重置对话框表单内容
dialog = editor.find("." + dialogName);
dialog.find("[type=\"text\"]").val("");
dialog.find("[type=\"file\"]").val("");
dialog.find("[data-link]").val("http://");
this.dialogShowMask(dialog);
this.dialogLockScreen();
dialog.show();
// 显示对话框
this.dialogShowMask(dialog);
this.dialogLockScreen();
dialog.show();
};
};
};
};
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
{
module.exports = factory;
}
else if (typeof define === "function") // AMD/CMD/Sea.js
else if (typeof define === "function") // AMD/CMD/Sea.js
{
if (define.amd) { // for Require.js
if (define.amd) { // for Require.js
define(["editormd"], function(editormd) {
define(["editormd"], function(editormd) {
factory(editormd);
});
} else { // for Sea.js
define(function(require) {
} else { // for Sea.js
define(function(require) {
var editormd = require("../../editormd");
factory(editormd);
});
}
}
else
{
}
}
else
{
factory(window.editormd);
}
}
})();
})();

@ -11,36 +11,50 @@
(function() {
// 工厂函数,用于定义链接对话框插件
// Factory function to define link dialog plugin
var factory = function (exports) {
var pluginName = "link-dialog";
exports.fn.linkDialog = function() {
var _this = this;
var cm = this.cm;
var editor = this.editor;
var settings = this.settings;
var selection = cm.getSelection();
var lang = this.lang;
var linkLang = lang.dialog.link;
var classPrefix = this.classPrefix;
var dialogName = classPrefix + pluginName, dialog;
cm.focus();
// 插件名称
// Plugin name
var pluginName = "link-dialog";
// 链接对话框插件主函数
// Main function of link dialog plugin
exports.fn.linkDialog = function() {
// 获取当前编辑器实例的各种属性和方法
// Get various properties and methods of the current editor instance
var _this = this;
var cm = this.cm; // CodeMirror实例
var editor = this.editor; // 编辑器DOM元素
var settings = this.settings; // 设置选项
var selection = cm.getSelection(); // 获取选中文本
var lang = this.lang; // 语言包
var linkLang = lang.dialog.link; // 链接对话框语言包
var classPrefix = this.classPrefix; // CSS类前缀
var dialogName = classPrefix + pluginName, dialog; // 对话框名称
// 设置焦点到CodeMirror编辑器
// Set focus to CodeMirror editor
cm.focus();
// 如果对话框已存在,则显示并重置内容
// If the dialog already exists, show it and reset the content
if (editor.find("." + dialogName).length > 0)
{
dialog = editor.find("." + dialogName);
dialog.find("[data-url]").val("http://");
dialog.find("[data-title]").val(selection);
this.dialogShowMask(dialog);
this.dialogLockScreen();
dialog.show();
this.dialogShowMask(dialog); // 显示对话框遮罩
this.dialogLockScreen(); // 锁定屏幕
dialog.show(); // 显示对话框
}
else
{
// 构建对话框HTML内容
// Build dialog HTML content
var dialogHTML = "<div class=\"" + classPrefix + "form\">" +
"<label>" + linkLang.url + "</label>" +
"<input type=\"text\" value=\"http://\" data-url />" +
@ -50,23 +64,26 @@
"<br/>" +
"</div>";
// 创建对话框
// Create dialog
dialog = this.createDialog({
title : linkLang.title,
width : 380,
height : 211,
content : dialogHTML,
mask : settings.dialogShowMask,
drag : settings.dialogDraggable,
lockScreen : settings.dialogLockScreen,
maskStyle : {
opacity : settings.dialogMaskOpacity,
backgroundColor : settings.dialogMaskBgColor
title : linkLang.title, // 对话框标题
width : 380, // 对话框宽度
height : 211, // 对话框高度
content : dialogHTML, // 对话框内容
mask : settings.dialogShowMask, // 是否显示遮罩
drag : settings.dialogDraggable, // 是否可拖拽
lockScreen : settings.dialogLockScreen, // 是否锁定屏幕
maskStyle : { // 遮罩样式
opacity : settings.dialogMaskOpacity, // 遮罩透明度
backgroundColor : settings.dialogMaskBgColor // 遮罩背景色
},
buttons : {
enter : [lang.buttons.enter, function() {
var url = this.find("[data-url]").val();
var title = this.find("[data-title]").val();
buttons : { // 对话框按钮
enter : [lang.buttons.enter, function() { // 确定按钮
var url = this.find("[data-url]").val(); // 获取链接地址
var title = this.find("[data-title]").val(); // 获取链接标题
// 检查链接地址是否为空
if (url === "http://" || url === "")
{
alert(linkLang.urlEmpty);
@ -79,55 +96,59 @@
return false;
}*/
// 构造Markdown链接格式
var str = "[" + title + "](" + url + " \"" + title + "\")";
// 如果标题为空,使用简化格式
if (title == "")
{
str = "[" + url + "](" + url + ")";
}
// 插入链接到编辑器
cm.replaceSelection(str);
// 隐藏对话框并解锁屏幕
this.hide().lockScreen(false).hideMask();
return false;
}],
cancel : [lang.buttons.cancel, function() {
cancel : [lang.buttons.cancel, function() { // 取消按钮
this.hide().lockScreen(false).hideMask();
return false;
}]
}
});
}
};
}
};
};
};
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
{
module.exports = factory;
}
else if (typeof define === "function") // AMD/CMD/Sea.js
else if (typeof define === "function") // AMD/CMD/Sea.js
{
if (define.amd) { // for Require.js
if (define.amd) { // for Require.js
define(["editormd"], function(editormd) {
define(["editormd"], function(editormd) {
factory(editormd);
});
} else { // for Sea.js
define(function(require) {
} else { // for Sea.js
define(function(require) {
var editormd = require("../../editormd");
factory(editormd);
});
}
}
else
{
}
}
else
{
factory(window.editormd);
}
}
})();
})();

@ -11,101 +11,106 @@
(function() {
// 工厂函数,用于定义插件模板
// Factory function to define plugin template
var factory = function (exports) {
var $ = jQuery; // if using module loader(Require.js/Sea.js).
// jQuery对象
var $ = jQuery; // if using module loader(Require.js/Sea.js).
var langs = {
"zh-cn" : {
toolbar : {
table : "表格"
},
dialog : {
table : {
title : "添加表格",
cellsLabel : "单元格数",
alignLabel : "对齐方式",
rows : "行数",
cols : "列数",
aligns : ["默认", "左对齐", "居中对齐", "右对齐"]
}
}
},
"zh-tw" : {
toolbar : {
table : "添加表格"
},
dialog : {
table : {
title : "添加表格",
cellsLabel : "單元格數",
alignLabel : "對齊方式",
rows : "行數",
cols : "列數",
aligns : ["默認", "左對齊", "居中對齊", "右對齊"]
}
}
},
"en" : {
toolbar : {
table : "Tables"
},
dialog : {
table : {
title : "Tables",
cellsLabel : "Cells",
alignLabel : "Align",
rows : "Rows",
cols : "Cols",
aligns : ["Default", "Left align", "Center align", "Right align"]
}
}
}
};
// 多语言支持
var langs = {
"zh-cn" : {
toolbar : {
table : "表格"
},
dialog : {
table : {
title : "添加表格",
cellsLabel : "单元格数",
alignLabel : "对齐方式",
rows : "行数",
cols : "列数",
aligns : ["默认", "左对齐", "居中对齐", "右对齐"]
}
}
},
"zh-tw" : {
toolbar : {
table : "添加表格"
},
dialog : {
table : {
title : "添加表格",
cellsLabel : "單元格數",
alignLabel : "對齊方式",
rows : "行數",
cols : "列數",
aligns : ["默認", "左對齊", "居中對齊", "右對齊"]
}
}
},
"en" : {
toolbar : {
table : "Tables"
},
dialog : {
table : {
title : "Tables",
cellsLabel : "Cells",
alignLabel : "Align",
rows : "Rows",
cols : "Cols",
aligns : ["Default", "Left align", "Center align", "Right align"]
}
}
}
};
exports.fn.htmlEntities = function() {
/*
var _this = this; // this == the current instance object of Editor.md
var lang = _this.lang;
var settings = _this.settings;
var editor = this.editor;
var cursor = cm.getCursor();
var selection = cm.getSelection();
var classPrefix = this.classPrefix;
// HTML实体插件主函数
exports.fn.htmlEntities = function() {
/*
var _this = this; // this == the current instance object of Editor.md
var lang = _this.lang;
var settings = _this.settings;
var editor = this.editor;
var cursor = cm.getCursor();
var selection = cm.getSelection();
var classPrefix = this.classPrefix;
$.extend(true, this.lang, langs[this.lang.name]); // l18n
this.setToolbar();
$.extend(true, this.lang, langs[this.lang.name]); // l18n
this.setToolbar();
cm.focus();
*/
//....
};
cm.focus();
*/
//....
};
};
};
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
{
module.exports = factory;
}
else if (typeof define === "function") // AMD/CMD/Sea.js
else if (typeof define === "function") // AMD/CMD/Sea.js
{
if (define.amd) { // for Require.js
if (define.amd) { // for Require.js
define(["editormd"], function(editormd) {
define(["editormd"], function(editormd) {
factory(editormd);
});
} else { // for Sea.js
define(function(require) {
} else { // for Sea.js
define(function(require) {
var editormd = require("./../../editormd");
factory(editormd);
});
}
}
else
{
}
}
else
{
factory(window.editormd);
}
}
})();
})();

@ -11,61 +11,73 @@
(function() {
// 工厂函数,用于定义预格式化文本对话框插件
// Factory function to define preformatted text dialog plugin
var factory = function (exports) {
var cmEditor;
var pluginName = "preformatted-text-dialog";
// CodeMirror编辑器实例
var cmEditor;
// 插件名称
var pluginName = "preformatted-text-dialog";
exports.fn.preformattedTextDialog = function() {
// 预格式化文本对话框插件主函数
exports.fn.preformattedTextDialog = function() {
// 获取当前编辑器实例的各种属性和方法
var _this = this;
var cm = this.cm;
var lang = this.lang;
var editor = this.editor;
var settings = this.settings;
var cursor = cm.getCursor();
var selection = cm.getSelection();
var classPrefix = this.classPrefix;
var dialogLang = lang.dialog.preformattedText;
var dialogName = classPrefix + pluginName, dialog;
cm.focus();
var cm = this.cm; // CodeMirror实例
var lang = this.lang; // 语言包
var editor = this.editor; // 编辑器DOM元素
var settings = this.settings; // 设置选项
var cursor = cm.getCursor(); // 获取光标位置
var selection = cm.getSelection(); // 获取选中文本
var classPrefix = this.classPrefix; // CSS类前缀
var dialogLang = lang.dialog.preformattedText; // 预格式化文本对话框语言包
var dialogName = classPrefix + pluginName, dialog; // 对话框名称
// 设置焦点到CodeMirror编辑器
cm.focus();
// 如果对话框已存在,则显示并重置内容
if (editor.find("." + dialogName).length > 0)
{
dialog = editor.find("." + dialogName);
dialog.find("textarea").val(selection);
this.dialogShowMask(dialog);
this.dialogLockScreen();
dialog.show();
this.dialogShowMask(dialog); // 显示对话框遮罩
this.dialogLockScreen(); // 锁定屏幕
dialog.show(); // 显示对话框
}
else
{
// 构建对话框内容
var dialogContent = "<textarea placeholder=\"coding now....\" style=\"display:none;\">" + selection + "</textarea>";
// 创建对话框
dialog = this.createDialog({
name : dialogName,
title : dialogLang.title,
width : 780,
height : 540,
mask : settings.dialogShowMask,
drag : settings.dialogDraggable,
content : dialogContent,
lockScreen : settings.dialogLockScreen,
maskStyle : {
opacity : settings.dialogMaskOpacity,
backgroundColor : settings.dialogMaskBgColor
name : dialogName, // 对话框名称
title : dialogLang.title, // 对话框标题
width : 780, // 对话框宽度
height : 540, // 对话框高度
mask : settings.dialogShowMask, // 是否显示遮罩
drag : settings.dialogDraggable, // 是否可拖拽
content : dialogContent, // 对话框内容
lockScreen : settings.dialogLockScreen, // 是否锁定屏幕
maskStyle : { // 遮罩样式
opacity : settings.dialogMaskOpacity, // 遮罩透明度
backgroundColor : settings.dialogMaskBgColor // 遮罩背景色
},
buttons : {
enter : [lang.buttons.enter, function() {
var codeTexts = this.find("textarea").val();
buttons : { // 对话框按钮
enter : [lang.buttons.enter, function() { // 确定按钮
var codeTexts = this.find("textarea").val(); // 获取代码文本
// 检查代码文本是否为空
if (codeTexts === "")
{
alert(dialogLang.emptyAlert);
return false;
}
// 为每行代码添加4个空格的缩进
codeTexts = codeTexts.split("\n");
for (var i in codeTexts)
@ -75,17 +87,20 @@
codeTexts = codeTexts.join("\n");
// 如果光标不在行首,添加换行符
if (cursor.ch !== 0) {
codeTexts = "\r\n\r\n" + codeTexts;
}
// 插入预格式化文本到编辑器
cm.replaceSelection(codeTexts);
// 隐藏对话框并解锁屏幕
this.hide().lockScreen(false).hideMask();
return false;
}],
cancel : [lang.buttons.cancel, function() {
cancel : [lang.buttons.cancel, function() { // 取消按钮
this.hide().lockScreen(false).hideMask();
return false;
@ -93,80 +108,86 @@
}
});
}
var cmConfig = {
mode : "text/html",
theme : settings.theme,
tabSize : 4,
autofocus : true,
autoCloseTags : true,
indentUnit : 4,
lineNumbers : true,
lineWrapping : true,
extraKeys : {"Ctrl-Q": function(cm){ cm.foldCode(cm.getCursor()); }},
foldGutter : true,
gutters : ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
matchBrackets : true,
indentWithTabs : true,
styleActiveLine : true,
styleSelectedText : true,
autoCloseBrackets : true,
showTrailingSpace : true,
highlightSelectionMatches : true
};
var textarea = dialog.find("textarea");
var cmObj = dialog.find(".CodeMirror");
if (dialog.find(".CodeMirror").length < 1)
{
cmEditor = exports.$CodeMirror.fromTextArea(textarea[0], cmConfig);
cmObj = dialog.find(".CodeMirror");
cmObj.css({
"float" : "none",
margin : "0 0 5px",
border : "1px solid #ddd",
fontSize : settings.fontSize,
width : "100%",
height : "410px"
});
cmEditor.on("change", function(cm) {
textarea.val(cm.getValue());
});
}
else
{
cmEditor.setValue(cm.getSelection());
}
};
};
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
// CodeMirror配置
var cmConfig = {
mode : "text/html", // 编辑器模式
theme : settings.theme, // 主题
tabSize : 4, // Tab大小
autofocus : true, // 自动聚焦
autoCloseTags : true, // 自动关闭标签
indentUnit : 4, // 缩进单位
lineNumbers : true, // 显示行号
lineWrapping : true, // 自动换行
extraKeys : {"Ctrl-Q": function(cm){ cm.foldCode(cm.getCursor()); }}, // 额外快捷键
foldGutter : true, // 代码折叠
gutters : ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], // 代码槽
matchBrackets : true, // 匹配括号
indentWithTabs : true, // 使用Tab缩进
styleActiveLine : true, // 高亮活动行
styleSelectedText : true, // 高亮选中文本
autoCloseBrackets : true, // 自动关闭括号
showTrailingSpace : true, // 显示尾随空格
highlightSelectionMatches : true // 高亮匹配选中
};
// 获取文本域和CodeMirror对象
var textarea = dialog.find("textarea");
var cmObj = dialog.find(".CodeMirror");
// 如果CodeMirror对象不存在则创建
if (dialog.find(".CodeMirror").length < 1)
{
cmEditor = exports.$CodeMirror.fromTextArea(textarea[0], cmConfig);
cmObj = dialog.find(".CodeMirror");
// 设置CodeMirror样式
cmObj.css({
"float" : "none",
margin : "0 0 5px",
border : "1px solid #ddd",
fontSize : settings.fontSize,
width : "100%",
height : "410px"
});
// 监听代码变化事件
cmEditor.on("change", function(cm) {
textarea.val(cm.getValue()); // 同步代码到文本域
});
}
else
{
// 设置CodeMirror值为选中文本
cmEditor.setValue(cm.getSelection());
}
};
};
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
{
module.exports = factory;
}
else if (typeof define === "function") // AMD/CMD/Sea.js
else if (typeof define === "function") // AMD/CMD/Sea.js
{
if (define.amd) { // for Require.js
if (define.amd) { // for Require.js
define(["editormd"], function(editormd) {
define(["editormd"], function(editormd) {
factory(editormd);
});
} else { // for Sea.js
define(function(require) {
} else { // for Sea.js
define(function(require) {
var editormd = require("../../editormd");
factory(editormd);
});
}
}
else
{
}
}
else
{
factory(window.editormd);
}
}
})();
})();

@ -11,28 +11,37 @@
(function() {
// 工厂函数,用于定义引用链接对话框插件
// Factory function to define reference link dialog plugin
var factory = function (exports) {
var pluginName = "reference-link-dialog";
var ReLinkId = 1;
// 插件名称
var pluginName = "reference-link-dialog";
// 引用链接ID计数器
var ReLinkId = 1;
exports.fn.referenceLinkDialog = function() {
// 引用链接对话框插件主函数
exports.fn.referenceLinkDialog = function() {
// 获取当前编辑器实例的各种属性和方法
var _this = this;
var cm = this.cm;
var lang = this.lang;
var editor = this.editor;
var settings = this.settings;
var cursor = cm.getCursor();
var selection = cm.getSelection();
var dialogLang = lang.dialog.referenceLink;
var classPrefix = this.classPrefix;
var dialogName = classPrefix + pluginName, dialog;
cm.focus();
var cm = this.cm; // CodeMirror实例
var lang = this.lang; // 语言包
var editor = this.editor; // 编辑器DOM元素
var settings = this.settings; // 设置选项
var cursor = cm.getCursor(); // 获取光标位置
var selection = cm.getSelection(); // 获取选中文本
var dialogLang = lang.dialog.referenceLink; // 引用链接对话框语言包
var classPrefix = this.classPrefix; // CSS类前缀
var dialogName = classPrefix + pluginName, dialog; // 对话框名称
// 设置焦点到CodeMirror编辑器
cm.focus();
// 如果对话框不存在,则创建
if (editor.find("." + dialogName).length < 1)
{
// 构建对话框HTML内容
var dialogHTML = "<div class=\"" + classPrefix + "form\">" +
"<label>" + dialogLang.name + "</label>" +
"<input type=\"text\" value=\"[" + ReLinkId + "]\" data-name />" +
@ -48,60 +57,69 @@
"<br/>" +
"</div>";
// 创建对话框
dialog = this.createDialog({
name : dialogName,
title : dialogLang.title,
width : 380,
height : 296,
content : dialogHTML,
mask : settings.dialogShowMask,
drag : settings.dialogDraggable,
lockScreen : settings.dialogLockScreen,
maskStyle : {
opacity : settings.dialogMaskOpacity,
backgroundColor : settings.dialogMaskBgColor
name : dialogName, // 对话框名称
title : dialogLang.title, // 对话框标题
width : 380, // 对话框宽度
height : 296, // 对话框高度
content : dialogHTML, // 对话框内容
mask : settings.dialogShowMask, // 是否显示遮罩
drag : settings.dialogDraggable, // 是否可拖拽
lockScreen : settings.dialogLockScreen, // 是否锁定屏幕
maskStyle : { // 遮罩样式
opacity : settings.dialogMaskOpacity, // 遮罩透明度
backgroundColor : settings.dialogMaskBgColor // 遮罩背景色
},
buttons : {
enter : [lang.buttons.enter, function() {
var name = this.find("[data-name]").val();
var url = this.find("[data-url]").val();
var rid = this.find("[data-url-id]").val();
var title = this.find("[data-title]").val();
buttons : { // 对话框按钮
enter : [lang.buttons.enter, function() { // 确定按钮
var name = this.find("[data-name]").val(); // 获取引用名称
var url = this.find("[data-url]").val(); // 获取URL
var rid = this.find("[data-url-id]").val(); // 获取URL ID
var title = this.find("[data-title]").val(); // 获取标题
// 检查引用名称是否为空
if (name === "")
{
alert(dialogLang.nameEmpty);
return false;
}
// 检查URL ID是否为空
if (rid === "")
{
alert(dialogLang.idEmpty);
return false;
}
// 检查URL是否为空
if (url === "http://" || url === "")
{
alert(dialogLang.urlEmpty);
return false;
}
// 插入引用链接到编辑器
//cm.replaceSelection("[" + title + "][" + name + "]\n[" + name + "]: " + url + "");
cm.replaceSelection("[" + name + "][" + rid + "]");
// 如果没有选中文本,设置光标位置
if (selection === "") {
cm.setCursor(cursor.line, cursor.ch + 1);
}
title = (title === "") ? "" : " \"" + title + "\"";
// 处理标题
title = (title === "") ? "" : " \"" + title + "\"";
cm.setValue(cm.getValue() + "\n[" + rid + "]: " + url + title + "");
// 在文档末尾添加引用链接定义
cm.setValue(cm.getValue() + "\n[" + rid + "]: " + url + title + "");
// 隐藏对话框并解锁屏幕
this.hide().lockScreen(false).hideMask();
return false;
}],
cancel : [lang.buttons.cancel, function() {
cancel : [lang.buttons.cancel, function() { // 取消按钮
this.hide().lockScreen(false).hideMask();
return false;
@ -110,44 +128,47 @@
});
}
dialog = editor.find("." + dialogName);
dialog.find("[data-name]").val("[" + ReLinkId + "]");
dialog.find("[data-url-id]").val("");
dialog.find("[data-url]").val("http://");
dialog.find("[data-title]").val(selection);
// 重置对话框表单内容
dialog = editor.find("." + dialogName);
dialog.find("[data-name]").val("[" + ReLinkId + "]");
dialog.find("[data-url-id]").val("");
dialog.find("[data-url]").val("http://");
dialog.find("[data-title]").val(selection);
this.dialogShowMask(dialog);
this.dialogLockScreen();
dialog.show();
// 显示对话框
this.dialogShowMask(dialog);
this.dialogLockScreen();
dialog.show();
ReLinkId++;
};
// 增加引用链接ID计数器
ReLinkId++;
};
};
};
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
{
module.exports = factory;
}
else if (typeof define === "function") // AMD/CMD/Sea.js
else if (typeof define === "function") // AMD/CMD/Sea.js
{
if (define.amd) { // for Require.js
if (define.amd) { // for Require.js
define(["editormd"], function(editormd) {
define(["editormd"], function(editormd) {
factory(editormd);
});
} else { // for Sea.js
define(function(require) {
} else { // for Sea.js
define(function(require) {
var editormd = require("../../editormd");
factory(editormd);
});
}
}
else
{
}
}
else
{
factory(window.editormd);
}
}
})();
})();

@ -11,208 +11,233 @@
(function() {
var factory = function (exports) {
var $ = jQuery;
var pluginName = "table-dialog";
var langs = {
"zh-cn" : {
toolbar : {
table : "表格"
},
dialog : {
table : {
title : "添加表格",
cellsLabel : "单元格数",
alignLabel : "对齐方式",
rows : "行数",
cols : "列数",
aligns : ["默认", "左对齐", "居中对齐", "右对齐"]
}
}
},
"zh-tw" : {
toolbar : {
table : "添加表格"
},
dialog : {
table : {
title : "添加表格",
cellsLabel : "單元格數",
alignLabel : "對齊方式",
rows : "行數",
cols : "列數",
aligns : ["默認", "左對齊", "居中對齊", "右對齊"]
}
}
},
"en" : {
toolbar : {
table : "Tables"
},
dialog : {
table : {
title : "Tables",
cellsLabel : "Cells",
alignLabel : "Align",
rows : "Rows",
cols : "Cols",
aligns : ["Default", "Left align", "Center align", "Right align"]
}
}
}
};
exports.fn.tableDialog = function() {
var _this = this;
var cm = this.cm;
var editor = this.editor;
var settings = this.settings;
var path = settings.path + "../plugins/" + pluginName +"/";
var classPrefix = this.classPrefix;
var dialogName = classPrefix + pluginName, dialog;
$.extend(true, this.lang, langs[this.lang.name]);
this.setToolbar();
var lang = this.lang;
var dialogLang = lang.dialog.table;
var dialogContent = [
"<div class=\"editormd-form\" style=\"padding: 13px 0;\">",
"<label>" + dialogLang.cellsLabel + "</label>",
dialogLang.rows + " <input type=\"number\" value=\"3\" class=\"number-input\" style=\"width:40px;\" max=\"100\" min=\"2\" data-rows />&nbsp;&nbsp;",
dialogLang.cols + " <input type=\"number\" value=\"2\" class=\"number-input\" style=\"width:40px;\" max=\"100\" min=\"1\" data-cols /><br/>",
"<label>" + dialogLang.alignLabel + "</label>",
"<div class=\"fa-btns\"></div>",
"</div>"
].join("\n");
if (editor.find("." + dialogName).length > 0)
{
// 工厂函数,用于定义表格对话框插件
// Factory function to define table dialog plugin
var factory = function (exports) {
// jQuery对象
var $ = jQuery;
// 插件名称
var pluginName = "table-dialog";
// 多语言支持
var langs = {
"zh-cn" : {
toolbar : {
table : "表格"
},
dialog : {
table : {
title : "添加表格",
cellsLabel : "单元格数",
alignLabel : "对齐方式",
rows : "行数",
cols : "列数",
aligns : ["默认", "左对齐", "居中对齐", "右对齐"]
}
}
},
"zh-tw" : {
toolbar : {
table : "添加表格"
},
dialog : {
table : {
title : "添加表格",
cellsLabel : "單元格數",
alignLabel : "對齊方式",
rows : "行數",
cols : "列數",
aligns : ["默認", "左對齊", "居中對齊", "右對齊"]
}
}
},
"en" : {
toolbar : {
table : "Tables"
},
dialog : {
table : {
title : "Tables",
cellsLabel : "Cells",
alignLabel : "Align",
rows : "Rows",
cols : "Cols",
aligns : ["Default", "Left align", "Center align", "Right align"]
}
}
}
};
// 表格对话框插件主函数
exports.fn.tableDialog = function() {
// 获取当前编辑器实例的各种属性和方法
var _this = this;
var cm = this.cm; // CodeMirror实例
var editor = this.editor; // 编辑器DOM元素
var settings = this.settings; // 设置选项
var path = settings.path + "../plugins/" + pluginName +"/"; // 插件路径
var classPrefix = this.classPrefix; // CSS类前缀
var dialogName = classPrefix + pluginName, dialog; // 对话框名称
// 扩展语言包并设置工具栏
$.extend(true, this.lang, langs[this.lang.name]);
this.setToolbar();
// 获取语言包
var lang = this.lang;
var dialogLang = lang.dialog.table;
// 构建对话框内容
var dialogContent = [
"<div class=\"editormd-form\" style=\"padding: 13px 0;\">",
"<label>" + dialogLang.cellsLabel + "</label>",
dialogLang.rows + " <input type=\"number\" value=\"3\" class=\"number-input\" style=\"width:40px;\" max=\"100\" min=\"2\" data-rows />&nbsp;&nbsp;",
dialogLang.cols + " <input type=\"number\" value=\"2\" class=\"number-input\" style=\"width:40px;\" max=\"100\" min=\"1\" data-cols /><br/>",
"<label>" + dialogLang.alignLabel + "</label>",
"<div class=\"fa-btns\"></div>",
"</div>"
].join("\n");
// 如果对话框已存在,则显示
if (editor.find("." + dialogName).length > 0)
{
dialog = editor.find("." + dialogName);
this.dialogShowMask(dialog);
this.dialogLockScreen();
dialog.show();
}
else
{
dialog = this.createDialog({
name : dialogName,
title : dialogLang.title,
width : 360,
height : 226,
mask : settings.dialogShowMask,
drag : settings.dialogDraggable,
content : dialogContent,
lockScreen : settings.dialogLockScreen,
maskStyle : {
opacity : settings.dialogMaskOpacity,
backgroundColor : settings.dialogMaskBgColor
},
buttons : {
enter : [lang.buttons.enter, function() {
var rows = parseInt(this.find("[data-rows]").val());
var cols = parseInt(this.find("[data-cols]").val());
var align = this.find("[name=\"table-align\"]:checked").val();
var table = "";
var hrLine = "------------";
var alignSign = {
_default : hrLine,
left : ":" + hrLine,
center : ":" + hrLine + ":",
right : hrLine + ":"
};
if ( rows > 1 && cols > 0)
{
for (var r = 0, len = rows; r < len; r++)
{
var row = [];
var head = [];
for (var c = 0, len2 = cols; c < len2; c++)
{
if (r === 1) {
head.push(alignSign[align]);
}
row.push(" ");
}
if (r === 1) {
table += "| " + head.join(" | ") + " |" + "\n";
}
table += "| " + row.join( (cols === 1) ? "" : " | " ) + " |" + "\n";
}
}
cm.replaceSelection(table);
this.dialogShowMask(dialog); // 显示对话框遮罩
this.dialogLockScreen(); // 锁定屏幕
dialog.show(); // 显示对话框
}
else
{
// 创建对话框
dialog = this.createDialog({
name : dialogName, // 对话框名称
title : dialogLang.title, // 对话框标题
width : 360, // 对话框宽度
height : 226, // 对话框高度
mask : settings.dialogShowMask, // 是否显示遮罩
drag : settings.dialogDraggable, // 是否可拖拽
content : dialogContent, // 对话框内容
lockScreen : settings.dialogLockScreen, // 是否锁定屏幕
maskStyle : { // 遮罩样式
opacity : settings.dialogMaskOpacity, // 遮罩透明度
backgroundColor : settings.dialogMaskBgColor // 遮罩背景色
},
buttons : { // 对话框按钮
enter : [lang.buttons.enter, function() { // 确定按钮
var rows = parseInt(this.find("[data-rows]").val()); // 获取行数
var cols = parseInt(this.find("[data-cols]").val()); // 获取列数
var align = this.find("[name=\"table-align\"]:checked").val(); // 获取对齐方式
var table = ""; // 表格内容
var hrLine = "------------"; // 分隔线
// 对齐标记映射
var alignSign = {
_default : hrLine, // 默认对齐
left : ":" + hrLine, // 左对齐
center : ":" + hrLine + ":", // 居中对齐
right : hrLine + ":" // 右对齐
};
// 生成表格Markdown代码
if ( rows > 1 && cols > 0)
{
for (var r = 0, len = rows; r < len; r++)
{
var row = []; // 表格行
var head = []; // 表头行
for (var c = 0, len2 = cols; c < len2; c++)
{
// 第二行生成表头分隔线
if (r === 1) {
head.push(alignSign[align]);
}
row.push(" ");
}
// 添加表头分隔行
if (r === 1) {
table += "| " + head.join(" | ") + " |" + "\n";
}
// 添加普通行
table += "| " + row.join( (cols === 1) ? "" : " | " ) + " |" + "\n";
}
}
// 插入表格到编辑器
cm.replaceSelection(table);
// 隐藏对话框并解锁屏幕
this.hide().lockScreen(false).hideMask();
return false;
}],
cancel : [lang.buttons.cancel, function() {
cancel : [lang.buttons.cancel, function() { // 取消按钮
this.hide().lockScreen(false).hideMask();
return false;
}]
}
});
}
var faBtns = dialog.find(".fa-btns");
if (faBtns.html() === "")
{
var icons = ["align-justify", "align-left", "align-center", "align-right"];
var _lang = dialogLang.aligns;
var values = ["_default", "left", "center", "right"];
for (var i = 0, len = icons.length; i < len; i++)
{
var checked = (i === 0) ? " checked=\"checked\"" : "";
var btn = "<a href=\"javascript:;\"><label for=\"editormd-table-dialog-radio"+i+"\" title=\"" + _lang[i] + "\">";
btn += "<input type=\"radio\" name=\"table-align\" id=\"editormd-table-dialog-radio"+i+"\" value=\"" + values[i] + "\"" +checked + " />&nbsp;";
btn += "<i class=\"fa fa-" + icons[i] + "\"></i>";
btn += "</label></a>";
faBtns.append(btn);
}
}
};
};
}
});
}
// 获取对齐方式按钮容器
var faBtns = dialog.find(".fa-btns");
// 如果按钮容器为空,则创建对齐方式按钮
if (faBtns.html() === "")
{
// 图标列表
var icons = ["align-justify", "align-left", "align-center", "align-right"];
// 语言文本
var _lang = dialogLang.aligns;
// 值列表
var values = ["_default", "left", "center", "right"];
// 创建对齐方式按钮
for (var i = 0, len = icons.length; i < len; i++)
{
var checked = (i === 0) ? " checked=\"checked\"" : "";
var btn = "<a href=\"javascript:;\"><label for=\"editormd-table-dialog-radio"+i+"\" title=\"" + _lang[i] + "\">";
btn += "<input type=\"radio\" name=\"table-align\" id=\"editormd-table-dialog-radio"+i+"\" value=\"" + values[i] + "\"" +checked + " />&nbsp;";
btn += "<i class=\"fa fa-" + icons[i] + "\"></i>";
btn += "</label></a>";
faBtns.append(btn);
}
}
};
};
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
{
module.exports = factory;
}
else if (typeof define === "function") // AMD/CMD/Sea.js
else if (typeof define === "function") // AMD/CMD/Sea.js
{
if (define.amd) { // for Require.js
if (define.amd) { // for Require.js
define(["editormd"], function(editormd) {
define(["editormd"], function(editormd) {
factory(editormd);
});
} else { // for Sea.js
define(function(require) {
} else { // for Sea.js
define(function(require) {
var editormd = require("../../editormd");
factory(editormd);
});
}
}
else
{
}
}
else
{
factory(window.editormd);
}
}
})();
})();

@ -1,106 +1,120 @@
(function(){
// 工厂函数,用于定义德语语言包
// Factory function to define German language package
var factory = function (exports) {
// 定义德语语言配置对象
// Define German language configuration object
var lang = {
name : "de",
description : "Open source online Markdown editor.",
tocTitle : "Inhalt",
name : "de", // 语言名称 - Language name
description : "Open source online Markdown editor.", // 描述 - Description
tocTitle : "Inhalt", // 目录标题 - Table of contents title
toolbar : {
undo : "Rückgängig(Ctrl+Z)",
redo : "Wiederholen(Ctrl+Y)",
bold : "Fett",
del : "Durchgestrichen",
italic : "Kursiv",
quote : "Block Zitat",
ucwords : "Erster Buchstabe des Worts groß",
uppercase : "Auswahltext in Großbuchstaben konvertieren",
lowercase : "Auswahltext in Kleinbuchstaben konvertieren",
h1 : "Überschrift 1",
h2 : "Überschrift 2",
h3 : "Überschrift 3",
h4 : "Überschrift 4",
h5 : "Überschrift 5",
h6 : "Überschrift 6",
"list-ul" : "Ungeordnete Liste",
"list-ol" : "Geordnete Liste",
hr : "Horizontale Linie",
link : "Link",
"reference-link" : "Referenzlink",
image : "Bild",
code : "Code inline",
"preformatted-text" : "Vorformatierter Text / Codeblock (Tab Einrückung)",
"code-block" : "Codeblock (Multi-languages)",
table : "Tabellen",
datetime : "Datum & Zeit",
emoji : "Emoji",
"html-entities" : "HTML Entities",
pagebreak : "Seitenumbruch",
watch : "Unwatch",
unwatch : "Watch",
preview : "HTML Vorschau (Shift + ESC zum verlassen)",
fullscreen : "Vollbild (ESC zum verlassen)",
clear : "Löschen",
search : "Suchen",
help : "Hilfe",
info : "Über " + exports.title
// 工具栏各按钮的德语翻译
// German translations for toolbar buttons
undo : "Rückgängig(Ctrl+Z)", // 撤销
redo : "Wiederholen(Ctrl+Y)", // 重做
bold : "Fett", // 粗体
del : "Durchgestrichen", // 删除线
italic : "Kursiv", // 斜体
quote : "Block Zitat", // 块引用
ucwords : "Erster Buchstabe des Worts groß", // 单词首字母大写
uppercase : "Auswahltext in Großbuchstaben konvertieren", // 转换为大写
lowercase : "Auswahltext in Kleinbuchstaben konvertieren", // 转换为小写
h1 : "Überschrift 1", // 标题1
h2 : "Überschrift 2", // 标题2
h3 : "Überschrift 3", // 标题3
h4 : "Überschrift 4", // 标题4
h5 : "Überschrift 5", // 标题5
h6 : "Überschrift 6", // 标题6
"list-ul" : "Ungeordnete Liste", // 无序列表
"list-ol" : "Geordnete Liste", // 有序列表
hr : "Horizontale Linie", // 水平线
link : "Link", // 链接
"reference-link" : "Referenzlink", // 引用链接
image : "Bild", // 图片
code : "Code inline", // 行内代码
"preformatted-text" : "Vorformatierter Text / Codeblock (Tab Einrückung)", // 预格式化文本/代码块
"code-block" : "Codeblock (Multi-languages)", // 代码块(多语言)
table : "Tabellen", // 表格
datetime : "Datum & Zeit", // 日期时间
emoji : "Emoji", // 表情符号
"html-entities" : "HTML Entities", // HTML实体
pagebreak : "Seitenumbruch", // 分页符
watch : "Unwatch", // 关闭实时预览
unwatch : "Watch", // 开启实时预览
preview : "HTML Vorschau (Shift + ESC zum verlassen)", // HTML预览
fullscreen : "Vollbild (ESC zum verlassen)", // 全屏
clear : "Löschen", // 清除
search : "Suchen", // 搜索
help : "Hilfe", // 帮助
info : "Über " + exports.title // 关于信息
},
buttons : {
enter : "Eingeben",
cancel : "Abbrechen",
close : "Schließen"
// 对话框按钮文本
// Dialog button texts
enter : "Eingeben", // 确定按钮
cancel : "Abbrechen", // 取消按钮
close : "Schließen" // 关闭按钮
},
dialog : {
// 各种对话框的德语翻译
// German translations for various dialogs
link : {
title : "Link",
url : "Adresse",
urlTitle : "Titel",
urlEmpty : "Fehler: Bitte Link Adresse angeben."
title : "Link", // 链接对话框标题
url : "Adresse", // URL地址标签
urlTitle : "Titel", // 标题标签
urlEmpty : "Fehler: Bitte Link Adresse angeben." // URL为空时的错误提示
},
referenceLink : {
title : "Referenzlink",
name : "Name",
url : "Adresse",
urlId : "ID",
urlTitle : "Titel",
nameEmpty: "Fehler: Referenzlink Name kann nicht leer sein.",
idEmpty : "Fehler: Bitte Referenzlink ID angeben.",
urlEmpty : "Fehler: Bitte Referenzlink URL Adresse angeben."
title : "Referenzlink", // 引用链接对话框标题
name : "Name", // 名称标签
url : "Adresse", // URL地址标签
urlId : "ID", // ID标签
urlTitle : "Titel", // 标题标签
nameEmpty: "Fehler: Referenzlink Name kann nicht leer sein.", // 名称为空时的错误提示
idEmpty : "Fehler: Bitte Referenzlink ID angeben.", // ID为空时的错误提示
urlEmpty : "Fehler: Bitte Referenzlink URL Adresse angeben." // URL为空时的错误提示
},
image : {
title : "Bild",
url : "Adresse",
link : "Link",
alt : "Titel",
uploadButton : "Hochladen",
imageURLEmpty : "Fehler: Bild URL Adresse darf nciht leer sein.",
uploadFileEmpty : "Fehler: Bild darf nicht leer sein!",
formatNotAllowed : "Fehler: nur Bilddatei upload möglich. Bitte im Format:"
title : "Bild", // 图片对话框标题
url : "Adresse", // URL地址标签
link : "Link", // 链接标签
alt : "Titel", // 标题(alt属性)标签
uploadButton : "Hochladen", // 上传按钮
imageURLEmpty : "Fehler: Bild URL Adresse darf nciht leer sein.", // 图片URL为空时的错误提示
uploadFileEmpty : "Fehler: Bild darf nicht leer sein!", // 上传文件为空时的错误提示
formatNotAllowed : "Fehler: nur Bilddatei upload möglich. Bitte im Format:" // 文件格式不允许时的错误提示
},
preformattedText : {
title : "Vorformatierter Text / Codeblock ",
emptyAlert : "Fehler: Bitte den vorformatierten text oder Code Inhalt eingeben.",
placeholder : "Codiere jetzt ..."
title : "Vorformatierter Text / Codeblock ", // 预格式化文本对话框标题
emptyAlert : "Fehler: Bitte den vorformatierten text oder Code Inhalt eingeben.", // 内容为空时的错误提示
placeholder : "Codiere jetzt ..." // 占位符文本
},
codeBlock : {
title : "Codeblock",
selectLabel : "Sprachen: ",
selectDefaultText : "Programmiersprache wählen...",
otherLanguage : "Andere sprachen",
unselectedLanguageAlert : "Fehler: Bitte Programmiersprache wählen.",
codeEmptyAlert : "Fehler: Bitte den Code Inhalt eingeben.",
placeholder : "Codiere jetzt ..."
title : "Codeblock", // 代码块对话框标题
selectLabel : "Sprachen: ", // 语言选择标签
selectDefaultText : "Programmiersprache wählen...", // 默认选择文本
otherLanguage : "Andere sprachen", // 其他语言选项
unselectedLanguageAlert : "Fehler: Bitte Programmiersprache wählen.", // 未选择语言时的错误提示
codeEmptyAlert : "Fehler: Bitte den Code Inhalt eingeben.", // 代码内容为空时的错误提示
placeholder : "Codiere jetzt ..." // 占位符文本
},
htmlEntities : {
title : "HTML Entities"
title : "HTML Entities" // HTML实体对话框标题
},
help : {
title : "Hilfe"
title : "Hilfe" // 帮助对话框标题
}
}
};
// 将语言配置导出
// Export language configuration
exports.defaults.lang = lang;
};
// 根据不同的模块加载方式导出语言包
// Export language package according to different module loading methods
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
{

@ -1,106 +1,120 @@
(function(){
// 工厂函数,用于定义英语语言包
// Factory function to define English language package
var factory = function (exports) {
// 定义英语语言配置对象
// Define English language configuration object
var lang = {
name : "en",
description : "Open source online Markdown editor.",
tocTitle : "Table of Contents",
name : "en", // 语言名称 - Language name
description : "Open source online Markdown editor.", // 描述 - Description
tocTitle : "Table of Contents", // 目录标题 - Table of contents title
toolbar : {
undo : "Undo(Ctrl+Z)",
redo : "Redo(Ctrl+Y)",
bold : "Bold",
del : "Strikethrough",
italic : "Italic",
quote : "Block quote",
ucwords : "Words first letter convert to uppercase",
uppercase : "Selection text convert to uppercase",
lowercase : "Selection text convert to lowercase",
h1 : "Heading 1",
h2 : "Heading 2",
h3 : "Heading 3",
h4 : "Heading 4",
h5 : "Heading 5",
h6 : "Heading 6",
"list-ul" : "Unordered list",
"list-ol" : "Ordered list",
hr : "Horizontal rule",
link : "Link",
"reference-link" : "Reference link",
image : "Image",
code : "Code inline",
"preformatted-text" : "Preformatted text / Code block (Tab indent)",
"code-block" : "Code block (Multi-languages)",
table : "Tables",
datetime : "Datetime",
emoji : "Emoji",
"html-entities" : "HTML Entities",
pagebreak : "Page break",
watch : "Unwatch",
unwatch : "Watch",
preview : "HTML Preview (Press Shift + ESC exit)",
fullscreen : "Fullscreen (Press ESC exit)",
clear : "Clear",
search : "Search",
help : "Help",
info : "About " + exports.title
// 工具栏各按钮的英语翻译
// English translations for toolbar buttons
undo : "Undo(Ctrl+Z)", // 撤销
redo : "Redo(Ctrl+Y)", // 重做
bold : "Bold", // 粗体
del : "Strikethrough", // 删除线
italic : "Italic", // 斜体
quote : "Block quote", // 块引用
ucwords : "Words first letter convert to uppercase", // 单词首字母大写
uppercase : "Selection text convert to uppercase", // 转换为大写
lowercase : "Selection text convert to lowercase", // 转换为小写
h1 : "Heading 1", // 标题1
h2 : "Heading 2", // 标题2
h3 : "Heading 3", // 标题3
h4 : "Heading 4", // 标题4
h5 : "Heading 5", // 标题5
h6 : "Heading 6", // 标题6
"list-ul" : "Unordered list", // 无序列表
"list-ol" : "Ordered list", // 有序列表
hr : "Horizontal rule", // 水平线
link : "Link", // 链接
"reference-link" : "Reference link", // 引用链接
image : "Image", // 图片
code : "Code inline", // 行内代码
"preformatted-text" : "Preformatted text / Code block (Tab indent)", // 预格式化文本/代码块
"code-block" : "Code block (Multi-languages)", // 代码块(多语言)
table : "Tables", // 表格
datetime : "Datetime", // 日期时间
emoji : "Emoji", // 表情符号
"html-entities" : "HTML Entities", // HTML实体
pagebreak : "Page break", // 分页符
watch : "Unwatch", // 关闭实时预览
unwatch : "Watch", // 开启实时预览
preview : "HTML Preview (Press Shift + ESC exit)", // HTML预览
fullscreen : "Fullscreen (Press ESC exit)", // 全屏
clear : "Clear", // 清除
search : "Search", // 搜索
help : "Help", // 帮助
info : "About " + exports.title // 关于信息
},
buttons : {
enter : "Enter",
cancel : "Cancel",
close : "Close"
// 对话框按钮文本
// Dialog button texts
enter : "Enter", // 确定按钮
cancel : "Cancel", // 取消按钮
close : "Close" // 关闭按钮
},
dialog : {
// 各种对话框的英语翻译
// English translations for various dialogs
link : {
title : "Link",
url : "Address",
urlTitle : "Title",
urlEmpty : "Error: Please fill in the link address."
title : "Link", // 链接对话框标题
url : "Address", // URL地址标签
urlTitle : "Title", // 标题标签
urlEmpty : "Error: Please fill in the link address." // URL为空时的错误提示
},
referenceLink : {
title : "Reference link",
name : "Name",
url : "Address",
urlId : "ID",
urlTitle : "Title",
nameEmpty: "Error: Reference name can't be empty.",
idEmpty : "Error: Please fill in reference link id.",
urlEmpty : "Error: Please fill in reference link url address."
title : "Reference link", // 引用链接对话框标题
name : "Name", // 名称标签
url : "Address", // URL地址标签
urlId : "ID", // ID标签
urlTitle : "Title", // 标题标签
nameEmpty: "Error: Reference name can't be empty.", // 名称为空时的错误提示
idEmpty : "Error: Please fill in reference link id.", // ID为空时的错误提示
urlEmpty : "Error: Please fill in reference link url address." // URL为空时的错误提示
},
image : {
title : "Image",
url : "Address",
link : "Link",
alt : "Title",
uploadButton : "Upload",
imageURLEmpty : "Error: picture url address can't be empty.",
uploadFileEmpty : "Error: upload pictures cannot be empty!",
formatNotAllowed : "Error: only allows to upload pictures file, upload allowed image file format:"
title : "Image", // 图片对话框标题
url : "Address", // URL地址标签
link : "Link", // 链接标签
alt : "Title", // 标题(alt属性)标签
uploadButton : "Upload", // 上传按钮
imageURLEmpty : "Error: picture url address can't be empty.", // 图片URL为空时的错误提示
uploadFileEmpty : "Error: upload pictures cannot be empty!", // 上传文件为空时的错误提示
formatNotAllowed : "Error: only allows to upload pictures file, upload allowed image file format:" // 文件格式不允许时的错误提示
},
preformattedText : {
title : "Preformatted text / Codes",
emptyAlert : "Error: Please fill in the Preformatted text or content of the codes.",
placeholder : "coding now...."
title : "Preformatted text / Codes", // 预格式化文本对话框标题
emptyAlert : "Error: Please fill in the Preformatted text or content of the codes.", // 内容为空时的错误提示
placeholder : "coding now...." // 占位符文本
},
codeBlock : {
title : "Code block",
selectLabel : "Languages: ",
selectDefaultText : "select a code language...",
otherLanguage : "Other languages",
unselectedLanguageAlert : "Error: Please select the code language.",
codeEmptyAlert : "Error: Please fill in the code content.",
placeholder : "coding now...."
title : "Code block", // 代码块对话框标题
selectLabel : "Languages: ", // 语言选择标签
selectDefaultText : "select a code language...", // 默认选择文本
otherLanguage : "Other languages", // 其他语言选项
unselectedLanguageAlert : "Error: Please select the code language.", // 未选择语言时的错误提示
codeEmptyAlert : "Error: Please fill in the code content.", // 代码内容为空时的错误提示
placeholder : "coding now...." // 占位符文本
},
htmlEntities : {
title : "HTML Entities"
title : "HTML Entities" // HTML实体对话框标题
},
help : {
title : "Help"
title : "Help" // 帮助对话框标题
}
}
};
// 将语言配置导出
// Export language configuration
exports.defaults.lang = lang;
};
// 根据不同的模块加载方式导出语言包
// Export language package according to different module loading methods
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
{

@ -1,106 +1,120 @@
(function(){
// 工厂函数,用于定义西班牙语语言包
// Factory function to define Spanish language package
var factory = function (exports) {
// 定义西班牙语语言配置对象
// Define Spanish language configuration object
var lang = {
name : "ex",
description : "Open source online Markdown editor.",
tocTitle : "Índice",
name : "ex", // 语言名称 - Language name
description : "Open source online Markdown editor.", // 描述 - Description
tocTitle : "Índice", // 目录标题 - Table of contents title
toolbar : {
undo : "Deshacer (Ctrl+Z)",
redo : "Rehacer (Ctrl+Y)",
bold : "Bold",
del : "Tachado",
italic : "Cursiva",
quote : "Citar",
ucwords : "Primera letra de las en mayúsculas",
uppercase : "Convertir la selección a mayúsculas",
lowercase : "Convertir la selección a minúsculas",
h1 : "Título 1",
h2 : "Título 2",
h3 : "Título 3",
h4 : "Título 4",
h5 : "Título 5",
h6 : "Título 6",
"list-ul" : "Lista no ordenada",
"list-ol" : "Lista ordenada",
hr : "Línea horizontal",
link : "Enlace",
"reference-link" : "Referencia a enlace",
image : "Imagen",
code : "Código",
"preformatted-text" : "Texto preformateado / Bloque de código (Indentado por Tab)",
"code-block" : "Bloque de código (Multi-lenguaje)",
table : "Tables",
datetime : "Datetime",
emoji : "Emoji",
"html-entities" : "Entidades HTML",
pagebreak : "Salto de página",
watch : "Unwatch",
unwatch : "Watch",
preview : "Vista previa HTML (Shift + ESC para salir)",
fullscreen : "Pantalla completa (ESC para salir)",
clear : "Borrar",
search : "Buscar",
help : "Ayuda",
info : "Sobre " + exports.title
// 工具栏各按钮的西班牙语翻译
// Spanish translations for toolbar buttons
undo : "Deshacer (Ctrl+Z)", // 撤销
redo : "Rehacer (Ctrl+Y)", // 重做
bold : "Bold", // 粗体
del : "Tachado", // 删除线
italic : "Cursiva", // 斜体
quote : "Citar", // 引用
ucwords : "Primera letra de las en mayúsculas", // 单词首字母大写
uppercase : "Convertir la selección a mayúsculas", // 转换为大写
lowercase : "Convertir la selección a minúsculas", // 转换为小写
h1 : "Título 1", // 标题1
h2 : "Título 2", // 标题2
h3 : "Título 3", // 标题3
h4 : "Título 4", // 标题4
h5 : "Título 5", // 标题5
h6 : "Título 6", // 标题6
"list-ul" : "Lista no ordenada", // 无序列表
"list-ol" : "Lista ordenada", // 有序列表
hr : "Línea horizontal", // 水平线
link : "Enlace", // 链接
"reference-link" : "Referencia a enlace", // 引用链接
image : "Imagen", // 图片
code : "Código", // 代码
"preformatted-text" : "Texto preformateado / Bloque de código (Indentado por Tab)", // 预格式化文本/代码块
"code-block" : "Bloque de código (Multi-lenguaje)", // 代码块(多语言)
table : "Tables", // 表格
datetime : "Datetime", // 日期时间
emoji : "Emoji", // 表情符号
"html-entities" : "Entidades HTML", // HTML实体
pagebreak : "Salto de página", // 分页符
watch : "Unwatch", // 关闭实时预览
unwatch : "Watch", // 开启实时预览
preview : "Vista previa HTML (Shift + ESC para salir)", // HTML预览
fullscreen : "Pantalla completa (ESC para salir)", // 全屏
clear : "Borrar", // 清除
search : "Buscar", // 搜索
help : "Ayuda", // 帮助
info : "Sobre " + exports.title // 关于信息
},
buttons : {
enter : "Intro",
cancel : "Cancelar",
close : "Cerrar"
// 对话框按钮文本
// Dialog button texts
enter : "Intro", // 确定按钮
cancel : "Cancelar", // 取消按钮
close : "Cerrar" // 关闭按钮
},
dialog : {
// 各种对话框的西班牙语翻译
// Spanish translations for various dialogs
link : {
title : "Enlace",
url : "Dirección",
urlTitle : "Título",
urlEmpty : "Error: Introduzca la dirección del enlace."
title : "Enlace", // 链接对话框标题
url : "Dirección", // URL地址标签
urlTitle : "Título", // 标题标签
urlEmpty : "Error: Introduzca la dirección del enlace." // URL为空时的错误提示
},
referenceLink : {
title : "Referencia a enlace",
name : "Nombre",
url : "Dirección",
urlId : "ID",
urlTitle : "Título",
nameEmpty: "Error: El nombre no puede estar vacío.",
idEmpty : "Error: Introduzca un ID.",
urlEmpty : "Error: Introduzca una dirección URL."
title : "Referencia a enlace", // 引用链接对话框标题
name : "Nombre", // 名称标签
url : "Dirección", // URL地址标签
urlId : "ID", // ID标签
urlTitle : "Título", // 标题标签
nameEmpty: "Error: El nombre no puede estar vacío.", // 名称为空时的错误提示
idEmpty : "Error: Introduzca un ID.", // ID为空时的错误提示
urlEmpty : "Error: Introduzca una dirección URL." // URL为空时的错误提示
},
image : {
title : "Imagen",
url : "Dirección",
link : "Enlace",
alt : "Título",
uploadButton : "Cargar",
imageURLEmpty : "Error: La dirección URL de la imagen no pueder estar vacia.",
uploadFileEmpty : "Error: upload pictures cannot be empty!",
formatNotAllowed : "Error: Sólo se pueden cargar ficheros de imagen, los formatos permitidos son:"
title : "Imagen", // 图片对话框标题
url : "Dirección", // URL地址标签
link : "Enlace", // 链接标签
alt : "Título", // 标题(alt属性)标签
uploadButton : "Cargar", // 上传按钮
imageURLEmpty : "Error: La dirección URL de la imagen no pueder estar vacia.", // 图片URL为空时的错误提示
uploadFileEmpty : "Error: upload pictures cannot be empty!", // 上传文件为空时的错误提示
formatNotAllowed : "Error: Sólo se pueden cargar ficheros de imagen, los formatos permitidos son:" // 文件格式不允许时的错误提示
},
preformattedText : {
title : "Texto preformateado / Código",
emptyAlert : "Error: Introduzca el texto preformateado o el código.",
placeholder : "Código...."
title : "Texto preformateado / Código", // 预格式化文本对话框标题
emptyAlert : "Error: Introduzca el texto preformateado o el código.", // 内容为空时的错误提示
placeholder : "Código...." // 占位符文本
},
codeBlock : {
title : "Bloque de código",
selectLabel : "Lenguajes: ",
selectDefaultText : "Selecciona un lenguaje...",
otherLanguage : "Otros",
unselectedLanguageAlert : "Error: Selecciona un lenguaje.",
codeEmptyAlert : "Error: Introduce el código.",
placeholder : "Código...."
title : "Bloque de código", // 代码块对话框标题
selectLabel : "Lenguajes: ", // 语言选择标签
selectDefaultText : "Selecciona un lenguaje...", // 默认选择文本
otherLanguage : "Otros", // 其他语言选项
unselectedLanguageAlert : "Error: Selecciona un lenguaje.", // 未选择语言时的错误提示
codeEmptyAlert : "Error: Introduce el código.", // 代码内容为空时的错误提示
placeholder : "Código...." // 占位符文本
},
htmlEntities : {
title : "Entidades HTML"
title : "Entidades HTML" // HTML实体对话框标题
},
help : {
title : "Ayuda"
title : "Ayuda" // 帮助对话框标题
}
}
};
// 将语言配置导出
// Export language configuration
exports.defaults.lang = lang;
};
// 根据不同的模块加载方式导出语言包
// Export language package according to different module loading methods
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
{
@ -126,4 +140,4 @@
factory(window.editormd);
}
})();
})();

@ -1,106 +1,120 @@
(function(){
// 工厂函数,用于定义巴西葡萄牙语语言包
// Factory function to define Brazilian Portuguese language package
var factory = function (exports) {
// 定义巴西葡萄牙语语言配置对象
// Define Brazilian Portuguese language configuration object
var lang = {
name : "pt_BR",
description : "Open source online Markdown editor.",
tocTitle : "Índice",
name : "pt_BR", // 语言名称 - Language name
description : "Open source online Markdown editor.", // 描述 - Description
tocTitle : "Índice", // 目录标题 - Table of contents title
toolbar : {
undo : "Desfazer(Ctrl+Z)",
redo : "Refazer(Ctrl+Y)",
bold : "Negrito",
del : "Tachado",
italic : "Itálico",
quote : "Bloco de Citação",
ucwords : "Palavras com a primeira letra em maiúscula",
uppercase : "Texto em maiúsculas",
lowercase : "Texto em minúsculas",
h1 : "Título 1",
h2 : "Título 2",
h3 : "Título 3",
h4 : "Título 4",
h5 : "Título 5",
h6 : "Título 6",
"list-ul" : "Lista não ordenada",
"list-ol" : "Lista ordenada",
hr : "Linha Horizontal",
link : "Link",
"reference-link" : "Link de referência",
image : "Imagem",
code : "Código embutído",
"preformatted-text" : "Texto pré-formatado / Bloco de Código (Identado por Tab)",
"code-block" : "Bloco de Código (Multi-linguagens)",
table : "Tabelas",
datetime : "Data/hora",
emoji : "Emoji",
"html-entities" : "Elemento de HTML",
pagebreak : "Quebra de página",
watch : "Não Ver",
unwatch : "Ver",
preview : "HTML Preview (Pressione Shift + ESC para sair)",
fullscreen : "Tela Cheia (Pressione ESC para sair)",
clear : "Limpar",
search : "Procurar",
help : "Ajuda",
info : "Sobre " + exports.title
// 工具栏各按钮的巴西葡萄牙语翻译
// Brazilian Portuguese translations for toolbar buttons
undo : "Desfazer(Ctrl+Z)", // 撤销
redo : "Refazer(Ctrl+Y)", // 重做
bold : "Negrito", // 粗体
del : "Tachado", // 删除线
italic : "Itálico", // 斜体
quote : "Bloco de Citação", // 块引用
ucwords : "Palavras com a primeira letra em maiúscula", // 单词首字母大写
uppercase : "Texto em maiúsculas", // 转换为大写
lowercase : "Texto em minúsculas", // 转换为小写
h1 : "Título 1", // 标题1
h2 : "Título 2", // 标题2
h3 : "Título 3", // 标题3
h4 : "Título 4", // 标题4
h5 : "Título 5", // 标题5
h6 : "Título 6", // 标题6
"list-ul" : "Lista não ordenada", // 无序列表
"list-ol" : "Lista ordenada", // 有序列表
hr : "Linha Horizontal", // 水平线
link : "Link", // 链接
"reference-link" : "Link de referência", // 引用链接
image : "Imagem", // 图片
code : "Código embutído", // 行内代码
"preformatted-text" : "Texto pré-formatado / Bloco de Código (Identado por Tab)", // 预格式化文本/代码块
"code-block" : "Bloco de Código (Multi-linguagens)", // 代码块(多语言)
table : "Tabelas", // 表格
datetime : "Data/hora", // 日期时间
emoji : "Emoji", // 表情符号
"html-entities" : "Elemento de HTML", // HTML实体
pagebreak : "Quebra de página", // 分页符
watch : "Não Ver", // 关闭实时预览
unwatch : "Ver", // 开启实时预览
preview : "HTML Preview (Pressione Shift + ESC para sair)", // HTML预览
fullscreen : "Tela Cheia (Pressione ESC para sair)", // 全屏
clear : "Limpar", // 清除
search : "Procurar", // 搜索
help : "Ajuda", // 帮助
info : "Sobre " + exports.title // 关于信息
},
buttons : {
enter : "OK",
cancel : "Cancelar",
close : "Fechar"
// 对话框按钮文本
// Dialog button texts
enter : "OK", // 确定按钮
cancel : "Cancelar", // 取消按钮
close : "Fechar" // 关闭按钮
},
dialog : {
// 各种对话框的巴西葡萄牙语翻译
// Brazilian Portuguese translations for various dialogs
link : {
title : "Link",
url : "Endereço",
urlTitle : "Titulo",
urlEmpty : "Erro: Pro favor, preença o endereço do link."
title : "Link", // 链接对话框标题
url : "Endereço", // URL地址标签
urlTitle : "Titulo", // 标题标签
urlEmpty : "Erro: Pro favor, preença o endereço do link." // URL为空时的错误提示
},
referenceLink : {
title : "Link de referência",
name : "Nome",
url : "Endereço",
urlId : "ID",
urlTitle : "Titulo",
nameEmpty: "Erro: Nome de referência não pode estar vazio.",
idEmpty : "Erro: Por favor preencha o link de refeência com id válido.",
urlEmpty : "Erro: Por favor preencha o link com uma url de referência correta."
title : "Link de referência", // 引用链接对话框标题
name : "Nome", // 名称标签
url : "Endereço", // URL地址标签
urlId : "ID", // ID标签
urlTitle : "Titulo", // 标题标签
nameEmpty: "Erro: Nome de referência não pode estar vazio.", // 名称为空时的错误提示
idEmpty : "Erro: Por favor preencha o link de refeência com id válido.", // ID为空时的错误提示
urlEmpty : "Erro: Por favor preencha o link com uma url de referência correta." // URL为空时的错误提示
},
image : {
title : "Imagem",
url : "Endereço",
link : "Link",
alt : "Titulo",
uploadButton : "Envio",
imageURLEmpty : "Erro: Endereço da imagem não pode estar em branco.",
uploadFileEmpty : "Erro: É necessário enviar a imagem. Não pode estar vazio!",
formatNotAllowed : "Erro: Somente é possível enviar arquivos de figuras, Formatos permitidos:"
title : "Imagem", // 图片对话框标题
url : "Endereço", // URL地址标签
link : "Link", // 链接标签
alt : "Titulo", // 标题(alt属性)标签
uploadButton : "Envio", // 上传按钮
imageURLEmpty : "Erro: Endereço da imagem não pode estar em branco.", // 图片URL为空时的错误提示
uploadFileEmpty : "Erro: É necessário enviar a imagem. Não pode estar vazio!", // 上传文件为空时的错误提示
formatNotAllowed : "Erro: Somente é possível enviar arquivos de figuras, Formatos permitidos:" // 文件格式不允许时的错误提示
},
preformattedText : {
title : "Texto pré-formatado / Código",
emptyAlert : "Erro: Por favor preencher aqui com seu texto pré formatado / código.",
placeholder : "codificando agora...."
title : "Texto pré-formatado / Código", // 预格式化文本对话框标题
emptyAlert : "Erro: Por favor preencher aqui com seu texto pré formatado / código.", // 内容为空时的错误提示
placeholder : "codificando agora...." // 占位符文本
},
codeBlock : {
title : "Bloco de Código",
selectLabel : "Linguagens: ",
selectDefaultText : "selecione o tipo de linguagem ...",
otherLanguage : "Outras linguagenss",
unselectedLanguageAlert : "Erro: Por favor selecione a linguagem do código.",
codeEmptyAlert : "Erro: Por favor selecione o conteúdo de código.",
placeholder : "codificando agora...."
title : "Bloco de Código", // 代码块对话框标题
selectLabel : "Linguagens: ", // 语言选择标签
selectDefaultText : "selecione o tipo de linguagem ...", // 默认选择文本
otherLanguage : "Outras linguagenss", // 其他语言选项
unselectedLanguageAlert : "Erro: Por favor selecione a linguagem do código.", // 未选择语言时的错误提示
codeEmptyAlert : "Erro: Por favor selecione o conteúdo de código.", // 代码内容为空时的错误提示
placeholder : "codificando agora...." // 占位符文本
},
htmlEntities : {
title : "Elementos HTML"
title : "Elementos HTML" // HTML实体对话框标题
},
help : {
title : "Ajuda"
title : "Ajuda" // 帮助对话框标题
}
}
};
// 将语言配置导出
// Export language configuration
exports.defaults.lang = lang;
};
// 根据不同的模块加载方式导出语言包
// Export language package according to different module loading methods
// CommonJS/Node.js
if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
{
@ -126,4 +140,4 @@
factory(window.editormd);
}
})();
})();

@ -1,293 +1,352 @@
/* 代码高亮样式 */
.codehilite .hll {
background-color: #ffffcc
}
/* 代码块背景 */
.codehilite {
background: #ffffff;
}
/* 注释 */
.codehilite .c {
color: #177500
}
/* Comment */
/* 注释 */
/* Error */
.codehilite .err {
color: #000000
}
/* Error */
/* 错误 */
/* Keyword */
.codehilite .k {
color: #A90D91
}
/* Keyword */
/* 关键字 */
/* Literal */
.codehilite .l {
color: #1C01CE
}
/* Literal */
/* 字面量 */
/* Name */
.codehilite .n {
color: #000000
}
/* Name */
/* 名称 */
/* Operator */
.codehilite .o {
color: #000000
}
/* Operator */
/* 操作符 */
/* Comment.Hashbang */
.codehilite .ch {
color: #177500
}
/* Comment.Hashbang */
/* Hashbang注释 */
/* Comment.Multiline */
.codehilite .cm {
color: #177500
}
/* Comment.Multiline */
/* 多行注释 */
/* Comment.Preproc */
.codehilite .cp {
color: #633820
}
/* Comment.Preproc */
/* 预处理注释 */
/* Comment.PreprocFile */
.codehilite .cpf {
color: #177500
}
/* Comment.PreprocFile */
/* 文件预处理注释 */
/* Comment.Single */
.codehilite .c1 {
color: #177500
}
/* Comment.Single */
/* 单行注释 */
/* Comment.Special */
.codehilite .cs {
color: #177500
}
/* Comment.Special */
/* 特殊注释 */
/* Keyword.Constant */
.codehilite .kc {
color: #A90D91
}
/* Keyword.Constant */
/* 常量关键字 */
/* Keyword.Declaration */
.codehilite .kd {
color: #A90D91
}
/* Keyword.Declaration */
/* 声明关键字 */
/* Keyword.Namespace */
.codehilite .kn {
color: #A90D91
}
/* Keyword.Namespace */
/* 命名空间关键字 */
/* Keyword.Pseudo */
.codehilite .kp {
color: #A90D91
}
/* Keyword.Pseudo */
/* 伪关键字 */
/* Keyword.Reserved */
.codehilite .kr {
color: #A90D91
}
/* Keyword.Reserved */
/* 保留关键字 */
/* Keyword.Type */
.codehilite .kt {
color: #A90D91
}
/* Keyword.Type */
/* 类型关键字 */
/* Literal.Date */
.codehilite .ld {
color: #1C01CE
}
/* Literal.Date */
/* 日期字面量 */
/* Literal.Number */
.codehilite .m {
color: #1C01CE
}
/* Literal.Number */
/* 数字字面量 */
/* Literal.String */
.codehilite .s {
color: #C41A16
}
/* Literal.String */
/* 字符串字面量 */
/* Name.Attribute */
.codehilite .na {
color: #836C28
}
/* Name.Attribute */
/* 属性名称 */
/* Name.Builtin */
.codehilite .nb {
color: #A90D91
}
/* Name.Builtin */
/* 内置名称 */
/* Name.Class */
.codehilite .nc {
color: #3F6E75
}
/* Name.Class */
/* 类名 */
/* Name.Constant */
.codehilite .no {
color: #000000
}
/* Name.Constant */
/* 常量名称 */
/* Name.Decorator */
.codehilite .nd {
color: #000000
}
/* Name.Decorator */
/* 装饰器名称 */
/* Name.Entity */
.codehilite .ni {
color: #000000
}
/* Name.Entity */
/* 实体名称 */
/* Name.Exception */
.codehilite .ne {
color: #000000
}
/* Name.Exception */
/* 异常名称 */
/* Name.Function */
.codehilite .nf {
color: #000000
}
/* Name.Function */
/* 函数名称 */
/* Name.Label */
.codehilite .nl {
color: #000000
}
/* Name.Label */
/* 标签名称 */
/* Name.Namespace */
.codehilite .nn {
color: #000000
}
/* Name.Namespace */
/* 命名空间名称 */
/* Name.Other */
.codehilite .nx {
color: #000000
}
/* Name.Other */
/* 其他名称 */
/* Name.Property */
.codehilite .py {
color: #000000
}
/* Name.Property */
/* 属性名称 */
/* Name.Tag */
.codehilite .nt {
color: #000000
}
/* Name.Tag */
/* 标签名称 */
/* Name.Variable */
.codehilite .nv {
color: #000000
}
/* Name.Variable */
/* 变量名称 */
/* Operator.Word */
.codehilite .ow {
color: #000000
}
/* Operator.Word */
/* 操作符单词 */
/* Literal.Number.Bin */
.codehilite .mb {
color: #1C01CE
}
/* Literal.Number.Bin */
/* 二进制数字 */
/* Literal.Number.Float */
.codehilite .mf {
color: #1C01CE
}
/* Literal.Number.Float */
/* 浮点数 */
/* Literal.Number.Hex */
.codehilite .mh {
color: #1C01CE
}
/* Literal.Number.Hex */
/* 十六进制数 */
/* Literal.Number.Integer */
.codehilite .mi {
color: #1C01CE
}
/* Literal.Number.Integer */
/* 整数 */
/* Literal.Number.Oct */
.codehilite .mo {
color: #1C01CE
}
/* Literal.Number.Oct */
/* 八进制数 */
/* Literal.String.Backtick */
.codehilite .sb {
color: #C41A16
}
/* Literal.String.Backtick */
/* 反引号字符串 */
/* Literal.String.Char */
.codehilite .sc {
color: #2300CE
}
/* Literal.String.Char */
/* 字符 */
/* Literal.String.Doc */
.codehilite .sd {
color: #C41A16
}
/* Literal.String.Doc */
/* 文档字符串 */
/* Literal.String.Double */
.codehilite .s2 {
color: #C41A16
}
/* Literal.String.Double */
/* 双引号字符串 */
/* Literal.String.Escape */
.codehilite .se {
color: #C41A16
}
/* Literal.String.Escape */
/* 转义字符串 */
/* Literal.String.Heredoc */
.codehilite .sh {
color: #C41A16
}
/* Literal.String.Heredoc */
/* Heredoc字符串 */
/* Literal.String.Interpol */
.codehilite .si {
color: #C41A16
}
/* Literal.String.Interpol */
/* 插值字符串 */
/* Literal.String.Other */
.codehilite .sx {
color: #C41A16
}
/* Literal.String.Other */
/* 其他字符串 */
/* Literal.String.Regex */
.codehilite .sr {
color: #C41A16
}
/* Literal.String.Regex */
/* 正则表达式 */
/* Literal.String.Single */
.codehilite .s1 {
color: #C41A16
}
/* Literal.String.Single */
/* 单引号字符串 */
/* Literal.String.Symbol */
.codehilite .ss {
color: #C41A16
}
/* Literal.String.Symbol */
/* 符号 */
/* Name.Builtin.Pseudo */
.codehilite .bp {
color: #5B269A
}
/* Name.Builtin.Pseudo */
/* 伪内置名称 */
/* Name.Variable.Class */
.codehilite .vc {
color: #000000
}
/* Name.Variable.Class */
/* 类变量 */
/* Name.Variable.Global */
.codehilite .vg {
color: #000000
}
/* Name.Variable.Global */
/* 全局变量 */
/* Name.Variable.Instance */
.codehilite .vi {
color: #000000
}
/* Name.Variable.Instance */
/* 实例变量 */
/* Literal.Number.Integer.Long */
.codehilite .il {
color: #1C01CE
}
/* Literal.Number.Integer.Long */
/* 长整数 */

@ -5,19 +5,41 @@ from django.utils.translation import gettext_lazy as _
def disable_commentstatus(modeladmin, request, queryset):
"""
批量操作函数禁用选中的评论
Args:
modeladmin: 模型管理对象
request: HTTP请求对象
queryset: 选中的评论查询集
"""
queryset.update(is_enable=False)
def enable_commentstatus(modeladmin, request, queryset):
"""
批量操作函数启用选中的评论
Args:
modeladmin: 模型管理对象
request: HTTP请求对象
queryset: 选中的评论查询集
"""
queryset.update(is_enable=True)
# 设置批量操作的显示名称
disable_commentstatus.short_description = _('Disable comments')
enable_commentstatus.short_description = _('Enable comments')
class CommentAdmin(admin.ModelAdmin):
"""
评论模型在Django管理后台的配置类
"""
# 每页显示的评论数量
list_per_page = 20
# 后台列表页显示的字段
list_display = (
'id',
'body',
@ -25,12 +47,25 @@ class CommentAdmin(admin.ModelAdmin):
'link_to_article',
'is_enable',
'creation_time')
# 可点击进入详情页的字段
list_display_links = ('id', 'body', 'is_enable')
# 右侧过滤器字段
list_filter = ('is_enable',)
# 后台表单中隐藏的字段
exclude = ('creation_time', 'last_modify_time')
# 注册批量操作函数
actions = [disable_commentstatus, enable_commentstatus]
def link_to_userinfo(self, obj):
"""
在列表页中显示作者信息的链接
Args:
obj (Comment): 评论对象
Returns:
str: HTML格式的链接字符串
"""
info = (obj.author._meta.app_label, obj.author._meta.model_name)
link = reverse('admin:%s_%s_change' % info, args=(obj.author.id,))
return format_html(
@ -38,10 +73,20 @@ class CommentAdmin(admin.ModelAdmin):
(link, obj.author.nickname if obj.author.nickname else obj.author.email))
def link_to_article(self, obj):
"""
在列表页中显示所属文章的链接
Args:
obj (Comment): 评论对象
Returns:
str: HTML格式的链接字符串
"""
info = (obj.article._meta.app_label, obj.article._meta.model_name)
link = reverse('admin:%s_%s_change' % info, args=(obj.article.id,))
return format_html(
u'<a href="%s">%s</a>' % (link, obj.article.title))
# 设置字段显示名称
link_to_userinfo.short_description = _('User')
link_to_article.short_description = _('Article')
link_to_article.short_description = _('Article')

@ -2,4 +2,9 @@ from django.apps import AppConfig
class CommentsConfig(AppConfig):
name = 'comments'
"""
评论应用配置类
继承自Django的AppConfig类
"""
# 应用名称,必须与应用目录名一致
name = 'comments'

@ -5,9 +5,17 @@ from .models import Comment
class CommentForm(ModelForm):
"""
评论表单类
用于处理用户提交的评论数据
继承自ModelForm与Comment模型关联
"""
# 父评论ID隐藏字段用于处理评论回复功能可选
parent_comment_id = forms.IntegerField(
widget=forms.HiddenInput, required=False)
class Meta:
# 指定关联的模型
model = Comment
fields = ['body']
# 指定表单包含的字段只包含body字段
fields = ['body']

@ -1,4 +1,5 @@
# Generated by Django 4.1.7 on 2023-03-02 07:14
# 这是一个Django迁移文件用于创建评论(Comment)模型的初始数据库表结构
from django.conf import settings
from django.db import migrations, models
@ -7,32 +8,46 @@ import django.utils.timezone
class Migration(migrations.Migration):
# initial = True 表示这是一个初始迁移文件
initial = True
# dependencies 列表定义了此迁移依赖的其他迁移
# 'blog.0001_initial' 表示依赖blog应用的0001_initial迁移
# AUTH_USER_MODEL 表示依赖用户模型的迁移
dependencies = [
('blog', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
# operations 包含了实际要执行的数据库操作
operations = [
# 创建Comment模型对应的数据库表
migrations.CreateModel(
name='Comment',
name='Comment', # 模型名称
fields=[
# 主键字段,自动创建
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
# 评论正文最大长度300字符
('body', models.TextField(max_length=300, verbose_name='正文')),
# 创建时间,默认为当前时间
('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
# 最后修改时间,默认为当前时间
('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
# 是否显示该评论默认为True(显示)
('is_enable', models.BooleanField(default=True, verbose_name='是否显示')),
# 外键关联到blog应用的Article模型表示评论所属的文章
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.article', verbose_name='文章')),
# 外键关联到用户模型,表示评论的作者
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='作者')),
# 外键关联到自身的Comment模型表示上级评论(用于实现评论回复功能)
('parent_comment', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='comments.comment', verbose_name='上级评论')),
],
# 模型的元数据选项
options={
'verbose_name': '评论',
'verbose_name_plural': '评论',
'ordering': ['-id'],
'get_latest_by': 'id',
'verbose_name': '评论', # 单数形式的可读名称
'verbose_name_plural': '评论', # 复数形式的可读名称
'ordering': ['-id'], # 默认排序方式按id倒序排列
'get_latest_by': 'id', # 用于获取最新对象的字段
},
),
]
]

@ -1,18 +1,22 @@
# Generated by Django 4.1.7 on 2023-04-24 13:48
# 这是一个Django迁移文件用于修改Comment模型的is_enable字段默认值
from django.db import migrations, models
class Migration(migrations.Migration):
# 依赖的迁移文件表示此迁移必须在0001_initial.py之后执行
dependencies = [
('comments', '0001_initial'),
]
# operations 包含了实际要执行的数据库操作
operations = [
# 修改Comment模型的is_enable字段
migrations.AlterField(
model_name='comment',
name='is_enable',
model_name='comment', # 指定模型名称
name='is_enable', # 指定字段名称
# 将is_enable字段的默认值从True改为False
field=models.BooleanField(default=False, verbose_name='是否显示'),
),
]
]

@ -1,4 +1,9 @@
# Generated by Django 4.2.5 on 2023-09-06 13:13
# 这是一个Django迁移文件用于对Comment模型进行多项修改
# 1. 修改模型选项
# 2. 删除旧的时间字段
# 3. 添加新的时间字段
# 4. 修改字段的verbose_name为英文
from django.conf import settings
from django.db import migrations, models
@ -7,54 +12,69 @@ import django.utils.timezone
class Migration(migrations.Migration):
# 依赖的迁移文件列表,当前迁移必须在这些迁移之后执行
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('blog', '0005_alter_article_options_alter_category_options_and_more'),
('comments', '0002_alter_comment_is_enable'),
]
# operations 包含了实际要执行的数据库操作列表
operations = [
# 修改Comment模型的选项设置
migrations.AlterModelOptions(
name='comment',
options={'get_latest_by': 'id', 'ordering': ['-id'], 'verbose_name': 'comment', 'verbose_name_plural': 'comment'},
name='comment', # 指定模型名称
options={
'get_latest_by': 'id', # 用于获取最新对象的字段
'ordering': ['-id'], # 默认排序方式按id倒序排列
'verbose_name': 'comment', # 单数形式的可读名称(已改为英文)
'verbose_name_plural': 'comment' # 复数形式的可读名称(已改为英文)
},
),
# 删除Comment模型中的created_time字段
migrations.RemoveField(
model_name='comment',
name='created_time',
),
# 删除Comment模型中的last_mod_time字段
migrations.RemoveField(
model_name='comment',
name='last_mod_time',
),
# 添加新的creation_time字段用于记录创建时间
migrations.AddField(
model_name='comment',
name='creation_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
),
# 添加新的last_modify_time字段用于记录最后修改时间
migrations.AddField(
model_name='comment',
name='last_modify_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'),
),
# 修改article外键字段的verbose_name为英文
migrations.AlterField(
model_name='comment',
name='article',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.article', verbose_name='article'),
),
# 修改author外键字段的verbose_name为英文
migrations.AlterField(
model_name='comment',
name='author',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author'),
),
# 修改is_enable字段的verbose_name为英文
migrations.AlterField(
model_name='comment',
name='is_enable',
field=models.BooleanField(default=False, verbose_name='enable'),
),
# 修改parent_comment外键字段的verbose_name为英文
migrations.AlterField(
model_name='comment',
name='parent_comment',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='comments.comment', verbose_name='parent comment'),
),
]
]

@ -9,31 +9,46 @@ from blog.models import Article
# Create your models here.
class Comment(models.Model):
"""
评论模型类
用于存储用户对文章的评论信息
"""
# 评论正文最大长度300字符
body = models.TextField('正文', max_length=300)
# 创建时间,默认为当前时间
creation_time = models.DateTimeField(_('creation time'), default=now)
# 最后修改时间,默认为当前时间
last_modify_time = models.DateTimeField(_('last modify time'), default=now)
# 评论作者,外键关联到用户模型
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
verbose_name=_('author'),
on_delete=models.CASCADE)
# 所属文章,外键关联到文章模型
article = models.ForeignKey(
Article,
verbose_name=_('article'),
on_delete=models.CASCADE)
# 父评论,用于实现评论回复功能,可以为空
parent_comment = models.ForeignKey(
'self',
verbose_name=_('parent comment'),
blank=True,
null=True,
on_delete=models.CASCADE)
# 是否启用显示该评论默认为False需要审核
is_enable = models.BooleanField(_('enable'),
default=False, blank=False, null=False)
class Meta:
ordering = ['-id']
verbose_name = _('comment')
verbose_name_plural = verbose_name
get_latest_by = 'id'
# 模型元数据选项
ordering = ['-id'] # 默认按ID倒序排列
verbose_name = _('comment') # 单数形式的可读名称
verbose_name_plural = verbose_name # 复数形式的可读名称
get_latest_by = 'id' # 用于获取最新对象的字段
def __str__(self):
return self.body
"""
返回评论的字符串表示形式返回评论正文
"""
return self.body

@ -1,19 +1,37 @@
from django import template
# 注册模板标签
register = template.Library()
@register.simple_tag
def parse_commenttree(commentlist, comment):
"""获得当前评论子评论的列表
用法: {% parse_commenttree article_comments comment as childcomments %}
"""
解析获取当前评论的所有子评论回复
用法: {% parse_commenttree article_comments comment as childcomments %}
Args:
commentlist: 评论列表查询集
comment: 父评论对象
Returns:
list: 包含所有子评论的列表
"""
datas = []
def parse(c):
"""
递归解析评论的子评论
Args:
c: 当前评论对象
"""
# 筛选出当前评论的已启用子评论
childs = commentlist.filter(parent_comment=c, is_enable=True)
for child in childs:
datas.append(child)
# 递归处理子评论的子评论
parse(child)
parse(comment)
@ -22,9 +40,19 @@ def parse_commenttree(commentlist, comment):
@register.inclusion_tag('comments/tags/comment_item.html')
def show_comment_item(comment, ischild):
"""评论"""
"""
显示单个评论项的 inclusion tag
Args:
comment: 评论对象
ischild: 是否为子评论回复
Returns:
dict: 传递给模板的上下文数据
"""
# 设置评论显示深度子评论深度为1父评论深度为2
depth = 1 if ischild else 2
return {
'comment_item': comment,
'depth': depth
}
}

@ -11,7 +11,16 @@ from djangoblog.utils import get_max_articleid_commentid
# Create your tests here.
class CommentsTest(TransactionTestCase):
"""
评论功能测试类
使用TransactionTestCase以确保测试方法之间数据库事务的隔离性
"""
def setUp(self):
"""
测试前的准备工作
初始化测试客户端请求工厂和创建测试用户
"""
self.client = Client()
self.factory = RequestFactory()
from blog.models import BlogSettings
@ -25,18 +34,31 @@ class CommentsTest(TransactionTestCase):
password="liangliangyy1")
def update_article_comment_status(self, article):
"""
更新文章的评论状态为启用
Args:
article (Article): 文章对象
"""
comments = article.comment_set.all()
for comment in comments:
comment.is_enable = True
comment.save()
def test_validate_comment(self):
"""
验证评论功能的测试方法
测试评论提交显示和回复等功能
"""
# 用户登录
self.client.login(username='liangliangyy1', password='liangliangyy1')
# 创建测试分类
category = Category()
category.name = "categoryccc"
category.save()
# 创建测试文章
article = Article()
article.title = "nicetitleccc"
article.body = "nicecontentccc"
@ -46,23 +68,30 @@ class CommentsTest(TransactionTestCase):
article.status = 'p'
article.save()
# 获取评论提交URL
comment_url = reverse(
'comments:postcomment', kwargs={
'article_id': article.id})
# 提交第一条评论
response = self.client.post(comment_url,
{
'body': '123ffffffffff'
})
# 检查重定向响应
self.assertEqual(response.status_code, 302)
# 获取更新后的文章对象,检查评论列表为空(因为评论需要审核)
article = Article.objects.get(pk=article.pk)
self.assertEqual(len(article.comment_list()), 0)
# 更新评论状态为启用
self.update_article_comment_status(article)
# 检查评论列表长度为1
self.assertEqual(len(article.comment_list()), 1)
# 提交第二条评论
response = self.client.post(comment_url,
{
'body': '123ffffffffff',
@ -70,11 +99,14 @@ class CommentsTest(TransactionTestCase):
self.assertEqual(response.status_code, 302)
# 更新评论状态并检查评论数量
article = Article.objects.get(pk=article.pk)
self.update_article_comment_status(article)
self.assertEqual(len(article.comment_list()), 2)
# 获取第一条评论ID作为父评论ID
parent_comment_id = article.comment_list()[0].id
# 提交回复评论(带格式的评论内容)
response = self.client.post(comment_url,
{
'body': '''
@ -96,14 +128,21 @@ class CommentsTest(TransactionTestCase):
self.assertEqual(response.status_code, 302)
self.update_article_comment_status(article)
article = Article.objects.get(pk=article.pk)
# 检查评论总数为32条普通评论+1条回复评论
self.assertEqual(len(article.comment_list()), 3)
# 获取父评论对象
comment = Comment.objects.get(id=parent_comment_id)
# 解析评论树结构
tree = parse_commenttree(article.comment_list(), comment)
# 检查回复评论数量为1
self.assertEqual(len(tree), 1)
# 测试显示评论项功能
data = show_comment_item(comment, True)
self.assertIsNotNone(data)
# 测试获取最大文章ID和评论ID功能
s = get_max_articleid_commentid()
self.assertIsNotNone(s)
# 测试发送评论邮件功能
from comments.utils import send_comment_email
send_comment_email(comment)
send_comment_email(comment)

@ -2,10 +2,13 @@ from django.urls import path
from . import views
# 定义命名空间用于区分不同应用的URL名称
app_name = "comments"
urlpatterns = [
# 评论提交URL
# 使用正则表达式捕获文章ID作为参数传递给视图函数
path(
'article/<int:article_id>/postcomment',
views.CommentPostView.as_view(),
name='postcomment'),
]
]

@ -9,9 +9,23 @@ logger = logging.getLogger(__name__)
def send_comment_email(comment):
"""
发送评论相关邮件通知
当用户发表评论后系统会发送邮件通知
1. 给评论作者发送感谢邮件
2. 如果是回复评论则给被回复的评论作者发送通知邮件
Args:
comment (Comment): 评论对象
"""
# 获取当前站点域名
site = get_current_site().domain
# 邮件主题
subject = _('Thanks for your comment')
# 构造文章链接
article_url = f"https://{site}{comment.article.get_absolute_url()}"
# 邮件内容 - 给评论作者的感谢邮件
html_content = _("""<p>Thank you very much for your comments on this site</p>
You can visit <a href="%(article_url)s" rel="bookmark">%(article_title)s</a>
to review your comments,
@ -19,9 +33,12 @@ def send_comment_email(comment):
<br />
If the link above cannot be opened, please copy this link to your browser.
%(article_url)s""") % {'article_url': article_url, 'article_title': comment.article.title}
# 获取评论作者邮箱
tomail = comment.author.email
# 发送邮件
send_email([tomail], subject, html_content)
try:
# 如果是回复评论,给被回复的评论作者发送通知邮件
if comment.parent_comment:
html_content = _("""Your comment on <a href="%(article_url)s" rel="bookmark">%(article_title)s</a><br/> has
received a reply. <br/> %(comment_body)s
@ -32,7 +49,10 @@ def send_comment_email(comment):
%(article_url)s
""") % {'article_url': article_url, 'article_title': comment.article.title,
'comment_body': comment.parent_comment.body}
# 获取被回复评论的作者邮箱
tomail = comment.parent_comment.author.email
# 发送邮件通知
send_email([tomail], subject, html_content)
except Exception as e:
logger.error(e)
# 记录异常日志
logger.error(e)

@ -13,51 +13,104 @@ from .models import Comment
class CommentPostView(FormView):
"""
处理评论提交的视图类
继承自FormView用于处理基于表单的评论提交
"""
# 指定使用的表单类
form_class = CommentForm
# 指定模板名称
template_name = 'blog/article_detail.html'
@method_decorator(csrf_protect)
def dispatch(self, *args, **kwargs):
"""
视图函数调度方法添加CSRF保护装饰器
"""
return super(CommentPostView, self).dispatch(*args, **kwargs)
def get(self, request, *args, **kwargs):
"""
处理GET请求重定向到文章详情页的评论部分
Args:
request: HTTP请求对象
Returns:
HttpResponseRedirect: 重定向响应
"""
# 从URL参数中获取文章ID
article_id = self.kwargs['article_id']
# 获取文章对象如果不存在则返回404
article = get_object_or_404(Article, pk=article_id)
# 获取文章的绝对URL
url = article.get_absolute_url()
# 重定向到文章详情页的评论部分
return HttpResponseRedirect(url + "#comments")
def form_invalid(self, form):
"""
处理表单验证失败的情况
Args:
form: 验证失败的表单对象
Returns:
TemplateResponse: 渲染模板的响应
"""
# 获取文章ID和文章对象
article_id = self.kwargs['article_id']
article = get_object_or_404(Article, pk=article_id)
# 重新渲染页面并显示错误信息
return self.render_to_response({
'form': form,
'article': article
})
def form_valid(self, form):
"""提交的数据验证合法后的逻辑"""
"""
提交的数据验证合法后的逻辑处理
Args:
form: 验证通过的表单对象
Returns:
HttpResponseRedirect: 重定向响应
"""
# 获取当前登录用户
user = self.request.user
# 获取作者对象
author = BlogUser.objects.get(pk=user.pk)
# 获取文章ID和文章对象
article_id = self.kwargs['article_id']
article = get_object_or_404(Article, pk=article_id)
# 检查文章是否关闭了评论功能
if article.comment_status == 'c' or article.status == 'c':
raise ValidationError("该文章评论已关闭.")
# 保存表单数据但不提交到数据库
comment = form.save(False)
# 关联评论与文章
comment.article = article
# 获取博客设置
from djangoblog.utils import get_blog_setting
settings = get_blog_setting()
# 根据设置决定是否需要审核评论
if not settings.comment_need_review:
comment.is_enable = True
# 关联评论与作者
comment.author = author
# 处理评论回复逻辑
if form.cleaned_data['parent_comment_id']:
parent_comment = Comment.objects.get(
pk=form.cleaned_data['parent_comment_id'])
comment.parent_comment = parent_comment
# 保存评论到数据库
comment.save(True)
# 重定向到评论位置锚点
return HttpResponseRedirect(
"%s#div-comment-%d" %
(article.get_absolute_url(), comment.pk))
(article.get_absolute_url(), comment.pk))
Loading…
Cancel
Save