-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathaerfaying-explore.user.js
2638 lines (2548 loc) · 123 KB
/
aerfaying-explore.user.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// ==UserScript==
// @name Aerfaying Explore - 阿儿法营/稽木世界社区优化插件
// @namespace waterblock79.github.io
// @version 1.20.0
// @description 提供优化、补丁及小功能提升社区内的探索效率和用户体验
// @author waterblock79
// @match http://gitblock.cn/*
// @match https://gitblock.cn/*
// @match http://aerfaying.com/*
// @match https://aerfaying.com/*
// @icon https://gitblock.cn/Content/logo.ico
// @grant GM_setValue
// @grant GM_getValue
// @grant unsafeWindow
// @license MIT
// ==/UserScript==
/*
aerfaying-explore 是一个非官方的、针对阿儿法营/稽木世界社区的开源优化插件
https://github.com/waterblock79/aerfaying-explore
*/
/*
Purge jsDeliver CDN cache: https://www.jsdelivr.com/tools/purge
- https://cdn.jsdelivr.net/gh/waterblock79/aerfaying-explore/aerfaying-explore.user.js
*/
(function () {
'use strict';
// 初始化信息
try {
var window = unsafeWindow || window;
} catch (e) { }
if (!window) {
try {
var window = self;
} catch (e) {
alert('似乎无法在您的浏览器上运行此脚本。')
}
}
const version = '1.20.0';
const DEFAULT_MAIN_COLOR = '#4c97ff',
DEFAULT_SECOND_COLOR = '#82d900';
if (location.search === '?NoUserscript') return;
window.aerfayingExplore = {};
// 判断 GM_setValue、GM_getValue 是否可用(貌似不存在的话,获取就报错,不能像 foo == undefined 那样获取它是否存在)
try {
if (GM_getValue && GM_setValue) {
window.GMAvailable = true;
} else {
window.GMAvailable = false;
}
} catch (e) {
window.GMAvailable = false;
}
// $(selector)
// 即 document.querySelectorAll(selector)
/**
* @param {string} selector
* @returns {HTMLElement[]}
*/
const $ = (selector) => document.querySelectorAll(selector);
// addSelectorEvent(selector, event, callback)
// 为全部符合 selector 选择器的元素自动添加 event 事件,若该事件被触发就会执行 callback 回调
let eventElement = [];
const addSelectorEvent = (selector, event, callback) => {
if (Array.isArray(event)) {
for (let i in event) {
addSelectorEvent(selector, event[i], callback);
}
return;
}
eventElement.push({
selector: selector,
event: event,
callback: callback,
handledElements: []
})
}
window.addSelectorEvent = addSelectorEvent;
// addFindElement(selector, callback)
// 当选择器发现新的符合 selector 的元素就执行 callback,callback 会传入该元素。
let findElement = [];
const addFindElement = (selector, callback) => {
findElement.push({
selector: selector,
callback: callback,
handledElements: []
})
// 此处返回该任务在 findElement 中的 index,方便后续删除该任务。
return findElement.length - 1;
};
window.addFindElement = addFindElement;
// addHrefChangeEvent(callback)
// 当页面 location.href 改变触发该事件
let lastHref = null;
let hrefChangeEvent = [];
const addHrefChangeEvent = (callback) => {
hrefChangeEvent.push({
callback: callback,
});
};
// →_→
// 通过 setInterval 实现 addFindElement 和 addSelectorEvent。
setInterval(() => {
// addFindElement
findElement.forEach((item) => {
$(item.selector)?.forEach((element) => {
if (!item.handledElements.find(e => e == element)) {
item.handledElements.push(element);
(async () => { item.callback(element) })();
}
})
})
// addSelectorEvent
eventElement.forEach((item) => {
$(item.selector)?.forEach((element) => {
if (!item.handledElements.find(e => e == element)) {
element.addEventListener(item.event, item.callback);
item.handledElements.push(element);
}
})
});
// addHrefChangeEvent
if (lastHref != location.href) {
hrefChangeEvent.forEach((item) => {
(async () => { item.callback(location.href) })();
});
}
lastHref = location.href;
}, 16);
// addStyle(css)
// 将 CSS 塞到 <style> 标签里然后添加到页面中
const addStyle = (css) => {
const style = document.createElement('style');
style.innerHTML = css;
document.head.appendChild(style);
};
// insertBefore(newElement, targetElement)
// 把 newElement 插入到 targetElement 前面
const insertBefore = (newElement, targetElement) => {
targetElement.parentNode.insertBefore(newElement, targetElement);
};
// encodeHTML(str)
// 转义字符串中的 HTML 字符
const encodeHTML = (str) => {
let div = document.createElement('div');
div.innerText = str;
return div.innerHTML;
};
// 监听请求(这里用的是 jQuery 的 $)
window.$(document).ajaxSuccess(function (event, xhr, settings, response) {
if (settings.url.search(/WebApi\/Projects\/[0-9]+\/Get/) == 1) { // /WebApi/Projects/*/Get 获取作品信息
projectThumbId = response?.project?.thumbId; // 在变量里保存获取到的作品封面
}
if (settings.url == '/WebApi/Comment/GetPage') { // /WebApi/Comment/GetPage 评论
response.replies.forEach((comment) => {
commentData[comment.id] = comment;
});
response.pagedThreads.items.forEach((comment) => {
commentData[comment.id] = comment;
});
}
if (settings.url == '/WebApi/Comment/Post') { // 匹配用户发送评论 /WebApi/Comment/Post
commentData[response.comment.id] = response.comment;
}
});
// 自动 HTTPS
if (localStorage['explore:https'] == 'true') {
if (location.toString().startsWith("http://")) {
location = location.toString().replace("http://", "https://", 1);
}
}
// 替换原不可用的 asset.mozhua.org:444 的资源地址
addFindElement('img[src*="asset.mozhua.org:444"]', (element) => {
element.src = element.src.replace('https://asset.mozhua.org:444/Media?name=', 'https://cdn.gitblock.cn/Media?name=');
});
// 添加控制台的提示
if (window.top == window.self) {
console.log(
`%cAerfaying-Explore %c\n当前版本:${version}\n本插件开源于 Github:\nhttps://github.com/waterblock79/aerfaying-explore/`,
'font-size: 1.5em; color: dodgerblue;',
'font-size: 1em; color: black;'
);
}
// 插件设置
let settings = [{
tag: 'explore:loading',
text: '加载中所显示的提示设置',
select: [
'保持原状',
'在导航栏显示“加载中”的文字和动画(最小)',
'在左下角显示不影响浏览的加载中动画(经典)'
],
type: 'radio',
default: 1,
desp: `
<a target="_blank" href="/AboutLoading">如何选择?</a>
`
}, {
tag: 'explore:emoji',
text: '在评论时添加贴吧或 QQ 表情',
select: [
'关闭【表情】按钮',
'使用百度贴吧表情',
'使用 QQ 表情'
],
type: 'radio',
default: 1,
desp: `表情图像的版权属于百度贴吧或腾讯 QQ`
}, {
tag: 'explore:https',
text: '自动 HTTPS',
type: 'check',
default: true,
}, {
tag: 'explore:hoverId',
text: '仅当鼠标悬停在评论上时显示评论 ID',
type: 'check',
default: false,
}, {
tag: 'explore:noMaxHeight',
text: '禁用个人简介的最大高度限制',
type: 'check',
default: true,
}, {
tag: 'explore:lessRecommendProject',
text: '单行显示推荐的精华作品',
type: 'check',
default: false,
}, {
tag: 'explore:copyLink',
text: '鼠标悬停页面右下角时显示复制页面 Markdown 链接的按钮',
type: 'check',
default: false,
disabled: !navigator.clipboard
}, {
tag: 'explore:fullscreenDisableScroll',
text: '作品全屏时禁用鼠标滚轮滚动',
type: 'check',
default: true
}, {
tag: 'explore:previewReply',
text: '在消息页面预览回复的内容',
type: 'check',
default: false,
desp: '实验性功能,请谨慎使用'
}, {
tag: 'explore:previewCommentMarkdown',
text: '在发表评论时预览评论 Markdown',
type: 'check',
default: false,
}, {
tag: 'explore:localSearch',
text: '快速搜索',
type: 'check',
default: true,
desp: `自动在本地存储并索引访问过的页面,使用 Ctrl + K 快捷键可以呼出搜索栏并搜索这些页面。`
}, {
tag: 'explore:betterPriseAndBlame',
text: '优化评论赞踩显示机制',
type: 'check',
default: false,
desp: `在评论下方只显示点赞数减去点踩数的值,并且这个值小于等于 0 时不显示,类似 B 站评论,可以对“点踩侠”眼不见心不烦`
}, {
tag: 'explore:projectAssetLoad',
text: '稳定与优化作品资源加载',
type: 'check',
default: false,
desp: `自动重新加载加载失败的作品资源,并显示重新的加载进度(实验性功能)`
}, {
tag: 'explore:commentVisibilityPredict',
text: '预测并提示评论发出后的仅好友可见状态',
type: 'check',
default: true
}, {
tag: 'explore:betterHomepage',
text: '优化主页',
type: 'check',
default: false,
desp: '优化了主页的样式、提供了实用功能。'
}, {
tag: 'explore:modalBackgroundBlur',
text: '对话框背景虚化',
type: 'check',
default: true
}
];
// 欢迎
if (localStorage['explore:commentVisibilityPredict'] == undefined) {
let interval = setInterval(() => {
if ($('.footer').length) {
$('.footer')[0].style.display = 'none';
clearInterval(interval);
}
});
Blockey.Utils.confirm(`欢迎使用 Aerfaying-Explore`, `
<b>嗨${Blockey.Utils.getLoggedInUser() ? `,${Blockey.Utils.getLoggedInUser().username}` : ''}!欢迎使用 Aerfaying-Explore 插件!</b><br/>
- 当您看到这条消息时说明您已经成功地安装了这个插件,希望这个插件能有效地提升您的社区探索体验!<br/>
- 大部分拓展功能默认是关闭的,您可以在 <a onclick="$('#nav-explore-setting')[0].click()">插件设置</a> 中选择启用或关闭插件功能。<br/>
- 如果您有功能建议或者遇到了 Bug,欢迎在 <a href="https://github.com/waterblock79/aerfaying-explore">Github</a> 或 <a href="/Users/1068072">作者的主页</a> 反馈~
`);
}
// 设置默认值
settings.forEach((item) => {
if (!localStorage[item.tag]) {
localStorage[item.tag] = item.default;
}
});
// 创建设置摁钮
let settingsButton = document.createElement('li');
settingsButton.innerHTML = '<a id="nav-explore-setting"><span>插件设置</span></a>';
addStyle(`
.explore-settings-label {
display: inline-table;
font-size: 14px;
font-weight: unset;
line-height: unset;
margin-bottom: 0px !important;
color: #575e75;
user-select: none;
}
`)
settingsButton.addEventListener('click', () => {
let html = '';
// 设置项标题
html += `
<b style="margin: 0 .3em">
设置
</b>
`
// 每项的设置
settings.forEach((item) => {
if (item.show == false) {
return;
}
html += `
<div style="
margin: .6em .5em;
display: flex;
justify-content: space-between;
">
`;
// 设置名称,如果是 check 类型的设置项,就用 span 包裹,否则就用 b 包裹
html += `
<label
class="explore-settings-label"
for="${item.tag}"
>
<span>${item.text}</span>
${item.desp ? `
<br/>
<small>${item.desp}</small>
` : ''}
</label>
`;
// Check 类型设置项的勾选控件
if (item.type == 'check') {
html += `
<input
type="checkbox"
name="${item.tag}"
id="${item.tag}"
${localStorage[item.tag] == 'true' ? 'checked' : ''}
onchange="localStorage['${item.tag}'] = this.checked"
style="margin-left: 0.8em; margin-left: 0.05em;"
${item.disabled ? 'disabled' : ''}
/>
`;
}
// Radio 类型设置项的设置选项
if (item.type == 'radio') {
// 设置选项
html += `<div style="margin-left: 0.8em;">`;
item.select.forEach((selectItem, index) => {
html += `
<input
type="radio"
name="${item.tag}"
value="${index}"
id="${item.tag}-${index}"
${index == localStorage[item.tag] ? 'checked' : ''}
onchange="localStorage['${item.tag}'] = ${index}"
/>
<label
class="explore-settings-label"
style="display: inline;"
for="${item.tag}-${index}"
>
${selectItem}
</label>
<br/>
`;
});
html += `</div>`;
}
html += '</div>';
});
// 自定义样式
html += `
<div style="
display: flex;
justify-content: space-between;
align-items: center;
margin: 1em 0;
">
<div style="margin: 0.3em 0">
<b style="display: block">个性化样式</b>
<small>设置主题色、自定义字体、添加自定 CSS...</small>
</div>
<a onclick="window.openCustomStyle()">前往设置</a>
</div>
`;
window.openCustomStyle = () => {
Blockey.Utils.confirm('自定义样式', `
<div style="
gap: 1em;
display: flex;
flex-direction: column;
margin-bottom: 1em;
">
<b>设置自定义字体</b>
<input type="text" class="form-control customFont" placeholder="自定义字体名称" onChange="window.setCustomStyle('font', event.target.value)">
<b>设置主题颜色</b>
<div style="
display: flex;
justify-content: space-between;
align-items: center;
">
<div style="
display: flex;
gap: 1em;
">
<input type="color" id="mainColor" onchange="window.setCustomStyle('mainColor', event.target.value)">
<input type="color" id="secondColor" onchange="window.setCustomStyle('secondColor', event.target.value)">
</div>
<button class="btn btn-primary btn-sm" onclick="window.setCustomStyle('defaultTheme')">恢复默认值</button>
</div>
<div>
<b>引入自定义 CSS 文件</b>
<small>添加自定义 CSS 文件前请确保您了解这个文件的内容和用途。</small>
</div>
<input type="text" class="form-control customCSSFile" placeholder="自定义 CSS 文件地址" onChange="window.setCustomStyle('cssFile', event.target.value)">
<div>
<b>自定 CSS</b>
<small>请确保您理解您所输入的 CSS 代码。</small>
</div>
<textarea
class="form-control customCSS"
placeholder="自定义 CSS"
style="resize: vertical;"
onChange="window.setCustomStyle('css', event.target.value)"
></textarea>
</div>
`);
window.setCustomStyle = function (name, value) {
if (name == 'cssFile') {
localStorage['explore:customCSSFile'] = value;
} else if (name == 'font') {
localStorage['explore:customFont'] = value;
} else if (name == 'css') {
localStorage['explore:customCSS'] = value;
} else if (name == 'mainColor') {
localStorage['explore:mainColor'] = value;
window.applyThemeColor();
$('.ok-button')[0].style.background = value;
$('.ok-button')[0].style.border = value;
} else if (name == 'secondColor') {
localStorage['explore:secondColor'] = value;
window.applyThemeColor();
} else if (name == 'defaultTheme') {
window.setCustomStyle('mainColor', DEFAULT_MAIN_COLOR);
window.setCustomStyle('secondColor', DEFAULT_SECOND_COLOR);
$('#mainColor')[0].value = localStorage['explore:mainColor'];
$('#secondColor')[0].value = localStorage['explore:secondColor'];
}
};
localStorage['explore:customCSS'] && ($('.customCSS')[0].value = localStorage['explore:customCSS']);
localStorage['explore:customCSSFile'] && ($('.customCSSFile')[0].value = localStorage['explore:customCSSFile']);
localStorage['explore:customFont'] && ($('.customFont')[0].value = localStorage['explore:customFont']);
$('#mainColor')[0].value = localStorage['explore:mainColor'];
$('#secondColor')[0].value = localStorage['explore:secondColor'];
}
// 自动跳转设置
html += `
<div style="
display: flex;
justify-content: space-between;
align-items: center;
margin: 1em 0;
">
<div style="margin: 0.3em 0">
<b style="display: block">自动跳转</b>
<small>${window.GMAvailable ? '若不理解该选项的用途,请勿修改' : '似乎不支持该功能?'}</small>
</div>
<select
style="height: 2em"
id="explore-redirect-selector"
onchange="SetRedirect(document.querySelector('#explore-redirect-selector').value)"
${window.GMAvailable ? '' : 'disabled'}
>
<option value="none">不自动跳转</option>
<option value="aerfaying">自动跳转 aerfaying.com</option>
<option value="gitblock">自动跳转 gitblock.cn</option>
</select>
</div>
`;
// 导出和导入配置
html += `
<div style="
display: flex;
justify-content: space-between;
align-items: center;
margin: 1em 0;
">
<div style="margin: 0.3em 0">
<b style="display: block">配置</b>
</div>
<div>
<a onclick="window.aerfayingExplore.importConfig()">导入</a> /
<a onclick="window.aerfayingExplore.exportConfig()">导出</a>
</div>
</div>
`;
window.aerfayingExplore.exportConfig = async () => {
let exportData = {};
settings.forEach((item) => {
exportData[item.tag] = localStorage[item.tag];
});
['explore:mainColor', 'explore:secondColor', 'explore:remark', 'explore:customCSS', 'explore:customCSSFile', 'explore:customFont'].forEach((key) => {
if (localStorage.getItem(key)) exportData[key] = localStorage[key];
});
try {
await navigator.clipboard.writeText(JSON.stringify(exportData));
Blockey.Utils.Alerter.info('已复制到剪贴板');
} catch (e) {
console.log(e);
Blockey.Utils.Alerter.info('复制到剪贴板失败');
}
};
window.aerfayingExplore.importConfig = () => {
Blockey.Utils.prompt('导入配置').then((data) => {
try {
let importData = JSON.parse(data);
settings.forEach((item) => {
if (item.type === 'radio' && Number.isInteger(Number(importData[item.tag])) || item.type === 'check' && ['true', 'false'].includes(importData[item.tag])) {
localStorage[item.tag] = importData[item.tag];
} else {
throw Error(`${item.tag} 在类型 ${item.type} 下错误的值 ${importData[item.tag]}`)
}
});
['explore:mainColor', 'explore:secondColor', 'explore:customCSS', 'explore:customCSSFile', 'explore:customFont'].forEach((key) => {
if (importData[key]) {
localStorage[key] = importData[key];
}
});
importData['explore:remark'] && (localStorage['explore:remark'] = JSON.stringify(JSON.parse(importData['explore:remark'])));
location.reload();
} catch (e) {
Blockey.Utils.Alerter.info(`配置存在错误!${e}`);
}
});
}
// 设置的尾部显示开源地址、版本
html += `<hr/>`;
html += `
<div style="text-align:center">
<a href="https://waterblock79.github.io/aerfaying-explore/" style="font-weight:600">插件官方页面</a>
|
<a href="https://github.com/waterblock79/aerfaying-explore" style="font-weight:600">开源仓库</a>
</div>`;
html += `<span style="display:block;text-align:center;margin-top:0.2em;font-size:85%;"> 插件版本 ${version} </span>`;
html += `<br/>`;
// 显示提示框
Blockey.Utils.confirm('插件设置', html);
// 移除掉“确定”按钮左边的“取消”按钮,并把“确定”摁钮中的文字替换为“关闭”
$('button.ok-button')[0].parentNode.childNodes[0].remove();
$('button.ok-button')[0].innerHTML = '关闭';
$('button.ok-button')[0].addEventListener('click', () => { location.reload(); });
});
// 设置自动跳转选项的初始值
addFindElement('select#explore-redirect-selector', (element) => {
if (!window.GMAvailable) {
element.value = 'none';
return;
}
element.value = GM_getValue('explore:autoRedirect') || 'none';
})
// 插入设置按钮
if (location.pathname.match(/\S+\/Editor/) == null && $('#nav-settings').length > 0) {// 当前页面不是作品编辑器页面时,并且已经登陆(#nav-settings 存在)
insertBefore(settingsButton, $('#nav-settings')[0]);
} else { // 如果现在没有插入这个元素,那就静待良机,等这个条件成立了以后再插入元素
let waitInsertSettingsButtonInterval = setInterval(() => {
if ($('#nav-settings').length > 0 && location.pathname.match(/\S+\/Editor/) == null) {
insertBefore(settingsButton, $('#nav-settings')[0]);
clearInterval(waitInsertSettingsButtonInterval);
}
}, 1000)
}
// 请求作品资源(GET https://cdn.gitblock.cn/Project/GetAsset?name=)失败时自动重新加载,并显示加载进度
if (localStorage['explore:projectAssetLoad'] == 'true') {
const fetch_old = window.fetch;
var projectAssetLoadLog = [];
var projectAssetLoadCount = {
total: 0,
success: 0,
failed: 0
}
window.fetch = (...args) => {
if (args[0].match(/http(s)?:\/\/cdn.gitblock.cn\/Project\/GetAsset\?name=/)) {
projectAssetLoadCount.total++;
return new Promise((resolve, reject) => {
let retryCount = 0;
const tryFetch = async () => {
let response;
try {
response = await fetch_old(...args);
} catch (e) {
response = false;
}
if (response?.status === 200) { // 请求成功
resolve(response);
projectAssetLoadLog.push(`成功 ${args[0].split('=')[1]}`);
projectAssetLoadCount.success++;
} else if (retryCount <= 8) { // 请求失败,重试
retryCount++;
setTimeout(tryFetch, 1000);
projectAssetLoadLog.push(`重试 ${args[0].split('=')[1]}(第 ${retryCount} 次)`);
} else { // 请求失败次数超过 8 次放弃加载并返回占位图
resolve(fetch(`data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAABmSURBVDhPtYwBCsAwCAP7/093c2tEaya6sQOhDcmNk/nx7kcXJ0BQxW5UIFQke98JhEzCukEgdDIqEGyedR4FwiqsX+Rfgc2zDhV0siBgRcC6TpCNwd5XQWUM7OYSdMYAOxW8uzEPkuP1J7Jf5JYAAAAASUVORK5CYII=`));
projectAssetLoadCount.failed++;
projectAssetLoadLog.push(`失败 ${args[0].split('=')[1]}`);
};
};
tryFetch();
})
} else {
return fetch_old(...args);
}
}
}
// 对于加载提示的介绍
if (location.pathname == '/AboutLoading') {
$('title')[0].innerHTML = `关于加载中的提示 - Aerfaying Explore`;
$('.container')[1].innerHTML = `
<img class="explore-about-loading" src="https://fastly.jsdelivr.net/gh/waterblock79/aerfaying-explore@main/assets/%E5%8A%A0%E8%BD%BD%E6%8F%90%E7%A4%BA.svg">
`;
$('.container')[1].classList.add('content-container');
addStyle(`
.content-container {
text-align: center;
}
.explore-about-loading {
max-width: 85%;
max-height: 30em;
background: white;
padding: 1em;
border-radius: 12px;
margin: 0.5em 0 2em 0;
box-shadow: 2px 2px 15px rgb(0 0 0 / 5%);
}
`)
}
// 使弹出框(如评论详细信息、原创声明)中的内容可以被复制
addStyle(`
.modal_modal-content_3brCX {
-webkit-user-select: auto !important;
-moz-user-select: auto !important;
-ms-user-select: auto !important;
user-select: auto !important;
}
.item-attached-thin-modal-body_wrapper_3KdPz { user-select: none; }
`);
// 不文明用语“警告!!!”的不再提示
addFindElement('div.modal_header-item_1WbOm.modal_header-item-title_1N2BE', (element) => {
// 如果这个弹出框的标题是“警告!!!”
if (element.innerHTML == '警告!!!') {
// 如果已经标记不再提示了那就直接帮忙点一下确定键就好了
if (sessionStorage.blockedAlert) {
$('.footer>.ok-button')[0].click();
return;
}
// 给真的确定摁钮加一个标记
$('.footer>.ok-button')[0].classList.add("real");
// 创建“不再提示”按钮
let blockAlert = document.createElement('button');
blockAlert.classList.add("ok-button");
blockAlert.style.background = "coral";
blockAlert.innerHTML = '不再提示';
blockAlert.addEventListener('click', () => {
$('.footer>.ok-button.real')[0].click(); // 点击真·确定按钮
sessionStorage.blockedAlert = true;
})
// 插入摁钮
insertBefore(blockAlert, $('.footer>.ok-button')[0]);
$('.footer')[0].style.marginTop = '0.5em';
}
});
// 替换掉原先全屏的加载遮盖
let projectThumbId = 'E0D08BE45041CB909364CE99790E7249.png'; // 在加载作品时候需要用到的作品封面 assets ID
addFindElement('.menu-bar_right-bar_3dIRQ', (element) => {
// 如果其设置为“保持原状”或者这页是签到的嵌入页,那就直接退出
if (localStorage['explore:loading'] == 0 || location.search === '?openRobotCheckIn' + localStorage['openRobotCheckInKey']) return;
// 先隐藏了原先的加载遮盖
addStyle(`
.loader_background_1-Rwn { display: none !important }
`);
// 方案 1:在顶部导航栏中显示“加载中”图标及文字
if (localStorage['explore:loading'] == 1) {
// 创建并插入“加载中”文字
let text = document.createElement('span');
text.classList.add('explore-loading-text');
text.innerText = '加载中';
element.insertBefore(text, element.firstChild);
// 创建并插入加载动画
let loading = document.createElement('div');
loading.classList.add('explore-loading');
element.insertBefore(loading, element.firstChild);
// CSS
addStyle(`
/* 加载动画和加载文字的 CSS */
.explore-loading {
border: 2.5px solid #f3f3f3b0;
border-top: 2.5px solid #fff;
border-radius: 100%;
min-width: 1em;
min-height: 1em;
display: inline-block;
animation: spin 2s linear infinite;
margin: 0 0.3em;
}
.explore-loading-text {
margin: 0 1.25em 0 0;
min-width: 2em;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 顶部通知图标稍微有一点歪,和这个加载提示在一起有点难看,要修正下 */
i.notification {
margin-bottom: 3px;
}
/* 若屏幕过窄就不显示加载文字 */
@media screen and (max-width: 380px) {
.explore-loading-text {
display: none !important;
width: 0 !important;
}
}
.explore-project-loading {
display: flex;
flex-direction: column;
align-items: center;
}
`);
// 默认隐藏
$('.explore-loading')[0].style.display = 'none';
try { $('.explore-loading-text')[0].style.display = 'none'; } catch (e) { }
}
// 方案 2:在左下角显示不影响浏览的加载提示(原方案)
else {
// 添加左下角加载提示
let loadingElement = document.createElement('div');
loadingElement.style = "width: 5em; height: 5em; position: fixed; background-color: #4c97ff; right: 5%; opacity: 0.8; bottom: 5%; border-radius: 8px;";
loadingElement.classList.add("explore-loading");
loadingElement.innerHTML = '<div class="loader_block-animation_2EgCQ" style="height: 3em;margin: 1em 1em 1em 1.25em;"><img class="loader_top-block_1-yuR" src="https://cdn.gitblock.cn/static/images/209cd016f099f4515cf80dff81e6e0f7.svg" style="margin: 0;"><img class="loader_middle-block_2ma0T" src="https://cdn.gitblock.cn/static/images/ab844ae9647bd855ed2f15b22c6b9337.svg" style="margin: 0;"><img class="loader_bottom-block_ABwSu" src="https://cdn.gitblock.cn/static/images/ee4f8261355c8d3b6fd7228a386c62be.svg" style="margin: 0;"></div>';
document.body.appendChild(loadingElement)
$('.explore-loading')[0].style.display = 'none';
}
// 如果发现了原先的加载遮盖,就显示新的加载提示
addFindElement('.loader_background_1-Rwn', (element) => {
$('.explore-loading')[0].style.display = 'block';
try { $('.explore-loading-text')[0].style.display = 'block'; } catch (e) { }
// 轮询直到原先的加载遮盖消失
let interval = setInterval(() => {
// 设置作品资源加载 Log
// console.log(projectAssetLoadLog)
if (localStorage['explore:projectAssetLoad'] == 'true' && $('pre.explore-asset-load-log')[0] && projectAssetLoadLog?.length) {
$('pre.explore-asset-load-log')[0].innerText = projectAssetLoadLog[projectAssetLoadLog.length - 1] + `\n` + `共 ${projectAssetLoadCount.total} 个,完成 ${projectAssetLoadCount.success} 个`;
}
// 清除加载遮盖
if (!$('.loader_background_1-Rwn')[0]) {
$('.explore-loading')[0].style.display = 'none';
try { $('.explore-loading-text')[0].style.display = 'none'; } catch (e) { }
// 作品加载完了就得删掉作品的加载动画了,并且恢复作品的大绿旗摁钮、恢复鼠标事件、删除作品封面背景
if ($('.explore-project-loading')[0]) {
// 删掉加载动画
$('.explore-project-loading')[0].remove();
// 恢复大绿旗摁钮
$('.stage_green-flag-overlay_219KT')[0].style.display = 'flex';
// 恢复鼠标事件
$('.controls_controls-container_3ZRI_')[0].style = '';
$('.stage_green-flag-overlay-wrapper_3bCO-')[0].style = '';
// 删除作品封面背景
try { $('.explore-project-cover')[0].remove(); } catch (e) { }
clearInterval(interval);
// 作品资源加载数据重置
if (localStorage['explore:projectAssetLoad'] == 'true') {
if (projectAssetLoadCount.failed > 0) {
Blockey.Utils.confirm('加载错误', `共 ${projectAssetLoadCount.failed} 个资源加载错误<br/>加载日志:` + projectAssetLoadLog.map(x => encodeHTML(x)).join('<br/>'));
}
console.log('加载资源统计', projectAssetLoadCount);
console.log('加载日志', projectAssetLoadLog);
projectAssetLoadCount = { success: 0, failed: 0, total: 0 };
projectAssetLoadLog = [];
}
}
}
}, 50);
});
// 如果还发现了只有作品加载的时候会出现的“加载消息”,那就得给作品也加上一个加载的小动画+提示
addFindElement('div.loader_message-container-outer_oYjTv', (element) => {
// 如果现在是在编辑器页面,那就不用添加这个小动画和提示了
if (location.pathname.match(/\S+\/Editor/) != null) return;
// 创建加载的小动画和提示
let projectLoad = document.createElement('div');
projectLoad.classList.add('explore-project-loading');
projectLoad.innerHTML = `
<div class="loader_block-animation_2EgCQ">
<img class="loader_top-block_1-yuR" src="https://cdn.gitblock.cn/static/gui/static/assets/bbbd98ae6a34eac772e34a57aaa5f977.svg">
<img class="loader_middle-block_2ma0T" src="https://cdn.gitblock.cn/static/gui/static/assets/f9dce53613d5f85b311ce9f84423c08b.svg">
<img class="loader_bottom-block_ABwSu" src="https://cdn.gitblock.cn/static/gui/static/assets/ce5820b006d753e4133f46ae776f4d96.svg">
</div>
<div class="loader_title_28GDz" style="
color: #fff;
">
<span>载入项目</span>
</div>
${localStorage['explore:projectAssetLoad'] == 'true' ?
`<pre class="explore-asset-load-log" style="
max-height: 4em;
padding: 0;
background: none;
border: none;
color: #fff;
text-align: center;
"></pre>` : ''
}
`;
$('div.stage_green-flag-overlay-wrapper_3bCO-.box_box_tWy-0')[0].appendChild(projectLoad);
// 隐藏作品的大绿旗摁钮
$('.stage_green-flag-overlay_219KT')[0].style.display = 'none';
// 禁止鼠标事件(别加载着一半就点绿旗开始运行了)
$('.controls_controls-container_3ZRI_')[0].style = 'pointer-events: none;';
$('.stage_green-flag-overlay-wrapper_3bCO-')[0].style = 'pointer-events: none;';
// 用这个“...canvas-wrapper-mobile_2WJLy”是否存在判断是否为手机端布局,不是手机端布局就加上作品封面背景
let projectImage = document.createElement('img');
projectImage.src = `https://cdn.gitblock.cn/Media?name=${projectThumbId}`;
projectImage.classList.add('explore-project-cover');
addStyle(`
.explore-project-cover {
position: absolute;
top: 0;
width: 100%;
filter: blur(6px);
overflow: hidden;
transform: scale(1.05);
}
/* 因为这个封面有模糊效果,它可能会超出边界,所以要给最外层的这个设置一个 overflow: hidden;,
再设置一个 border-radius: 0.5rem; 修一下边 */
div.stage-wrapper_stage-canvas-wrapper_n2Q5r.box_box_tWy-0 {
border-radius: 0.5rem;
overflow: hidden;
}
div.stage-wrapper_stage-canvas-wrapper-mobile_2WJLy.box_box_tWy-0 {
border-radius: 0.5rem;
overflow: hidden;
}
`);
insertBefore(projectImage, $('div.stage_green-flag-overlay-wrapper_3bCO-.box_box_tWy-0')[0]);
});
})
// 让手机端布局的用户主页也能显示用户 ID、金币、比特石
addStyle(`
@media (max-width: 768px) {
.profile-head_bitStones_1GFkj, .profile-head_goldCoins_TxdJM {
display: inline-flex !important;
}
}
`);
// 在用户主页显示被邀请的信息、显示邀请的用户的入口
addHrefChangeEvent((url) => {
if (url.match(/\/Users\/([0-9]+\/?)/g) != location.pathname) return; // 如果这个页面不是个用户的主页就退出掉(不匹配 /Users/NUMBER/ 或 /Users/NUMBER)
if ($('.profile-head_join_HPHzg')[0]?.innerText?.includes('邀请')) return;
let userId = url.match(/[0-9]+/); // 从 URL 匹配用户 ID
window.$.ajax({
method: 'POST',
url: `/WebApi/Users/${userId}/GetPagedInvitedUsers`,
data: {
pageIndex: 1, pageSize: 10
},
success: (data) => {
let length = data.invitorPath.length; // 邀请链深度
// 若该用户不是在邀请链的第一层上,那就是被邀请的用户
if (data.invitorPath.length != 1) {
let userId = data.invitorPath[length - 2].id,
userName = data.invitorPath[length - 2].username;
let showInvitingUser = addFindElement('.profile-head_join_HPHzg>small', (element) => {
element.innerHTML += ` · 由<a href="/Users/${encodeHTML(userId)}">${encodeHTML(userName)}</a>邀请`;
delete findElement[showInvitingUser];
});
}
}
})
// 在关注、粉丝、下面添加一个“显示邀请的用户”的入口
let showInvitedUsers = addFindElement('div.grid-2fr1.grid-gap-xl', (element) => {
if ($('#showInvitedUsers').length > 0) return; // 如果已经添加过了就退出掉(防止重复添加
// 生成查看该用户邀请过的用户的链接
let targetUrl = location.pathname;
if (targetUrl.slice(-1) == '/') targetUrl = targetUrl.slice(0, -1);
targetUrl += '/My/InvitedUsers'
// 找到“关注”、“粉丝”的父级元素
let parent = element.childNodes[1];
// 生成“邀请”栏的元素
let newElement = document.createElement('div');
newElement.className = 'panel2_wrapper_3UZFE panel-border-bottom';
newElement.id = 'showInvitedUsers';
newElement.innerHTML = `
<div class="panel2_panel_1hPqt">
<div class="panel2_panelHead_1Bn6y panel-head">
<h2>
<span class="panel2_border_2Slyp" style="background-color: rgb(77, 151, 255);"></span>邀请
</h2>
<a class="more" href="${encodeURI(targetUrl)}">查看»</a>
</div>
</div>
`;
// 将此元素放到“关注”、“粉丝”后面
if (window.innerWidth <= 768) { // 如果是手机端布局,那么关注、邀请后面还会有个评论,这个时候就需要特判一下,让邀请栏放在评论前面
parent.insertBefore(newElement, parent.childNodes[2]);
} else {
parent.appendChild(newElement);
}
delete findElement[showInvitedUsers];
});
})
// 修复作品“继续加载”的预览图尺寸问题
addFindElement('.img-responsive', (element) => {
element.style.width = '100%';
})
// 评论显示评论 ID
let commentData = {};
addStyle(`
.explore-comment-info-icon {
margin-right: .4em;
}
`);
// 自动隐藏评论 ID,鼠标 hover 时再显示
if (localStorage['explore:hoverId'] == 'true') {
addStyle(`
.explore-comment-id {
display: none;
}
.comment_base_info:hover .explore-comment-id {
display: inline-block;
}
`);