Compare commits
No commits in common. 'wy_branch' and 'master' have entirely different histories.
@ -1,176 +0,0 @@
|
||||
name: 自动部署到生产环境
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Django CI"]
|
||||
types:
|
||||
- completed
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
environment:
|
||||
description: '部署环境'
|
||||
required: true
|
||||
default: 'production'
|
||||
type: choice
|
||||
options:
|
||||
- production
|
||||
- staging
|
||||
image_tag:
|
||||
description: '镜像标签 (默认: latest)'
|
||||
required: false
|
||||
default: 'latest'
|
||||
type: string
|
||||
skip_tests:
|
||||
description: '跳过测试直接部署'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
env:
|
||||
REGISTRY: registry.cn-shenzhen.aliyuncs.com
|
||||
IMAGE_NAME: liangliangyy/djangoblog
|
||||
NAMESPACE: djangoblog
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: 构建镜像并部署到生产环境
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
|
||||
|
||||
steps:
|
||||
- name: 检出代码
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 设置部署参数
|
||||
id: deploy-params
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
echo "trigger_type=手动触发" >> $GITHUB_OUTPUT
|
||||
echo "environment=${{ github.event.inputs.environment }}" >> $GITHUB_OUTPUT
|
||||
echo "image_tag=${{ github.event.inputs.image_tag }}" >> $GITHUB_OUTPUT
|
||||
echo "skip_tests=${{ github.event.inputs.skip_tests }}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "trigger_type=CI自动触发" >> $GITHUB_OUTPUT
|
||||
echo "environment=production" >> $GITHUB_OUTPUT
|
||||
echo "image_tag=latest" >> $GITHUB_OUTPUT
|
||||
echo "skip_tests=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: 显示部署信息
|
||||
run: |
|
||||
echo "🚀 部署信息:"
|
||||
echo " 触发方式: ${{ steps.deploy-params.outputs.trigger_type }}"
|
||||
echo " 部署环境: ${{ steps.deploy-params.outputs.environment }}"
|
||||
echo " 镜像标签: ${{ steps.deploy-params.outputs.image_tag }}"
|
||||
echo " 跳过测试: ${{ steps.deploy-params.outputs.skip_tests }}"
|
||||
|
||||
- name: 设置Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: 登录私有镜像仓库
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- name: 提取镜像元数据
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=sha,prefix={{branch}}-
|
||||
type=raw,value=${{ steps.deploy-params.outputs.image_tag }}
|
||||
|
||||
- name: 构建并推送Docker镜像
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
platforms: linux/amd64
|
||||
|
||||
- name: 部署到生产服务器
|
||||
uses: appleboy/ssh-action@v1.0.3
|
||||
with:
|
||||
host: ${{ secrets.PRODUCTION_HOST }}
|
||||
username: ${{ secrets.PRODUCTION_USER }}
|
||||
key: ${{ secrets.PRODUCTION_SSH_KEY }}
|
||||
port: ${{ secrets.PRODUCTION_PORT || 22 }}
|
||||
script: |
|
||||
echo "🚀 开始部署 DjangoBlog..."
|
||||
|
||||
# 检查kubectl是否可用
|
||||
if ! command -v kubectl &> /dev/null; then
|
||||
echo "❌ 错误: kubectl 未安装或不在PATH中"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查命名空间是否存在
|
||||
if ! kubectl get namespace ${{ env.NAMESPACE }} &> /dev/null; then
|
||||
echo "❌ 错误: 命名空间 ${{ env.NAMESPACE }} 不存在"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 更新deployment镜像
|
||||
echo "📦 更新deployment镜像为: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.deploy-params.outputs.image_tag }}"
|
||||
kubectl set image deployment/djangoblog \
|
||||
djangoblog=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.deploy-params.outputs.image_tag }} \
|
||||
-n ${{ env.NAMESPACE }}
|
||||
|
||||
# 重启deployment
|
||||
echo "🔄 重启deployment..."
|
||||
kubectl -n ${{ env.NAMESPACE }} rollout restart deployment djangoblog
|
||||
|
||||
# 等待deployment完成
|
||||
echo "⏳ 等待deployment完成..."
|
||||
kubectl rollout status deployment/djangoblog -n ${{ env.NAMESPACE }} --timeout=300s
|
||||
|
||||
# 检查deployment状态
|
||||
echo "✅ 检查deployment状态..."
|
||||
kubectl get deployment djangoblog -n ${{ env.NAMESPACE }}
|
||||
kubectl get pods -l app=djangoblog -n ${{ env.NAMESPACE }}
|
||||
|
||||
echo "🎉 部署完成!"
|
||||
|
||||
- name: 发送部署通知
|
||||
if: always()
|
||||
run: |
|
||||
# 设置通知内容
|
||||
if [ "${{ job.status }}" = "success" ]; then
|
||||
TITLE="✅ DjangoBlog部署成功"
|
||||
STATUS="成功"
|
||||
else
|
||||
TITLE="❌ DjangoBlog部署失败"
|
||||
STATUS="失败"
|
||||
fi
|
||||
|
||||
MESSAGE="部署状态: ${STATUS}
|
||||
触发方式: ${{ steps.deploy-params.outputs.trigger_type }}
|
||||
部署环境: ${{ steps.deploy-params.outputs.environment }}
|
||||
镜像标签: ${{ steps.deploy-params.outputs.image_tag }}
|
||||
提交者: ${{ github.actor }}
|
||||
时间: $(date '+%Y-%m-%d %H:%M:%S')
|
||||
|
||||
查看详情: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||
|
||||
# 发送Server酱通知
|
||||
if [ -n "${{ secrets.SERVERCHAN_KEY }}" ]; then
|
||||
echo "{\"title\": \"${TITLE}\", \"desp\": \"${MESSAGE}\"}" > /tmp/serverchan.json
|
||||
|
||||
curl --location "https://sctapi.ftqq.com/${{ secrets.SERVERCHAN_KEY }}.send" \
|
||||
--header "Content-Type: application/json" \
|
||||
--data @/tmp/serverchan.json \
|
||||
--silent > /dev/null
|
||||
|
||||
rm -f /tmp/serverchan.json
|
||||
echo "📱 部署通知已发送"
|
||||
fi
|
||||
@ -1,371 +0,0 @@
|
||||
name: Django CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- dev
|
||||
paths-ignore:
|
||||
- '**/*.md'
|
||||
- '**/*.css'
|
||||
- '**/*.js'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- dev
|
||||
paths-ignore:
|
||||
- '**/*.md'
|
||||
- '**/*.css'
|
||||
- '**/*.js'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# 标准测试 - Python 3.10
|
||||
- python-version: "3.10"
|
||||
test-type: "standard"
|
||||
database: "mysql"
|
||||
elasticsearch: false
|
||||
coverage: false
|
||||
|
||||
# 标准测试 - Python 3.11
|
||||
- python-version: "3.11"
|
||||
test-type: "standard"
|
||||
database: "mysql"
|
||||
elasticsearch: false
|
||||
coverage: false
|
||||
|
||||
# 完整测试 - 包含ES和覆盖率
|
||||
- python-version: "3.11"
|
||||
test-type: "full"
|
||||
database: "mysql"
|
||||
elasticsearch: true
|
||||
coverage: true
|
||||
|
||||
# Docker构建测试
|
||||
- python-version: "3.11"
|
||||
test-type: "docker"
|
||||
database: "none"
|
||||
elasticsearch: false
|
||||
coverage: false
|
||||
|
||||
name: Test (${{ matrix.test-type }}, Python ${{ matrix.python-version }})
|
||||
|
||||
steps:
|
||||
- name: Checkout代码
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 设置测试信息
|
||||
id: test-info
|
||||
run: |
|
||||
echo "test_name=${{ matrix.test-type }}-py${{ matrix.python-version }}" >> $GITHUB_OUTPUT
|
||||
if [ "${{ matrix.test-type }}" = "docker" ]; then
|
||||
echo "skip_python_setup=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "skip_python_setup=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
# MySQL数据库设置 (只有需要数据库的测试才执行)
|
||||
- name: 启动MySQL数据库
|
||||
if: matrix.database == 'mysql'
|
||||
uses: samin/mysql-action@v1.3
|
||||
with:
|
||||
host port: 3306
|
||||
container port: 3306
|
||||
character set server: utf8mb4
|
||||
collation server: utf8mb4_general_ci
|
||||
mysql version: latest
|
||||
mysql root password: root
|
||||
mysql database: djangoblog
|
||||
mysql user: root
|
||||
mysql password: root
|
||||
|
||||
# Elasticsearch设置 (只有完整测试才执行)
|
||||
- name: 配置系统参数 (ES)
|
||||
if: matrix.elasticsearch == true
|
||||
run: |
|
||||
sudo swapoff -a
|
||||
sudo sysctl -w vm.swappiness=1
|
||||
sudo sysctl -w fs.file-max=262144
|
||||
sudo sysctl -w vm.max_map_count=262144
|
||||
|
||||
- name: 启动Elasticsearch
|
||||
if: matrix.elasticsearch == true
|
||||
uses: miyataka/elasticsearch-github-actions@1
|
||||
with:
|
||||
stack-version: '7.12.1'
|
||||
plugins: 'https://release.infinilabs.com/analysis-ik/stable/elasticsearch-analysis-ik-7.12.1.zip'
|
||||
|
||||
# Python环境设置 (Docker测试跳过)
|
||||
- name: 设置Python ${{ matrix.python-version }}
|
||||
if: steps.test-info.outputs.skip_python_setup == 'false'
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'requirements.txt'
|
||||
|
||||
# 多层缓存策略优化
|
||||
- name: 缓存Python依赖
|
||||
if: steps.test-info.outputs.skip_python_setup == 'false'
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/pip
|
||||
.pytest_cache
|
||||
key: ${{ runner.os }}-python-${{ matrix.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('**/pyproject.toml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-python-${{ matrix.python-version }}-${{ hashFiles('requirements.txt') }}-
|
||||
${{ runner.os }}-python-${{ matrix.python-version }}-
|
||||
${{ runner.os }}-python-
|
||||
|
||||
# Django缓存优化 (测试数据库等)
|
||||
- name: 缓存Django资源
|
||||
if: matrix.test-type != 'docker'
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.coverage*
|
||||
htmlcov/
|
||||
.django_cache/
|
||||
key: ${{ runner.os }}-django-${{ matrix.test-type }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-django-${{ matrix.test-type }}-
|
||||
${{ runner.os }}-django-
|
||||
|
||||
- name: 安装Python依赖
|
||||
if: steps.test-info.outputs.skip_python_setup == 'false'
|
||||
run: |
|
||||
echo "📦 安装Python依赖 (Python ${{ matrix.python-version }})"
|
||||
python -m pip install --upgrade pip setuptools wheel
|
||||
|
||||
# 安装基础依赖
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 根据测试类型安装额外依赖
|
||||
if [ "${{ matrix.coverage }}" = "true" ]; then
|
||||
echo "📊 安装覆盖率工具"
|
||||
pip install coverage[toml]
|
||||
fi
|
||||
|
||||
# 验证关键依赖
|
||||
echo "🔍 验证关键依赖安装"
|
||||
python -c "import django; print(f'Django version: {django.get_version()}')"
|
||||
python -c "import MySQLdb; print('MySQL client: OK')" || python -c "import pymysql; print('PyMySQL client: OK')"
|
||||
|
||||
if [ "${{ matrix.elasticsearch }}" = "true" ]; then
|
||||
python -c "import elasticsearch; print('Elasticsearch client: OK')"
|
||||
fi
|
||||
|
||||
# Django环境准备
|
||||
- name: 准备Django环境
|
||||
if: matrix.test-type != 'docker'
|
||||
env:
|
||||
DJANGO_MYSQL_PASSWORD: root
|
||||
DJANGO_MYSQL_HOST: 127.0.0.1
|
||||
DJANGO_ELASTICSEARCH_HOST: ${{ matrix.elasticsearch && '127.0.0.1:9200' || '' }}
|
||||
run: |
|
||||
echo "🔧 准备Django测试环境"
|
||||
|
||||
# 等待数据库就绪
|
||||
echo "⏳ 等待MySQL数据库启动..."
|
||||
for i in {1..30}; do
|
||||
if python -c "import MySQLdb; MySQLdb.connect(host='127.0.0.1', user='root', passwd='root', db='djangoblog')" 2>/dev/null; then
|
||||
echo "✅ MySQL数据库连接成功"
|
||||
break
|
||||
fi
|
||||
echo "🔄 等待数据库启动... ($i/30)"
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# 等待Elasticsearch就绪 (如果启用)
|
||||
if [ "${{ matrix.elasticsearch }}" = "true" ]; then
|
||||
echo "⏳ 等待Elasticsearch启动..."
|
||||
for i in {1..30}; do
|
||||
if curl -s http://127.0.0.1:9200/_cluster/health | grep -q '"status":"green"\|"status":"yellow"'; then
|
||||
echo "✅ Elasticsearch连接成功"
|
||||
break
|
||||
fi
|
||||
echo "🔄 等待Elasticsearch启动... ($i/30)"
|
||||
sleep 2
|
||||
done
|
||||
fi
|
||||
|
||||
# Django测试执行
|
||||
- name: 执行数据库迁移
|
||||
if: matrix.test-type != 'docker'
|
||||
env:
|
||||
DJANGO_MYSQL_PASSWORD: root
|
||||
DJANGO_MYSQL_HOST: 127.0.0.1
|
||||
DJANGO_ELASTICSEARCH_HOST: ${{ matrix.elasticsearch && '127.0.0.1:9200' || '' }}
|
||||
run: |
|
||||
echo "🗄️ 执行数据库迁移"
|
||||
|
||||
# 检查迁移文件
|
||||
echo "📋 检查待应用的迁移..."
|
||||
python manage.py showmigrations
|
||||
|
||||
# 检查是否有未创建的迁移
|
||||
python manage.py makemigrations --check --verbosity 2
|
||||
|
||||
# 执行迁移
|
||||
python manage.py migrate --verbosity 2
|
||||
|
||||
echo "✅ 数据库迁移完成"
|
||||
|
||||
- name: 运行Django测试
|
||||
if: matrix.test-type != 'docker'
|
||||
env:
|
||||
DJANGO_MYSQL_PASSWORD: root
|
||||
DJANGO_MYSQL_HOST: 127.0.0.1
|
||||
DJANGO_ELASTICSEARCH_HOST: ${{ matrix.elasticsearch && '127.0.0.1:9200' || '' }}
|
||||
run: |
|
||||
echo "🧪 开始执行 ${{ matrix.test-type }} 测试 (Python ${{ matrix.python-version }})"
|
||||
|
||||
# 显示Django配置信息
|
||||
python manage.py diffsettings | head -20
|
||||
|
||||
# 运行测试
|
||||
if [ "${{ matrix.coverage }}" = "true" ]; then
|
||||
echo "📊 运行测试并生成覆盖率报告"
|
||||
coverage run --source='.' --omit='*/venv/*,*/migrations/*,*/tests/*,manage.py' manage.py test --verbosity=2
|
||||
|
||||
echo "📈 生成覆盖率报告"
|
||||
coverage xml
|
||||
coverage report --show-missing
|
||||
coverage html
|
||||
|
||||
echo "📋 覆盖率统计:"
|
||||
coverage report | tail -1
|
||||
else
|
||||
echo "🧪 运行标准测试"
|
||||
python manage.py test --verbosity=2 --failfast
|
||||
fi
|
||||
|
||||
echo "✅ 测试执行完成"
|
||||
|
||||
# 覆盖率报告上传 (只有完整测试才执行)
|
||||
- name: 上传覆盖率到Codecov
|
||||
if: matrix.coverage == true && success()
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
file: ./coverage.xml
|
||||
flags: unittests
|
||||
name: codecov-${{ steps.test-info.outputs.test_name }}
|
||||
fail_ci_if_error: false
|
||||
verbose: true
|
||||
|
||||
- name: 上传覆盖率到Codecov (备用)
|
||||
if: matrix.coverage == true && failure()
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
file: ./coverage.xml
|
||||
flags: unittests
|
||||
name: codecov-${{ steps.test-info.outputs.test_name }}-fallback
|
||||
fail_ci_if_error: false
|
||||
verbose: true
|
||||
|
||||
# Docker构建测试
|
||||
- name: 设置QEMU
|
||||
if: matrix.test-type == 'docker'
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: 设置Docker Buildx
|
||||
if: matrix.test-type == 'docker'
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Docker构建测试
|
||||
if: matrix.test-type == 'docker'
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
tags: djangoblog/djangoblog:test-${{ github.sha }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
# 收集测试工件 (失败时收集调试信息)
|
||||
- name: 收集测试工件
|
||||
if: failure() && matrix.test-type != 'docker'
|
||||
run: |
|
||||
echo "🔍 收集测试失败的调试信息"
|
||||
|
||||
# 收集Django日志
|
||||
if [ -d "logs" ]; then
|
||||
echo "📄 Django日志文件:"
|
||||
ls -la logs/
|
||||
if [ -f "logs/djangoblog.log" ]; then
|
||||
echo "🔍 最新日志内容:"
|
||||
tail -100 logs/djangoblog.log
|
||||
fi
|
||||
fi
|
||||
|
||||
# 显示数据库状态
|
||||
echo "🗄️ 数据库连接状态:"
|
||||
python -c "
|
||||
try:
|
||||
from django.db import connection
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('SELECT VERSION()')
|
||||
print(f'MySQL版本: {cursor.fetchone()[0]}')
|
||||
cursor.execute('SHOW TABLES')
|
||||
tables = cursor.fetchall()
|
||||
print(f'数据库表数量: {len(tables)}')
|
||||
except Exception as e:
|
||||
print(f'数据库连接错误: {e}')
|
||||
" || true
|
||||
|
||||
# Elasticsearch状态 (如果启用)
|
||||
if [ "${{ matrix.elasticsearch }}" = "true" ]; then
|
||||
echo "🔍 Elasticsearch状态:"
|
||||
curl -s http://127.0.0.1:9200/_cluster/health?pretty || true
|
||||
fi
|
||||
|
||||
# 上传测试工件
|
||||
- name: 上传覆盖率HTML报告
|
||||
if: matrix.coverage == true && always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-report-${{ steps.test-info.outputs.test_name }}
|
||||
path: htmlcov/
|
||||
retention-days: 30
|
||||
|
||||
# 性能统计
|
||||
- name: 测试性能统计
|
||||
if: always() && matrix.test-type != 'docker'
|
||||
run: |
|
||||
echo "⚡ 测试性能统计:"
|
||||
echo " 开始时间: $(date -d '@${{ job.started_at }}' '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo '未知')"
|
||||
echo " 当前时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
|
||||
# 系统资源使用情况
|
||||
echo "💻 系统资源:"
|
||||
echo " CPU使用: $(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)%"
|
||||
echo " 内存使用: $(free -h | awk '/^Mem:/ {printf "%.1f%%", $3/$2 * 100}')"
|
||||
echo " 磁盘使用: $(df -h / | awk 'NR==2{printf "%s", $5}')"
|
||||
|
||||
# 测试结果汇总
|
||||
- name: 测试完成总结
|
||||
if: always()
|
||||
run: |
|
||||
echo "📋 ============ 测试执行总结 ============"
|
||||
echo " 🏷️ 测试类型: ${{ matrix.test-type }}"
|
||||
echo " 🐍 Python版本: ${{ matrix.python-version }}"
|
||||
echo " 🗄️ 数据库: ${{ matrix.database }}"
|
||||
echo " 🔍 Elasticsearch: ${{ matrix.elasticsearch }}"
|
||||
echo " 📊 覆盖率: ${{ matrix.coverage }}"
|
||||
echo " ⚡ 状态: ${{ job.status }}"
|
||||
echo " 📅 完成时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
echo "============================================"
|
||||
|
||||
# 根据测试结果显示不同消息
|
||||
if [ "${{ job.status }}" = "success" ]; then
|
||||
echo "🎉 测试执行成功!"
|
||||
else
|
||||
echo "❌ 测试执行失败,请检查上面的日志"
|
||||
fi
|
||||
@ -0,0 +1,8 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
@ -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/DjangoBlog-master/DjangoBlog-master" />
|
||||
<option name="settingsModule" value="settings.py" />
|
||||
<option name="manageScript" value="$MODULE_DIR$/src/DjangoBlog-master/DjangoBlog-master/manage.py" />
|
||||
<option name="environment" value="<map/>" />
|
||||
<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/DjangoBlog-master/DjangoBlog-master" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Django" 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/DjangoBlog-master/DjangoBlog-master/templates" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</module>
|
||||
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Django" 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/DjangoBlog.iml" filepath="$PROJECT_DIR$/.idea/DjangoBlog.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="357be86a-1284-4635-a32e-bf0ce39e4b81" name="更改" comment="" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="PUSH_TAGS">
|
||||
<GitPushTagMode>
|
||||
<option name="argument" value="--follow-tags" />
|
||||
<option name="title" value="Current Branch" />
|
||||
</GitPushTagMode>
|
||||
</option>
|
||||
<option name="RECENT_BRANCH_BY_REPOSITORY">
|
||||
<map>
|
||||
<entry key="$PROJECT_DIR$" value="master" />
|
||||
</map>
|
||||
</option>
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"associatedIndex": 3
|
||||
}</component>
|
||||
<component name="ProjectId" id="33xRndo8NxWInlIDa0Lp1mySOhn" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
||||
"RunOnceActivity.OpenDjangoStructureViewOnStart": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"git-widget-placeholder": "master",
|
||||
"ignore.virus.scanning.warn.message": "true",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}]]></component>
|
||||
<component name="RunManager">
|
||||
<configuration name="zyl_django" type="Python.DjangoServer" factoryName="Django server">
|
||||
<module name="zyl_django" />
|
||||
<option name="ENV_FILES" value="" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs>
|
||||
<env name="PYTHONUNBUFFERED" value="1" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="" />
|
||||
<option name="IS_MODULE_SDK" value="false" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<option name="launchJavascriptDebuger" value="false" />
|
||||
<option name="port" value="8000" />
|
||||
<option name="host" value="" />
|
||||
<option name="additionalOptions" value="" />
|
||||
<option name="browserUrl" value="" />
|
||||
<option name="runTestServer" value="false" />
|
||||
<option name="runNoReload" value="false" />
|
||||
<option name="useCustomRunCommand" value="false" />
|
||||
<option name="customRunCommand" value="" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
<component name="SharedIndexes">
|
||||
<attachedChunks>
|
||||
<set>
|
||||
<option value="bundled-js-predefined-d6986cc7102b-b598e85cdad2-JavaScript-PY-252.25557.178" />
|
||||
<option value="bundled-python-sdk-ce6832f46686-7b97d883f26b-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-252.25557.178" />
|
||||
</set>
|
||||
</attachedChunks>
|
||||
</component>
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="默认任务">
|
||||
<changelist id="357be86a-1284-4635-a32e-bf0ce39e4b81" name="更改" comment="" />
|
||||
<created>1760256904468</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1760256904468</updated>
|
||||
<workItem from="1760256905584" duration="153000" />
|
||||
<workItem from="1760257111491" duration="1866000" />
|
||||
<workItem from="1763889357146" duration="268000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
</project>
|
||||
@ -1,9 +0,0 @@
|
||||
.button {
|
||||
border: none;
|
||||
padding: 4px 80px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
margin: 4px 2px;
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
let wait = 60;
|
||||
|
||||
function time(o) {
|
||||
if (wait == 0) {
|
||||
o.removeAttribute("disabled");
|
||||
o.value = "获取验证码";
|
||||
wait = 60
|
||||
return false
|
||||
} else {
|
||||
o.setAttribute("disabled", true);
|
||||
o.value = "重新发送(" + wait + ")";
|
||||
wait--;
|
||||
setTimeout(function () {
|
||||
time(o)
|
||||
},
|
||||
1000)
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("btn").onclick = function () {
|
||||
let id_email = $("#id_email")
|
||||
let token = $("*[name='csrfmiddlewaretoken']").val()
|
||||
let ts = this
|
||||
let myErr = $("#myErr")
|
||||
$.ajax(
|
||||
{
|
||||
url: "/forget_password_code/",
|
||||
type: "POST",
|
||||
data: {
|
||||
"email": id_email.val(),
|
||||
"csrfmiddlewaretoken": token
|
||||
},
|
||||
success: function (result) {
|
||||
if (result != "ok") {
|
||||
myErr.remove()
|
||||
id_email.after("<ul className='errorlist' id='myErr'><li>" + result + "</li></ul>")
|
||||
return
|
||||
}
|
||||
myErr.remove()
|
||||
time(ts)
|
||||
},
|
||||
error: function (e) {
|
||||
alert("发送失败,请重试")
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,13 +0,0 @@
|
||||
/*!
|
||||
* IE10 viewport hack for Surface/desktop Windows 8 bug
|
||||
* Copyright 2014-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
/*
|
||||
* See the Getting Started docs for more information:
|
||||
* http://getbootstrap.com/getting-started/#support-ie10-width
|
||||
*/
|
||||
@-ms-viewport { width: device-width; }
|
||||
@-o-viewport { width: device-width; }
|
||||
@viewport { width: device-width; }
|
||||
@ -1,58 +0,0 @@
|
||||
body {
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.form-signin {
|
||||
max-width: 330px;
|
||||
padding: 15px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.form-signin-heading {
|
||||
margin: 0 0 15px;
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
color: #555;
|
||||
}
|
||||
.form-signin .checkbox {
|
||||
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;
|
||||
}
|
||||
.form-signin .form-control:focus {
|
||||
z-index: 2;
|
||||
}
|
||||
.form-signin input[type="email"] {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.form-signin input[type="password"] {
|
||||
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);
|
||||
}
|
||||
.card-signin {
|
||||
width: 354px;
|
||||
padding: 40px;
|
||||
}
|
||||
.card-signin .profile-img {
|
||||
display: block;
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
margin: 0 auto 10px;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 221 B |
@ -1,51 +0,0 @@
|
||||
// NOTICE!! DO NOT USE ANY OF THIS JAVASCRIPT
|
||||
// IT'S JUST JUNK FOR OUR DOCS!
|
||||
// ++++++++++++++++++++++++++++++++++++++++++
|
||||
/*!
|
||||
* Copyright 2014-2015 Twitter, Inc.
|
||||
*
|
||||
* Licensed under the Creative Commons Attribution 3.0 Unported License. For
|
||||
* details, see https://creativecommons.org/licenses/by/3.0/.
|
||||
*/
|
||||
// Intended to prevent false-positive bug reports about Bootstrap not working properly in old versions of IE due to folks testing using IE's unreliable emulation modes.
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function emulatedIEMajorVersion() {
|
||||
var groups = /MSIE ([0-9.]+)/.exec(window.navigator.userAgent)
|
||||
if (groups === null) {
|
||||
return null
|
||||
}
|
||||
var ieVersionNum = parseInt(groups[1], 10)
|
||||
var ieMajorVersion = Math.floor(ieVersionNum)
|
||||
return ieMajorVersion
|
||||
}
|
||||
|
||||
function actualNonEmulatedIEMajorVersion() {
|
||||
// Detects the actual version of IE in use, even if it's in an older-IE emulation mode.
|
||||
// IE JavaScript conditional compilation docs: https://msdn.microsoft.com/library/121hztk3%28v=vs.94%29.aspx
|
||||
// @cc_on docs: https://msdn.microsoft.com/library/8ka90k2e%28v=vs.94%29.aspx
|
||||
var jscriptVersion = new Function('/*@cc_on return @_jscript_version; @*/')() // jshint ignore:line
|
||||
if (jscriptVersion === undefined) {
|
||||
return 11 // IE11+ not in emulation mode
|
||||
}
|
||||
if (jscriptVersion < 9) {
|
||||
return 8 // IE8 (or lower; haven't tested on IE<8)
|
||||
}
|
||||
return jscriptVersion // IE9 or IE10 in any mode, or IE11 in non-IE11 mode
|
||||
}
|
||||
|
||||
var ua = window.navigator.userAgent
|
||||
if (ua.indexOf('Opera') > -1 || ua.indexOf('Presto') > -1) {
|
||||
return // Opera, which might pretend to be IE
|
||||
}
|
||||
var emulated = emulatedIEMajorVersion()
|
||||
if (emulated === null) {
|
||||
return // Not IE
|
||||
}
|
||||
var nonEmulated = actualNonEmulatedIEMajorVersion()
|
||||
|
||||
if (emulated !== nonEmulated) {
|
||||
window.alert('WARNING: You appear to be using IE' + nonEmulated + ' in IE' + emulated + ' emulation mode.\nIE emulation modes can behave significantly differently from ACTUAL older versions of IE.\nPLEASE DON\'T FILE BOOTSTRAP BUGS based on testing in IE emulation modes!')
|
||||
}
|
||||
})();
|
||||
@ -1,23 +0,0 @@
|
||||
/*!
|
||||
* IE10 viewport hack for Surface/desktop Windows 8 bug
|
||||
* Copyright 2014-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
// See the Getting Started docs for more information:
|
||||
// http://getbootstrap.com/getting-started/#support-ie10-width
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
if (navigator.userAgent.match(/IEMobile\/10\.0/)) {
|
||||
var msViewportStyle = document.createElement('style')
|
||||
msViewportStyle.appendChild(
|
||||
document.createTextNode(
|
||||
'@-ms-viewport{width:auto!important}'
|
||||
)
|
||||
)
|
||||
document.querySelector('head').appendChild(msViewportStyle)
|
||||
}
|
||||
|
||||
})();
|
||||
@ -1,273 +0,0 @@
|
||||
/*
|
||||
Styles for older IE versions (previous to IE9).
|
||||
*/
|
||||
|
||||
body {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
body.custom-background-empty {
|
||||
background-color: #fff;
|
||||
}
|
||||
body.custom-background-empty .site,
|
||||
body.custom-background-white .site {
|
||||
box-shadow: none;
|
||||
margin-bottom: 0;
|
||||
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%;
|
||||
}
|
||||
img.size-full,
|
||||
img.size-large,
|
||||
img.header-image,
|
||||
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 */
|
||||
}
|
||||
.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;
|
||||
max-width: 960px;
|
||||
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;
|
||||
border-top: 1px solid #ededed;
|
||||
display: inline-block !important;
|
||||
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 .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;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
z-index: 1;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
overflow: hidden;
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
}
|
||||
.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 {
|
||||
border-left: 0;
|
||||
clip: inherit;
|
||||
overflow: inherit;
|
||||
height: inherit;
|
||||
width: inherit;
|
||||
}
|
||||
.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;
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
line-height: 2.181818182;
|
||||
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,
|
||||
.main-navigation .current_page_ancestor > a {
|
||||
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 */
|
||||
.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 {
|
||||
float: left;
|
||||
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 {
|
||||
float: right;
|
||||
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 */
|
||||
input[type="password"] {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
/* RTL overrides for IE7 and IE8
|
||||
-------------------------------------------------------------- */
|
||||
.rtl .site-header h1,
|
||||
.rtl .site-header h2 {
|
||||
text-align: right;
|
||||
}
|
||||
.rtl .widget-area,
|
||||
.rtl .author-description {
|
||||
float: left;
|
||||
}
|
||||
.rtl .author-avatar,
|
||||
.rtl .site-content {
|
||||
float: right;
|
||||
}
|
||||
.rtl .main-navigation ul.nav-menu,
|
||||
.rtl .main-navigation div.nav-menu > ul {
|
||||
text-align: right;
|
||||
}
|
||||
.rtl .main-navigation ul li ul li,
|
||||
.rtl .main-navigation ul li ul li ul li {
|
||||
margin-left: 40px;
|
||||
margin-right: auto;
|
||||
}
|
||||
.rtl .main-navigation li ul ul {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
.ie7 .rtl .main-navigation li ul ul {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
.ie7 .rtl .main-navigation ul li {
|
||||
z-index: 99;
|
||||
}
|
||||
.ie7 .rtl .main-navigation li ul {
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
.ie7 .rtl .main-navigation li {
|
||||
margin-right: auto;
|
||||
margin-left: 40px;
|
||||
}
|
||||
.ie7 .rtl .main-navigation li ul ul ul {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
@ -1,74 +0,0 @@
|
||||
/* Make clicks pass-through */
|
||||
#nprogress {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#nprogress .bar {
|
||||
background: red;
|
||||
|
||||
position: fixed;
|
||||
z-index: 1031;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
/* Fancy blur effect */
|
||||
#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);
|
||||
}
|
||||
|
||||
/* Remove these to get rid of the spinner */
|
||||
#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); }
|
||||
}
|
||||
|
||||
@ -1,305 +0,0 @@
|
||||
|
||||
.icon-sn-google {
|
||||
background-position: 0 -28px;
|
||||
}
|
||||
|
||||
.icon-sn-bg-google {
|
||||
background-color: #4285f4;
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
.fa-sn-google {
|
||||
color: #4285f4;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.icon-sn-qq {
|
||||
background-position: -84px -28px;
|
||||
}
|
||||
|
||||
.icon-sn-bg-qq {
|
||||
background-color: #0098e6;
|
||||
background-position: -84px 0;
|
||||
}
|
||||
|
||||
.fa-sn-qq {
|
||||
color: #0098e6;
|
||||
}
|
||||
|
||||
.icon-sn-twitter {
|
||||
background-position: -112px -28px;
|
||||
}
|
||||
|
||||
.icon-sn-bg-twitter {
|
||||
background-color: #50abf1;
|
||||
background-position: -112px 0;
|
||||
}
|
||||
|
||||
.fa-sn-twitter {
|
||||
color: #50abf1;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.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('../img/icon-sn.svg');
|
||||
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);
|
||||
}
|
||||
|
||||
.btn-sn-google {
|
||||
background: #4285f4;
|
||||
}
|
||||
|
||||
.btn-sn-google:active, .btn-sn-google:focus, .btn-sn-google:hover {
|
||||
background: #2a75f3;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.btn-sn-qq {
|
||||
background: #0098e6;
|
||||
}
|
||||
|
||||
.btn-sn-qq:active, .btn-sn-qq:focus, .btn-sn-qq:hover {
|
||||
background: #0087cd;
|
||||
}
|
||||
|
||||
.btn-sn-twitter {
|
||||
background: #50abf1;
|
||||
}
|
||||
|
||||
.btn-sn-twitter:active, .btn-sn-twitter:focus, .btn-sn-twitter:hover {
|
||||
background: #38a0ef;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,600 +0,0 @@
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtE6F15M.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWvU6F15M.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtU6F15M.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWuk6F15M.woff2) format('woff2');
|
||||
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
|
||||
}
|
||||
/* hebrew */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWu06F15M.woff2) format('woff2');
|
||||
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
|
||||
}
|
||||
/* math */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWxU6F15M.woff2) format('woff2');
|
||||
unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
|
||||
}
|
||||
/* symbols */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqW106F15M.woff2) format('woff2');
|
||||
unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtk6F15M.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWt06F15M.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWuU6F.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtE6F15M.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWvU6F15M.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtU6F15M.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWuk6F15M.woff2) format('woff2');
|
||||
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
|
||||
}
|
||||
/* hebrew */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWu06F15M.woff2) format('woff2');
|
||||
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
|
||||
}
|
||||
/* math */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWxU6F15M.woff2) format('woff2');
|
||||
unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
|
||||
}
|
||||
/* symbols */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqW106F15M.woff2) format('woff2');
|
||||
unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtk6F15M.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWt06F15M.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWuU6F.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtE6F15M.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWvU6F15M.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtU6F15M.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWuk6F15M.woff2) format('woff2');
|
||||
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
|
||||
}
|
||||
/* hebrew */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWu06F15M.woff2) format('woff2');
|
||||
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
|
||||
}
|
||||
/* math */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWxU6F15M.woff2) format('woff2');
|
||||
unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
|
||||
}
|
||||
/* symbols */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqW106F15M.woff2) format('woff2');
|
||||
unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtk6F15M.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWt06F15M.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWuU6F.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSKmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSumu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSOmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSymu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
|
||||
}
|
||||
/* hebrew */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS2mu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
|
||||
}
|
||||
/* math */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTVOmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
|
||||
}
|
||||
/* symbols */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTUGmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSCmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSGmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS-muw.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSKmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSumu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSOmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSymu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
|
||||
}
|
||||
/* hebrew */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS2mu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
|
||||
}
|
||||
/* math */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTVOmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
|
||||
}
|
||||
/* symbols */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTUGmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSCmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSGmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS-muw.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSKmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSumu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSOmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSymu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
|
||||
}
|
||||
/* hebrew */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS2mu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
|
||||
}
|
||||
/* math */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTVOmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
|
||||
}
|
||||
/* symbols */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTUGmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSCmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSGmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS-muw.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 25 KiB |
@ -1,91 +0,0 @@
|
||||
/**
|
||||
* Created by liangliang on 2016/11/20.
|
||||
*/
|
||||
|
||||
|
||||
function do_reply(parentid) {
|
||||
console.log(parentid);
|
||||
$("#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_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));
|
||||
|
||||
function debounce(func, wait) {
|
||||
var timeout;
|
||||
return function () {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(func, wait);
|
||||
};
|
||||
}
|
||||
|
||||
function slideTopSet() {
|
||||
var top = $(document).scrollTop();
|
||||
|
||||
if (top > 200) {
|
||||
rocket.addClass('show');
|
||||
} else {
|
||||
rocket.removeClass('show');
|
||||
}
|
||||
}
|
||||
|
||||
$(document).on('click', '#rocket', function (event) {
|
||||
rocket.addClass('move');
|
||||
$('body, html').animate({
|
||||
scrollTop: 0
|
||||
}, 800);
|
||||
});
|
||||
$(document).on('animationEnd', function () {
|
||||
setTimeout(function () {
|
||||
rocket.removeClass('move');
|
||||
}, 400);
|
||||
|
||||
});
|
||||
$(document).on('webkitAnimationEnd', function () {
|
||||
setTimeout(function () {
|
||||
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);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// $(document).ready(function () {
|
||||
// var form = $('#i18n-form');
|
||||
// var selector = $('.i18n-select');
|
||||
// selector.on('change', function () {
|
||||
// form.submit();
|
||||
// });
|
||||
// });
|
||||
@ -1,8 +0,0 @@
|
||||
/*
|
||||
HTML5 Shiv v3.7.0 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
|
||||
*/
|
||||
(function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag();
|
||||
a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/[\w\-]+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x<style>article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}</style>";
|
||||
c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="<xyz></xyz>";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode||
|
||||
"undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:"3.7.0",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);
|
||||
if(g)return a.createDocumentFragment();for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d<h;d++)c.createElement(e[d]);return c}};l.html5=e;q(f)})(this,document);
|
||||
File diff suppressed because one or more lines are too long
@ -1,55 +0,0 @@
|
||||
/**
|
||||
* Handles toggling the navigation menu for small screens and
|
||||
* accessibility for submenu items.
|
||||
*/
|
||||
( function() {
|
||||
var nav = document.getElementById( 'site-navigation' ), button, menu;
|
||||
if ( ! nav ) {
|
||||
return;
|
||||
}
|
||||
|
||||
button = nav.getElementsByTagName( 'button' )[0];
|
||||
menu = nav.getElementsByTagName( 'ul' )[0];
|
||||
if ( ! button ) {
|
||||
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', '' );
|
||||
} else {
|
||||
button.className += ' toggled-on';
|
||||
menu.className += ' toggled-on';
|
||||
}
|
||||
};
|
||||
} )();
|
||||
|
||||
// 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' );
|
||||
|
||||
if ( ! el.hasClass( 'focus' ) ) {
|
||||
e.preventDefault();
|
||||
el.toggleClass( 'focus' );
|
||||
el.siblings( '.focus').removeClass( 'focus' );
|
||||
}
|
||||
} );
|
||||
}
|
||||
} )( jQuery );
|
||||
@ -1,480 +0,0 @@
|
||||
/* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress
|
||||
* @license MIT */
|
||||
|
||||
;(function(root, factory) {
|
||||
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
module.exports = factory();
|
||||
} else {
|
||||
root.NProgress = factory();
|
||||
}
|
||||
|
||||
})(this, function() {
|
||||
var NProgress = {};
|
||||
|
||||
NProgress.version = '0.2.0';
|
||||
|
||||
var Settings = NProgress.settings = {
|
||||
minimum: 0.08,
|
||||
easing: 'linear',
|
||||
positionUsing: '',
|
||||
speed: 200,
|
||||
trickle: true,
|
||||
trickleSpeed: 200,
|
||||
showSpinner: true,
|
||||
barSelector: '[role="bar"]',
|
||||
spinnerSelector: '[role="spinner"]',
|
||||
parent: 'body',
|
||||
template: '<div class="bar" role="bar"><div class="peg"></div></div><div class="spinner" role="spinner"><div class="spinner-icon"></div></div>'
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates configuration.
|
||||
*
|
||||
* NProgress.configure({
|
||||
* minimum: 0.1
|
||||
* });
|
||||
*/
|
||||
NProgress.configure = function(options) {
|
||||
var key, value;
|
||||
for (key in options) {
|
||||
value = options[key];
|
||||
if (value !== undefined && options.hasOwnProperty(key)) Settings[key] = value;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Last number.
|
||||
*/
|
||||
|
||||
NProgress.status = null;
|
||||
|
||||
/**
|
||||
* Sets the progress bar status, where `n` is a number from `0.0` to `1.0`.
|
||||
*
|
||||
* NProgress.set(0.4);
|
||||
* NProgress.set(1.0);
|
||||
*/
|
||||
|
||||
NProgress.set = function(n) {
|
||||
var started = NProgress.isStarted();
|
||||
|
||||
n = clamp(n, Settings.minimum, 1);
|
||||
NProgress.status = (n === 1 ? null : n);
|
||||
|
||||
var progress = NProgress.render(!started),
|
||||
bar = progress.querySelector(Settings.barSelector),
|
||||
speed = Settings.speed,
|
||||
ease = Settings.easing;
|
||||
|
||||
progress.offsetWidth; /* Repaint */
|
||||
|
||||
queue(function(next) {
|
||||
// Set positionUsing if it hasn't already been set
|
||||
if (Settings.positionUsing === '') Settings.positionUsing = NProgress.getPositioningCSS();
|
||||
|
||||
// Add transition
|
||||
css(bar, barPositionCSS(n, speed, ease));
|
||||
|
||||
if (n === 1) {
|
||||
// Fade out
|
||||
css(progress, {
|
||||
transition: 'none',
|
||||
opacity: 1
|
||||
});
|
||||
progress.offsetWidth; /* Repaint */
|
||||
|
||||
setTimeout(function() {
|
||||
css(progress, {
|
||||
transition: 'all ' + speed + 'ms linear',
|
||||
opacity: 0
|
||||
});
|
||||
setTimeout(function() {
|
||||
NProgress.remove();
|
||||
next();
|
||||
}, speed);
|
||||
}, speed);
|
||||
} else {
|
||||
setTimeout(next, speed);
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
NProgress.isStarted = function() {
|
||||
return typeof NProgress.status === 'number';
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows the progress bar.
|
||||
* This is the same as setting the status to 0%, except that it doesn't go backwards.
|
||||
*
|
||||
* NProgress.start();
|
||||
*
|
||||
*/
|
||||
NProgress.start = function() {
|
||||
if (!NProgress.status) NProgress.set(0);
|
||||
|
||||
var work = function() {
|
||||
setTimeout(function() {
|
||||
if (!NProgress.status) return;
|
||||
NProgress.trickle();
|
||||
work();
|
||||
}, Settings.trickleSpeed);
|
||||
};
|
||||
|
||||
if (Settings.trickle) work();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hides the progress bar.
|
||||
* This is the *sort of* the same as setting the status to 100%, with the
|
||||
* difference being `done()` makes some placebo effect of some realistic motion.
|
||||
*
|
||||
* NProgress.done();
|
||||
*
|
||||
* If `true` is passed, it will show the progress bar even if its hidden.
|
||||
*
|
||||
* NProgress.done(true);
|
||||
*/
|
||||
|
||||
NProgress.done = function(force) {
|
||||
if (!force && !NProgress.status) return this;
|
||||
|
||||
return NProgress.inc(0.3 + 0.5 * Math.random()).set(1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Increments by a random amount.
|
||||
*/
|
||||
|
||||
NProgress.inc = function(amount) {
|
||||
var n = NProgress.status;
|
||||
|
||||
if (!n) {
|
||||
return NProgress.start();
|
||||
} else if(n > 1) {
|
||||
|
||||
} else {
|
||||
if (typeof amount !== 'number') {
|
||||
if (n >= 0 && n < 0.2) { amount = 0.1; }
|
||||
else if (n >= 0.2 && n < 0.5) { amount = 0.04; }
|
||||
else if (n >= 0.5 && n < 0.8) { amount = 0.02; }
|
||||
else if (n >= 0.8 && n < 0.99) { amount = 0.005; }
|
||||
else { amount = 0; }
|
||||
}
|
||||
|
||||
n = clamp(n + amount, 0, 0.994);
|
||||
return NProgress.set(n);
|
||||
}
|
||||
};
|
||||
|
||||
NProgress.trickle = function() {
|
||||
return NProgress.inc();
|
||||
};
|
||||
|
||||
/**
|
||||
* Waits for all supplied jQuery promises and
|
||||
* increases the progress as the promises resolve.
|
||||
*
|
||||
* @param $promise jQUery Promise
|
||||
*/
|
||||
(function() {
|
||||
var initial = 0, current = 0;
|
||||
|
||||
NProgress.promise = function($promise) {
|
||||
if (!$promise || $promise.state() === "resolved") {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (current === 0) {
|
||||
NProgress.start();
|
||||
}
|
||||
|
||||
initial++;
|
||||
current++;
|
||||
|
||||
$promise.always(function() {
|
||||
current--;
|
||||
if (current === 0) {
|
||||
initial = 0;
|
||||
NProgress.done();
|
||||
} else {
|
||||
NProgress.set((initial - current) / initial);
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
/**
|
||||
* (Internal) renders the progress bar markup based on the `template`
|
||||
* setting.
|
||||
*/
|
||||
|
||||
NProgress.render = function(fromStart) {
|
||||
if (NProgress.isRendered()) return document.getElementById('nprogress');
|
||||
|
||||
addClass(document.documentElement, 'nprogress-busy');
|
||||
|
||||
var progress = document.createElement('div');
|
||||
progress.id = 'nprogress';
|
||||
progress.innerHTML = Settings.template;
|
||||
|
||||
var bar = progress.querySelector(Settings.barSelector),
|
||||
perc = fromStart ? '-100' : toBarPerc(NProgress.status || 0),
|
||||
parent = document.querySelector(Settings.parent),
|
||||
spinner;
|
||||
|
||||
css(bar, {
|
||||
transition: 'all 0 linear',
|
||||
transform: 'translate3d(' + perc + '%,0,0)'
|
||||
});
|
||||
|
||||
if (!Settings.showSpinner) {
|
||||
spinner = progress.querySelector(Settings.spinnerSelector);
|
||||
spinner && removeElement(spinner);
|
||||
}
|
||||
|
||||
if (parent != document.body) {
|
||||
addClass(parent, 'nprogress-custom-parent');
|
||||
}
|
||||
|
||||
parent.appendChild(progress);
|
||||
return progress;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the element. Opposite of render().
|
||||
*/
|
||||
|
||||
NProgress.remove = function() {
|
||||
removeClass(document.documentElement, 'nprogress-busy');
|
||||
removeClass(document.querySelector(Settings.parent), 'nprogress-custom-parent');
|
||||
var progress = document.getElementById('nprogress');
|
||||
progress && removeElement(progress);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the progress bar is rendered.
|
||||
*/
|
||||
|
||||
NProgress.isRendered = function() {
|
||||
return !!document.getElementById('nprogress');
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine which positioning CSS rule to use.
|
||||
*/
|
||||
|
||||
NProgress.getPositioningCSS = function() {
|
||||
// Sniff on document.body.style
|
||||
var bodyStyle = document.body.style;
|
||||
|
||||
// Sniff prefixes
|
||||
var vendorPrefix = ('WebkitTransform' in bodyStyle) ? 'Webkit' :
|
||||
('MozTransform' in bodyStyle) ? 'Moz' :
|
||||
('msTransform' in bodyStyle) ? 'ms' :
|
||||
('OTransform' in bodyStyle) ? 'O' : '';
|
||||
|
||||
if (vendorPrefix + 'Perspective' in bodyStyle) {
|
||||
// Modern browsers with 3D support, e.g. Webkit, IE10
|
||||
return 'translate3d';
|
||||
} else if (vendorPrefix + 'Transform' in bodyStyle) {
|
||||
// Browsers without 3D support, e.g. IE9
|
||||
return 'translate';
|
||||
} else {
|
||||
// Browsers without translate() support, e.g. IE7-8
|
||||
return 'margin';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
function clamp(n, min, max) {
|
||||
if (n < min) return min;
|
||||
if (n > max) return max;
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* (Internal) converts a percentage (`0..1`) to a bar translateX
|
||||
* percentage (`-100%..0%`).
|
||||
*/
|
||||
|
||||
function toBarPerc(n) {
|
||||
return (-1 + n) * 100;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Internal) returns the correct CSS for changing the bar's
|
||||
* position given an n percentage, and speed and ease from Settings
|
||||
*/
|
||||
|
||||
function barPositionCSS(n, speed, ease) {
|
||||
var barCSS;
|
||||
|
||||
if (Settings.positionUsing === 'translate3d') {
|
||||
barCSS = { transform: 'translate3d('+toBarPerc(n)+'%,0,0)' };
|
||||
} else if (Settings.positionUsing === 'translate') {
|
||||
barCSS = { transform: 'translate('+toBarPerc(n)+'%,0)' };
|
||||
} else {
|
||||
barCSS = { 'margin-left': toBarPerc(n)+'%' };
|
||||
}
|
||||
|
||||
barCSS.transition = 'all '+speed+'ms '+ease;
|
||||
|
||||
return barCSS;
|
||||
}
|
||||
|
||||
/**
|
||||
* (Internal) Queues a function to be executed.
|
||||
*/
|
||||
|
||||
var queue = (function() {
|
||||
var pending = [];
|
||||
|
||||
function next() {
|
||||
var fn = pending.shift();
|
||||
if (fn) {
|
||||
fn(next);
|
||||
}
|
||||
}
|
||||
|
||||
return function(fn) {
|
||||
pending.push(fn);
|
||||
if (pending.length == 1) next();
|
||||
};
|
||||
})();
|
||||
|
||||
/**
|
||||
* (Internal) Applies css properties to an element, similar to the jQuery
|
||||
* css method.
|
||||
*
|
||||
* While this helper does assist with vendor prefixed property names, it
|
||||
* does not perform any manipulation of values prior to setting styles.
|
||||
*/
|
||||
|
||||
var css = (function() {
|
||||
var cssPrefixes = [ 'Webkit', 'O', 'Moz', 'ms' ],
|
||||
cssProps = {};
|
||||
|
||||
function camelCase(string) {
|
||||
return string.replace(/^-ms-/, 'ms-').replace(/-([\da-z])/gi, function(match, letter) {
|
||||
return letter.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
function getVendorProp(name) {
|
||||
var style = document.body.style;
|
||||
if (name in style) return name;
|
||||
|
||||
var i = cssPrefixes.length,
|
||||
capName = name.charAt(0).toUpperCase() + name.slice(1),
|
||||
vendorName;
|
||||
while (i--) {
|
||||
vendorName = cssPrefixes[i] + capName;
|
||||
if (vendorName in style) return vendorName;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
function getStyleProp(name) {
|
||||
name = camelCase(name);
|
||||
return cssProps[name] || (cssProps[name] = getVendorProp(name));
|
||||
}
|
||||
|
||||
function applyCss(element, prop, value) {
|
||||
prop = getStyleProp(prop);
|
||||
element.style[prop] = value;
|
||||
}
|
||||
|
||||
return function(element, properties) {
|
||||
var args = arguments,
|
||||
prop,
|
||||
value;
|
||||
|
||||
if (args.length == 2) {
|
||||
for (prop in properties) {
|
||||
value = properties[prop];
|
||||
if (value !== undefined && properties.hasOwnProperty(prop)) applyCss(element, prop, value);
|
||||
}
|
||||
} else {
|
||||
applyCss(element, args[1], args[2]);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
/**
|
||||
* (Internal) Determines if an element or space separated list of class names contains a class name.
|
||||
*/
|
||||
|
||||
function hasClass(element, name) {
|
||||
var list = typeof element == 'string' ? element : classList(element);
|
||||
return list.indexOf(' ' + name + ' ') >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* (Internal) Adds a class to an element.
|
||||
*/
|
||||
|
||||
function addClass(element, name) {
|
||||
var oldList = classList(element),
|
||||
newList = oldList + name;
|
||||
|
||||
if (hasClass(oldList, name)) return;
|
||||
|
||||
// Trim the opening space.
|
||||
element.className = newList.substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Internal) Removes a class from an element.
|
||||
*/
|
||||
|
||||
function removeClass(element, name) {
|
||||
var oldList = classList(element),
|
||||
newList;
|
||||
|
||||
if (!hasClass(element, name)) return;
|
||||
|
||||
// Replace the class name.
|
||||
newList = oldList.replace(' ' + name + ' ', ' ');
|
||||
|
||||
// Trim the opening and closing spaces.
|
||||
element.className = newList.substring(1, newList.length - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Internal) Gets a space separated list of the class names on the element.
|
||||
* The list is wrapped with a single space on each end to facilitate finding
|
||||
* matches within the list.
|
||||
*/
|
||||
|
||||
function classList(element) {
|
||||
return (' ' + (element && element.className || '') + ' ').replace(/\s+/gi, ' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* (Internal) Removes an element from the DOM.
|
||||
*/
|
||||
|
||||
function removeElement(element) {
|
||||
element && element.parentNode && element.parentNode.removeChild(element);
|
||||
}
|
||||
|
||||
return NProgress;
|
||||
});
|
||||
@ -1,293 +0,0 @@
|
||||
.codehilite .hll {
|
||||
background-color: #ffffcc
|
||||
}
|
||||
|
||||
.codehilite {
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.codehilite .c {
|
||||
color: #177500
|
||||
}
|
||||
|
||||
/* Comment */
|
||||
.codehilite .err {
|
||||
color: #000000
|
||||
}
|
||||
|
||||
/* Error */
|
||||
.codehilite .k {
|
||||
color: #A90D91
|
||||
}
|
||||
|
||||
/* Keyword */
|
||||
.codehilite .l {
|
||||
color: #1C01CE
|
||||
}
|
||||
|
||||
/* Literal */
|
||||
.codehilite .n {
|
||||
color: #000000
|
||||
}
|
||||
|
||||
/* Name */
|
||||
.codehilite .o {
|
||||
color: #000000
|
||||
}
|
||||
|
||||
/* Operator */
|
||||
.codehilite .ch {
|
||||
color: #177500
|
||||
}
|
||||
|
||||
/* Comment.Hashbang */
|
||||
.codehilite .cm {
|
||||
color: #177500
|
||||
}
|
||||
|
||||
/* Comment.Multiline */
|
||||
.codehilite .cp {
|
||||
color: #633820
|
||||
}
|
||||
|
||||
/* Comment.Preproc */
|
||||
.codehilite .cpf {
|
||||
color: #177500
|
||||
}
|
||||
|
||||
/* Comment.PreprocFile */
|
||||
.codehilite .c1 {
|
||||
color: #177500
|
||||
}
|
||||
|
||||
/* Comment.Single */
|
||||
.codehilite .cs {
|
||||
color: #177500
|
||||
}
|
||||
|
||||
/* Comment.Special */
|
||||
.codehilite .kc {
|
||||
color: #A90D91
|
||||
}
|
||||
|
||||
/* Keyword.Constant */
|
||||
.codehilite .kd {
|
||||
color: #A90D91
|
||||
}
|
||||
|
||||
/* Keyword.Declaration */
|
||||
.codehilite .kn {
|
||||
color: #A90D91
|
||||
}
|
||||
|
||||
/* Keyword.Namespace */
|
||||
.codehilite .kp {
|
||||
color: #A90D91
|
||||
}
|
||||
|
||||
/* Keyword.Pseudo */
|
||||
.codehilite .kr {
|
||||
color: #A90D91
|
||||
}
|
||||
|
||||
/* Keyword.Reserved */
|
||||
.codehilite .kt {
|
||||
color: #A90D91
|
||||
}
|
||||
|
||||
/* Keyword.Type */
|
||||
.codehilite .ld {
|
||||
color: #1C01CE
|
||||
}
|
||||
|
||||
/* Literal.Date */
|
||||
.codehilite .m {
|
||||
color: #1C01CE
|
||||
}
|
||||
|
||||
/* Literal.Number */
|
||||
.codehilite .s {
|
||||
color: #C41A16
|
||||
}
|
||||
|
||||
/* Literal.String */
|
||||
.codehilite .na {
|
||||
color: #836C28
|
||||
}
|
||||
|
||||
/* Name.Attribute */
|
||||
.codehilite .nb {
|
||||
color: #A90D91
|
||||
}
|
||||
|
||||
/* Name.Builtin */
|
||||
.codehilite .nc {
|
||||
color: #3F6E75
|
||||
}
|
||||
|
||||
/* Name.Class */
|
||||
.codehilite .no {
|
||||
color: #000000
|
||||
}
|
||||
|
||||
/* Name.Constant */
|
||||
.codehilite .nd {
|
||||
color: #000000
|
||||
}
|
||||
|
||||
/* Name.Decorator */
|
||||
.codehilite .ni {
|
||||
color: #000000
|
||||
}
|
||||
|
||||
/* Name.Entity */
|
||||
.codehilite .ne {
|
||||
color: #000000
|
||||
}
|
||||
|
||||
/* Name.Exception */
|
||||
.codehilite .nf {
|
||||
color: #000000
|
||||
}
|
||||
|
||||
/* Name.Function */
|
||||
.codehilite .nl {
|
||||
color: #000000
|
||||
}
|
||||
|
||||
/* Name.Label */
|
||||
.codehilite .nn {
|
||||
color: #000000
|
||||
}
|
||||
|
||||
/* Name.Namespace */
|
||||
.codehilite .nx {
|
||||
color: #000000
|
||||
}
|
||||
|
||||
/* Name.Other */
|
||||
.codehilite .py {
|
||||
color: #000000
|
||||
}
|
||||
|
||||
/* Name.Property */
|
||||
.codehilite .nt {
|
||||
color: #000000
|
||||
}
|
||||
|
||||
/* Name.Tag */
|
||||
.codehilite .nv {
|
||||
color: #000000
|
||||
}
|
||||
|
||||
/* Name.Variable */
|
||||
.codehilite .ow {
|
||||
color: #000000
|
||||
}
|
||||
|
||||
/* Operator.Word */
|
||||
.codehilite .mb {
|
||||
color: #1C01CE
|
||||
}
|
||||
|
||||
/* Literal.Number.Bin */
|
||||
.codehilite .mf {
|
||||
color: #1C01CE
|
||||
}
|
||||
|
||||
/* Literal.Number.Float */
|
||||
.codehilite .mh {
|
||||
color: #1C01CE
|
||||
}
|
||||
|
||||
/* Literal.Number.Hex */
|
||||
.codehilite .mi {
|
||||
color: #1C01CE
|
||||
}
|
||||
|
||||
/* Literal.Number.Integer */
|
||||
.codehilite .mo {
|
||||
color: #1C01CE
|
||||
}
|
||||
|
||||
/* Literal.Number.Oct */
|
||||
.codehilite .sb {
|
||||
color: #C41A16
|
||||
}
|
||||
|
||||
/* Literal.String.Backtick */
|
||||
.codehilite .sc {
|
||||
color: #2300CE
|
||||
}
|
||||
|
||||
/* Literal.String.Char */
|
||||
.codehilite .sd {
|
||||
color: #C41A16
|
||||
}
|
||||
|
||||
/* Literal.String.Doc */
|
||||
.codehilite .s2 {
|
||||
color: #C41A16
|
||||
}
|
||||
|
||||
/* Literal.String.Double */
|
||||
.codehilite .se {
|
||||
color: #C41A16
|
||||
}
|
||||
|
||||
/* Literal.String.Escape */
|
||||
.codehilite .sh {
|
||||
color: #C41A16
|
||||
}
|
||||
|
||||
/* Literal.String.Heredoc */
|
||||
.codehilite .si {
|
||||
color: #C41A16
|
||||
}
|
||||
|
||||
/* Literal.String.Interpol */
|
||||
.codehilite .sx {
|
||||
color: #C41A16
|
||||
}
|
||||
|
||||
/* Literal.String.Other */
|
||||
.codehilite .sr {
|
||||
color: #C41A16
|
||||
}
|
||||
|
||||
/* Literal.String.Regex */
|
||||
.codehilite .s1 {
|
||||
color: #C41A16
|
||||
}
|
||||
|
||||
/* Literal.String.Single */
|
||||
.codehilite .ss {
|
||||
color: #C41A16
|
||||
}
|
||||
|
||||
/* Literal.String.Symbol */
|
||||
.codehilite .bp {
|
||||
color: #5B269A
|
||||
}
|
||||
|
||||
/* Name.Builtin.Pseudo */
|
||||
.codehilite .vc {
|
||||
color: #000000
|
||||
}
|
||||
|
||||
/* Name.Variable.Class */
|
||||
.codehilite .vg {
|
||||
color: #000000
|
||||
}
|
||||
|
||||
/* Name.Variable.Global */
|
||||
.codehilite .vi {
|
||||
color: #000000
|
||||
}
|
||||
|
||||
/* Name.Variable.Instance */
|
||||
.codehilite .il {
|
||||
color: #1C01CE
|
||||
}
|
||||
|
||||
/* Literal.Number.Integer.Long */
|
||||
@ -1,87 +0,0 @@
|
||||
codecov:
|
||||
require_ci_to_pass: yes
|
||||
|
||||
coverage:
|
||||
precision: 2
|
||||
round: down
|
||||
range: "70...100"
|
||||
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: auto
|
||||
threshold: 1%
|
||||
informational: true
|
||||
patch:
|
||||
default:
|
||||
target: auto
|
||||
threshold: 1%
|
||||
informational: true
|
||||
|
||||
parsers:
|
||||
gcov:
|
||||
branch_detection:
|
||||
conditional: yes
|
||||
loop: yes
|
||||
method: no
|
||||
macro: no
|
||||
|
||||
comment:
|
||||
layout: "reach,diff,flags,tree"
|
||||
behavior: default
|
||||
require_changes: no
|
||||
|
||||
ignore:
|
||||
# Django 相关
|
||||
- "*/migrations/*"
|
||||
- "manage.py"
|
||||
- "*/settings.py"
|
||||
- "*/wsgi.py"
|
||||
- "*/asgi.py"
|
||||
|
||||
# 测试相关
|
||||
- "*/tests/*"
|
||||
- "*/test_*.py"
|
||||
- "*/*test*.py"
|
||||
|
||||
# 静态文件和模板
|
||||
- "*/static/*"
|
||||
- "*/templates/*"
|
||||
- "*/collectedstatic/*"
|
||||
|
||||
# 国际化文件
|
||||
- "*/locale/*"
|
||||
- "**/*.po"
|
||||
- "**/*.mo"
|
||||
|
||||
# 文档和部署
|
||||
- "*/docs/*"
|
||||
- "*/deploy/*"
|
||||
- "README*.md"
|
||||
- "LICENSE"
|
||||
- "Dockerfile"
|
||||
- "docker-compose*.yml"
|
||||
- "*.yaml"
|
||||
- "*.yml"
|
||||
|
||||
# 开发环境
|
||||
- "*/venv/*"
|
||||
- "*/__pycache__/*"
|
||||
- "*.pyc"
|
||||
- ".coverage"
|
||||
- "coverage.xml"
|
||||
|
||||
# 日志文件
|
||||
- "*/logs/*"
|
||||
- "*.log"
|
||||
|
||||
# 特定文件
|
||||
- "*/whoosh_cn_backend.py" # 搜索后端
|
||||
- "*/elasticsearch_backend.py" # 搜索后端
|
||||
- "*/MemcacheStorage.py" # 缓存存储
|
||||
- "*/robot.py" # 机器人相关
|
||||
|
||||
# 配置文件
|
||||
- "codecov.yml"
|
||||
- ".coveragerc"
|
||||
- "requirements*.txt"
|
||||
@ -1,194 +0,0 @@
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from django.template import TemplateDoesNotExist
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BasePlugin:
|
||||
# 插件元数据
|
||||
PLUGIN_NAME = None
|
||||
PLUGIN_DESCRIPTION = None
|
||||
PLUGIN_VERSION = None
|
||||
PLUGIN_AUTHOR = None
|
||||
|
||||
# 插件配置
|
||||
SUPPORTED_POSITIONS = [] # 支持的显示位置
|
||||
DEFAULT_PRIORITY = 100 # 默认优先级(数字越小优先级越高)
|
||||
POSITION_PRIORITIES = {} # 各位置的优先级 {'sidebar': 50, 'article_bottom': 80}
|
||||
|
||||
def __init__(self):
|
||||
if not all([self.PLUGIN_NAME, self.PLUGIN_DESCRIPTION, self.PLUGIN_VERSION]):
|
||||
raise ValueError("Plugin metadata (PLUGIN_NAME, PLUGIN_DESCRIPTION, PLUGIN_VERSION) must be defined.")
|
||||
|
||||
# 设置插件路径
|
||||
self.plugin_dir = self._get_plugin_directory()
|
||||
self.plugin_slug = self._get_plugin_slug()
|
||||
|
||||
self.init_plugin()
|
||||
self.register_hooks()
|
||||
|
||||
def _get_plugin_directory(self):
|
||||
"""获取插件目录路径"""
|
||||
import inspect
|
||||
plugin_file = inspect.getfile(self.__class__)
|
||||
return Path(plugin_file).parent
|
||||
|
||||
def _get_plugin_slug(self):
|
||||
"""获取插件标识符(目录名)"""
|
||||
return self.plugin_dir.name
|
||||
|
||||
def init_plugin(self):
|
||||
"""
|
||||
插件初始化逻辑
|
||||
子类可以重写此方法来实现特定的初始化操作
|
||||
"""
|
||||
logger.info(f'{self.PLUGIN_NAME} initialized.')
|
||||
|
||||
def register_hooks(self):
|
||||
"""
|
||||
注册插件钩子
|
||||
子类可以重写此方法来注册特定的钩子
|
||||
"""
|
||||
pass
|
||||
|
||||
# === 位置渲染系统 ===
|
||||
def render_position_widget(self, position, context, **kwargs):
|
||||
"""
|
||||
根据位置渲染插件组件
|
||||
|
||||
Args:
|
||||
position: 位置标识
|
||||
context: 模板上下文
|
||||
**kwargs: 额外参数
|
||||
|
||||
Returns:
|
||||
dict: {'html': 'HTML内容', 'priority': 优先级} 或 None
|
||||
"""
|
||||
if position not in self.SUPPORTED_POSITIONS:
|
||||
return None
|
||||
|
||||
# 检查条件显示
|
||||
if not self.should_display(position, context, **kwargs):
|
||||
return None
|
||||
|
||||
# 调用具体的位置渲染方法
|
||||
method_name = f'render_{position}_widget'
|
||||
if hasattr(self, method_name):
|
||||
html = getattr(self, method_name)(context, **kwargs)
|
||||
if html:
|
||||
priority = self.POSITION_PRIORITIES.get(position, self.DEFAULT_PRIORITY)
|
||||
return {
|
||||
'html': html,
|
||||
'priority': priority,
|
||||
'plugin_name': self.PLUGIN_NAME
|
||||
}
|
||||
|
||||
return None
|
||||
|
||||
def should_display(self, position, context, **kwargs):
|
||||
"""
|
||||
判断插件是否应该在指定位置显示
|
||||
子类可重写此方法实现条件显示逻辑
|
||||
|
||||
Args:
|
||||
position: 位置标识
|
||||
context: 模板上下文
|
||||
**kwargs: 额外参数
|
||||
|
||||
Returns:
|
||||
bool: 是否显示
|
||||
"""
|
||||
return True
|
||||
|
||||
# === 各位置渲染方法 - 子类重写 ===
|
||||
def render_sidebar_widget(self, context, **kwargs):
|
||||
"""渲染侧边栏组件"""
|
||||
return None
|
||||
|
||||
def render_article_bottom_widget(self, context, **kwargs):
|
||||
"""渲染文章底部组件"""
|
||||
return None
|
||||
|
||||
def render_article_top_widget(self, context, **kwargs):
|
||||
"""渲染文章顶部组件"""
|
||||
return None
|
||||
|
||||
def render_header_widget(self, context, **kwargs):
|
||||
"""渲染页头组件"""
|
||||
return None
|
||||
|
||||
def render_footer_widget(self, context, **kwargs):
|
||||
"""渲染页脚组件"""
|
||||
return None
|
||||
|
||||
def render_comment_before_widget(self, context, **kwargs):
|
||||
"""渲染评论前组件"""
|
||||
return None
|
||||
|
||||
def render_comment_after_widget(self, context, **kwargs):
|
||||
"""渲染评论后组件"""
|
||||
return None
|
||||
|
||||
# === 模板系统 ===
|
||||
def render_template(self, template_name, context=None):
|
||||
"""
|
||||
渲染插件模板
|
||||
|
||||
Args:
|
||||
template_name: 模板文件名
|
||||
context: 模板上下文
|
||||
|
||||
Returns:
|
||||
HTML字符串
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
template_path = f"plugins/{self.plugin_slug}/{template_name}"
|
||||
|
||||
try:
|
||||
return render_to_string(template_path, context)
|
||||
except TemplateDoesNotExist:
|
||||
logger.warning(f"Plugin template not found: {template_path}")
|
||||
return ""
|
||||
|
||||
# === 静态资源系统 ===
|
||||
def get_static_url(self, static_file):
|
||||
"""获取插件静态文件URL"""
|
||||
from django.templatetags.static import static
|
||||
return static(f"{self.plugin_slug}/static/{self.plugin_slug}/{static_file}")
|
||||
|
||||
def get_css_files(self):
|
||||
"""获取插件CSS文件列表"""
|
||||
return []
|
||||
|
||||
def get_js_files(self):
|
||||
"""获取插件JavaScript文件列表"""
|
||||
return []
|
||||
|
||||
def get_head_html(self, context=None):
|
||||
"""获取需要插入到<head>中的HTML内容"""
|
||||
return ""
|
||||
|
||||
def get_body_html(self, context=None):
|
||||
"""获取需要插入到<body>底部的HTML内容"""
|
||||
return ""
|
||||
|
||||
def get_plugin_info(self):
|
||||
"""
|
||||
获取插件信息
|
||||
:return: 包含插件元数据的字典
|
||||
"""
|
||||
return {
|
||||
'name': self.PLUGIN_NAME,
|
||||
'description': self.PLUGIN_DESCRIPTION,
|
||||
'version': self.PLUGIN_VERSION,
|
||||
'author': self.PLUGIN_AUTHOR,
|
||||
'slug': self.plugin_slug,
|
||||
'directory': str(self.plugin_dir),
|
||||
'supported_positions': self.SUPPORTED_POSITIONS,
|
||||
'priorities': self.POSITION_PRIORITIES
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
ARTICLE_DETAIL_LOAD = 'article_detail_load'
|
||||
ARTICLE_CREATE = 'article_create'
|
||||
ARTICLE_UPDATE = 'article_update'
|
||||
ARTICLE_DELETE = 'article_delete'
|
||||
|
||||
ARTICLE_CONTENT_HOOK_NAME = "the_content"
|
||||
|
||||
# 位置钩子常量
|
||||
POSITION_HOOKS = {
|
||||
'article_top': 'article_top_widgets',
|
||||
'article_bottom': 'article_bottom_widgets',
|
||||
'sidebar': 'sidebar_widgets',
|
||||
'header': 'header_widgets',
|
||||
'footer': 'footer_widgets',
|
||||
'comment_before': 'comment_before_widgets',
|
||||
'comment_after': 'comment_after_widgets',
|
||||
}
|
||||
|
||||
# 资源注入钩子
|
||||
HEAD_RESOURCES_HOOK = 'head_resources'
|
||||
BODY_RESOURCES_HOOK = 'body_resources'
|
||||
|
||||
@ -1,64 +0,0 @@
|
||||
import os
|
||||
import logging
|
||||
from django.conf import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 全局插件注册表
|
||||
_loaded_plugins = []
|
||||
|
||||
def load_plugins():
|
||||
"""
|
||||
Dynamically loads and initializes plugins from the 'plugins' directory.
|
||||
This function is intended to be called when the Django app registry is ready.
|
||||
"""
|
||||
global _loaded_plugins
|
||||
_loaded_plugins = []
|
||||
|
||||
for plugin_name in settings.ACTIVE_PLUGINS:
|
||||
plugin_path = os.path.join(settings.PLUGINS_DIR, plugin_name)
|
||||
if os.path.isdir(plugin_path) and os.path.exists(os.path.join(plugin_path, 'plugin.py')):
|
||||
try:
|
||||
# 导入插件模块
|
||||
plugin_module = __import__(f'plugins.{plugin_name}.plugin', fromlist=['plugin'])
|
||||
|
||||
# 获取插件实例
|
||||
if hasattr(plugin_module, 'plugin'):
|
||||
plugin_instance = plugin_module.plugin
|
||||
_loaded_plugins.append(plugin_instance)
|
||||
logger.info(f"Successfully loaded plugin: {plugin_name} - {plugin_instance.PLUGIN_NAME}")
|
||||
else:
|
||||
logger.warning(f"Plugin {plugin_name} does not have 'plugin' instance")
|
||||
|
||||
except ImportError as e:
|
||||
logger.error(f"Failed to import plugin: {plugin_name}", exc_info=e)
|
||||
except AttributeError as e:
|
||||
logger.error(f"Failed to get plugin instance: {plugin_name}", exc_info=e)
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error loading plugin: {plugin_name}", exc_info=e)
|
||||
|
||||
def get_loaded_plugins():
|
||||
"""获取所有已加载的插件"""
|
||||
return _loaded_plugins
|
||||
|
||||
def get_plugin_by_name(plugin_name):
|
||||
"""根据名称获取插件"""
|
||||
for plugin in _loaded_plugins:
|
||||
if plugin.plugin_slug == plugin_name:
|
||||
return plugin
|
||||
return None
|
||||
|
||||
def get_plugin_by_slug(plugin_slug):
|
||||
"""根据slug获取插件"""
|
||||
for plugin in _loaded_plugins:
|
||||
if plugin.plugin_slug == plugin_slug:
|
||||
return plugin
|
||||
return None
|
||||
|
||||
def get_plugins_info():
|
||||
"""获取所有插件的信息"""
|
||||
return [plugin.get_plugin_info() for plugin in _loaded_plugins]
|
||||
|
||||
def get_plugins_by_position(position):
|
||||
"""获取支持指定位置的插件"""
|
||||
return [plugin for plugin in _loaded_plugins if position in plugin.SUPPORTED_POSITIONS]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +0,0 @@
|
||||
# 文章推荐插件
|
||||
@ -1,166 +0,0 @@
|
||||
/* 文章推荐插件样式 - 与网站风格保持一致 */
|
||||
|
||||
/* 文章底部推荐样式 */
|
||||
.article-recommendations {
|
||||
margin: 30px 0;
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border: 1px solid #e1e1e1;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.recommendations-title {
|
||||
margin: 0 0 15px 0;
|
||||
font-size: 18px;
|
||||
color: #444;
|
||||
font-weight: bold;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 2px solid #21759b;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.recommendations-icon {
|
||||
margin-right: 5px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.recommendations-grid {
|
||||
display: grid;
|
||||
gap: 15px;
|
||||
grid-template-columns: 1fr;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.recommendation-card {
|
||||
background: #fff;
|
||||
border: 1px solid #e1e1e1;
|
||||
border-radius: 3px;
|
||||
transition: all 0.2s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.recommendation-card:hover {
|
||||
border-color: #21759b;
|
||||
box-shadow: 0 2px 5px rgba(33, 117, 155, 0.1);
|
||||
}
|
||||
|
||||
.recommendation-link {
|
||||
display: block;
|
||||
padding: 15px;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.recommendation-title {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 15px;
|
||||
font-weight: normal;
|
||||
color: #444;
|
||||
line-height: 1.4;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.recommendation-card:hover .recommendation-title {
|
||||
color: #21759b;
|
||||
}
|
||||
|
||||
.recommendation-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
color: #757575;
|
||||
}
|
||||
|
||||
.recommendation-category {
|
||||
background: #ebebeb;
|
||||
color: #5e5e5e;
|
||||
padding: 2px 6px;
|
||||
border-radius: 2px;
|
||||
font-size: 11px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.recommendation-date {
|
||||
font-weight: normal;
|
||||
color: #757575;
|
||||
}
|
||||
|
||||
/* 侧边栏推荐样式 */
|
||||
.widget_recommendations {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.widget_recommendations .widget-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #007cba;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.recommendations-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.recommendations-list .recommendation-item {
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.recommendations-list .recommendation-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.recommendations-list .recommendation-item a {
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.recommendations-list .recommendation-item a:hover {
|
||||
color: #007cba;
|
||||
}
|
||||
|
||||
.recommendations-list .recommendation-meta {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.recommendations-list .recommendation-meta span {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/* 响应式设计 - 分栏显示 */
|
||||
@media (min-width: 768px) {
|
||||
.recommendations-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.recommendations-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.recommendations-grid {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 15px;
|
||||
}
|
||||
}
|
||||
@ -1,93 +0,0 @@
|
||||
/**
|
||||
* 文章推荐插件JavaScript
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// 等待DOM加载完成
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initRecommendations();
|
||||
});
|
||||
|
||||
function initRecommendations() {
|
||||
// 添加点击统计
|
||||
trackRecommendationClicks();
|
||||
|
||||
// 懒加载优化(如果需要)
|
||||
lazyLoadRecommendations();
|
||||
}
|
||||
|
||||
function trackRecommendationClicks() {
|
||||
const recommendationLinks = document.querySelectorAll('.recommendation-item a');
|
||||
|
||||
recommendationLinks.forEach(function(link) {
|
||||
link.addEventListener('click', function(e) {
|
||||
// 可以在这里添加点击统计逻辑
|
||||
const articleTitle = this.textContent.trim();
|
||||
const articleUrl = this.href;
|
||||
|
||||
// 发送统计数据到后端(可选)
|
||||
if (typeof gtag !== 'undefined') {
|
||||
gtag('event', 'click', {
|
||||
'event_category': 'recommendation',
|
||||
'event_label': articleTitle,
|
||||
'value': 1
|
||||
});
|
||||
}
|
||||
|
||||
console.log('Recommendation clicked:', articleTitle, articleUrl);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function lazyLoadRecommendations() {
|
||||
// 如果推荐内容很多,可以实现懒加载
|
||||
const recommendationContainer = document.querySelector('.article-recommendations');
|
||||
|
||||
if (!recommendationContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否在视窗中
|
||||
const observer = new IntersectionObserver(function(entries) {
|
||||
entries.forEach(function(entry) {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('loaded');
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
}, {
|
||||
threshold: 0.1
|
||||
});
|
||||
|
||||
const recommendationItems = document.querySelectorAll('.recommendation-item');
|
||||
recommendationItems.forEach(function(item) {
|
||||
observer.observe(item);
|
||||
});
|
||||
}
|
||||
|
||||
// 添加一些动画效果
|
||||
function addAnimations() {
|
||||
const recommendationItems = document.querySelectorAll('.recommendation-item');
|
||||
|
||||
recommendationItems.forEach(function(item, index) {
|
||||
item.style.opacity = '0';
|
||||
item.style.transform = 'translateY(20px)';
|
||||
item.style.transition = 'opacity 0.5s ease, transform 0.5s ease';
|
||||
|
||||
setTimeout(function() {
|
||||
item.style.opacity = '1';
|
||||
item.style.transform = 'translateY(0)';
|
||||
}, index * 100);
|
||||
});
|
||||
}
|
||||
|
||||
// 如果需要,可以在这里添加更多功能
|
||||
window.ArticleRecommendation = {
|
||||
init: initRecommendations,
|
||||
track: trackRecommendationClicks,
|
||||
animate: addAnimations
|
||||
};
|
||||
|
||||
})();
|
||||
@ -1 +0,0 @@
|
||||
# Image Lazy Loading Plugin
|
||||
@ -0,0 +1,8 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
@ -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$/DjangoBlog-master" />
|
||||
<option name="settingsModule" value="settings.py" />
|
||||
<option name="manageScript" value="$MODULE_DIR$/DjangoBlog-master/manage.py" />
|
||||
<option name="environment" value="<map/>" />
|
||||
<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$/DjangoBlog-master" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Django" 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$/DjangoBlog-master/templates" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</module>
|
||||
@ -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="Django" 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/DjangoBlog-master.iml" filepath="$PROJECT_DIR$/.idea/DjangoBlog-master.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,10 @@
|
||||
[run]
|
||||
source = .
|
||||
include = *.py
|
||||
omit =
|
||||
*migrations*
|
||||
*tests*
|
||||
*.html
|
||||
*whoosh_cn_backend*
|
||||
*settings.py*
|
||||
*venv*
|
||||
@ -1,12 +1,11 @@
|
||||
bin/data/
|
||||
# virtualenv
|
||||
venv/
|
||||
collectedstatic/
|
||||
djangoblog/whoosh_index/
|
||||
uploads/
|
||||
settings_production.py
|
||||
*.md
|
||||
docs/
|
||||
logs/
|
||||
static/
|
||||
.github/
|
||||
bin/data/
|
||||
# virtualenv
|
||||
venv/
|
||||
collectedstatic/
|
||||
djangoblog/whoosh_index/
|
||||
uploads/
|
||||
settings_production.py
|
||||
*.md
|
||||
docs/
|
||||
logs/
|
||||
static/
|
||||
@ -0,0 +1,136 @@
|
||||
name: Django CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- dev
|
||||
paths-ignore:
|
||||
- '**/*.md'
|
||||
- '**/*.css'
|
||||
- '**/*.js'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- dev
|
||||
paths-ignore:
|
||||
- '**/*.md'
|
||||
- '**/*.css'
|
||||
- '**/*.js'
|
||||
|
||||
jobs:
|
||||
build-normal:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
max-parallel: 4
|
||||
matrix:
|
||||
python-version: ["3.10","3.11" ]
|
||||
|
||||
steps:
|
||||
- name: Start MySQL
|
||||
uses: samin/mysql-action@v1.3
|
||||
with:
|
||||
host port: 3306
|
||||
container port: 3306
|
||||
character set server: utf8mb4
|
||||
collation server: utf8mb4_general_ci
|
||||
mysql version: latest
|
||||
mysql root password: root
|
||||
mysql database: djangoblog
|
||||
mysql user: root
|
||||
mysql password: root
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
- name: Run Tests
|
||||
env:
|
||||
DJANGO_MYSQL_PASSWORD: root
|
||||
DJANGO_MYSQL_HOST: 127.0.0.1
|
||||
run: |
|
||||
python manage.py makemigrations
|
||||
python manage.py migrate
|
||||
python manage.py test
|
||||
|
||||
build-with-es:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
max-parallel: 4
|
||||
matrix:
|
||||
python-version: ["3.10","3.11" ]
|
||||
|
||||
steps:
|
||||
- name: Start MySQL
|
||||
uses: samin/mysql-action@v1.3
|
||||
with:
|
||||
host port: 3306
|
||||
container port: 3306
|
||||
character set server: utf8mb4
|
||||
collation server: utf8mb4_general_ci
|
||||
mysql version: latest
|
||||
mysql root password: root
|
||||
mysql database: djangoblog
|
||||
mysql user: root
|
||||
mysql password: root
|
||||
|
||||
- name: Configure sysctl limits
|
||||
run: |
|
||||
sudo swapoff -a
|
||||
sudo sysctl -w vm.swappiness=1
|
||||
sudo sysctl -w fs.file-max=262144
|
||||
sudo sysctl -w vm.max_map_count=262144
|
||||
|
||||
- uses: miyataka/elasticsearch-github-actions@1
|
||||
|
||||
with:
|
||||
stack-version: '7.12.1'
|
||||
plugins: 'https://release.infinilabs.com/analysis-ik/stable/elasticsearch-analysis-ik-7.12.1.zip'
|
||||
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
- name: Run Tests
|
||||
env:
|
||||
DJANGO_MYSQL_PASSWORD: root
|
||||
DJANGO_MYSQL_HOST: 127.0.0.1
|
||||
DJANGO_ELASTICSEARCH_HOST: 127.0.0.1:9200
|
||||
run: |
|
||||
python manage.py makemigrations
|
||||
python manage.py migrate
|
||||
coverage run manage.py test
|
||||
coverage xml
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v1
|
||||
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
tags: djangoblog/djangoblog:dev
|
||||
@ -0,0 +1,59 @@
|
||||
from django import forms
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
from django.contrib.auth.forms import UserChangeForm
|
||||
from django.contrib.auth.forms import UsernameField
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
# Register your models here.
|
||||
from .models import BlogUser
|
||||
|
||||
|
||||
class BlogUserCreationForm(forms.ModelForm):
|
||||
password1 = forms.CharField(label=_('password'), widget=forms.PasswordInput)
|
||||
password2 = forms.CharField(label=_('Enter password again'), widget=forms.PasswordInput)
|
||||
|
||||
class Meta:
|
||||
model = BlogUser
|
||||
fields = ('email',)
|
||||
|
||||
def clean_password2(self):
|
||||
# Check that the two password entries match
|
||||
password1 = self.cleaned_data.get("password1")
|
||||
password2 = self.cleaned_data.get("password2")
|
||||
if password1 and password2 and password1 != password2:
|
||||
raise forms.ValidationError(_("passwords do not match"))
|
||||
return password2
|
||||
|
||||
def save(self, commit=True):
|
||||
# Save the provided password in hashed format
|
||||
user = super().save(commit=False)
|
||||
user.set_password(self.cleaned_data["password1"])
|
||||
if commit:
|
||||
user.source = 'adminsite'
|
||||
user.save()
|
||||
return user
|
||||
|
||||
|
||||
class BlogUserChangeForm(UserChangeForm):
|
||||
class Meta:
|
||||
model = BlogUser
|
||||
fields = '__all__'
|
||||
field_classes = {'username': UsernameField}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class BlogUserAdmin(UserAdmin):
|
||||
form = BlogUserChangeForm
|
||||
add_form = BlogUserCreationForm
|
||||
list_display = (
|
||||
'id',
|
||||
'nickname',
|
||||
'username',
|
||||
'email',
|
||||
'last_login',
|
||||
'date_joined',
|
||||
'source')
|
||||
list_display_links = ('id', 'username')
|
||||
ordering = ('-id',)
|
||||
@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AccountsConfig(AppConfig):
|
||||
name = 'accounts'
|
||||
@ -0,0 +1,49 @@
|
||||
# Generated by Django 4.1.7 on 2023-03-02 07:14
|
||||
|
||||
import django.contrib.auth.models
|
||||
import django.contrib.auth.validators
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('auth', '0012_alter_user_first_name_max_length'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='BlogUser',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
||||
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
||||
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
||||
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||
('nickname', models.CharField(blank=True, max_length=100, 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='修改时间')),
|
||||
('source', models.CharField(blank=True, max_length=100, verbose_name='创建来源')),
|
||||
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
|
||||
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '用户',
|
||||
'verbose_name_plural': '用户',
|
||||
'ordering': ['-id'],
|
||||
'get_latest_by': 'id',
|
||||
},
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,46 @@
|
||||
# Generated by Django 4.2.5 on 2023-09-06 13:13
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='bloguser',
|
||||
options={'get_latest_by': 'id', 'ordering': ['-id'], 'verbose_name': 'user', 'verbose_name_plural': 'user'},
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='bloguser',
|
||||
name='created_time',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='bloguser',
|
||||
name='last_mod_time',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='bloguser',
|
||||
name='creation_time',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='bloguser',
|
||||
name='last_modify_time',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='bloguser',
|
||||
name='nickname',
|
||||
field=models.CharField(blank=True, max_length=100, verbose_name='nick name'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='bloguser',
|
||||
name='source',
|
||||
field=models.CharField(blank=True, max_length=100, verbose_name='create source'),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,35 @@
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from djangoblog.utils import get_current_site
|
||||
|
||||
|
||||
# Create your models here.
|
||||
|
||||
class BlogUser(AbstractUser):
|
||||
nickname = models.CharField(_('nick name'), max_length=100, blank=True)
|
||||
creation_time = models.DateTimeField(_('creation time'), default=now)
|
||||
last_modify_time = models.DateTimeField(_('last modify time'), default=now)
|
||||
source = models.CharField(_('create source'), max_length=100, blank=True)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse(
|
||||
'blog:author_detail', kwargs={
|
||||
'author_name': self.username})
|
||||
|
||||
def __str__(self):
|
||||
return self.email
|
||||
|
||||
def get_full_url(self):
|
||||
site = get_current_site().domain
|
||||
url = "https://{site}{path}".format(site=site,
|
||||
path=self.get_absolute_url())
|
||||
return url
|
||||
|
||||
class Meta:
|
||||
ordering = ['-id']
|
||||
verbose_name = _('user')
|
||||
verbose_name_plural = verbose_name
|
||||
get_latest_by = 'id'
|
||||
@ -0,0 +1,207 @@
|
||||
from django.test import Client, RequestFactory, TestCase
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from accounts.models import BlogUser
|
||||
from blog.models import Article, Category
|
||||
from djangoblog.utils import *
|
||||
from . import utils
|
||||
|
||||
|
||||
# Create your tests here.
|
||||
|
||||
class AccountTest(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
self.factory = RequestFactory()
|
||||
self.blog_user = BlogUser.objects.create_user(
|
||||
username="test",
|
||||
email="admin@admin.com",
|
||||
password="12345678"
|
||||
)
|
||||
self.new_test = "xxx123--="
|
||||
|
||||
def test_validate_account(self):
|
||||
site = get_current_site().domain
|
||||
user = BlogUser.objects.create_superuser(
|
||||
email="liangliangyy1@gmail.com",
|
||||
username="liangliangyy1",
|
||||
password="qwer!@#$ggg")
|
||||
testuser = BlogUser.objects.get(username='liangliangyy1')
|
||||
|
||||
loginresult = self.client.login(
|
||||
username='liangliangyy1',
|
||||
password='qwer!@#$ggg')
|
||||
self.assertEqual(loginresult, True)
|
||||
response = self.client.get('/admin/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
category = Category()
|
||||
category.name = "categoryaaa"
|
||||
category.creation_time = timezone.now()
|
||||
category.last_modify_time = timezone.now()
|
||||
category.save()
|
||||
|
||||
article = Article()
|
||||
article.title = "nicetitleaaa"
|
||||
article.body = "nicecontentaaa"
|
||||
article.author = user
|
||||
article.category = category
|
||||
article.type = 'a'
|
||||
article.status = 'p'
|
||||
article.save()
|
||||
|
||||
response = self.client.get(article.get_admin_url())
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_validate_register(self):
|
||||
self.assertEquals(
|
||||
0, len(
|
||||
BlogUser.objects.filter(
|
||||
email='user123@user.com')))
|
||||
response = self.client.post(reverse('account:register'), {
|
||||
'username': 'user1233',
|
||||
'email': 'user123@user.com',
|
||||
'password1': 'password123!q@wE#R$T',
|
||||
'password2': 'password123!q@wE#R$T',
|
||||
})
|
||||
self.assertEquals(
|
||||
1, len(
|
||||
BlogUser.objects.filter(
|
||||
email='user123@user.com')))
|
||||
user = BlogUser.objects.filter(email='user123@user.com')[0]
|
||||
sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
|
||||
path = reverse('accounts:result')
|
||||
url = '{path}?type=validation&id={id}&sign={sign}'.format(
|
||||
path=path, id=user.id, sign=sign)
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
self.client.login(username='user1233', password='password123!q@wE#R$T')
|
||||
user = BlogUser.objects.filter(email='user123@user.com')[0]
|
||||
user.is_superuser = True
|
||||
user.is_staff = True
|
||||
user.save()
|
||||
delete_sidebar_cache()
|
||||
category = Category()
|
||||
category.name = "categoryaaa"
|
||||
category.creation_time = timezone.now()
|
||||
category.last_modify_time = timezone.now()
|
||||
category.save()
|
||||
|
||||
article = Article()
|
||||
article.category = category
|
||||
article.title = "nicetitle333"
|
||||
article.body = "nicecontentttt"
|
||||
article.author = user
|
||||
|
||||
article.type = 'a'
|
||||
article.status = 'p'
|
||||
article.save()
|
||||
|
||||
response = self.client.get(article.get_admin_url())
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
response = self.client.get(reverse('account:logout'))
|
||||
self.assertIn(response.status_code, [301, 302, 200])
|
||||
|
||||
response = self.client.get(article.get_admin_url())
|
||||
self.assertIn(response.status_code, [301, 302, 200])
|
||||
|
||||
response = self.client.post(reverse('account:login'), {
|
||||
'username': 'user1233',
|
||||
'password': 'password123'
|
||||
})
|
||||
self.assertIn(response.status_code, [301, 302, 200])
|
||||
|
||||
response = self.client.get(article.get_admin_url())
|
||||
self.assertIn(response.status_code, [301, 302, 200])
|
||||
|
||||
def test_verify_email_code(self):
|
||||
to_email = "admin@admin.com"
|
||||
code = generate_code()
|
||||
utils.set_code(to_email, code)
|
||||
utils.send_verify_email(to_email, code)
|
||||
|
||||
err = utils.verify("admin@admin.com", code)
|
||||
self.assertEqual(err, None)
|
||||
|
||||
err = utils.verify("admin@123.com", code)
|
||||
self.assertEqual(type(err), str)
|
||||
|
||||
def test_forget_password_email_code_success(self):
|
||||
resp = self.client.post(
|
||||
path=reverse("account:forget_password_code"),
|
||||
data=dict(email="admin@admin.com")
|
||||
)
|
||||
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertEqual(resp.content.decode("utf-8"), "ok")
|
||||
|
||||
def test_forget_password_email_code_fail(self):
|
||||
resp = self.client.post(
|
||||
path=reverse("account:forget_password_code"),
|
||||
data=dict()
|
||||
)
|
||||
self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
|
||||
|
||||
resp = self.client.post(
|
||||
path=reverse("account:forget_password_code"),
|
||||
data=dict(email="admin@com")
|
||||
)
|
||||
self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
|
||||
|
||||
def test_forget_password_email_success(self):
|
||||
code = generate_code()
|
||||
utils.set_code(self.blog_user.email, code)
|
||||
data = dict(
|
||||
new_password1=self.new_test,
|
||||
new_password2=self.new_test,
|
||||
email=self.blog_user.email,
|
||||
code=code,
|
||||
)
|
||||
resp = self.client.post(
|
||||
path=reverse("account:forget_password"),
|
||||
data=data
|
||||
)
|
||||
self.assertEqual(resp.status_code, 302)
|
||||
|
||||
# 验证用户密码是否修改成功
|
||||
blog_user = BlogUser.objects.filter(
|
||||
email=self.blog_user.email,
|
||||
).first() # type: BlogUser
|
||||
self.assertNotEqual(blog_user, None)
|
||||
self.assertEqual(blog_user.check_password(data["new_password1"]), True)
|
||||
|
||||
def test_forget_password_email_not_user(self):
|
||||
data = dict(
|
||||
new_password1=self.new_test,
|
||||
new_password2=self.new_test,
|
||||
email="123@123.com",
|
||||
code="123456",
|
||||
)
|
||||
resp = self.client.post(
|
||||
path=reverse("account:forget_password"),
|
||||
data=data
|
||||
)
|
||||
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
|
||||
def test_forget_password_email_code_error(self):
|
||||
code = generate_code()
|
||||
utils.set_code(self.blog_user.email, code)
|
||||
data = dict(
|
||||
new_password1=self.new_test,
|
||||
new_password2=self.new_test,
|
||||
email=self.blog_user.email,
|
||||
code="111111",
|
||||
)
|
||||
resp = self.client.post(
|
||||
path=reverse("account:forget_password"),
|
||||
data=data
|
||||
)
|
||||
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue