fetch太慢去除历史提交记录
@@ -0,0 +1,5 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/web.iml" filepath="$PROJECT_DIR$/.idea/web.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/../../../../.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
|
||||
* Digest Algorithm, as defined in RFC 1321.
|
||||
* Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
|
||||
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
|
||||
* Distributed under the BSD License
|
||||
* See http://pajhome.org.uk/crypt/md5 for more info.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Configurable variables. You may need to tweak these to be compatible with
|
||||
* the server-side, but the defaults work in most cases.
|
||||
*/
|
||||
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
|
||||
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
|
||||
var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
|
||||
|
||||
/*
|
||||
* These are the functions you'll usually want to call
|
||||
* They take string arguments and return either hex or base-64 encoded strings
|
||||
*/
|
||||
function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
|
||||
function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
|
||||
function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
|
||||
function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
|
||||
function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
|
||||
function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
|
||||
|
||||
/*
|
||||
* Perform a simple self-test to see if the VM is working
|
||||
*/
|
||||
function md5_vm_test()
|
||||
{
|
||||
return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the MD5 of an array of little-endian words, and a bit length
|
||||
*/
|
||||
function core_md5(x, len)
|
||||
{
|
||||
/* append padding */
|
||||
x[len >> 5] |= 0x80 << ((len) % 32);
|
||||
x[(((len + 64) >>> 9) << 4) + 14] = len;
|
||||
|
||||
var a = 1732584193;
|
||||
var b = -271733879;
|
||||
var c = -1732584194;
|
||||
var d = 271733878;
|
||||
|
||||
for(var i = 0; i < x.length; i += 16)
|
||||
{
|
||||
var olda = a;
|
||||
var oldb = b;
|
||||
var oldc = c;
|
||||
var oldd = d;
|
||||
|
||||
a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
|
||||
d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
|
||||
c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
|
||||
b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
|
||||
a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
|
||||
d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
|
||||
c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
|
||||
b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
|
||||
a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
|
||||
d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
|
||||
c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
|
||||
b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
|
||||
a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
|
||||
d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
|
||||
c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
|
||||
b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
|
||||
|
||||
a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
|
||||
d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
|
||||
c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
|
||||
b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
|
||||
a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
|
||||
d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
|
||||
c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
|
||||
b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
|
||||
a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
|
||||
d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
|
||||
c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
|
||||
b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
|
||||
a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
|
||||
d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
|
||||
c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
|
||||
b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
|
||||
|
||||
a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
|
||||
d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
|
||||
c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
|
||||
b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
|
||||
a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
|
||||
d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
|
||||
c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
|
||||
b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
|
||||
a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
|
||||
d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
|
||||
c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
|
||||
b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
|
||||
a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
|
||||
d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
|
||||
c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
|
||||
b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
|
||||
|
||||
a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
|
||||
d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
|
||||
c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
|
||||
b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
|
||||
a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
|
||||
d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
|
||||
c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
|
||||
b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
|
||||
a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
|
||||
d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
|
||||
c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
|
||||
b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
|
||||
a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
|
||||
d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
|
||||
c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
|
||||
b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
|
||||
|
||||
a = safe_add(a, olda);
|
||||
b = safe_add(b, oldb);
|
||||
c = safe_add(c, oldc);
|
||||
d = safe_add(d, oldd);
|
||||
}
|
||||
return Array(a, b, c, d);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* These functions implement the four basic operations the algorithm uses.
|
||||
*/
|
||||
function md5_cmn(q, a, b, x, s, t)
|
||||
{
|
||||
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
|
||||
}
|
||||
function md5_ff(a, b, c, d, x, s, t)
|
||||
{
|
||||
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
|
||||
}
|
||||
function md5_gg(a, b, c, d, x, s, t)
|
||||
{
|
||||
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
|
||||
}
|
||||
function md5_hh(a, b, c, d, x, s, t)
|
||||
{
|
||||
return md5_cmn(b ^ c ^ d, a, b, x, s, t);
|
||||
}
|
||||
function md5_ii(a, b, c, d, x, s, t)
|
||||
{
|
||||
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the HMAC-MD5, of a key and some data
|
||||
*/
|
||||
function core_hmac_md5(key, data)
|
||||
{
|
||||
var bkey = str2binl(key);
|
||||
if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
|
||||
|
||||
var ipad = Array(16), opad = Array(16);
|
||||
for(var i = 0; i < 16; i++)
|
||||
{
|
||||
ipad[i] = bkey[i] ^ 0x36363636;
|
||||
opad[i] = bkey[i] ^ 0x5C5C5C5C;
|
||||
}
|
||||
|
||||
var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
|
||||
return core_md5(opad.concat(hash), 512 + 128);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
|
||||
* to work around bugs in some JS interpreters.
|
||||
*/
|
||||
function safe_add(x, y)
|
||||
{
|
||||
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
|
||||
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
|
||||
return (msw << 16) | (lsw & 0xFFFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bitwise rotate a 32-bit number to the left.
|
||||
*/
|
||||
function bit_rol(num, cnt)
|
||||
{
|
||||
return (num << cnt) | (num >>> (32 - cnt));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a string to an array of little-endian words
|
||||
* If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
|
||||
*/
|
||||
function str2binl(str)
|
||||
{
|
||||
var bin = Array();
|
||||
var mask = (1 << chrsz) - 1;
|
||||
for(var i = 0; i < str.length * chrsz; i += chrsz)
|
||||
bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
|
||||
return bin;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an array of little-endian words to a string
|
||||
*/
|
||||
function binl2str(bin)
|
||||
{
|
||||
var str = "";
|
||||
var mask = (1 << chrsz) - 1;
|
||||
for(var i = 0; i < bin.length * 32; i += chrsz)
|
||||
str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an array of little-endian words to a hex string.
|
||||
*/
|
||||
function binl2hex(binarray)
|
||||
{
|
||||
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
|
||||
var str = "";
|
||||
for(var i = 0; i < binarray.length * 4; i++)
|
||||
{
|
||||
str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
|
||||
hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an array of little-endian words to a base-64 string
|
||||
*/
|
||||
function binl2b64(binarray)
|
||||
{
|
||||
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
var str = "";
|
||||
for(var i = 0; i < binarray.length * 4; i += 3)
|
||||
{
|
||||
var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16)
|
||||
| (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
|
||||
| ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
|
||||
for(var j = 0; j < 4; j++)
|
||||
{
|
||||
if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
|
||||
else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
.editor {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
}
|
||||
.setbox,
|
||||
.menu,
|
||||
.outbox {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
max-height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.menu {
|
||||
justify-content: center;
|
||||
max-width: 90px;
|
||||
margin: 0 5px;
|
||||
}
|
||||
.menu .button {
|
||||
width: 90px;
|
||||
height: 30px;
|
||||
min-height: 30px;
|
||||
margin: 5px 0px;
|
||||
cursor: pointer;
|
||||
}
|
||||
@keyframes stroker {
|
||||
0% {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: -240;
|
||||
}
|
||||
}
|
||||
.button rect {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
fill: transparent;
|
||||
stroke: #666;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
.button rect.busy {
|
||||
stroke: #fd1850;
|
||||
stroke-dasharray: 30 90;
|
||||
animation: stroker 1s linear infinite;
|
||||
}
|
||||
.button text {
|
||||
text-anchor: middle;
|
||||
dominant-baseline: middle;
|
||||
}
|
||||
.setbox {
|
||||
min-width: 40em;
|
||||
}
|
||||
.rules {
|
||||
overflow: auto;
|
||||
}
|
||||
.tabbox {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
.rules > * {
|
||||
display: flex;
|
||||
margin: 2px 0;
|
||||
}
|
||||
.rules textarea {
|
||||
flex: 1;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.rules > *,
|
||||
.rules > * > div,
|
||||
.rules textarea {
|
||||
min-height: 1em;
|
||||
}
|
||||
textarea {
|
||||
word-break: break-all;
|
||||
}
|
||||
.tabtitle {
|
||||
display: flex;
|
||||
z-index: 1;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.tabtitle > div {
|
||||
cursor: pointer;
|
||||
padding: 1px 10px 0 10px;
|
||||
border-bottom: 3px solid transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
.tabtitle > .this {
|
||||
color: #4f9da6;
|
||||
border-bottom-color: #4ebbe4;
|
||||
}
|
||||
.tabbody {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
margin-top: -1px;
|
||||
border: 1px solid #a9a9a9;
|
||||
height: 0;
|
||||
}
|
||||
.tabbody > * {
|
||||
flex: 1;
|
||||
flex-flow: column;
|
||||
display: none;
|
||||
}
|
||||
.tabbody > .this {
|
||||
display: flex;
|
||||
}
|
||||
.tabbody > * > .titlebar {
|
||||
display: flex;
|
||||
}
|
||||
.tabbody > * > .titlebar > * {
|
||||
flex: 1;
|
||||
margin: 1px 1px 1px 1px;
|
||||
}
|
||||
.tabbody > * > .context {
|
||||
flex: 1;
|
||||
flex-flow: column;
|
||||
border: 0;
|
||||
padding: 5px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.tabbody > * > .inputbox {
|
||||
border: 0;
|
||||
border-bottom: #a9a9a9 solid 1px;
|
||||
height: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
.link > * {
|
||||
display: flex;
|
||||
margin: 5px;
|
||||
border-bottom: 1px solid;
|
||||
text-decoration: none;
|
||||
}
|
||||
#RuleList > label > * {
|
||||
background: #eee;
|
||||
padding-left: 3px;
|
||||
margin: 2px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
#RuleList input[type="radio"] {
|
||||
display: none;
|
||||
}
|
||||
#RuleList input[type="radio"]:checked + * {
|
||||
background: #15cda8;
|
||||
}
|
||||
.isError {
|
||||
color: #ff0000;
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>阅读3.0源编辑器_V4.0</title>
|
||||
<link rel="icon" href="favicon.ico">
|
||||
<link rel="stylesheet" type="text/css" href="index.css"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="editor">
|
||||
<div class="setbox">
|
||||
<div>
|
||||
<a href="../index.html">←主页</a>
|
||||
<b>书源</b>
|
||||
</div>
|
||||
<div class="rules">
|
||||
<div><b>基本</b></div>
|
||||
<div>
|
||||
<div>源域名 :</div>
|
||||
<textarea rows="1" id="bookSourceUrl" class="base" title="bookSourceUrl"
|
||||
placeholder="<必填>通常填写网站主页,例: https://www.qidian.com"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>源类型 :</div>
|
||||
<textarea rows="1" id="bookSourceType" class="base" title="bookSourceType"
|
||||
placeholder="<必填>0:文本 1:音频"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>源名称 :</div>
|
||||
<textarea rows="1" id="bookSourceName" class="base" title="bookSourceName"
|
||||
placeholder="<必填>会显示在源列表"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>源分组 :</div>
|
||||
<textarea rows="1" id="bookSourceGroup" class="base" title="bookSourceGroup"
|
||||
placeholder="<选填>描述源的特征信息"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>源注释 :</div>
|
||||
<textarea rows="1" id="bookSourceComment" class="base" title="bookSourceComment"
|
||||
placeholder="<选填>描述源作者和状态"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>登录地址:</div>
|
||||
<textarea rows="1" id="loginUrl" class="base" title="loginUrl"
|
||||
placeholder="<选填>填写网站登录网址,仅在需要登录的源有用"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>登录界面:</div>
|
||||
<textarea rows="3" id="loginUi" class="base" title="loginUi"
|
||||
placeholder="<选填>自定义登录界面"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>登录检测:</div>
|
||||
<textarea rows="3" id="loginCheckJs" class="base" title="loginCheckJs"
|
||||
placeholder="<选填>登录检测js"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>并发率 :</div>
|
||||
<textarea rows="1" id="concurrentRate" class="base" title="concurrentRate"
|
||||
placeholder="<选填>并发率"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>请求头 :</div>
|
||||
<textarea rows="3" id="header" class="base" title="header"
|
||||
placeholder="<选填>客户端标识"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>链接验证:</div>
|
||||
<textarea rows="1" id="bookUrlPattern" class="base" title="bookUrlPattern"
|
||||
placeholder="<选填>当详情页URL与源URL的域名不一致时有效,用于添加网址"></textarea>
|
||||
</div>
|
||||
<p></p>
|
||||
<div><b>搜索</b></div>
|
||||
<div>
|
||||
<div>搜索地址:</div>
|
||||
<textarea rows="1" id="searchUrl" class="base" title="searchUrl"
|
||||
placeholder="[域名可省略]/search.php@kw={{key}}"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>校验文字:</div>
|
||||
<textarea rows="1" id="ruleSearch_checkKeyWord" class="ruleSearch"
|
||||
title="checkKeyWord"
|
||||
placeholder="校验关键字"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>列表规则:</div>
|
||||
<textarea rows="1" id="ruleSearch_bookList" class="ruleSearch" title="bookList"
|
||||
placeholder="选择书籍节点 (规则结果为List<Element>)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>书名规则:</div>
|
||||
<textarea rows="1" id="ruleSearch_name" class="ruleSearch" title="name"
|
||||
placeholder="选择节点书名 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>作者规则:</div>
|
||||
<textarea rows="1" id="ruleSearch_author" class="ruleSearch" title="author"
|
||||
placeholder="选择节点作者 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>分类规则:</div>
|
||||
<textarea rows="1" id="ruleSearch_kind" class="ruleSearch" title="kind"
|
||||
placeholder="选择节点分类信息 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>字数规则:</div>
|
||||
<textarea rows="1" id="ruleSearch_wordCount" class="ruleSearch" title="wordCount"
|
||||
placeholder="选择节点字数信息 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>最新章节:</div>
|
||||
<textarea rows="1" id="ruleSearch_lastChapter" class="ruleSearch"
|
||||
title="lastChapter"
|
||||
placeholder="选择节点最新章节 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>简介规则:</div>
|
||||
<textarea rows="1" id="ruleSearch_intro" class="ruleSearch" title="intro"
|
||||
placeholder="选择节点书籍简介 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>封面规则:</div>
|
||||
<textarea rows="1" id="ruleSearch_coverUrl" class="ruleSearch" title="coverUrl"
|
||||
placeholder="选择节点书籍封面 (规则结果为String类型的url)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>详情地址:</div>
|
||||
<textarea rows="1" id="ruleSearch_bookUrl" class="ruleSearch" title="bookUrl"
|
||||
placeholder="选择书籍详情页网址 (规则结果为String类型的url)"></textarea>
|
||||
</div>
|
||||
<p></p>
|
||||
<div><b>发现</b></div>
|
||||
<div>
|
||||
<div>发现地址:</div>
|
||||
<textarea rows="6" id="exploreUrl" class="base" title="exploreUrl"
|
||||
placeholder="内容能显示在发现菜单 每行一条发现分类(网址域名可省略),例: 名称1::网址(Url)1 名称2::网址(Url)2 ..."></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>列表规则:</div>
|
||||
<textarea rows="1" id="ruleExplore_bookList" class="ruleExplore" title="bookList"
|
||||
placeholder="选择书籍节点 (规则结果为List<Element>)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>书名规则:</div>
|
||||
<textarea rows="1" id="ruleExplore_name" class="ruleExplore" title="name"
|
||||
placeholder="选择节点书名 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>作者规则:</div>
|
||||
<textarea rows="1" id="ruleExplore_author" class="ruleExplore" title="author"
|
||||
placeholder="选择节点作者 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>分类规则:</div>
|
||||
<textarea rows="1" id="ruleExplore_kind" class="ruleExplore" title="kind"
|
||||
placeholder="选择节点分类信息 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>字数规则:</div>
|
||||
<textarea rows="1" id="ruleExplore_wordCount" class="ruleExplore" title="wordCount"
|
||||
placeholder="选择节点字数信息 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>最新章节:</div>
|
||||
<textarea rows="1" id="ruleExplore_lastChapter" class="ruleExplore"
|
||||
title="lastChapter"
|
||||
placeholder="选择节点最新章节 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>简介规则:</div>
|
||||
<textarea rows="1" id="ruleExplore_intro" class="ruleExplore" title="intro"
|
||||
placeholder="选择节点书籍简介 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>封面规则:</div>
|
||||
<textarea rows="1" id="ruleExplore_coverUrl" class="ruleExplore" title="coverUrl"
|
||||
placeholder="选择节点书籍封面 (规则结果为String类型的url)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>详情地址:</div>
|
||||
<textarea rows="1" id="ruleExplore_bookUrl" class="ruleExplore" title="bookUrl"
|
||||
placeholder="选择书籍详情页网址 (规则结果为String类型的url)"></textarea>
|
||||
</div>
|
||||
<p></p>
|
||||
<div><b>详情</b></div>
|
||||
<div>
|
||||
<div>预处理 :</div>
|
||||
<textarea rows="3" id="ruleBookInfo_init" class="ruleBookInfo" title="init"
|
||||
placeholder="用于加速详情信息检索,只支持AllInOne规则"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>书名规则:</div>
|
||||
<textarea rows="1" id="ruleBookInfo_name" class="ruleBookInfo" title="name"
|
||||
placeholder="选择节点书名 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>作者规则:</div>
|
||||
<textarea rows="1" id="ruleBookInfo_author" class="ruleBookInfo" title="author"
|
||||
placeholder="选择节点作者 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>分类规则:</div>
|
||||
<textarea rows="1" id="ruleBookInfo_kind" class="ruleBookInfo" title="kind"
|
||||
placeholder="选择节点分类信息 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>字数规则:</div>
|
||||
<textarea rows="1" id="ruleBookInfo_wordCount" class="ruleBookInfo"
|
||||
title="wordCount"
|
||||
placeholder="选择节点字数信息 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>最新章节:</div>
|
||||
<textarea rows="1" id="ruleBookInfo_lastChapter" class="ruleBookInfo"
|
||||
title="lastChapter"
|
||||
placeholder="选择节点最新章节 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>简介规则:</div>
|
||||
<textarea rows="1" id="ruleBookInfo_intro" class="ruleBookInfo" title="intro"
|
||||
placeholder="选择节点书籍简介 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>封面规则:</div>
|
||||
<textarea rows="1" id="ruleBookInfo_coverUrl" class="ruleBookInfo" title="coverUrl"
|
||||
placeholder="选择节点书籍封面 (规则结果为String类型的url)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>目录地址:</div>
|
||||
<textarea rows="1" id="ruleBookInfo_tocUrl" class="ruleBookInfo" title="tocUrl"
|
||||
placeholder="选择书籍详情页网址 (规则结果为String类型的url, 与详情页相同时可省略)"></textarea>
|
||||
</div>
|
||||
<p></p>
|
||||
<div><b>目录</b></div>
|
||||
<div>
|
||||
<div>列表规则:</div>
|
||||
<textarea rows="3" id="ruleToc_chapterList" class="ruleToc" title="chapterList"
|
||||
placeholder="选择目录列表的章节节点 (规则结果为List<Element>)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>章节名称:</div>
|
||||
<textarea rows="1" id="ruleToc_chapterName" class="ruleToc" title="chapterName"
|
||||
placeholder="选择章节名称 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>章节地址:</div>
|
||||
<textarea rows="1" id="ruleToc_chapterUrl" class="ruleToc" title="chapterUrl"
|
||||
placeholder="选择章节链接 (规则结果为String类型的Url)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>收费标识:</div>
|
||||
<textarea rows="1" id="ruleToc_isVip" class="ruleToc" title="isVip"
|
||||
placeholder="章节是否为VIP章节 (规则结果为Bool)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>购买标识:</div>
|
||||
<textarea rows="1" id="ruleToc_isPay" class="ruleToc" title="isPay"
|
||||
placeholder="章节是否为已购买 (规则结果为Bool)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>章节信息:</div>
|
||||
<textarea rows="1" id="ruleToc_updateTime" class="ruleToc" title="updateTime"
|
||||
placeholder="选择章节信息 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>翻页规则:</div>
|
||||
<textarea rows="1" id="ruleToc_nextTocUrl" class="ruleToc" title="nextTocUrl"
|
||||
placeholder="选择目录下一页链接 (规则结果为List<String>或String)"></textarea>
|
||||
</div>
|
||||
<p></p>
|
||||
<div><b>正文</b></div>
|
||||
<div>
|
||||
<div>脚本注入:</div>
|
||||
<textarea rows="3" id="ruleContent_webJs" class="ruleContent" title="webJs"
|
||||
placeholder="注入javascript,用于模拟鼠标点击等,必须有返回值,一般为String类型"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>正文规则:</div>
|
||||
<textarea rows="1" id="ruleContent_content" class="ruleContent" title="content"
|
||||
placeholder="选择正文内容 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>翻页规则:</div>
|
||||
<textarea rows="1" id="ruleContent_nextContentUrl" class="ruleContent"
|
||||
title="nextContentUrl"
|
||||
placeholder="选择下一分页(不是下一章)链接 (规则结果为String类型的Url)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>资源正则:</div>
|
||||
<textarea rows="1" id="ruleContent_sourceRegex" class="ruleContent"
|
||||
title="sourceRegex"
|
||||
placeholder="匹配资源的url特征,用于嗅探"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>替换规则:</div>
|
||||
<textarea rows="1" id="ruleContent_replaceRegex" class="ruleContent"
|
||||
title="replaceRegex"
|
||||
placeholder="多页内容合并后替换,用于正文净化"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>图片样式:</div>
|
||||
<textarea rows="1" id="ruleContent_imageStyle" class="ruleContent"
|
||||
title="imageStyle"
|
||||
placeholder="FULL:铺满 不填:默认样式"></textarea>
|
||||
</div>
|
||||
<p></p>
|
||||
<div><b>其它规则</b></div>
|
||||
<div>
|
||||
<div>启用搜索:</div>
|
||||
<textarea rows="1" id="enabled" class="base" title="enabled"
|
||||
placeholder="启用: true 关闭: false (可选,默认true)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>启用发现:</div>
|
||||
<textarea rows="1" id="enabledExplore" class="base" title="enabledExplore"
|
||||
placeholder="启用: true 关闭: false (可选,默认true)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>搜索权重:</div>
|
||||
<textarea rows="1" id="weight" class="base" title="weight"
|
||||
placeholder="整数: 0~N (可选,默认0) | 数字越大越靠前"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>排序编号:</div>
|
||||
<textarea rows="1" id="customOrder" class="base" title="customOrder"
|
||||
placeholder="整数: 0~N (可选,默认0) | 数字越小越靠前"></textarea>
|
||||
</div>
|
||||
<div style="display:none;">
|
||||
<div>更新时间:</div>
|
||||
<textarea rows="1" id="lastUpdateTime" class="base" title="lastUpdateTime"
|
||||
placeholder="毫秒级时间戳 (自动生成) | 请勿手动填写"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu">
|
||||
<svg class="button">
|
||||
<text x="50%" y="55%">⇈推送源</text>
|
||||
<rect id="push"></rect>
|
||||
</svg>
|
||||
<svg class="button">
|
||||
<text x="50%" y="55%">⇊拉取源</text>
|
||||
<rect id="pull"></rect>
|
||||
</svg>
|
||||
<svg class="button">
|
||||
<text x="50%" y="55%">⋘编辑源</text>
|
||||
<rect id="editor"></rect>
|
||||
</svg>
|
||||
<svg class="button">
|
||||
<text x="50%" y="55%">⋙生成源</text>
|
||||
<rect id="conver"></rect>
|
||||
</svg>
|
||||
<svg class="button">
|
||||
<text x="50%" y="55%">✗清空表单</text>
|
||||
<rect id="initial"></rect>
|
||||
</svg>
|
||||
<svg class="button">
|
||||
<text x="50%" y="55%">↶撤销操作</text>
|
||||
<rect id="undo"></rect>
|
||||
</svg>
|
||||
<svg class="button">
|
||||
<text x="50%" y="55%">↷重做操作</text>
|
||||
<rect id="redo"></rect>
|
||||
</svg>
|
||||
<svg class="button">
|
||||
<text x="50%" y="55%">⇏调试源</text>
|
||||
<rect id="debug"></rect>
|
||||
</svg>
|
||||
<svg class="button">
|
||||
<text x="50%" y="55%">✓保存源</text>
|
||||
<rect id="accept"></rect>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="outbox">
|
||||
<div class="tabbox">
|
||||
<div class="tabtitle">
|
||||
<div name="编辑源" class="tab1 this">编辑源</div>
|
||||
<div name="调试源" class="tab2">调试源</div>
|
||||
<div name="源列表" class="tab3">源列表</div>
|
||||
<div name="帮助信息" class="tab4">帮助信息</div>
|
||||
</div>
|
||||
<div class="tabbody">
|
||||
<div class="tab1 this">
|
||||
<textarea class="context" id="RuleJsonString"
|
||||
placeholder="这里输出序列化的JSON数据,可直接导入'阅读'APP"></textarea>
|
||||
</div>
|
||||
<div class="tab2">
|
||||
<input type="text" class="inputbox" id="DebugKey" placeholder="输入搜索关键字,默认搜「我的」">
|
||||
<textarea class="context" id="DebugConsole" placeholder="这里用于输出调试信息"></textarea>
|
||||
</div>
|
||||
<div class="tab3">
|
||||
<input type="text" class="inputbox" id="Filter"
|
||||
placeholder="输入筛选关键词(源名称、源URL或源分组)后按回车筛选源">
|
||||
<div class="titlebar">
|
||||
<button id="Import">导入源文件</button>
|
||||
<button id="Export">导出源文件</button>
|
||||
<button id="Delete">删除选中源</button>
|
||||
<button id="ClrAll">清空列表</button>
|
||||
</div>
|
||||
<div class="context" id="RuleList"></div>
|
||||
</div>
|
||||
<div class="tab4">
|
||||
<div class="context link">
|
||||
<a target="_blank" href="https://alanskycn.gitee.io/teachme">源制作教程</a>
|
||||
<a target="_blank"
|
||||
href="https://zhuanlan.zhihu.com/p/29436838">Xpath基础教程</a>
|
||||
<a target="_blank"
|
||||
href="https://zhuanlan.zhihu.com/p/32187820">Xpath高级教程</a>
|
||||
<a target="_blank" href="https://www.w3cschool.cn/regex_rmjc">正则表达式教程</a>
|
||||
<a target="_blank" href="https://regexr.com">正则表达式在线验证工具</a>
|
||||
<div>^$()[]{}.?+*| 这些是Java正则特殊符号,匹配需转义
|
||||
<br>(?s) 前缀表示跨行解析
|
||||
<br>(?m) 前缀表示逐行匹配
|
||||
<br>(?i) 前缀表示忽略大小写
|
||||
</div>
|
||||
<a target="_blank" href="https://www.beta.browxy.com">代码在线运行工具</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="index.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,516 @@
|
||||
// 简化js原生选择器
|
||||
function $(selector) { return document.querySelector(selector); }
|
||||
function $$(selector) { return document.querySelectorAll(selector); }
|
||||
// 读写Hash值(val未赋值时为读取)
|
||||
function hashParam(key, val) {
|
||||
let hashstr = decodeURIComponent(window.location.hash);
|
||||
let regKey = new RegExp(`${key}=([^&]*)`);
|
||||
let getVal = regKey.test(hashstr) ? hashstr.match(regKey)[1] : null;
|
||||
if (val == undefined) return getVal;
|
||||
if (hashstr == '' || hashstr == '#') {
|
||||
window.location.hash = `#${key}=${val}`;
|
||||
}
|
||||
else {
|
||||
if (getVal) window.location.hash = hashstr.replace(getVal, val);
|
||||
else {
|
||||
window.location.hash = hashstr.indexOf(key) > -1 ? hashstr.replace(regKey, `${key}=${val}`) : `${hashstr}&${key}=${val}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 创建源规则容器对象
|
||||
function Container() {
|
||||
let ruleJson = {};
|
||||
let searchJson = {};
|
||||
let exploreJson = {};
|
||||
let bookInfoJson = {};
|
||||
let tocJson = {};
|
||||
let contentJson = {};
|
||||
|
||||
// 基本以及其他
|
||||
$$('.rules .base').forEach(item => ruleJson[item.title] = '');
|
||||
ruleJson.lastUpdateTime = 0;
|
||||
ruleJson.customOrder = 0;
|
||||
ruleJson.weight = 0;
|
||||
ruleJson.enabled = true;
|
||||
ruleJson.enabledExplore = true;
|
||||
|
||||
// 搜索规则
|
||||
$$('.rules .ruleSearch').forEach(item => searchJson[item.title] = '');
|
||||
ruleJson.ruleSearch = searchJson;
|
||||
|
||||
// 发现规则
|
||||
$$('.rules .ruleExplore').forEach(item => exploreJson[item.title] = '');
|
||||
ruleJson.ruleExplore = exploreJson;
|
||||
|
||||
// 详情页规则
|
||||
$$('.rules .ruleBookInfo').forEach(item => bookInfoJson[item.title] = '');
|
||||
ruleJson.ruleBookInfo = bookInfoJson;
|
||||
|
||||
// 目录规则
|
||||
$$('.rules .ruleToc').forEach(item => tocJson[item.title] = '');
|
||||
ruleJson.ruleToc = tocJson;
|
||||
|
||||
// 正文规则
|
||||
$$('.rules .ruleContent').forEach(item => contentJson[item.title] = '');
|
||||
ruleJson.ruleContent = contentJson;
|
||||
|
||||
return ruleJson;
|
||||
}
|
||||
// 选项卡Tab切换事件处理
|
||||
function showTab(tabName) {
|
||||
$$('.tabtitle>*').forEach(node => { node.className = node.className.replace(' this', ''); });
|
||||
$$('.tabbody>*').forEach(node => { node.className = node.className.replace(' this', ''); });
|
||||
$(`.tabbody>.${$(`.tabtitle>*[name=${tabName}]`).className}`).className += ' this';
|
||||
$(`.tabtitle>*[name=${tabName}]`).className += ' this';
|
||||
hashParam('tab', tabName);
|
||||
}
|
||||
// 源列表列表标签构造函数
|
||||
function newRule(rule) {
|
||||
return `<label for="${rule.bookSourceUrl}"><input type="radio" name="rule" id="${rule.bookSourceUrl}"><div>${rule.bookSourceName}<br>${rule.bookSourceUrl}</div></label>`;
|
||||
}
|
||||
// 缓存规则列表
|
||||
var RuleSources = [];
|
||||
if (localStorage.getItem('BookSources')) {
|
||||
RuleSources = JSON.parse(localStorage.getItem('BookSources'));
|
||||
RuleSources.forEach(item => $('#RuleList').innerHTML += newRule(item));
|
||||
}
|
||||
// 页面加载完成事件
|
||||
window.onload = () => {
|
||||
$$('.tabtitle>*').forEach(item => {
|
||||
item.addEventListener('click', () => {
|
||||
showTab(item.innerHTML);
|
||||
});
|
||||
});
|
||||
if (hashParam('tab')) showTab(hashParam('tab'));
|
||||
}
|
||||
// 获取数据
|
||||
function HttpGet(url) {
|
||||
return fetch(hashParam('domain') ? hashParam('domain') + url : url)
|
||||
.then(res => res.json()).catch(err => console.error('Error:', err));
|
||||
}
|
||||
// 提交数据
|
||||
function HttpPost(url, data) {
|
||||
return fetch(hashParam('domain') ? hashParam('domain') + url : url, {
|
||||
body: JSON.stringify(data),
|
||||
method: 'POST',
|
||||
mode: "cors",
|
||||
headers: new Headers({
|
||||
'Content-Type': 'application/json;charset=utf-8'
|
||||
})
|
||||
}).then(res => res.json()).catch(err => console.error('Error:', err));
|
||||
}
|
||||
// 将源表单转化为源对象
|
||||
function rule2json() {
|
||||
let RuleJSON = Container();
|
||||
// 转换base
|
||||
Object.keys(RuleJSON).forEach(key => {
|
||||
if (!key.startsWith("rule")) {
|
||||
RuleJSON[key] = $('#' + key).value;
|
||||
}
|
||||
});
|
||||
|
||||
// 转换搜索规则
|
||||
let searchJson = {};
|
||||
Object.keys(RuleJSON.ruleSearch).forEach(key => {
|
||||
if ($('#' + 'ruleSearch_' + key).value)
|
||||
searchJson[key] = $('#' + 'ruleSearch_' + key).value;
|
||||
});
|
||||
RuleJSON.ruleSearch = searchJson;
|
||||
|
||||
// 转换发现规则
|
||||
let exploreJson = {};
|
||||
Object.keys(RuleJSON.ruleExplore).forEach(key => {
|
||||
if ($('#' + 'ruleExplore_' + key).value)
|
||||
exploreJson[key] = $('#' + 'ruleExplore_' + key).value;
|
||||
});
|
||||
RuleJSON.ruleExplore = exploreJson;
|
||||
|
||||
// 转换详情页规则
|
||||
let bookInfoJson = {};
|
||||
Object.keys(RuleJSON.ruleBookInfo).forEach(key => {
|
||||
if ($('#' + 'ruleBookInfo_' + key).value)
|
||||
bookInfoJson[key] = $('#' + 'ruleBookInfo_' + key).value;
|
||||
});
|
||||
RuleJSON.ruleBookInfo = bookInfoJson;
|
||||
|
||||
// 转换目录规则
|
||||
let tocJson = {};
|
||||
Object.keys(RuleJSON.ruleToc).forEach(key => {
|
||||
if ($('#' + 'ruleToc_' + key).value)
|
||||
tocJson[key] = $('#' + 'ruleToc_' + key).value;
|
||||
});
|
||||
RuleJSON.ruleToc = tocJson;
|
||||
|
||||
// 转换正文规则
|
||||
let contentJson = {};
|
||||
Object.keys(RuleJSON.ruleContent).forEach(key => {
|
||||
if ($('#' + 'ruleContent_' + key).value)
|
||||
contentJson[key] = $('#' + 'ruleContent_' + key).value;
|
||||
});
|
||||
RuleJSON.ruleContent = contentJson;
|
||||
|
||||
RuleJSON.lastUpdateTime = new Date().getTime();
|
||||
RuleJSON.customOrder = RuleJSON.customOrder == '' ? 0 : parseInt(RuleJSON.customOrder);
|
||||
RuleJSON.weight = RuleJSON.weight == '' ? 0 : parseInt(RuleJSON.weight);
|
||||
RuleJSON.bookSourceType == RuleJSON.bookSourceType == '' ? 0 : parseInt(RuleJSON.bookSourceType);
|
||||
RuleJSON.enabled = RuleJSON.enabled == '' || String(RuleJSON.enabled).toLocaleLowerCase().replace(/^\s*|\s*$/g, '') == 'true';
|
||||
RuleJSON.enabledExplore = RuleJSON.enabledExplore == '' || String(RuleJSON.enabledExplore).toLocaleLowerCase().replace(/^\s*|\s*$/g, '') == 'true';
|
||||
return RuleJSON;
|
||||
}
|
||||
// 将源对象填充到源表单
|
||||
function json2rule(RuleEditor) {
|
||||
let RuleJSON = Container();
|
||||
// 转换base
|
||||
Object.keys(RuleJSON).forEach(key => {
|
||||
if (!key.startsWith("rule")) {
|
||||
let val = RuleEditor[key];
|
||||
if (typeof val == "number") {
|
||||
$("#" + key).value = val ? String(val) : '0';
|
||||
}
|
||||
else if (typeof val == "boolean") {
|
||||
$("#" + key).value = val ? String(val) : 'false';
|
||||
}
|
||||
else {
|
||||
$("#" + key).value = val ? String(val) : '';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 转换搜索规则
|
||||
if (RuleEditor.ruleSearch) {
|
||||
let searchJson = RuleEditor.ruleSearch;
|
||||
Object.keys(RuleJSON.ruleSearch).forEach(key => {
|
||||
$('#' + 'ruleSearch_' + key).value = searchJson[key] ? searchJson[key] : '';
|
||||
});
|
||||
}
|
||||
|
||||
// 转换发现规则
|
||||
if (RuleEditor.ruleExplore) {
|
||||
let exploreJson = RuleEditor.ruleExplore;
|
||||
Object.keys(RuleJSON.ruleExplore).forEach(key => {
|
||||
$('#' + 'ruleExplore_' + key).value = exploreJson[key] ? exploreJson[key] : '';
|
||||
});
|
||||
}
|
||||
|
||||
// 转换详情页规则
|
||||
if (RuleEditor.ruleBookInfo) {
|
||||
let bookInfoJson = RuleEditor.ruleBookInfo;
|
||||
Object.keys(RuleJSON.ruleBookInfo).forEach(key => {
|
||||
$('#' + 'ruleBookInfo_' + key).value = bookInfoJson[key] ? bookInfoJson[key] : '';
|
||||
});
|
||||
}
|
||||
|
||||
// 转换目录规则
|
||||
if (RuleEditor.ruleToc) {
|
||||
let tocJson = RuleEditor.ruleToc;
|
||||
Object.keys(RuleJSON.ruleToc).forEach(key => {
|
||||
$('#' + 'ruleToc_' + key).value = tocJson[key] ? tocJson[key] : '';
|
||||
});
|
||||
}
|
||||
|
||||
// 转换正文规则
|
||||
if (RuleEditor.ruleContent) {
|
||||
let contentJson = RuleEditor.ruleContent;
|
||||
Object.keys(RuleJSON.ruleContent).forEach(key => {
|
||||
$('#' + 'ruleContent_' + key).value = contentJson[key] ? contentJson[key] : '';
|
||||
});
|
||||
}
|
||||
}
|
||||
// 记录操作过程
|
||||
var course = { "old": [], "now": {}, "new": [] };
|
||||
if (localStorage.getItem('bookSourceCourse')) {
|
||||
course = JSON.parse(localStorage.getItem('bookSourceCourse'));
|
||||
json2rule(course.now);
|
||||
}
|
||||
else {
|
||||
course.now = rule2json();
|
||||
window.localStorage.setItem('bookSourceCourse', JSON.stringify(course));
|
||||
}
|
||||
function todo() {
|
||||
course.old.push(Object.assign({}, course.now));
|
||||
course.now = rule2json();
|
||||
course.new = [];
|
||||
if (course.old.length > 50) course.old.shift(); // 限制历史记录堆栈大小
|
||||
localStorage.setItem('bookSourceCourse', JSON.stringify(course));
|
||||
}
|
||||
function undo() {
|
||||
course = JSON.parse(localStorage.getItem('bookSourceCourse'));
|
||||
if (course.old.length > 0) {
|
||||
course.new.push(course.now);
|
||||
course.now = course.old.pop();
|
||||
localStorage.setItem('bookSourceCourse', JSON.stringify(course));
|
||||
json2rule(course.now);
|
||||
}
|
||||
}
|
||||
function redo() {
|
||||
course = JSON.parse(localStorage.getItem('bookSourceCourse'));
|
||||
if (course.new.length > 0) {
|
||||
course.old.push(course.now);
|
||||
course.now = course.new.pop();
|
||||
localStorage.setItem('bookSourceCourse', JSON.stringify(course));
|
||||
json2rule(course.now);
|
||||
}
|
||||
}
|
||||
function setRule(editRule) {
|
||||
let checkRule = RuleSources.find(x => x.bookSourceUrl == editRule.bookSourceUrl);
|
||||
if ($(`input[id="${editRule.bookSourceUrl}"]`)) {
|
||||
Object.keys(checkRule).forEach(key => { checkRule[key] = editRule[key]; });
|
||||
$(`input[id="${editRule.bookSourceUrl}"]+*`).innerHTML = `${editRule.bookSourceName}<br>${editRule.bookSourceUrl}`;
|
||||
} else {
|
||||
RuleSources.push(editRule);
|
||||
$('#RuleList').innerHTML += newRule(editRule);
|
||||
}
|
||||
}
|
||||
$$('input').forEach((item) => { item.addEventListener('change', () => { todo() }) });
|
||||
$$('textarea').forEach((item) => { item.addEventListener('change', () => { todo() }) });
|
||||
// 处理按钮点击事件
|
||||
$('.menu').addEventListener('click', e => {
|
||||
let thisNode = e.target;
|
||||
thisNode = thisNode.parentNode.nodeName == 'svg' ? thisNode.parentNode.querySelector('rect') :
|
||||
thisNode.nodeName == 'svg' ? thisNode.querySelector('rect') : null;
|
||||
if (!thisNode) return;
|
||||
if (thisNode.getAttribute('class') == 'busy') return;
|
||||
thisNode.setAttribute('class', 'busy');
|
||||
switch (thisNode.id) {
|
||||
case 'push':
|
||||
$$('#RuleList>label>div').forEach(item => { item.className = ''; });
|
||||
(async () => {
|
||||
await HttpPost(`/saveBookSources`, RuleSources).then(json => {
|
||||
if (json.isSuccess) {
|
||||
let okData = json.data;
|
||||
if (Array.isArray(okData)) {
|
||||
let failMsg = ``;
|
||||
if (RuleSources.length > okData.length) {
|
||||
RuleSources.forEach(item => {
|
||||
if (okData.find(x => x.bookSourceUrl == item.bookSourceUrl)) { }
|
||||
else { $(`#RuleList #${item.bookSourceUrl}+*`).className += 'isError'; }
|
||||
});
|
||||
failMsg = '\n推送失败的源将用红色字体标注!';
|
||||
}
|
||||
alert(`批量推送源到「阅读3.0APP」\n共计: ${RuleSources.length} 条\n成功: ${okData.length} 条\n失败: ${RuleSources.length - okData.length} 条${failMsg}`);
|
||||
}
|
||||
else {
|
||||
alert(`批量推送源到「阅读3.0APP」成功!\n共计: ${RuleSources.length} 条`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
alert(`批量推送源失败!\nErrorMsg: ${json.errorMsg}`);
|
||||
}
|
||||
}).catch(err => { alert(`批量推送源失败,无法连接到「阅读3.0APP」!\n${err}`); });
|
||||
thisNode.setAttribute('class', '');
|
||||
})();
|
||||
return;
|
||||
case 'pull':
|
||||
showTab('源列表');
|
||||
(async () => {
|
||||
await HttpGet(`/getBookSources`).then(json => {
|
||||
if (json.isSuccess) {
|
||||
$('#RuleList').innerHTML = ''
|
||||
localStorage.setItem('BookSources', JSON.stringify(RuleSources = json.data));
|
||||
RuleSources.forEach(item => {
|
||||
$('#RuleList').innerHTML += newRule(item);
|
||||
});
|
||||
alert(`成功拉取 ${RuleSources.length} 条源`);
|
||||
}
|
||||
else {
|
||||
alert(`批量拉取源失败!\nErrorMsg: ${json.errorMsg}`);
|
||||
}
|
||||
}).catch(err => { alert(`批量拉取源失败,无法连接到「阅读3.0APP」!\n${err}`); });
|
||||
thisNode.setAttribute('class', '');
|
||||
})();
|
||||
return;
|
||||
case 'editor':
|
||||
if ($('#RuleJsonString').value == '') break;
|
||||
try {
|
||||
json2rule(JSON.parse($('#RuleJsonString').value));
|
||||
todo();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
alert(error);
|
||||
}
|
||||
break;
|
||||
case 'conver':
|
||||
showTab('编辑源');
|
||||
$('#RuleJsonString').value = JSON.stringify(rule2json(), null, 4);
|
||||
break;
|
||||
case 'initial':
|
||||
$$('.rules textarea').forEach(item => { item.value = '' });
|
||||
todo();
|
||||
break;
|
||||
case 'undo':
|
||||
undo()
|
||||
break;
|
||||
case 'redo':
|
||||
redo()
|
||||
break;
|
||||
case 'debug':
|
||||
showTab('调试源');
|
||||
let wsOrigin = (hashParam('domain') || location.origin).replace(/^.*?:/, 'ws:').replace(/\d+$/, (port) => (parseInt(port) + 1));
|
||||
let DebugInfos = $('#DebugConsole');
|
||||
function DebugPrint(msg) { DebugInfos.value += `\n${msg}`; DebugInfos.scrollTop = DebugInfos.scrollHeight; }
|
||||
let saveRule = [rule2json()];
|
||||
HttpPost(`/saveBookSources`, saveRule).then(sResult => {
|
||||
if (sResult.isSuccess) {
|
||||
let sKey = DebugKey.value ? DebugKey.value : '我的';
|
||||
$('#DebugConsole').value = `源《${saveRule[0].bookSourceName}》保存成功!使用搜索关键字“${sKey}”开始调试...`;
|
||||
let ws = new WebSocket(`${wsOrigin}/bookSourceDebug`);
|
||||
ws.onopen = () => {
|
||||
ws.send(`{"tag":"${saveRule[0].bookSourceUrl}", "key":"${sKey}"}`);
|
||||
};
|
||||
ws.onmessage = (msg) => {
|
||||
console.log('[调试]', msg);
|
||||
DebugPrint(msg.data);
|
||||
};
|
||||
ws.onerror = (err) => {
|
||||
throw `${err.data}`;
|
||||
}
|
||||
ws.onclose = () => {
|
||||
thisNode.setAttribute('class', '');
|
||||
DebugPrint(`\n调试服务已关闭!`);
|
||||
}
|
||||
} else throw `${sResult.errorMsg}`;
|
||||
}).catch(err => {
|
||||
DebugPrint(`调试过程意外中止,以下是详细错误信息:\n${err}`);
|
||||
thisNode.setAttribute('class', '');
|
||||
});
|
||||
return;
|
||||
case 'accept':
|
||||
(async () => {
|
||||
let saveRule = [rule2json()];
|
||||
await HttpPost(`/saveBookSource`, saveRule[0]).then(json => {
|
||||
alert(json.isSuccess ? `源《${saveRule[0].bookSourceName}》已成功保存到「阅读3.0APP」` : `源《${saveRule[0].bookSourceName}》保存失败!\nErrorMsg: ${json.errorMsg}`);
|
||||
setRule(saveRule[0]);
|
||||
}).catch(err => { alert(`保存源失败,无法连接到「阅读3.0APP」!\n${err}`); });
|
||||
thisNode.setAttribute('class', '');
|
||||
})();
|
||||
return;
|
||||
default:
|
||||
}
|
||||
setTimeout(() => { thisNode.setAttribute('class', ''); }, 500);
|
||||
});
|
||||
$('#DebugKey').addEventListener('keydown', e => {
|
||||
if (e.keyCode == 13) {
|
||||
let clickEvent = document.createEvent('MouseEvents');
|
||||
clickEvent.initEvent("click", true, false);
|
||||
$('#debug').dispatchEvent(clickEvent);
|
||||
}
|
||||
});
|
||||
$('#Filter').addEventListener('keydown', e => {
|
||||
if (e.keyCode == 13) {
|
||||
let cashList = [];
|
||||
$('#RuleList').innerHTML = "";
|
||||
let sKey = Filter.value ? Filter.value : '';
|
||||
if (sKey == '') {
|
||||
cashList = RuleSources;
|
||||
} else {
|
||||
let patt = new RegExp(sKey);
|
||||
RuleSources.forEach(source => {
|
||||
if (patt.test(source.bookSourceUrl) || patt.test(source.bookSourceName) || patt.test(source.bookSourceGroup)) {
|
||||
cashList.push(source);
|
||||
}
|
||||
})
|
||||
}
|
||||
cashList.forEach(source => {
|
||||
$('#RuleList').innerHTML += newRule(source);
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// 列表规则更改事件
|
||||
$('#RuleList').addEventListener('click', e => {
|
||||
let editRule = null;
|
||||
if (e.target && e.target.getAttribute('name') == 'rule') {
|
||||
editRule = rule2json();
|
||||
json2rule(RuleSources.find(x => x.bookSourceUrl == e.target.id));
|
||||
} else return;
|
||||
if (editRule.bookSourceUrl == '') return;
|
||||
if (editRule.bookSourceName == '') editRule.bookSourceName = editRule.bookSourceUrl.replace(/.*?\/\/|\/.*/g, '');
|
||||
setRule(editRule);
|
||||
localStorage.setItem('BookSources', JSON.stringify(RuleSources));
|
||||
});
|
||||
// 处理列表按钮事件
|
||||
$('.tab3>.titlebar').addEventListener('click', e => {
|
||||
let thisNode = e.target;
|
||||
if (thisNode.nodeName != 'BUTTON') return;
|
||||
switch (thisNode.id) {
|
||||
case 'Import':
|
||||
let fileImport = document.createElement('input');
|
||||
fileImport.type = 'file';
|
||||
fileImport.accept = '.json';
|
||||
fileImport.addEventListener('change', () => {
|
||||
let file = fileImport.files[0];
|
||||
let reader = new FileReader();
|
||||
reader.onloadend = function (evt) {
|
||||
if (evt.target.readyState == FileReader.DONE) {
|
||||
let fileText = evt.target.result;
|
||||
try {
|
||||
let fileJson = JSON.parse(fileText);
|
||||
let newSources = [];
|
||||
newSources.push(...fileJson);
|
||||
if (window.confirm(`如何处理导入的源?\n"确定": 覆盖当前列表(不会删除APP源)\n"取消": 插入列表尾部(自动忽略重复源)`)) {
|
||||
localStorage.setItem('BookSources', JSON.stringify(RuleSources = newSources));
|
||||
$('#RuleList').innerHTML = ''
|
||||
RuleSources.forEach(item => {
|
||||
$('#RuleList').innerHTML += newRule(item);
|
||||
});
|
||||
}
|
||||
else {
|
||||
newSources = newSources.filter(item => !JSON.stringify(RuleSources).includes(item.bookSourceUrl));
|
||||
RuleSources.push(...newSources);
|
||||
localStorage.setItem('BookSources', JSON.stringify(RuleSources));
|
||||
newSources.forEach(item => {
|
||||
$('#RuleList').innerHTML += newRule(item);
|
||||
});
|
||||
}
|
||||
alert(`成功导入 ${newSources.length} 条源`);
|
||||
}
|
||||
catch (err) {
|
||||
alert(`导入源文件失败!\n${err}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}, false);
|
||||
fileImport.click();
|
||||
break;
|
||||
case 'Export':
|
||||
let fileExport = document.createElement('a');
|
||||
fileExport.download = `Rules${Date().replace(/.*?\s(\d+)\s(\d+)\s(\d+:\d+:\d+).*/, '$2$1$3').replace(/:/g, '')}.json`;
|
||||
let myBlob = new Blob([JSON.stringify(RuleSources, null, 4)], { type: "application/json" });
|
||||
fileExport.href = window.URL.createObjectURL(myBlob);
|
||||
fileExport.click();
|
||||
break;
|
||||
case 'Delete':
|
||||
let selectRule = $('#RuleList input:checked');
|
||||
if (!selectRule) {
|
||||
alert(`没有源被选中!`);
|
||||
return;
|
||||
}
|
||||
if (confirm(`确定要删除选定源吗?\n(同时删除APP内源)`)) {
|
||||
let selectRuleUrl = selectRule.id;
|
||||
let deleteSources = RuleSources.filter(item => item.bookSourceUrl == selectRuleUrl); // 提取待删除的源
|
||||
let laveSources = RuleSources.filter(item => !(item.bookSourceUrl == selectRuleUrl)); // 提取待留下的源
|
||||
HttpPost(`/deleteBookSources`, deleteSources).then(json => {
|
||||
if (json.isSuccess) {
|
||||
let selectNode = document.getElementById(selectRuleUrl).parentNode;
|
||||
selectNode.parentNode.removeChild(selectNode);
|
||||
localStorage.setItem('BookSources', JSON.stringify(RuleSources = laveSources));
|
||||
if ($('#bookSourceUrl').value == selectRuleUrl) {
|
||||
$$('.rules textarea').forEach(item => { item.value = '' });
|
||||
todo();
|
||||
}
|
||||
console.log(deleteSources);
|
||||
console.log(`以上源已删除!`)
|
||||
}
|
||||
}).catch(err => { alert(`删除源失败,无法连接到「阅读3.0APP」!\n${err}`); });
|
||||
}
|
||||
break;
|
||||
case 'ClrAll':
|
||||
if (confirm(`确定要清空当前源列表吗?\n(不会删除APP内源)`)) {
|
||||
localStorage.setItem('BookSources', JSON.stringify(RuleSources = []));
|
||||
$('#RuleList').innerHTML = ''
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#2c3e50;margin:0;height:100%}
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 799 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,149 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="16.000000pt" height="16.000000pt" viewBox="0 0 16.000000 16.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.11, written by Peter Selinger 2001-2013
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,16.000000) scale(0.000320,-0.000320)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M18 46618 c45 -75 122 -207 122 -211 0 -2 25 -45 55 -95 30 -50 55
|
||||
-96 55 -102 0 -5 5 -10 10 -10 6 0 10 -4 10 -9 0 -5 73 -135 161 -288 89 -153
|
||||
173 -298 187 -323 14 -25 32 -57 41 -72 88 -149 187 -324 189 -335 2 -7 8 -13
|
||||
13 -13 5 0 9 -4 9 -10 0 -5 46 -89 103 -187 175 -302 490 -846 507 -876 8 -16
|
||||
20 -36 25 -45 28 -46 290 -498 339 -585 13 -23 74 -129 136 -236 61 -107 123
|
||||
-215 137 -240 14 -25 29 -50 33 -56 5 -5 23 -37 40 -70 18 -33 38 -67 44 -75
|
||||
11 -16 21 -33 63 -109 14 -25 29 -50 33 -56 4 -5 21 -35 38 -65 55 -100 261
|
||||
-455 269 -465 4 -5 14 -21 20 -35 15 -29 41 -75 103 -180 24 -41 52 -88 60
|
||||
-105 9 -16 57 -100 107 -185 112 -193 362 -626 380 -660 8 -14 23 -38 33 -55
|
||||
11 -16 23 -37 27 -45 4 -8 26 -46 48 -85 23 -38 53 -90 67 -115 46 -81 64
|
||||
-113 178 -310 62 -107 121 -210 132 -227 37 -67 56 -99 85 -148 16 -27 32 -57
|
||||
36 -65 4 -8 15 -27 25 -42 9 -15 53 -89 96 -165 44 -76 177 -307 296 -513 120
|
||||
-206 268 -463 330 -570 131 -227 117 -203 200 -348 36 -62 73 -125 82 -140 10
|
||||
-15 21 -34 25 -42 4 -8 20 -37 36 -65 17 -27 38 -65 48 -82 49 -85 64 -111 87
|
||||
-153 13 -25 28 -49 32 -55 4 -5 78 -134 165 -285 87 -151 166 -288 176 -305
|
||||
10 -16 26 -43 35 -59 9 -17 125 -217 257 -445 132 -229 253 -441 270 -471 17
|
||||
-30 45 -79 64 -108 18 -29 33 -54 33 -57 0 -2 20 -37 44 -77 24 -40 123 -212
|
||||
221 -383 97 -170 190 -330 205 -355 16 -25 39 -65 53 -90 13 -25 81 -144 152
|
||||
-265 70 -121 137 -238 150 -260 12 -22 37 -65 55 -95 18 -30 43 -73 55 -95 12
|
||||
-22 48 -85 80 -140 77 -132 163 -280 190 -330 13 -22 71 -123 130 -225 59
|
||||
-102 116 -199 126 -217 10 -17 29 -50 43 -72 15 -22 26 -43 26 -45 0 -2 27
|
||||
-50 60 -106 33 -56 60 -103 60 -105 0 -2 55 -98 90 -155 8 -14 182 -316 239
|
||||
-414 13 -22 45 -79 72 -124 27 -46 49 -86 49 -89 0 -2 14 -24 30 -48 16 -24
|
||||
30 -46 30 -49 0 -5 74 -135 100 -176 5 -8 24 -42 43 -75 50 -88 58 -101 262
|
||||
-455 104 -179 199 -345 213 -370 14 -25 28 -49 32 -55 4 -5 17 -26 28 -45 10
|
||||
-19 62 -109 114 -200 114 -197 133 -230 170 -295 16 -27 33 -57 38 -65 17 -28
|
||||
96 -165 103 -180 4 -8 16 -28 26 -45 10 -16 77 -131 148 -255 72 -124 181
|
||||
-313 243 -420 62 -107 121 -209 131 -227 35 -62 323 -560 392 -678 38 -66 83
|
||||
-145 100 -175 16 -30 33 -59 37 -65 4 -5 17 -27 29 -47 34 -61 56 -100 90
|
||||
-156 17 -29 31 -55 31 -57 0 -2 17 -32 39 -67 21 -35 134 -229 251 -433 117
|
||||
-203 235 -407 261 -451 27 -45 49 -85 49 -88 0 -4 8 -19 19 -34 15 -21 200
|
||||
-341 309 -533 10 -19 33 -58 51 -87 17 -29 31 -54 31 -56 0 -2 25 -44 55 -94
|
||||
30 -50 55 -95 55 -98 0 -4 6 -15 14 -23 7 -9 27 -41 43 -71 17 -30 170 -297
|
||||
342 -594 171 -296 311 -542 311 -547 0 -5 5 -9 10 -9 6 0 10 -4 10 -10 0 -5
|
||||
22 -47 49 -92 27 -46 58 -99 68 -118 24 -43 81 -140 93 -160 5 -8 66 -114 135
|
||||
-235 69 -121 130 -227 135 -235 12 -21 259 -447 283 -490 10 -19 28 -47 38
|
||||
-62 11 -14 19 -29 19 -32 0 -3 37 -69 83 -148 99 -170 305 -526 337 -583 13
|
||||
-22 31 -53 41 -70 11 -16 22 -37 26 -45 7 -14 82 -146 103 -180 14 -24 181
|
||||
-311 205 -355 13 -22 46 -80 75 -130 29 -49 64 -110 78 -135 14 -25 51 -88 82
|
||||
-140 31 -52 59 -102 63 -110 4 -8 18 -33 31 -55 205 -353 284 -489 309 -535
|
||||
17 -30 45 -78 62 -106 18 -28 36 -60 39 -72 4 -12 12 -22 17 -22 5 0 9 -4 9
|
||||
-10 0 -5 109 -197 241 -427 133 -230 250 -431 259 -448 51 -90 222 -385 280
|
||||
-485 37 -63 78 -135 92 -160 14 -25 67 -117 118 -205 51 -88 101 -175 111
|
||||
-193 34 -58 55 -95 149 -257 51 -88 101 -173 110 -190 9 -16 76 -131 147 -255
|
||||
72 -124 140 -241 151 -260 61 -108 281 -489 355 -615 38 -66 77 -133 87 -150
|
||||
35 -63 91 -161 100 -175 14 -23 99 -169 128 -220 54 -97 135 -235 142 -245 4
|
||||
-5 20 -32 35 -60 26 -48 238 -416 276 -480 10 -16 26 -46 37 -65 30 -53 382
|
||||
-661 403 -695 10 -16 22 -37 26 -45 4 -8 26 -48 50 -88 24 -41 43 -75 43 -77
|
||||
0 -2 22 -40 50 -85 27 -45 50 -84 50 -86 0 -3 38 -69 83 -147 84 -142 302
|
||||
-520 340 -587 10 -19 34 -60 52 -90 18 -30 44 -75 57 -100 14 -25 45 -79 70
|
||||
-120 25 -41 56 -96 70 -121 14 -25 77 -133 138 -240 62 -107 122 -210 132
|
||||
-229 25 -43 310 -535 337 -581 11 -19 26 -45 34 -59 17 -32 238 -414 266 -460
|
||||
11 -19 24 -41 28 -49 3 -7 75 -133 160 -278 84 -146 153 -269 153 -274 0 -5 5
|
||||
-9 10 -9 6 0 10 -4 10 -10 0 -5 82 -150 181 -322 182 -314 201 -346 240 -415
|
||||
12 -21 80 -139 152 -263 71 -124 141 -245 155 -270 14 -25 28 -49 32 -55 6 -8
|
||||
145 -248 220 -380 37 -66 209 -362 229 -395 11 -19 24 -42 28 -49 4 -8 67
|
||||
-118 140 -243 73 -125 133 -230 133 -233 0 -2 15 -28 33 -57 19 -29 47 -78 64
|
||||
-108 17 -30 53 -93 79 -139 53 -90 82 -141 157 -272 82 -142 115 -199 381
|
||||
-659 142 -245 268 -463 281 -485 12 -22 71 -125 132 -230 60 -104 172 -298
|
||||
248 -430 76 -132 146 -253 156 -270 11 -16 22 -36 26 -44 3 -8 30 -54 60 -103
|
||||
29 -49 53 -91 53 -93 0 -3 18 -34 40 -70 22 -36 40 -67 40 -69 0 -2 37 -66 81
|
||||
-142 45 -77 98 -168 119 -204 20 -36 47 -81 58 -100 12 -19 27 -47 33 -62 6
|
||||
-16 15 -28 20 -28 5 0 9 -4 9 -9 0 -6 63 -118 140 -251 77 -133 140 -243 140
|
||||
-245 0 -2 18 -33 41 -70 22 -37 49 -83 60 -101 10 -19 29 -51 40 -71 25 -45
|
||||
109 -189 126 -218 7 -11 17 -29 22 -40 6 -11 22 -38 35 -60 14 -22 37 -62 52
|
||||
-90 14 -27 35 -62 45 -77 11 -14 19 -29 19 -32 0 -3 18 -35 40 -71 22 -36 40
|
||||
-67 40 -69 0 -2 19 -35 42 -72 23 -38 55 -94 72 -124 26 -47 139 -244 171
|
||||
-298 6 -9 21 -36 34 -60 28 -48 37 -51 51 -19 6 12 19 36 29 52 10 17 27 46
|
||||
38 65 11 19 104 181 208 360 103 179 199 345 213 370 14 25 42 74 64 109 21
|
||||
34 38 65 38 67 0 2 18 33 40 69 22 36 40 67 40 69 0 3 177 310 199 346 16 26
|
||||
136 234 140 244 2 5 25 44 52 88 27 44 49 81 49 84 0 2 18 34 40 70 22 36 40
|
||||
67 40 69 0 2 20 36 43 77 35 58 169 289 297 513 9 17 50 86 90 155 40 69 86
|
||||
150 103 180 16 30 35 62 41 70 6 8 16 24 22 35 35 64 72 129 167 293 59 100
|
||||
116 199 127 220 11 20 30 53 41 72 43 72 1070 1850 1121 1940 14 25 65 113
|
||||
113 195 48 83 96 166 107 185 10 19 28 50 38 68 11 18 73 124 137 235 64 111
|
||||
175 303 246 427 71 124 173 299 225 390 52 91 116 202 143 248 27 45 49 85 49
|
||||
89 0 4 6 14 14 22 7 9 28 43 46 76 26 47 251 436 378 655 11 19 29 51 40 70
|
||||
11 19 101 176 201 348 99 172 181 317 181 323 0 5 5 9 10 9 6 0 10 5 10 11 0
|
||||
6 8 23 18 37 11 15 32 52 49 82 16 30 130 228 253 440 122 212 234 405 248
|
||||
430 13 25 39 70 57 100 39 65 69 117 130 225 25 44 50 87 55 95 12 19 78 134
|
||||
220 380 61 107 129 224 150 260 161 277 222 382 246 425 15 28 47 83 71 123
|
||||
24 41 43 78 43 83 0 5 4 9 8 9 4 0 13 12 19 28 7 15 23 45 36 67 66 110 277
|
||||
478 277 483 0 3 6 13 14 21 7 9 27 41 43 71 17 30 45 80 63 110 34 57 375 649
|
||||
394 685 6 11 16 27 22 35 6 8 26 42 44 75 18 33 41 74 51 90 10 17 24 41 32
|
||||
55 54 97 72 128 88 152 11 14 19 28 19 30 0 3 79 141 175 308 96 167 175 305
|
||||
175 308 0 3 6 13 14 21 7 9 26 39 41 66 33 60 276 483 338 587 24 40 46 80 50
|
||||
88 4 8 13 24 20 35 14 23 95 163 125 215 11 19 52 91 92 160 40 69 80 139 90
|
||||
155 9 17 103 179 207 360 105 182 200 346 211 365 103 181 463 802 489 845 7
|
||||
11 15 27 19 35 4 8 29 51 55 95 64 110 828 1433 848 1470 9 17 24 41 33 55 9
|
||||
14 29 48 45 77 15 28 52 93 82 145 30 51 62 107 71 123 17 30 231 398 400 690
|
||||
51 88 103 179 115 202 12 23 26 48 32 55 6 7 24 38 40 68 17 30 61 107 98 170
|
||||
37 63 84 144 103 180 19 36 41 72 48 81 8 8 14 18 14 21 0 4 27 51 59 106 32
|
||||
55 72 124 89 154 16 29 71 125 122 213 51 88 104 180 118 205 13 25 28 50 32
|
||||
55 4 6 17 26 28 45 11 19 45 80 77 135 31 55 66 116 77 135 11 19 88 152 171
|
||||
295 401 694 620 1072 650 1125 11 19 87 152 170 295 83 143 158 273 166 288 9
|
||||
16 21 36 26 45 6 9 31 52 55 96 25 43 54 94 66 115 11 20 95 164 186 321 91
|
||||
157 173 299 182 315 9 17 26 46 37 65 12 19 66 114 121 210 56 96 108 186 117
|
||||
200 8 14 24 40 34 59 24 45 383 664 412 713 5 9 17 29 26 45 15 28 120 210
|
||||
241 419 36 61 68 117 72 125 4 8 12 23 19 34 35 57 245 420 262 453 11 20 35
|
||||
61 53 90 17 29 32 54 32 56 0 3 28 51 62 108 33 57 70 119 80 138 10 19 23 42
|
||||
28 50 5 8 32 53 59 100 27 47 149 258 271 470 122 212 234 405 248 430 30 53
|
||||
62 108 80 135 6 11 15 27 19 35 4 8 85 150 181 315 96 165 187 323 202 350 31
|
||||
56 116 202 130 225 5 8 25 42 43 75 19 33 92 159 162 280 149 257 157 271 202
|
||||
350 19 33 38 67 43 75 9 14 228 392 275 475 12 22 55 96 95 165 40 69 80 139
|
||||
90 155 24 42 202 350 221 383 9 15 27 47 41 72 14 25 75 131 136 236 61 106
|
||||
121 210 134 232 99 172 271 470 279 482 5 8 23 40 40 70 18 30 81 141 142 245
|
||||
60 105 121 210 135 235 14 25 71 124 127 220 56 96 143 247 194 335 51 88 96
|
||||
167 102 175 14 24 180 311 204 355 23 43 340 590 356 615 5 8 50 87 101 175
|
||||
171 301 517 898 582 1008 25 43 46 81 46 83 0 2 12 23 27 47 14 23 40 67 56
|
||||
97 16 30 35 62 42 70 7 8 15 22 18 30 4 8 20 38 37 65 16 28 33 57 37 65 6 12
|
||||
111 196 143 250 5 8 55 95 112 193 57 98 113 195 126 215 12 20 27 46 32 57 6
|
||||
11 14 27 20 35 5 8 76 130 156 270 80 140 165 287 187 325 23 39 52 90 66 115
|
||||
13 25 30 52 37 61 8 8 14 18 14 21 0 4 41 77 92 165 50 87 175 302 276 478
|
||||
101 176 208 360 236 408 28 49 67 117 86 152 19 35 41 70 48 77 6 6 12 15 12
|
||||
19 0 7 124 224 167 291 12 21 23 40 23 42 0 2 21 40 46 83 26 43 55 92 64 109
|
||||
54 95 327 568 354 614 19 30 45 75 59 100 71 128 82 145 89 148 4 2 8 8 8 13
|
||||
0 5 42 82 94 172 311 538 496 858 518 897 14 25 40 70 58 100 18 30 42 71 53
|
||||
90 10 19 79 139 152 265 73 127 142 246 153 265 10 19 43 76 72 125 29 50 63
|
||||
108 75 130 65 116 80 140 87 143 4 2 8 8 8 12 0 8 114 212 140 250 6 8 14 24
|
||||
20 35 5 11 54 97 108 190 l100 170 -9611 3 c-5286 1 -9614 -1 -9618 -5 -5 -6
|
||||
-419 -719 -619 -1068 -89 -155 -267 -463 -323 -560 -38 -66 -81 -140 -95 -165
|
||||
-31 -56 -263 -457 -526 -910 -110 -190 -224 -388 -254 -440 -29 -52 -61 -109
|
||||
-71 -125 -23 -39 -243 -420 -268 -465 -11 -19 -204 -352 -428 -740 -224 -388
|
||||
-477 -826 -563 -975 -85 -148 -185 -322 -222 -385 -37 -63 -120 -207 -185
|
||||
-320 -65 -113 -177 -306 -248 -430 -72 -124 -172 -297 -222 -385 -51 -88 -142
|
||||
-245 -202 -350 -131 -226 -247 -427 -408 -705 -65 -113 -249 -432 -410 -710
|
||||
-160 -278 -388 -673 -506 -877 -118 -205 -216 -373 -219 -373 -3 0 -52 82
|
||||
-109 183 -58 100 -144 250 -192 332 -95 164 -402 696 -647 1120 -85 149 -228
|
||||
396 -317 550 -212 365 -982 1700 -1008 1745 -10 19 -43 76 -72 125 -29 50 -64
|
||||
110 -77 135 -14 25 -63 110 -110 190 -47 80 -96 165 -110 190 -14 25 -99 171
|
||||
-188 325 -89 154 -174 300 -188 325 -13 25 -64 113 -112 195 -48 83 -140 242
|
||||
-205 355 -65 113 -183 317 -263 454 -79 137 -152 264 -163 282 -50 89 -335
|
||||
583 -354 614 -12 19 -34 58 -50 85 -15 28 -129 226 -253 440 -124 215 -235
|
||||
408 -247 430 -12 22 -69 121 -127 220 -58 99 -226 389 -373 645 -148 256 -324
|
||||
561 -392 678 -67 117 -134 232 -147 255 -13 23 -33 59 -46 80 l-22 37 -9615 0
|
||||
-9615 0 20 -32z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" style="padding: 0;height:100%">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<!--[if IE]>
|
||||
<link rel="icon" href="favicon.ico"/><![endif]--><title>Legado Bookshelf</title>
|
||||
<link href="css/about.b9bb4fe0.css" rel="prefetch">
|
||||
<link href="css/detail.e03dc50b.css" rel="prefetch">
|
||||
<link href="js/about.9f8f9ac0.js" rel="prefetch">
|
||||
<link href="js/about~detail.8270a871.js" rel="prefetch">
|
||||
<link href="js/detail.4e6a53a9.js" rel="prefetch">
|
||||
<link href="css/app.e4c919b7.css" rel="preload" as="style">
|
||||
<link href="css/chunk-vendors.8a465a1d.css" rel="preload" as="style">
|
||||
<link href="js/app.e84ee963.js" rel="preload" as="script">
|
||||
<link href="js/chunk-vendors.3ef7796f.js" rel="preload" as="script">
|
||||
<link href="css/chunk-vendors.8a465a1d.css" rel="stylesheet">
|
||||
<link href="css/app.e4c919b7.css" rel="stylesheet">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="img/icons/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="img/icons/favicon-16x16.png">
|
||||
<link rel="manifest" href="manifest.json">
|
||||
<meta name="theme-color" content="#4DBA87">
|
||||
<meta name="apple-mobile-web-app-capable" content="no">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<meta name="apple-mobile-web-app-title" content="yd-web-tool">
|
||||
<link rel="apple-touch-icon" href="img/icons/apple-touch-icon-152x152.png">
|
||||
<link rel="mask-icon" href="img/icons/safari-pinned-tab.svg" color="#4DBA87">
|
||||
<meta name="msapplication-TileImage" content="img/icons/msapplication-icon-144x144.png">
|
||||
<meta name="msapplication-TileColor" content="#000000">
|
||||
</head>
|
||||
<style>body::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<body style="margin: 0;height:100%">
|
||||
<noscript><strong>We're sorry but yd-web-tool doesn't work properly without JavaScript enabled.
|
||||
Please enable it to continue.</strong></noscript>
|
||||
<div id="app"></div>
|
||||
<script src="js/chunk-vendors.3ef7796f.js"></script>
|
||||
<script src="js/app.e84ee963.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
{"name":"yd-web-tool","short_name":"yd-web-tool","theme_color":"#4DBA87","icons":[{"src":"./img/icons/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"./img/icons/android-chrome-512x512.png","sizes":"512x512","type":"image/png"},{"src":"./img/icons/android-chrome-maskable-192x192.png","sizes":"192x192","type":"image/png","purpose":"maskable"},{"src":"./img/icons/android-chrome-maskable-512x512.png","sizes":"512x512","type":"image/png","purpose":"maskable"}],"start_url":".","display":"standalone","background_color":"#000000"}
|
||||
@@ -0,0 +1,70 @@
|
||||
self.__precacheManifest = (self.__precacheManifest || []).concat([
|
||||
{
|
||||
"revision": "c6913d775f2f965ac5f3",
|
||||
"url": "css/about.b9bb4fe0.css"
|
||||
},
|
||||
{
|
||||
"revision": "c7b3e35a4e0391b1ed37",
|
||||
"url": "css/app.e4c919b7.css"
|
||||
},
|
||||
{
|
||||
"revision": "9a65f05f9810a3ea7f46",
|
||||
"url": "css/chunk-vendors.8a465a1d.css"
|
||||
},
|
||||
{
|
||||
"revision": "5ab5c6be15b21e2d609b",
|
||||
"url": "css/detail.e03dc50b.css"
|
||||
},
|
||||
{
|
||||
"revision": "535877f50039c0cb49a6196a5b7517cd",
|
||||
"url": "fonts/element-icons.535877f5.woff"
|
||||
},
|
||||
{
|
||||
"revision": "732389ded34cb9c52dd88271f1345af9",
|
||||
"url": "fonts/element-icons.732389de.ttf"
|
||||
},
|
||||
{
|
||||
"revision": "f9a3fb0e145017e166dd4d91d9280cc4",
|
||||
"url": "fonts/iconfont.f9a3fb0e.woff"
|
||||
},
|
||||
{
|
||||
"revision": "f39ecc1a1d2a1eff3aca8aadd818bb61",
|
||||
"url": "fonts/popfont.f39ecc1a.ttf"
|
||||
},
|
||||
{
|
||||
"revision": "6c094b6d4ae9404dbed273c41b06fae8",
|
||||
"url": "fonts/shelffont.6c094b6d.ttf"
|
||||
},
|
||||
{
|
||||
"revision": "8a8424347500238b7b6c08a98d0f89af",
|
||||
"url": "index.html"
|
||||
},
|
||||
{
|
||||
"revision": "c6913d775f2f965ac5f3",
|
||||
"url": "js/about.9f8f9ac0.js"
|
||||
},
|
||||
{
|
||||
"revision": "5998ccb313ed338c15e1",
|
||||
"url": "js/about~detail.8270a871.js"
|
||||
},
|
||||
{
|
||||
"revision": "c7b3e35a4e0391b1ed37",
|
||||
"url": "js/app.e84ee963.js"
|
||||
},
|
||||
{
|
||||
"revision": "9a65f05f9810a3ea7f46",
|
||||
"url": "js/chunk-vendors.3ef7796f.js"
|
||||
},
|
||||
{
|
||||
"revision": "5ab5c6be15b21e2d609b",
|
||||
"url": "js/detail.4e6a53a9.js"
|
||||
},
|
||||
{
|
||||
"revision": "b46d04eb43bc31ca0f9f95121646440d",
|
||||
"url": "manifest.json"
|
||||
},
|
||||
{
|
||||
"revision": "b6216d61c03e6ce0c9aea6ca7808f7ca",
|
||||
"url": "robots.txt"
|
||||
}
|
||||
]);
|
||||
@@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Welcome to your Workbox-powered service worker!
|
||||
*
|
||||
* You'll need to register this file in your web app and you should
|
||||
* disable HTTP caching for this file too.
|
||||
* See https://goo.gl/nhQhGp
|
||||
*
|
||||
* The rest of the code is auto-generated. Please don't update this file
|
||||
* directly; instead, make changes to your Workbox build configuration
|
||||
* and re-run your build process.
|
||||
* See https://goo.gl/2aRDsh
|
||||
*/
|
||||
|
||||
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
|
||||
|
||||
importScripts(
|
||||
"precache-manifest.5ae9ceec57e7f0f3cc808807b7fe5f32.js"
|
||||
);
|
||||
|
||||
workbox.core.setCacheNameDetails({prefix: "yd-web-tool"});
|
||||
|
||||
self.addEventListener('message', (event) => {
|
||||
if (event.data && event.data.type === 'SKIP_WAITING') {
|
||||
self.skipWaiting();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The workboxSW.precacheAndRoute() method efficiently caches and responds to
|
||||
* requests for URLs in the manifest.
|
||||
* See https://goo.gl/S9QRab
|
||||
*/
|
||||
self.__precacheManifest = [].concat(self.__precacheManifest || []);
|
||||
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 92 KiB |
@@ -0,0 +1,68 @@
|
||||
<!DOCTYPE HTML>
|
||||
<!--
|
||||
Forty by HTML5 UP
|
||||
html5up.net | @ajlkn
|
||||
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
|
||||
-->
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Legado web 导航</title>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/>
|
||||
<link rel="stylesheet" href="assets/css/main.css"/>
|
||||
</head>
|
||||
|
||||
<body class="is-preload">
|
||||
|
||||
<!-- Wrapper -->
|
||||
<div id="wrapper">
|
||||
|
||||
<!-- Header -->
|
||||
<header id="header" class="alt">
|
||||
<nav>
|
||||
<a href="#menu">Menu</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<!-- Menu -->
|
||||
<nav id="menu">
|
||||
<ul class="links">
|
||||
<li><a href="https://github.com/gedoor/legado">Github</a></li>
|
||||
<li><a href="https://github.com/zsakvo">zsakvo</a></li>
|
||||
<li><a href="#">Design: HTML5 UP</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<!-- Banner -->
|
||||
<section id="banner" class="major">
|
||||
<div class="inner">
|
||||
<header class="major">
|
||||
<h1>昨日邻家乞新火,晓窗分与读书灯</h1>
|
||||
</header>
|
||||
<div class="content">
|
||||
<!-- <p>A responsive site template designed by HTML5 UP<br />
|
||||
and released under the Creative Commons.</p> -->
|
||||
<ul class="actions">
|
||||
<li><a href="bookshelf/index.html" class="button next scrolly">书架</a></li>
|
||||
</ul>
|
||||
<ul class="actions">
|
||||
<li><a href="bookSource/index.html" class="button next scrolly">书源</a></li>
|
||||
</ul>
|
||||
<ul class="actions">
|
||||
<li><a href="uploadBook/index.html" class="button next scrolly">传书</a></li>
|
||||
</ul>
|
||||
<ul class="actions">
|
||||
<li><a href="rssSource/index.html" class="button next scrolly">订阅源</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="assets/js/dist.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,150 @@
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
.editor {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
}
|
||||
.setbox,
|
||||
.menu,
|
||||
.outbox {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
max-height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.menu {
|
||||
justify-content: center;
|
||||
max-width: 90px;
|
||||
margin: 0 5px;
|
||||
}
|
||||
.menu .button {
|
||||
width: 90px;
|
||||
height: 30px;
|
||||
min-height: 30px;
|
||||
margin: 5px 0px;
|
||||
cursor: pointer;
|
||||
}
|
||||
@keyframes stroker {
|
||||
0% {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: -240;
|
||||
}
|
||||
}
|
||||
.button rect {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
fill: transparent;
|
||||
stroke: #666;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
.button rect.busy {
|
||||
stroke: #fd1850;
|
||||
stroke-dasharray: 30 90;
|
||||
animation: stroker 1s linear infinite;
|
||||
}
|
||||
.button text {
|
||||
text-anchor: middle;
|
||||
dominant-baseline: middle;
|
||||
}
|
||||
.setbox {
|
||||
min-width: 40em;
|
||||
}
|
||||
.rules {
|
||||
overflow: auto;
|
||||
}
|
||||
.tabbox {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
.rules > * {
|
||||
display: flex;
|
||||
margin: 2px 0;
|
||||
}
|
||||
.rules textarea {
|
||||
flex: 1;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.rules > *,
|
||||
.rules > * > div,
|
||||
.rules textarea {
|
||||
min-height: 1em;
|
||||
}
|
||||
textarea {
|
||||
word-break: break-all;
|
||||
}
|
||||
.tabtitle {
|
||||
display: flex;
|
||||
z-index: 1;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.tabtitle > div {
|
||||
cursor: pointer;
|
||||
padding: 1px 10px 0 10px;
|
||||
border-bottom: 3px solid transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
.tabtitle > .this {
|
||||
color: #4f9da6;
|
||||
border-bottom-color: #4ebbe4;
|
||||
}
|
||||
.tabbody {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
margin-top: -1px;
|
||||
border: 1px solid #a9a9a9;
|
||||
height: 0;
|
||||
}
|
||||
.tabbody > * {
|
||||
flex: 1;
|
||||
flex-flow: column;
|
||||
display: none;
|
||||
}
|
||||
.tabbody > .this {
|
||||
display: flex;
|
||||
}
|
||||
.tabbody > * > .titlebar {
|
||||
display: flex;
|
||||
}
|
||||
.tabbody > * > .titlebar > * {
|
||||
flex: 1;
|
||||
margin: 1px 1px 1px 1px;
|
||||
}
|
||||
.tabbody > * > .context {
|
||||
flex: 1;
|
||||
flex-flow: column;
|
||||
border: 0;
|
||||
padding: 5px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.tabbody > * > .inputbox {
|
||||
border: 0;
|
||||
border-bottom: #a9a9a9 solid 1px;
|
||||
height: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
.link > * {
|
||||
display: flex;
|
||||
margin: 5px;
|
||||
border-bottom: 1px solid;
|
||||
text-decoration: none;
|
||||
}
|
||||
#RuleList > label > * {
|
||||
background: #eee;
|
||||
padding-left: 3px;
|
||||
margin: 2px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
#RuleList input[type="radio"] {
|
||||
display: none;
|
||||
}
|
||||
#RuleList input[type="radio"]:checked + * {
|
||||
background: #15cda8;
|
||||
}
|
||||
.isError {
|
||||
color: #ff0000;
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>阅读3.0订阅源编辑器_V4.0</title>
|
||||
<link rel="icon" href="favicon.ico">
|
||||
<link rel="stylesheet" type="text/css" href="index.css"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="editor">
|
||||
<div class="setbox">
|
||||
<div>
|
||||
<a href="../index.html">←主页</a>
|
||||
<b>订阅源</b>
|
||||
</div>
|
||||
<div class="rules">
|
||||
<div><b>基本</b></div>
|
||||
<div>
|
||||
<div>源域名 :</div>
|
||||
<textarea rows="1" id="sourceUrl" class="base" title="sourceUrl"
|
||||
placeholder="<必填>通常填写网站主页,例: https://www.qidian.com"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>源名称 :</div>
|
||||
<textarea rows="1" id="sourceName" class="base" title="sourceName"
|
||||
placeholder="<必填>源名称"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>图标 :</div>
|
||||
<textarea rows="1" id="sourceIcon" class="base" title="sourceIcon"
|
||||
placeholder="<选填>图标"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>源分组 :</div>
|
||||
<textarea rows="1" id="sourceGroup" class="base" title="sourceGroup"
|
||||
placeholder="<选填>描述源的特征信息"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>源注释 :</div>
|
||||
<textarea rows="1" id="sourceComment" class="base" title="sourceComment"
|
||||
placeholder="<选填>描述源作者和状态"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>登录地址:</div>
|
||||
<textarea rows="1" id="loginUrl" class="base" title="loginUrl"
|
||||
placeholder="<选填>填写网站登录网址,仅在需要登录的源有用"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>登录界面:</div>
|
||||
<textarea rows="3" id="loginUi" class="base" title="loginUi"
|
||||
placeholder="<选填>自定义登录界面"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>登录检测:</div>
|
||||
<textarea rows="3" id="loginCheckJs" class="base" title="loginCheckJs"
|
||||
placeholder="<选填>登录检测js"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>并发率 :</div>
|
||||
<textarea rows="1" id="concurrentRate" class="base" title="concurrentRate"
|
||||
placeholder="<选填>并发率"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>请求头 :</div>
|
||||
<textarea rows="3" id="header" class="base" title="header"
|
||||
placeholder="<选填>客户端标识"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>分类地址:</div>
|
||||
<textarea rows="3" id="sortUrl" class="base" title="sortUrl"
|
||||
placeholder="<选填>例: 名称1::网址(Url)1 名称2::网址(Url)2 ..."></textarea>
|
||||
</div>
|
||||
<p></p>
|
||||
<div><b>列表规则</b></div>
|
||||
<div>
|
||||
<div>列表样式:</div>
|
||||
<textarea rows="1" id="articleStyle" class="base" title="articleStyle"
|
||||
placeholder="列表样式:0,1,2"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>列表规则:</div>
|
||||
<textarea rows="1" id="ruleArticles" class="base" title="ruleArticles"
|
||||
placeholder="列表规则 (规则结果为List<Element>)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>标题规则:</div>
|
||||
<textarea rows="1" id="ruleTitle" class="base" title="ruleTitle"
|
||||
placeholder="选择节点书名 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>时间规则:</div>
|
||||
<textarea rows="1" id="rulePubDate" class="base" title="rulePubDate"
|
||||
placeholder="发表时间 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>翻页规则:</div>
|
||||
<textarea rows="1" id="ruleNextPage" class="base" title="ruleNextPage"
|
||||
placeholder="下一页链接 (规则结果为List<String>或String)"></textarea>
|
||||
</div>
|
||||
<p></p>
|
||||
<div><b>WebView规则</b></div>
|
||||
<div>
|
||||
<div>加载地址:</div>
|
||||
<textarea rows="1" id="loadWithBaseUrl" class="base" title="loadWithBaseUrl"
|
||||
placeholder="是否加载url (启用: true 关闭: false (可选,默认true))"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>启用JS :</div>
|
||||
<textarea rows="1" id="enableJs" class="base" title="enableJs"
|
||||
placeholder="是否启用Js (启用: true 关闭: false (可选,默认true))"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>描述规则:</div>
|
||||
<textarea rows="1" id="ruleDescription" class="base" title="ruleDescription"
|
||||
placeholder="rss内容 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>图片地址:</div>
|
||||
<textarea rows="1" id="ruleImage" class="base" title="ruleImage"
|
||||
placeholder="图片rul规则 (规则结果为url)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>原文链接:</div>
|
||||
<textarea rows="1" id="ruleLink" class="base" title="ruleLink"
|
||||
placeholder="原文链接规则 (规则结果为url)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>内容规则:</div>
|
||||
<textarea rows="1" id="ruleContent" class="base" title="ruleContent"
|
||||
placeholder="内容规则 (规则结果为String)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>内容样式:</div>
|
||||
<textarea rows="1" id="style" class="base" title="style"
|
||||
placeholder="内容样式 (css样式)"></textarea>
|
||||
</div>
|
||||
<p></p>
|
||||
<div><b>其它规则</b></div>
|
||||
<div>
|
||||
<div>启用 :</div>
|
||||
<textarea rows="1" id="enabled" class="base" title="enabled"
|
||||
placeholder="启用: true 关闭: false (可选,默认true)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<div>排序编号:</div>
|
||||
<textarea rows="1" id="customOrder" class="base" title="customOrder"
|
||||
placeholder="整数: 0~N (可选,默认0) | 数字越小越靠前"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu">
|
||||
<svg class="button">
|
||||
<text x="50%" y="55%">⇈推送源</text>
|
||||
<rect id="push"></rect>
|
||||
</svg>
|
||||
<svg class="button">
|
||||
<text x="50%" y="55%">⇊拉取源</text>
|
||||
<rect id="pull"></rect>
|
||||
</svg>
|
||||
<svg class="button">
|
||||
<text x="50%" y="55%">⋘编辑源</text>
|
||||
<rect id="editor"></rect>
|
||||
</svg>
|
||||
<svg class="button">
|
||||
<text x="50%" y="55%">⋙生成源</text>
|
||||
<rect id="conver"></rect>
|
||||
</svg>
|
||||
<svg class="button">
|
||||
<text x="50%" y="55%">✗清空表单</text>
|
||||
<rect id="initial"></rect>
|
||||
</svg>
|
||||
<svg class="button">
|
||||
<text x="50%" y="55%">↶撤销操作</text>
|
||||
<rect id="undo"></rect>
|
||||
</svg>
|
||||
<svg class="button">
|
||||
<text x="50%" y="55%">↷重做操作</text>
|
||||
<rect id="redo"></rect>
|
||||
</svg>
|
||||
<svg class="button">
|
||||
<text x="50%" y="55%">⇏调试源</text>
|
||||
<rect id="debug"></rect>
|
||||
</svg>
|
||||
<svg class="button">
|
||||
<text x="50%" y="55%">✓保存源</text>
|
||||
<rect id="accept"></rect>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="outbox">
|
||||
<div class="tabbox">
|
||||
<div class="tabtitle">
|
||||
<div name="编辑源" class="tab1 this">编辑源</div>
|
||||
<div name="调试源" class="tab2">调试源</div>
|
||||
<div name="源列表" class="tab3">源列表</div>
|
||||
<div name="帮助信息" class="tab4">帮助信息</div>
|
||||
</div>
|
||||
<div class="tabbody">
|
||||
<div class="tab1 this">
|
||||
<textarea class="context" id="RuleJsonString"
|
||||
placeholder="这里输出序列化的JSON数据,可直接导入'阅读'APP"></textarea>
|
||||
</div>
|
||||
<div class="tab2">
|
||||
<textarea class="context" id="DebugConsole" placeholder="这里用于输出调试信息"></textarea>
|
||||
</div>
|
||||
<div class="tab3">
|
||||
<input type="text" class="inputbox" id="Filter"
|
||||
placeholder="输入筛选关键词(源名称、源URL或源分组)后按回车筛选源">
|
||||
<div class="titlebar">
|
||||
<button id="Import">导入源文件</button>
|
||||
<button id="Export">导出源文件</button>
|
||||
<button id="Delete">删除选中源</button>
|
||||
<button id="ClrAll">清空列表</button>
|
||||
</div>
|
||||
<div class="context" id="RuleList"></div>
|
||||
</div>
|
||||
<div class="tab4">
|
||||
<div class="context link">
|
||||
<a target="_blank" href="https://alanskycn.gitee.io/teachme">源制作教程</a>
|
||||
<a target="_blank"
|
||||
href="https://zhuanlan.zhihu.com/p/29436838">Xpath基础教程</a>
|
||||
<a target="_blank"
|
||||
href="https://zhuanlan.zhihu.com/p/32187820">Xpath高级教程</a>
|
||||
<a target="_blank" href="https://www.w3cschool.cn/regex_rmjc">正则表达式教程</a>
|
||||
<a target="_blank" href="https://regexr.com">正则表达式在线验证工具</a>
|
||||
<div>^$()[]{}.?+*| 这些是Java正则特殊符号,匹配需转义
|
||||
<br>(?s) 前缀表示跨行解析
|
||||
<br>(?m) 前缀表示逐行匹配
|
||||
<br>(?i) 前缀表示忽略大小写
|
||||
</div>
|
||||
<a target="_blank" href="https://www.beta.browxy.com">代码在线运行工具</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/assets/js/md5.js"></script>
|
||||
<script type="text/javascript" src="index.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,395 @@
|
||||
// 简化js原生选择器
|
||||
function $(selector) { return document.querySelector(selector); }
|
||||
function $$(selector) { return document.querySelectorAll(selector); }
|
||||
// 读写Hash值(val未赋值时为读取)
|
||||
function hashParam(key, val) {
|
||||
let hashstr = decodeURIComponent(window.location.hash);
|
||||
let regKey = new RegExp(`${key}=([^&]*)`);
|
||||
let getVal = regKey.test(hashstr) ? hashstr.match(regKey)[1] : null;
|
||||
if (val == undefined) return getVal;
|
||||
if (hashstr == '' || hashstr == '#') {
|
||||
window.location.hash = `#${key}=${val}`;
|
||||
}
|
||||
else {
|
||||
if (getVal) window.location.hash = hashstr.replace(getVal, val);
|
||||
else {
|
||||
window.location.hash = hashstr.indexOf(key) > -1 ? hashstr.replace(regKey, `${key}=${val}`) : `${hashstr}&${key}=${val}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 创建源规则容器对象
|
||||
function Container() {
|
||||
let ruleJson = {};
|
||||
|
||||
// 基本以及其他
|
||||
$$('.rules .base').forEach(item => ruleJson[item.title] = '');
|
||||
ruleJson.customOrder = 0;
|
||||
ruleJson.enabled = true;
|
||||
|
||||
return ruleJson;
|
||||
}
|
||||
// 选项卡Tab切换事件处理
|
||||
function showTab(tabName) {
|
||||
$$('.tabtitle>*').forEach(node => { node.className = node.className.replace(' this', ''); });
|
||||
$$('.tabbody>*').forEach(node => { node.className = node.className.replace(' this', ''); });
|
||||
$(`.tabbody>.${$(`.tabtitle>*[name=${tabName}]`).className}`).className += ' this';
|
||||
$(`.tabtitle>*[name=${tabName}]`).className += ' this';
|
||||
hashParam('tab', tabName);
|
||||
}
|
||||
// 源列表列表标签构造函数
|
||||
function newRule(rule) {
|
||||
return `<label for="${hex_md5(rule.sourceUrl)}"><input type="radio" name="rule" id="${hex_md5(rule.sourceUrl)}"><div>${rule.sourceName}<br>${rule.sourceUrl}</div></label>`;
|
||||
}
|
||||
// 缓存规则列表
|
||||
var RuleSources = [];
|
||||
if (localStorage.getItem('RssSources')) {
|
||||
RuleSources = JSON.parse(localStorage.getItem('RssSources'));
|
||||
RuleSources.forEach(item => $('#RuleList').innerHTML += newRule(item));
|
||||
}
|
||||
// 页面加载完成事件
|
||||
window.onload = () => {
|
||||
$$('.tabtitle>*').forEach(item => {
|
||||
item.addEventListener('click', () => {
|
||||
showTab(item.innerHTML);
|
||||
});
|
||||
});
|
||||
if (hashParam('tab')) showTab(hashParam('tab'));
|
||||
}
|
||||
// 获取数据
|
||||
function HttpGet(url) {
|
||||
return fetch(hashParam('domain') ? hashParam('domain') + url : url)
|
||||
.then(res => res.json()).catch(err => console.error('Error:', err));
|
||||
}
|
||||
// 提交数据
|
||||
function HttpPost(url, data) {
|
||||
return fetch(hashParam('domain') ? hashParam('domain') + url : url, {
|
||||
body: JSON.stringify(data),
|
||||
method: 'POST',
|
||||
mode: "cors",
|
||||
headers: new Headers({
|
||||
'Content-Type': 'application/json;charset=utf-8'
|
||||
})
|
||||
}).then(res => res.json()).catch(err => console.error('Error:', err));
|
||||
}
|
||||
// 将源表单转化为源对象
|
||||
function rule2json() {
|
||||
let RuleJSON = Container();
|
||||
// 转换base
|
||||
Object.keys(RuleJSON).forEach(key => {
|
||||
if (!key.startsWith("rule")) {
|
||||
RuleJSON[key] = $('#' + key).value;
|
||||
}
|
||||
});
|
||||
|
||||
RuleJSON.lastUpdateTime = new Date().getTime();
|
||||
RuleJSON.customOrder = RuleJSON.customOrder == '' ? 0 : parseInt(RuleJSON.customOrder);
|
||||
RuleJSON.enabled = RuleJSON.enabled == '' || String(RuleJSON.enabled).toLocaleLowerCase().replace(/^\s*|\s*$/g, '') == 'true';
|
||||
return RuleJSON;
|
||||
}
|
||||
// 将源对象填充到源表单
|
||||
function json2rule(RuleEditor) {
|
||||
let RuleJSON = Container();
|
||||
// 转换base
|
||||
Object.keys(RuleJSON).forEach(key => {
|
||||
let val = RuleEditor[key];
|
||||
if (typeof val == "number") {
|
||||
$("#" + key).value = val ? String(val) : '0';
|
||||
}
|
||||
else if (typeof val == "boolean") {
|
||||
$("#" + key).value = val ? String(val) : 'false';
|
||||
}
|
||||
else {
|
||||
$("#" + key).value = val ? String(val) : '';
|
||||
}
|
||||
});
|
||||
}
|
||||
// 记录操作过程
|
||||
var course = { "old": [], "now": {}, "new": [] };
|
||||
if (localStorage.getItem('rssSourceCourse')) {
|
||||
course = JSON.parse(localStorage.getItem('rssSourceCourse'));
|
||||
json2rule(course.now);
|
||||
}
|
||||
else {
|
||||
course.now = rule2json();
|
||||
window.localStorage.setItem('rssSourceCourse', JSON.stringify(course));
|
||||
}
|
||||
function todo() {
|
||||
course.old.push(Object.assign({}, course.now));
|
||||
course.now = rule2json();
|
||||
course.new = [];
|
||||
if (course.old.length > 50) course.old.shift(); // 限制历史记录堆栈大小
|
||||
localStorage.setItem('rssSourceCourse', JSON.stringify(course));
|
||||
}
|
||||
function undo() {
|
||||
course = JSON.parse(localStorage.getItem('rssSourceCourse'));
|
||||
if (course.old.length > 0) {
|
||||
course.new.push(course.now);
|
||||
course.now = course.old.pop();
|
||||
localStorage.setItem('rssSourceCourse', JSON.stringify(course));
|
||||
json2rule(course.now);
|
||||
}
|
||||
}
|
||||
function redo() {
|
||||
course = JSON.parse(localStorage.getItem('rssSourceCourse'));
|
||||
if (course.new.length > 0) {
|
||||
course.old.push(course.now);
|
||||
course.now = course.new.pop();
|
||||
localStorage.setItem('rssSourceCourse', JSON.stringify(course));
|
||||
json2rule(course.now);
|
||||
}
|
||||
}
|
||||
function setRule(editRule) {
|
||||
let checkRule = RuleSources.find(x => x.sourceUrl == editRule.sourceUrl);
|
||||
if ($(`input[id="${hex_md5(editRule.sourceUrl)}"]`)) {
|
||||
Object.keys(checkRule).forEach(key => { checkRule[key] = editRule[key]; });
|
||||
$(`input[id="${hex_md5(editRule.sourceUrl)}"]+*`).innerHTML = `${editRule.sourceName}<br>${editRule.sourceUrl}`;
|
||||
} else {
|
||||
RuleSources.push(editRule);
|
||||
$('#RuleList').innerHTML += newRule(editRule);
|
||||
}
|
||||
}
|
||||
$$('input').forEach((item) => { item.addEventListener('change', () => { todo() }) });
|
||||
$$('textarea').forEach((item) => { item.addEventListener('change', () => { todo() }) });
|
||||
// 处理按钮点击事件
|
||||
$('.menu').addEventListener('click', e => {
|
||||
let thisNode = e.target;
|
||||
thisNode = thisNode.parentNode.nodeName == 'svg' ? thisNode.parentNode.querySelector('rect') :
|
||||
thisNode.nodeName == 'svg' ? thisNode.querySelector('rect') : null;
|
||||
if (!thisNode) return;
|
||||
if (thisNode.getAttribute('class') == 'busy') return;
|
||||
thisNode.setAttribute('class', 'busy');
|
||||
switch (thisNode.id) {
|
||||
case 'push':
|
||||
$$('#RuleList>label>div').forEach(item => { item.className = ''; });
|
||||
(async () => {
|
||||
await HttpPost(`/saveRssSources`, RuleSources).then(json => {
|
||||
if (json.isSuccess) {
|
||||
let okData = json.data;
|
||||
if (Array.isArray(okData)) {
|
||||
let failMsg = ``;
|
||||
if (RuleSources.length > okData.length) {
|
||||
RuleSources.forEach(item => {
|
||||
if (okData.find(x => x.sourceUrl == item.sourceUrl)) { }
|
||||
else { $(`#RuleList #${item.sourceUrl}+*`).className += 'isError'; }
|
||||
});
|
||||
failMsg = '\n推送失败的源将用红色字体标注!';
|
||||
}
|
||||
alert(`批量推送源到「阅读3.0APP」\n共计: ${RuleSources.length} 条\n成功: ${okData.length} 条\n失败: ${RuleSources.length - okData.length} 条${failMsg}`);
|
||||
}
|
||||
else {
|
||||
alert(`批量推送源到「阅读3.0APP」成功!\n共计: ${RuleSources.length} 条`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
alert(`批量推送源失败!\nErrorMsg: ${json.errorMsg}`);
|
||||
}
|
||||
}).catch(err => { alert(`批量推送源失败,无法连接到「阅读3.0APP」!\n${err}`); });
|
||||
thisNode.setAttribute('class', '');
|
||||
})();
|
||||
return;
|
||||
case 'pull':
|
||||
showTab('源列表');
|
||||
(async () => {
|
||||
await HttpGet(`/getRssSources`).then(json => {
|
||||
if (json.isSuccess) {
|
||||
$('#RuleList').innerHTML = ''
|
||||
localStorage.setItem('RssSources', JSON.stringify(RuleSources = json.data));
|
||||
RuleSources.forEach(item => {
|
||||
$('#RuleList').innerHTML += newRule(item);
|
||||
});
|
||||
alert(`成功拉取 ${RuleSources.length} 条源`);
|
||||
}
|
||||
else {
|
||||
alert(`批量拉取源失败!\nErrorMsg: ${json.errorMsg}`);
|
||||
}
|
||||
}).catch(err => { alert(`批量拉取源失败,无法连接到「阅读3.0APP」!\n${err}`); });
|
||||
thisNode.setAttribute('class', '');
|
||||
})();
|
||||
return;
|
||||
case 'editor':
|
||||
if ($('#RuleJsonString').value == '') break;
|
||||
try {
|
||||
json2rule(JSON.parse($('#RuleJsonString').value));
|
||||
todo();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
alert(error);
|
||||
}
|
||||
break;
|
||||
case 'conver':
|
||||
showTab('编辑源');
|
||||
$('#RuleJsonString').value = JSON.stringify(rule2json(), null, 4);
|
||||
break;
|
||||
case 'initial':
|
||||
$$('.rules textarea').forEach(item => { item.value = '' });
|
||||
todo();
|
||||
break;
|
||||
case 'undo':
|
||||
undo()
|
||||
break;
|
||||
case 'redo':
|
||||
redo()
|
||||
break;
|
||||
case 'debug':
|
||||
showTab('调试源');
|
||||
let wsOrigin = (hashParam('domain') || location.origin).replace(/^.*?:/, 'ws:').replace(/\d+$/, (port) => (parseInt(port) + 1));
|
||||
let DebugInfos = $('#DebugConsole');
|
||||
function DebugPrint(msg) { DebugInfos.value += `\n${msg}`; DebugInfos.scrollTop = DebugInfos.scrollHeight; }
|
||||
let saveRule = [rule2json()];
|
||||
HttpPost(`/saveRssSource`, saveRule[0]).then(sResult => {
|
||||
if (sResult.isSuccess) {
|
||||
$('#DebugConsole').value = `源《${saveRule[0].sourceName}》保存成功!开始调试...`;
|
||||
let ws = new WebSocket(`${wsOrigin}/rssSourceDebug`);
|
||||
ws.onopen = () => {
|
||||
ws.send(`{"tag":"${saveRule[0].sourceUrl}", "key":""}`);
|
||||
};
|
||||
ws.onmessage = (msg) => {
|
||||
console.log('[调试]', msg);
|
||||
DebugPrint(msg.data);
|
||||
};
|
||||
ws.onerror = (err) => {
|
||||
throw `${err.data}`;
|
||||
}
|
||||
ws.onclose = () => {
|
||||
thisNode.setAttribute('class', '');
|
||||
DebugPrint(`\n调试服务已关闭!`);
|
||||
}
|
||||
} else throw `${sResult.errorMsg}`;
|
||||
}).catch(err => {
|
||||
DebugPrint(`调试过程意外中止,以下是详细错误信息:\n${err}`);
|
||||
thisNode.setAttribute('class', '');
|
||||
});
|
||||
return;
|
||||
case 'accept':
|
||||
(async () => {
|
||||
let saveRule = [rule2json()];
|
||||
await HttpPost(`/saveRssSources`, saveRule).then(json => {
|
||||
alert(json.isSuccess ? `源《${saveRule[0].sourceName}》已成功保存到「阅读3.0APP」` : `源《${saveRule[0].sourceName}》保存失败!\nErrorMsg: ${json.errorMsg}`);
|
||||
setRule(saveRule[0]);
|
||||
}).catch(err => { alert(`保存源失败,无法连接到「阅读3.0APP」!\n${err}`); });
|
||||
thisNode.setAttribute('class', '');
|
||||
})();
|
||||
return;
|
||||
default:
|
||||
}
|
||||
setTimeout(() => { thisNode.setAttribute('class', ''); }, 500);
|
||||
});
|
||||
$('#Filter').addEventListener('keydown', e => {
|
||||
if (e.keyCode == 13) {
|
||||
let cashList = [];
|
||||
$('#RuleList').innerHTML = "";
|
||||
let sKey = Filter.value ? Filter.value : '';
|
||||
if (sKey == '') {
|
||||
cashList = RuleSources;
|
||||
} else {
|
||||
let patt = new RegExp(sKey);
|
||||
RuleSources.forEach(source => {
|
||||
if (patt.test(source.sourceUrl) || patt.test(source.sourceName) || patt.test(source.bookSourceGroup)) {
|
||||
cashList.push(source);
|
||||
}
|
||||
})
|
||||
}
|
||||
cashList.forEach(source => {
|
||||
$('#RuleList').innerHTML += newRule(source);
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// 列表规则更改事件
|
||||
$('#RuleList').addEventListener('click', e => {
|
||||
let editRule = null;
|
||||
if (e.target && e.target.getAttribute('name') == 'rule') {
|
||||
editRule = rule2json();
|
||||
json2rule(RuleSources.find(x => hex_md5(x.sourceUrl) == e.target.id));
|
||||
} else return;
|
||||
if (editRule.sourceUrl == '') return;
|
||||
if (editRule.sourceName == '') editRule.sourceName = editRule.sourceUrl.replace(/.*?\/\/|\/.*/g, '');
|
||||
setRule(editRule);
|
||||
localStorage.setItem('RssSources', JSON.stringify(RuleSources));
|
||||
});
|
||||
// 处理列表按钮事件
|
||||
$('.tab3>.titlebar').addEventListener('click', e => {
|
||||
let thisNode = e.target;
|
||||
if (thisNode.nodeName != 'BUTTON') return;
|
||||
switch (thisNode.id) {
|
||||
case 'Import':
|
||||
let fileImport = document.createElement('input');
|
||||
fileImport.type = 'file';
|
||||
fileImport.accept = '.json';
|
||||
fileImport.addEventListener('change', () => {
|
||||
let file = fileImport.files[0];
|
||||
let reader = new FileReader();
|
||||
reader.onloadend = function (evt) {
|
||||
if (evt.target.readyState == FileReader.DONE) {
|
||||
let fileText = evt.target.result;
|
||||
try {
|
||||
let fileJson = JSON.parse(fileText);
|
||||
let newSources = [];
|
||||
newSources.push(...fileJson);
|
||||
if (window.confirm(`如何处理导入的源?\n"确定": 覆盖当前列表(不会删除APP源)\n"取消": 插入列表尾部(自动忽略重复源)`)) {
|
||||
localStorage.setItem('RssSources', JSON.stringify(RuleSources = newSources));
|
||||
$('#RuleList').innerHTML = ''
|
||||
RuleSources.forEach(item => {
|
||||
$('#RuleList').innerHTML += newRule(item);
|
||||
});
|
||||
}
|
||||
else {
|
||||
newSources = newSources.filter(item => !JSON.stringify(RuleSources).includes(item.sourceUrl));
|
||||
RuleSources.push(...newSources);
|
||||
localStorage.setItem('RssSources', JSON.stringify(RuleSources));
|
||||
newSources.forEach(item => {
|
||||
$('#RuleList').innerHTML += newRule(item);
|
||||
});
|
||||
}
|
||||
alert(`成功导入 ${newSources.length} 条源`);
|
||||
}
|
||||
catch (err) {
|
||||
alert(`导入源文件失败!\n${err}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}, false);
|
||||
fileImport.click();
|
||||
break;
|
||||
case 'Export':
|
||||
let fileExport = document.createElement('a');
|
||||
fileExport.download = `Rules${Date().replace(/.*?\s(\d+)\s(\d+)\s(\d+:\d+:\d+).*/, '$2$1$3').replace(/:/g, '')}.json`;
|
||||
let myBlob = new Blob([JSON.stringify(RuleSources, null, 4)], { type: "application/json" });
|
||||
fileExport.href = window.URL.createObjectURL(myBlob);
|
||||
fileExport.click();
|
||||
break;
|
||||
case 'Delete':
|
||||
let selectRule = $('#RuleList input:checked');
|
||||
if (!selectRule) {
|
||||
alert(`没有源被选中!`);
|
||||
return;
|
||||
}
|
||||
if (confirm(`确定要删除选定源吗?\n(同时删除APP内源)`)) {
|
||||
let selectRuleUrl = selectRule.id;
|
||||
let deleteSources = RuleSources.filter(item => item.sourceUrl == selectRuleUrl); // 提取待删除的源
|
||||
let laveSources = RuleSources.filter(item => !(item.sourceUrl == selectRuleUrl)); // 提取待留下的源
|
||||
HttpPost(`/deleteRssSources`, deleteSources).then(json => {
|
||||
if (json.isSuccess) {
|
||||
let selectNode = document.getElementById(selectRuleUrl).parentNode;
|
||||
selectNode.parentNode.removeChild(selectNode);
|
||||
localStorage.setItem('RssSources', JSON.stringify(RuleSources = laveSources));
|
||||
if ($('#sourceUrl').value == selectRuleUrl) {
|
||||
$$('.rules textarea').forEach(item => { item.value = '' });
|
||||
todo();
|
||||
}
|
||||
console.log(deleteSources);
|
||||
console.log(`以上源已删除!`)
|
||||
}
|
||||
}).catch(err => { alert(`删除源失败,无法连接到「阅读3.0APP」!\n${err}`); });
|
||||
}
|
||||
break;
|
||||
case 'ClrAll':
|
||||
if (confirm(`确定要清空当前源列表吗?\n(不会删除APP内源)`)) {
|
||||
localStorage.setItem('RssSources', JSON.stringify(RuleSources = []));
|
||||
$('#RuleList').innerHTML = ''
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
@charset "utf-8";
|
||||
/* CSS Document */
|
||||
body, div, p, ul, h1, h2, h3, h4, h5,span , img,dl,dt,dd{ margin: 0; padding: 0; }
|
||||
html, body { height: 100%;}
|
||||
body { font-family: Helvetica,'微软雅黑';background:#03a9f4
|
||||
/*url(../i/bg.jpg)*/; font-size: 14px; color: #313131; }
|
||||
h1, h2, h3, h4, h5 {margin:0;padding:0;}
|
||||
em { font-style: normal; }
|
||||
ul, ol, li { list-style: none }
|
||||
img { border: 0 none }
|
||||
a { text-decoration: none; color: #858585; }
|
||||
.fl{float:left;}
|
||||
.fr{float:right;}
|
||||
/*layut css*/
|
||||
.wrap{width:860px; margin:0 auto; overflow:hidden;zoom:1;}
|
||||
.right{width:527px; float:right; background:#eeeeed; overflow:hidden;}
|
||||
.title{height:50px; background:#8bc34a; line-height:3.0em;color:#fff;font-size:16px;padding-left:25px;}
|
||||
.jdt{ background:#bebebe; height:3px; position:relative;}
|
||||
.jdt p{ position:absolute;left:0;top:0; height:3px; }
|
||||
.jdt p.orange{background:#8bc34a;}
|
||||
.jdt p.gray{background:#999;}
|
||||
|
||||
.first{background:#8bc34a;}
|
||||
.right dl{ overflow:hidden;zoom:1;}
|
||||
.grybg{background:#eaeae9;}
|
||||
.first dt,.first dd{ background:url(../i/wifi_d_bg.png) no-repeat right top;_background:none;_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src="wifi_d_bg.png")}
|
||||
.right dt,.first dt{width:255px;padding-left:25px;}
|
||||
.right dd,.first dd{width:110px; text-align:center;}
|
||||
.right dt,.right dd{color:#555;float:left; line-height:2.8em;font-size:16px;}
|
||||
.right dd.orange{color:#8bc34a; cursor:pointer}
|
||||
.right dd.gray{color:#aaa;}
|
||||
|
||||
.scroll{height:900px; overflow-y:scroll;padding-bottom:10px;position:relative;z-index:9999;}
|
||||
.left{margin-right:527px;}
|
||||
.logo{height:112px; background:url(../i/wifi_logo_3.png) no-repeat left 24px;margin-bottom:36px;}
|
||||
.wf_box{height:204px;margin-bottom:46px; overflow:hidden;}
|
||||
.wf_box h3{padding:124px 0 0 0;color:#fff;width:200px; height:76px; margin-left:55px; text-align:center;}
|
||||
.wf_wifi{ background:url(../i/wifi_img.png) no-repeat 47px top;}
|
||||
.wf_active,.wf_normal{background:url(../i/wifi_tz0.png) no-repeat;}
|
||||
.wf_active{background-position:56px -210px;}
|
||||
.wf_normal{background-position:56px top;}
|
||||
.wf_btn{width:203px;height:100px;padding-left:37px;margin-bottom:16px;}
|
||||
.wf_left{width:6px; height:100px; background:url(../i/wifi_left.png) no-repeat left top; float:left;}
|
||||
.wf_right{width:6px; height:100px; background:url(../i/wifi_right.png) no-repeat left top; float:left;}
|
||||
.wf_center{width:228px; float:left; background:#8bc34a; text-align:center;color:#fff;height:100px;}
|
||||
.wf_center h3{font-size:24px; line-height:1.6em;margin-top:22px;}
|
||||
.wf_center p{font-size:14px; line-height:1.6em;}
|
||||
.wf_gs{color:#fff; line-height:1.6em;padding-left:37px;}
|
||||
.right dd.wf_bgn{ background:none;}
|
||||
|
After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="108dp" height="108dp" viewBox="0 0 108 108">
|
||||
<path d="M0,0h108v108h-108z" fill="#FFFFFF" />
|
||||
<path
|
||||
d="M55.659,79.068c-1.258,0 -2.521,-0.094 -3.753,-0.279c-0.773,-0.115 -1.307,-0.838 -1.19,-1.611s0.841,-1.311 1.612,-1.191c1.093,0.164 2.214,0.248 3.331,0.248c12.261,0 22.235,-9.975 22.235,-22.235c0,-12.26 -9.975,-22.234 -22.235,-22.234c-10.531,0 -19.695,7.479 -21.79,17.781c-0.155,0.768 -0.902,1.262 -1.671,1.107c-0.767,-0.156 -1.263,-0.904 -1.106,-1.672c2.361,-11.618 12.693,-20.051 24.567,-20.051c13.823,0 25.069,11.246 25.069,25.068C80.729,67.822 69.482,79.068 55.659,79.068z"
|
||||
fill="#568FCC" />
|
||||
<path
|
||||
d="M30.986,53.461c0,0 -3.656,-1.06 -3.656,1.636c0,2.693 0,15.295 0,15.295s-0.674,2.598 2.598,3.369c3.271,0.77 7.697,2.404 7.697,2.404s3.464,0.387 5.58,-0.479c2.116,-0.867 7.505,-2.215 7.505,-2.215s2.213,-0.383 2.213,-3.656c0,-3.271 0,-14.24 0,-14.24s-0.771,-3.366 -4.233,-1.922c-3.464,1.444 -5.869,2.118 -5.869,2.118s-4.33,0.48 -6.157,-0.385C34.834,54.519 30.986,53.461 30.986,53.461z"
|
||||
fill="#568FCC" />
|
||||
<path d="M53.445,48.757m-3.5,0a3.5,3.5 0,1 1,7 0a3.5,3.5 0,1 1,-7 0" fill="#568FCC" />
|
||||
<path d="M66.861,48.757m-3.5,0a3.5,3.5 0,1 1,7 0a3.5,3.5 0,1 1,-7 0" fill="#568FCC" />
|
||||
<path
|
||||
d="M39.758,74.008c-0.783,0 -1.418,-0.635 -1.418,-1.418v-12.5c0,-0.783 0.635,-1.416 1.418,-1.416s1.418,0.633 1.418,1.416v12.5C41.176,73.373 40.541,74.008 39.758,74.008z"
|
||||
fill="#FFFFFF" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
<link href="css/stylewf.css" type="text/css" rel="stylesheet"/>
|
||||
<title data-js="t0"></title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<div class="right">
|
||||
<p class="title" data-js="t1"></p>
|
||||
<dl class="first">
|
||||
<dt data-js="t2"></dt>
|
||||
<dd data-js="t3"></dd>
|
||||
<dd class="wf_bgn" data-js="t4"></dd>
|
||||
</dl>
|
||||
|
||||
<div class="scroll">
|
||||
<ul class="" id="tableStyle"></ul>
|
||||
</div><!--scroll end-->
|
||||
</div><!--right end-->
|
||||
<div class="left">
|
||||
<div class="logo"></div>
|
||||
<div id="drag_area" class="wf_box">
|
||||
<h3 data-js="t6"></h3>
|
||||
</div><!--wf_bg1和wf_bg2两个背景样式还有wf_on样式拖拽时候的状态-->
|
||||
<div class="wf_btn">
|
||||
<div id="spanButtonPlaceHolder"></div>
|
||||
</div>
|
||||
<p class="wf_gs" data-js="t5"></p>
|
||||
</div><!--left end -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<script src="js/langSwich.js"></script>
|
||||
<script src="js/swfupload.js"></script>
|
||||
<script src="js/common.js"></script>
|
||||
<script src="js/swf_fun.js"></script>
|
||||
<script src="js/html5_fun.js"></script>
|
||||
@@ -0,0 +1,236 @@
|
||||
/**
|
||||
* 公共函数
|
||||
*/
|
||||
|
||||
//全局的配置文件
|
||||
var config = {
|
||||
fileTypes: "txt|epub|umd", //允许上传的文件格式 "txt|epub" // |doc|docx|wps|xls|xlsx|et|ppt|pptx|dps
|
||||
//url : "http://"+location.host+"?action=addBook",//"http://localhost/t/post.php",//
|
||||
url: "../addLocalBook",
|
||||
fileLimitSize: 500 * 1024 * 1024,
|
||||
};
|
||||
|
||||
var file = {
|
||||
inQueue: [], //已经在队列里面的文件,包括 HTML5上传和 Flash上传的
|
||||
clientHaveFiles: [], // 客户端已经存在的文件列表
|
||||
};
|
||||
|
||||
/**
|
||||
* HTML5 和 flash 公用,所有文件对象集合
|
||||
* @var array
|
||||
*/
|
||||
var filesUpload = []; //
|
||||
|
||||
//统计文件大小
|
||||
function countFileSize(fileSize) {
|
||||
var KB = 1024;
|
||||
var MB = 1024 * 1024;
|
||||
if (KB >= fileSize) {
|
||||
return fileSize + "B";
|
||||
} else if (MB >= fileSize) {
|
||||
return (fileSize / KB).toFixed(2) + "KB";
|
||||
} else {
|
||||
return (fileSize / MB).toFixed(2) + "MB";
|
||||
}
|
||||
}
|
||||
|
||||
//如果文件太长进行截取
|
||||
function substr_string(name) {
|
||||
var maxLen = 15;
|
||||
var len = name.length;
|
||||
if (len < 17) return name;
|
||||
|
||||
var lastIndex = name.lastIndexOf(".");
|
||||
var suffix = name.substr(lastIndex);
|
||||
var pre = name.substr(0, lastIndex);
|
||||
var preLen = pre.length;
|
||||
var preStart = preLen - 10;
|
||||
//前面10个 + 后面5个
|
||||
var fileName =
|
||||
pre.substr(0, 10) +
|
||||
"...." +
|
||||
pre.substr(preStart > 4 ? -4 : -preStart, 4) +
|
||||
suffix;
|
||||
return fileName;
|
||||
}
|
||||
|
||||
function checkFile(file) {
|
||||
if (file.size > config.fileLimitSize) {
|
||||
return jsonLang.t11;
|
||||
}
|
||||
|
||||
if (
|
||||
!file.name ||
|
||||
!file.name.toLowerCase().match("(" + config.fileTypes + ")$")
|
||||
) {
|
||||
return jsonLang.t12;
|
||||
}
|
||||
|
||||
var len = filesUpload.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (filesUpload[i].name == file.name) {
|
||||
return jsonLang.t13;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加文件时,回调的函数
|
||||
* @param object file 文件对象
|
||||
* @param int type 0 是swf 上传的,1 是html5上传的
|
||||
*/
|
||||
function fileQueued(file, type) {
|
||||
var size = 0,
|
||||
fid = file.id,
|
||||
name = "";
|
||||
type = type || 0;
|
||||
|
||||
if (file != undefined) {
|
||||
//计算文件大小 单位MB
|
||||
size = countFileSize(file.size);
|
||||
name = substr_string(file.name);
|
||||
//创建要插入的元素
|
||||
var li = document.createElement("li");
|
||||
li.id = `tr_${fid}`;
|
||||
li.innerHTML = `<dl class="grybg">
|
||||
<dt>${name}</dt>
|
||||
<dd>${size}</dd>
|
||||
<dd id="handle_button_${fid}" onclick=userCancelUpload("${fid}",${type}) class="orange">
|
||||
<span id="progress_bar_span_${fid}">0%</span> ${jsonLang.t9}
|
||||
</dd>
|
||||
</dl>
|
||||
<div class="jdt"><p id="progress_bar_p_${fid}" ></p></div>`;
|
||||
var table = document.getElementById("tableStyle");
|
||||
table.appendChild(li);
|
||||
//保存falsh_id,为上传做准备
|
||||
//global_flash_id.push(file.id);
|
||||
//更改背景颜色
|
||||
changeTrBackGroundColor();
|
||||
}
|
||||
}
|
||||
|
||||
function changeTrBackGroundColor() {
|
||||
var getTr = document.getElementById("tableStyle").getElementsByTagName("dl");
|
||||
trNum = getTr.length;
|
||||
for (var i = 0; i < trNum; i++) {
|
||||
if (i % 2 == 0) {
|
||||
getTr[i].style.backgroundColor = "#f3f3f3";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//上传时返回的状态
|
||||
function uploadProgress(file, bytesLoaded, bytesTotal) {
|
||||
var progressClass = document.getElementById(
|
||||
"progress_bar_p_" + file.id
|
||||
).classList;
|
||||
if (!progressClass.contains("orange")) {
|
||||
progressClass.add("orange");
|
||||
}
|
||||
document.getElementById("progress_bar_p_" + file.id).style.width =
|
||||
(bytesLoaded / bytesTotal) * 100 + "%";
|
||||
document.getElementById("progress_bar_span_" + file.id).innerHTML =
|
||||
parseInt((bytesLoaded / bytesTotal) * 100) + "%";
|
||||
}
|
||||
|
||||
//上传成功
|
||||
function uploadSuccess(file, serverData, res) {
|
||||
var id = "handle_button_" + file.id;
|
||||
var dd = document.createElement("dd");
|
||||
dd.innerHTML = jsonLang.t10;
|
||||
document.getElementById(id).replaceWith(dd);
|
||||
}
|
||||
|
||||
//取消上传
|
||||
function userCancelUpload(file_id, type) {
|
||||
if (type == 0) {
|
||||
SWFFuns.cancelUpload(file_id);
|
||||
} else {
|
||||
HTML5Funs.cancelUpload(file_id);
|
||||
}
|
||||
var element = document.getElementById("handle_button_" + file_id);
|
||||
element.innerHTML = jsonLang.t14;
|
||||
element.classList.remove("orange");
|
||||
element.classList.add("gray");
|
||||
//如果已经上传一部分了
|
||||
var progressElement = document.getElementById("progress_bar_p_" + file_id);
|
||||
if (progressElement.classList.contains("orange")) {
|
||||
progressElement.classList.remove("orange");
|
||||
progressElement.classList.add("gray");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过文件名称 从全局的文件列表中获取文件对象
|
||||
*
|
||||
*/
|
||||
function getFileFomeFilesUpload(filename) {
|
||||
var len = filesUpload.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (filesUpload[i].name == filename) {
|
||||
return filesUpload[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 往全局的 上传列表添加一个数据
|
||||
*/
|
||||
function addFileToFilesUpload(file) {
|
||||
if (typeof file == "string") {
|
||||
filesUpload.push({ name: file });
|
||||
return true;
|
||||
} else if (typeof file == "object") {
|
||||
filesUpload.push(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 往全局的 上传列表添加一个数据
|
||||
*/
|
||||
function updateFileToFilesUpload(file) {
|
||||
var len = filesUpload.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (filesUpload[i].name == file.name) {
|
||||
filesUpload[i] = file;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找在数组中的位置
|
||||
*/
|
||||
function findObjectKey(object, fid) {
|
||||
var len = object.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (object[i].id == fid) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从全局的文件集合中移除文件,一般上传失败时使用
|
||||
* @param array files 文件对象集合 [{},{},{}]
|
||||
* @param int fid 要删除的文件id
|
||||
* @return 删除后的数组, 其实数组是引用类型可以不返回
|
||||
*/
|
||||
function removeFileFromFilesUpload(files, fid) {
|
||||
var filesUploadKey = -1;
|
||||
|
||||
filesUploadKey = findObjectKey(files, fid);
|
||||
//从全局文件中移除
|
||||
if (filesUploadKey > -1) files.splice(filesUploadKey, 1);
|
||||
|
||||
return files;
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* 处理拖拽上传
|
||||
*/
|
||||
var isDragOver = false; //拖拽触发点
|
||||
var fileNumber = -1; //上传文件编号
|
||||
var fileNumberPex = "zyFileUpload_"; //编号前缀
|
||||
var currUploadfile = {}; //当前上传的文件对象
|
||||
|
||||
var uploadQueue = []; //上传队列集合
|
||||
var isUploading = false; //是否正在上传
|
||||
|
||||
var XHR;
|
||||
try {
|
||||
XHR = new XMLHttpRequest();
|
||||
} catch (e) {}
|
||||
|
||||
(function (isSupportFileUpload) {
|
||||
//不支持拖拽上传,或者 不支持FormData ,显示WiFi表示
|
||||
if (!isSupportFileUpload) {
|
||||
document.getElementById("drag_area").classList.add("wf_wifi");
|
||||
document.getElementById("drag_area").innerHTML = "";
|
||||
return;
|
||||
//更换样式
|
||||
} else {
|
||||
document.getElementById("drag_area").classList.add("wf_normal");
|
||||
}
|
||||
|
||||
addEvent();
|
||||
|
||||
/**
|
||||
* 添加事件
|
||||
*/
|
||||
function addEvent() {
|
||||
var dropArea = document.getElementById("drag_area");
|
||||
dropArea.addEventListener("dragover", handleDragOver, false);
|
||||
dropArea.addEventListener("dragleave", handleDragLeave, false);
|
||||
dropArea.addEventListener("drop", handleDrop, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 松开拖拽文件的处理,进行上传
|
||||
*/
|
||||
function handleDrop(evt) {
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
var classList = document.getElementById("drag_area").classList;
|
||||
classList.add("wf_normal");
|
||||
classList.remove("wf_active");
|
||||
|
||||
console.log("Drop");
|
||||
isDragOver = false;
|
||||
|
||||
var file = {};
|
||||
var errorMsgs = [];
|
||||
var len = evt.dataTransfer.files.length;
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
fileNumber++;
|
||||
file = evt.dataTransfer.files[i];
|
||||
file.id = fileNumberPex + fileNumber;
|
||||
|
||||
//检测文件
|
||||
msg = checkFile(file);
|
||||
//文件可以通过
|
||||
if (!msg) {
|
||||
//添加全局
|
||||
filesUpload.push(file);
|
||||
//添加上传队列
|
||||
uploadQueue.push(file);
|
||||
//在页面进行展示
|
||||
fileQueued(file, 1);
|
||||
} else {
|
||||
errorMsgs.push(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (errorMsgs.length > 0) {
|
||||
//只选择做一个进行上传
|
||||
if (len == 1) {
|
||||
alert(errorMsgs[0]);
|
||||
} else {
|
||||
a1 = len;
|
||||
a2 = len - errorMsgs.length;
|
||||
|
||||
var replaceArr = new Array(a1, a2);
|
||||
alert(stringReplace(jsonLang.t7, replaceArr));
|
||||
}
|
||||
}
|
||||
|
||||
//拿出第一个,进行上传
|
||||
if (!isUploading && uploadQueue.length > 0)
|
||||
uploadFiles(uploadQueue.shift());
|
||||
}
|
||||
|
||||
function handleDragOver(evt) {
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
//防止多次DOM操作
|
||||
if (!isDragOver) {
|
||||
console.log("Over");
|
||||
var classList = document.getElementById("drag_area").classList;
|
||||
classList.add("wf_active");
|
||||
classList.remove("wf_normal");
|
||||
isDragOver = true;
|
||||
}
|
||||
}
|
||||
|
||||
function handleDragLeave(evt) {
|
||||
console.log("DragLeave");
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
isDragOver = false;
|
||||
var classList = document.getElementById("drag_area").classList;
|
||||
classList.add("wf_normal");
|
||||
classList.remove("wf_active");
|
||||
}
|
||||
|
||||
function uploadFiles(file) {
|
||||
//正在上传
|
||||
isUploading = true;
|
||||
//设置上传的数据
|
||||
var reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = function (e) {
|
||||
var data = e.target.result;
|
||||
var fd = new FormData();
|
||||
fd.append("fileName", file.name);
|
||||
fd.append("fileData", data);
|
||||
//设置当前的上传对象
|
||||
currUploadfile = file;
|
||||
|
||||
if (XHR.readyState > 0) {
|
||||
XHR = new XMLHttpRequest();
|
||||
}
|
||||
|
||||
XHR.upload.addEventListener("progress", progress, false);
|
||||
XHR.upload.addEventListener("load", requestLoad, false);
|
||||
XHR.upload.addEventListener("error", error, false);
|
||||
XHR.upload.addEventListener("abort", abort, false);
|
||||
XHR.upload.addEventListener("loadend", loadend, false);
|
||||
XHR.upload.addEventListener("loadstart", loadstart, false);
|
||||
XHR.open("POST", config.url);
|
||||
XHR.send(fd);
|
||||
XHR.onreadystatechange = function () {
|
||||
//只要上传完成不管成功失败
|
||||
if (XHR.readyState == 4) {
|
||||
console.log("onreadystatechange ", XHR.status, +new Date());
|
||||
|
||||
if (XHR.status == 200) {
|
||||
uploadSuccess(currUploadfile, {}, XHR.status);
|
||||
} else {
|
||||
uploadError();
|
||||
}
|
||||
|
||||
//进行下一个上传
|
||||
nextUpload();
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
//请求完成,无论失败或成功
|
||||
function loadend(evt) {
|
||||
console.log("loadend", +new Date(), evt);
|
||||
}
|
||||
//请求开始
|
||||
function loadstart(evt) {
|
||||
console.log("loadstart", evt);
|
||||
}
|
||||
|
||||
//在请求发送或接收数据期间,在服务器指定的时间间隔触发。
|
||||
function progress(evt) {
|
||||
uploadProgress(currUploadfile, evt.loaded || evt.position, evt.total);
|
||||
}
|
||||
|
||||
//在请求被取消时触发,例如,在调用 abort() 方法时。
|
||||
function abort(evt) {
|
||||
console.log("abort", evt);
|
||||
}
|
||||
|
||||
//在请求失败时触发。
|
||||
function error(evt) {
|
||||
//终止ajax请求
|
||||
XHR.abort();
|
||||
uploadError();
|
||||
nextUpload();
|
||||
}
|
||||
|
||||
//在请求成功完成时触发。
|
||||
function requestLoad(evt) {
|
||||
console.log("requestLoad", +new Date(), evt);
|
||||
}
|
||||
|
||||
//进行下一个上传
|
||||
function nextUpload() {
|
||||
isUploading = false;
|
||||
if (uploadQueue.length > 0) {
|
||||
uploadFiles(uploadQueue.shift());
|
||||
} else {
|
||||
//米有正在上传的了
|
||||
currUploadfile = {};
|
||||
}
|
||||
}
|
||||
|
||||
//上传出错误了,比如断网,
|
||||
function uploadError() {
|
||||
//移除全局变量中的,上传出错的
|
||||
removeFileFromFilesUpload(filesUpload, currUploadfile.id);
|
||||
var file = currUploadfile;
|
||||
var dd = document.createElement("dd")
|
||||
dd.innerHTML = jsonLang.t8
|
||||
document
|
||||
.getElementById("handle_button_" + file.id)
|
||||
.replaceWith(dd);
|
||||
}
|
||||
|
||||
//对外部注册的函数
|
||||
var HTML5Funs = {
|
||||
/**
|
||||
* 取消上传
|
||||
* @param string fid 文件的Id
|
||||
*/
|
||||
cancelUpload: function (fid) {
|
||||
var filesUploadKey = -1;
|
||||
var uploadQueueKey = -1;
|
||||
|
||||
//从全局中删除文件
|
||||
removeFileFromFilesUpload(filesUpload, fid);
|
||||
|
||||
//如果是正在上传的,AJAX取消
|
||||
if (currUploadfile.id == fid) {
|
||||
XHR.abort();
|
||||
} else {
|
||||
//从上传队列中移除
|
||||
removeFileFromFilesUpload(uploadQueue, fid);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
window.HTML5Funs = HTML5Funs;
|
||||
})("FormData" in window && "ondrop" in document.body);
|
||||
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* wifi传书功能,供客户端调用
|
||||
* 切换语言环境 addby zhongweikang@zhangyue.com 2015/09/23
|
||||
*/
|
||||
|
||||
var language; //当前语言
|
||||
var jsonEn; //英文
|
||||
var jsonZh; //中文
|
||||
|
||||
/**
|
||||
* 语言包
|
||||
*/
|
||||
jsonEn = {
|
||||
t0: "WiFi Upload",
|
||||
t1: "Documents",
|
||||
t2: "Name",
|
||||
t3: "Size",
|
||||
t4: "Operation",
|
||||
t5: "Supported Format:TXT、EPUB、UMD",
|
||||
t6: "Drag here and Drop to upload",
|
||||
t7: "You choosed [txt] files ,we only can upload [txt] of them。\nPlease choose TXT、EPUB、UMD files, file name cannot be repeated.",
|
||||
t8: "Failed",
|
||||
t9: "Cancel",
|
||||
t10: "Done",
|
||||
t11: "One of books is over 500MB",
|
||||
t12: "Bad file mode",
|
||||
t13: "File already exists",
|
||||
t14: "Canceled",
|
||||
};
|
||||
jsonZh = {
|
||||
t0: "传书",
|
||||
t1: "文档",
|
||||
t2: "名字",
|
||||
t3: "大小",
|
||||
t4: "操作",
|
||||
t5: "支持类型:TXT、EPUB、UMD",
|
||||
t6: "拖至此处上传",
|
||||
t7: "你选择了[txt]个文件,我们只能上传其中的[txt]。\n请选择 TXT、EPUB、UMD 类型的文件,文件的名字不能重复。",
|
||||
t8: "失败",
|
||||
t9: "取消",
|
||||
t10: "完成",
|
||||
t11: "上传文件不能大于500MB",
|
||||
t12: "无效的文件格式",
|
||||
t13: "文件已存在",
|
||||
t14: "已取消",
|
||||
};
|
||||
|
||||
if (navigator.language) {
|
||||
language = navigator.language;
|
||||
} else {
|
||||
language = navigator.browserLanguage;
|
||||
}
|
||||
language = language.toLowerCase(language);
|
||||
|
||||
/**
|
||||
* 切换语言
|
||||
*/
|
||||
function langSwich(s) {
|
||||
switch (s) {
|
||||
case "en-us":
|
||||
_strReplace(jsonEn);
|
||||
break;
|
||||
case "zh-cn":
|
||||
_strReplace(jsonZh);
|
||||
break;
|
||||
default:
|
||||
_strReplace(jsonEn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
function _strReplace(d) {
|
||||
window.jsonLang = d; //全局变量在其他js文件中会遇到,勿删。
|
||||
for (var i in jsonLang) {
|
||||
var element = document.querySelector("[data-js=" + i + "]");
|
||||
if (element) {
|
||||
element.innerHTML = d[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 特殊字符串替换,例:语言包中的 t7
|
||||
* content 替换前的文本
|
||||
* replace 待插入的文本 (支持变量或数组)
|
||||
*/
|
||||
var isArray = function (obj) {
|
||||
return Object.prototype.toString.call(obj) === "[object Array]";
|
||||
};
|
||||
function stringReplace(content, replace) {
|
||||
var str = content;
|
||||
if (!str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isArray(replace)) {
|
||||
strs = str.split("[txt]");
|
||||
count = strs.length - 1;
|
||||
|
||||
var string = "";
|
||||
for (var i = 0; i < count; i++) {
|
||||
string = string + strs[i] + replace[i];
|
||||
}
|
||||
string = string + strs[count];
|
||||
} else {
|
||||
string = str.replace(replace, "[txt]");
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
//执行
|
||||
langSwich(language);
|
||||
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* swf 上传
|
||||
*/
|
||||
var swfu;//swfupload 对象
|
||||
var swfSelectCount = 0;// 当前选中的文件数量
|
||||
|
||||
window.onload = function () {
|
||||
var settings = {
|
||||
upload_url: config.url,
|
||||
/*post_params: {"PHPSESSID" : "<?php echo session_id(); ?>"},*/
|
||||
file_size_limit: config.fileLimitSize + " B",
|
||||
file_types: "*." + config.fileTypes.split("|").join(";*."),
|
||||
|
||||
file_types_description: "All Files",
|
||||
file_upload_limit: 1000, //配置上传个数
|
||||
file_queue_limit: 0,
|
||||
custom_settings: {
|
||||
progressTarget: "fsUploadProgress",
|
||||
cancelButtonId: "btnCancel"
|
||||
},
|
||||
debug: 0,
|
||||
|
||||
button_cursor: SWFUpload.CURSOR.HAND,
|
||||
button_image_url: "i/wifi_btn_b.png",
|
||||
button_width: "240",
|
||||
button_height: "100",
|
||||
button_float: "right",
|
||||
button_placeholder_id: "spanButtonPlaceHolder",
|
||||
button_text: '<span class="theFont"></span>',
|
||||
|
||||
assume_success_timeout: 30,
|
||||
file_queued_handler: swfFileQueued,
|
||||
file_queue_error_handler: fileQueueError,
|
||||
file_dialog_complete_handler: fileDialogComplete,
|
||||
upload_start_handler: uploadStart,
|
||||
upload_progress_handler: uploadProgress,
|
||||
upload_error_handler: uploadError,
|
||||
upload_success_handler: uploadSuccess,
|
||||
upload_complete_handler: uploadComplete,
|
||||
queue_complete_handler: queueComplete
|
||||
};
|
||||
swfu = new SWFUpload(settings);
|
||||
};
|
||||
|
||||
|
||||
|
||||
//上传完成
|
||||
function uploadComplete(file, server, response) {
|
||||
//继续下一个文件的上传
|
||||
this.startUpload();
|
||||
}
|
||||
|
||||
//完成队列里的上传
|
||||
function queueComplete(numFilesUploaded) {
|
||||
|
||||
}
|
||||
|
||||
function userStartUpload(file_id) {
|
||||
swfu.startUpload(file_id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function fileQueueError(file, errorCode, message) {
|
||||
switch (errorCode) {
|
||||
case -100:
|
||||
//alert("Over 100 books");
|
||||
case -110:
|
||||
//alert("One of books is over 500MB");
|
||||
break;
|
||||
case -120:
|
||||
//alert("One of books is 0KB");
|
||||
break;
|
||||
}
|
||||
}
|
||||
//入列完毕
|
||||
function fileDialogComplete(numFilesSelected, numFilesQueued) {
|
||||
if (numFilesSelected > 0) {
|
||||
this.startUpload()
|
||||
}
|
||||
}
|
||||
//开始上传
|
||||
function uploadStart(file) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//上传出错
|
||||
function uploadError(file, errorCode, message) {
|
||||
switch (errorCode) {
|
||||
case SWFUpload.UPLOAD_ERROR.HTTP_ERROR:
|
||||
errorMessage = "Error";
|
||||
break;
|
||||
case SWFUpload.UPLOAD_ERROR.UPLOAD_FAILED:
|
||||
errorMessage = "Failed";
|
||||
break;
|
||||
case SWFUpload.UPLOAD_ERROR.IO_ERROR:
|
||||
errorMessage = "Please open wifi upload page";
|
||||
break;
|
||||
case SWFUpload.UPLOAD_ERROR.SECURITY_ERROR:
|
||||
errorMessage = "Security error";
|
||||
break;
|
||||
case SWFUpload.UPLOAD_ERROR.UPLOAD_LIMIT_EXCEEDED:
|
||||
errorMessage = "Security error";
|
||||
break;
|
||||
case SWFUpload.UPLOAD_ERROR.FILE_VALIDATION_FAILED:
|
||||
errorMessage = "Unable to verify. Skip ";
|
||||
break;
|
||||
default:
|
||||
errorMessage = "Unhandled error";
|
||||
break;
|
||||
}
|
||||
|
||||
//从上传队列中移除
|
||||
removeFileFromFilesUpload(filesUpload, file.id)
|
||||
|
||||
errorMessage = jsonLang.t8;
|
||||
var dd = document.createElement("dd")
|
||||
dd.innerHTML = errorMessage
|
||||
document.getElementById("handle_button_" + file.id).replaceWith(dd)
|
||||
}
|
||||
|
||||
|
||||
var tmp = 0;
|
||||
var errorFile = 0;
|
||||
var errorMsgs = [];
|
||||
|
||||
function swfFileQueued(file) {
|
||||
|
||||
|
||||
//本次上传选中的文件个数
|
||||
if (swfSelectCount == 0) swfSelectCount = this.getStats().files_queued
|
||||
//检测文件
|
||||
msg = checkFile(file)
|
||||
|
||||
|
||||
//文件可以通过
|
||||
if (!msg) {
|
||||
//添加全局的队列
|
||||
filesUpload.push(file);
|
||||
//在页面进行展示
|
||||
fileQueued(file, 0)
|
||||
} else {
|
||||
//从上传队列移除,验证失败的文件
|
||||
this.cancelUpload(file.id, false)
|
||||
errorMsgs.push(msg)
|
||||
}
|
||||
|
||||
|
||||
|
||||
//队列选择完毕,初始化所有的数据
|
||||
if (++tmp == swfSelectCount) {
|
||||
|
||||
if (errorMsgs.length > 0) {
|
||||
//只选择做一个进行上传
|
||||
if (swfSelectCount == 1) {
|
||||
alert(errorMsgs[0]);
|
||||
} else {
|
||||
a1 = swfSelectCount;
|
||||
a2 = swfSelectCount - errorMsgs.length;
|
||||
|
||||
var replaceArr = new Array(a1, a2);
|
||||
alert(stringReplace(jsonLang.t7, replaceArr));
|
||||
}
|
||||
}
|
||||
|
||||
tmp = 0;
|
||||
errorFile = 0;
|
||||
swfSelectCount = 0;
|
||||
errorMsgs = []
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
var SWFFuns = {
|
||||
cancelUpload: function (fid) {
|
||||
swfu.cancelUpload(fid, false);
|
||||
}
|
||||
|
||||
}
|
||||