1var CONFIG = {"version":"0.2.5","hostname":"https://yhuanhuan01.github.io","root":"/","statics":"/","favicon":{"normal":"images/favicon.ico","hidden":"images/failure.ico"},"darkmode":true,"auto_scroll":false,"js":{"valine":"gh/amehime/[email protected]/dist/MiniValine.min.js","chart":"npm/[email protected]/dist/frappe-charts.min.iife.min.js","copy_tex":"npm/[email protected]/dist/contrib/copy-tex.min.js","fancybox":"combine/npm/[email protected]/dist/jquery.min.js,npm/@fancyapps/[email protected]/dist/jquery.fancybox.min.js,npm/[email protected]/dist/js/jquery.justifiedGallery.min.js"},"css":{"valine":"css/comment.css","katex":"npm/[email protected]/dist/katex.min.css","mermaid":"css/mermaid.css","fancybox":"combine/npm/@fancyapps/[email protected]/dist/jquery.fancybox.min.css,npm/[email protected]/dist/css/justifiedGallery.min.css"},"loader":{"start":true,"switch":true},"search":null,"valine":{"appId":"flsOZ3PfHqRDqTbt7BCWqEc6-gzGzoHsz","appKey":"8VlaE0vIPnXadBDvKK6ShFyD","placeholder":"妿 æ¢å¢ã½(â´â`)ï¾âª","avatar":"mp","pageSize":10,"lang":"zh-CN","visitor":true,"NoRecordIP":false,"serverURLs":null,"powerMode":true,"tagMeta":{"visitor":"æ°æå","master":"主人","friend":"å°ä¼ä¼´","investor":"é主ç²ç²"},"tagColor":{"master":"var(--color-orange)","friend":"var(--color-aqua)","investor":"var(--color-pink)"},"tagMember":{"master":null,"friend":null,"investor":null}},"quicklink":{"timeout":3000,"priority":true},"audio":[{"title":"å表1","list":["https://music.163.com/#/my/m/music/playlist?id=2454234066","https://music.163.com/#/my/m/music/playlist?id=8161689066"]},{"title":"å表2","list":["https://music.163.com/#/my/m/music/playlist?id=7746881212"]}],"fireworks":["rgba(255,182,185,.9)","rgba(250,227,217,.9)","rgba(187,222,214,.9)","rgba(138,198,209,.9)"]};const getRndInteger = function (min, max) { 2 return Math.floor(Math.random() * (max - min + 1)) + min; 3} 4 5const getDocHeight = function () { 6 return $('main > .inner').offsetHeight; 7} 8 9const getScript = function(url, callback, condition) { 10 if (condition) { 11 callback(); 12 } else { 13 var script = document.createElement('script'); 14 script.onload = script.onreadystatechange = function(_, isAbort) { 15 if (isAbort || !script.readyState || /loaded|complete/.test(script.readyState)) { 16 script.onload = script.onreadystatechange = null; 17 script = undefined; 18 if (!isAbort && callback) setTimeout(callback, 0); 19 } 20 }; 21 script.src = url; 22 document.head.appendChild(script); 23 } 24} 25 26const assetUrl = function(asset, type) { 27 var str = CONFIG[asset][type] 28 if(str.indexOf('npm')>-1||str.indexOf('gh')>-1||str.indexOf('combine')>-1) 29 return "//cdn.jsdelivr.net/" + str 30 31 if(str.indexOf('http')>-1) 32 return str 33 34 return statics + str; 35} 36 37const vendorJs = function(type, callback, condition) { 38 if(LOCAL[type]) { 39 getScript(assetUrl("js", type), callback || function(){ 40 window[type] = true; 41 }, condition || window[type]); 42 } 43} 44 45const vendorCss = function(type, condition) { 46 if(window['css'+type]) 47 return; 48 49 if(LOCAL[type]) { 50 51 document.head.createChild('link', { 52 rel: 'stylesheet', 53 href: assetUrl("css", type) 54 }); 55 56 window['css'+type] = true; 57 } 58} 59 60const pjaxScript = function(element) { 61 var code = element.text || element.textContent || element.innerHTML || ''; 62 var parent = element.parentNode; 63 parent.removeChild(element); 64 var script = document.createElement('script'); 65 if (element.id) { 66 script.id = element.id; 67 } 68 if (element.className) { 69 script.className = element.className; 70 } 71 if (element.type) { 72 script.type = element.type; 73 } 74 if (element.src) { 75 script.src = element.src; 76 // Force synchronous loading of peripheral JS. 77 script.async = false; 78 } 79 if (element.dataset.pjax !== undefined) { 80 script.dataset.pjax = ''; 81 } 82 if (code !== '') { 83 script.appendChild(document.createTextNode(code)); 84 } 85 parent.appendChild(script); 86} 87 88const pageScroll = function(target, offset, complete) { 89 var opt = { 90 targets: typeof offset == 'number' ? target.parentNode : document.scrollingElement, 91 duration: 500, 92 easing: "easeInOutQuad", 93 scrollTop: offset || (typeof target == 'number' ? target : (target ? target.top() + document.documentElement.scrollTop - siteNavHe
93ight : 0)), 94 complete: function() { 95 complete && complete() 96 } 97 } 98 anime(opt); 99} 100 101const transition = function(target, type, complete) { 102 var animation = {} 103 var display = 'none' 104 switch(type) { 105 case 0: 106 animation = {opacity: [1, 0]} 107 break; 108 case 1: 109 animation = {opacity: [0, 1]} 110 display = 'block' 111 break; 112 case 'bounceUpIn': 113 animation = { 114 begin: function(anim) { 115 target.display('block') 116 }, 117 translateY: [ 118 { value: -60, duration: 200 }, 119 { value: 10, duration: 200 }, 120 { value: -5, duration: 200 }, 121 { value: 0, duration: 200 } 122 ], 123 opacity: [0, 1] 124 } 125 display = 'block' 126 break; 127 case 'shrinkIn': 128 animation = { 129 begin: function(anim) { 130 target.display('block') 131 }, 132 scale: [ 133 { value: 1.1, duration: 300 }, 134 { value: 1, duration: 200 } 135 ], 136 opacity: 1 137 } 138 display = 'block' 139 break; 140 case 'slideRightIn': 141 animation = { 142 begin: function(anim) { 143 target.display('block') 144 }, 145 translateX: [100, 0], 146 opacity: [0, 1] 147 } 148 display = 'block' 149 break; 150 case 'slideRightOut': 151 animation = { 152 translateX: [0, 100], 153 opacity: [1, 0] 154 } 155 break; 156 default: 157 animation = type 158 display = type.display 159 break; 160 } 161 anime(Object.assign({ 162 targets: target, 163 duration: 200, 164 easing: 'linear' 165 }, animation)).finished.then(function() { 166 target.display(display) 167 complete && complete() 168 }); 169} 170 171const store = { 172 get: function(item) { 173 return localStorage.getItem(item); 174 }, 175 set: function(item, str) { 176 localStorage.setItem(item, str); 177 return str; 178 }, 179 del: function(item) { 180 localStorage.removeItem(item); 181 } 182} 183const $ = function(selector, element) { 184 element = element || document; 185 if(selector.indexOf('#') === 0) { 186 return element.getElementById(selector.replace('#', '')) 187 } 188 return element.querySelector(selector) 189}; 190 191$.all = function(selector, element) { 192 element = element || document; 193 return element.querySelectorAll(selector) 194}; 195 196$.each = function(selector, callback, element) { 197 return $.all(selector, element).forEach(callback) 198} 199 200 201Object.assign(HTMLElement.prototype, { 202 createChild: function(tag, obj, positon) { 203 var child = document.createElement(tag); 204 Object.assign(child, obj) 205 switch(positon) { 206 case 'after': 207 this.insertAfter(child) 208 break; 209 case 'replace': 210 this.innerHTML = "" 211 default: 212 this.appendChild(child) 213 } 214 return child 215 }, 216 wrap: function (obj) { 217 var box = document.createElement('div'); 218 Object.assign(box, obj) 219 this.parentNode.insertBefore(box, this); 220 this.parentNode.removeChild(this); 221 box.appendChild(this); 222 }, 223 height: function(h) { 224 if(h) { 225 this.style.height = typeof h == 'number' ? h + 'rem' : h; 226 } 227 return this.getBoundingClientRect().height 228 }, 229 width: function(w) { 230 if(w) { 231 this.style.width = typeof w == 'number' ? w + 'rem' : w; 232 } 233 return this.getBoundingClientRect().width 234 }, 235 top: function() { 236 return this.getBoundingClientRect().top 237 }, 238 left:function() { 239 return this.getBoundingClientRect().left 240 }, 241 attr: function(type, value) { 242 if(value === null) { 243 return this.removeAttribute(type) 244 } 245 246 if(value) { 247 this.setAttribute(type, value) 248 return this 249 } else { 250 return this.getAttribute(type) 251 } 252 }, 253 insertAfter: function(element) { 254 var parent = this.parentNode; 255 if(parent.lastChild == this){ 256 parent.appendChild(element); 257 }else{ 258 parent.insertBefore(element, this.nextSibling); 259 } 260 }, 261 display: function(d) { 262 if(d == null) { 263 return this.style.display 264 } else { 265 this.style.display = d; 266 return this 267 } 268 }, 269 child: function(selector) { 270 return $(selector, this) 271 }, 272 find: function(selector) { 273 return $.all(selector, this) 274 }, 275 _class: function(type, className, display) { 276 var classNames = className.indexOf(' ') ? className.split(' ') : [className]; 277 var that = this;
278 classNames.forEach(function(name) { 279 if(type == 'toggle') { 280 that.classList.toggle(name, display) 281 } else { 282 that.classList[type](name) 283 } 284 }) 285 }, 286 addClass: function(className) { 287 this._class('add', className); 288 return this; 289 }, 290 removeClass: function(className) { 291 this._class('remove', className); 292 return this; 293 }, 294 toggleClass: function(className, display) { 295 this._class('toggle', className, display); 296 return this; 297 }, 298 hasClass: function(className) { 299 return this.classList.contains(className) 300 } 301}); 302var NOWPLAYING = null 303const isMobile = /mobile/i.test(window.navigator.userAgent); 304const mediaPlayer = function(t, config) { 305 var option = { 306 type: 'audio', 307 mode: 'random', 308 btns: ['play-pause', 'music'], 309 controls: ['mode', 'backward', 'play-pause', 'forward', 'volume'], 310 events: { 311 "play-pause": function(event) { 312 if(source.paused) { 313 t.player.play() 314 } else { 315 t.player.pause() 316 } 317 }, 318 "music": function(event) { 319 if(info.el.hasClass('show')) { 320 info.hide() 321 } else { 322 info.el.addClass('show'); 323 playlist.scroll().title() 324 } 325 } 326 } 327 }, utils = { 328 random: function(len) { 329 return Math.floor((Math.random()*len)) 330 }, 331 parse: function(link) { 332 var result = []; 333 [ 334 ['music.163.com.*song.*id=(\\d+)', 'netease', 'song'], 335 ['music.163.com.*album.*id=(\\d+)', 'netease', 'album'], 336 ['music.163.com.*artist.*id=(\\d+)', 'netease', 'artist'], 337 ['music.163.com.*playlist.*id=(\\d+)', 'netease', 'playlist'], 338 ['music.163.com.*discover/toplist.*id=(\\d+)', 'netease', 'playlist'], 339 ['y.qq.com.*song/(\\w+).html', 'tencent', 'song'], 340 ['y.qq.com.*album/(\\w+).html', 'tencent', 'album'], 341 ['y.qq.com.*singer/(\\w+).html', 'tencent', 'artist'], 342 ['y.qq.com.*playsquare/(\\w+).html', 'tencent', 'playlist'], 343 ['y.qq.com.*playlist/(\\w+).html', 'tencent', 'playlist'], 344 ['xiami.com.*song/(\\w+)', 'xiami', 'song'], 345 ['xiami.com.*album/(\\w+)', 'xiami', 'album'], 346 ['xiami.com.*artist/(\\w+)', 'xiami', 'artist'], 347 ['xiami.com.*collect/(\\w+)', 'xiami', 'playlist'], 348 ].forEach(function(rule) { 349 var patt = new RegExp(rule[0]) 350 var res = patt.exec(link) 351 if (res !== null) { 352 result = [rule[1], rule[2], res[1]] 353 } 354 }) 355 return result 356 }, 357 fetch: function(source) { 358 var list = [] 359 360 return new Promise(function(resolve, reject) { 361 source.forEach(function(raw) { 362 var meta = utils.parse(raw) 363 if(meta[0]) { 364 var skey = JSON.stringify(meta) 365 var playlist = store.get(skey) 366 if(playlist) { 367 list.push.apply(list, JSON.parse(playlist)); 368 resolve(list); 369 } else { 370 fetch('https://api.i-meto.com/meting/api?server='+meta[0]+'&type='+meta[1]+'&id='+meta[2]+'&r='+ Math.random()) 371 .then(function(response) { 372 return response.json() 373 }).then(function(json) { 374 store.set(skey, JSON.stringify(json)) 375 list.push.apply(list, json); 376 resolve(list); 377 }).catch(function(ex) {}) 378 } 379 } else { 380 list.push(raw); 381 resolve(list); 382 } 383 }) 384 }) 385 }, 386 secondToTime: function(second) { 387 var add0 = function(num) { 388 return isNaN(num) ? '00' : (num < 10 ? '0' + num : '' + num) 389 }; 390 var hour = Math.floor(second / 3600); 391 var min = Math.floor((second - hour * 3600) / 60); 392 var sec = Math.floor(second - hour * 3600 - min * 60); 393 return (hour > 0 ? [hour, min, sec] : [min, sec]).map(add0).join(':'); 394 }, 395 nameMap: { 396 dragStart: isMobile ? 'touchstart' : 'mousedown', 397 dragMove: isMobile ? 'touchmove' : 'mousemove', 398 dragEnd: isMobile ? 'touchend' : 'mouseup', 399 } 400 }, source = null; 401 402 t.player = { 403 _id: utils.random(999999), 404 group: true, 405 // å è½½ææ¾å表 406 load: function(newList) { 407 var d = "" 408 var that = this 409 410 if(newList && newList.length > 0) { 411 if(this.options.rawList !== newList) { 412 this.options.rawList = newList; 413 playlist.clear() 414 // è·åæ°å表 415 //this.fetch() 416 } 417 } else { 418 // 没æå表æ¶ï¼éèæé® 419 d = "none" 420 this.pause() 421 } 422 for(var el in buttons.el) { 423 buttons.el[el].display(d) 424 } 425 return this 426 }, 427 fetch: function () { 428 var that = this; 429 return new Promise(function(resolve, reject) { 430 if(playlist.data.length > 0) { 431 resolve() 432 } else { 433 if(that.options.rawList) { 434 var promises = []; 435 436 that.options.rawList.forEach(function(raw, index) { 437 promises.push(new Promise(function(resolve, reject) { 438 var group = index 439 var source 440 if(!raw.list) { 441 group = 0 442 that.group = false 443 source = [raw] 444 } else { 445 that.group = true 446 source = raw.list 447 } 448 utils.fetch(source).then(function(list) { 449 playlist.add(group, list) 450 resolve() 451 }) 452 })) 453 }) 454 455 Promise.all(promises).then(function() { 456 resolve(true) 457 }) 458 } 459 } 460 }).then(function(c) { 461 if(c) { 462 playlist.create() 463 controller.create() 464 that.mode() 465 } 466 }) 467 }, 468 // æ ¹æ®æ¨¡å¼åæ¢å½åæ²ç®index 469 mode: function() { 470 var total = playlist.data.length; 471 472 if(!total || playlist.errnum == total) 473 return; 474 475 var step = controller.step == 'next' ? 1 : -1 476 477 var next = function() { 478 var index = playlist.index + step 479 if(index > total || index < 0) { 480 index = controller.step == 'next' ? 0 : total-1; 481 } 482 playlist.index = index; 483 } 484 485 var random = function() { 486 var p = utils.random(total) 487 if(playlist.index !== p) { 488 playlist.index = p 489 } else { 490 next() 491 } 492 } 493 494 switch (this.options.mode) { 495 case 'random': 496 random() 497 break; 498 case 'order': 499 next() 500 break; 501 case 'loop': 502 if(controller.step) 503 next() 504 505 if(playlist.index == -1) 506 random() 507 break; 508 } 509 510 this.init() 511 }, 512 // ç´æ¥è®¾ç½®å½åæ²ç®index 513 switch: function(index) { 514 if(typeof index == 'number' 515 && index != playlist.index 516 && playlist.current() 517 && !playlist.current().error) { 518 playlist.index = index; 519 this.init() 520 } 521 }, 522 // æ´æ°source为å½åæ²ç®index 523 init: function() { 524 var item = playlist.current() 525 526 if(!item || item['error']) { 527 this.mode(); 528 return; 529 } 530 531 var playing = false;
532 if(!source.paused) { 533 playing = true 534 this.stop() 535 } 536 537 source.attr('src', item.url); 538 source.attr('title', item.name + ' - ' + item.artist); 539 this.volume(store.get('_PlayerVolume') || '0.7') 540 this.muted(store.get('_PlayerMuted')) 541 542 progress.create() 543 544 if(this.options.type == 'audio') 545 preview.create() 546 547 if(playing == true) { 548 this.play() 549 } 550 }, 551 play: function() { 552 NOWPLAYING && NOWPLAYING.player.pause() 553 554 if(playlist.current().error) { 555 this.mode(); 556 return; 557 } 558 var that = this 559 source.play().then(function() { 560 playlist.scroll() 561 }).catch(function(e) {}); 562 }, 563 pause: function() { 564 source.pause() 565 document.title = originTitle 566 }, 567 stop: function() { 568 source.pause(); 569 source.currentTime = 0; 570 document.title = originTitle; 571 }, 572 seek: function(time) { 573 time = Math.max(time, 0) 574 time = Math.min(time, source.duration) 575 source.currentTime = time; 576 progress.update(time / source.duration) 577 }, 578 muted: function(status) { 579 if(status == 'muted') { 580 source.muted = status 581 store.set('_PlayerMuted', status) 582 controller.update(0) 583 } else { 584 store.del('_PlayerMuted') 585 source.muted = false 586 controller.update(source.volume) 587 } 588 }, 589 volume: function(percentage) { 590 if (!isNaN(percentage)) { 591 controller.update(percentage) 592 store.set('_PlayerVolume', percentage) 593 source.volume = percentage 594 } 595 }, 596 mini: function() { 597 info.hide() 598 } 599 }; 600 601 var info = { 602 el: null, 603 create: function() { 604 if(this.el) 605 return; 606 607 this.el = t.createChild('div', { 608 className: 'player-info', 609 innerHTML: (t.player.options.type == 'audio' ? '<div class="preview"></div>' : '') + '<div class="controller"></div><div class="playlist"></div>' 610 }, 'after'); 611 612 preview.el = this.el.child(".preview"); 613 playlist.el = this.el.child(".playlist"); 614 controller.el = this.el.child(".controller"); 615 }, 616 hide: function() { 617 var el = this.el 618 el.addClass('hide'); 619 window.setTimeout(function() { 620 el.removeClass('show hide') 621 }, 300); 622 } 623 } 624 625 var playlist = { 626 el: null, 627 data: [], 628 index: -1, 629 errnum: 0, 630 add: function(group, list) { 631 var that = this 632 list.forEach(function(item, i) { 633 item.group = group; 634 item.name = item.name || item.title || 'Meida name'; 635 item.artist = item.artist || item.author || 'Anonymous'; 636 item.cover = item.cover || item.pic; 637 item.type = item.type || 'normal'; 638 639 that.data.push(item); 640 }); 641 }, 642 clear: function() { 643 this.data = [] 644 this.el.innerHTML = "" 645 646 if(this.index !== -1) { 647 this.index = -1 648 t.player.fetch() 649 } 650 }, 651 create: function() { 652 var el = this.el 653 654 this.data.map(function(item, index) { 655 if(item.el) 656 return 657 658 var id = 'list-' + t.player._id + '-'+item.group 659 var tab = $('#' + id) 660 if(!tab) { 661 tab = el.createChild('div', { 662 id: id, 663 className: t.player.group ?'tab':'', 664 innerHTML: '<ol></ol>', 665 }) 666 if(t.player.group) { 667 tab.attr('data-title', t.player.options.rawList[item.group]['title']) 668 .attr('data-id', t.player._id) 669 } 670 } 671 672 item.el = tab.child('ol').createChild('li', { 673 title: item.name + ' - ' + item.artist, 674 innerHTML: '<span class="info"><span>'+item.name+'</span><span>'+item.artist+'</span></span>', 675 onclick: function(event) { 676 var current = event.currentTarget; 677 if(playlist.index === index && progress.el) { 678 if(source.paused) { 679 t.player.play(); 680 } else { 681 t.player.seek(source.duration * progress.percent(event, current)) 682 } 683 return; 684 } 685 t.player.switch(index); 686 t.player.play(); 687 } 688 }) 689 690 return item 691 }) 692 693 tabFormat() 694 }, 695 current: function() { 696 return this.data[this.index] 697 }, 698 scroll: function() { 699 var item = this.current() 700 var li = this.el.child('li.active') 701 li && li.removeClass('active') 702 var tab = this.el.child('.tab.active') 703 tab && tab.removeClass('active') 704 li = this.el.find('.nav li')[item.group] 705 li && li.addClass('active') 706 tab = this.el.find('.tab')[item.group] 707 tab && tab.addClass('active') 708 709 pageScroll(item.el, item.el.offsetTop) 710 711 return this 712 }, 713 title: function() { 714 if(source.paused) 715 return 716 717 var current = this.current() 718 document.title = 'Now Playing...' + current['name'] + ' - ' + current['artist'] + ' | ' + originTitle; 719 }, 720 error: function() { 721 var current = this.current() 722 current.el.removeClass('current').addClass('error') 723 current.error = true 724 this.errnum++ 725 } 726 } 727 728 var lyrics = { 729 el: null, 730 data: null, 731 index: 0, 732 create: function(box) { 733 var current = playlist.index 734 var that = this 735 var raw = playlist.current().lrc 736 737 var callback = function(body) { 738 if(current !== playlist.index) 739 return; 740 741 that.data = that.parse(body) 742 743 var lrc = '' 744 that.data.forEach(function(line, index) { 745 lrc += '<p'+(index===0?' class="current"':'')+'>'+line[1]+'</p>'; 746 }) 747 748 that.el = box.createChild('div', { 749 className: 'inner', 750 innerHTML: lrc 751 }, 'replace') 752 753 that.index = 0; 754 } 755 756 if(raw.startsWith('http')) 757 this.fetch(raw, callback) 758 else 759 callback(raw) 760 }, 761 update: function(currentTime) { 762 if(!this.data) 763 return 764 765 if (this.index > this.data.length - 1 || currentTime < this.data[this.index][0] || (!this.data[this.index + 1] || currentTime >= this.data[this.index + 1][0])) { 766 for (var i = 0; i < this.data.length; i++) { 767 if (currentTime >= this.data[i][0] && (!this.data[i + 1] || currentTime < this.data[i + 1][0])) { 768 this.index = i; 769 var y = -(this.index-1); 770 this.el.style.transform = 'translateY('+y+'rem)'; 771 this.el.style.webkitTransform = 'translateY('+y+'rem)'; 772 this.el.getElementsByClassName('current')[0].removeClass('current'); 773 this.el.getElementsByTagName('p')[i].addClass('current'); 774 } 775 } 776 } 777 }, 778 parse: function(lrc_s) { 779 if (lrc_s) { 780 lrc_s = lrc_s.replace(/([^\]^\n])\[/g, function(match, p1){return p1 + '\n['}); 781 const lyric = lrc_s.split('\n'); 782 var lrc = []; 783 const lyricLen = lyric.length; 784 for (var i = 0; i < lyricLen; i++) { 785 // match lrc time 786 const lrcTimes = lyric[i].match(/\[(\d{2}):(\d{2})(\.(\d{2,3}))?]/g); 787 // match lrc text 788 const lrcText = lyric[i] 789 .replace(/.*\[(\d{2}):(\d{2})(\.(\d{2,3}))?]/g, '') 790 .replace(/<(\d{2}):(\d{2})(\.(\d{2,3}))?>/g, '') 791 .replace(/^\s+|\s+$/g, ''); 792 793 if (lrcTimes) { 794 // handle multiple time tag 795 const timeLen = lrcTimes.length; 796 for (var j = 0; j < timeLen; j++) { 797 const oneTime = /\[(\d{2}):(\d{2})(\.(\d{2,3}))?]/.exec(lrcTimes[j]); 798 const min2sec = oneTime[1] * 60; 799 const sec2sec = parseInt(oneTime[2]); 800 const msec2sec = oneTime[4] ? parseInt(oneTime[4]) / ((oneTime[4] + '').length === 2 ? 100 : 1000) : 0; 801 const lrcTime = min2sec + sec2sec + msec2sec; 802 lrc.push([lrcTime, lrcText]); 803 } 804 } 805 } 806 // sort by time 807 lrc = lrc.filter(function(item){return item[1]}); 808 lrc.sort(function(a, b){return a[0] - b[0]}); 809 return lrc; 810 } else { 811 return []; 812 } 813 }, 814 fetch: function(url, callback) { 815 fetch(url) 816 .then(function(response) { 817 return response.text() 818 }).then(function(body) { 819 callback(body) 820 }).catch(function(ex) {}) 821 } 822 } 823 824 var preview = { 825 el: null, 826 create: function () { 827 var current = playlist.current() 828 829 this.el.innerHTML = '<div class="cover"><div class="disc"><img src="'+(current.cover)+'" class="blur" /></div></div>' 830 + '<div class="info"><h4 class="title">
830'+current.name+'</h4><span>'+current.artist+'</span>' 831 + '<div class="lrc"></div></div>' 832 833 this.el.child('.cover').addEventListener('click', t.player.options.events['play-pause']) 834 835 lyrics.create(this.el.child('.lrc')) 836 } 837 } 838 839 var progress = { 840 el: null, 841 bar: null, 842 create: function() { 843 var current = playlist.current().el 844 845 if(current) { 846 847 if(this.el) { 848 this.el.parentNode.removeClass('current') 849 .removeEventListener(utils.nameMap.dragStart, this.drag) 850 this.el.remove() 851 } 852 853 this.el = current.createChild('div', { 854 className: 'progress' 855 }) 856 857 this.el.attr('data-dtime', utils.secondToTime(0)) 858 859 this.bar = this.el.createChild('div', { 860 className: 'bar', 861 }) 862 863 current.addClass('current') 864 865 current.addEventListener(utils.nameMap.dragStart, this.drag); 866 867 playlist.scroll() 868 } 869 }, 870 update: function(percent) { 871 this.bar.width(Math.floor(percent * 100) + '%') 872 this.el.attr('data-ptime', utils.secondToTime(percent * source.duration)) 873 }, 874 seeking: function(type) { 875 if(type) 876 this.el.addClass('seeking') 877 else 878 this.el.removeClass('seeking') 879 }, 880 percent: function(e, el) { 881 var percentage = ((e.clientX || e.changedTouches[0].clientX) - el.left()) / el.width(); 882 percentage = Math.max(percentage, 0); 883 return Math.min(percentage, 1) 884 }, 885 drag: function(e) { 886 e.preventDefault() 887 888 var current = playlist.current().el 889 890 var thumbMove = function(e) { 891 e.preventDefault() 892 var percentage = progress.percent(e, current) 893 progress.update(percentage) 894 lyrics.update(percentage * source.duration); 895 }; 896 897 var thumbUp = function(e) { 898 e.preventDefault() 899 current.removeEventListener(utils.nameMap.dragEnd, thumbUp) 900 current.removeEventListener(utils.nameMap.dragMove, thumbMove) 901 var percentage = progress.percent(e, current) 902 progress.update(percentage) 903 t.player.seek(percentage * source.duration) 904 source.disableTimeupdate = false 905 progress.seeking(false) 906 }; 907 908 source.disableTimeupdate = true 909 progress.seeking(true) 910 current.addEventListener(utils.nameMap.dragMove, thumbMove) 911 current.addEventListener(utils.nameMap.dragEnd, thumbUp) 912 } 913 } 914 915 var controller = { 916 el: null, 917 btns: {}, 918 step: 'next', 919 create: function () { 920 if(!t.player.options.controls) 921 return 922 923 var that = this 924 t.player.options.controls.forEach(function(item) { 925 if(that.btns[item]) 926 return; 927 928 var opt = { 929 onclick: function(event){ 930 that.events[item] ? that.events[item](event) : t.player.options.events[item](event) 931 } 932 } 933 934 switch(item) { 935 case 'volume': 936 opt.className = ' ' + (source.muted ? 'off' : 'on') 937 opt.innerHTML = '<div class="bar"></div>' 938 opt['on'+utils.nameMap.dragStart] = that.events['volume'] 939 opt.onclick = null 940 break; 941 case 'mode': 942 opt.className = ' ' + t.player.options.mode 943 break; 944 default: 945 opt.className = '' 946 break; 947 } 948 949 opt.className = item + opt.className + ' btn' 950 951 that.btns[item] = that.el.createChild('div', opt) 952 }) 953 954 that.btns['volume'].bar = that.btns['volume'].child('.bar') 955 }, 956 events: { 957 mode: function(e) { 958 switch(t.player.options.mode) { 959 case 'loop': 960 t.player.options.mode = 'random' 961 break; 962 case 'random': 963 t.player.options.mode = 'order' 964 break; 965 default: 966 t.player.options.mode = 'loop' 967 } 968 969 controller.btns['mode'].className = 'mode ' + t.player.options.mode + ' btn' 970 store.set('_PlayerMode', t.player.options.mode) 971 }, 972 volume: function(e) { 973 e.preventDefault() 974 975 var current = e.currentTarget 976 977 var drag = false 978 979 var thumbMove = function(e) { 980 e.preventDefault() 981 t.player.volume(controller.percent(e, current)) 982 drag = true 983 }; 984 985 var thumbUp = function(e) { 986 e.preventDefault() 987 current.removeEventListener(utils.nameMap.dragEnd, thumbUp) 988 current.removeEventListener(utils.nameMap.dragMove, thumbMove) 989 if(drag) { 990 t.player.muted() 991 t.player.volume(controller.percent(e, current)) 992 } else { 993 if (source.muted) { 994 t.player.muted() 995 t.player.volume(source.volume) 996 } else { 997 t.player.muted('muted') 998 controller.update(0) 999 } 1000 } 1001 }; 1002 1003 current.addEventListener(utils.nameMap.dragMove, thumbMove) 1004 current.addEventListener(utils.nameMap.dragEnd, thumbUp) 1005 }, 1006 backward: function(e) { 1007 controller.step = 'prev' 1008 t.player.mode() 1009 }, 1010 forward: function(e) { 1011 controller.step = 'next' 1012 t.player.mode() 1013 }, 1014 }, 1015 update: function(percent) { 1016 controller.btns['volume'].className = 'volume '+ (!source.muted && percent > 0? 'on' :'off') +' btn' 1017 controller.btns['volume'].bar.width(Math.floor(percent * 100) + '%') 1018 }, 1019 percent: function(e, el) { 1020 var percentage = ((e.clientX || e.changedTouches[0].clientX) - el.left()) / el.width(); 1021 percentage = Math.max(percentage, 0); 1022 return Math.min(percentage, 1); 1023 } 1024 } 1025 1026 var events = { 1027 onerror: function() { 1028 playlist.error() 1029 t.player.mode() 1030 }, 1031 ondurationchange: function() { 1032 if (source.duration !== 1) { 1033 progress.el.attr('data-dtime', utils.secondToTime(source.duration)) 1034 } 1035 }, 1036 onloadedmetadata: function() { 1037 t.player.seek(0) 1038 progress.el.attr('data-dtime', utils.secondToTime(source.duration)) 1039 }, 1040 onplay: function() { 1041 t.parentNode.addClass('playing') 1042 showtip(this.attr('title')) 1043 NOWPLAYING = t 1044 }, 1045 onpause: function() { 1046 t.parentNode.removeClass('playing') 1047 NOWPLAYING = null 1048 }, 1049 ontimeupdate: function() { 1050 if(!this.disableTimeupdate) { 1051 progress.update(this.currentTime / this.duration) 1052 lyrics.update(this.currentTime) 1053 } 1054 }, 1055 onended: function(argument) { 1056 t.player.mode() 1057 t.player.play() 1058 } 1059 } 1060 1061 var buttons = { 1062 el: {}, 1063 create: function() { 1064 if(!t.player.options.btns) 1065 return 1066 1067 var that = this 1068 t.player.options.btns.forEach(function(item) { 1069 if(that.el[item]) 1070 return; 1071 1072 that.el[item] = t.createChild('div', { 1073 className: item + ' btn', 1074 onclick: function(event){ 1075 t.player.fetch().then(function() { 1076 t.player.options.events[item](event) 1077 }) 1078 } 1079 }); 1080 }); 1081 } 1082 } 1083 1084 var init = function(config) { 1085 if(t.player.created) 1086 return; 1087 1088 1089 t.player.options = Object.assign(option, config); 1090 t.player.options.mode = store.get('_PlayerMode') || t.player.options.mode 1091
1092 // åå§åbuttonãcontrols以åclickäºä»¶ 1093 buttons.create() 1094 1095 // åå§åaudio or video 1096 source = t.createChild(t.player.options.type, events); 1097 // åå§åææ¾å表ãé¢è§ãæ§ä»¶æé®ç 1098 info.create(); 1099 1100 t.parentNode.addClass(t.player.options.type) 1101 1102 t.player.created = true; 1103 } 1104 1105 init(config) 1106 1107 return t; 1108} 1109var statics = CONFIG.statics.indexOf('//') > 0 ? CONFIG.statics : CONFIG.root 1110var scrollAction = { x: 'undefined', y: 'undefined' }; 1111var diffY = 0; 1112var originTitle, titleTime; 1113 1114const BODY = document.getElementsByTagName('body')[0]; 1115const HTML = document.documentElement; 1116const Container = $('#container'); 1117const loadCat = $('#loading'); 1118const siteNav = $('#nav'); 1119const siteHeader = $('#header'); 1120const menuToggle = siteNav.child('.toggle'); 1121const quickBtn = $('#quick'); 1122const sideBar = $('#sidebar'); 1123const siteBrand = $('#brand'); 1124var toolBtn = $('#tool'), toolPlayer, backToTop, goToComment, showContents; 1125var siteSearch = $('#search'); 1126var siteNavHeight, headerHightInner, headerHight; 1127var oWinHeight = window.innerHeight; 1128var oWinWidth = window.innerWidth; 1129var LOCAL_HASH = 0, LOCAL_URL = window.location.href; 1130var pjax; 1131const lazyload = lozad('img, [data-background-image]', { 1132 loaded: function(el) { 1133 el.addClass('lozaded'); 1134 } 1135}) 1136 1137const Loader = { 1138 timer: null, 1139 lock: false, 1140 show: function() { 1141 clearTimeout(this.timer); 1142 document.body.removeClass('loaded'); 1143 loadCat.attr('style', 'display:block'); 1144 Loader.lock = false; 1145 }, 1146 hide: function(sec) { 1147 if(!CONFIG.loader.start) 1148 sec = -1 1149 this.timer = setTimeout(this.vanish, sec||3000); 1150 }, 1151 vanish: function() { 1152 if(Loader.lock) 1153 return; 1154 if(CONFIG.loader.start) 1155 transition(loadCat, 0) 1156 document.body.addClass('loaded'); 1157 Loader.lock = true; 1158 } 1159} 1160 1161const changeTheme = function(type) { 1162 var btn = $('.theme .ic') 1163 if(type == 'dark') { 1164 HTML.attr('data-theme', type); 1165 btn.removeClass('i-sun') 1166 btn.addClass('i-moon') 1167 } else { 1168 HTML.attr('data-theme', null); 1169 btn.removeClass('i-moon'); 1170 btn.addClass('i-sun'); 1171 } 1172} 1173 1174const changeMetaTheme = function(color) { 1175 if(HTML.attr('data-theme') == 'dark') 1176 color = '#222' 1177 1178 $('meta[name="theme-color"]').attr('content', color); 1179} 1180 1181const themeColorListener = function () { 1182 window.matchMedia('(prefers-color-scheme: dark)').addListener(function(mediaQueryList) { 1183 if(mediaQueryList.matches){ 1184 changeTheme('dark'); 1185 } else { 1186 changeTheme(); 1187 } 1188 }); 1189 1190 var t = store.get('theme'); 1191 if(t) { 1192 changeTheme(t); 1193 } else { 1194 if(CONFIG.darkmode) { 1195 changeTheme('dark'); 1196 } 1197 } 1198 1199 $('.theme').addEventListener('click', function(event) { 1200 var btn = event.currentTarget.child('.ic') 1201 1202 var neko = BODY.createChild('div', { 1203 id: 'neko', 1204 innerHTML: '<div class="planet"><div class="sun"></div><div class="moon"></div></div><div class="body"><div class="face"><section class="eyes left"><span class="pupil"></span></section><section class="eyes right"><span class="pupil"></span></section><span class="nose"></span></div></div>' 1205 }); 1206 1207 var hideNeko = function() { 1208 transition(neko, { 1209 delay: 2500, 1210 opacity: 0 1211 }, function() { 1212 BODY.removeChild(neko) 1213 }); 1214 } 1215 1216 if(btn.hasClass('i-sun')) { 1217 var c = function() { 1218 neko.addClass('dark'); 1219 changeTheme('dark'); 1220 store.set('theme', 'dark'); 1221 hideNeko(); 1222 } 1223 } else { 1224 neko.addClass('dark'); 1225 var c = function() { 1226 neko.removeClass('dark'); 1227 changeTheme(); 1228 store.set('theme', 'light'); 1229 hideNeko(); 1230 } 1231 } 1232 transition(neko, 1, function() { 1233 setTimeout(c, 210) 1234 }) 1235 }); 1236} 1237 1238const visibilityListener = function () { 1239 document.addEventListener('visibilitychange', function() { 1240 switch(document.visibilityState) { 1241 case 'hidden': 1242 $('[rel="icon"]').attr('href', statics + CONFIG.favicon.hidden); 1243 document.title = LOCAL.favicon.hide; 1244 if(CONFIG.loader.switch) 1245 Loader.show() 1246 clearTimeout(titleTime); 1247 break; 1248 case 'visible': 1249 $('[rel="icon"]').attr('href', statics + CONFIG.favicon.normal); 1250 document.title = LOCAL.favicon.show; 1251 if(CONFIG.loader.switch) 1252 Loader.hide(1000) 1253 titleTime = setTimeout(function () { 1254 document.title = originTitle; 1255 }, 2000); 1256 break; 1257 } 1258 }); 1259} 1260 1261const showtip = function(msg) { 1262 if(!msg) 1263 return 1264 1265 var tipbox = BODY.createChild('div', { 1266 innerHTML: msg, 1267 className: 'tip' 1268 }); 1269 1270 setTimeout(function() { 1271 tipbox.addClass('hide') 1272 setTimeout(function() { 1273 BODY.removeChild(tipbox); 1274 }, 300); 1275 }, 3000); 1276} 1277 1278const resizeHandle = function (event) {
1279 siteNavHeight = siteNav.height(); 1280 headerHightInner = siteHeader.height(); 1281 headerHight = headerHightInner + $('#waves').height(); 1282 1283 if(oWinWidth != window.innerWidth) 1284 sideBarToggleHandle(null, 1); 1285 1286 oWinHeight = window.innerHeight; 1287 oWinWidth = window.innerWidth; 1288 sideBar.child('.panels').height(oWinHeight + 'px') 1289} 1290 1291const scrollHandle = function (event) { 1292 var winHeight = window.innerHeight; 1293 var docHeight = getDocHeight(); 1294 var contentVisibilityHeight = docHeight > winHeight ? docHeight - winHeight : document.body.scrollHeight - winHeight; 1295 var SHOW = window.pageYOffset > headerHightInner; 1296 var startScroll = window.pageYOffset > 0; 1297 1298 if (SHOW) { 1299 changeMetaTheme('#FFF'); 1300 } else { 1301 changeMetaTheme('#222'); 1302 } 1303 1304 siteNav.toggleClass('show', SHOW); 1305 toolBtn.toggleClass('affix', startScroll); 1306 siteBrand.toggleClass('affix', startScroll); 1307 sideBar.toggleClass('affix', window.pageYOffset > headerHight && document.body.offsetWidth > 991); 1308 1309 if (typeof scrollAction.y == 'undefined') { 1310 scrollAction.y = window.pageYOffset; 1311 //scrollAction.x = Container.scrollLeft; 1312 //scrollAction.y = Container.scrollTop; 1313 } 1314 //var diffX = scrollAction.x - Container.scrollLeft; 1315 diffY = scrollAction.y - window.pageYOffset; 1316 1317 //if (diffX < 0) { 1318 // Scroll right 1319 //} else if (diffX > 0) { 1320 // Scroll left 1321 //} else 1322 if (diffY < 0) { 1323 // Scroll down 1324 siteNav.removeClass('up') 1325 siteNav.toggleClass('down', SHOW); 1326 } else if (diffY > 0) { 1327 // Scroll up 1328 siteNav.removeClass('down') 1329 siteNav.toggleClass('up', SHOW); 1330 } else { 1331 // First scroll event 1332 } 1333 //scrollAction.x = Container.scrollLeft; 1334 scrollAction.y = window.pageYOffset; 1335 1336 var scrollPercent = Math.round(Math.min(100 * window.pageYOffset / contentVisibilityHeight, 100)) + '%'; 1337 backToTop.child('span').innerText = scrollPercent; 1338 $('.percent').width(scrollPercent); 1339} 1340 1341const pagePosition = function() { 1342 if(CONFIG.auto_scroll) 1343 store.set(LOCAL_URL, scrollAction.y) 1344} 1345 1346const positionInit = function(comment) { 1347 var anchor = window.location.hash 1348 var target = null; 1349 if(LOCAL_HASH) { 1350 store.del(LOCAL_URL); 1351 return 1352 } 1353 1354 if(anchor) 1355 target = $(decodeURI(anchor)) 1356 else { 1357 target = CONFIG.auto_scroll ? parseInt(store.get(LOCAL_URL)) : 0 1358 } 1359 1360 if(target) { 1361 pageScroll(target); 1362 LOCAL_HASH = 1; 1363 } 1364 1365 if(comment && anchor && !LOCAL_HASH) { 1366 pageScroll(target); 1367 LOCAL_HASH = 1; 1368 } 1369 1370} 1371 1372const clipBoard = function(str, callback) { 1373 var ta = BODY.createChild('textarea', { 1374 style: { 1375 top: window.scrollY + 'px', // Prevent page scrolling 1376 position: 'absolute', 1377 opacity: '0' 1378 }, 1379 readOnly: true, 1380 value: str 1381 }); 1382 1383 const selection = document.getSelection(); 1384 const selected = selection.rangeCount > 0 ? selection.getRangeAt(0) : false; 1385 ta.select(); 1386 ta.setSelectionRange(0, str.length); 1387 ta.readOnly = false; 1388 var result = document.execCommand('copy'); 1389 callback && callback(result); 1390 ta.blur(); // For iOS 1391 if (selected) { 1392 selection.removeAllRanges(); 1393 selection.addRange(selected); 1394 } 1395 BODY.removeChild(ta); 1396} 1397 1398const sideBarToggleHandle = function (event, force) { 1399 if(sideBar.hasClass('on')) { 1400 sideBar.removeClass('on'); 1401 menuToggle.removeClass('close'); 1402 if(force) { 1403 sideBar.style = ''; 1404 } else { 1405 transition(sideBar, 'slideRightOut'); 1406 } 1407 } else { 1408 if(force) { 1409 sideBar.style = ''; 1410 } else { 1411 transition(sideBar, 'slideRightIn', function () { 1412 sideBar.addClass('on'); 1413 menuToggle.addClass('close'); 1414 }); 1415 } 1416 } 1417} 1418 1419const sideBarTab = function () { 1420 var sideBarInner = sideBar.child('.inner'); 1421 var panels = sideBar.find('.panel'); 1422 1423 if(sideBar.child('.tab')) { 1424 sideBarInner.removeChild(sideBar.child('.tab')); 1425 } 1426 1427 var list = document.createElement('ul'), active = 'active'; 1428 list.className = 'tab'; 1429 1430 ['contents', 'related', 'overview'].forEach(function (item) { 1431 var element = sideBar.child('.panel.' + item) 1432 1433 if(element.innerHTML.replace(/(^\s*)|(\s*$)/g, "").length < 1) { 1434 if(item == 'contents') { 1435 showContents.display("none") 1436 } 1437 return; 1438 } 1439 1440 if(item == 'contents') { 1441 showContents.display("") 1442 } 1443 1444 var tab = document.createElement('li') 1445 var span = document.createElement('span') 1446 var text = document.createTextNode(element.attr('data-title')); 1447 span.appendChild(text); 1448 tab.appendChild(span); 1449 tab.addClass(item + ' item'); 1450 1451 if(active) { 1452 element.addClass(active); 1453 tab.addClass(active); 1454 } else { 1455 element.removeClass('active'); 1456 } 1457 1458 tab.addEventListener('click', function (element) { 1459 var target = event.currentTarget; 1460 if (target.hasClass('active')) 1461 return; 1462 1463 sideBar.find('.tab .item').forEach(function (element) { 1464 element.removeClass('active') 1465 }); 1466 1467 sideBar.find('.panel').forEach(function (element) { 1468 element.removeClass('active') 1469 }); 1470 1471 sideBar.child('.panel.' + target.className.replace(' item', '')).addClass('active'); 1472 1473 target.addClass('active'); 1474 }); 1475 1476 list.appendChild(tab); 1477 active = ''; 1478 }); 1479 1480 if (list.childNodes.length > 1) { 1481 sideBarInner.insertBefore(list, sideBarInner.childNodes[0]); 1482 sideBar.child('.panels').style.paddingTop = '' 1483 } else { 1484 sideBar.child('.panels').style.paddingTop = '.625rem' 1485 } 1486} 1487 1488const sidebarTOC = function () { 1489 var navItems = $.all('.contents li'); 1490 1491 if (navItems.length < 1) { 1492 return; 1493 } 1494 1495 var sections = Array.prototype.slice.call(navItems) || []; 1496 var activeLock = null; 1497 1498 sections = sections.map(function (element, index) { 1499 var link = element.child('a.toc-link'); 1500 var anchor = $(decodeURI(link.attr('href'))); 1501 if(!anchor) 1502 return
1503 var alink = anchor.child('a.anchor'); 1504 1505 var anchorScroll = function (event) { 1506 event.preventDefault(); 1507 var target = $(decodeURI(event.currentTarget.attr('href'))); 1508 1509 activeLock = index; 1510 pageScroll(target, null, function() { 1511 activateNavByIndex(index) 1512 activeLock = null 1513 }) 1514 }; 1515 1516 // TOC item animation navigate. 1517 link.addEventListener('click', anchorScroll); 1518 alink && alink.addEventListener('click', function(event) { 1519 anchorScroll(event) 1520 clipBoard(CONFIG.hostname + '/' + LOCAL.path + event.currentTarget.attr('href')) 1521 }); 1522 return anchor; 1523 }); 1524 1525 var tocElement = sideBar.child('.contents.panel'); 1526 1527 var activateNavByIndex = function (index, lock) { 1528 var target = navItems[index] 1529 1530 if (!target) 1531 return; 1532 1533 if (target.hasClass('current')) { 1534 return; 1535 } 1536 1537 $.each('.toc .active', function (element) { 1538 element && element.removeClass('active current'); 1539 }); 1540 1541 sections.forEach(function (element) { 1542 element && element.removeClass('active'); 1543 }); 1544 1545 target.addClass('active current'); 1546 sections[index] && sections[index].addClass('active'); 1547 1548 var parent = target.parentNode; 1549 1550 while (!parent.matches('.contents')) { 1551 if (parent.matches('li')) { 1552 parent.addClass('active'); 1553 var t = $(parent.child('a.toc-link').attr('href')) 1554 if(t) { 1555 t.addClass('active'); 1556 } 1557 } 1558 parent = parent.parentNode; 1559 } 1560 // Scrolling to center active TOC element if TOC content is taller then viewport. 1561 if(getComputedStyle(sideBar).display != 'none' && tocElement.hasClass('active')) { 1562 pageScroll(tocElement, target.offsetTop- (tocElement.offsetHeight / 4)) 1563 } 1564 } 1565 1566 var findIndex = function(entries) { 1567 var index = 0; 1568 var entry = entries[index]; 1569 1570 if (entry.boundingClientRect.top > 0) { 1571 index = sections.indexOf(entry.target); 1572 return index === 0 ? 0 : index - 1; 1573 } 1574 for (; index < entries.length; index++) { 1575 if (entries[index].boundingClientRect.top <= 0) { 1576 entry = entries[index]; 1577 } else { 1578 return sections.indexOf(entry.target); 1579 } 1580 } 1581 return sections.indexOf(entry.target); 1582 } 1583 1584 var createIntersectionObserver = function() { 1585 if (!window.IntersectionObserver) 1586 return 1587 1588 var observer = new IntersectionObserver(function (entries, observe) { 1589 var index = findIndex(entries) + (diffY < 0? 1 : 0); 1590 if(activeLock === null) { 1591 activateNavByIndex(index); 1592 } 1593 }, { 1594 rootMargin: '0px 0px -100% 0px', 1595 threshold: 0 1596 }); 1597 1598 sections.forEach(function (element) { 1599 element && observer.observe(element); 1600 }); 1601 } 1602 1603 createIntersectionObserver(); 1604} 1605 1606const backToTopHandle = function () { 1607 pageScroll(0); 1608} 1609 1610const goToBottomHandle = function () { 1611 pageScroll(parseInt(Container.height())); 1612} 1613 1614const goToCommentHandle = function () { 1615 pageScroll($('#comments')); 1616} 1617 1618const menuActive = function () { 1619 $.each('.menu .item:not(.title)', function (element) { 1620 var target = element.child('a[href]'); 1621 var parentItem = element.parentNode.parentNode; 1622 if (!target) return; 1623 var isSamePath = target.pathname === location.pathname || target.pathname === location.pathname.replace('index.html', ''); 1624 var isSubPath = !CONFIG.root.startsWith(target.pathname) && location.pathname.startsWith(target.pathname); 1625 var active = target.hostname === location.hostname && (isSamePath || isSubPath) 1626 element.toggleClass('active', active); 1627 if(element.parentNode.child('.active') && parentItem.hasClass('dropdown')) { 1628 parentItem.removeClass('active').addClass('expand'); 1629 } else { 1630 parentItem.removeClass('expand'); 1631 } 1632 }); 1633} 1634const cardActive = function() { 1635 if(!$('.index.wrap')) 1636 return 1637 1638 if (!window.IntersectionObserver) { 1639 $.each('.index.wrap article.item, .index.wrap section.item', function(article) { 1640 if( article.hasClass("show") === false){ 1641 article.addClass("show"); 1642 } 1643 }) 1644 } else { 1645 var io = new IntersectionObserver(function(entries) { 1646
1647 entries.forEach(function(article) { 1648 if (article.target.hasClass("show")) { 1649 io.unobserve(article.target) 1650 } else { 1651 if (article.isIntersecting || article.intersectionRatio > 0) { 1652 article.target.addClass("show"); 1653 io.unobserve(article.target); 1654 } 1655 } 1656 }) 1657 }, { 1658 root: null, 1659 threshold: [0.3] 1660 }); 1661 1662 $.each('.index.wrap article.item, .index.wrap section.item', function(article) { 1663 io.observe(article) 1664 }) 1665 1666 $('.index.wrap .item:first-child').addClass("show") 1667 } 1668 1669 $.each('.cards .item', function(element, index) { 1670 ['mouseenter', 'touchstart'].forEach(function(item){ 1671 element.addEventListener(item, function(event) { 1672 if($('.cards .item.active')) { 1673 $('.cards .item.active').removeClass('active') 1674 } 1675 element.addClass('active') 1676 }) 1677 }); 1678 ['mouseleave'].forEach(function(item){ 1679 element.addEventListener(item, function(event) { 1680 element.removeClass('active') 1681 }) 1682 }); 1683 }); 1684} 1685 1686const registerExtURL = function() { 1687 $.each('span.exturl', function(element) { 1688 var link = document.createElement('a'); 1689 // https://stackoverflow.com/questions/30106476/using-javascripts-atob-to-decode-base64-doesnt-properly-decode-utf-8-strings 1690 link.href = decodeURIComponent(atob(element.dataset.url).split('').map(function(c) { 1691 return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); 1692 }).join('')); 1693 link.rel = 'noopener external nofollow noreferrer'; 1694 link.target = '_blank'; 1695 link.className = element.className; 1696 link.title = element.title || element.innerText; 1697 link.innerHTML = element.innerHTML; 1698 if(element.dataset.backgroundImage) { 1699 link.dataset.backgroundImage = element.dataset.backgroundImage; 1700 } 1701 element.parentNode.replaceChild(link, element); 1702 }); 1703} 1704 1705const postFancybox = function(p) { 1706 if($(p + ' .md img')) { 1707 vendorCss('fancybox'); 1708 vendorJs('fancybox', function() { 1709 var q = jQuery.noConflict(); 1710 1711 $.each(p + ' p.gallery', function(element) { 1712 var box = document.createElement('div'); 1713 box.className = 'gallery'; 1714 box.attr('data-height', element.attr('data-height')||220); 1715 1716 box.innerHTML = element.innerHTML.replace(/<br>/g, "") 1717 1718 element.parentNode.insertBefore(box, element); 1719 element.remove(); 1720 }); 1721 1722 $.each(p + ' .md img:not(.emoji):not(.vemoji)', function(element) { 1723 var $image = q(element); 1724 var info, captionClass = 'image-info'; 1725 if(!$image.is('a img')) { 1726 var imageLink = $image.attr('data-src') || $image.attr('src'); 1727 $image.data('safe-src', imageLink) 1728 var $imageWrapLink = $image.wrap('<a class="fancybox" href="'+imageLink+'" itemscope itemtype="http://schema.org/ImageObject" itemprop="url"></a>').parent('a'); 1729 if (!$image.is('.gallery img')) { 1730 $imageWrapLink.attr('data-fancybox', 'default').attr('rel', 'default'); 1731 } else { 1732 captionClass = 'jg-caption' 1733 } 1734 } 1735 if(info = element.attr('title')) { 1736 $imageWrapLink.attr('data-caption', info); 1737 var para = document.createElement('span'); 1738 var txt = document.createTextNode(info); 1739 para.appendChild(txt); 1740 para.addClass(captionClass); 1741 element.insertAfter(para); 1742 } 1743 }); 1744 1745 $.each(p + ' div.gallery', function (el, i) { 1746 q(el).justifiedGallery({rowHeight: q(el).data('height')||120, rel: 'gallery-' + i}).on('jg.complete', function () { 1747 q(this).find('a').each(function(k, ele) { 1748 ele.attr('data-fancybox', 'gallery-' + i); 1749 }); 1750 }); 1751 }); 1752 1753 q.fancybox.defaults.hash = false; 1754 q(p + ' .fancybox').fancybox({ 1755 loop : true, 1756 helpers: { 1757 overlay: { 1758 locked: false 1759 } 1760 } 1761 }); 1762 }, window.jQuery); 1763 } 1764} 1765 1766const postBeauty = function () { 1767 loadComments(); 1768 1769 if(!$('.md')) 1770 return 1771 1772 postFancybox('.post.block'); 1773 1774 $('.post.block').oncopy = function(event) { 1775 showtip(LOCAL.copyright) 1776 1777 if(LOCAL.nocopy) { 1778 event.preventDefault() 1779 return 1780 } 1781 1782 var copyright = $('#copyright') 1783 if(window.getSelection().toString().length > 30 && copyright) { 1784 event.preventDefault(); 1785 var author = "# " + copyright.child('.author').innerText 1786 var link = "# " + copyright.child('.link').innerText 1787 var license = "# " + copyright.child('.license').innerText
1788 var htmlData = author + "<br>" + link + "<br>" + license + "<br><br>" + window.getSelection().toString().replace(/\r\n/g, "<br>");; 1789 var textData = author + "\n" + link + "\n" + license + "\n\n" + window.getSelection().toString().replace(/\r\n/g, "\n"); 1790 if (event.clipboardData) { 1791 event.clipboardData.setData("text/html", htmlData); 1792 event.clipboardData.setData("text/plain", textData); 1793 } else if (window.clipboardData) { 1794 return window.clipboardData.setData("text", textData); 1795 } 1796 } 1797 } 1798 1799 $.each('li ruby', function(element) { 1800 var parent = element.parentNode; 1801 if(element.parentNode.tagName != 'LI') { 1802 parent = element.parentNode.parentNode; 1803 } 1804 parent.addClass('ruby'); 1805 }) 1806 1807 $.each('ol[start]', function(element) { 1808 element.style.counterReset = "counter " + parseInt(element.attr('start') - 1) 1809 }) 1810 1811 $.each('.md table', function (element) { 1812 element.wrap({ 1813 className: 'table-container' 1814 }); 1815 }); 1816 1817 $.each('.highlight > .table-container', function (element) { 1818 element.className = 'code-container' 1819 }); 1820 1821 $.each('figure.highlight', function (element) { 1822 1823 var code_container = element.child('.code-container'); 1824 var caption = element.child('figcaption'); 1825 1826 element.insertAdjacentHTML('beforeend', '<div class="operation"><span class="breakline-btn"><i class="ic i-align-left"></i></span><span class="copy-btn"><i class="ic i-clipboard"></i></span><span class="fullscreen-btn"><i class="ic i-expand"></i></span></div>'); 1827 1828 var copyBtn = element.child('.copy-btn'); 1829 if(LOCAL.nocopy) { 1830 copyBtn.remove() 1831 } else { 1832 copyBtn.addEventListener('click', function (event) { 1833 var target = event.currentTarget; 1834 var comma = '', code = ''; 1835 code_container.find('pre').forEach(function(line) { 1836 code += comma + line.innerText; 1837 comma = '\n' 1838 }) 1839 1840 clipBoard(code, function(result) { 1841 target.child('.ic').className = result ? 'ic i-check' : 'ic i-times'; 1842 target.blur(); 1843 showtip(LOCAL.copyright); 1844 }) 1845 }); 1846 copyBtn.addEventListener('mouseleave', function (event) { 1847 setTimeout(function () { 1848 event.target.child('.ic').className = 'ic i-clipboard'; 1849 }, 1000); 1850 }); 1851 } 1852 1853 var breakBtn = element.child('.breakline-btn'); 1854 breakBtn.addEventListener('click', function (event) { 1855 var target = event.currentTarget; 1856 if (element.hasClass('breakline')) { 1857 element.removeClass('breakline'); 1858 target.child('.ic').className = 'ic i-align-left'; 1859 } else { 1860 element.addClass('breakline'); 1861 target.child('.ic').className = 'ic i-align-justify'; 1862 } 1863 }); 1864 1865 var fullscreenBtn = element.child('.fullscreen-btn'); 1866 var removeFullscreen = function() { 1867 element.removeClass('fullscreen'); 1868 element.scrollTop = 0; 1869 BODY.removeClass('fullscreen'); 1870 fullscreenBtn.child('.ic').className = 'ic i-expand'; 1871 } 1872 var fullscreenHandle = function(event) { 1873 var target = event.currentTarget; 1874 if (element.hasClass('fullscreen')) { 1875 removeFullscreen(); 1876 hideCode && hideCode(); 1877 pageScroll(element) 1878 } else { 1879 element.addClass('fullscreen'); 1880 BODY.addClass('fullscreen'); 1881 fullscreenBtn.child('.ic').className = 'ic i-compress'; 1882 showCode && showCode(); 1883 } 1884 } 1885 fullscreenBtn.addEventListener('click', fullscreenHandle); 1886 caption && caption.addEventListener('click', fullscreenHandle); 1887 1888 if(code_container && code_container.find("tr").length > 15) { 1889 1890 code_container.style.maxHeight = "300px"; 1891 code_container.insertAdjacentHTML('beforeend', '<div class="show-btn"><i class="ic i-angle-down"></i></div>'); 1892 var showBtn = code_container.child('.show-btn'); 1893 1894 var showCode = function() { 1895 code_container.style.maxHeight = "" 1896 showBtn.addClass('open') 1897 } 1898 1899 var hideCode = function() { 1900 code_container.style.maxHeight = "300px" 1901 showBtn.removeClass('open') 1902 } 1903 1904 showBtn.addEventListener('click', function(event) { 1905 if (showBtn.hasClass('open')) { 1906 removeFullscreen() 1907 hideCode() 1908 pageScroll(code_container) 1909 } else { 1910 showCode() 1911 } 1912 }); 1913 } 1914 }); 1915 1916 $.each('pre.mermaid > svg', function (element) { 1917 element.style.maxWidth = '' 1918 }); 1919 1920 $.each('.reward button', function (element) { 1921 element.addEventListener('click', function (event) { 1922 event.preventDefault(); 1923 var qr = $('#qr')
1924 if(qr.display() === 'inline-flex') { 1925 transition(qr, 0) 1926 } else { 1927 transition(qr, 1, function() { 1928 qr.display('inline-flex') 1929 }) // slideUpBigIn 1930 } 1931 }); 1932 }); 1933 1934 //quiz 1935 $.each('.quiz > ul.options li', function (element) { 1936 element.addEventListener('click', function (event) { 1937 if (element.hasClass('correct')) { 1938 element.toggleClass('right') 1939 element.parentNode.parentNode.addClass('show') 1940 } else { 1941 element.toggleClass('wrong') 1942 } 1943 }); 1944 }); 1945 1946 $.each('.quiz > p', function (element) { 1947 element.addEventListener('click', function (event) { 1948 element.parentNode.toggleClass('show') 1949 }); 1950 }); 1951 1952 $.each('.quiz > p:first-child', function (element) { 1953 var quiz = element.parentNode; 1954 var type = 'choice' 1955 if(quiz.hasClass('true') || quiz.hasClass('false')) 1956 type = 'true_false' 1957 if(quiz.hasClass('multi')) 1958 type = 'multiple' 1959 if(quiz.hasClass('fill')) 1960 type = 'gap_fill' 1961 if(quiz.hasClass('essay')) 1962 type = 'essay' 1963 element.attr('data-type', LOCAL.quiz[type]) 1964 }); 1965 1966 $.each('.quiz .mistake', function (element) { 1967 element.attr('data-type', LOCAL.quiz.mistake) 1968 }); 1969 1970 $.each('div.tags a', function(element) { 1971 element.className = ['primary', 'success', 'info', 'warning', 'danger'][Math.floor(Math.random() * 5)] 1972 }) 1973 1974 $.each('.md div.player', function(element) { 1975 mediaPlayer(element, { 1976 type: element.attr('data-type'), 1977 mode: 'order', 1978 btns: [] 1979 }).player.load(JSON.parse(element.attr('data-src'))).fetch() 1980 }) 1981} 1982 1983const tabFormat = function() { 1984 // tab 1985 var first_tab 1986 $.each('div.tab', function(element, index) { 1987 if(element.attr('data-ready')) 1988 return 1989 1990 var id = element.attr('data-id'); 1991 var title = element.attr('data-title'); 1992 var box = $('#' + id); 1993 if(!box) { 1994 box = document.createElement('div'); 1995 box.className = 'tabs'; 1996 box.id = id; 1997 box.innerHTML = '<div class="show-btn"></div>' 1998 1999 var showBtn = box.child('.show-btn'); 2000 showBtn.addEventListener('click', function(event) { 2001 pageScroll(box) 2002 }); 2003 2004 element.parentNode.insertBefore(box, element); 2005 first_tab = true; 2006 } else { 2007 first_tab = false; 2008 } 2009 2010 var ul = box.child('.nav ul'); 2011 if(!ul) { 2012 ul = box.createChild('div', { 2013 className: 'nav', 2014 innerHTML: '<ul></ul>' 2015 }).child('ul'); 2016 } 2017 2018 var li = ul.createChild('li', { 2019 innerHTML: title 2020 }); 2021 2022 if(first_tab) { 2023 li.addClass('active'); 2024 element.addClass('active'); 2025 } 2026 2027 li.addEventListener('click', function(event) { 2028 var target = event.currentTarget; 2029 box.find('.active').forEach(function(el) { 2030 el.removeClass('active'); 2031 }) 2032 element.addClass('active'); 2033 target.addClass('active'); 2034 }); 2035 2036 box.appendChild(element); 2037 element.attr('data-ready', true) 2038 }); 2039} 2040 2041const loadComments = function () { 2042 var element = $('#comments'); 2043 if (!element) { 2044 goToComment.display("none") 2045 return; 2046 } else { 2047 goToComment.display("") 2048 } 2049 2050 if (!window.IntersectionObserver) { 2051 vendorCss('valine'); 2052 } else { 2053 var io = new IntersectionObserver(function(entries, observer) { 2054 var entry = entries[0]; 2055 vendorCss('valine'); 2056 if (entry.isIntersecting || entry.intersectionRatio > 0) { 2057 transition($('#comments'), 'bounceUpIn'); 2058 observer.disconnect(); 2059 } 2060 }); 2061 2062 io.observe(element); 2063 } 2064} 2065 2066const algoliaSearch = function(pjax) { 2067 if(CONFIG.search === null) 2068 return 2069 2070 if(!siteSearch) { 2071 siteSearch = BODY.createChild('div', { 2072 id: 'search', 2073 innerHTML: '<div class="inner"><div class="header"><span class="icon"><i class="ic i-search"></i></span><div class="search-input-container"></div><span class="close-btn"><i class="ic i-times-circle"></i></span></div><div class="results"><div class="inner"><div id="search-stats"></div><div id="search-hits"></div><div id="search-pagination"></div></div></div></div>' 2074 }); 2075 } 2076 2077 var search = instantsearch({ 2078 indexName: CONFIG.search.indexName, 2079 searchClient : algoliasearch(CONFIG.search.appID, CONFIG.search.apiKey), 2080 searchFunction: function(helper) { 2081 var searchInput = $('.search-input'); 2082 if (searchInput.value) { 2083 helper.search(); 2084 } 2085 } 2086 }); 2087 2088 search.on('render', function() { 2089 pjax.refresh($('#search-hits')); 2090 }); 2091 2092 // Registering Widgets 2093 search.addWidgets([ 2094 instantsearch.widgets.configure({ 2095 hitsPerPage: CONFIG.search.hits.per_page || 10 2096 }), 2097 2098 instantsearch.widgets.searchBox({ 2099 container : '.search-input-container', 2100 placeholder : LOCAL.search.placeholder, 2101 // Hide default icons of algolia search 2102 showReset : false, 2103 showSubmit : false, 2104 showLoadingIndicator: false,
2105 cssClasses : { 2106 input: 'search-input' 2107 } 2108 }), 2109 2110 instantsearch.widgets.stats({ 2111 container: '#search-stats', 2112 templates: { 2113 text: function(data) { 2114 var stats = LOCAL.search.stats 2115 .replace(/\$\{hits}/, data.nbHits) 2116 .replace(/\$\{time}/, data.processingTimeMS); 2117 return stats + '<span class="algolia-powered"></span><hr>'; 2118 } 2119 } 2120 }), 2121 2122 instantsearch.widgets.hits({ 2123 container: '#search-hits', 2124 templates: { 2125 item: function(data) { 2126 var cats = data.categories ? '<span>'+data.categories.join('<i class="ic i-angle-right"></i>')+'</span>' : ''; 2127 return '<a href="' + CONFIG.root + data.path +'">'+cats+data._highlightResult.title.value+'</a>'; 2128 }, 2129 empty: function(data) { 2130 return '<div id="hits-empty">'+ 2131 LOCAL.search.empty.replace(/\$\{query}/, data.query) + 2132 '</div>'; 2133 } 2134 }, 2135 cssClasses: { 2136 item: 'item' 2137 } 2138 }), 2139 2140 instantsearch.widgets.pagination({ 2141 container: '#search-pagination', 2142 scrollTo : false, 2143 showFirst: false, 2144 showLast : false, 2145 templates: { 2146 first : '<i class="ic i-angle-double-left"></i>', 2147 last : '<i class="ic i-angle-double-right"></i>', 2148 previous: '<i class="ic i-angle-left"></i>', 2149 next : '<i class="ic i-angle-right"></i>' 2150 }, 2151 cssClasses: { 2152 root : 'pagination', 2153 item : 'pagination-item', 2154 link : 'page-number', 2155 selectedItem: 'current', 2156 disabledItem: 'disabled-item' 2157 } 2158 }) 2159 ]); 2160 2161 search.start(); 2162 2163 // Handle and trigger popup window 2164 $.each('.search', function(element) { 2165 element.addEventListener('click', function() { 2166 document.body.style.overflow = 'hidden'; 2167 transition(siteSearch, 'shrinkIn', function() { 2168 $('.search-input').focus(); 2169 }) // transition.shrinkIn 2170 }); 2171 }); 2172 2173 // Monitor main search box 2174 const onPopupClose = function() { 2175 document.body.style.overflow = ''; 2176 transition(siteSearch, 0); // "transition.shrinkOut" 2177 }; 2178 2179 siteSearch.addEventListener('click', function(event) { 2180 if (event.target === siteSearch) { 2181 onPopupClose(); 2182 } 2183 }); 2184 $('.close-btn').addEventListener('click', onPopupClose); 2185 window.addEventListener('pjax:success', onPopupClose); 2186 window.addEventListener('keyup', function(event) { 2187 if (event.key === 'Escape') { 2188 onPopupClose(); 2189 } 2190 }); 2191} 2192const domInit = function() { 2193 $.each('.overview .menu > .item', function(el) { 2194 siteNav.child('.menu').appendChild(el.cloneNode(true)); 2195 }) 2196 2197 loadCat.addEventListener('click', Loader.vanish); 2198 menuToggle.addEventListener('click', sideBarToggleHandle); 2199 $('.dimmer').addEventListener('click', sideBarToggleHandle); 2200 2201 quickBtn.child('.down').addEventListener('click', goToBottomHandle); 2202 quickBtn.child('.up').addEventListener('click', backToTopHandle); 2203 2204 if(!toolBtn) { 2205 toolBtn = siteHeader.createChild('div', { 2206 id: 'tool', 2207 innerHTML: '<div class="item player"></div><div class="item contents"><i class="ic i-list-ol"></i></div><div class="item chat"><i class="ic i-comments"></i></div><div class="item back-to-top"><i class="ic i-arrow-up"></i><span>0%</span></div>' 2208 }); 2209 } 2210 2211 toolPlayer = toolBtn.child('.player'); 2212 backToTop = toolBtn.child('.back-to-top'); 2213 goToComment = toolBtn.child('.chat'); 2214 showContents = toolBtn.child('.contents'); 2215 2216 backToTop.addEventListener('click', backToTopHandle); 2217 goToComment.addEventListener('click', goToCommentHandle); 2218 showContents.addEventListener('click', sideBarToggleHandle); 2219 2220 mediaPlayer(toolPlayer) 2221 $('main').addEventListener('click', function() { 2222 toolPlayer.player.mini() 2223 }) 2224} 2225 2226const pjaxReload = function () { 2227 pagePosition() 2228 2229 if(sideBar.hasClass('on')) { 2230 transition(sideBar, function () { 2231 sideBar.removeClass('on'); 2232 menuToggle.removeClass('close'); 2233 }); // 'transition.slideRightOut' 2234 } 2235 2236 $('#main').innerHTML = '' 2237 $('#main').appendChild(loadCat.lastChild.cloneNode(true)); 2238 pageScroll(0); 2239} 2240 2241const siteRefresh = function (reload) { 2242 LOCAL_HASH = 0 2243 LOCAL_URL = window.location.href 2244 2245 vendorCss('katex'); 2246 vendorJs('copy_tex'); 2247 vendorCss('mermaid'); 2248 vendorJs('chart'); 2249 vendorJs('valine', function() { 2250 var options = Object.assign({}, CONFIG.valine); 2251 options = Object.assign(options, LOCAL.valine||{}); 2252 options.el = '#comments'; 2253 options.pathname = LOCAL.path; 2254 options.pjax = pjax; 2255 options.lazyload = lazyload; 2256 2257 new MiniValine(options); 2258 2259 setTimeout(function(){ 2260 positionInit(1); 2261 postFancybox('.v'); 2262 }, 1000); 2263 }, window.MiniValine); 2264 2265 if(!reload) { 2266 $.each('script[data-pjax]', pjaxScript); 2267 } 2268 2269 originTitle = document.title 2270 2271 resizeHandle() 2272 2273 menuActive() 2274 2275 sideBarTab() 2276 sidebarTOC() 2277 2278 registerExtURL() 2279 postBeauty() 2280 tabFormat() 2281 2282 toolPlayer.player.load(LOCAL.audio || CONFIG.audio || {}) 2283 2284 Loader.hide() 2285 2286 setTimeout(function(){ 2287 positionInit() 2288 }, 500); 2289 2290 cardActive() 2291 2292 lazyload.observe() 2293} 2294 2295const siteInit = function () { 2296 2297 domInit() 2298 2299 pjax = new Pjax({ 2300 selectors: [ 2301 'head title', 2302 '.languages', 2303 '.pjax', 2304 'script[data-config]' 2305 ],
2306 analytics: false, 2307 cacheBust: false 2308 }) 2309 2310 CONFIG.quicklink.ignores = LOCAL.ignores 2311 quicklink.listen(CONFIG.quicklink) 2312 2313 visibilityListener() 2314 themeColorListener() 2315 2316 algoliaSearch(pjax) 2317 2318 window.addEventListener('scroll', scrollHandle) 2319 2320 window.addEventListener('resize', resizeHandle) 2321 2322 window.addEventListener('pjax:send', pjaxReload) 2323 2324 window.addEventListener('pjax:success', siteRefresh) 2325 2326 window.addEventListener('beforeunload', function() { 2327 pagePosition() 2328 }) 2329 2330 siteRefresh(1) 2331} 2332 2333window.addEventListener('DOMContentLoaded', siteInit); 2334 2335console.log('%c Theme.Shoka v' + CONFIG.version + ' %c https://shoka.lostyu.me/ ', 'color: white; background: #e9546b; padding:5px 0;', 'padding:4px;border:1px solid #e9546b;') 2336var canvasEl = document.createElement('canvas'); 2337canvasEl.style.cssText = 'position:fixed;top:0;left:0;pointer-events:none;z-index:9999999'; 2338document.body.appendChild(canvasEl); 2339 2340var ctx = canvasEl.getContext('2d'); 2341var numberOfParticules = 30; 2342var pointerX = 0; 2343var pointerY = 0; 2344var tap = 'click'; // ('ontouchstart' in window || navigator.msMaxTouchPoints) ? 'touchstart' : 'mousedown' 2345var colors = CONFIG.fireworks; 2346 2347function setCanvasSize() { 2348 canvasEl.width = window.innerWidth * 2; 2349 canvasEl.height = window.innerHeight * 2; 2350 canvasEl.style.width = window.innerWidth + 'px'; 2351 canvasEl.style.height = window.innerHeight + 'px'; 2352 canvasEl.getContext('2d').scale(2, 2); 2353} 2354 2355function updateCoords(e) { 2356 pointerX = e.clientX || e.touches && e.touches[0].clientX; 2357 pointerY = e.clientY || e.touches && e.touches[0].clientY; 2358} 2359 2360function setParticuleDirection(p) { 2361 var angle = anime.random(0, 360) * Math.PI / 180; 2362 var value = anime.random(50, 180); 2363 var radius = [-1, 1][anime.random(0, 1)] * value; 2364 return { 2365 x: p.x + radius * Math.cos(angle), 2366 y: p.y + radius * Math.sin(angle) 2367 } 2368} 2369 2370function createParticule(x,y) { 2371 var p = {}; 2372 p.x = x; 2373 p.y = y; 2374 p.color = colors[anime.random(0, colors.length - 1)]; 2375 p.radius = anime.random(16, 32); 2376 p.endPos = setParticuleDirection(p); 2377 p.draw = function() { 2378 ctx.beginPath(); 2379 ctx.arc(p.x, p.y, p.radius, 0, 2 * Math.PI, true); 2380 ctx.fillStyle = p.color; 2381 ctx.fill(); 2382 } 2383 return p; 2384} 2385 2386function createCircle(x,y) { 2387 var p = {}; 2388 p.x = x; 2389 p.y = y; 2390 p.color = '#FFF'; 2391 p.radius = 0.1; 2392 p.alpha = .5; 2393 p.lineWidth = 6; 2394 p.draw = function() { 2395 ctx.globalAlpha = p.alpha; 2396 ctx.beginPath(); 2397 ctx.arc(p.x, p.y, p.radius, 0, 2 * Math.PI, true); 2398 ctx.lineWidth = p.lineWidth; 2399 ctx.strokeStyle = p.color; 2400 ctx.stroke(); 2401 ctx.globalAlpha = 1; 2402 } 2403 return p; 2404} 2405 2406function renderParticule(anim) { 2407 for (var i = 0; i < anim.animatables.length; i++) { 2408 anim.animatables[i].target.draw(); 2409 } 2410} 2411 2412function animateParticules(x, y) { 2413 var circle = createCircle(x, y); 2414 var particules = []; 2415 for (var i = 0; i < numberOfParticules; i++) { 2416 particules.push(createParticule(x, y)); 2417 } 2418 anime.timeline().add({ 2419 targets: particules, 2420 x: function(p) { return p.endPos.x; }, 2421 y: function(p) { return p.endPos.y; }, 2422 radius: 0.1, 2423 duration: anime.random(1200, 1800), 2424 easing: 'easeOutExpo', 2425 update: renderParticule 2426 }).add({ 2427 targets: circle, 2428 radius: anime.random(80, 160), 2429 lineWidth: 0, 2430 alpha: { 2431 value: 0, 2432 easing: 'linear', 2433 duration: anime.random(600, 800), 2434 }, 2435 duration: anime.random(1200, 1800), 2436 easing: 'easeOutExpo', 2437 update: renderParticule 2438 }, 0); 2439} 2440 2441var render = anime({ 2442 duration: Infinity, 2443 update: function() { 2444 ctx.clearRect(0, 0, canvasEl.width, canvasEl.height); 2445 } 2446}); 2447 2448document.addEventListener(tap, function(e) { 2449 render.play(); 2450 updateCoords(e); 2451 animateParticules(pointerX, pointerY); 2452}, false); 2453 2454setCanvasSize(); 2455window.addEventListener('resize', setCanvasSize, false);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.