@mlikiowa/nanaeo 1.0.1702966759079 → 1.0.1702967739786

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/2022/08/04/NewBlog/index.html +3473 -0
  2. package/2022/08/13/GaussWave/index.html +3407 -0
  3. package/about/index.html +3236 -0
  4. package/archives/2022/08/index.html +3411 -0
  5. package/archives/2022/index.html +3411 -0
  6. package/archives/index.html +3308 -0
  7. package/asset/Sotheby.ttf +0 -0
  8. package/asset/backimg.png +0 -0
  9. package/atom.xml +171 -0
  10. package/categories/DevLog/index.html +3351 -0
  11. package/categories/SiteLog/index.html +3351 -0
  12. package/categories/index.html +3174 -0
  13. package/category/devlog/atom.xml +58 -0
  14. package/category/devlog/feed.json +19 -0
  15. package/category/devlog/rss.xml +62 -0
  16. package/category/sitelog/atom.xml +125 -0
  17. package/category/sitelog/feed.json +20 -0
  18. package/category/sitelog/rss.xml +129 -0
  19. package/content.json +1 -0
  20. package/css/Readme.html +9 -0
  21. package/css/first.css +1504 -0
  22. package/css/style.css +7106 -0
  23. package/favicon.ico +0 -0
  24. package/feed.json +31 -0
  25. package/friends/index.html +3661 -0
  26. package/index.html +3421 -0
  27. package/js/app.js +1223 -0
  28. package/js/plugins/aplayer.js +186 -0
  29. package/js/plugins/parallax.js +191 -0
  30. package/js/plugins/rightMenu.js +577 -0
  31. package/js/plugins/rightMenus.js +618 -0
  32. package/js/plugins/tags/contributors.js +92 -0
  33. package/js/plugins/tags/friends.js +93 -0
  34. package/js/plugins/tags/sites.js +96 -0
  35. package/js/search/hexo.js +192 -0
  36. package/package.json +1 -1
  37. package/rss.xml +175 -0
  38. package/tag/devlog/atom.xml +58 -0
  39. package/tag/devlog/feed.json +19 -0
  40. package/tag/devlog/rss.xml +62 -0
  41. package/tag/gauss/atom.xml +58 -0
  42. package/tag/gauss/feed.json +19 -0
  43. package/tag/gauss/rss.xml +62 -0
  44. package/tag/hexo/atom.xml +125 -0
  45. package/tag/hexo/feed.json +20 -0
  46. package/tag/hexo/rss.xml +129 -0
  47. package/tag/hexothemes/atom.xml +125 -0
  48. package/tag/hexothemes/feed.json +20 -0
  49. package/tag/hexothemes/rss.xml +129 -0
  50. package/tag/sitelog/atom.xml +125 -0
  51. package/tag/sitelog/feed.json +20 -0
  52. package/tag/sitelog/rss.xml +129 -0
  53. package/tags/DevLog/index.html +3351 -0
  54. package/tags/Gauss/index.html +3351 -0
  55. package/tags/Hexo/index.html +3351 -0
  56. package/tags/HexoThemes/index.html +3351 -0
  57. package/tags/SiteLog/index.html +3351 -0
  58. package/tags/index.html +3159 -0
  59. package/volantis-sw.js +797 -0
package/js/app.js ADDED
@@ -0,0 +1,1223 @@
1
+ document.addEventListener("DOMContentLoaded", function () {
2
+ volantis.requestAnimationFrame(() => {
3
+ VolantisApp.init();
4
+ VolantisApp.subscribe();
5
+ VolantisFancyBox.init();
6
+ highlightKeyWords.startFromURL();
7
+ locationHash();
8
+
9
+ volantis.pjax.push(() => {
10
+ VolantisApp.pjaxReload();
11
+ VolantisFancyBox.init();
12
+ sessionStorage.setItem("domTitle", document.title);
13
+ highlightKeyWords.startFromURL();
14
+ }, 'app.js');
15
+ volantis.pjax.send(() => {
16
+ volantis.dom.switcher.removeClass('active'); // 关闭移动端激活的搜索框
17
+ volantis.dom.header.removeClass('z_search-open'); // 关闭移动端激活的搜索框
18
+ volantis.dom.wrapper.removeClass('sub'); // 跳转页面时关闭二级导航
19
+ volantis.EventListener.remove() // 移除事件监听器 see: layout/_partial/scripts/global.ejs
20
+ }, 'app.js');
21
+ });
22
+ });
23
+
24
+ /* 锚点定位 */
25
+ const locationHash = () => {
26
+ if (window.location.hash) {
27
+ let locationID = decodeURI(window.location.hash.split('#')[1]).replace(/\ /g, '-');
28
+ let target = document.getElementById(locationID);
29
+ if (target) {
30
+ setTimeout(() => {
31
+ if (window.location.hash.startsWith('#fn')) { // hexo-reference https://github.com/volantis-x/hexo-theme-volantis/issues/647
32
+ volantis.scroll.to(target, { addTop: - volantis.dom.header.offsetHeight - 5, behavior: 'instant', observer: true })
33
+ } else {
34
+ // 锚点中上半部有大片空白 高度大概是 volantis.dom.header.offsetHeight
35
+ volantis.scroll.to(target, { addTop: 5, behavior: 'instant', observer: true })
36
+ }
37
+ }, 1000)
38
+ }
39
+ }
40
+ }
41
+ Object.freeze(locationHash);
42
+
43
+ /* Main */
44
+ const VolantisApp = (() => {
45
+ const fn = {},
46
+ COPYHTML = '<button class="btn-copy" data-clipboard-snippet=""><i class="fa-solid fa-copy"></i><span>COPY</span></button>';
47
+ let scrollCorrection = 80;
48
+
49
+ fn.init = () => {
50
+ if (volantis.dom.header) {
51
+ scrollCorrection = volantis.dom.header.clientHeight + 16;
52
+ }
53
+
54
+ window.onresize = () => {
55
+ if (document.documentElement.clientWidth < 500) {
56
+ volantis.isMobile = 1;
57
+ } else {
58
+ volantis.isMobile = 0;
59
+ }
60
+ if (volantis.isMobile != volantis.isMobileOld) {
61
+ fn.setGlobalHeaderMenuEvent();
62
+ fn.setHeader();
63
+ fn.setHeaderSearch();
64
+ }
65
+ }
66
+ volantis.scroll.push(fn.scrollEventCallBack, "scrollEventCallBack")
67
+ }
68
+
69
+ fn.event = () => {
70
+ volantis.dom.$(document.getElementById("scroll-down"))?.on('click', function () {
71
+ fn.scrolltoElement(volantis.dom.bodyAnchor);
72
+ });
73
+
74
+ // 如果 sidebar 为空,隐藏 sidebar。
75
+ const sidebar = document.querySelector("#l_side")
76
+ if (sidebar) {
77
+ const sectionList = sidebar.querySelectorAll("section")
78
+ if (!sectionList.length) {
79
+ document.querySelector("#l_main").classList.add("no_sidebar")
80
+ }
81
+ }
82
+
83
+ // 站点信息 最后活动日期
84
+ if (volantis.GLOBAL_CONFIG.sidebar.for_page.includes('webinfo') || volantis.GLOBAL_CONFIG.sidebar.for_post.includes('webinfo')) {
85
+ const lastupd = volantis.GLOBAL_CONFIG.sidebar.webinfo.lastupd;
86
+ if (!!document.getElementById('last-update-show') && lastupd.enable && lastupd.friendlyShow) {
87
+ document.getElementById('last-update-show').innerHTML = fn.utilTimeAgo(volantis.GLOBAL_CONFIG.lastupdate);
88
+ }
89
+ }
90
+
91
+ // 站点信息 运行时间
92
+ if (!!document.getElementById('webinfo-runtime-count')) {
93
+ let BirthDay = new Date(volantis.GLOBAL_CONFIG.sidebar.webinfo.runtime.data);
94
+ let timeold = (new Date().getTime() - BirthDay.getTime());
95
+ let daysold = Math.floor(timeold / (24 * 60 * 60 * 1000));
96
+ document.getElementById('webinfo-runtime-count').innerHTML = `${daysold} ${volantis.GLOBAL_CONFIG.sidebar.webinfo.runtime.unit}`;
97
+ }
98
+
99
+ // 消息提示 复制时弹出
100
+ document.body.oncopy = function () {
101
+ fn.messageCopyright()
102
+ };
103
+ }
104
+
105
+ fn.restData = () => {
106
+ scrollCorrection = volantis.dom.header ? volantis.dom.header.clientHeight + 16 : 80;
107
+ }
108
+
109
+ fn.setIsMobile = () => {
110
+ if (document.documentElement.clientWidth < 500) {
111
+ volantis.isMobile = 1;
112
+ volantis.isMobileOld = 1;
113
+ } else {
114
+ volantis.isMobile = 0;
115
+ volantis.isMobileOld = 0;
116
+ }
117
+ }
118
+
119
+ // 校正页面定位(被导航栏挡住的区域)
120
+ fn.scrolltoElement = (elem, correction = scrollCorrection) => {
121
+ volantis.scroll.to(elem, {
122
+ top: elem.offsetTop - correction
123
+ })
124
+ }
125
+
126
+ // 滚动事件回调们
127
+ fn.scrollEventCallBack = () => {
128
+ // 【移动端 PC】//////////////////////////////////////////////////////////////////////
129
+
130
+ // 显示/隐藏 Header导航 topBtn 【移动端 PC】
131
+ const showHeaderPoint = volantis.dom.bodyAnchor.offsetTop - scrollCorrection;
132
+ const scrollTop = volantis.scroll.getScrollTop(); // 滚动条距离顶部的距离
133
+
134
+ // topBtn
135
+ if (volantis.dom.topBtn) {
136
+ if (scrollTop > volantis.dom.bodyAnchor.offsetTop) {
137
+ volantis.dom.topBtn.addClass('show');
138
+ // 向上滚动高亮 topBtn
139
+ if (volantis.scroll.del > 0) {
140
+ volantis.dom.topBtn.removeClass('hl');
141
+ } else {
142
+ volantis.dom.topBtn.addClass('hl');
143
+ }
144
+ } else {
145
+ volantis.dom.topBtn.removeClass('show').removeClass('hl');
146
+ }
147
+ }
148
+
149
+ // Header导航
150
+ if (volantis.dom.header) {
151
+ if (scrollTop - showHeaderPoint > -1) {
152
+ volantis.dom.header.addClass('show');
153
+ } else {
154
+ volantis.dom.header.removeClass('show');
155
+ }
156
+ }
157
+
158
+ // 决定一二级导航栏的切换 【向上滚动切换为一级导航栏;向下滚动切换为二级导航栏】 【移动端 PC】
159
+ if (pdata.ispage && volantis.dom.wrapper) {
160
+ if (volantis.scroll.del > 0 && scrollTop > 100) { // 向下滚动
161
+ volantis.dom.wrapper.addClass('sub'); // <---- 二级导航显示
162
+ } else if (volantis.scroll.del < 0) { // 向上滚动
163
+ volantis.dom.wrapper.removeClass('sub'); // <---- 取消二级导航显示 一级导航显示
164
+ }
165
+ }
166
+
167
+ // 【移动端】//////////////////////////////////////////////////////////////////////
168
+ if (volantis.isMobile) {
169
+ // 【移动端】 页面滚动 隐藏 移动端toc目录按钮
170
+ if (pdata.ispage && volantis.dom.tocTarget && volantis.dom.toc) {
171
+ volantis.dom.tocTarget.removeClass('active');
172
+ volantis.dom.toc.removeClass('active');
173
+ }
174
+ // 【移动端】 滚动时隐藏子菜单
175
+ if (volantis.dom.mPhoneList) {
176
+ volantis.dom.mPhoneList.forEach(function (e) {
177
+ volantis.dom.$(e).hide();
178
+ })
179
+ }
180
+ }
181
+ }
182
+
183
+ // 设置滚动锚点
184
+ fn.setScrollAnchor = () => {
185
+ // click topBtn 滚动至bodyAnchor 【移动端 PC】
186
+ if (volantis.dom.topBtn && volantis.dom.bodyAnchor) {
187
+ volantis.dom.topBtn.click(e => {
188
+ e.preventDefault();
189
+ e.stopPropagation();
190
+ fn.scrolltoElement(volantis.dom.bodyAnchor);
191
+ e.stopImmediatePropagation();
192
+ });
193
+ }
194
+
195
+ }
196
+
197
+ // 设置导航栏
198
+ fn.setHeader = () => {
199
+ // !!! 此处的Dom对象需要重载 !!!
200
+ if (!pdata.ispage) return;
201
+
202
+ // 填充二级导航文章标题 【移动端 PC】
203
+ volantis.dom.wrapper.find('.nav-sub .title').html(document.title.split(" - ")[0]);
204
+
205
+ // ====== bind events to every btn =========
206
+ // 评论按钮 【移动端 PC】
207
+ volantis.dom.comment = volantis.dom.$(document.getElementById("s-comment")); // 评论按钮 桌面端 移动端
208
+ volantis.dom.commentTarget = volantis.dom.$(document.querySelector('#l_main article#comments')); // 评论区域
209
+ if (volantis.dom.commentTarget) {
210
+ volantis.dom.comment.click(e => { // 评论按钮点击后 跳转到评论区域
211
+ e.preventDefault();
212
+ e.stopPropagation();
213
+ fn.scrolltoElement(volantis.dom.commentTarget);
214
+ e.stopImmediatePropagation();
215
+ });
216
+ } else volantis.dom.comment.style.display = 'none'; // 关闭了评论,则隐藏评论按钮
217
+
218
+ // 移动端toc目录按钮 【移动端】
219
+ if (volantis.isMobile) {
220
+ volantis.dom.toc = volantis.dom.$(document.getElementById("s-toc")); // 目录按钮 仅移动端
221
+ volantis.dom.tocTarget = volantis.dom.$(document.querySelector('#l_side .toc-wrapper')); // 侧边栏的目录列表
222
+ if (volantis.dom.tocTarget) {
223
+ // 点击移动端目录按钮 激活目录按钮 显示侧边栏的目录列表
224
+ volantis.dom.toc.click((e) => {
225
+ e.stopPropagation();
226
+ volantis.dom.tocTarget.toggleClass('active');
227
+ volantis.dom.toc.toggleClass('active');
228
+ });
229
+ // 点击空白 隐藏
230
+ volantis.dom.$(document).click(function (e) {
231
+ e.stopPropagation();
232
+ if (volantis.dom.tocTarget) {
233
+ volantis.dom.tocTarget.removeClass('active');
234
+ }
235
+ volantis.dom.toc.removeClass('active');
236
+ });
237
+ } else volantis.dom.toc.style.display = 'none'; // 隐藏toc目录按钮
238
+ }
239
+ }
240
+
241
+ // 设置导航栏菜单选中状态 【移动端 PC】
242
+ fn.setHeaderMenuSelection = () => {
243
+ // !!! 此处的Dom对象需要重载 !!!
244
+ volantis.dom.headerMenu = volantis.dom.$(document.querySelectorAll('#l_header .navigation,#l_cover .navigation,#l_side .navigation')); // 导航列表
245
+
246
+ // 先把已经激活的取消激活
247
+ volantis.dom.headerMenu.forEach(element => {
248
+ let li = volantis.dom.$(element).find('li a.active')
249
+ if (li)
250
+ li.removeClass('active')
251
+ let div = volantis.dom.$(element).find('div a.active')
252
+ if (div)
253
+ div.removeClass('active')
254
+ });
255
+
256
+ // replace '%' '/' '.'
257
+ var idname = location.pathname.replace(/\/|%|\./g, '');
258
+ if (idname.length == 0) {
259
+ idname = 'home';
260
+ }
261
+ var page = idname.match(/page\d{0,}$/g);
262
+ if (page) {
263
+ page = page[0];
264
+ idname = idname.split(page)[0];
265
+ }
266
+ var index = idname.match(/index.html/);
267
+ if (index) {
268
+ index = index[0];
269
+ idname = idname.split(index)[0];
270
+ }
271
+ // 转义字符如 [, ], ~, #, @
272
+ idname = idname.replace(/(\[|\]|~|#|@)/g, '\\$1');
273
+ if (idname && volantis.dom.headerMenu) {
274
+ volantis.dom.headerMenu.forEach(element => {
275
+ // idname 不能为数字开头, 加一个 action- 前缀
276
+ let id = element.querySelector("[active-action=action-" + idname + "]")
277
+ if (id) {
278
+ volantis.dom.$(id).addClass('active')
279
+ }
280
+ });
281
+ }
282
+ }
283
+
284
+ // 设置全局事件
285
+ fn.setGlobalHeaderMenuEvent = () => {
286
+ if (volantis.isMobile) {
287
+ // 【移动端】 关闭已经展开的子菜单 点击展开子菜单
288
+ document.querySelectorAll('#l_header .m-phone li').forEach(function (_e) {
289
+ if (_e.querySelector(".list-v")) {
290
+ // 点击菜单
291
+ volantis.dom.$(_e).click(function (e) {
292
+ e.stopPropagation();
293
+ let menuType = ''
294
+ // 关闭.menu-phone
295
+ Array.from(e.currentTarget.children).some(val => {
296
+ if(val.classList.contains('s-menu')) {
297
+ menuType = 'menu' // 代表点击的是一级菜单外层的icon
298
+ return
299
+ }
300
+ if(val.classList.contains('menuitem')) {
301
+ menuType = 'item' // 点击的是下拉一级菜单
302
+ return
303
+ }
304
+ })
305
+ if(menuType === 'item') {
306
+ // 关闭已经展开的子菜单, 这一步是针对点击多个拥有二级子菜单的一级菜单,关闭其他所有一级菜单的二级菜单
307
+ // ①
308
+ e.currentTarget.parentElement.childNodes.forEach(function (e2) {
309
+ if (Object.prototype.toString.call(e2) == '[object HTMLLIElement]') {
310
+ e2.childNodes.forEach(function (e1) {
311
+ if (Object.prototype.toString.call(e1) == '[object HTMLUListElement]') {
312
+ volantis.dom.$(e1).hide()
313
+ }
314
+ })
315
+ }
316
+ })
317
+ // 点击展开二级子菜单
318
+
319
+ /*
320
+ 由于采用事件委托,因此此处点击, 两种情况,currentTarget指向菜单按钮a.s-menu和ul的共同父元素li, 第二,指向ul中的li元素,也就是子菜单
321
+ 区分:情况一的第一个子元素a的类名是s-menu;情况二的子元素a的类名为menuitem
322
+ 我们要点击外部的menu icon时要关闭的是.menu-phone而不是.menuitem
323
+ */
324
+ let array = e.currentTarget.children
325
+ for (let index = 0; index < array.length; index++) {
326
+ const element = array[index];
327
+ if (volantis.dom.$(element).title === 'menu') { // 移动端菜单栏异常
328
+ volantis.dom.$(element).style.display = "flex" // https://github.com/volantis-x/hexo-theme-volantis/issues/706
329
+ } else {
330
+ volantis.dom.$(element).show()
331
+ }
332
+ }
333
+ } else {
334
+ let menuPhone = document.querySelector('.switcher .menu-phone')
335
+ let isHiding = window.getComputedStyle(menuPhone).display === 'none'
336
+ if(isHiding) {
337
+ volantis.dom.$(menuPhone).show()
338
+ } else {
339
+ volantis.dom.$(menuPhone).hide()
340
+ // 别忘了再执行①
341
+ // 准备关闭所有二级菜单, 注意此时的e和点击一级菜单时候的e层级不同
342
+ // 此处好像不能使用变量存储的menuPhone?要重新查询
343
+ document.querySelector('.switcher .menu-phone').childNodes.forEach(function (e2) {
344
+ if (Object.prototype.toString.call(e2) == '[object HTMLLIElement]') {
345
+ e2.childNodes.forEach(function (e1) {
346
+ if (Object.prototype.toString.call(e1) == '[object HTMLUListElement]') {
347
+ volantis.dom.$(e1).hide()
348
+ }
349
+ })
350
+ }
351
+ })
352
+ }
353
+ }
354
+
355
+ }, 0);
356
+ }
357
+ })
358
+ } else {
359
+ // 【PC端】 hover时展开子菜单,点击时[target.baseURI==origin时]隐藏子菜单? 现有逻辑大部分情况不隐藏子菜单
360
+ document.querySelectorAll('#wrapper .m-pc li > a[href]').forEach(function (e) {
361
+ volantis.dom.$(e.parentElement).click(function (e) {
362
+ e.stopPropagation();
363
+ if (e.target.origin == e.target.baseURI) {
364
+ document.querySelectorAll('#wrapper .m-pc .list-v').forEach(function (e) {
365
+ volantis.dom.$(e).hide(); // 大概率不会执行
366
+ })
367
+ }
368
+ }, 0);
369
+ })
370
+ }
371
+ fn.setPageHeaderMenuEvent();
372
+ }
373
+
374
+ // 【移动端】隐藏子菜单
375
+ fn.setPageHeaderMenuEvent = () => {
376
+ if (!volantis.isMobile) return
377
+ // 【移动端】 点击空白处隐藏子菜单
378
+ volantis.dom.$(document).click(function (e) {
379
+ volantis.dom.mPhoneList.forEach(function (e) {
380
+ volantis.dom.$(e).hide();
381
+ })
382
+ });
383
+ }
384
+
385
+ // 设置导航栏搜索框 【移动端】
386
+ fn.setHeaderSearch = () => {
387
+ if (!volantis.isMobile) return;
388
+ if (!volantis.dom.switcher) return;
389
+ // 点击移动端搜索按钮
390
+ volantis.dom.switcher.click(function (e) {
391
+ e.stopPropagation();
392
+ volantis.dom.header.toggleClass('z_search-open'); // 激活移动端搜索框
393
+ volantis.dom.switcher.toggleClass('active'); // 移动端搜索按钮
394
+ }, false); // false : pjax 不移除监听
395
+ // 点击空白取消激活
396
+ volantis.dom.$(document).click(function (e) {
397
+ volantis.dom.header.removeClass('z_search-open');
398
+ volantis.dom.switcher.removeClass('active');
399
+ }, false); // false : pjax 不移除监听
400
+ // 移动端点击搜索框 停止事件传播
401
+ volantis.dom.search.click(function (e) {
402
+ e.stopPropagation();
403
+ }, false); // false : pjax 不移除监听
404
+ }
405
+
406
+ // 设置 tabs 标签 【移动端 PC】
407
+ fn.setTabs = () => {
408
+ let tabs = document.querySelectorAll('#l_main .tabs .nav-tabs')
409
+ if (!tabs) return
410
+ tabs.forEach(function (e) {
411
+ e.querySelectorAll('a').forEach(function (e) {
412
+ volantis.dom.$(e).on('click', (e) => {
413
+ e.preventDefault();
414
+ e.stopPropagation();
415
+ const $tab = volantis.dom.$(e.target.parentElement.parentElement.parentElement);
416
+ $tab.find('.nav-tabs .active').removeClass('active');
417
+ volantis.dom.$(e.target.parentElement).addClass('active');
418
+ $tab.find('.tab-content .active').removeClass('active');
419
+ $tab.find(e.target.className).addClass('active');
420
+ return false;
421
+ });
422
+ })
423
+ })
424
+ }
425
+
426
+ // hexo-reference 页脚跳转 https://github.com/volantis-x/hexo-theme-volantis/issues/647
427
+ fn.footnotes = () => {
428
+ let ref = document.querySelectorAll('#l_main .footnote-backref, #l_main .footnote-ref > a');
429
+ ref.forEach(function (e, i) {
430
+ ref[i].click = () => { }; // 强制清空原 click 事件
431
+ volantis.dom.$(e).on('click', (e) => {
432
+ e.stopPropagation();
433
+ e.preventDefault();
434
+ let targetID = decodeURI(e.target.hash.split('#')[1]).replace(/\ /g, '-');
435
+ let target = document.getElementById(targetID);
436
+ if (target) {
437
+ volantis.scroll.to(target, { addTop: - volantis.dom.header.offsetHeight - 5, behavior: 'instant' })
438
+ }
439
+ });
440
+ })
441
+ }
442
+
443
+ // 工具类:代码块复制
444
+ fn.utilCopyCode = (Selector) => {
445
+ document.querySelectorAll(Selector).forEach(node => {
446
+ const test = node.insertAdjacentHTML("beforebegin", COPYHTML);
447
+ const _BtnCopy = node.previousSibling;
448
+ _BtnCopy.onclick = e => {
449
+ e.stopPropagation();
450
+ const _icon = _BtnCopy.querySelector('i');
451
+ const _span = _BtnCopy.querySelector('span');
452
+
453
+ node.focus();
454
+ const range = new Range();
455
+ range.selectNodeContents(node);
456
+ document.getSelection().removeAllRanges();
457
+ document.getSelection().addRange(range);
458
+
459
+ const str = document.getSelection().toString();
460
+ fn.utilWriteClipText(str).then(() => {
461
+ fn.messageCopyright();
462
+ _BtnCopy.classList.add('copied');
463
+ _icon.classList.remove('fa-copy');
464
+ _icon.classList.add('fa-check-circle');
465
+ _span.innerText = "COPIED";
466
+ setTimeout(() => {
467
+ _icon.classList.remove('fa-check-circle');
468
+ _icon.classList.add('fa-copy');
469
+ _span.innerText = "COPY";
470
+ }, 2000)
471
+ }).catch(e => {
472
+ VolantisApp.message('系统提示', e, {
473
+ icon: 'fa fa-exclamation-circle red'
474
+ });
475
+ _BtnCopy.classList.add('copied-failed');
476
+ _icon.classList.remove('fa-copy');
477
+ _icon.classList.add('fa-exclamation-circle');
478
+ _span.innerText = "COPY FAILED";
479
+ setTimeout(() => {
480
+ _icon.classList.remove('fa-exclamation-circle');
481
+ _icon.classList.add('fa-copy');
482
+ _span.innerText = "COPY";
483
+ })
484
+ })
485
+ }
486
+ });
487
+ }
488
+
489
+ // 工具类:复制字符串到剪切板
490
+ fn.utilWriteClipText = (str) => {
491
+ return navigator.clipboard
492
+ .writeText(str)
493
+ .then(() => {
494
+ return Promise.resolve()
495
+ })
496
+ .catch(e => {
497
+ const input = document.createElement('textarea');
498
+ input.setAttribute('readonly', 'readonly');
499
+ document.body.appendChild(input);
500
+ input.innerHTML = str;
501
+ input.select();
502
+ try {
503
+ let result = document.execCommand('copy')
504
+ document.body.removeChild(input);
505
+ if (!result || result === 'unsuccessful') {
506
+ return Promise.reject('复制文本失败!')
507
+ } else {
508
+ return Promise.resolve()
509
+ }
510
+ } catch (e) {
511
+ document.body.removeChild(input);
512
+ return Promise.reject(
513
+ '当前浏览器不支持复制功能,请检查更新或更换其他浏览器操作!'
514
+ )
515
+ }
516
+ })
517
+ }
518
+
519
+ // 工具类:返回时间间隔
520
+ fn.utilTimeAgo = (dateTimeStamp) => {
521
+ const minute = 1e3 * 60, hour = minute * 60, day = hour * 24, week = day * 7, month = day * 30;
522
+ const now = new Date().getTime();
523
+ const diffValue = now - dateTimeStamp;
524
+ const minC = diffValue / minute,
525
+ hourC = diffValue / hour,
526
+ dayC = diffValue / day,
527
+ weekC = diffValue / week,
528
+ monthC = diffValue / month;
529
+ if (diffValue < 0) {
530
+ result = ""
531
+ } else if (monthC >= 1 && monthC < 7) {
532
+ result = " " + parseInt(monthC) + " 月前"
533
+ } else if (weekC >= 1 && weekC < 4) {
534
+ result = " " + parseInt(weekC) + " 周前"
535
+ } else if (dayC >= 1 && dayC < 7) {
536
+ result = " " + parseInt(dayC) + " 天前"
537
+ } else if (hourC >= 1 && hourC < 24) {
538
+ result = " " + parseInt(hourC) + " 小时前"
539
+ } else if (minC >= 1 && minC < 60) {
540
+ result = " " + parseInt(minC) + " 分钟前"
541
+ } else if (diffValue >= 0 && diffValue <= minute) {
542
+ result = "刚刚"
543
+ } else {
544
+ const datetime = new Date();
545
+ datetime.setTime(dateTimeStamp);
546
+ const Nyear = datetime.getFullYear();
547
+ const Nmonth = datetime.getMonth() + 1 < 10 ? "0" + (datetime.getMonth() + 1) : datetime.getMonth() + 1;
548
+ const Ndate = datetime.getDate() < 10 ? "0" + datetime.getDate() : datetime.getDate();
549
+ const Nhour = datetime.getHours() < 10 ? "0" + datetime.getHours() : datetime.getHours();
550
+ const Nminute = datetime.getMinutes() < 10 ? "0" + datetime.getMinutes() : datetime.getMinutes();
551
+ const Nsecond = datetime.getSeconds() < 10 ? "0" + datetime.getSeconds() : datetime.getSeconds();
552
+ result = Nyear + "-" + Nmonth + "-" + Ndate
553
+ }
554
+ return result;
555
+ }
556
+
557
+ // 消息提示:标准
558
+ fn.message = (title, message, option = {}, done = null) => {
559
+ if (typeof iziToast === "undefined") {
560
+ volantis.css(volantis.GLOBAL_CONFIG.plugins.message.css)
561
+ volantis.js(volantis.GLOBAL_CONFIG.plugins.message.js, () => {
562
+ tozashMessage(title, message, option, done);
563
+ });
564
+ } else {
565
+ tozashMessage(title, message, option, done);
566
+ }
567
+ function tozashMessage(title, message, option, done) {
568
+ const {
569
+ icon,
570
+ time,
571
+ position,
572
+ transitionIn,
573
+ transitionOut,
574
+ messageColor,
575
+ titleColor,
576
+ backgroundColor,
577
+ zindex,
578
+ displayMode
579
+ } = option;
580
+ iziToast.show({
581
+ layout: '2',
582
+ icon: 'Fontawesome',
583
+ closeOnEscape: 'true',
584
+ displayMode: displayMode || 'replace',
585
+ transitionIn: transitionIn || volantis.GLOBAL_CONFIG.plugins.message.transitionIn,
586
+ transitionOut: transitionOut || volantis.GLOBAL_CONFIG.plugins.message.transitionOut,
587
+ messageColor: messageColor || volantis.GLOBAL_CONFIG.plugins.message.messageColor,
588
+ titleColor: titleColor || volantis.GLOBAL_CONFIG.plugins.message.titleColor,
589
+ backgroundColor: backgroundColor || volantis.GLOBAL_CONFIG.plugins.message.backgroundColor,
590
+ zindex: zindex || volantis.GLOBAL_CONFIG.plugins.message.zindex,
591
+ icon: icon || volantis.GLOBAL_CONFIG.plugins.message.icon.default,
592
+ timeout: time || volantis.GLOBAL_CONFIG.plugins.message.time.default,
593
+ position: position || volantis.GLOBAL_CONFIG.plugins.message.position,
594
+ title: title,
595
+ message: message,
596
+ onClosed: () => {
597
+ if (done) done();
598
+ },
599
+ });
600
+ }
601
+ }
602
+
603
+ // 消息提示:询问
604
+ fn.question = (title, message, option = {}, success = null, cancel = null, done = null) => {
605
+ if (typeof iziToast === "undefined") {
606
+ volantis.css(volantis.GLOBAL_CONFIG.plugins.message.css)
607
+ volantis.js(volantis.GLOBAL_CONFIG.plugins.message.js, () => {
608
+ tozashQuestion(title, message, option, success, cancel, done);
609
+ });
610
+ } else {
611
+ tozashQuestion(title, message, option, success, cancel, done);
612
+ }
613
+
614
+ function tozashQuestion(title, message, option, success, cancel, done) {
615
+ const {
616
+ icon,
617
+ time,
618
+ position,
619
+ transitionIn,
620
+ transitionOut,
621
+ messageColor,
622
+ titleColor,
623
+ backgroundColor,
624
+ zindex
625
+ } = option;
626
+ iziToast.question({
627
+ id: 'question',
628
+ icon: 'Fontawesome',
629
+ close: false,
630
+ overlay: true,
631
+ displayMode: 'once',
632
+ position: 'center',
633
+ messageColor: messageColor || volantis.GLOBAL_CONFIG.plugins.message.messageColor,
634
+ titleColor: titleColor || volantis.GLOBAL_CONFIG.plugins.message.titleColor,
635
+ backgroundColor: backgroundColor || volantis.GLOBAL_CONFIG.plugins.message.backgroundColor,
636
+ zindex: zindex || volantis.GLOBAL_CONFIG.plugins.message.zindex,
637
+ icon: icon || volantis.GLOBAL_CONFIG.plugins.message.icon.quection,
638
+ timeout: time || volantis.GLOBAL_CONFIG.plugins.message.time.quection,
639
+ title: title,
640
+ message: message,
641
+ buttons: [
642
+ ['<button><b>是</b></button>', (instance, toast) => {
643
+ instance.hide({ transitionOut: transitionOut || 'fadeOut' }, toast, 'button');
644
+ if (success) success(instance, toast)
645
+ }],
646
+ ['<button><b>否</b></button>', (instance, toast) => {
647
+ instance.hide({ transitionOut: transitionOut || 'fadeOut' }, toast, 'button');
648
+ if (cancel) cancel(instance, toast)
649
+ }]
650
+ ],
651
+ onClosed: (instance, toast, closedBy) => {
652
+ if (done) done(instance, toast, closedBy);
653
+ }
654
+ });
655
+ }
656
+ }
657
+
658
+ // 消息提示:隐藏
659
+ fn.hideMessage = (done = null) => {
660
+ const toast = document.querySelector('.iziToast');
661
+ if (!toast) {
662
+ if (done) done()
663
+ return;
664
+ }
665
+
666
+ if (typeof iziToast === "undefined") {
667
+ volantis.css(volantis.GLOBAL_CONFIG.plugins.message.css)
668
+ volantis.js(volantis.GLOBAL_CONFIG.plugins.message.js, () => {
669
+ hideMessage(done);
670
+ });
671
+ } else {
672
+ hideMessage(done);
673
+ }
674
+
675
+ function hideMessage(done) {
676
+ iziToast.hide({}, toast);
677
+ if (done) done();
678
+ }
679
+ }
680
+
681
+ // 消息提示:复制
682
+ let messageCopyrightShow = 0;
683
+ fn.messageCopyright = () => {
684
+ // 消息提示 复制时弹出
685
+ if (volantis.GLOBAL_CONFIG.plugins.message.enable
686
+ && volantis.GLOBAL_CONFIG.plugins.message.copyright.enable
687
+ && messageCopyrightShow < 1) {
688
+ messageCopyrightShow++;
689
+ VolantisApp.message(volantis.GLOBAL_CONFIG.plugins.message.copyright.title,
690
+ volantis.GLOBAL_CONFIG.plugins.message.copyright.message, {
691
+ icon: volantis.GLOBAL_CONFIG.plugins.message.copyright.icon,
692
+ transitionIn: 'flipInX',
693
+ transitionOut: 'flipOutX',
694
+ displayMode: 1
695
+ });
696
+ }
697
+ }
698
+
699
+ return {
700
+ init: () => {
701
+ fn.init();
702
+ fn.event();
703
+ },
704
+ subscribe: () => {
705
+ fn.setIsMobile();
706
+ fn.setHeader();
707
+ fn.setHeaderMenuSelection();
708
+ fn.setGlobalHeaderMenuEvent();
709
+ fn.setHeaderSearch();
710
+ fn.setScrollAnchor();
711
+ fn.setTabs();
712
+ fn.footnotes();
713
+ },
714
+ pjaxReload: () => {
715
+ fn.event();
716
+ fn.restData();
717
+ fn.setHeader();
718
+ fn.setHeaderMenuSelection();
719
+ fn.setPageHeaderMenuEvent();
720
+ fn.setScrollAnchor();
721
+ fn.setTabs();
722
+ fn.footnotes();
723
+
724
+ // 移除小尾巴的移除
725
+ document.querySelector("#l_header .nav-main").querySelectorAll('.list-v:not(.menu-phone)').forEach(function (e) {
726
+ e.removeAttribute("style")
727
+ })
728
+ document.querySelector("#l_header .menu-phone.list-v").removeAttribute("style");
729
+ messageCopyrightShow = 0;
730
+ },
731
+ utilCopyCode: fn.utilCopyCode,
732
+ utilWriteClipText: fn.utilWriteClipText,
733
+ utilTimeAgo: fn.utilTimeAgo,
734
+ message: fn.message,
735
+ question: fn.question,
736
+ hideMessage: fn.hideMessage,
737
+ messageCopyright: fn.messageCopyright,
738
+ scrolltoElement: fn.scrolltoElement
739
+ }
740
+ })()
741
+ Object.freeze(VolantisApp);
742
+
743
+ /* FancyBox */
744
+ const VolantisFancyBox = (() => {
745
+ const fn = {};
746
+
747
+ fn.loadFancyBox = (done) => {
748
+ volantis.css(volantis.GLOBAL_CONFIG.plugins.fancybox.css);
749
+ volantis.js(volantis.GLOBAL_CONFIG.plugins.fancybox.js).then(() => {
750
+ if (done) done();
751
+ })
752
+ }
753
+
754
+ /**
755
+ * 加载及处理
756
+ *
757
+ * @param {*} checkMain 是否只处理文章区域的文章
758
+ * @param {*} done FancyBox 加载完成后的动作,默认执行分组绑定
759
+ * @returns
760
+ */
761
+ fn.init = (checkMain = true, done = fn.groupBind) => {
762
+ if (!document.querySelector(".md .gallery img, .fancybox") && checkMain) return;
763
+ if (typeof Fancybox === "undefined") {
764
+ fn.loadFancyBox(done);
765
+ } else {
766
+ done();
767
+ }
768
+ }
769
+
770
+ /**
771
+ * 图片元素预处理
772
+ *
773
+ * @param {*} selectors 选择器
774
+ * @param {*} name 分组
775
+ */
776
+ fn.elementHandling = (selectors, name) => {
777
+ const nodeList = document.querySelectorAll(selectors);
778
+ nodeList.forEach($item => {
779
+ if ($item.hasAttribute('fancybox')) return;
780
+ $item.setAttribute('fancybox', '');
781
+ const $link = document.createElement('a');
782
+ $link.setAttribute('href', $item.src);
783
+ $link.setAttribute('data-caption', $item.alt);
784
+ $link.setAttribute('data-fancybox', name);
785
+ $link.classList.add('fancybox');
786
+ $link.append($item.cloneNode());
787
+ $item.replaceWith($link);
788
+ })
789
+ }
790
+
791
+ /**
792
+ * 原生绑定
793
+ *
794
+ * @param {*} selectors 选择器
795
+ */
796
+ fn.bind = (selectors) => {
797
+ fn.init(false, () => {
798
+ Fancybox.bind(selectors, {
799
+ groupAll: true,
800
+ Hash: false,
801
+ hideScrollbar: false,
802
+ Thumbs: {
803
+ autoStart: false,
804
+ },
805
+ caption: function (fancybox, carousel, slide) {
806
+ return slide.$trigger.alt || null
807
+ }
808
+ });
809
+ });
810
+ }
811
+
812
+ /**
813
+ * 分组绑定
814
+ *
815
+ * @param {*} groupName 分组名称
816
+ */
817
+ fn.groupBind = (groupName = null) => {
818
+ const group = new Set();
819
+
820
+ document.querySelectorAll(".gallery").forEach(ele => {
821
+ if (ele.querySelector("img")) {
822
+ group.add(ele.getAttribute('data-group') || 'default');
823
+ }
824
+ })
825
+
826
+ if (!!groupName) group.add(groupName);
827
+
828
+ for (const iterator of group) {
829
+ Fancybox.unbind('[data-fancybox="' + iterator + '"]');
830
+ Fancybox.bind('[data-fancybox="' + iterator + '"]', {
831
+ Hash: false,
832
+ hideScrollbar: false,
833
+ Thumbs: {
834
+ autoStart: false,
835
+ }
836
+ });
837
+ }
838
+ }
839
+
840
+ return {
841
+ init: fn.init,
842
+ bind: fn.bind,
843
+ groupBind: (selectors, groupName = 'default') => {
844
+ try {
845
+ fn.elementHandling(selectors, groupName);
846
+ fn.init(false, () => {
847
+ fn.groupBind(groupName)
848
+ });
849
+ } catch (error) {
850
+ console.error(error)
851
+ }
852
+ }
853
+ }
854
+ })()
855
+ Object.freeze(VolantisFancyBox);
856
+
857
+ // highlightKeyWords 与 搜索功能搭配 https://github.com/next-theme/hexo-theme-next/blob/eb194a7258058302baf59f02d4b80b6655338b01/source/js/third-party/search/local-search.js
858
+ // Question: 锚点稳定性未知
859
+ // ToDo: 查找模式
860
+ // 0. (/////////要知道浏览器自带全页面查找功能 CTRL + F)
861
+ // 1. 右键开启查找模式 / 导航栏菜单开启?? / CTRL + F ???
862
+ // 2. 查找模式面板 (可拖动? or 固定?)
863
+ // 3. keyword mark id 从 0 开始编号 查找下一处 highlightKeyWords.scrollToNextHighlightKeywordMark() 查找上一处 scrollToPrevHighlightKeywordMark() 循环查找(取模%)
864
+ // 4. 可输入修改 查找关键词 keywords(type:list)
865
+ // 5. 区分大小写 caseSensitive (/ 全字匹配?? / 正则匹配??)
866
+ // 6. 在选定区域中查找 querySelector ??
867
+ // 7. 关闭查找模式
868
+ // 8. 搜索跳转 (URL 入口) 自动开启查找模式 调用 scrollToNextHighlightKeywordMark()
869
+ const highlightKeyWords = (() => {
870
+ let fn = {}
871
+ fn.markNum = 0
872
+ fn.markNextId = -1
873
+ fn.startFromURL = () => {
874
+ const params = decodeURI(new URL(location.href).searchParams.get('keyword'));
875
+ const keywords = params ? params.split(' ') : [];
876
+ const post = document.querySelector('#l_main');
877
+ if (keywords.length == 1 && keywords[0] == "null") {
878
+ return;
879
+ }
880
+ fn.start(keywords, post); // 渲染耗时较长
881
+ fn.scrollToFirstHighlightKeywordMark()
882
+ }
883
+ fn.scrollToFirstHighlightKeywordMark = () => {
884
+ volantis.cleanContentVisibility();
885
+ let target = fn.scrollToNextHighlightKeywordMark("0");
886
+ if (!target) {
887
+ volantis.requestAnimationFrame(fn.scrollToFirstHighlightKeywordMark)
888
+ }
889
+ }
890
+ fn.scrollToNextHighlightKeywordMark = (id) => {
891
+ // Next Id
892
+ let input = id || (fn.markNextId + 1) % fn.markNum;
893
+ fn.markNextId = parseInt(input)
894
+ let target = document.getElementById("keyword-mark-" + fn.markNextId);
895
+ if (!target) {
896
+ fn.markNextId = (fn.markNextId + 1) % fn.markNum;
897
+ target = document.getElementById("keyword-mark-" + fn.markNextId);
898
+ }
899
+ if (target) {
900
+ volantis.scroll.to(target, { addTop: - volantis.dom.header.offsetHeight - 5, behavior: 'instant' })
901
+ }
902
+ // Current target
903
+ return target
904
+ }
905
+ fn.scrollToPrevHighlightKeywordMark = (id) => {
906
+ // Prev Id
907
+ let input = id || (fn.markNextId - 1 + fn.markNum) % fn.markNum;
908
+ fn.markNextId = parseInt(input)
909
+ let target = document.getElementById("keyword-mark-" + fn.markNextId);
910
+ if (!target) {
911
+ fn.markNextId = (fn.markNextId - 1 + fn.markNum) % fn.markNum;
912
+ target = document.getElementById("keyword-mark-" + fn.markNextId);
913
+ }
914
+ if (target) {
915
+ volantis.scroll.to(target, { addTop: - volantis.dom.header.offsetHeight - 5, behavior: 'instant' })
916
+ }
917
+ // Current target
918
+ return target
919
+ }
920
+ fn.start = (keywords, querySelector) => {
921
+ fn.markNum = 0
922
+ if (!keywords.length || !querySelector || (keywords.length == 1 && keywords[0] == "null")) return;
923
+ console.log(keywords);
924
+ const walk = document.createTreeWalker(querySelector, NodeFilter.SHOW_TEXT, null);
925
+ const allNodes = [];
926
+ while (walk.nextNode()) {
927
+ if (!walk.currentNode.parentNode.matches('button, select, textarea')) allNodes.push(walk.currentNode);
928
+ }
929
+ allNodes.forEach(node => {
930
+ const [indexOfNode] = fn.getIndexByWord(keywords, node.nodeValue);
931
+ if (!indexOfNode.length) return;
932
+ const slice = fn.mergeIntoSlice(0, node.nodeValue.length, indexOfNode);
933
+ fn.highlightText(node, slice, 'keyword');
934
+ fn.highlightStyle()
935
+ });
936
+ }
937
+ fn.getIndexByWord = (words, text, caseSensitive = false) => {
938
+ const index = [];
939
+ const included = new Set();
940
+ words.forEach(word => {
941
+ const div = document.createElement('div');
942
+ div.innerText = word;
943
+ word = div.innerHTML;
944
+
945
+ const wordLen = word.length;
946
+ if (wordLen === 0) return;
947
+ let startPosition = 0;
948
+ let position = -1;
949
+ if (!caseSensitive) {
950
+ text = text.toLowerCase();
951
+ word = word.toLowerCase();
952
+ }
953
+ while ((position = text.indexOf(word, startPosition)) > -1) {
954
+ index.push({ position, word });
955
+ included.add(word);
956
+ startPosition = position + wordLen;
957
+ }
958
+ });
959
+ index.sort((left, right) => {
960
+ if (left.position !== right.position) {
961
+ return left.position - right.position;
962
+ }
963
+ return right.word.length - left.word.length;
964
+ });
965
+ return [index, included];
966
+ };
967
+ fn.mergeIntoSlice = (start, end, index) => {
968
+ let item = index[0];
969
+ let { position, word } = item;
970
+ const hits = [];
971
+ const count = new Set();
972
+ while (position + word.length <= end && index.length !== 0) {
973
+ count.add(word);
974
+ hits.push({
975
+ position,
976
+ length: word.length
977
+ });
978
+ const wordEnd = position + word.length;
979
+
980
+ index.shift();
981
+ while (index.length !== 0) {
982
+ item = index[0];
983
+ position = item.position;
984
+ word = item.word;
985
+ if (wordEnd > position) {
986
+ index.shift();
987
+ } else {
988
+ break;
989
+ }
990
+ }
991
+ }
992
+ return {
993
+ hits,
994
+ start,
995
+ end,
996
+ count: count.size
997
+ };
998
+ };
999
+ fn.highlightText = (node, slice, className) => {
1000
+ const val = node.nodeValue;
1001
+ let index = slice.start;
1002
+ const children = [];
1003
+ for (const { position, length } of slice.hits) {
1004
+ const text = document.createTextNode(val.substring(index, position));
1005
+ index = position + length;
1006
+ let mark = document.createElement('mark');
1007
+ mark.className = className;
1008
+ mark = fn.highlightStyle(mark)
1009
+ mark.appendChild(document.createTextNode(val.substr(position, length)));
1010
+ children.push(text, mark);
1011
+ }
1012
+ node.nodeValue = val.substring(index, slice.end);
1013
+ children.forEach(element => {
1014
+ node.parentNode.insertBefore(element, node);
1015
+ });
1016
+ }
1017
+ fn.highlightStyle = (mark) => {
1018
+ if (!mark) return;
1019
+ mark.id = "keyword-mark-" + fn.markNum;
1020
+ fn.markNum++;
1021
+ mark.style.background = "transparent";
1022
+ mark.style["border-bottom"] = "1px dashed #ff2a2a";
1023
+ mark.style["color"] = "#ff2a2a";
1024
+ mark.style["font-weight"] = "bold";
1025
+ return mark
1026
+ }
1027
+ fn.cleanHighlightStyle = () => {
1028
+ document.querySelectorAll(".keyword").forEach(mark => {
1029
+ mark.style.background = "transparent";
1030
+ mark.style["border-bottom"] = null;
1031
+ mark.style["color"] = null;
1032
+ mark.style["font-weight"] = null;
1033
+ })
1034
+ }
1035
+ return {
1036
+ start: (keywords, querySelector) => {
1037
+ fn.start(keywords, querySelector)
1038
+ },
1039
+ startFromURL: () => {
1040
+ fn.startFromURL()
1041
+ },
1042
+ scrollToNextHighlightKeywordMark: (id) => {
1043
+ fn.scrollToNextHighlightKeywordMark(id)
1044
+ },
1045
+ scrollToPrevHighlightKeywordMark: (id) => {
1046
+ fn.scrollToPrevHighlightKeywordMark(id)
1047
+ },
1048
+ cleanHighlightStyle: () => {
1049
+ fn.cleanHighlightStyle()
1050
+ },
1051
+ }
1052
+ })()
1053
+ Object.freeze(highlightKeyWords);
1054
+
1055
+ /* DOM 控制 */
1056
+ const DOMController = {
1057
+ /**
1058
+ * 控制元素显隐
1059
+ */
1060
+ visible: (ele, type = true) => {
1061
+ if (ele) ele.style.display = type === true ? 'block' : 'none';
1062
+ },
1063
+
1064
+ /**
1065
+ * 移除元素
1066
+ */
1067
+ remove: (param) => {
1068
+ const node = document.querySelectorAll(param);
1069
+ node.forEach(ele => {
1070
+ ele.remove();
1071
+ })
1072
+ },
1073
+
1074
+ removeList: (list) => {
1075
+ list.forEach(param => {
1076
+ DOMController.remove(param)
1077
+ })
1078
+ },
1079
+
1080
+ /**
1081
+ * 设置属性
1082
+ */
1083
+ setAttribute: (param, attrName, attrValue) => {
1084
+ const node = document.querySelectorAll(param);
1085
+ node.forEach(ele => {
1086
+ ele.setAttribute(attrName, attrValue)
1087
+ })
1088
+ },
1089
+
1090
+ setAttributeList: (list) => {
1091
+ list.forEach(item => {
1092
+ DOMController.setAttribute(item[0], item[1], item[2])
1093
+ })
1094
+ },
1095
+
1096
+ /**
1097
+ * 设置样式
1098
+ */
1099
+ setStyle: (param, styleName, styleValue) => {
1100
+ const node = document.querySelectorAll(param);
1101
+ node.forEach(ele => {
1102
+ ele.style[styleName] = styleValue;
1103
+ })
1104
+ },
1105
+
1106
+ setStyleList: (list) => {
1107
+ list.forEach(item => {
1108
+ DOMController.setStyle(item[0], item[1], item[2])
1109
+ })
1110
+ },
1111
+
1112
+ fadeIn: (e) => {
1113
+ if (!e) return;
1114
+ e.style.visibility = "visible";
1115
+ e.style.opacity = 1;
1116
+ e.style.display = "block";
1117
+ e.style.transition = "all 0.5s linear";
1118
+ return e
1119
+ },
1120
+
1121
+ fadeOut: (e) => {
1122
+ if (!e) return;
1123
+ e.style.visibility = "hidden";
1124
+ e.style.opacity = 0;
1125
+ e.style.display = "none";
1126
+ e.style.transition = "all 0.5s linear";
1127
+ return e
1128
+ },
1129
+
1130
+ fadeToggle: (e) => {
1131
+ if (!e) return;
1132
+ if (e.style.visibility == "hidden") {
1133
+ e = DOMController.fadeIn(e)
1134
+ } else {
1135
+ e = DOMController.fadeOut(e)
1136
+ }
1137
+ return e
1138
+ },
1139
+
1140
+ fadeToggleList: (list) => {
1141
+ list.forEach(param => {
1142
+ DOMController.fadeToggle(param)
1143
+ })
1144
+ },
1145
+
1146
+ hasClass: (e, c) => {
1147
+ if (!e) return;
1148
+ return e.className.match(new RegExp('(\\s|^)' + c + '(\\s|$)'));
1149
+ },
1150
+
1151
+ addClass: (e, c) => {
1152
+ if (!e) return;
1153
+ e.classList.add(c);
1154
+ return e
1155
+ },
1156
+
1157
+ removeClass: (e, c) => {
1158
+ if (!e) return;
1159
+ e.classList.remove(c);
1160
+ return e
1161
+ },
1162
+
1163
+ toggleClass: (e, c) => {
1164
+ if (!e) return;
1165
+ if (DOMController.hasClass(e, c)) {
1166
+ DOMController.removeClass(e, c)
1167
+ } else {
1168
+ DOMController.addClass(e, c)
1169
+ }
1170
+ return e
1171
+ },
1172
+
1173
+ toggleClassList: (list) => {
1174
+ list.forEach(item => {
1175
+ DOMController.toggleClass(item[0], item[1])
1176
+ })
1177
+ }
1178
+ }
1179
+ Object.freeze(DOMController);
1180
+
1181
+ const VolantisRequest = {
1182
+ timeoutFetch: (url, ms, requestInit) => {
1183
+ const controller = new AbortController()
1184
+ requestInit.signal?.addEventListener('abort', () => controller.abort())
1185
+ let promise = fetch(url, { ...requestInit, signal: controller.signal })
1186
+ if (ms > 0) {
1187
+ const timer = setTimeout(() => controller.abort(), ms)
1188
+ promise.finally(() => { clearTimeout(timer) })
1189
+ }
1190
+ promise = promise.catch((err) => {
1191
+ throw ((err || {}).name === 'AbortError') ? new Error(`Fetch timeout: ${url}`) : err
1192
+ })
1193
+ return promise
1194
+ },
1195
+
1196
+ Fetch: async (url, requestInit, timeout = 15000) => {
1197
+ const resp = await VolantisRequest.timeoutFetch(url, timeout, requestInit);
1198
+ if (!resp.ok) throw new Error(`Fetch error: ${url} | ${resp.status}`);
1199
+ let json = await resp.json()
1200
+ if (!json.success) throw json
1201
+ return json
1202
+ },
1203
+
1204
+ POST: async (url, data) => {
1205
+ const requestInit = {
1206
+ method: 'POST',
1207
+ }
1208
+ if (data) {
1209
+ const formData = new FormData();
1210
+ Object.keys(data).forEach(key => formData.append(key, String(data[key])))
1211
+ requestInit.body = formData;
1212
+ }
1213
+ const json = await VolantisRequest.Fetch(url, requestInit)
1214
+ return json.data;
1215
+ },
1216
+
1217
+ Get: async (url, data) => {
1218
+ const json = await VolantisRequest.Fetch(url + (data ? (`?${new URLSearchParams(data)}`) : ''), {
1219
+ method: 'GET'
1220
+ })
1221
+ }
1222
+ }
1223
+ Object.freeze(VolantisRequest);