forked from mrlan/EnglishPal
Repalce old app folder with SoftArch王炫/english-pal-master/app/
parent
72deae7d3a
commit
4d2dd2b68e
|
@ -0,0 +1,137 @@
|
|||
from WordFreq import WordFreq
|
||||
from wordfreqCMD import youdao_link, sort_in_descending_order
|
||||
from UseSqlite import InsertQuery, RecordQuery
|
||||
import pickle_idea, pickle_idea2
|
||||
import os
|
||||
import random, glob
|
||||
import hashlib
|
||||
from datetime import datetime
|
||||
from flask import Flask, request, redirect, render_template, url_for, session, abort, flash, get_flashed_messages
|
||||
from difficulty import get_difficulty_level, text_difficulty_level, user_difficulty_level
|
||||
|
||||
|
||||
path_prefix = '/var/www/wordfreq/wordfreq/'
|
||||
path_prefix = './' # comment this line in deployment
|
||||
|
||||
|
||||
def total_number_of_essays():
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions("SELECT * FROM article")
|
||||
rq.do()
|
||||
result = rq.get_results()
|
||||
return len(result)
|
||||
|
||||
|
||||
def get_article_title(s):
|
||||
return s.split('\n')[0]
|
||||
|
||||
|
||||
def get_article_body(s):
|
||||
lst = s.split('\n')
|
||||
lst.pop(0) # remove the first line
|
||||
return '\n'.join(lst)
|
||||
|
||||
|
||||
def get_today_article(user_word_list, articleID):
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
if articleID == None:
|
||||
rq.instructions("SELECT * FROM article")
|
||||
else:
|
||||
rq.instructions('SELECT * FROM article WHERE article_id=%d' % (articleID))
|
||||
rq.do()
|
||||
result = rq.get_results()
|
||||
random.shuffle(result)
|
||||
|
||||
# Choose article according to reader's level
|
||||
d1 = load_freq_history(path_prefix + 'static/frequency/frequency.p')
|
||||
d2 = load_freq_history(path_prefix + 'static/words_and_tests.p')
|
||||
d3 = get_difficulty_level(d1, d2)
|
||||
|
||||
d = {}
|
||||
d_user = load_freq_history(user_word_list)
|
||||
user_level = user_difficulty_level(d_user,
|
||||
d3) # more consideration as user's behaviour is dynamic. Time factor should be considered.
|
||||
random.shuffle(result) # shuffle list
|
||||
d = random.choice(result)
|
||||
text_level = text_difficulty_level(d['text'], d3)
|
||||
if articleID == None:
|
||||
for reading in result:
|
||||
text_level = text_difficulty_level(reading['text'], d3)
|
||||
factor = random.gauss(0.8,
|
||||
0.1) # a number drawn from Gaussian distribution with a mean of 0.8 and a stand deviation of 1
|
||||
if within_range(text_level, user_level, (8.0 - user_level) * factor):
|
||||
d = reading
|
||||
break
|
||||
|
||||
s = '<div class="alert alert-success" role="alert">According to your word list, your level is <span class="badge bg-success">%4.2f</span> and we have chosen an article with a difficulty level of <span class="badge bg-success">%4.2f</span> for you.</div>' % (
|
||||
user_level, text_level)
|
||||
s += '<p class="text-muted">Article added on: %s</p>' % (d['date'])
|
||||
s += '<div class="p-3 mb-2 bg-light text-dark">'
|
||||
article_title = get_article_title(d['text'])
|
||||
article_body = get_article_body(d['text'])
|
||||
s += '<p class="display-3">%s</p>' % (article_title)
|
||||
s += '<p class="lead"><font id="article" size=2>%s</font></p>' % (article_body)
|
||||
s += '<p><small class="text-muted">%s</small></p>' % (d['source'])
|
||||
s += '<p><b>%s</b></p>' % (get_question_part(d['question']))
|
||||
s = s.replace('\n', '<br/>')
|
||||
s += '%s' % (get_answer_part(d['question']))
|
||||
s += '</div>'
|
||||
session['articleID'] = d['article_id']
|
||||
return s
|
||||
|
||||
|
||||
def load_freq_history(path):
|
||||
d = {}
|
||||
if os.path.exists(path):
|
||||
d = pickle_idea.load_record(path)
|
||||
return d
|
||||
|
||||
|
||||
def within_range(x, y, r):
|
||||
return x > y and abs(x - y) <= r
|
||||
|
||||
|
||||
def get_question_part(s):
|
||||
s = s.strip()
|
||||
result = []
|
||||
flag = 0
|
||||
for line in s.split('\n'):
|
||||
line = line.strip()
|
||||
if line == 'QUESTION':
|
||||
result.append(line)
|
||||
flag = 1
|
||||
elif line == 'ANSWER':
|
||||
flag = 0
|
||||
elif flag == 1:
|
||||
result.append(line)
|
||||
return '\n'.join(result)
|
||||
|
||||
|
||||
def get_answer_part(s):
|
||||
s = s.strip()
|
||||
result = []
|
||||
flag = 0
|
||||
for line in s.split('\n'):
|
||||
line = line.strip()
|
||||
if line == 'ANSWER':
|
||||
flag = 1
|
||||
elif flag == 1:
|
||||
result.append(line)
|
||||
# https://css-tricks.com/snippets/javascript/showhide-element/
|
||||
js = '''
|
||||
<script type="text/javascript">
|
||||
|
||||
function toggle_visibility(id) {
|
||||
var e = document.getElementById(id);
|
||||
if(e.style.display == 'block')
|
||||
e.style.display = 'none';
|
||||
else
|
||||
e.style.display = 'block';
|
||||
}
|
||||
</script>
|
||||
'''
|
||||
html_code = js
|
||||
html_code += '\n'
|
||||
html_code += '<button onclick="toggle_visibility(\'answer\');">ANSWER</button>\n'
|
||||
html_code += '<div id="answer" style="display:none;">%s</div>\n' % ('\n'.join(result))
|
||||
return html_code
|
|
@ -0,0 +1,77 @@
|
|||
import hashlib
|
||||
from datetime import datetime
|
||||
|
||||
from UseSqlite import InsertQuery, RecordQuery
|
||||
|
||||
path_prefix = '/var/www/wordfreq/wordfreq/'
|
||||
path_prefix = './' # comment this line in deployment
|
||||
|
||||
|
||||
def verify_user(username, password):
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
password = md5(username + password)
|
||||
rq.instructions_with_parameters("SELECT * FROM user WHERE name=? AND password=?", (username, password))
|
||||
rq.do_with_parameters()
|
||||
result = rq.get_results()
|
||||
return result != []
|
||||
|
||||
|
||||
def add_user(username, password):
|
||||
start_date = datetime.now().strftime('%Y%m%d')
|
||||
expiry_date = '20211230'
|
||||
# 将用户名和密码一起加密,以免暴露不同用户的相同密码
|
||||
password = md5(username + password)
|
||||
rq = InsertQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions("INSERT INTO user Values ('%s', '%s', '%s', '%s')" % (username, password, start_date, expiry_date))
|
||||
rq.do()
|
||||
|
||||
|
||||
def check_username_availability(username):
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions("SELECT * FROM user WHERE name='%s'" % (username))
|
||||
rq.do()
|
||||
result = rq.get_results()
|
||||
return result == []
|
||||
|
||||
|
||||
def change_password(username, old_psd, new_psd):
|
||||
'''
|
||||
修改密码
|
||||
:param username: 用户名
|
||||
:param old_psd: 旧的密码
|
||||
:param new_psd: 新密码
|
||||
:return: 修改成功:True 否则:False
|
||||
'''
|
||||
if not verify_user(username, old_psd): # 旧密码错误
|
||||
return False
|
||||
# 将用户名和密码一起加密,以免暴露不同用户的相同密码
|
||||
password = md5(username + new_psd)
|
||||
rq = InsertQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions("UPDATE user SET password = '%s' WHERE name = '%s'" % (password, username))
|
||||
rq.do()
|
||||
return True
|
||||
|
||||
|
||||
def get_expiry_date(username):
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions("SELECT expiry_date FROM user WHERE name='%s'" % (username))
|
||||
rq.do()
|
||||
result = rq.get_results()
|
||||
if len(result) > 0:
|
||||
return result[0]['expiry_date']
|
||||
else:
|
||||
return '20191024'
|
||||
|
||||
|
||||
def md5(str):
|
||||
'''
|
||||
MD5摘要
|
||||
:param str: 字符串
|
||||
:return: 经MD5以后的字符串
|
||||
'''
|
||||
# 当前MD5已被关闭,若要开启需删除下面两行
|
||||
print("MD5尚未开启")
|
||||
return str
|
||||
# 当前MD5已被关闭,若要开启需删除上面两行
|
||||
h = hashlib.md5(str.encode(encoding='utf-8'))
|
||||
return h.hexdigest()
|
|
@ -0,0 +1,27 @@
|
|||
'''
|
||||
Yaml.py
|
||||
配置文件包括:
|
||||
./static/config.yml
|
||||
./layout/partial/header.html
|
||||
./layout/partial/footer.html
|
||||
'''
|
||||
import yaml as YAML
|
||||
import os
|
||||
|
||||
path_prefix = './' # comment this line in deployment
|
||||
|
||||
# YAML文件路径
|
||||
ymlPath = path_prefix + 'static/config.yml'
|
||||
|
||||
# partial文件夹路径
|
||||
partialPath = path_prefix + 'layout/partial/'
|
||||
f = open(ymlPath, 'r', encoding='utf-8') # 以'UTF-8'格式打开YAML文件
|
||||
cont = f.read() # 以文本形式读取YAML
|
||||
|
||||
yml = YAML.load(cont, Loader=YAML.FullLoader) # 加载YAML
|
||||
|
||||
with open(partialPath + 'header.html', 'r', encoding='utf-8') as f:
|
||||
yml['header'] = f.read() # header内的文本会被直接添加到所有页面的head标签内
|
||||
|
||||
with open(partialPath + 'footer.html', 'r', encoding='utf-8') as f:
|
||||
yml['footer'] = f.read() # footer内的文本会被直接添加到所有页面的最底部
|
|
@ -0,0 +1,129 @@
|
|||
from flask import *
|
||||
from Login import check_username_availability, verify_user, add_user, get_expiry_date, change_password
|
||||
|
||||
# 初始化蓝图
|
||||
accountService = Blueprint("accountService", __name__)
|
||||
|
||||
|
||||
### Sign-up, login, logout ###
|
||||
@accountService.route("/signup", methods=['GET', 'POST'])
|
||||
def signup():
|
||||
'''
|
||||
注册
|
||||
:return: 根据注册是否成功返回不同界面
|
||||
'''
|
||||
if request.method == 'GET':
|
||||
# GET方法直接返回注册页面
|
||||
return render_template('signup.html')
|
||||
elif request.method == 'POST':
|
||||
# POST方法需判断是否注册成功,再根据结果返回不同的内容
|
||||
username = request.form['username']
|
||||
password = request.form['password']
|
||||
|
||||
available = check_username_availability(username)
|
||||
if not available: # 用户名不可用
|
||||
flash('用户名 %s 已经被注册。' % (username))
|
||||
return render_template('signup.html')
|
||||
elif len(password.strip()) < 4: # 密码过短
|
||||
return '密码过于简单。'
|
||||
else: # 添加账户信息
|
||||
add_user(username, password)
|
||||
verified = verify_user(username, password)
|
||||
if verified:
|
||||
# 写入session
|
||||
session['logged_in'] = True
|
||||
session[username] = username
|
||||
session['username'] = username
|
||||
session['expiry_date'] = get_expiry_date(username)
|
||||
session['articleID'] = None
|
||||
return '<p>恭喜,你已成功注册, 你的用户名是 <a href="%s">%s</a>。</p>\
|
||||
<p><a href="/%s">开始使用</a> <a href="/">返回首页</a><p/>' % (username, username, username)
|
||||
else:
|
||||
return '用户名密码验证失败。'
|
||||
|
||||
|
||||
@accountService.route("/login", methods=['GET', 'POST'])
|
||||
def login():
|
||||
'''
|
||||
登录
|
||||
:return: 根据登录是否成功返回不同页面
|
||||
'''
|
||||
if request.method == 'GET':
|
||||
# GET请求
|
||||
if not session.get('logged_in'):
|
||||
# 未登录,返回登录页面
|
||||
return render_template('login.html')
|
||||
else:
|
||||
# 已登录,提示信息并显示登出按钮
|
||||
return '你已登录 <a href="/%s">%s</a>。 登出点击<a href="/logout">这里</a>。' % (
|
||||
session['username'], session['username'])
|
||||
elif request.method == 'POST':
|
||||
# POST方法用于判断登录是否成功
|
||||
# check database and verify user
|
||||
username = request.form['username']
|
||||
password = request.form['password']
|
||||
verified = verify_user(username, password)
|
||||
if verified:
|
||||
# 登录成功,写入session
|
||||
session['logged_in'] = True
|
||||
session[username] = username
|
||||
session['username'] = username
|
||||
user_expiry_date = get_expiry_date(username)
|
||||
session['expiry_date'] = user_expiry_date
|
||||
session['articleID'] = None
|
||||
return redirect(url_for('user_bp.userpage', username=username))
|
||||
else:
|
||||
return '无法通过验证。'
|
||||
|
||||
|
||||
@accountService.route("/logout", methods=['GET', 'POST'])
|
||||
def logout():
|
||||
'''
|
||||
登出
|
||||
:return: 重定位到主界面
|
||||
'''
|
||||
# 将session标记为登出状态
|
||||
session['logged_in'] = False
|
||||
return redirect(url_for('mainpage'))
|
||||
|
||||
|
||||
@accountService.route("/reset", methods=['GET', 'POST'])
|
||||
def reset():
|
||||
'''
|
||||
重设密码
|
||||
:return: 返回适当的页面
|
||||
'''
|
||||
# 下列方法用于防止未登录状态下的修改密码
|
||||
if not session.get('logged_in'):
|
||||
return render_template('login.html')
|
||||
username = session['username']
|
||||
if username == '':
|
||||
return redirect('/login')
|
||||
if request.method == 'GET':
|
||||
# GET请求返回修改密码页面
|
||||
return render_template('reset.html', username=session['username'], state='wait')
|
||||
else:
|
||||
# POST请求用于提交修改后信息
|
||||
old_psd = request.form['old-psd']
|
||||
new_psd = request.form['new-psd']
|
||||
flag = change_password(username, old_psd, new_psd) # flag表示是否修改成功
|
||||
if flag:
|
||||
session['logged_in'] = False
|
||||
return \
|
||||
'''
|
||||
<script>
|
||||
alert('修改密码成功!!!请重新登录');
|
||||
window.location.href="/login";
|
||||
</script>
|
||||
|
||||
'''
|
||||
|
||||
else:
|
||||
return \
|
||||
'''
|
||||
<script>
|
||||
alert('修改密码失败!!!');
|
||||
window.location.href="/reset";
|
||||
</script>
|
||||
|
||||
'''
|
|
@ -0,0 +1,21 @@
|
|||
<!--
|
||||
以html的形式插入到所有页面的页尾
|
||||
-->
|
||||
<!--
|
||||
<br/><br/><br/>
|
||||
<div class="aplayer-container">
|
||||
<meting-js
|
||||
theme='#1BCDFC'
|
||||
autoplay='false'
|
||||
volume='0.5'
|
||||
loop='all'
|
||||
order='list'
|
||||
fixed='true'
|
||||
list-max-height='160px'
|
||||
server='netease'
|
||||
type='playlist'
|
||||
id='6927872292'
|
||||
list-folded='true'>
|
||||
</meting-js>
|
||||
</div>
|
||||
-->
|
|
@ -0,0 +1,3 @@
|
|||
<!--
|
||||
以html的形式插入所有网页的head标签中
|
||||
-->
|
478
app/main.py
478
app/main.py
|
@ -6,214 +6,61 @@
|
|||
# Written permission must be obtained from the author for commercial uses.
|
||||
###########################################################################
|
||||
|
||||
from WordFreq import WordFreq
|
||||
from wordfreqCMD import youdao_link, sort_in_descending_order
|
||||
from UseSqlite import InsertQuery, RecordQuery
|
||||
import pickle_idea, pickle_idea2
|
||||
import os
|
||||
import random, glob
|
||||
from datetime import datetime
|
||||
from flask import Flask, request, redirect, render_template, url_for, session, abort, flash, get_flashed_messages
|
||||
from difficulty import get_difficulty_level, text_difficulty_level, user_difficulty_level
|
||||
|
||||
from Login import *
|
||||
from Article import *
|
||||
import Yaml
|
||||
from user_service import userService
|
||||
from account_service import accountService
|
||||
app = Flask(__name__)
|
||||
app.secret_key = 'lunch.time!'
|
||||
|
||||
# 将蓝图注册到Lab app
|
||||
app.register_blueprint(userService)
|
||||
app.register_blueprint(accountService)
|
||||
|
||||
path_prefix = '/var/www/wordfreq/wordfreq/'
|
||||
path_prefix = './' # comment this line in deployment
|
||||
|
||||
|
||||
def get_random_image(path):
|
||||
'''
|
||||
返回随机图
|
||||
:param path: 图片文件(JPEG格式),不包含后缀名
|
||||
:return:
|
||||
'''
|
||||
img_path = random.choice(glob.glob(os.path.join(path, '*.jpg')))
|
||||
|
||||
return img_path[img_path.rfind('/static'):]
|
||||
|
||||
|
||||
def get_random_ads():
|
||||
'''
|
||||
返回随机广告
|
||||
:return: 一个广告(包含HTML标签)
|
||||
'''
|
||||
ads = random.choice(['个性化分析精准提升', '你的专有单词本', '智能捕捉阅读弱点,针对性提高你的阅读水平'])
|
||||
return ads + '。 <a href="/signup">试试</a>吧!'
|
||||
|
||||
def total_number_of_essays():
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions("SELECT * FROM article")
|
||||
rq.do()
|
||||
result = rq.get_results()
|
||||
return len(result)
|
||||
|
||||
def load_freq_history(path):
|
||||
d = {}
|
||||
if os.path.exists(path):
|
||||
d = pickle_idea.load_record(path)
|
||||
return d
|
||||
|
||||
def verify_user(username, password):
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions_with_parameters("SELECT * FROM user WHERE name=? AND password=?", (username, password))
|
||||
rq.do_with_parameters()
|
||||
result = rq.get_results()
|
||||
return result != []
|
||||
|
||||
def add_user(username, password):
|
||||
start_date = datetime.now().strftime('%Y%m%d')
|
||||
expiry_date = '20211230'
|
||||
rq = InsertQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions("INSERT INTO user Values ('%s', '%s', '%s', '%s')" % (username, password, start_date, expiry_date))
|
||||
rq.do()
|
||||
|
||||
|
||||
def check_username_availability(username):
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions("SELECT * FROM user WHERE name='%s'" % (username))
|
||||
rq.do()
|
||||
result = rq.get_results()
|
||||
return result == []
|
||||
|
||||
def get_expiry_date(username):
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
rq.instructions("SELECT expiry_date FROM user WHERE name='%s'" % (username))
|
||||
rq.do()
|
||||
result = rq.get_results()
|
||||
if len(result) > 0:
|
||||
return result[0]['expiry_date']
|
||||
else:
|
||||
return '20191024'
|
||||
|
||||
|
||||
|
||||
def within_range(x, y, r):
|
||||
return x > y and abs(x - y) <= r
|
||||
|
||||
|
||||
def get_article_title(s):
|
||||
return s.split('\n')[0]
|
||||
|
||||
|
||||
def get_article_body(s):
|
||||
lst = s.split('\n')
|
||||
lst.pop(0) # remove the first line
|
||||
return '\n'.join(lst)
|
||||
|
||||
def get_today_article(user_word_list, articleID):
|
||||
|
||||
rq = RecordQuery(path_prefix + 'static/wordfreqapp.db')
|
||||
if articleID == None:
|
||||
rq.instructions("SELECT * FROM article")
|
||||
else:
|
||||
rq.instructions('SELECT * FROM article WHERE article_id=%d' % (articleID))
|
||||
rq.do()
|
||||
result = rq.get_results()
|
||||
random.shuffle(result)
|
||||
|
||||
# Choose article according to reader's level
|
||||
d1 = load_freq_history(path_prefix + 'static/frequency/frequency.p')
|
||||
d2 = load_freq_history(path_prefix + 'static/words_and_tests.p')
|
||||
d3 = get_difficulty_level(d1, d2)
|
||||
|
||||
d = {}
|
||||
d_user = load_freq_history(user_word_list)
|
||||
user_level = user_difficulty_level(d_user, d3) # more consideration as user's behaviour is dynamic. Time factor should be considered.
|
||||
random.shuffle(result) # shuffle list
|
||||
d = random.choice(result)
|
||||
text_level = text_difficulty_level(d['text'], d3)
|
||||
if articleID == None:
|
||||
for reading in result:
|
||||
text_level = text_difficulty_level(reading['text'], d3)
|
||||
factor = random.gauss(0.8, 0.1) # a number drawn from Gaussian distribution with a mean of 0.8 and a stand deviation of 1
|
||||
if within_range(text_level, user_level, (8.0 - user_level)*factor):
|
||||
d = reading
|
||||
break
|
||||
|
||||
s = '<div class="alert alert-success" role="alert">According to your word list, your level is <span class="badge bg-success">%4.2f</span> and we have chosen an article with a difficulty level of <span class="badge bg-success">%4.2f</span> for you.</div>' % (user_level, text_level)
|
||||
s += '<p class="text-muted">Article added on: %s</p>' % (d['date'])
|
||||
s += '<div class="p-3 mb-2 bg-light text-dark">'
|
||||
article_title = get_article_title(d['text'])
|
||||
article_body = get_article_body(d['text'])
|
||||
s += '<p class="display-3">%s</p>' % (article_title)
|
||||
s += '<p class="lead">%s</p>' % (article_body)
|
||||
s += '<p><small class="text-muted">%s</small></p>' % (d['source'])
|
||||
s += '<p><b>%s</b></p>' % (get_question_part(d['question']))
|
||||
s = s.replace('\n', '<br/>')
|
||||
s += '%s' % (get_answer_part(d['question']))
|
||||
s += '</div>'
|
||||
session['articleID'] = d['article_id']
|
||||
return s
|
||||
|
||||
|
||||
def appears_in_test(word, d):
|
||||
'''
|
||||
如果字符串里没有指定的单词,则返回逗号加单词
|
||||
:param word: 指定单词
|
||||
:param d: 字符串
|
||||
:return: 逗号加单词
|
||||
'''
|
||||
if not word in d:
|
||||
return ''
|
||||
else:
|
||||
return ','.join(d[word])
|
||||
|
||||
|
||||
def get_time():
|
||||
return datetime.now().strftime('%Y%m%d%H%M') # upper to minutes
|
||||
|
||||
|
||||
def get_question_part(s):
|
||||
s = s.strip()
|
||||
result = []
|
||||
flag = 0
|
||||
for line in s.split('\n'):
|
||||
line = line.strip()
|
||||
if line == 'QUESTION':
|
||||
result.append(line)
|
||||
flag = 1
|
||||
elif line == 'ANSWER':
|
||||
flag = 0
|
||||
elif flag == 1:
|
||||
result.append(line)
|
||||
return '\n'.join(result)
|
||||
|
||||
|
||||
def get_answer_part(s):
|
||||
s = s.strip()
|
||||
result = []
|
||||
flag = 0
|
||||
for line in s.split('\n'):
|
||||
line = line.strip()
|
||||
if line == 'ANSWER':
|
||||
flag = 1
|
||||
elif flag == 1:
|
||||
result.append(line)
|
||||
# https://css-tricks.com/snippets/javascript/showhide-element/
|
||||
js = '''
|
||||
<script type="text/javascript">
|
||||
|
||||
function toggle_visibility(id) {
|
||||
var e = document.getElementById(id);
|
||||
if(e.style.display == 'block')
|
||||
e.style.display = 'none';
|
||||
else
|
||||
e.style.display = 'block';
|
||||
}
|
||||
</script>
|
||||
'''
|
||||
html_code = js
|
||||
html_code += '\n'
|
||||
html_code += '<button onclick="toggle_visibility(\'answer\');">ANSWER</button>\n'
|
||||
html_code += '<div id="answer" style="display:none;">%s</div>\n' % ('\n'.join(result))
|
||||
return html_code
|
||||
|
||||
|
||||
def get_flashed_messages_if_any():
|
||||
messages = get_flashed_messages()
|
||||
s = ''
|
||||
for message in messages:
|
||||
s += '<div class="alert alert-warning" role="alert">'
|
||||
s += f'Congratulations! {message}'
|
||||
s += '</div>'
|
||||
return s
|
||||
|
||||
|
||||
@app.route("/<username>/reset", methods=['GET', 'POST'])
|
||||
def user_reset(username):
|
||||
if request.method == 'GET':
|
||||
session['articleID'] = None
|
||||
return redirect(url_for('userpage', username=username))
|
||||
else:
|
||||
return 'Under construction'
|
||||
|
||||
|
||||
@app.route("/mark", methods=['GET', 'POST'])
|
||||
def mark_word():
|
||||
'''
|
||||
标记单词
|
||||
:return: 重定位到主界面
|
||||
'''
|
||||
if request.method == 'POST':
|
||||
d = load_freq_history(path_prefix + 'static/frequency/frequency.p')
|
||||
lst_history = pickle_idea.dict2lst(d)
|
||||
|
@ -223,276 +70,45 @@ def mark_word():
|
|||
d = pickle_idea.merge_frequency(lst, lst_history)
|
||||
pickle_idea.save_frequency_to_pickle(d, path_prefix + 'static/frequency/frequency.p')
|
||||
return redirect(url_for('mainpage'))
|
||||
else:
|
||||
else: # 不回应GET请求
|
||||
return 'Under construction'
|
||||
|
||||
|
||||
|
||||
@app.route("/", methods=['GET', 'POST'])
|
||||
def mainpage():
|
||||
'''
|
||||
根据GET或POST方法来返回不同的主界面
|
||||
:return: 主界面
|
||||
'''
|
||||
if request.method == 'POST': # when we submit a form
|
||||
content = request.form['content']
|
||||
f = WordFreq(content)
|
||||
lst = f.get_freq()
|
||||
page = '<form method="post" action="/mark">\n'
|
||||
count = 1
|
||||
for x in lst:
|
||||
page += '<p><font color="grey">%d</font>: <a href="%s">%s</a> (%d) <input type="checkbox" name="marked" value="%s"></p>\n' % (count, youdao_link(x[0]), x[0], x[1], x[0])
|
||||
count += 1
|
||||
page += ' <input type="submit" value="确定并返回"/>\n'
|
||||
page += '</form>\n'
|
||||
# save history
|
||||
d = load_freq_history(path_prefix + 'static/frequency/frequency.p')
|
||||
lst_history = pickle_idea.dict2lst(d)
|
||||
d = pickle_idea.merge_frequency(lst, lst_history)
|
||||
pickle_idea.save_frequency_to_pickle(d, path_prefix + 'static/frequency/frequency.p')
|
||||
return render_template('mainpage_post.html', lst=lst, yml=Yaml.yml)
|
||||
|
||||
return page
|
||||
elif request.method == 'GET': # when we load a html page
|
||||
page = '''
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
|
||||
<title>EnglishPal 英文单词高效记</title>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
'''
|
||||
page += '<div class="container-fluid">'
|
||||
page += '<p><b><font size="+3" color="red">English Pal - Learn English smartly!</font></b></p>'
|
||||
if session.get('logged_in'):
|
||||
page += ' <a href="%s">%s</a></p>\n' % (session['username'], session['username'])
|
||||
else:
|
||||
page += '<p><a href="/login">登录</a> <a href="/signup">成为会员</a> <a href="/static/usr/instructions.html">使用说明</a></p>\n'
|
||||
#page += '<p><img src="%s" width="400px" alt="advertisement"/></p>' % (get_random_image(path_prefix + 'static/img/'))
|
||||
page += '<p><b>%s</b></p>' % (get_random_ads())
|
||||
page += '<div class="alert alert-success" role="alert">共有文章 <span class="badge bg-success"> %d </span> 篇</div>' % (total_number_of_essays())
|
||||
page += '<p>粘帖1篇文章 (English only)</p>'
|
||||
page += '<form method="post" action="/">'
|
||||
page += ' <textarea name="content" rows="10" cols="120"></textarea><br/>'
|
||||
page += ' <input type="submit" value="get文章中的词频"/>'
|
||||
page += ' <input type="reset" value="清除"/>'
|
||||
page += '</form>'
|
||||
random_ads = get_random_ads()
|
||||
number_of_essays = total_number_of_essays()
|
||||
d = load_freq_history(path_prefix + 'static/frequency/frequency.p')
|
||||
if len(d) > 0:
|
||||
page += '<p><b>最常见的词</b></p>'
|
||||
for x in sort_in_descending_order(pickle_idea.dict2lst(d)):
|
||||
if x[1] <= 99:
|
||||
break
|
||||
page += '<a href="%s">%s</a> %d\n' % (youdao_link(x[0]), x[0], x[1])
|
||||
d_len = len(d)
|
||||
lst = sort_in_descending_order(pickle_idea.dict2lst(d))
|
||||
return render_template('mainpage_get.html', random_ads=random_ads, number_of_essays=number_of_essays,
|
||||
d_len=d_len, lst=lst, yml=Yaml.yml)
|
||||
|
||||
page += ' <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>'
|
||||
page += '</div>'
|
||||
page += '</body></html>'
|
||||
return page
|
||||
|
||||
|
||||
@app.route("/<username>/mark", methods=['GET', 'POST'])
|
||||
def user_mark_word(username):
|
||||
username = session[username]
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
if request.method == 'POST':
|
||||
d = load_freq_history(user_freq_record)
|
||||
lst_history = pickle_idea2.dict2lst(d)
|
||||
lst = []
|
||||
for word in request.form.getlist('marked'):
|
||||
lst.append((word, [get_time()]))
|
||||
d = pickle_idea2.merge_frequency(lst, lst_history)
|
||||
pickle_idea2.save_frequency_to_pickle(d, user_freq_record)
|
||||
return redirect(url_for('userpage', username=username))
|
||||
else:
|
||||
return 'Under construction'
|
||||
|
||||
|
||||
@app.route("/<username>/<word>/unfamiliar", methods=['GET', 'POST'])
|
||||
def unfamiliar(username,word):
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
pickle_idea.unfamiliar(user_freq_record,word)
|
||||
session['thisWord'] = word # 1. put a word into session
|
||||
session['time'] = 1
|
||||
return redirect(url_for('userpage', username=username))
|
||||
|
||||
@app.route("/<username>/<word>/familiar", methods=['GET', 'POST'])
|
||||
def familiar(username,word):
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
pickle_idea.familiar(user_freq_record,word)
|
||||
session['thisWord'] = word # 1. put a word into session
|
||||
session['time'] = 1
|
||||
return redirect(url_for('userpage', username=username))
|
||||
|
||||
@app.route("/<username>/<word>/del", methods=['GET', 'POST'])
|
||||
def deleteword(username,word):
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
pickle_idea2.deleteRecord(user_freq_record,word)
|
||||
flash(f'<strong>{word}</strong> is no longer in your word list.')
|
||||
return redirect(url_for('userpage', username=username))
|
||||
|
||||
@app.route("/<username>", methods=['GET', 'POST'])
|
||||
def userpage(username):
|
||||
|
||||
if not session.get('logged_in'):
|
||||
return '<p>请先<a href="/login">登录</a>。</p>'
|
||||
|
||||
user_expiry_date = session.get('expiry_date')
|
||||
if datetime.now().strftime('%Y%m%d') > user_expiry_date:
|
||||
return '<p>账号 %s 过期。</p><p>为了提高服务质量,English Pal 收取会员费用, 每天0元。</p> <p>请决定你要试用的时间长度,扫描下面支付宝二维码支付。 支付时请注明<i>English Pal Membership Fee</i>。 我们会于12小时内激活账号。</p><p><img src="static/donate-the-author-hidden.jpg" width="120px" alt="支付宝二维码" /></p><p>如果有问题,请加开发者微信 torontohui。</p> <p><a href="/logout">登出</a></p>' % (username)
|
||||
|
||||
|
||||
username = session.get('username')
|
||||
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
|
||||
if request.method == 'POST': # when we submit a form
|
||||
content = request.form['content']
|
||||
f = WordFreq(content)
|
||||
lst = f.get_freq()
|
||||
page = '<meta charset="UTF8">'
|
||||
page += '<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />'
|
||||
page += '<p>勾选不认识的单词</p>'
|
||||
page += '<form method="post" action="/%s/mark">\n' % (username)
|
||||
page += ' <input type="submit" name="add-btn" value="加入我的生词簿"/>\n'
|
||||
count = 1
|
||||
words_tests_dict = pickle_idea.load_record(path_prefix + 'static/words_and_tests.p')
|
||||
for x in lst:
|
||||
page += '<p><font color="grey">%d</font>: <a href="%s" title="%s">%s</a> (%d) <input type="checkbox" name="marked" value="%s"></p>\n' % (count, youdao_link(x[0]), appears_in_test(x[0], words_tests_dict), x[0], x[1], x[0])
|
||||
count += 1
|
||||
page += '</form>\n'
|
||||
return page
|
||||
|
||||
elif request.method == 'GET': # when we load a html page
|
||||
page = '<meta charset="UTF8">\n'
|
||||
page += '<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />\n'
|
||||
page += '<meta name="format-detection" content="telephone=no" />\n' # forbid treating numbers as cell numbers in smart phones
|
||||
page += '<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">'
|
||||
page += '<title>EnglishPal Study Room for %s</title>' % (username)
|
||||
page += '<div class="container-fluid">'
|
||||
page += '<p><b>English Pal for <font color="red">%s</font></b> <a class="btn btn-secondary" href="/logout" role="button">登出</a></p>' % (username)
|
||||
page += get_flashed_messages_if_any()
|
||||
page += '<p><b>阅读文章并回答问题</b></p>\n'
|
||||
page += '<p><a class="btn btn-success" href="/%s/reset" role="button"> 下一篇 Next Article </a></p>' % (username)
|
||||
page += '<div id="text-content">%s</div>' % (get_today_article(user_freq_record, session['articleID']))
|
||||
page += '<p><b>收集生词吧</b> (可以在正文中划词,也可以复制黏贴)</p>'
|
||||
page += '<form method="post" action="/%s">' % (username)
|
||||
page += ' <textarea name="content" id="selected-words" rows="10" cols="120"></textarea><br/>'
|
||||
page += ' <input type="submit" value="get 所有词的频率"/>'
|
||||
page += ' <input type="reset" value="清除"/>'
|
||||
page += '</form>\n'
|
||||
page += '''
|
||||
<script>
|
||||
function getWord(){
|
||||
var word = window.getSelection?window.getSelection():document.selection.createRange().text;
|
||||
return word;
|
||||
}
|
||||
function fillinWord(){
|
||||
var element = document.getElementById("selected-words");
|
||||
element.value = element.value + " " + getWord();
|
||||
}
|
||||
document.getElementById("text-content").addEventListener("click", fillinWord, false);
|
||||
document.getElementById("text-content").addEventListener("touchstart", fillinWord, false);
|
||||
</script>
|
||||
'''
|
||||
if session.get('thisWord'):
|
||||
page += '''
|
||||
<script type="text/javascript">
|
||||
//point to the anchor in the page whose id is aaa if it exists
|
||||
window.onload = function(){
|
||||
var element = document.getElementsByName("aaa");
|
||||
if (element != null)
|
||||
document.getElementsByName("aaa")[0].scrollIntoView(true);
|
||||
}
|
||||
</script>
|
||||
'''
|
||||
|
||||
d = load_freq_history(user_freq_record)
|
||||
if len(d) > 0:
|
||||
page += '<p><b>我的生词簿</b></p>'
|
||||
lst = pickle_idea2.dict2lst(d)
|
||||
lst2 = []
|
||||
for t in lst:
|
||||
lst2.append((t[0], len(t[1])))
|
||||
for x in sort_in_descending_order(lst2):
|
||||
word = x[0]
|
||||
freq = x[1]
|
||||
if session.get('thisWord') == x[0] and session.get('time') == 1:
|
||||
page += '<a name="aaa"></a>' # 3. anchor
|
||||
session['time'] = 0 # discard anchor
|
||||
if isinstance(d[word], list): # d[word] is a list of dates
|
||||
if freq > 1:
|
||||
page += '<p class="new-word"> <a class="btn btn-light" href="%s" role="button">%s</a>(<a title="%s">%d</a>) <a class="btn btn-success" href="%s/%s/familiar" role="button">熟悉</a> <a class="btn btn-warning" href="%s/%s/unfamiliar" role="button">不熟悉</a> <a class="btn btn-danger" href="%s/%s/del" role="button">删除</a> </p>\n' % (youdao_link(word), word, '; '.join(d[word]), freq,username, word,username,word, username,word)
|
||||
else:
|
||||
page += '<p class="new-word"> <a class="btn btn-light" href="%s" role="button">%s</a>(<a title="%s">%d</a>) <a class="btn btn-success" href="%s/%s/familiar" role="button">熟悉</a> <a class="btn btn-warning" href="%s/%s/unfamiliar" role="button">不熟悉</a> <a class="btn btn-danger" href="%s/%s/del" role="button">删除</a> </p>\n' % (youdao_link(word), word, '; '.join(d[word]), freq,username, word,username,word, username,word)
|
||||
elif isinstance(d[word], int): # d[word] is a frequency. to migrate from old format.
|
||||
page += '<a href="%s">%s</a>%d\n' % (youdao_link(word), word, freq)
|
||||
page += '<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>'
|
||||
page += '</div>'
|
||||
return page
|
||||
|
||||
### Sign-up, login, logout ###
|
||||
@app.route("/signup", methods=['GET', 'POST'])
|
||||
def signup():
|
||||
if request.method == 'GET':
|
||||
return render_template('signup.html')
|
||||
elif request.method == 'POST':
|
||||
username = request.form['username']
|
||||
password = request.form['password']
|
||||
|
||||
available = check_username_availability(username)
|
||||
if not available:
|
||||
flash('用户名 %s 已经被注册。' % (username))
|
||||
return render_template('signup.html')
|
||||
elif len(password.strip()) < 4:
|
||||
return '密码过于简单。'
|
||||
else:
|
||||
add_user(username, password)
|
||||
verified = verify_user(username, password)
|
||||
if verified:
|
||||
session['logged_in'] = True
|
||||
session[username] = username
|
||||
session['username'] = username
|
||||
session['expiry_date'] = get_expiry_date(username)
|
||||
session['articleID'] = None
|
||||
return '<p>恭喜,你已成功注册, 你的用户名是 <a href="%s">%s</a>。</p>\
|
||||
<p><a href="/%s">开始使用</a> <a href="/">返回首页</a><p/>' % (username, username, username)
|
||||
else:
|
||||
return '用户名密码验证失败。'
|
||||
|
||||
|
||||
@app.route("/login", methods=['GET', 'POST'])
|
||||
def login():
|
||||
if request.method == 'GET':
|
||||
if not session.get('logged_in'):
|
||||
return render_template('login.html')
|
||||
else:
|
||||
return '你已登录 <a href="/%s">%s</a>。 登出点击<a href="/logout">这里</a>。' % (session['username'], session['username'])
|
||||
elif request.method == 'POST':
|
||||
# check database and verify user
|
||||
username = request.form['username']
|
||||
password = request.form['password']
|
||||
verified = verify_user(username, password)
|
||||
if verified:
|
||||
session['logged_in'] = True
|
||||
session[username] = username
|
||||
session['username'] = username
|
||||
user_expiry_date = get_expiry_date(username)
|
||||
session['expiry_date'] = user_expiry_date
|
||||
session['articleID'] = None
|
||||
return redirect(url_for('userpage', username=username))
|
||||
else:
|
||||
return '无法通过验证。'
|
||||
|
||||
|
||||
@app.route("/logout", methods=['GET', 'POST'])
|
||||
def logout():
|
||||
session['logged_in'] = False
|
||||
return redirect(url_for('mainpage'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
'''
|
||||
运行程序
|
||||
'''
|
||||
# app.secret_key = os.urandom(16)
|
||||
# app.run(debug=False, port='6000')
|
||||
app.run(debug=True)
|
||||
# app.run(debug=True, port='6000')
|
||||
# app.run(host='0.0.0.0', debug=True, port='6000')
|
||||
# print(mod5('123'))
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
# 全局引入的css文件地址
|
||||
css:
|
||||
item:
|
||||
- static/css/bootstrap.css
|
||||
# - static/css/aplayercss.css
|
||||
# - static/css/custom.css
|
||||
|
||||
# 全局引入的js文件地址
|
||||
js:
|
||||
head: # 在页面加载之前加载
|
||||
# - static/js/APlayer.js
|
||||
# - static/js/Meting.js
|
||||
bottom: # 在页面加载完之后加载
|
||||
- static/js/fillword.js
|
||||
- static/js/highlight.js
|
||||
|
||||
# 高亮样式,目前仅支持修改颜色
|
||||
highlight:
|
||||
color: ff0000
|
|
@ -0,0 +1,709 @@
|
|||
.aplayer {
|
||||
background: #fff;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
margin: 5px;
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .07), 0 1px 5px 0 rgba(0, 0, 0, .1);
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
line-height: normal;
|
||||
position: relative
|
||||
}
|
||||
|
||||
.aplayer * {
|
||||
box-sizing: content-box
|
||||
}
|
||||
|
||||
.aplayer svg {
|
||||
width: 100%;
|
||||
height: 100%
|
||||
}
|
||||
|
||||
.aplayer svg circle, .aplayer svg path {
|
||||
fill: #fff
|
||||
}
|
||||
|
||||
.aplayer.aplayer-withlist .aplayer-info {
|
||||
border-bottom: 1px solid #e9e9e9
|
||||
}
|
||||
|
||||
.aplayer.aplayer-withlist .aplayer-list {
|
||||
display: block
|
||||
}
|
||||
|
||||
.aplayer.aplayer-withlist .aplayer-icon-order, .aplayer.aplayer-withlist .aplayer-info .aplayer-controller .aplayer-time .aplayer-icon.aplayer-icon-menu {
|
||||
display: inline
|
||||
}
|
||||
|
||||
.aplayer.aplayer-withlrc .aplayer-pic {
|
||||
height: 90px;
|
||||
width: 90px
|
||||
}
|
||||
|
||||
.aplayer.aplayer-withlrc .aplayer-info {
|
||||
margin-left: 90px;
|
||||
height: 90px;
|
||||
padding: 10px 7px 0
|
||||
}
|
||||
|
||||
.aplayer.aplayer-withlrc .aplayer-lrc {
|
||||
display: block
|
||||
}
|
||||
|
||||
.aplayer.aplayer-narrow {
|
||||
width: 66px
|
||||
}
|
||||
|
||||
.aplayer.aplayer-narrow .aplayer-info, .aplayer.aplayer-narrow .aplayer-list {
|
||||
display: none
|
||||
}
|
||||
|
||||
.aplayer.aplayer-narrow .aplayer-body, .aplayer.aplayer-narrow .aplayer-pic {
|
||||
height: 66px;
|
||||
width: 66px
|
||||
}
|
||||
|
||||
.aplayer.aplayer-fixed {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0;
|
||||
z-index: 99;
|
||||
overflow: visible;
|
||||
max-width: 400px;
|
||||
box-shadow: none
|
||||
}
|
||||
|
||||
.aplayer.aplayer-fixed .aplayer-list {
|
||||
margin-bottom: 65px;
|
||||
border: 1px solid #eee;
|
||||
border-bottom: none
|
||||
}
|
||||
|
||||
.aplayer.aplayer-fixed .aplayer-body {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0;
|
||||
z-index: 99;
|
||||
background: #fff;
|
||||
padding-right: 18px;
|
||||
transition: all .3s ease;
|
||||
max-width: 400px
|
||||
}
|
||||
|
||||
.aplayer.aplayer-fixed .aplayer-lrc {
|
||||
display: block;
|
||||
position: fixed;
|
||||
bottom: 10px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0;
|
||||
z-index: 98;
|
||||
pointer-events: none;
|
||||
text-shadow: -1px -1px 0 #fff
|
||||
}
|
||||
|
||||
.aplayer.aplayer-fixed .aplayer-lrc:after, .aplayer.aplayer-fixed .aplayer-lrc:before {
|
||||
display: none
|
||||
}
|
||||
|
||||
.aplayer.aplayer-fixed .aplayer-info {
|
||||
-webkit-transform: scaleX(1);
|
||||
transform: scaleX(1);
|
||||
-webkit-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
transition: all .3s ease;
|
||||
border-bottom: none;
|
||||
border-top: 1px solid #e9e9e9
|
||||
}
|
||||
|
||||
.aplayer.aplayer-fixed .aplayer-info .aplayer-music {
|
||||
width: calc(100% - 105px)
|
||||
}
|
||||
|
||||
.aplayer.aplayer-fixed .aplayer-miniswitcher {
|
||||
display: block
|
||||
}
|
||||
|
||||
.aplayer.aplayer-fixed.aplayer-narrow .aplayer-info {
|
||||
display: block;
|
||||
-webkit-transform: scaleX(0);
|
||||
transform: scaleX(0)
|
||||
}
|
||||
|
||||
.aplayer.aplayer-fixed.aplayer-narrow .aplayer-body {
|
||||
width: 66px !important
|
||||
}
|
||||
|
||||
.aplayer.aplayer-fixed.aplayer-narrow .aplayer-miniswitcher .aplayer-icon {
|
||||
-webkit-transform: rotateY(0);
|
||||
transform: rotateY(0)
|
||||
}
|
||||
|
||||
.aplayer.aplayer-fixed .aplayer-icon-back, .aplayer.aplayer-fixed .aplayer-icon-forward, .aplayer.aplayer-fixed .aplayer-icon-lrc, .aplayer.aplayer-fixed .aplayer-icon-play {
|
||||
display: inline-block
|
||||
}
|
||||
|
||||
.aplayer.aplayer-fixed .aplayer-icon-back, .aplayer.aplayer-fixed .aplayer-icon-forward, .aplayer.aplayer-fixed .aplayer-icon-menu, .aplayer.aplayer-fixed .aplayer-icon-play {
|
||||
position: absolute;
|
||||
bottom: 27px;
|
||||
width: 20px;
|
||||
height: 20px
|
||||
}
|
||||
|
||||
.aplayer.aplayer-fixed .aplayer-icon-back {
|
||||
right: 75px
|
||||
}
|
||||
|
||||
.aplayer.aplayer-fixed .aplayer-icon-play {
|
||||
right: 50px
|
||||
}
|
||||
|
||||
.aplayer.aplayer-fixed .aplayer-icon-forward {
|
||||
right: 25px
|
||||
}
|
||||
|
||||
.aplayer.aplayer-fixed .aplayer-icon-menu {
|
||||
right: 0
|
||||
}
|
||||
|
||||
.aplayer.aplayer-arrow .aplayer-icon-loop, .aplayer.aplayer-arrow .aplayer-icon-order, .aplayer.aplayer-mobile .aplayer-icon-volume-down {
|
||||
display: none
|
||||
}
|
||||
|
||||
.aplayer.aplayer-loading .aplayer-info .aplayer-controller .aplayer-loading-icon {
|
||||
display: block
|
||||
}
|
||||
|
||||
.aplayer.aplayer-loading .aplayer-info .aplayer-controller .aplayer-bar-wrap .aplayer-bar .aplayer-played .aplayer-thumb {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1)
|
||||
}
|
||||
|
||||
.aplayer .aplayer-body {
|
||||
position: relative
|
||||
}
|
||||
|
||||
.aplayer .aplayer-icon {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
opacity: .8;
|
||||
vertical-align: middle;
|
||||
padding: 0;
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
display: inline-block
|
||||
}
|
||||
|
||||
.aplayer .aplayer-icon path {
|
||||
transition: all .2s ease-in-out
|
||||
}
|
||||
|
||||
.aplayer .aplayer-icon-back, .aplayer .aplayer-icon-forward, .aplayer .aplayer-icon-lrc, .aplayer .aplayer-icon-order, .aplayer .aplayer-icon-play {
|
||||
display: none
|
||||
}
|
||||
|
||||
.aplayer .aplayer-icon-lrc-inactivity svg {
|
||||
opacity: .4
|
||||
}
|
||||
|
||||
.aplayer .aplayer-icon-forward {
|
||||
-webkit-transform: rotate(180deg);
|
||||
transform: rotate(180deg)
|
||||
}
|
||||
|
||||
.aplayer .aplayer-lrc-content {
|
||||
display: none
|
||||
}
|
||||
|
||||
.aplayer .aplayer-pic {
|
||||
position: relative;
|
||||
float: left;
|
||||
height: 66px;
|
||||
width: 66px;
|
||||
background-size: cover;
|
||||
background-position: 50%;
|
||||
transition: all .3s ease;
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
.aplayer .aplayer-pic:hover .aplayer-button {
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
.aplayer .aplayer-pic .aplayer-button {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
opacity: .8;
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, .2);
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, .2);
|
||||
background: rgba(0, 0, 0, .2);
|
||||
transition: all .1s ease
|
||||
}
|
||||
|
||||
.aplayer .aplayer-pic .aplayer-button path {
|
||||
fill: #fff
|
||||
}
|
||||
|
||||
.aplayer .aplayer-pic .aplayer-hide {
|
||||
display: none
|
||||
}
|
||||
|
||||
.aplayer .aplayer-pic .aplayer-play {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border: 2px solid #fff;
|
||||
bottom: 50%;
|
||||
right: 50%;
|
||||
margin: 0 -15px -15px 0
|
||||
}
|
||||
|
||||
.aplayer .aplayer-pic .aplayer-play svg {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 4px;
|
||||
height: 20px;
|
||||
width: 20px
|
||||
}
|
||||
|
||||
.aplayer .aplayer-pic .aplayer-pause {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid #fff;
|
||||
bottom: 4px;
|
||||
right: 4px
|
||||
}
|
||||
|
||||
.aplayer .aplayer-pic .aplayer-pause svg {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
height: 12px;
|
||||
width: 12px
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info {
|
||||
margin-left: 66px;
|
||||
padding: 14px 7px 0 10px;
|
||||
height: 66px;
|
||||
box-sizing: border-box
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-music {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
margin: 0 0 13px 5px;
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
cursor: default;
|
||||
padding-bottom: 2px;
|
||||
height: 20px
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-music .aplayer-title {
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-music .aplayer-author {
|
||||
font-size: 12px;
|
||||
color: #666
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller {
|
||||
position: relative;
|
||||
display: flex
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-bar-wrap {
|
||||
margin: 0 0 0 5px;
|
||||
padding: 4px 0;
|
||||
cursor: pointer !important;
|
||||
flex: 1
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-bar-wrap:hover .aplayer-bar .aplayer-played .aplayer-thumb {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1)
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-bar-wrap .aplayer-bar {
|
||||
position: relative;
|
||||
height: 2px;
|
||||
width: 100%;
|
||||
background: #cdcdcd
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-bar-wrap .aplayer-bar .aplayer-loaded {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background: #aaa;
|
||||
height: 2px;
|
||||
transition: all .5s ease
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-bar-wrap .aplayer-bar .aplayer-played {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
height: 2px
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-bar-wrap .aplayer-bar .aplayer-played .aplayer-thumb {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 5px;
|
||||
margin-top: -4px;
|
||||
margin-right: -10px;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
transition: all .3s ease-in-out;
|
||||
-webkit-transform: scale(0);
|
||||
transform: scale(0)
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-time {
|
||||
position: relative;
|
||||
right: 0;
|
||||
bottom: 4px;
|
||||
height: 17px;
|
||||
color: #999;
|
||||
font-size: 11px;
|
||||
padding-left: 7px
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-time .aplayer-time-inner {
|
||||
vertical-align: middle
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-time .aplayer-icon {
|
||||
cursor: pointer;
|
||||
transition: all .2s ease
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-time .aplayer-icon path {
|
||||
fill: #666
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-time .aplayer-icon.aplayer-icon-loop {
|
||||
margin-right: 2px
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-time .aplayer-icon:hover path {
|
||||
fill: #000
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-time .aplayer-icon.aplayer-icon-menu, .aplayer .aplayer-info .aplayer-controller .aplayer-time.aplayer-time-narrow .aplayer-icon-menu, .aplayer .aplayer-info .aplayer-controller .aplayer-time.aplayer-time-narrow .aplayer-icon-mode {
|
||||
display: none
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-volume-wrap {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-left: 3px;
|
||||
cursor: pointer !important
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-volume-wrap:hover .aplayer-volume-bar-wrap {
|
||||
height: 40px
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-volume-wrap .aplayer-volume-bar-wrap {
|
||||
position: absolute;
|
||||
bottom: 15px;
|
||||
right: -3px;
|
||||
width: 25px;
|
||||
height: 0;
|
||||
z-index: 99;
|
||||
overflow: hidden;
|
||||
transition: all .2s ease-in-out
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-volume-wrap .aplayer-volume-bar-wrap.aplayer-volume-bar-wrap-active {
|
||||
height: 40px
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-volume-wrap .aplayer-volume-bar-wrap .aplayer-volume-bar {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 10px;
|
||||
width: 5px;
|
||||
height: 35px;
|
||||
background: #aaa;
|
||||
border-radius: 2.5px;
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-volume-wrap .aplayer-volume-bar-wrap .aplayer-volume-bar .aplayer-volume {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 5px;
|
||||
transition: all .1s ease
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-loading-icon {
|
||||
display: none
|
||||
}
|
||||
|
||||
.aplayer .aplayer-info .aplayer-controller .aplayer-loading-icon svg {
|
||||
position: absolute;
|
||||
-webkit-animation: rotate 1s linear infinite;
|
||||
animation: rotate 1s linear infinite
|
||||
}
|
||||
|
||||
.aplayer .aplayer-lrc {
|
||||
display: none;
|
||||
position: relative;
|
||||
height: 30px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
margin: -10px 0 7px
|
||||
}
|
||||
|
||||
.aplayer .aplayer-lrc:before {
|
||||
top: 0;
|
||||
height: 10%;
|
||||
background: linear-gradient(180deg, #fff 0, hsla(0, 0%, 100%, 0));
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#ffffff", endColorstr="#00ffffff", GradientType=0)
|
||||
}
|
||||
|
||||
.aplayer .aplayer-lrc:after, .aplayer .aplayer-lrc:before {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
content: " "
|
||||
}
|
||||
|
||||
.aplayer .aplayer-lrc:after {
|
||||
bottom: 0;
|
||||
height: 33%;
|
||||
background: linear-gradient(180deg, hsla(0, 0%, 100%, 0) 0, hsla(0, 0%, 100%, .8));
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#00ffffff", endColorstr="#ccffffff", GradientType=0)
|
||||
}
|
||||
|
||||
.aplayer .aplayer-lrc p {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
line-height: 16px !important;
|
||||
height: 16px !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
transition: all .5s ease-out;
|
||||
opacity: .4;
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.aplayer .aplayer-lrc p.aplayer-lrc-current {
|
||||
opacity: 1;
|
||||
overflow: visible;
|
||||
height: auto !important;
|
||||
min-height: 16px
|
||||
}
|
||||
|
||||
.aplayer .aplayer-lrc.aplayer-lrc-hide {
|
||||
display: none
|
||||
}
|
||||
|
||||
.aplayer .aplayer-lrc .aplayer-lrc-contents {
|
||||
width: 100%;
|
||||
transition: all .5s ease-out;
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
cursor: default
|
||||
}
|
||||
|
||||
.aplayer .aplayer-list {
|
||||
overflow: auto;
|
||||
transition: all .5s ease;
|
||||
will-change: height;
|
||||
display: none;
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.aplayer .aplayer-list.aplayer-list-hide {
|
||||
max-height: 0 !important
|
||||
}
|
||||
|
||||
.aplayer .aplayer-list ol {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow-y: auto
|
||||
}
|
||||
|
||||
.aplayer .aplayer-list ol::-webkit-scrollbar {
|
||||
width: 5px
|
||||
}
|
||||
|
||||
.aplayer .aplayer-list ol::-webkit-scrollbar-thumb {
|
||||
border-radius: 3px;
|
||||
background-color: #eee
|
||||
}
|
||||
|
||||
.aplayer .aplayer-list ol::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #ccc
|
||||
}
|
||||
|
||||
.aplayer .aplayer-list ol li {
|
||||
position: relative;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
padding: 0 15px;
|
||||
font-size: 12px;
|
||||
border-top: 1px solid #e9e9e9;
|
||||
cursor: pointer;
|
||||
transition: all .2s ease;
|
||||
overflow: hidden;
|
||||
margin: 0
|
||||
}
|
||||
|
||||
.aplayer .aplayer-list ol li:first-child {
|
||||
border-top: none
|
||||
}
|
||||
|
||||
.aplayer .aplayer-list ol li:hover {
|
||||
background: #efefef
|
||||
}
|
||||
|
||||
.aplayer .aplayer-list ol li.aplayer-list-light {
|
||||
background: #e9e9e9
|
||||
}
|
||||
|
||||
.aplayer .aplayer-list ol li.aplayer-list-light .aplayer-list-cur {
|
||||
display: inline-block
|
||||
}
|
||||
|
||||
.aplayer .aplayer-list ol li .aplayer-list-cur {
|
||||
display: none;
|
||||
width: 3px;
|
||||
height: 22px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 5px;
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
.aplayer .aplayer-list ol li .aplayer-list-index {
|
||||
color: #666;
|
||||
margin-right: 12px;
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
.aplayer .aplayer-list ol li .aplayer-list-author {
|
||||
color: #666;
|
||||
float: right;
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
.aplayer .aplayer-notice {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
padding: 5px 10px;
|
||||
transition: all .3s ease-in-out;
|
||||
overflow: hidden;
|
||||
color: #fff;
|
||||
pointer-events: none;
|
||||
background-color: #f4f4f5;
|
||||
color: #909399
|
||||
}
|
||||
|
||||
.aplayer .aplayer-miniswitcher {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
background: #e6e6e6;
|
||||
width: 18px;
|
||||
border-radius: 0 2px 2px 0
|
||||
}
|
||||
|
||||
.aplayer .aplayer-miniswitcher .aplayer-icon {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
-webkit-transform: rotateY(180deg);
|
||||
transform: rotateY(180deg);
|
||||
transition: all .3s ease
|
||||
}
|
||||
|
||||
.aplayer .aplayer-miniswitcher .aplayer-icon path {
|
||||
fill: #666
|
||||
}
|
||||
|
||||
.aplayer .aplayer-miniswitcher .aplayer-icon:hover path {
|
||||
fill: #000
|
||||
}
|
||||
|
||||
@-webkit-keyframes aplayer-roll {
|
||||
0% {
|
||||
left: 0
|
||||
}
|
||||
to {
|
||||
left: -100%
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes aplayer-roll {
|
||||
0% {
|
||||
left: 0
|
||||
}
|
||||
to {
|
||||
left: -100%
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes rotate {
|
||||
0% {
|
||||
-webkit-transform: rotate(0);
|
||||
transform: rotate(0)
|
||||
}
|
||||
to {
|
||||
-webkit-transform: rotate(1turn);
|
||||
transform: rotate(1turn)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
0% {
|
||||
-webkit-transform: rotate(0);
|
||||
transform: rotate(0)
|
||||
}
|
||||
to {
|
||||
-webkit-transform: rotate(1turn);
|
||||
transform: rotate(1turn)
|
||||
}
|
||||
}
|
||||
|
||||
.aplayer{
|
||||
width: 500px;
|
||||
margin: auto;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,4 @@
|
|||
mark {
|
||||
color: #FFFF00;
|
||||
background-color: rgba(0,0,0,0);
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,84 @@
|
|||
"use strict";
|
||||
|
||||
function _objectSpread(a) {
|
||||
for (var b = 1; b < arguments.length; b++) {
|
||||
var c = null == arguments[b] ? {} : arguments[b], d = Object.keys(c);
|
||||
"function" == typeof Object.getOwnPropertySymbols && (d = d.concat(Object.getOwnPropertySymbols(c).filter(function (a) {
|
||||
return Object.getOwnPropertyDescriptor(c, a).enumerable
|
||||
}))), d.forEach(function (b) {
|
||||
_defineProperty(a, b, c[b])
|
||||
})
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
function _defineProperty(a, b, c) {
|
||||
return b in a ? Object.defineProperty(a, b, {
|
||||
value: c,
|
||||
enumerable: !0,
|
||||
configurable: !0,
|
||||
writable: !0
|
||||
}) : a[b] = c, a
|
||||
}
|
||||
|
||||
class MetingJSElement extends HTMLElement {
|
||||
connectedCallback() {
|
||||
window.APlayer && window.fetch && (this._init(), this._parse())
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
this.lock || this.aplayer.destroy()
|
||||
}
|
||||
|
||||
_camelize(a) {
|
||||
return a.replace(/^[_.\- ]+/, "").toLowerCase().replace(/[_.\- ]+(\w|$)/g, (a, b) => b.toUpperCase())
|
||||
}
|
||||
|
||||
_init() {
|
||||
let a = {};
|
||||
for (let b = 0; b < this.attributes.length; b += 1) a[this._camelize(this.attributes[b].name)] = this.attributes[b].value;
|
||||
let b = ["server", "type", "id", "api", "auth", "auto", "lock", "name", "title", "artist", "author", "url", "cover", "pic", "lyric", "lrc"];
|
||||
this.meta = {};
|
||||
for (var c = 0; c < b.length; c++) {
|
||||
let d = b[c];
|
||||
this.meta[d] = a[d], delete a[d]
|
||||
}
|
||||
this.config = a, this.api = this.meta.api || window.meting_api || "https://api.i-meto.com/meting/api?server=:server&type=:type&id=:id&r=:r", this.meta.auto && this._parse_link()
|
||||
}
|
||||
|
||||
_parse_link() {
|
||||
let a = [["music.163.com.*song.*id=(\\d+)", "netease", "song"], ["music.163.com.*album.*id=(\\d+)", "netease", "album"], ["music.163.com.*artist.*id=(\\d+)", "netease", "artist"], ["music.163.com.*playlist.*id=(\\d+)", "netease", "playlist"], ["music.163.com.*discover/toplist.*id=(\\d+)", "netease", "playlist"], ["y.qq.com.*song/(\\w+).html", "tencent", "song"], ["y.qq.com.*album/(\\w+).html", "tencent", "album"], ["y.qq.com.*singer/(\\w+).html", "tencent", "artist"], ["y.qq.com.*playsquare/(\\w+).html", "tencent", "playlist"], ["y.qq.com.*playlist/(\\w+).html", "tencent", "playlist"], ["xiami.com.*song/(\\w+)", "xiami", "song"], ["xiami.com.*album/(\\w+)", "xiami", "album"], ["xiami.com.*artist/(\\w+)", "xiami", "artist"], ["xiami.com.*collect/(\\w+)", "xiami", "playlist"]];
|
||||
for (var b = 0; b < a.length; b++) {
|
||||
let c = a[b], d = new RegExp(c[0]), e = d.exec(this.meta.auto);
|
||||
if (null !== e) return this.meta.server = c[1], this.meta.type = c[2], void (this.meta.id = e[1])
|
||||
}
|
||||
}
|
||||
|
||||
_parse() {
|
||||
if (this.meta.url) {
|
||||
let a = {
|
||||
name: this.meta.name || this.meta.title || "Audio name",
|
||||
artist: this.meta.artist || this.meta.author || "Audio artist",
|
||||
url: this.meta.url,
|
||||
cover: this.meta.cover || this.meta.pic,
|
||||
lrc: this.meta.lrc || this.meta.lyric || "",
|
||||
type: this.meta.type || "auto"
|
||||
};
|
||||
return a.lrc || (this.meta.lrcType = 0), this.innerText && (a.lrc = this.innerText, this.meta.lrcType = 2), void this._loadPlayer([a])
|
||||
}
|
||||
let a = this.api.replace(":server", this.meta.server).replace(":type", this.meta.type).replace(":id", this.meta.id).replace(":auth", this.meta.auth).replace(":r", Math.random());
|
||||
fetch(a).then(a => a.json()).then(a => this._loadPlayer(a))
|
||||
}
|
||||
|
||||
_loadPlayer(a) {
|
||||
let b = {audio: a, mutex: !0, lrcType: this.meta.lrcType || 3, storageName: "metingjs"};
|
||||
if (a.length) {
|
||||
let a = _objectSpread({}, b, this.config);
|
||||
for (let b in a) ("true" === a[b] || "false" === a[b]) && (a[b] = "true" === a[b]);
|
||||
let c = document.createElement("div");
|
||||
a.container = c, this.appendChild(c), this.aplayer = new APlayer(a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log("\n %c MetingJS v2.0.1 %c https://github.com/metowolf/MetingJS \n", "color: #fadfa3; background: #030307; padding:5px 0;", "background: #fadfa3; padding:5px 0;"), window.customElements && !window.customElements.get("meting-js") && (window.MetingJSElement = MetingJSElement, window.customElements.define("meting-js", MetingJSElement));
|
|
@ -0,0 +1,29 @@
|
|||
isRead = true;
|
||||
isChoose = true;
|
||||
var reader = window.speechSynthesis; // 全局定义朗读者,以便朗读和暂停
|
||||
|
||||
function getWord(){
|
||||
var word = window.getSelection?window.getSelection():document.selection.createRange().text;
|
||||
return word;
|
||||
}
|
||||
function fillinWord(){
|
||||
var word = getWord();
|
||||
if (isRead) read(word);
|
||||
if (!isChoose) return;
|
||||
var element = document.getElementById("selected-words");
|
||||
element.value = element.value + " " + word;
|
||||
}
|
||||
document.getElementById("text-content").addEventListener("click", fillinWord, false);
|
||||
function read(s){
|
||||
var msg = new SpeechSynthesisUtterance(s);
|
||||
reader.speak(msg);
|
||||
}
|
||||
function onReadClick(){
|
||||
isRead = !isRead;
|
||||
if(!isRead){
|
||||
reader.cancel();
|
||||
}
|
||||
}
|
||||
function onChooseClick(){
|
||||
isChoose = !isChoose;
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
var isHighlight = true;
|
||||
|
||||
function cancelBtnHandler() {
|
||||
cancel_highLight();
|
||||
document.getElementById("text-content").removeEventListener("click", fillinWord, false);
|
||||
document.getElementById("text-content").removeEventListener("touchstart", fillinWord, false);
|
||||
document.getElementById("text-content").addEventListener("click", fillinWord2, false);
|
||||
document.getElementById("text-content").addEventListener("touchstart", fillinWord2, false);
|
||||
}
|
||||
|
||||
function showBtnHandler() {
|
||||
document.getElementById("text-content").removeEventListener("click", fillinWord2, false);
|
||||
document.getElementById("text-content").removeEventListener("touchstart", fillinWord2, false);
|
||||
document.getElementById("text-content").addEventListener("click", fillinWord, false);
|
||||
document.getElementById("text-content").addEventListener("touchstart", fillinWord, false);
|
||||
highLight();
|
||||
}
|
||||
|
||||
function getWord() {
|
||||
var word = window.getSelection ? window.getSelection() : document.selection.createRange().text;
|
||||
return word;
|
||||
}
|
||||
|
||||
function highLight() {
|
||||
if(!isHighlight) return;
|
||||
var txt = document.getElementById("article").innerText;
|
||||
var sel_word1 = document.getElementById("selected-words");
|
||||
var sel_word2 = document.getElementById("selected-words2");
|
||||
if (sel_word1 != null) {
|
||||
var list = sel_word1.value.split(" ");
|
||||
for (var i = 0; i < list.length; ++i) {
|
||||
list[i] = list[i].replace(/(^\s*)|(\s*$)/g, "");
|
||||
if (list[i] != "" && "<mark>".indexOf(list[i]) == -1 && "</mark>".indexOf(list[i]) == -1) {
|
||||
txt = txt.replace(new RegExp(list[i], "g"), "<mark>" + list[i] + "</mark>");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sel_word2 != null) {
|
||||
var list2 = sel_word2.value.split(" ");
|
||||
for (var i = 0; i < list2.length; ++i) {
|
||||
list2[i] = list2[i].replace(/(^\s*)|(\s*$)/g, "");
|
||||
if (list2[i] != "" && "<mark>".indexOf(list2[i]) == -1 && "</mark>".indexOf(list2[i]) == -1) {
|
||||
txt = txt.replace(new RegExp(list2[i], "g"), "<mark>" + list2[i] + "</mark>");
|
||||
}
|
||||
}
|
||||
}
|
||||
document.getElementById("article").innerHTML = txt;
|
||||
}
|
||||
|
||||
function cancel_highLight() {
|
||||
var txt = document.getElementById("article").innerText;
|
||||
var sel_word1 = document.getElementById("selected-words");
|
||||
var sel_word2 = document.getElementById("selected-words2");
|
||||
if (sel_word1 != null) {
|
||||
var list = sel_word1.value.split(" ");
|
||||
for (var i = 0; i < list.length; ++i) {
|
||||
list[i] = list[i].replace(/(^\s*)|(\s*$)/g, "");
|
||||
if (list[i] != "") {
|
||||
txt = txt.replace("<mark>" + list[i] + "</mark>", "list[i]");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sel_word2 != null) {
|
||||
var list2 = sel_word1.value.split(" ");
|
||||
for (var i = 0; i < list2.length; ++i) {
|
||||
var list2 = sel_word2.value.split(" ");
|
||||
list2[i] = list2[i].replace(/(^\s*)|(\s*$)/g, "");
|
||||
if (list2[i] != "") {
|
||||
txt = txt.replace("<mark>" + list[i] + "</mark>", "list[i]");
|
||||
}
|
||||
}
|
||||
}
|
||||
document.getElementById("article").innerHTML = txt;
|
||||
}
|
||||
|
||||
function fillinWord() {
|
||||
highLight();
|
||||
}
|
||||
|
||||
function fillinWord2() {
|
||||
cancel_highLight();
|
||||
}
|
||||
|
||||
function ChangeHighlight() {
|
||||
if (isHighlight) {
|
||||
isHighlight = false;
|
||||
cancel_highLight();
|
||||
} else {
|
||||
isHighlight = true;
|
||||
highLight();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
showBtnHandler();
|
|
@ -4,11 +4,17 @@
|
|||
You're logged in already!
|
||||
|
||||
{% else %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />
|
||||
<meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />
|
||||
<form action="/login" method="POST">
|
||||
<p><input type="username" name="username" placeholder="邮箱地址、电话号码"></p>
|
||||
<p><input type="password" name="password" placeholder="密码"></p>
|
||||
<p><input type="submit" value="登录"></p>
|
||||
<p>
|
||||
<input type="username" name="username" placeholder="邮箱地址、电话号码">
|
||||
</p>
|
||||
<p>
|
||||
<input type="password" name="password" placeholder="密码">
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" value="登录">
|
||||
</p>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />
|
||||
|
||||
{{ yml['header'] | safe }}
|
||||
{% if yml['css']['item'] %}
|
||||
{% for css in yml['css']['item'] %}
|
||||
<link href="{{ css }}" rel="stylesheet">
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if yml['js']['head'] %}
|
||||
{% for js in yml['js']['head'] %}
|
||||
<script src="{{ js }}" ></script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<title>EnglishPal 英文单词高效记</title>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<p><b><font size="+3" color="red">English Pal - Learn English smartly!</font></b></p>
|
||||
{% if session['logged_in'] %}
|
||||
<a href="/{{session['username']}}">{{session['username']}}</a></p>
|
||||
{% else %}
|
||||
<p><a href="/login">登录</a> <a href="/signup">成为会员</a> <a href="/static/usr/instructions.html">使用说明</a></p >
|
||||
<p><b>{{random_ads|safe}}</b></p>
|
||||
{% endif %}
|
||||
<div class="alert alert-success" role="alert">共有文章 <span class="badge bg-success"> {{number_of_essays}} </span> 篇</div>
|
||||
<p>粘帖1篇文章 (English only)</p>
|
||||
<form method="post" action="/">
|
||||
<textarea name="content" rows="10" cols="120"></textarea><br/>
|
||||
<input type="submit" value="get文章中的词频"/>
|
||||
<input type="reset" value="清除"/>
|
||||
</form>
|
||||
{% if d_len > 0 %}
|
||||
<p><b>最常见的词()</b></p>
|
||||
{% for x in lst if x[1]>99 %}
|
||||
<a href="http://youdao.com/w/eng/{{x[0]}}/#keyfrom=dict2.index">{{x[0]}}</a> {{x[1]}}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||||
</div>
|
||||
{{ yml['footer'] | safe }}
|
||||
{% if yml['js']['bottom'] %}
|
||||
{% for js in yml['js']['bottom'] %}
|
||||
<script src="{{ js }}" ></script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,40 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
|
||||
{{ yml['header'] | safe }}
|
||||
{% if yml['css']['item'] %}
|
||||
{% for css in yml['css']['item'] %}
|
||||
<link href="{{ css }}" rel="stylesheet">
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if yml['js']['head'] %}
|
||||
{% for js in yml['js']['head'] %}
|
||||
<script src="{{ js }}" ></script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<form method="post" action="/mark">
|
||||
{% for x in lst %}
|
||||
<p>
|
||||
<font color="grey">{{loop.index}}</font>
|
||||
:
|
||||
<a href="http://youdao.com/w/eng/{{x[0]}}/#keyfrom=dict2.index">{{x[0]}}</a>
|
||||
({{x[1]}})
|
||||
<input type="checkbox" name="marked" value="{{x[0]}}">
|
||||
</p>
|
||||
{% endfor %}
|
||||
<input type="submit" value="确定并返回"/>
|
||||
</form>
|
||||
{{ yml['footer'] | safe }}
|
||||
{% if yml['js']['bottom'] %}
|
||||
{% for js in yml['js']['bottom'] %}
|
||||
<script src="{{ js }}" ></script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,5 @@
|
|||
{% block body %}
|
||||
{% if not session['logged_in'] %}
|
||||
<p>请先<a href="/login">登录</a></p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>账号过期</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>您的账号{{ username }}过期。</p>
|
||||
<p>为了提高服务质量,English Pal 收取会员费用, 每天0元。</p>
|
||||
<p>请决定你要试用的时间长度,扫描下面支付宝二维码支付。 支付时请注明<i>English Pal Membership Fee</i>。 我们会于12小时内激活账号。</p>
|
||||
<p><img src="static/donate-the-author-hidden.jpg" width="120px" alt="支付宝二维码" /></p>
|
||||
<p>如果有问题,请加开发者微信 torontohui。</p>
|
||||
<p><a href="/logout">登出</a></p>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,14 @@
|
|||
<html>
|
||||
<body>
|
||||
<form action="/reset" method='POST'>
|
||||
旧密码:
|
||||
<input type="password" name="old-psd" />
|
||||
<br/>
|
||||
新密码:
|
||||
<input type="password" name="new-psd" />
|
||||
<br/>
|
||||
<input type="submit" name="submit" value="提交" />
|
||||
<input type="button" name="submit" value="放弃修改" onclick="window.location.href='/{{ username }}'"/>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,106 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes"/>
|
||||
<meta name="format-detection" content="telephone=no"/>
|
||||
|
||||
{{ yml['header'] | safe }}
|
||||
{% if yml['css']['item'] %}
|
||||
{% for css in yml['css']['item'] %}
|
||||
<link href="{{ css }}" rel="stylesheet">
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if yml['js']['head'] %}
|
||||
{% for js in yml['js']['head'] %}
|
||||
<script src="{{ js }}"></script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<title>EnglishPal Study Room for {{ username }}</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<p><b>English Pal for <font color="red">{{ username }}</font></b>
|
||||
<a class="btn btn-secondary" href="/logout" role="button">登出</a>
|
||||
<a class="btn btn-secondary" href="/reset" role="button">重设密码</a>
|
||||
</p>
|
||||
{{ flashed_messages|safe }}
|
||||
<p><b>阅读文章并回答问题</b></p>
|
||||
<input type="checkbox" onclick="onReadClick()" checked/>大声朗读
|
||||
<input type="checkbox" onclick="onChooseClick()" checked/>划词入库
|
||||
<input type="checkbox" onclick="ChangeHighlight()" checked/>单词高亮
|
||||
<p><a class="btn btn-success" href="/{{ username }}/reset" role="button"> 下一篇 Next Article </a></p>
|
||||
<div id="text-content">{{ today_article|safe }}</div>
|
||||
<p><b>收集生词吧</b> (可以在正文中划词,也可以复制黏贴)</p>
|
||||
<form method="post" action="/{{ username }}">
|
||||
<textarea name="content" id="selected-words" rows="10" cols="120"></textarea><br/>
|
||||
<input type="submit" value="get 所有词的频率"/>
|
||||
<input type="reset" value="清除"/>
|
||||
</form>
|
||||
{% if session.get['thisWord'] %}
|
||||
<script type="text/javascript">
|
||||
//point to the anchor in the page whose id is aaa if it exists
|
||||
window.onload = function () {
|
||||
var element = document.getElementsByName("aaa");
|
||||
if (element != null)
|
||||
document.getElementsByName("aaa")[0].scrollIntoView(true);
|
||||
}
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
{% if d_len > 0 %}
|
||||
<p><b>我的生词簿</b></p>
|
||||
{% for x in lst3 %}
|
||||
{% set word = x[0] %}
|
||||
|
||||
{% set freq = x[1] %}
|
||||
{% if session.get('thisWord') == x[0] and session.get('time') == 1 %}
|
||||
<a name="aaa"></a>
|
||||
{% endif %}
|
||||
{% if freq > 1 %}
|
||||
<p class="new-word">
|
||||
<a class="btn btn-light" href='http://youdao.com/w/eng/{{ word }}/#keyfrom=dict2.index'
|
||||
role="button">{{ word }}</a>
|
||||
(
|
||||
<a title="{{ word }}">{{ freq }}</a>
|
||||
)
|
||||
|
||||
<a class="btn btn-success" href={{ username }}/{{ word }}/familiar role="button">熟悉</a>
|
||||
<a class="btn btn-warning" href={{ username }}/{{ word }}/unfamiliar role="button">不熟悉</a>
|
||||
<a class="btn btn-danger" href={{ username }}/{{ word }}/del role="button">删除</a>
|
||||
</p>
|
||||
{% else %}
|
||||
<p class="new-word">
|
||||
<a class="btn btn-light" href='http://youdao.com/w/eng/{{ word }}/#keyfrom=dict2.index'
|
||||
role="button">{{ word }}</a>
|
||||
(
|
||||
<a title="{{ word }}">{{ freq }}</a>
|
||||
)
|
||||
<a class="btn btn-success" href={{ username }}/{{ word }}/familiar role="button">熟悉</a>
|
||||
<a class="btn btn-warning" href={{ username }}/{{ word }}/unfamiliar role="button">不熟悉</a>
|
||||
<a class="btn btn-danger" href={{ username }}/{{ word }}/del role="button">删除</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<a href='http://youdao.com/w/eng/{{ word }}/#keyfrom=dict2.index'>{{ word }}</a>{{ freq }}
|
||||
{% endfor %}
|
||||
<input id="selected-words2" type="hidden" value="{{ words }}">
|
||||
{% endif %}
|
||||
</div>
|
||||
{{ yml['footer'] | safe }}
|
||||
{% if yml['js']['bottom'] %}
|
||||
{% for js in yml['js']['bottom'] %}
|
||||
<script src="{{ js }}"></script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</body>
|
||||
<style>
|
||||
mark {
|
||||
color: #{{ yml['highlight']['color'] }};
|
||||
background-color: rgba(0,0,0,0);
|
||||
}
|
||||
</style>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=3.0, user-scalable=yes" />
|
||||
<meta name="format-detection" content="telephone=no" />
|
||||
|
||||
{{ yml['header'] | safe }}
|
||||
{% if yml['css']['item'] %}
|
||||
{% for css in yml['css']['item'] %}
|
||||
<link href="{{ css }}" rel="stylesheet">
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if yml['js']['head'] %}
|
||||
{% for js in yml['js']['head'] %}
|
||||
<script src="{{ js }}" ></script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<title>EnglishPal Study Room for {{username}}</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>勾选不认识的单词</p>
|
||||
<form method="post" action="/{{username}}/mark">
|
||||
<input type="submit" name="add-btn" value="加入我的生词簿"/>
|
||||
{% for x in lst %}
|
||||
{% set word = x[0]%}
|
||||
<p>
|
||||
<font color="grey">{{loop.index}}</font>
|
||||
:
|
||||
<a href='http://youdao.com/w/eng/{{word}}/#keyfrom=dict2.index' title={{word}}>{{word}}</a>
|
||||
({{x[1]}})
|
||||
<input type="checkbox" name="marked" value={{word}}>
|
||||
</p>
|
||||
|
||||
{% endfor %}
|
||||
</form>
|
||||
{{ yml['footer'] | safe }}
|
||||
{% if yml['js']['bottom'] %}
|
||||
{% for js in yml['js']['bottom'] %}
|
||||
<script src="{{ js }}" ></script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
|
@ -6,18 +6,19 @@ from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
|||
|
||||
import random, time
|
||||
import string
|
||||
import pytest
|
||||
|
||||
#driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
#driver.implicitly_wait(10)
|
||||
driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
driver.implicitly_wait(10)
|
||||
|
||||
HOME_PAGE = 'http://121.4.94.30:91/'
|
||||
|
||||
|
||||
def has_punctuation(s):
|
||||
return [c for c in s if c in string.punctuation] != []
|
||||
|
||||
@pytest.mark.usefixtures
|
||||
def test_add_word(URL, driver):
|
||||
def test_add_word():
|
||||
try:
|
||||
driver.get(URL)
|
||||
driver.get(HOME_PAGE)
|
||||
assert 'English Pal -' in driver.page_source
|
||||
|
||||
# login
|
||||
|
|
|
@ -3,21 +3,22 @@
|
|||
# docker run -d -p 4444:4444 selenium/standalone-chrome
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
import pytest
|
||||
|
||||
import random, time
|
||||
import string
|
||||
|
||||
#driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
#driver.implicitly_wait(10)
|
||||
driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
driver.implicitly_wait(10)
|
||||
|
||||
HOME_PAGE = 'http://121.4.94.30:91/'
|
||||
|
||||
|
||||
def has_punctuation(s):
|
||||
return [c for c in s if c in string.punctuation] != []
|
||||
|
||||
@pytest.mark.usefixtures
|
||||
def test_add_word_and_essay_does_not_change(URL, driver):
|
||||
def test_add_word_and_essay_does_not_change():
|
||||
try:
|
||||
driver.get(URL)
|
||||
driver.get(HOME_PAGE)
|
||||
assert 'English Pal -' in driver.page_source
|
||||
|
||||
# login
|
||||
|
|
|
@ -3,22 +3,25 @@
|
|||
# docker run -d -p 4444:4444 selenium/standalone-chrome
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
import pytest
|
||||
|
||||
import random, time
|
||||
import string
|
||||
|
||||
# 调用本地chromedriver
|
||||
# driver = webdriver.Chrome(executable_path="D:\ChromeDriver\chromedriver.exe")
|
||||
# driver.get("http://127.0.0.1:5000/")
|
||||
#driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
#driver.implicitly_wait(10)
|
||||
driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
driver.implicitly_wait(10)
|
||||
# driver.maximize_window()
|
||||
# HOME_PAGE = "http://127.0.0.1:5000/"
|
||||
|
||||
@pytest.mark.usefixtures
|
||||
|
||||
HOME_PAGE = 'http://121.4.94.30:91/'
|
||||
|
||||
|
||||
def test_delete_word():
|
||||
try:
|
||||
driver.get(URL)
|
||||
driver.get(HOME_PAGE)
|
||||
assert 'English Pal -' in driver.page_source
|
||||
# login
|
||||
elem = driver.find_element_by_link_text('登录')
|
||||
|
@ -55,3 +58,4 @@ def test_delete_word():
|
|||
finally:
|
||||
driver.quit()
|
||||
|
||||
# test_delete_word()
|
||||
|
|
|
@ -3,17 +3,19 @@
|
|||
# docker run -d -p 4444:4444 selenium/standalone-chrome
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
import pytest
|
||||
|
||||
import random, string
|
||||
|
||||
#driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
#driver.implicitly_wait(10)
|
||||
driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
driver.implicitly_wait(10)
|
||||
|
||||
HOME_PAGE = 'http://121.4.94.30:91/'
|
||||
|
||||
|
||||
@pytest.mark.usefixtures
|
||||
def test_login(URL, driver):
|
||||
|
||||
def test_login():
|
||||
try:
|
||||
driver.get(URL)
|
||||
driver.get(HOME_PAGE)
|
||||
driver.save_screenshot('./app/test/test_login_pic0.png')
|
||||
|
||||
assert 'English Pal -' in driver.page_source
|
||||
|
@ -39,7 +41,7 @@ def test_login(URL, driver):
|
|||
assert uname in driver.page_source
|
||||
|
||||
# logout
|
||||
driver.get(URL + 'logout')
|
||||
driver.get(HOME_PAGE + 'logout')
|
||||
driver.save_screenshot('./app/test/test_login_pic3.png')
|
||||
|
||||
# login
|
||||
|
|
|
@ -3,16 +3,17 @@
|
|||
# docker run -d -p 4444:4444 selenium/standalone-chrome
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
import pytest
|
||||
|
||||
import random, string
|
||||
|
||||
#driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
#driver.implicitly_wait(10)
|
||||
driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
driver.implicitly_wait(10)
|
||||
|
||||
HOME_PAGE = 'http://121.4.94.30:91/'
|
||||
|
||||
@pytest.mark.usefixtures
|
||||
def test_login_security_fix():
|
||||
try:
|
||||
driver.get(URL)
|
||||
driver.get(HOME_PAGE)
|
||||
|
||||
elem = driver.find_element_by_link_text('登录')
|
||||
elem.click()
|
||||
|
|
|
@ -3,16 +3,19 @@
|
|||
# docker run -d -p 4444:4444 selenium/standalone-chrome
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
import pytest
|
||||
|
||||
import random, string, time
|
||||
|
||||
#driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
#driver.implicitly_wait(10)
|
||||
driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
driver.implicitly_wait(10)
|
||||
|
||||
@pytest.mark.usefixtures
|
||||
def test_next(URL, driver):
|
||||
HOME_PAGE = 'http://121.4.94.30:91/'
|
||||
|
||||
|
||||
|
||||
def test_next():
|
||||
try:
|
||||
driver.get(URL)
|
||||
driver.get(HOME_PAGE)
|
||||
assert 'English Pal -' in driver.page_source
|
||||
|
||||
# login
|
||||
|
|
|
@ -6,10 +6,15 @@ Click the Familiar or Unfamiliar button (current word frequency is 1), and the p
|
|||
from random import randint
|
||||
|
||||
from selenium import webdriver
|
||||
import pytest
|
||||
from selenium.common.exceptions import NoSuchElementException
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
|
||||
driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
driver.implicitly_wait(10)
|
||||
|
||||
HOME_PAGE = 'http://121.4.94.30:91/'
|
||||
|
||||
|
||||
def click_by_random(text):
|
||||
elements = driver.find_elements_by_link_text(text) # 点击单词表中的第一个单词的熟悉按钮
|
||||
elements[randint(0, len(elements) - 1)].click()
|
||||
|
@ -28,10 +33,10 @@ def get_scrollTop():
|
|||
roll_height = driver.execute_script(js)
|
||||
return roll_height
|
||||
|
||||
@pytest.mark.usefixtures
|
||||
def test_page_position(URL, driver):
|
||||
|
||||
def test_page_position():
|
||||
try:
|
||||
driver.get(URL)
|
||||
driver.get(HOME_PAGE)
|
||||
# login
|
||||
driver.find_element_by_link_text('登录').click()
|
||||
|
||||
|
|
|
@ -3,19 +3,19 @@
|
|||
# docker run -d -p 4444:4444 selenium/standalone-chrome
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
import pytest
|
||||
|
||||
import random, string
|
||||
|
||||
#driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
#driver.implicitly_wait(10)
|
||||
driver = webdriver.Remote('http://localhost:4444/wd/hub', DesiredCapabilities.FIREFOX)
|
||||
driver.implicitly_wait(10)
|
||||
|
||||
HOME_PAGE = 'http://121.4.94.30:91/'
|
||||
|
||||
|
||||
|
||||
|
||||
@pytest.mark.usefixtures
|
||||
def test_signup(URL, driver):
|
||||
def test_signup():
|
||||
try:
|
||||
driver.get(URL)
|
||||
driver.get(HOME_PAGE)
|
||||
driver.save_screenshot('test_signup_pic0.png')
|
||||
|
||||
assert 'English Pal -' in driver.page_source
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
from datetime import datetime
|
||||
|
||||
from flask import *
|
||||
|
||||
# from app import Yaml
|
||||
# from app.Article import get_today_article, load_freq_history
|
||||
# from app.WordFreq import WordFreq
|
||||
# from app.wordfreqCMD import sort_in_descending_order
|
||||
|
||||
import Yaml
|
||||
from Article import get_today_article, load_freq_history
|
||||
from WordFreq import WordFreq
|
||||
from wordfreqCMD import sort_in_descending_order
|
||||
|
||||
import pickle_idea
|
||||
import pickle_idea2
|
||||
|
||||
# 初始化蓝图
|
||||
userService = Blueprint("user_bp", __name__)
|
||||
|
||||
path_prefix = '/var/www/wordfreq/wordfreq/'
|
||||
path_prefix = './' # comment this line in deployment
|
||||
|
||||
|
||||
@userService.route("/<username>/reset", methods=['GET', 'POST'])
|
||||
def user_reset(username):
|
||||
'''
|
||||
用户界面
|
||||
:param username: 用户名
|
||||
:return: 返回页面内容
|
||||
'''
|
||||
if request.method == 'GET':
|
||||
session['articleID'] = None
|
||||
return redirect(url_for('user_bp.userpage', username=username))
|
||||
else:
|
||||
return 'Under construction'
|
||||
|
||||
|
||||
@userService.route("/<username>/<word>/unfamiliar", methods=['GET', 'POST'])
|
||||
def unfamiliar(username, word):
|
||||
'''
|
||||
|
||||
:param username:
|
||||
:param word:
|
||||
:return:
|
||||
'''
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
pickle_idea.unfamiliar(user_freq_record, word)
|
||||
session['thisWord'] = word # 1. put a word into session
|
||||
session['time'] = 1
|
||||
return redirect(url_for('user_bp.userpage', username=username))
|
||||
|
||||
|
||||
@userService.route("/<username>/<word>/familiar", methods=['GET', 'POST'])
|
||||
def familiar(username, word):
|
||||
'''
|
||||
|
||||
:param username:
|
||||
:param word:
|
||||
:return:
|
||||
'''
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
pickle_idea.familiar(user_freq_record, word)
|
||||
session['thisWord'] = word # 1. put a word into session
|
||||
session['time'] = 1
|
||||
return redirect(url_for('user_bp.userpage', username=username))
|
||||
|
||||
|
||||
@userService.route("/<username>/<word>/del", methods=['GET', 'POST'])
|
||||
def deleteword(username, word):
|
||||
'''
|
||||
删除单词
|
||||
:param username: 用户名
|
||||
:param word: 单词
|
||||
:return: 重定位到用户界面
|
||||
'''
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
pickle_idea2.deleteRecord(user_freq_record, word)
|
||||
flash(f'<strong>{word}</strong> is no longer in your word list.')
|
||||
return redirect(url_for('user_bp.userpage', username=username))
|
||||
|
||||
|
||||
@userService.route("/<username>", methods=['GET', 'POST'])
|
||||
def userpage(username):
|
||||
'''
|
||||
用户界面
|
||||
:param username: 用户名
|
||||
:return: 返回用户界面
|
||||
'''
|
||||
# 未登录,跳转到未登录界面
|
||||
if not session.get('logged_in'):
|
||||
return render_template('not_login.html')
|
||||
|
||||
# 用户过期
|
||||
user_expiry_date = session.get('expiry_date')
|
||||
# if datetime.now().strftime('%Y%m%d') > user_expiry_date:
|
||||
# return render_template('out_time.html')
|
||||
|
||||
# 获取session里的用户名
|
||||
username = session.get('username')
|
||||
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
|
||||
if request.method == 'POST': # when we submit a form
|
||||
content = request.form['content']
|
||||
f = WordFreq(content)
|
||||
lst = f.get_freq()
|
||||
return render_template('userpage_post.html',username=username,lst = lst, yml=Yaml.yml)
|
||||
|
||||
elif request.method == 'GET': # when we load a html page
|
||||
d = load_freq_history(user_freq_record)
|
||||
lst = pickle_idea2.dict2lst(d)
|
||||
lst2 = []
|
||||
for t in lst:
|
||||
lst2.append((t[0], len(t[1])))
|
||||
lst3 = sort_in_descending_order(lst2)
|
||||
words = ''
|
||||
for x in lst3:
|
||||
words += x[0] + ' '
|
||||
return render_template('userpage_get.html',
|
||||
username=username,
|
||||
session=session,
|
||||
flashed_messages=get_flashed_messages_if_any(),
|
||||
today_article=get_today_article(user_freq_record, session['articleID']),
|
||||
d_len=len(d),
|
||||
lst3=lst3,
|
||||
yml=Yaml.yml,
|
||||
words=words)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@userService.route("/<username>/mark", methods=['GET', 'POST'])
|
||||
def user_mark_word(username):
|
||||
'''
|
||||
标记单词
|
||||
:param username: 用户名
|
||||
:return: 重定位到用户界面
|
||||
'''
|
||||
username = session[username]
|
||||
user_freq_record = path_prefix + 'static/frequency/' + 'frequency_%s.pickle' % (username)
|
||||
if request.method == 'POST':
|
||||
# 提交标记的单词
|
||||
d = load_freq_history(user_freq_record)
|
||||
lst_history = pickle_idea2.dict2lst(d)
|
||||
lst = []
|
||||
for word in request.form.getlist('marked'):
|
||||
lst.append((word, [get_time()]))
|
||||
d = pickle_idea2.merge_frequency(lst, lst_history)
|
||||
pickle_idea2.save_frequency_to_pickle(d, user_freq_record)
|
||||
return redirect(url_for('user_bp.userpage', username=username))
|
||||
else:
|
||||
return 'Under construction'
|
||||
|
||||
def get_time():
|
||||
'''
|
||||
获取当前时间
|
||||
:return: 当前时间
|
||||
'''
|
||||
return datetime.now().strftime('%Y%m%d%H%M') # upper to minutes
|
||||
|
||||
def get_flashed_messages_if_any():
|
||||
'''
|
||||
在用户界面显示黄色提示信息
|
||||
:return: 包含HTML标签的提示信息
|
||||
'''
|
||||
messages = get_flashed_messages()
|
||||
s = ''
|
||||
for message in messages:
|
||||
s += '<div class="alert alert-warning" role="alert">'
|
||||
s += f'Congratulations! {message}'
|
||||
s += '</div>'
|
||||
return s
|
Loading…
Reference in New Issue