diff --git a/.gitignore b/.gitignore index 33ca463..904ed55 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .idea node_modules demo2 -npm-debug.log \ No newline at end of file +npm-debug.log +DPlayer.log \ No newline at end of file diff --git a/README.md b/README.md index 1cf0ac2..f95db91 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![Travis](https://img.shields.io/travis/DIYgod/DPlayer.svg?style=flat-square)](https://travis-ci.org/DIYgod/DPlayer) [![%e2%9d%a4](https://img.shields.io/badge/made%20with-%e2%9d%a4-ff69b4.svg?style=flat-square)](https://www.anotherhome.net/) -Wow, such a lovely HTML5 danmaku video player +> Wow, such a lovely HTML5 danmaku video player ## Introduction @@ -43,8 +43,6 @@ $ npm run build - [ ] 快捷键: 空格暂停,双击全屏, 方向键控制进度 -- [ ] 弹幕相关 - - [ ] icon 动画 - [ ] 右键 diff --git a/demo/index.html b/demo/index.html index d92f8f2..8de237a 100644 --- a/demo/index.html +++ b/demo/index.html @@ -66,7 +66,8 @@ }, danmaku: { id: '9E2E3368B56CDBB4', - api: 'http://dplayer.daoapp.io/' + api: 'https://dplayer.daoapp.io/', + token: 'tokendemo' } }); dp.init(); diff --git a/dist/DPlayer.min.js b/dist/DPlayer.min.js index b17a2c6..7962ffc 100644 --- a/dist/DPlayer.min.js +++ b/dist/DPlayer.min.js @@ -1,3 +1,3 @@ -"use strict";function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e},_createClass=function(){function e(e,t){for(var n=0;n\n \n \n \n '},this.isMobile=navigator.userAgent.match(/(iPad)|(iPhone)|(iPod)|(android)|(webOS)/i),this.isMobile&&(t.autoplay=!1);var a={element:document.getElementsByClassName("dplayer")[0],autoplay:!1,theme:"#b7daff",loop:!1};for(var s in a)a.hasOwnProperty(s)&&!t.hasOwnProperty(s)&&(t[s]=a[s]);this.option=t,this.loop=t.loop,this.secondToTime=function(e){var t=function(e){return 10>e?"0"+e:""+e},n=parseInt(e/60),a=parseInt(e-60*n);return t(n)+":"+t(a)},this.updateBar=function(e,t,a){t=t>0?t:0,t=1>t?t:1,n[e+"Bar"].style[a]=100*t+"%"},this.eventTypes=["play","pause","canplay","playing","ended","error"],this.event={};for(var l=0;l\n
\n \n
\n
\n
\n
\n
\n \n
\n \n
\n
\n
\n \n
\n
\n
\n
\n 0:00 / 0:00\n
\n
\n
\n \n
\n
\n
\n
\n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n \n \n
\n
\n
\n \n
\n
\n \n
\n
\n
\n
\n
\n \n
\n
\n
\n
\n '),this.audio=this.element.getElementsByClassName("dplayer-video")[0],this.ptime=this.element.getElementsByClassName("dplayer-ptime")[0],this.playButton=this.element.getElementsByClassName("dplayer-play-icon")[0],this.toggle=function(){e.audio.paused?e.play():e.pause()},this.playButton.addEventListener("click",this.toggle),this.element.getElementsByClassName("dplayer-video-wrap")[0].addEventListener("click",this.toggle),this.element.getElementsByClassName("dplayer-controller-mask")[0].addEventListener("click",this.toggle);var t=function(t){var n=t.offsetLeft,a=t.offsetParent,s=void 0;if(document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement)for(;null!==a&&a!==e.element;)n+=a.offsetLeft,a=a.offsetParent;else for(;null!==a;)n+=a.offsetLeft,a=a.offsetParent;return s=document.body.scrollLeft+document.documentElement.scrollLeft,n-s};this.playedBar=this.element.getElementsByClassName("dplayer-played")[0],this.loadedBar=this.element.getElementsByClassName("dplayer-loaded")[0],this.bar=this.element.getElementsByClassName("dplayer-bar-wrap")[0];var n=void 0;this.audio.addEventListener("seeked",function(){if(e.option.danmaku)for(var t=0;t=e.audio.currentTime)return void(e.danIndex=t)}),this.setTime=function(){e.option.danmaku?e.playedTime=setInterval(function(){e.updateBar("played",e.audio.currentTime/e.audio.duration,"width"),e.ptime.innerHTML=e.secondToTime(e.audio.currentTime),e.trigger("playing");var t=e.dan[e.danIndex];t&&e.audio.currentTime>=parseFloat(t.time)&&(e.danmakuIn(t.text,t.color,t.type),e.danIndex++)},100):e.playedTime=setInterval(function(){e.updateBar("played",e.audio.currentTime/e.audio.duration,"width"),e.ptime.innerHTML=e.secondToTime(e.audio.currentTime),e.trigger("playing")},100)},this.bar.addEventListener("click",function(a){var s=a||window.event;n=e.bar.clientWidth;var l=(s.clientX-t(e.bar))/n;l=l>0?l:0,l=1>l?l:1,e.updateBar("played",l,"width"),e.element.getElementsByClassName("dplayer-ptime")[0].innerHTML=e.secondToTime(l*e.audio.duration),e.audio.currentTime=parseFloat(e.playedBar.style.width)/100*e.audio.duration});var a=function(a){var s=a||window.event,l=(s.clientX-t(e.bar))/n;l=l>0?l:0,l=1>l?l:1,e.updateBar("played",l,"width"),e.element.getElementsByClassName("dplayer-ptime")[0].innerHTML=e.secondToTime(l*e.audio.duration)},s=function _(){document.removeEventListener("mouseup",_),document.removeEventListener("mousemove",a),e.audio.currentTime=parseFloat(e.playedBar.style.width)/100*e.audio.duration,e.setTime()};this.bar.addEventListener("mousedown",function(){n=e.bar.clientWidth,clearInterval(e.playedTime),document.addEventListener("mousemove",a),document.addEventListener("mouseup",s)}),this.volumeBar=this.element.getElementsByClassName("dplayer-volume-bar-inner")[0];var l=this.element.getElementsByClassName("dplayer-volume")[0],i=this.element.getElementsByClassName("dplayer-volume-bar-wrap")[0],d=this.element.getElementsByClassName("dplayer-volume-bar")[0],o=this.element.getElementsByClassName("dplayer-volume-icon")[0],r=35,c=function(){e.audio.volume>=.8?o.innerHTML=e.getSVG("volume-up"):e.audio.volume>0?o.innerHTML=e.getSVG("volume-down"):o.innerHTML=e.getSVG("volume-off")},u=function(n){var a=n||window.event,s=(a.clientX-t(d)-5.5)/r;s=s>0?s:0,s=1>s?s:1,e.updateBar("volume",s,"width"),e.audio.volume=s,e.audio.muted&&(e.audio.muted=!1),c()},p=function A(){document.removeEventListener("mouseup",A),document.removeEventListener("mousemove",u),l.classList.remove("dplayer-volume-active")};i.addEventListener("click",function(n){var a=n||window.event,s=(a.clientX-t(d)-5.5)/r;s=s>0?s:0,s=1>s?s:1,e.updateBar("volume",s,"width"),e.audio.volume=s,e.audio.muted&&(e.audio.muted=!1),c()}),i.addEventListener("mousedown",function(){document.addEventListener("mousemove",u),document.addEventListener("mouseup",p),l.classList.add("dplayer-volume-active")}),o.addEventListener("click",function(){e.audio.muted?(e.audio.muted=!1,c(),e.updateBar("volume",e.audio.volume,"width")):(e.audio.muted=!0,o.innerHTML=e.getSVG("volume-off"),e.updateBar("volume",0,"width"))});var m=0,y=function(){e.element.classList.remove("dplayer-hide-controller"),clearTimeout(m),m=setTimeout(function(){e.audio.played.length&&(e.element.classList.add("dplayer-hide-controller"),b(),P())},2e3)};this.element.addEventListener("mousemove",y),this.element.addEventListener("click",y),this.danOpacity=.7;var v={original:'\n
\n 洗脑循环\n
\n \n \n
\n
\n
\n 速度\n
'+this.getSVG("right")+('
\n
\n
\n 弹幕透明度\n
\n
\n
\n \n
\n
\n
\n
'),speed:'\n
\n 0.5\n
\n
\n 0.75\n
\n
\n 正常\n
\n
\n 1.25\n
\n
\n 1.5\n
\n
\n 2\n
'},h=this.element.getElementsByClassName("dplayer-setting-icon")[0],f=this.element.getElementsByClassName("dplayer-setting-box")[0],g=this.element.getElementsByClassName("dplayer-mask")[0];f.innerHTML=v.original;var b=function(){f.classList.contains("dplayer-setting-box-open")&&(f.classList.remove("dplayer-setting-box-open"),g.classList.remove("dplayer-mask-show"),setTimeout(function(){f.classList.remove("dplayer-setting-box-narrow"),f.innerHTML=v.original,L()},300))},k=function(){f.classList.add("dplayer-setting-box-open"),g.classList.add("dplayer-mask-show")};g.addEventListener("click",function(){b()}),h.addEventListener("click",function(){k()});var L=function(){var n=e.element.getElementsByClassName("dplayer-setting-loop")[0],a=n.getElementsByClassName("dplayer-toggle-setting-input")[0];a.checked=e.loop,n.addEventListener("click",function(){a.checked=!a.checked,a.checked?(e.loop=!0,e.audio.loop=e.loop):(e.loop=!1,e.audio.loop=e.loop),b()}),a.addEventListener("change",function(){a.checked?(e.loop=!0,e.audio.loop=e.loop):(e.loop=!1,e.audio.loop=e.loop),b()});var s=e.element.getElementsByClassName("dplayer-setting-speed")[0];s.addEventListener("click",function(){f.classList.add("dplayer-setting-box-narrow"),f.innerHTML=v.speed;for(var t=f.getElementsByClassName("dplayer-setting-speed-item"),n=function(n){t[n].addEventListener("click",function(){e.audio.playbackRate=t[n].dataset.speed,b()})},a=0;a0?s:0,s=1>s?s:1,e.updateBar("danmaku",s,"width");for(var l=e.element.getElementsByClassName("dplayer-danmaku-item"),d=0;d0?s:0,s=1>s?s:1,e.updateBar("danmaku",s,"width");for(var l=e.element.getElementsByClassName("dplayer-danmaku-item"),d=0;d=i)break;if(l===s.length-1)return q[t][a+""].push(e),e.addEventListener("animationend",function(){q[t][a+""].splice(0,1)}),{v:a%T}}},s=0;;s++){var l=a(s);if("object"===("undefined"==typeof l?"undefined":_typeof(l)))return l.v}};this.danmakuIn=function(t,n,a){B=E.offsetWidth,C=E.offsetHeight,T=C/w;var s=document.createElement("div"),l=document.createTextNode(t);switch(s.classList.add("dplayer-danmaku-item"),s.classList.add("dplayer-danmaku-"+a),s.appendChild(l),s.style.opacity=e.danOpacity,E.appendChild(s),s.style.color=n,a){case"right":s.style.top=w*x(s,a)+"px",s.style.width=s.offsetWidth+1+"px",s.style.transform="translateX(-"+B+"px)",s.addEventListener("animationend",function(){E.removeChild(s)});break;case"top":s.style.top=w*x(s,a)+"px",s.addEventListener("animationend",function(){E.removeChild(s)});break;case"bottom":s.style.bottom=w*x(s,a)+"px",s.addEventListener("animationend",function(){E.removeChild(s)});break;default:console.error("Can't handled danmaku type: "+a)}s.classList.add("dplayer-danmaku-move")},this.option.danmaku?!function(){e.danIndex=0;var t=new XMLHttpRequest;t.onreadystatechange=function(){4===t.readyState&&(t.status>=200&&t.status<300||304===t.status?(e.dan=JSON.parse(t.responseText).danmaku.sort(function(e,t){return e.time-t.time}),console.log(e.dan),e.option.autoplay&&!e.isMobile?e.play():e.isMobile&&e.pause()):console.log("Request was unsuccessful: "+t.status))},t.open("get",e.option.danmaku.api+"?id="+e.option.danmaku.id,!0),t.send(null)}():this.option.autoplay&&!this.isMobile?this.play():this.isMobile&&this.pause();var S=this.element.getElementsByClassName("dplayer-comment-input")[0],z=this.element.getElementsByClassName("dplayer-comment-icon")[0],H=this.element.getElementsByClassName("dplayer-comment-box")[0],F=this.element.getElementsByClassName("dplayer-comment-setting")[0],I=this.element.getElementsByClassName("dplayer-comment-setting-box")[0],G=this.element.getElementsByClassName("dplayer-send-icon")[0],V=function(){I.classList.contains("dplayer-comment-setting-open")&&I.classList.remove("dplayer-comment-setting-open")},O=function(){I.classList.contains("dplayer-comment-setting-open")?I.classList.remove("dplayer-comment-setting-open"):I.classList.add("dplayer-comment-setting-open")},R=0,P=function(){H.classList.contains("dplayer-comment-box-open")&&(H.classList.remove("dplayer-comment-box-open"),g.classList.remove("dplayer-mask-show"),clearInterval(R),e.element.classList.remove("dplayer-show-controller"),V())},X=function(){H.classList.add("dplayer-comment-box-open"),g.classList.add("dplayer-mask-show"),R=setInterval(function(){clearTimeout(m)},1e3),e.element.classList.add("dplayer-show-controller")};g.addEventListener("click",function(){P()}),z.addEventListener("click",function(){X(),setTimeout(function(){S.focus()},300)}),F.addEventListener("click",function(){O()}),this.element.getElementsByClassName("dplayer-comment-setting-color")[0].addEventListener("click",function(){var t=e.element.querySelector('input[name="dplayer-danmaku-color"]:checked+span');t&&F.setAttribute("style",t.getAttribute("style"))}),S.addEventListener("click",function(){V()}),G.addEventListener("click",function(){if(!S.value.replace(/^\s+|\s+$/g,""))return void alert("要输入弹幕内容啊喂!");var t={player:e.option.danmaku.id,time:e.audio.currentTime,text:S.value,color:e.element.querySelector('input[name="dplayer-danmaku-color"]:checked').value,type:e.element.querySelector('input[name="dplayer-danmaku-type"]:checked').value},n=new XMLHttpRequest;n.onreadystatechange=function(){4===n.readyState&&(n.status>=200&&n.status<300||304===n.status?console.log(JSON.parse(n.responseText)):console.log("Request was unsuccessful: "+n.status))},n.open("post",e.option.danmaku.api,!0),n.send(JSON.stringify(t)),S.value="",P(),e.danmakuIn(t.text,t.color,t.type)});var W=function(){B=E.offsetWidth;for(var t=e.element.getElementsByClassName("dplayer-danmaku-item"),n=0;n\n \n \n \n '},this.isMobile=navigator.userAgent.match(/(iPad)|(iPhone)|(iPod)|(android)|(webOS)/i),this.isMobile&&(t.autoplay=!1);var a={element:document.getElementsByClassName("dplayer")[0],autoplay:!1,theme:"#b7daff",loop:!1};for(var s in a)a.hasOwnProperty(s)&&!t.hasOwnProperty(s)&&(t[s]=a[s]);this.option=t,this.loop=t.loop,this.secondToTime=function(e){var t=function(e){return 10>e?"0"+e:""+e},n=parseInt(e/60),a=parseInt(e-60*n);return t(n)+":"+t(a)},this.updateBar=function(e,t,a){t=t>0?t:0,t=1>t?t:1,n[e+"Bar"].style[a]=100*t+"%"},this.eventTypes=["play","pause","canplay","playing","ended","error"],this.event={};for(var l=0;l\n
\n \n
\n
\n
\n
\n
\n \n
\n \n
\n
\n
\n \n
\n
\n
\n
\n 0:00 / 0:00\n
\n
\n
\n \n
\n
\n
\n
\n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n \n \n
\n
\n
\n \n
\n
\n \n
\n
\n
\n
\n
\n \n
\n
\n
\n
\n '),this.audio=this.element.getElementsByClassName("dplayer-video")[0],this.ptime=this.element.getElementsByClassName("dplayer-ptime")[0],this.playButton=this.element.getElementsByClassName("dplayer-play-icon")[0],this.toggle=function(){e.audio.paused?e.play():e.pause()},this.playButton.addEventListener("click",this.toggle),this.element.getElementsByClassName("dplayer-video-wrap")[0].addEventListener("click",this.toggle),this.element.getElementsByClassName("dplayer-controller-mask")[0].addEventListener("click",this.toggle);var t=function(t){var n=t.offsetLeft,a=t.offsetParent,s=void 0;if(document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement)for(;null!==a&&a!==e.element;)n+=a.offsetLeft,a=a.offsetParent;else for(;null!==a;)n+=a.offsetLeft,a=a.offsetParent;return s=document.body.scrollLeft+document.documentElement.scrollLeft,n-s};this.playedBar=this.element.getElementsByClassName("dplayer-played")[0],this.loadedBar=this.element.getElementsByClassName("dplayer-loaded")[0],this.bar=this.element.getElementsByClassName("dplayer-bar-wrap")[0];var n=void 0;this.audio.addEventListener("seeked",function(){if(e.option.danmaku)for(var t=0;t=e.audio.currentTime)return void(e.danIndex=t)}),this.setTime=function(){e.option.danmaku?e.playedTime=setInterval(function(){e.updateBar("played",e.audio.currentTime/e.audio.duration,"width"),e.ptime.innerHTML=e.secondToTime(e.audio.currentTime),e.trigger("playing");var t=e.dan[e.danIndex];t&&e.audio.currentTime>=parseFloat(t.time)&&(e.danmakuIn(t.text,t.color,t.type),e.danIndex++)},100):e.playedTime=setInterval(function(){e.updateBar("played",e.audio.currentTime/e.audio.duration,"width"),e.ptime.innerHTML=e.secondToTime(e.audio.currentTime),e.trigger("playing")},100)},this.bar.addEventListener("click",function(a){var s=a||window.event;n=e.bar.clientWidth;var l=(s.clientX-t(e.bar))/n;l=l>0?l:0,l=1>l?l:1,e.updateBar("played",l,"width"),e.element.getElementsByClassName("dplayer-ptime")[0].innerHTML=e.secondToTime(l*e.audio.duration),e.audio.currentTime=parseFloat(e.playedBar.style.width)/100*e.audio.duration});var a=function(a){var s=a||window.event,l=(s.clientX-t(e.bar))/n;l=l>0?l:0,l=1>l?l:1,e.updateBar("played",l,"width"),e.element.getElementsByClassName("dplayer-ptime")[0].innerHTML=e.secondToTime(l*e.audio.duration)},s=function A(){document.removeEventListener("mouseup",A),document.removeEventListener("mousemove",a),e.audio.currentTime=parseFloat(e.playedBar.style.width)/100*e.audio.duration,e.setTime()};this.bar.addEventListener("mousedown",function(){n=e.bar.clientWidth,clearInterval(e.playedTime),document.addEventListener("mousemove",a),document.addEventListener("mouseup",s)}),this.volumeBar=this.element.getElementsByClassName("dplayer-volume-bar-inner")[0];var l=this.element.getElementsByClassName("dplayer-volume")[0],i=this.element.getElementsByClassName("dplayer-volume-bar-wrap")[0],d=this.element.getElementsByClassName("dplayer-volume-bar")[0],o=this.element.getElementsByClassName("dplayer-volume-icon")[0],r=35,c=function(){e.audio.volume>=.8?o.innerHTML=e.getSVG("volume-up"):e.audio.volume>0?o.innerHTML=e.getSVG("volume-down"):o.innerHTML=e.getSVG("volume-off")},u=function(n){var a=n||window.event,s=(a.clientX-t(d)-5.5)/r;s=s>0?s:0,s=1>s?s:1,e.updateBar("volume",s,"width"),e.audio.volume=s,e.audio.muted&&(e.audio.muted=!1),c()},p=function J(){document.removeEventListener("mouseup",J),document.removeEventListener("mousemove",u),l.classList.remove("dplayer-volume-active")};i.addEventListener("click",function(n){var a=n||window.event,s=(a.clientX-t(d)-5.5)/r;s=s>0?s:0,s=1>s?s:1,e.updateBar("volume",s,"width"),e.audio.volume=s,e.audio.muted&&(e.audio.muted=!1),c()}),i.addEventListener("mousedown",function(){document.addEventListener("mousemove",u),document.addEventListener("mouseup",p),l.classList.add("dplayer-volume-active")}),o.addEventListener("click",function(){e.audio.muted?(e.audio.muted=!1,c(),e.updateBar("volume",e.audio.volume,"width")):(e.audio.muted=!0,o.innerHTML=e.getSVG("volume-off"),e.updateBar("volume",0,"width"))});var m=0,y=function(){e.element.classList.remove("dplayer-hide-controller"),clearTimeout(m),m=setTimeout(function(){e.audio.played.length&&(e.element.classList.add("dplayer-hide-controller"),b(),X())},2e3)};this.element.addEventListener("mousemove",y),this.element.addEventListener("click",y),this.danOpacity=.7;var v={original:'\n
\n 洗脑循环\n
\n \n \n
\n
\n
\n 速度\n
'+this.getSVG("right")+('
\n
\n
\n 弹幕透明度\n
\n
\n
\n \n
\n
\n
\n
'),speed:'\n
\n 0.5\n
\n
\n 0.75\n
\n
\n 正常\n
\n
\n 1.25\n
\n
\n 1.5\n
\n
\n 2\n
'},h=this.element.getElementsByClassName("dplayer-setting-icon")[0],f=this.element.getElementsByClassName("dplayer-setting-box")[0],g=this.element.getElementsByClassName("dplayer-mask")[0];f.innerHTML=v.original;var b=function(){f.classList.contains("dplayer-setting-box-open")&&(f.classList.remove("dplayer-setting-box-open"),g.classList.remove("dplayer-mask-show"),setTimeout(function(){f.classList.remove("dplayer-setting-box-narrow"),f.innerHTML=v.original,L()},300))},k=function(){f.classList.add("dplayer-setting-box-open"),g.classList.add("dplayer-mask-show")};g.addEventListener("click",function(){b()}),h.addEventListener("click",function(){k()});var L=function(){var n=e.element.getElementsByClassName("dplayer-setting-loop")[0],a=n.getElementsByClassName("dplayer-toggle-setting-input")[0];a.checked=e.loop,n.addEventListener("click",function(){a.checked=!a.checked,a.checked?(e.loop=!0,e.audio.loop=e.loop):(e.loop=!1,e.audio.loop=e.loop),b()}),a.addEventListener("change",function(){a.checked?(e.loop=!0,e.audio.loop=e.loop):(e.loop=!1,e.audio.loop=e.loop),b()});var s=e.element.getElementsByClassName("dplayer-setting-speed")[0];s.addEventListener("click",function(){f.classList.add("dplayer-setting-box-narrow"),f.innerHTML=v.speed;for(var t=f.getElementsByClassName("dplayer-setting-speed-item"),n=function(n){t[n].addEventListener("click",function(){e.audio.playbackRate=t[n].dataset.speed,b()})},a=0;a0?s:0,s=1>s?s:1,e.updateBar("danmaku",s,"width");for(var l=e.element.getElementsByClassName("dplayer-danmaku-item"),d=0;d0?s:0,s=1>s?s:1,e.updateBar("danmaku",s,"width");for(var l=e.element.getElementsByClassName("dplayer-danmaku-item"),d=0;d=i)break;if(l===s.length-1)return q[t][a+""].push(e),e.addEventListener("animationend",function(){q[t][a+""].splice(0,1)}),{v:a%T}}},s=0;;s++){var l=a(s);if("object"===("undefined"==typeof l?"undefined":_typeof(l)))return l.v}};this.danmakuIn=function(t,n,a){B=E.offsetWidth,C=E.offsetHeight,T=C/w;var s=document.createElement("div"),l=document.createTextNode(t);switch(s.classList.add("dplayer-danmaku-item"),s.classList.add("dplayer-danmaku-"+a),s.appendChild(l),s.style.opacity=e.danOpacity,E.appendChild(s),s.style.color=n,a){case"right":s.style.top=w*x(s,a)+"px",s.style.width=s.offsetWidth+1+"px",s.style.transform="translateX(-"+B+"px)",s.addEventListener("animationend",function(){E.removeChild(s)});break;case"top":s.style.top=w*x(s,a)+"px",s.addEventListener("animationend",function(){E.removeChild(s)});break;case"bottom":s.style.bottom=w*x(s,a)+"px",s.addEventListener("animationend",function(){E.removeChild(s)});break;default:console.error("Can't handled danmaku type: "+a)}s.classList.add("dplayer-danmaku-move")},this.option.danmaku?!function(){e.danIndex=0;var t=new XMLHttpRequest;t.onreadystatechange=function(){4===t.readyState&&(t.status>=200&&t.status<300||304===t.status?(e.dan=JSON.parse(t.responseText).danmaku.sort(function(e,t){return e.time-t.time}),console.log(e.dan),e.option.autoplay&&!e.isMobile?e.play():e.isMobile&&e.pause()):console.log("Request was unsuccessful: "+t.status))},t.open("get",e.option.danmaku.api+"?id="+e.option.danmaku.id,!0),t.send(null)}():this.option.autoplay&&!this.isMobile?this.play():this.isMobile&&this.pause();var S=this.element.getElementsByClassName("dplayer-comment-input")[0],z=this.element.getElementsByClassName("dplayer-comment-icon")[0],H=this.element.getElementsByClassName("dplayer-comment-box")[0],F=this.element.getElementsByClassName("dplayer-comment-setting")[0],I=this.element.getElementsByClassName("dplayer-comment-setting-box")[0],G=this.element.getElementsByClassName("dplayer-send-icon")[0],V=function(){if(!S.value.replace(/^\s+|\s+$/g,""))return void alert("要输入弹幕内容啊喂!");var t={token:e.option.danmaku.token,player:e.option.danmaku.id,author:"DIYgod",time:e.audio.currentTime,text:S.value,color:e.element.querySelector('input[name="dplayer-danmaku-color"]:checked').value,type:e.element.querySelector('input[name="dplayer-danmaku-type"]:checked').value},n=new XMLHttpRequest;n.onreadystatechange=function(){4===n.readyState&&(n.status>=200&&n.status<300||304===n.status?console.log(JSON.parse(n.responseText)):console.log("Request was unsuccessful: "+n.status))},n.open("post",e.option.danmaku.api,!0),n.send(JSON.stringify(t)),S.value="",X(),e.danmakuIn(t.text,t.color,t.type)},O=function(){I.classList.contains("dplayer-comment-setting-open")&&I.classList.remove("dplayer-comment-setting-open")},R=function(){I.classList.contains("dplayer-comment-setting-open")?I.classList.remove("dplayer-comment-setting-open"):I.classList.add("dplayer-comment-setting-open")},P=0,X=function(){H.classList.contains("dplayer-comment-box-open")&&(H.classList.remove("dplayer-comment-box-open"),g.classList.remove("dplayer-mask-show"),clearInterval(P),e.element.classList.remove("dplayer-show-controller"),O())},W=function(){H.classList.add("dplayer-comment-box-open"),g.classList.add("dplayer-mask-show"),P=setInterval(function(){clearTimeout(m)},1e3),e.element.classList.add("dplayer-show-controller")};g.addEventListener("click",function(){X()}),z.addEventListener("click",function(){W(),setTimeout(function(){S.focus()},300)}),F.addEventListener("click",function(){R()}),this.element.getElementsByClassName("dplayer-comment-setting-color")[0].addEventListener("click",function(){var t=e.element.querySelector('input[name="dplayer-danmaku-color"]:checked+span'); +t&&F.setAttribute("style",t.getAttribute("style"))}),S.addEventListener("click",function(){O()}),S.addEventListener("keydown",function(e){var t=e||window.event;13===t.keyCode&&V()}),G.addEventListener("click",V);var _=function(){B=E.offsetWidth;for(var t=e.element.getElementsByClassName("dplayer-danmaku-item"),n=0;n {\n\n class DPlayer {\n /**\n * DPlayer constructor function\n *\n * @param {Object} option - See README\n * @constructor\n */\n constructor(option) {\n this.svg = {\n 'play': ['0 0 16 32', 'M15.552 15.168q0.448 0.32 0.448 0.832 0 0.448-0.448 0.768l-13.696 8.512q-0.768 0.512-1.312 0.192t-0.544-1.28v-16.448q0-0.96 0.544-1.28t1.312 0.192z'],\n 'pause': ['0 0 17 32', 'M14.080 4.8q2.88 0 2.88 2.048v18.24q0 2.112-2.88 2.112t-2.88-2.112v-18.24q0-2.048 2.88-2.048zM2.88 4.8q2.88 0 2.88 2.048v18.24q0 2.112-2.88 2.112t-2.88-2.112v-18.24q0-2.048 2.88-2.048z'],\n 'volume-up': ['0 0 21 32', 'M13.728 6.272v19.456q0 0.448-0.352 0.8t-0.8 0.32-0.8-0.32l-5.952-5.952h-4.672q-0.48 0-0.8-0.352t-0.352-0.8v-6.848q0-0.48 0.352-0.8t0.8-0.352h4.672l5.952-5.952q0.32-0.32 0.8-0.32t0.8 0.32 0.352 0.8zM20.576 16q0 1.344-0.768 2.528t-2.016 1.664q-0.16 0.096-0.448 0.096-0.448 0-0.8-0.32t-0.32-0.832q0-0.384 0.192-0.64t0.544-0.448 0.608-0.384 0.512-0.64 0.192-1.024-0.192-1.024-0.512-0.64-0.608-0.384-0.544-0.448-0.192-0.64q0-0.48 0.32-0.832t0.8-0.32q0.288 0 0.448 0.096 1.248 0.48 2.016 1.664t0.768 2.528zM25.152 16q0 2.72-1.536 5.056t-4 3.36q-0.256 0.096-0.448 0.096-0.48 0-0.832-0.352t-0.32-0.8q0-0.704 0.672-1.056 1.024-0.512 1.376-0.8 1.312-0.96 2.048-2.4t0.736-3.104-0.736-3.104-2.048-2.4q-0.352-0.288-1.376-0.8-0.672-0.352-0.672-1.056 0-0.448 0.32-0.8t0.8-0.352q0.224 0 0.48 0.096 2.496 1.056 4 3.36t1.536 5.056zM29.728 16q0 4.096-2.272 7.552t-6.048 5.056q-0.224 0.096-0.448 0.096-0.48 0-0.832-0.352t-0.32-0.8q0-0.64 0.704-1.056 0.128-0.064 0.384-0.192t0.416-0.192q0.8-0.448 1.44-0.896 2.208-1.632 3.456-4.064t1.216-5.152-1.216-5.152-3.456-4.064q-0.64-0.448-1.44-0.896-0.128-0.096-0.416-0.192t-0.384-0.192q-0.704-0.416-0.704-1.056 0-0.448 0.32-0.8t0.832-0.352q0.224 0 0.448 0.096 3.776 1.632 6.048 5.056t2.272 7.552z'],\n 'volume-down': ['0 0 21 32', 'M13.728 6.272v19.456q0 0.448-0.352 0.8t-0.8 0.32-0.8-0.32l-5.952-5.952h-4.672q-0.48 0-0.8-0.352t-0.352-0.8v-6.848q0-0.48 0.352-0.8t0.8-0.352h4.672l5.952-5.952q0.32-0.32 0.8-0.32t0.8 0.32 0.352 0.8zM20.576 16q0 1.344-0.768 2.528t-2.016 1.664q-0.16 0.096-0.448 0.096-0.448 0-0.8-0.32t-0.32-0.832q0-0.384 0.192-0.64t0.544-0.448 0.608-0.384 0.512-0.64 0.192-1.024-0.192-1.024-0.512-0.64-0.608-0.384-0.544-0.448-0.192-0.64q0-0.48 0.32-0.832t0.8-0.32q0.288 0 0.448 0.096 1.248 0.48 2.016 1.664t0.768 2.528z'],\n 'volume-off': ['0 0 21 32', 'M13.728 6.272v19.456q0 0.448-0.352 0.8t-0.8 0.32-0.8-0.32l-5.952-5.952h-4.672q-0.48 0-0.8-0.352t-0.352-0.8v-6.848q0-0.48 0.352-0.8t0.8-0.352h4.672l5.952-5.952q0.32-0.32 0.8-0.32t0.8 0.32 0.352 0.8z'],\n 'loop': ['0 0 32 32', 'M1.882 16.941c0 4.152 3.221 7.529 7.177 7.529v1.882c-4.996 0-9.060-4.222-9.060-9.412s4.064-9.412 9.060-9.412h7.96l-3.098-3.098 1.331-1.331 5.372 5.37-5.37 5.372-1.333-1.333 3.1-3.098h-7.962c-3.957 0-7.177 3.377-7.177 7.529zM22.94 7.529v1.882c3.957 0 7.177 3.377 7.177 7.529s-3.221 7.529-7.177 7.529h-7.962l3.098-3.098-1.331-1.331-5.37 5.37 5.372 5.372 1.331-1.331-3.1-3.1h7.96c4.998 0 9.062-4.222 9.062-9.412s-4.064-9.412-9.060-9.412z'],\n 'full': ['0 0 32 33', 'M6.667 28h-5.333c-0.8 0-1.333-0.533-1.333-1.333v-5.333c0-0.8 0.533-1.333 1.333-1.333s1.333 0.533 1.333 1.333v4h4c0.8 0 1.333 0.533 1.333 1.333s-0.533 1.333-1.333 1.333zM30.667 28h-5.333c-0.8 0-1.333-0.533-1.333-1.333s0.533-1.333 1.333-1.333h4v-4c0-0.8 0.533-1.333 1.333-1.333s1.333 0.533 1.333 1.333v5.333c0 0.8-0.533 1.333-1.333 1.333zM30.667 12c-0.8 0-1.333-0.533-1.333-1.333v-4h-4c-0.8 0-1.333-0.533-1.333-1.333s0.533-1.333 1.333-1.333h5.333c0.8 0 1.333 0.533 1.333 1.333v5.333c0 0.8-0.533 1.333-1.333 1.333zM1.333 12c-0.8 0-1.333-0.533-1.333-1.333v-5.333c0-0.8 0.533-1.333 1.333-1.333h5.333c0.8 0 1.333 0.533 1.333 1.333s-0.533 1.333-1.333 1.333h-4v4c0 0.8-0.533 1.333-1.333 1.333z'],\n 'setting': ['0 0 32 28', 'M28.633 17.104c0.035 0.21 0.026 0.463-0.026 0.76s-0.14 0.598-0.262 0.904c-0.122 0.306-0.271 0.581-0.445 0.825s-0.367 0.419-0.576 0.524c-0.209 0.105-0.393 0.157-0.55 0.157s-0.332-0.035-0.524-0.105c-0.175-0.052-0.393-0.1-0.655-0.144s-0.528-0.052-0.799-0.026c-0.271 0.026-0.541 0.083-0.812 0.17s-0.502 0.236-0.694 0.445c-0.419 0.437-0.664 0.934-0.734 1.493s0.009 1.092 0.236 1.598c0.175 0.349 0.148 0.699-0.079 1.048-0.105 0.14-0.271 0.284-0.498 0.432s-0.476 0.284-0.747 0.406-0.555 0.218-0.851 0.288c-0.297 0.070-0.559 0.105-0.786 0.105-0.157 0-0.306-0.061-0.445-0.183s-0.236-0.253-0.288-0.393h-0.026c-0.192-0.541-0.52-1.009-0.982-1.402s-1-0.589-1.611-0.589c-0.594 0-1.131 0.197-1.611 0.589s-0.816 0.851-1.009 1.375c-0.087 0.21-0.218 0.362-0.393 0.458s-0.367 0.144-0.576 0.144c-0.244 0-0.52-0.044-0.825-0.131s-0.611-0.197-0.917-0.327c-0.306-0.131-0.581-0.284-0.825-0.458s-0.428-0.349-0.55-0.524c-0.087-0.122-0.135-0.266-0.144-0.432s0.057-0.397 0.197-0.694c0.192-0.402 0.266-0.86 0.223-1.375s-0.266-0.991-0.668-1.428c-0.244-0.262-0.541-0.432-0.891-0.511s-0.681-0.109-0.995-0.092c-0.367 0.017-0.742 0.087-1.127 0.21-0.244 0.070-0.489 0.052-0.734-0.052-0.192-0.070-0.371-0.231-0.537-0.485s-0.314-0.533-0.445-0.838c-0.131-0.306-0.231-0.62-0.301-0.943s-0.087-0.59-0.052-0.799c0.052-0.384 0.227-0.629 0.524-0.734 0.524-0.21 0.995-0.555 1.415-1.035s0.629-1.017 0.629-1.611c0-0.611-0.21-1.144-0.629-1.598s-0.891-0.786-1.415-0.996c-0.157-0.052-0.288-0.179-0.393-0.38s-0.157-0.406-0.157-0.616c0-0.227 0.035-0.48 0.105-0.76s0.162-0.55 0.275-0.812 0.244-0.502 0.393-0.72c0.148-0.218 0.31-0.38 0.485-0.485 0.14-0.087 0.275-0.122 0.406-0.105s0.275 0.052 0.432 0.105c0.524 0.21 1.070 0.275 1.637 0.197s1.070-0.327 1.506-0.747c0.21-0.209 0.362-0.467 0.458-0.773s0.157-0.607 0.183-0.904c0.026-0.297 0.026-0.568 0-0.812s-0.048-0.419-0.065-0.524c-0.035-0.105-0.066-0.227-0.092-0.367s-0.013-0.262 0.039-0.367c0.105-0.244 0.293-0.458 0.563-0.642s0.563-0.336 0.878-0.458c0.314-0.122 0.62-0.214 0.917-0.275s0.533-0.092 0.707-0.092c0.227 0 0.406 0.074 0.537 0.223s0.223 0.301 0.275 0.458c0.192 0.471 0.507 0.886 0.943 1.244s0.952 0.537 1.546 0.537c0.611 0 1.153-0.17 1.624-0.511s0.803-0.773 0.996-1.297c0.070-0.14 0.179-0.284 0.327-0.432s0.301-0.223 0.458-0.223c0.244 0 0.511 0.035 0.799 0.105s0.572 0.166 0.851 0.288c0.279 0.122 0.537 0.279 0.773 0.472s0.423 0.402 0.563 0.629c0.087 0.14 0.113 0.293 0.079 0.458s-0.070 0.284-0.105 0.354c-0.227 0.506-0.297 1.039-0.21 1.598s0.341 1.048 0.76 1.467c0.419 0.419 0.934 0.651 1.546 0.694s1.179-0.057 1.703-0.301c0.14-0.087 0.31-0.122 0.511-0.105s0.371 0.096 0.511 0.236c0.262 0.244 0.493 0.616 0.694 1.113s0.336 1 0.406 1.506c0.035 0.297-0.013 0.528-0.144 0.694s-0.266 0.275-0.406 0.327c-0.542 0.192-1.004 0.528-1.388 1.009s-0.576 1.026-0.576 1.637c0 0.594 0.162 1.113 0.485 1.559s0.747 0.764 1.27 0.956c0.122 0.070 0.227 0.14 0.314 0.21 0.192 0.157 0.323 0.358 0.393 0.602v0zM16.451 19.462c0.786 0 1.528-0.149 2.227-0.445s1.305-0.707 1.821-1.231c0.515-0.524 0.921-1.131 1.218-1.821s0.445-1.428 0.445-2.214c0-0.786-0.148-1.524-0.445-2.214s-0.703-1.292-1.218-1.808c-0.515-0.515-1.122-0.921-1.821-1.218s-1.441-0.445-2.227-0.445c-0.786 0-1.524 0.148-2.214 0.445s-1.292 0.703-1.808 1.218c-0.515 0.515-0.921 1.118-1.218 1.808s-0.445 1.428-0.445 2.214c0 0.786 0.149 1.524 0.445 2.214s0.703 1.297 1.218 1.821c0.515 0.524 1.118 0.934 1.808 1.231s1.428 0.445 2.214 0.445v0z'],\n 'right': ['0 0 32 32', 'M22 16l-10.105-10.6-1.895 1.987 8.211 8.613-8.211 8.612 1.895 1.988 8.211-8.613z'],\n 'comment': ['0 0 32 32', 'M27.128 0.38h-22.553c-2.336 0-4.229 1.825-4.229 4.076v16.273c0 2.251 1.893 4.076 4.229 4.076h4.229v-2.685h8.403l-8.784 8.072 1.566 1.44 7.429-6.827h9.71c2.335 0 4.229-1.825 4.229-4.076v-16.273c0-2.252-1.894-4.076-4.229-4.076zM28.538 19.403c0 1.5-1.262 2.717-2.819 2.717h-8.36l-0.076-0.070-0.076 0.070h-11.223c-1.557 0-2.819-1.217-2.819-2.717v-13.589c0-1.501 1.262-2.718 2.819-2.718h19.734c1.557 0 2.819-0.141 2.819 1.359v14.947zM9.206 10.557c-1.222 0-2.215 0.911-2.215 2.036s0.992 2.035 2.215 2.035c1.224 0 2.216-0.911 2.216-2.035s-0.992-2.036-2.216-2.036zM22.496 10.557c-1.224 0-2.215 0.911-2.215 2.036s0.991 2.035 2.215 2.035c1.224 0 2.215-0.911 2.215-2.035s-0.991-2.036-2.215-2.036zM15.852 10.557c-1.224 0-2.215 0.911-2.215 2.036s0.991 2.035 2.215 2.035c1.222 0 2.215-0.911 2.215-2.035s-0.992-2.036-2.215-2.036z'],\n 'comment-off': ['0 0 32 32', 'M27.090 0.131h-22.731c-2.354 0-4.262 1.839-4.262 4.109v16.401c0 2.269 1.908 4.109 4.262 4.109h4.262v-2.706h8.469l-8.853 8.135 1.579 1.451 7.487-6.88h9.787c2.353 0 4.262-1.84 4.262-4.109v-16.401c0-2.27-1.909-4.109-4.262-4.109v0zM28.511 19.304c0 1.512-1.272 2.738-2.841 2.738h-8.425l-0.076-0.070-0.076 0.070h-11.311c-1.569 0-2.841-1.226-2.841-2.738v-13.696c0-1.513 1.272-2.739 2.841-2.739h19.889c1.569 0 2.841-0.142 2.841 1.37v15.064z'],\n 'send': ['0 0 32 32', 'M13.725 30l3.9-5.325-3.9-1.125v6.45zM0 17.5l11.050 3.35 13.6-11.55-10.55 12.425 11.8 3.65 6.1-23.375-32 15.5z']\n };\n this.getSVG = (type) => {\n return `\n \n \n \n \n `;\n };\n\n this.isMobile = navigator.userAgent.match(/(iPad)|(iPhone)|(iPod)|(android)|(webOS)/i);\n // compatibility: some mobile browsers don't suppose autoplay\n if (this.isMobile) {\n option.autoplay = false;\n }\n\n // default options\n const defaultOption = {\n element: document.getElementsByClassName('dplayer')[0],\n autoplay: false,\n theme: '#b7daff',\n loop: false\n };\n for (let defaultKey in defaultOption) {\n if (defaultOption.hasOwnProperty(defaultKey) && !option.hasOwnProperty(defaultKey)) {\n option[defaultKey] = defaultOption[defaultKey];\n }\n }\n\n this.option = option;\n this.loop = option.loop;\n\n /**\n * Parse second to 00:00 format\n *\n * @param {Number} second\n * @return {String} 00:00 format\n */\n this.secondToTime = (second) => {\n const add0 = (num) => {\n return num < 10 ? '0' + num : '' + num;\n };\n const min = parseInt(second / 60);\n const sec = parseInt(second - min * 60);\n return add0(min) + ':' + add0(sec);\n };\n\n /**\n * Update progress bar, including loading progress bar and play progress bar\n *\n * @param {String} type - Point out which bar it is, should be played loaded or volume\n * @param {Number} percentage\n * @param {String} direction - Point out the direction of this bar, Should be height or width\n */\n this.updateBar = (type, percentage, direction) => {\n percentage = percentage > 0 ? percentage : 0;\n percentage = percentage < 1 ? percentage : 1;\n this[type + 'Bar'].style[direction] = percentage * 100 + '%';\n };\n\n // define DPlayer events\n this.eventTypes = ['play', 'pause', 'canplay', 'playing', 'ended', 'error'];\n this.event = {};\n for (let i = 0; i < this.eventTypes.length; i++) {\n this.event[this.eventTypes[i]] = [];\n }\n this.trigger = (type) => {\n for (let i = 0; i < this.event[type].length; i++) {\n this.event[type][i]();\n }\n }\n }\n\n /**\n * AutoLink initialization function\n */\n init() {\n this.element = this.option.element;\n\n this.element.innerHTML = `\n
\n
\n \n
\n
\n
\n
\n
\n \n
\n \n
\n
\n
\n \n
\n
\n
\n
\n 0:00 / 0:00\n
\n
\n
\n \n
\n
\n
\n
\n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n \n \n
\n
\n
\n \n
\n
\n \n
\n
\n
\n
\n
\n \n
\n
\n
\n
\n `;\n\n // get this audio object\n this.audio = this.element.getElementsByClassName('dplayer-video')[0];\n\n this.ptime = this.element.getElementsByClassName('dplayer-ptime')[0];\n\n // play and pause button\n this.playButton = this.element.getElementsByClassName('dplayer-play-icon')[0];\n this.toggle = () => {\n if (this.audio.paused) {\n this.play();\n }\n else {\n this.pause();\n }\n };\n this.playButton.addEventListener('click', this.toggle);\n this.element.getElementsByClassName('dplayer-video-wrap')[0].addEventListener('click', this.toggle);\n this.element.getElementsByClassName('dplayer-controller-mask')[0].addEventListener('click', this.toggle);\n\n\n /**\n * control play progress\n */\n // get element's view position\n const getElementViewLeft = (element) => {\n let actualLeft = element.offsetLeft;\n let current = element.offsetParent;\n let elementScrollLeft;\n if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) {\n while (current !== null) {\n actualLeft += current.offsetLeft;\n current = current.offsetParent;\n }\n }\n else {\n while (current !== null && current !== this.element) {\n actualLeft += current.offsetLeft;\n current = current.offsetParent;\n }\n }\n elementScrollLeft = document.body.scrollLeft + document.documentElement.scrollLeft;\n return actualLeft - elementScrollLeft;\n };\n\n const getElementViewTop = (element) => {\n let actualTop = element.offsetTop;\n let current = element.offsetParent;\n let elementScrollTop;\n if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) {\n while (current !== null) {\n actualTop += current.offsetTop;\n current = current.offsetParent;\n }\n }\n else {\n while (current !== null && current !== this.element) {\n actualTop += current.offsetTop;\n current = current.offsetParent;\n }\n }\n elementScrollTop = document.body.scrollTop + document.documentElement.scrollTop;\n return actualTop - elementScrollTop;\n };\n\n this.playedBar = this.element.getElementsByClassName('dplayer-played')[0];\n this.loadedBar = this.element.getElementsByClassName('dplayer-loaded')[0];\n this.bar = this.element.getElementsByClassName('dplayer-bar-wrap')[0];\n let barWidth;\n\n this.audio.addEventListener('seeked', () => {\n if (this.option.danmaku) {\n for (let i = 0; i < this.dan.length; i++) {\n if (this.dan[i].time >= this.audio.currentTime) {\n this.danIndex = i;\n return;\n }\n }\n }\n });\n\n this.setTime = () => {\n if (this.option.danmaku) {\n this.playedTime = setInterval(() => {\n this.updateBar('played', this.audio.currentTime / this.audio.duration, 'width');\n this.ptime.innerHTML = this.secondToTime(this.audio.currentTime);\n this.trigger('playing');\n\n const item = this.dan[this.danIndex];\n if (item && this.audio.currentTime >= parseFloat(item.time)) {\n this.danmakuIn(item.text, item.color, item.type);\n this.danIndex++;\n }\n }, 100);\n }\n else {\n this.playedTime = setInterval(() => {\n this.updateBar('played', this.audio.currentTime / this.audio.duration, 'width');\n this.ptime.innerHTML = this.secondToTime(this.audio.currentTime);\n this.trigger('playing');\n }, 100);\n }\n };\n\n this.bar.addEventListener('click', (event) => {\n const e = event || window.event;\n barWidth = this.bar.clientWidth;\n let percentage = (e.clientX - getElementViewLeft(this.bar)) / barWidth;\n percentage = percentage > 0 ? percentage : 0;\n percentage = percentage < 1 ? percentage : 1;\n this.updateBar('played', percentage, 'width');\n this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = this.secondToTime(percentage * this.audio.duration);\n this.audio.currentTime = parseFloat(this.playedBar.style.width) / 100 * this.audio.duration;\n });\n\n const thumbMove = (event) => {\n const e = event || window.event;\n let percentage = (e.clientX - getElementViewLeft(this.bar)) / barWidth;\n percentage = percentage > 0 ? percentage : 0;\n percentage = percentage < 1 ? percentage : 1;\n this.updateBar('played', percentage, 'width');\n this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = this.secondToTime(percentage * this.audio.duration);\n };\n\n const thumbUp = () => {\n document.removeEventListener('mouseup', thumbUp);\n document.removeEventListener('mousemove', thumbMove);\n this.audio.currentTime = parseFloat(this.playedBar.style.width) / 100 * this.audio.duration;\n this.setTime();\n };\n\n this.bar.addEventListener('mousedown', () => {\n barWidth = this.bar.clientWidth;\n clearInterval(this.playedTime);\n document.addEventListener('mousemove', thumbMove);\n document.addEventListener('mouseup', thumbUp);\n });\n\n\n /**\n * control volume\n */\n this.volumeBar = this.element.getElementsByClassName('dplayer-volume-bar-inner')[0];\n const volumeEle = this.element.getElementsByClassName('dplayer-volume')[0];\n const volumeBarWrapWrap = this.element.getElementsByClassName('dplayer-volume-bar-wrap')[0];\n const volumeBarWrap = this.element.getElementsByClassName('dplayer-volume-bar')[0];\n const volumeicon = this.element.getElementsByClassName('dplayer-volume-icon')[0];\n const vWidth = 35;\n const switchVolumeIcon = () => {\n if (this.audio.volume >= 0.8) {\n volumeicon.innerHTML = this.getSVG('volume-up');\n }\n else if (this.audio.volume > 0) {\n volumeicon.innerHTML = this.getSVG('volume-down');\n }\n else {\n volumeicon.innerHTML = this.getSVG('volume-off');\n }\n };\n const volumeMove = (event) => {\n const e = event || window.event;\n let percentage = (e.clientX - getElementViewLeft(volumeBarWrap) - 5.5) / vWidth;\n percentage = percentage > 0 ? percentage : 0;\n percentage = percentage < 1 ? percentage : 1;\n this.updateBar('volume', percentage, 'width');\n this.audio.volume = percentage;\n if (this.audio.muted) {\n this.audio.muted = false;\n }\n switchVolumeIcon();\n };\n const volumeUp = () => {\n document.removeEventListener('mouseup', volumeUp);\n document.removeEventListener('mousemove', volumeMove);\n volumeEle.classList.remove('dplayer-volume-active');\n };\n\n volumeBarWrapWrap.addEventListener('click', (event) => {\n const e = event || window.event;\n let percentage = (e.clientX - getElementViewLeft(volumeBarWrap) - 5.5) / vWidth;\n percentage = percentage > 0 ? percentage : 0;\n percentage = percentage < 1 ? percentage : 1;\n this.updateBar('volume', percentage, 'width');\n this.audio.volume = percentage;\n if (this.audio.muted) {\n this.audio.muted = false;\n }\n switchVolumeIcon();\n });\n volumeBarWrapWrap.addEventListener('mousedown', () => {\n document.addEventListener('mousemove', volumeMove);\n document.addEventListener('mouseup', volumeUp);\n volumeEle.classList.add('dplayer-volume-active');\n });\n volumeicon.addEventListener('click', () => {\n if (this.audio.muted) {\n this.audio.muted = false;\n switchVolumeIcon();\n this.updateBar('volume', this.audio.volume, 'width');\n }\n else {\n this.audio.muted = true;\n volumeicon.innerHTML = this.getSVG('volume-off');\n this.updateBar('volume', 0, 'width');\n }\n });\n\n\n /**\n * auto hide controller\n */\n let hideTime = 0;\n const hideController = () => {\n this.element.classList.remove('dplayer-hide-controller');\n clearTimeout(hideTime);\n hideTime = setTimeout(() => {\n if (this.audio.played.length) {\n this.element.classList.add('dplayer-hide-controller');\n closeSetting();\n closeComment();\n }\n }, 2000);\n };\n this.element.addEventListener('mousemove', hideController);\n this.element.addEventListener('click', hideController);\n\n\n /***\n * setting\n */\n this.danOpacity = 0.7;\n const settingHTML = {\n 'original': `\n
\n 洗脑循环\n
\n \n \n
\n
\n
\n 速度\n
`\n + this.getSVG('right')\n + `
\n
\n
\n 弹幕透明度\n
\n
\n
\n \n
\n
\n
\n
`,\n 'speed': `\n
\n 0.5\n
\n
\n 0.75\n
\n
\n 正常\n
\n
\n 1.25\n
\n
\n 1.5\n
\n
\n 2\n
`\n };\n\n // toggle setting box\n const settingIcon = this.element.getElementsByClassName('dplayer-setting-icon')[0];\n const settingBox = this.element.getElementsByClassName('dplayer-setting-box')[0];\n const mask = this.element.getElementsByClassName('dplayer-mask')[0];\n settingBox.innerHTML = settingHTML.original;\n\n const closeSetting = () => {\n if (settingBox.classList.contains('dplayer-setting-box-open')) {\n settingBox.classList.remove('dplayer-setting-box-open');\n mask.classList.remove('dplayer-mask-show');\n setTimeout(() => {\n settingBox.classList.remove('dplayer-setting-box-narrow');\n settingBox.innerHTML = settingHTML.original;\n settingEvent();\n }, 300);\n }\n };\n const openSetting = () => {\n settingBox.classList.add('dplayer-setting-box-open');\n mask.classList.add('dplayer-mask-show');\n };\n\n mask.addEventListener('click', () => {\n closeSetting();\n });\n settingIcon.addEventListener('click', () => {\n openSetting();\n });\n\n const settingEvent = () => {\n // loop control\n const loopEle = this.element.getElementsByClassName('dplayer-setting-loop')[0];\n const loopToggle = loopEle.getElementsByClassName('dplayer-toggle-setting-input')[0];\n\n loopToggle.checked = this.loop;\n\n loopEle.addEventListener('click', () => {\n loopToggle.checked = !loopToggle.checked;\n if (loopToggle.checked) {\n this.loop = true;\n this.audio.loop = this.loop;\n }\n else {\n this.loop = false;\n this.audio.loop = this.loop;\n }\n closeSetting();\n });\n loopToggle.addEventListener('change', () => {\n if (loopToggle.checked) {\n this.loop = true;\n this.audio.loop = this.loop;\n }\n else {\n this.loop = false;\n this.audio.loop = this.loop;\n }\n closeSetting();\n });\n\n // speed control\n const speedEle = this.element.getElementsByClassName('dplayer-setting-speed')[0];\n speedEle.addEventListener('click', () => {\n settingBox.classList.add('dplayer-setting-box-narrow');\n settingBox.innerHTML = settingHTML.speed;\n\n const speedItem = settingBox.getElementsByClassName('dplayer-setting-speed-item');\n for (let i = 0; i < speedItem.length; i++) {\n speedItem[i].addEventListener('click', () => {\n this.audio.playbackRate = speedItem[i].dataset.speed;\n closeSetting();\n });\n }\n });\n\n // danmaku opacity\n this.danmakuBar = this.element.getElementsByClassName('dplayer-danmaku-bar-inner')[0];\n const danmakuBarWrapWrap = this.element.getElementsByClassName('dplayer-danmaku-bar-wrap')[0];\n const danmakuBarWrap = this.element.getElementsByClassName('dplayer-danmaku-bar')[0];\n const danmakuSettingBox = this.element.getElementsByClassName('dplayer-setting-danmaku')[0];\n const dWidth = 130;\n this.updateBar('danmaku', this.danOpacity, 'width');\n\n const danmakuMove = (event) => {\n const e = event || window.event;\n let percentage = (e.clientX - getElementViewLeft(danmakuBarWrap)) / dWidth;\n percentage = percentage > 0 ? percentage : 0;\n percentage = percentage < 1 ? percentage : 1;\n this.updateBar('danmaku', percentage, 'width');\n const items = this.element.getElementsByClassName('dplayer-danmaku-item');\n for (let i = 0; i < items.length; i++) {\n items[i].style.opacity = percentage;\n }\n this.danOpacity = percentage;\n };\n const danmakuUp = () => {\n document.removeEventListener('mouseup', danmakuUp);\n document.removeEventListener('mousemove', danmakuMove);\n danmakuSettingBox.classList.remove('dplayer-setting-danmaku-active');\n };\n\n danmakuBarWrapWrap.addEventListener('click', (event) => {\n const e = event || window.event;\n let percentage = (e.clientX - getElementViewLeft(danmakuBarWrap)) / dWidth;\n percentage = percentage > 0 ? percentage : 0;\n percentage = percentage < 1 ? percentage : 1;\n this.updateBar('danmaku', percentage, 'width');\n const items = this.element.getElementsByClassName('dplayer-danmaku-item');\n for (let i = 0; i < items.length; i++) {\n items[i].style.opacity = percentage;\n }\n this.danOpacity = percentage;\n });\n danmakuBarWrapWrap.addEventListener('mousedown', () => {\n document.addEventListener('mousemove', danmakuMove);\n document.addEventListener('mouseup', danmakuUp);\n danmakuSettingBox.classList.add('dplayer-setting-danmaku-active');\n });\n };\n settingEvent();\n\n\n /**\n * audio events\n */\n // show audio time: the metadata has loaded or changed\n this.audio.addEventListener('durationchange', () => {\n if (this.audio.duration !== 1) { // compatibility: Android browsers will output 1 at first\n this.element.getElementsByClassName('dplayer-dtime')[0].innerHTML = this.secondToTime(this.audio.duration);\n }\n });\n\n // show audio loaded bar: to inform interested parties of progress downloading the media\n this.audio.addEventListener('progress', () => {\n const percentage = this.audio.buffered.length ? this.audio.buffered.end(this.audio.buffered.length - 1) / this.audio.duration : 0;\n this.updateBar('loaded', percentage, 'width');\n });\n\n // audio download error: an error occurs\n this.audio.addEventListener('error', () => {\n this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = `Error happens ╥﹏╥`;\n this.trigger('pause');\n });\n\n // audio can play: enough data is available that the media can be played\n this.audio.addEventListener('canplay', () => {\n this.trigger('canplay');\n });\n\n // music end\n this.ended = false;\n this.audio.addEventListener('ended', () => {\n this.updateBar('played', 1, 'width');\n if (!this.loop) {\n this.ended = true;\n this.pause();\n this.trigger('ended');\n }\n });\n\n // control volume\n this.audio.volume = parseInt(this.element.getElementsByClassName('dplayer-volume-bar-inner')[0].style.width) / 100;\n\n // loop\n this.audio.loop = this.loop;\n\n // set duration time\n if (this.audio.duration !== 1) { // compatibility: Android browsers will output 1 at first\n this.element.getElementsByClassName('dplayer-dtime')[0].innerHTML = this.audio.duration ? this.secondToTime(this.audio.duration) : '00:00';\n }\n\n\n /**\n * danmaku display\n */\n const danContainer = this.element.getElementsByClassName('dplayer-danmaku')[0];\n const itemHeight = 30;\n let danWidth;\n let danHeight;\n let itemY;\n let danTunnel = {\n right: {},\n top: {},\n bottom: {}\n };\n\n const danItemRight = (ele) => {\n return danContainer.getBoundingClientRect().right - ele.getBoundingClientRect().right;\n };\n\n const danSpeed = (ele) => {\n return (danWidth + ele.offsetWidth) / 5;\n };\n\n const getTunnel = (ele, type) => {\n const tmp = danWidth / danSpeed(ele);\n\n for (let i = 0; ; i++) {\n let item = danTunnel[type][i + ''];\n if (item && item.length) {\n for (let j = 0; j < item.length; j++) {\n const danRight = danItemRight(item[j]) - 10;\n if (danRight <= 640 - (tmp * danSpeed(item[j])) || danRight <= 0) {\n break;\n }\n if (j === item.length - 1) {\n danTunnel[type][i + ''].push(ele);\n ele.addEventListener('animationend', () => {\n danTunnel[type][i + ''].splice(0, 1);\n });\n return i % itemY;\n }\n }\n }\n else {\n danTunnel[type][i + ''] = [ele];\n ele.addEventListener('animationend', () => {\n danTunnel[type][i + ''].splice(0, 1);\n });\n return i % itemY;\n }\n }\n };\n\n this.danmakuIn = (text, color, type) => {\n danWidth = danContainer.offsetWidth;\n danHeight = danContainer.offsetHeight;\n itemY = danHeight / itemHeight;\n let item = document.createElement(`div`);\n let content = document.createTextNode(text);\n item.classList.add(`dplayer-danmaku-item`);\n item.classList.add(`dplayer-danmaku-${type}`);\n item.appendChild(content);\n item.style.opacity = this.danOpacity;\n\n // insert\n danContainer.appendChild(item);\n\n // adjust\n item.style.color = color;\n switch (type) {\n case 'right':\n item.style.top = itemHeight * getTunnel(item, type) + 'px';\n item.style.width = (item.offsetWidth + 1) + 'px';\n item.style.transform = `translateX(-${danWidth}px)`;\n item.addEventListener('animationend', () => {\n danContainer.removeChild(item);\n });\n break;\n case 'top':\n item.style.top = itemHeight * getTunnel(item, type) + 'px';\n item.addEventListener('animationend', () => {\n danContainer.removeChild(item);\n });\n break;\n case 'bottom':\n item.style.bottom = itemHeight * getTunnel(item, type) + 'px';\n item.addEventListener('animationend', () => {\n danContainer.removeChild(item);\n });\n break;\n default:\n console.error(`Can't handled danmaku type: ${type}`);\n }\n\n // move\n item.classList.add(`dplayer-danmaku-move`);\n };\n \n // danmaku\n if (this.option.danmaku) {\n this.danIndex = 0;\n const xhr = new XMLHttpRequest();\n xhr.onreadystatechange = () => {\n if (xhr.readyState === 4) {\n if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {\n this.dan = JSON.parse(xhr.responseText).danmaku.sort((a, b) => a.time - b.time);\n console.log(this.dan);\n\n // autoplay\n if (this.option.autoplay && !this.isMobile) {\n this.play();\n }\n else if (this.isMobile) {\n this.pause();\n }\n }\n else {\n console.log('Request was unsuccessful: ' + xhr.status);\n }\n }\n };\n xhr.open('get', this.option.danmaku.api + '?id=' + this.option.danmaku.id, true);\n xhr.send(null);\n }\n else {\n // autoplay\n if (this.option.autoplay && !this.isMobile) {\n this.play();\n }\n else if (this.isMobile) {\n this.pause();\n }\n }\n\n\n /**\n * comment\n */\n const commentInput = this.element.getElementsByClassName('dplayer-comment-input')[0];\n const commentIcon = this.element.getElementsByClassName('dplayer-comment-icon')[0];\n const commentBox = this.element.getElementsByClassName('dplayer-comment-box')[0];\n const commentSettingIcon = this.element.getElementsByClassName('dplayer-comment-setting')[0];\n const commentSettingBox = this.element.getElementsByClassName('dplayer-comment-setting-box')[0];\n const commentSendIcon = this.element.getElementsByClassName('dplayer-send-icon')[0];\n\n const closeCommentSetting = () => {\n if (commentSettingBox.classList.contains('dplayer-comment-setting-open')) {\n commentSettingBox.classList.remove('dplayer-comment-setting-open');\n }\n };\n const toggleCommentSetting = () => {\n if (commentSettingBox.classList.contains('dplayer-comment-setting-open')) {\n commentSettingBox.classList.remove('dplayer-comment-setting-open');\n }\n else {\n commentSettingBox.classList.add('dplayer-comment-setting-open');\n }\n };\n\n let disableHide = 0;\n const closeComment = () => {\n if (commentBox.classList.contains('dplayer-comment-box-open')) {\n commentBox.classList.remove('dplayer-comment-box-open');\n mask.classList.remove('dplayer-mask-show');\n clearInterval(disableHide);\n this.element.classList.remove('dplayer-show-controller');\n closeCommentSetting();\n }\n };\n const openComment = () => {\n commentBox.classList.add('dplayer-comment-box-open');\n mask.classList.add('dplayer-mask-show');\n disableHide = setInterval(() => {\n clearTimeout(hideTime);\n }, 1000);\n this.element.classList.add('dplayer-show-controller');\n };\n\n mask.addEventListener('click', () => {\n closeComment();\n });\n commentIcon.addEventListener('click', () => {\n openComment();\n setTimeout(() => {\n commentInput.focus();\n }, 300);\n });\n commentSettingIcon.addEventListener('click', () => {\n toggleCommentSetting();\n });\n\n // comment setting box\n this.element.getElementsByClassName('dplayer-comment-setting-color')[0].addEventListener('click', () => {\n const sele = this.element.querySelector('input[name=\"dplayer-danmaku-color\"]:checked+span');\n if (sele) {\n commentSettingIcon.setAttribute('style', sele.getAttribute('style'));\n }\n });\n\n commentInput.addEventListener('click', () => {\n closeCommentSetting();\n });\n\n commentSendIcon.addEventListener('click', () => {\n // text can't be empty\n if (!commentInput.value.replace(/^\\s+|\\s+$/g, '')) {\n alert('要输入弹幕内容啊喂!');\n return;\n }\n\n const danmakuData = {\n player: this.option.danmaku.id,\n time: this.audio.currentTime,\n text: commentInput.value,\n color: this.element.querySelector('input[name=\"dplayer-danmaku-color\"]:checked').value,\n type: this.element.querySelector('input[name=\"dplayer-danmaku-type\"]:checked').value\n };\n const xhr = new XMLHttpRequest();\n xhr.onreadystatechange = () => {\n if (xhr.readyState === 4) {\n if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {\n console.log(JSON.parse(xhr.responseText));\n }\n else {\n console.log('Request was unsuccessful: ' + xhr.status);\n }\n }\n };\n xhr.open('post', this.option.danmaku.api, true);\n xhr.send(JSON.stringify(danmakuData));\n\n commentInput.value = '';\n closeComment();\n this.danmakuIn(danmakuData.text, danmakuData.color, danmakuData.type);\n });\n\n\n /**\n * full screen\n */\n const resetAnimation = () => {\n danWidth = danContainer.offsetWidth;\n const items = this.element.getElementsByClassName('dplayer-danmaku-item');\n for (let i = 0; i < items.length; i++) {\n items[i].style.transform = `translateX(-${danWidth}px)`;\n }\n };\n\n this.element.addEventListener('fullscreenchange', () => {\n resetAnimation();\n });\n this.element.addEventListener('mozfullscreenchange', () => {\n resetAnimation();\n });\n this.element.addEventListener('webkitfullscreenchange', () => {\n resetAnimation();\n });\n this.element.getElementsByClassName('dplayer-full-icon')[0].addEventListener('click', () => {\n if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) {\n if (this.element.requestFullscreen) {\n this.element.requestFullscreen();\n }\n else if (this.element.mozRequestFullScreen) {\n this.element.mozRequestFullScreen();\n }\n else if (this.element.webkitRequestFullscreen) {\n this.element.webkitRequestFullscreen();\n }\n }\n else {\n if (document.cancelFullScreen) {\n document.cancelFullScreen();\n }\n else if (document.mozCancelFullScreen) {\n document.mozCancelFullScreen();\n }\n else if (document.webkitCancelFullScreen) {\n document.webkitCancelFullScreen();\n }\n }\n resetAnimation();\n });\n }\n\n /**\n * Play music\n */\n play() {\n if (this.audio.paused) {\n this.playButton.innerHTML = this.getSVG('pause');\n\n this.audio.play();\n if (this.playedTime) {\n clearInterval(this.playedTime);\n }\n this.setTime();\n this.element.classList.add('dplayer-playing');\n this.trigger('play');\n }\n }\n\n /**\n * Pause music\n */\n pause() {\n if (!this.audio.paused || this.ended) {\n this.ended = false;\n this.playButton.innerHTML = this.getSVG('play');\n this.audio.pause();\n clearInterval(this.playedTime);\n this.element.classList.remove('dplayer-playing');\n this.trigger('pause');\n }\n }\n\n /**\n * attach event\n */\n on(name, func) {\n if (typeof func === 'function') {\n this.event[name].push(func);\n }\n }\n}\n\nif (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {\n module.exports = DPlayer\n}\nelse {\n window.DPlayer = DPlayer;\n}\n})();"],"sourceRoot":"/source/"} \ No newline at end of file +{"version":3,"sources":["DPlayer.js"],"names":["_classCallCheck","instance","Constructor","TypeError","_typeof","Symbol","iterator","obj","constructor","_createClass","defineProperties","target","props","i","length","descriptor","enumerable","configurable","writable","Object","defineProperty","key","protoProps","staticProps","prototype","DPlayer","option","_this","this","svg","play","pause","volume-up","volume-down","volume-off","loop","full","setting","right","comment","comment-off","send","getSVG","type","isMobile","navigator","userAgent","match","autoplay","defaultOption","element","document","getElementsByClassName","theme","defaultKey","hasOwnProperty","secondToTime","second","add0","num","min","parseInt","sec","updateBar","percentage","direction","style","eventTypes","event","trigger","_i","value","_this2","innerHTML","video","pic","url","audio","ptime","playButton","toggle","paused","addEventListener","getElementViewLeft","actualLeft","offsetLeft","current","offsetParent","elementScrollLeft","fullscreenElement","mozFullScreenElement","webkitFullscreenElement","body","scrollLeft","documentElement","playedBar","loadedBar","bar","barWidth","danmaku","dan","time","currentTime","danIndex","setTime","playedTime","setInterval","duration","item","parseFloat","danmakuIn","text","color","e","window","clientWidth","clientX","width","thumbMove","thumbUp","removeEventListener","clearInterval","volumeBar","volumeEle","volumeBarWrapWrap","volumeBarWrap","volumeicon","vWidth","switchVolumeIcon","volume","volumeMove","muted","volumeUp","classList","remove","add","hideTime","hideController","clearTimeout","setTimeout","played","closeSetting","closeComment","danOpacity","settingHTML","original","speed","settingIcon","settingBox","mask","contains","settingEvent","openSetting","loopEle","loopToggle","checked","speedEle","speedItem","_loop","playbackRate","dataset","danmakuBar","danmakuBarWrapWrap","danmakuBarWrap","danmakuSettingBox","dWidth","danmakuMove","items","opacity","danmakuUp","buffered","end","ended","danContainer","itemHeight","danWidth","danHeight","itemY","danTunnel","top","bottom","danItemRight","ele","getBoundingClientRect","danSpeed","offsetWidth","getTunnel","tmp","_loop2","splice","v","j","danRight","push","_ret2","offsetHeight","createElement","content","createTextNode","appendChild","transform","removeChild","console","error","xhr","XMLHttpRequest","onreadystatechange","readyState","status","JSON","parse","responseText","sort","a","b","log","open","api","id","commentInput","commentIcon","commentBox","commentSettingIcon","commentSettingBox","commentSendIcon","sendComment","replace","alert","danmakuData","token","player","author","querySelector","stringify","closeCommentSetting","toggleCommentSetting","disableHide","openComment","focus","sele","setAttribute","getAttribute","keyCode","resetAnimation","cancelFullScreen","mozCancelFullScreen","webkitCancelFullScreen","requestFullscreen","mozRequestFullScreen","webkitRequestFullscreen","name","func","module","exports"],"mappings":"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","file":"DPlayer.min.js","sourcesContent":["(() => {\n\n class DPlayer {\n /**\n * DPlayer constructor function\n *\n * @param {Object} option - See README\n * @constructor\n */\n constructor(option) {\n this.svg = {\n 'play': ['0 0 16 32', 'M15.552 15.168q0.448 0.32 0.448 0.832 0 0.448-0.448 0.768l-13.696 8.512q-0.768 0.512-1.312 0.192t-0.544-1.28v-16.448q0-0.96 0.544-1.28t1.312 0.192z'],\n 'pause': ['0 0 17 32', 'M14.080 4.8q2.88 0 2.88 2.048v18.24q0 2.112-2.88 2.112t-2.88-2.112v-18.24q0-2.048 2.88-2.048zM2.88 4.8q2.88 0 2.88 2.048v18.24q0 2.112-2.88 2.112t-2.88-2.112v-18.24q0-2.048 2.88-2.048z'],\n 'volume-up': ['0 0 21 32', 'M13.728 6.272v19.456q0 0.448-0.352 0.8t-0.8 0.32-0.8-0.32l-5.952-5.952h-4.672q-0.48 0-0.8-0.352t-0.352-0.8v-6.848q0-0.48 0.352-0.8t0.8-0.352h4.672l5.952-5.952q0.32-0.32 0.8-0.32t0.8 0.32 0.352 0.8zM20.576 16q0 1.344-0.768 2.528t-2.016 1.664q-0.16 0.096-0.448 0.096-0.448 0-0.8-0.32t-0.32-0.832q0-0.384 0.192-0.64t0.544-0.448 0.608-0.384 0.512-0.64 0.192-1.024-0.192-1.024-0.512-0.64-0.608-0.384-0.544-0.448-0.192-0.64q0-0.48 0.32-0.832t0.8-0.32q0.288 0 0.448 0.096 1.248 0.48 2.016 1.664t0.768 2.528zM25.152 16q0 2.72-1.536 5.056t-4 3.36q-0.256 0.096-0.448 0.096-0.48 0-0.832-0.352t-0.32-0.8q0-0.704 0.672-1.056 1.024-0.512 1.376-0.8 1.312-0.96 2.048-2.4t0.736-3.104-0.736-3.104-2.048-2.4q-0.352-0.288-1.376-0.8-0.672-0.352-0.672-1.056 0-0.448 0.32-0.8t0.8-0.352q0.224 0 0.48 0.096 2.496 1.056 4 3.36t1.536 5.056zM29.728 16q0 4.096-2.272 7.552t-6.048 5.056q-0.224 0.096-0.448 0.096-0.48 0-0.832-0.352t-0.32-0.8q0-0.64 0.704-1.056 0.128-0.064 0.384-0.192t0.416-0.192q0.8-0.448 1.44-0.896 2.208-1.632 3.456-4.064t1.216-5.152-1.216-5.152-3.456-4.064q-0.64-0.448-1.44-0.896-0.128-0.096-0.416-0.192t-0.384-0.192q-0.704-0.416-0.704-1.056 0-0.448 0.32-0.8t0.832-0.352q0.224 0 0.448 0.096 3.776 1.632 6.048 5.056t2.272 7.552z'],\n 'volume-down': ['0 0 21 32', 'M13.728 6.272v19.456q0 0.448-0.352 0.8t-0.8 0.32-0.8-0.32l-5.952-5.952h-4.672q-0.48 0-0.8-0.352t-0.352-0.8v-6.848q0-0.48 0.352-0.8t0.8-0.352h4.672l5.952-5.952q0.32-0.32 0.8-0.32t0.8 0.32 0.352 0.8zM20.576 16q0 1.344-0.768 2.528t-2.016 1.664q-0.16 0.096-0.448 0.096-0.448 0-0.8-0.32t-0.32-0.832q0-0.384 0.192-0.64t0.544-0.448 0.608-0.384 0.512-0.64 0.192-1.024-0.192-1.024-0.512-0.64-0.608-0.384-0.544-0.448-0.192-0.64q0-0.48 0.32-0.832t0.8-0.32q0.288 0 0.448 0.096 1.248 0.48 2.016 1.664t0.768 2.528z'],\n 'volume-off': ['0 0 21 32', 'M13.728 6.272v19.456q0 0.448-0.352 0.8t-0.8 0.32-0.8-0.32l-5.952-5.952h-4.672q-0.48 0-0.8-0.352t-0.352-0.8v-6.848q0-0.48 0.352-0.8t0.8-0.352h4.672l5.952-5.952q0.32-0.32 0.8-0.32t0.8 0.32 0.352 0.8z'],\n 'loop': ['0 0 32 32', 'M1.882 16.941c0 4.152 3.221 7.529 7.177 7.529v1.882c-4.996 0-9.060-4.222-9.060-9.412s4.064-9.412 9.060-9.412h7.96l-3.098-3.098 1.331-1.331 5.372 5.37-5.37 5.372-1.333-1.333 3.1-3.098h-7.962c-3.957 0-7.177 3.377-7.177 7.529zM22.94 7.529v1.882c3.957 0 7.177 3.377 7.177 7.529s-3.221 7.529-7.177 7.529h-7.962l3.098-3.098-1.331-1.331-5.37 5.37 5.372 5.372 1.331-1.331-3.1-3.1h7.96c4.998 0 9.062-4.222 9.062-9.412s-4.064-9.412-9.060-9.412z'],\n 'full': ['0 0 32 33', 'M6.667 28h-5.333c-0.8 0-1.333-0.533-1.333-1.333v-5.333c0-0.8 0.533-1.333 1.333-1.333s1.333 0.533 1.333 1.333v4h4c0.8 0 1.333 0.533 1.333 1.333s-0.533 1.333-1.333 1.333zM30.667 28h-5.333c-0.8 0-1.333-0.533-1.333-1.333s0.533-1.333 1.333-1.333h4v-4c0-0.8 0.533-1.333 1.333-1.333s1.333 0.533 1.333 1.333v5.333c0 0.8-0.533 1.333-1.333 1.333zM30.667 12c-0.8 0-1.333-0.533-1.333-1.333v-4h-4c-0.8 0-1.333-0.533-1.333-1.333s0.533-1.333 1.333-1.333h5.333c0.8 0 1.333 0.533 1.333 1.333v5.333c0 0.8-0.533 1.333-1.333 1.333zM1.333 12c-0.8 0-1.333-0.533-1.333-1.333v-5.333c0-0.8 0.533-1.333 1.333-1.333h5.333c0.8 0 1.333 0.533 1.333 1.333s-0.533 1.333-1.333 1.333h-4v4c0 0.8-0.533 1.333-1.333 1.333z'],\n 'setting': ['0 0 32 28', 'M28.633 17.104c0.035 0.21 0.026 0.463-0.026 0.76s-0.14 0.598-0.262 0.904c-0.122 0.306-0.271 0.581-0.445 0.825s-0.367 0.419-0.576 0.524c-0.209 0.105-0.393 0.157-0.55 0.157s-0.332-0.035-0.524-0.105c-0.175-0.052-0.393-0.1-0.655-0.144s-0.528-0.052-0.799-0.026c-0.271 0.026-0.541 0.083-0.812 0.17s-0.502 0.236-0.694 0.445c-0.419 0.437-0.664 0.934-0.734 1.493s0.009 1.092 0.236 1.598c0.175 0.349 0.148 0.699-0.079 1.048-0.105 0.14-0.271 0.284-0.498 0.432s-0.476 0.284-0.747 0.406-0.555 0.218-0.851 0.288c-0.297 0.070-0.559 0.105-0.786 0.105-0.157 0-0.306-0.061-0.445-0.183s-0.236-0.253-0.288-0.393h-0.026c-0.192-0.541-0.52-1.009-0.982-1.402s-1-0.589-1.611-0.589c-0.594 0-1.131 0.197-1.611 0.589s-0.816 0.851-1.009 1.375c-0.087 0.21-0.218 0.362-0.393 0.458s-0.367 0.144-0.576 0.144c-0.244 0-0.52-0.044-0.825-0.131s-0.611-0.197-0.917-0.327c-0.306-0.131-0.581-0.284-0.825-0.458s-0.428-0.349-0.55-0.524c-0.087-0.122-0.135-0.266-0.144-0.432s0.057-0.397 0.197-0.694c0.192-0.402 0.266-0.86 0.223-1.375s-0.266-0.991-0.668-1.428c-0.244-0.262-0.541-0.432-0.891-0.511s-0.681-0.109-0.995-0.092c-0.367 0.017-0.742 0.087-1.127 0.21-0.244 0.070-0.489 0.052-0.734-0.052-0.192-0.070-0.371-0.231-0.537-0.485s-0.314-0.533-0.445-0.838c-0.131-0.306-0.231-0.62-0.301-0.943s-0.087-0.59-0.052-0.799c0.052-0.384 0.227-0.629 0.524-0.734 0.524-0.21 0.995-0.555 1.415-1.035s0.629-1.017 0.629-1.611c0-0.611-0.21-1.144-0.629-1.598s-0.891-0.786-1.415-0.996c-0.157-0.052-0.288-0.179-0.393-0.38s-0.157-0.406-0.157-0.616c0-0.227 0.035-0.48 0.105-0.76s0.162-0.55 0.275-0.812 0.244-0.502 0.393-0.72c0.148-0.218 0.31-0.38 0.485-0.485 0.14-0.087 0.275-0.122 0.406-0.105s0.275 0.052 0.432 0.105c0.524 0.21 1.070 0.275 1.637 0.197s1.070-0.327 1.506-0.747c0.21-0.209 0.362-0.467 0.458-0.773s0.157-0.607 0.183-0.904c0.026-0.297 0.026-0.568 0-0.812s-0.048-0.419-0.065-0.524c-0.035-0.105-0.066-0.227-0.092-0.367s-0.013-0.262 0.039-0.367c0.105-0.244 0.293-0.458 0.563-0.642s0.563-0.336 0.878-0.458c0.314-0.122 0.62-0.214 0.917-0.275s0.533-0.092 0.707-0.092c0.227 0 0.406 0.074 0.537 0.223s0.223 0.301 0.275 0.458c0.192 0.471 0.507 0.886 0.943 1.244s0.952 0.537 1.546 0.537c0.611 0 1.153-0.17 1.624-0.511s0.803-0.773 0.996-1.297c0.070-0.14 0.179-0.284 0.327-0.432s0.301-0.223 0.458-0.223c0.244 0 0.511 0.035 0.799 0.105s0.572 0.166 0.851 0.288c0.279 0.122 0.537 0.279 0.773 0.472s0.423 0.402 0.563 0.629c0.087 0.14 0.113 0.293 0.079 0.458s-0.070 0.284-0.105 0.354c-0.227 0.506-0.297 1.039-0.21 1.598s0.341 1.048 0.76 1.467c0.419 0.419 0.934 0.651 1.546 0.694s1.179-0.057 1.703-0.301c0.14-0.087 0.31-0.122 0.511-0.105s0.371 0.096 0.511 0.236c0.262 0.244 0.493 0.616 0.694 1.113s0.336 1 0.406 1.506c0.035 0.297-0.013 0.528-0.144 0.694s-0.266 0.275-0.406 0.327c-0.542 0.192-1.004 0.528-1.388 1.009s-0.576 1.026-0.576 1.637c0 0.594 0.162 1.113 0.485 1.559s0.747 0.764 1.27 0.956c0.122 0.070 0.227 0.14 0.314 0.21 0.192 0.157 0.323 0.358 0.393 0.602v0zM16.451 19.462c0.786 0 1.528-0.149 2.227-0.445s1.305-0.707 1.821-1.231c0.515-0.524 0.921-1.131 1.218-1.821s0.445-1.428 0.445-2.214c0-0.786-0.148-1.524-0.445-2.214s-0.703-1.292-1.218-1.808c-0.515-0.515-1.122-0.921-1.821-1.218s-1.441-0.445-2.227-0.445c-0.786 0-1.524 0.148-2.214 0.445s-1.292 0.703-1.808 1.218c-0.515 0.515-0.921 1.118-1.218 1.808s-0.445 1.428-0.445 2.214c0 0.786 0.149 1.524 0.445 2.214s0.703 1.297 1.218 1.821c0.515 0.524 1.118 0.934 1.808 1.231s1.428 0.445 2.214 0.445v0z'],\n 'right': ['0 0 32 32', 'M22 16l-10.105-10.6-1.895 1.987 8.211 8.613-8.211 8.612 1.895 1.988 8.211-8.613z'],\n 'comment': ['0 0 32 32', 'M27.128 0.38h-22.553c-2.336 0-4.229 1.825-4.229 4.076v16.273c0 2.251 1.893 4.076 4.229 4.076h4.229v-2.685h8.403l-8.784 8.072 1.566 1.44 7.429-6.827h9.71c2.335 0 4.229-1.825 4.229-4.076v-16.273c0-2.252-1.894-4.076-4.229-4.076zM28.538 19.403c0 1.5-1.262 2.717-2.819 2.717h-8.36l-0.076-0.070-0.076 0.070h-11.223c-1.557 0-2.819-1.217-2.819-2.717v-13.589c0-1.501 1.262-2.718 2.819-2.718h19.734c1.557 0 2.819-0.141 2.819 1.359v14.947zM9.206 10.557c-1.222 0-2.215 0.911-2.215 2.036s0.992 2.035 2.215 2.035c1.224 0 2.216-0.911 2.216-2.035s-0.992-2.036-2.216-2.036zM22.496 10.557c-1.224 0-2.215 0.911-2.215 2.036s0.991 2.035 2.215 2.035c1.224 0 2.215-0.911 2.215-2.035s-0.991-2.036-2.215-2.036zM15.852 10.557c-1.224 0-2.215 0.911-2.215 2.036s0.991 2.035 2.215 2.035c1.222 0 2.215-0.911 2.215-2.035s-0.992-2.036-2.215-2.036z'],\n 'comment-off': ['0 0 32 32', 'M27.090 0.131h-22.731c-2.354 0-4.262 1.839-4.262 4.109v16.401c0 2.269 1.908 4.109 4.262 4.109h4.262v-2.706h8.469l-8.853 8.135 1.579 1.451 7.487-6.88h9.787c2.353 0 4.262-1.84 4.262-4.109v-16.401c0-2.27-1.909-4.109-4.262-4.109v0zM28.511 19.304c0 1.512-1.272 2.738-2.841 2.738h-8.425l-0.076-0.070-0.076 0.070h-11.311c-1.569 0-2.841-1.226-2.841-2.738v-13.696c0-1.513 1.272-2.739 2.841-2.739h19.889c1.569 0 2.841-0.142 2.841 1.37v15.064z'],\n 'send': ['0 0 32 32', 'M13.725 30l3.9-5.325-3.9-1.125v6.45zM0 17.5l11.050 3.35 13.6-11.55-10.55 12.425 11.8 3.65 6.1-23.375-32 15.5z']\n };\n this.getSVG = (type) => {\n return `\n \n \n \n \n `;\n };\n \n this.isMobile = navigator.userAgent.match(/(iPad)|(iPhone)|(iPod)|(android)|(webOS)/i);\n // compatibility: some mobile browsers don't suppose autoplay\n if (this.isMobile) {\n option.autoplay = false;\n }\n \n // default options\n const defaultOption = {\n element: document.getElementsByClassName('dplayer')[0],\n autoplay: false,\n theme: '#b7daff',\n loop: false\n };\n for (let defaultKey in defaultOption) {\n if (defaultOption.hasOwnProperty(defaultKey) && !option.hasOwnProperty(defaultKey)) {\n option[defaultKey] = defaultOption[defaultKey];\n }\n }\n \n this.option = option;\n this.loop = option.loop;\n \n /**\n * Parse second to 00:00 format\n *\n * @param {Number} second\n * @return {String} 00:00 format\n */\n this.secondToTime = (second) => {\n const add0 = (num) => {\n return num < 10 ? '0' + num : '' + num;\n };\n const min = parseInt(second / 60);\n const sec = parseInt(second - min * 60);\n return add0(min) + ':' + add0(sec);\n };\n \n /**\n * Update progress bar, including loading progress bar and play progress bar\n *\n * @param {String} type - Point out which bar it is, should be played loaded or volume\n * @param {Number} percentage\n * @param {String} direction - Point out the direction of this bar, Should be height or width\n */\n this.updateBar = (type, percentage, direction) => {\n percentage = percentage > 0 ? percentage : 0;\n percentage = percentage < 1 ? percentage : 1;\n this[type + 'Bar'].style[direction] = percentage * 100 + '%';\n };\n \n // define DPlayer events\n this.eventTypes = ['play', 'pause', 'canplay', 'playing', 'ended', 'error'];\n this.event = {};\n for (let i = 0; i < this.eventTypes.length; i++) {\n this.event[this.eventTypes[i]] = [];\n }\n this.trigger = (type) => {\n for (let i = 0; i < this.event[type].length; i++) {\n this.event[type][i]();\n }\n }\n }\n \n /**\n * AutoLink initialization function\n */\n init() {\n this.element = this.option.element;\n \n this.element.innerHTML = `\n
\n
\n \n
\n
\n
\n
\n
\n \n
\n \n
\n
\n
\n \n
\n
\n
\n
\n 0:00 / 0:00\n
\n
\n
\n \n
\n
\n
\n
\n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n \n \n
\n
\n
\n \n
\n
\n \n
\n
\n
\n
\n
\n \n
\n
\n
\n
\n `;\n \n // get this audio object\n this.audio = this.element.getElementsByClassName('dplayer-video')[0];\n \n this.ptime = this.element.getElementsByClassName('dplayer-ptime')[0];\n \n // play and pause button\n this.playButton = this.element.getElementsByClassName('dplayer-play-icon')[0];\n this.toggle = () => {\n if (this.audio.paused) {\n this.play();\n }\n else {\n this.pause();\n }\n };\n this.playButton.addEventListener('click', this.toggle);\n this.element.getElementsByClassName('dplayer-video-wrap')[0].addEventListener('click', this.toggle);\n this.element.getElementsByClassName('dplayer-controller-mask')[0].addEventListener('click', this.toggle);\n \n \n /**\n * control play progress\n */\n // get element's view position\n const getElementViewLeft = (element) => {\n let actualLeft = element.offsetLeft;\n let current = element.offsetParent;\n let elementScrollLeft;\n if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) {\n while (current !== null) {\n actualLeft += current.offsetLeft;\n current = current.offsetParent;\n }\n }\n else {\n while (current !== null && current !== this.element) {\n actualLeft += current.offsetLeft;\n current = current.offsetParent;\n }\n }\n elementScrollLeft = document.body.scrollLeft + document.documentElement.scrollLeft;\n return actualLeft - elementScrollLeft;\n };\n \n const getElementViewTop = (element) => {\n let actualTop = element.offsetTop;\n let current = element.offsetParent;\n let elementScrollTop;\n if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) {\n while (current !== null) {\n actualTop += current.offsetTop;\n current = current.offsetParent;\n }\n }\n else {\n while (current !== null && current !== this.element) {\n actualTop += current.offsetTop;\n current = current.offsetParent;\n }\n }\n elementScrollTop = document.body.scrollTop + document.documentElement.scrollTop;\n return actualTop - elementScrollTop;\n };\n \n this.playedBar = this.element.getElementsByClassName('dplayer-played')[0];\n this.loadedBar = this.element.getElementsByClassName('dplayer-loaded')[0];\n this.bar = this.element.getElementsByClassName('dplayer-bar-wrap')[0];\n let barWidth;\n \n this.audio.addEventListener('seeked', () => {\n if (this.option.danmaku) {\n for (let i = 0; i < this.dan.length; i++) {\n if (this.dan[i].time >= this.audio.currentTime) {\n this.danIndex = i;\n return;\n }\n }\n }\n });\n \n this.setTime = () => {\n if (this.option.danmaku) {\n this.playedTime = setInterval(() => {\n this.updateBar('played', this.audio.currentTime / this.audio.duration, 'width');\n this.ptime.innerHTML = this.secondToTime(this.audio.currentTime);\n this.trigger('playing');\n \n const item = this.dan[this.danIndex];\n if (item && this.audio.currentTime >= parseFloat(item.time)) {\n this.danmakuIn(item.text, item.color, item.type);\n this.danIndex++;\n }\n }, 100);\n }\n else {\n this.playedTime = setInterval(() => {\n this.updateBar('played', this.audio.currentTime / this.audio.duration, 'width');\n this.ptime.innerHTML = this.secondToTime(this.audio.currentTime);\n this.trigger('playing');\n }, 100);\n }\n };\n \n this.bar.addEventListener('click', (event) => {\n const e = event || window.event;\n barWidth = this.bar.clientWidth;\n let percentage = (e.clientX - getElementViewLeft(this.bar)) / barWidth;\n percentage = percentage > 0 ? percentage : 0;\n percentage = percentage < 1 ? percentage : 1;\n this.updateBar('played', percentage, 'width');\n this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = this.secondToTime(percentage * this.audio.duration);\n this.audio.currentTime = parseFloat(this.playedBar.style.width) / 100 * this.audio.duration;\n });\n \n const thumbMove = (event) => {\n const e = event || window.event;\n let percentage = (e.clientX - getElementViewLeft(this.bar)) / barWidth;\n percentage = percentage > 0 ? percentage : 0;\n percentage = percentage < 1 ? percentage : 1;\n this.updateBar('played', percentage, 'width');\n this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = this.secondToTime(percentage * this.audio.duration);\n };\n \n const thumbUp = () => {\n document.removeEventListener('mouseup', thumbUp);\n document.removeEventListener('mousemove', thumbMove);\n this.audio.currentTime = parseFloat(this.playedBar.style.width) / 100 * this.audio.duration;\n this.setTime();\n };\n \n this.bar.addEventListener('mousedown', () => {\n barWidth = this.bar.clientWidth;\n clearInterval(this.playedTime);\n document.addEventListener('mousemove', thumbMove);\n document.addEventListener('mouseup', thumbUp);\n });\n \n \n /**\n * control volume\n */\n this.volumeBar = this.element.getElementsByClassName('dplayer-volume-bar-inner')[0];\n const volumeEle = this.element.getElementsByClassName('dplayer-volume')[0];\n const volumeBarWrapWrap = this.element.getElementsByClassName('dplayer-volume-bar-wrap')[0];\n const volumeBarWrap = this.element.getElementsByClassName('dplayer-volume-bar')[0];\n const volumeicon = this.element.getElementsByClassName('dplayer-volume-icon')[0];\n const vWidth = 35;\n const switchVolumeIcon = () => {\n if (this.audio.volume >= 0.8) {\n volumeicon.innerHTML = this.getSVG('volume-up');\n }\n else if (this.audio.volume > 0) {\n volumeicon.innerHTML = this.getSVG('volume-down');\n }\n else {\n volumeicon.innerHTML = this.getSVG('volume-off');\n }\n };\n const volumeMove = (event) => {\n const e = event || window.event;\n let percentage = (e.clientX - getElementViewLeft(volumeBarWrap) - 5.5) / vWidth;\n percentage = percentage > 0 ? percentage : 0;\n percentage = percentage < 1 ? percentage : 1;\n this.updateBar('volume', percentage, 'width');\n this.audio.volume = percentage;\n if (this.audio.muted) {\n this.audio.muted = false;\n }\n switchVolumeIcon();\n };\n const volumeUp = () => {\n document.removeEventListener('mouseup', volumeUp);\n document.removeEventListener('mousemove', volumeMove);\n volumeEle.classList.remove('dplayer-volume-active');\n };\n \n volumeBarWrapWrap.addEventListener('click', (event) => {\n const e = event || window.event;\n let percentage = (e.clientX - getElementViewLeft(volumeBarWrap) - 5.5) / vWidth;\n percentage = percentage > 0 ? percentage : 0;\n percentage = percentage < 1 ? percentage : 1;\n this.updateBar('volume', percentage, 'width');\n this.audio.volume = percentage;\n if (this.audio.muted) {\n this.audio.muted = false;\n }\n switchVolumeIcon();\n });\n volumeBarWrapWrap.addEventListener('mousedown', () => {\n document.addEventListener('mousemove', volumeMove);\n document.addEventListener('mouseup', volumeUp);\n volumeEle.classList.add('dplayer-volume-active');\n });\n volumeicon.addEventListener('click', () => {\n if (this.audio.muted) {\n this.audio.muted = false;\n switchVolumeIcon();\n this.updateBar('volume', this.audio.volume, 'width');\n }\n else {\n this.audio.muted = true;\n volumeicon.innerHTML = this.getSVG('volume-off');\n this.updateBar('volume', 0, 'width');\n }\n });\n \n \n /**\n * auto hide controller\n */\n let hideTime = 0;\n const hideController = () => {\n this.element.classList.remove('dplayer-hide-controller');\n clearTimeout(hideTime);\n hideTime = setTimeout(() => {\n if (this.audio.played.length) {\n this.element.classList.add('dplayer-hide-controller');\n closeSetting();\n closeComment();\n }\n }, 2000);\n };\n this.element.addEventListener('mousemove', hideController);\n this.element.addEventListener('click', hideController);\n \n \n /***\n * setting\n */\n this.danOpacity = 0.7;\n const settingHTML = {\n 'original': `\n
\n 洗脑循环\n
\n \n \n
\n
\n
\n 速度\n
`\n + this.getSVG('right')\n + `
\n
\n
\n 弹幕透明度\n
\n
\n
\n \n
\n
\n
\n
`,\n 'speed': `\n
\n 0.5\n
\n
\n 0.75\n
\n
\n 正常\n
\n
\n 1.25\n
\n
\n 1.5\n
\n
\n 2\n
`\n };\n \n // toggle setting box\n const settingIcon = this.element.getElementsByClassName('dplayer-setting-icon')[0];\n const settingBox = this.element.getElementsByClassName('dplayer-setting-box')[0];\n const mask = this.element.getElementsByClassName('dplayer-mask')[0];\n settingBox.innerHTML = settingHTML.original;\n \n const closeSetting = () => {\n if (settingBox.classList.contains('dplayer-setting-box-open')) {\n settingBox.classList.remove('dplayer-setting-box-open');\n mask.classList.remove('dplayer-mask-show');\n setTimeout(() => {\n settingBox.classList.remove('dplayer-setting-box-narrow');\n settingBox.innerHTML = settingHTML.original;\n settingEvent();\n }, 300);\n }\n };\n const openSetting = () => {\n settingBox.classList.add('dplayer-setting-box-open');\n mask.classList.add('dplayer-mask-show');\n };\n \n mask.addEventListener('click', () => {\n closeSetting();\n });\n settingIcon.addEventListener('click', () => {\n openSetting();\n });\n \n const settingEvent = () => {\n // loop control\n const loopEle = this.element.getElementsByClassName('dplayer-setting-loop')[0];\n const loopToggle = loopEle.getElementsByClassName('dplayer-toggle-setting-input')[0];\n \n loopToggle.checked = this.loop;\n \n loopEle.addEventListener('click', () => {\n loopToggle.checked = !loopToggle.checked;\n if (loopToggle.checked) {\n this.loop = true;\n this.audio.loop = this.loop;\n }\n else {\n this.loop = false;\n this.audio.loop = this.loop;\n }\n closeSetting();\n });\n loopToggle.addEventListener('change', () => {\n if (loopToggle.checked) {\n this.loop = true;\n this.audio.loop = this.loop;\n }\n else {\n this.loop = false;\n this.audio.loop = this.loop;\n }\n closeSetting();\n });\n \n // speed control\n const speedEle = this.element.getElementsByClassName('dplayer-setting-speed')[0];\n speedEle.addEventListener('click', () => {\n settingBox.classList.add('dplayer-setting-box-narrow');\n settingBox.innerHTML = settingHTML.speed;\n \n const speedItem = settingBox.getElementsByClassName('dplayer-setting-speed-item');\n for (let i = 0; i < speedItem.length; i++) {\n speedItem[i].addEventListener('click', () => {\n this.audio.playbackRate = speedItem[i].dataset.speed;\n closeSetting();\n });\n }\n });\n \n // danmaku opacity\n this.danmakuBar = this.element.getElementsByClassName('dplayer-danmaku-bar-inner')[0];\n const danmakuBarWrapWrap = this.element.getElementsByClassName('dplayer-danmaku-bar-wrap')[0];\n const danmakuBarWrap = this.element.getElementsByClassName('dplayer-danmaku-bar')[0];\n const danmakuSettingBox = this.element.getElementsByClassName('dplayer-setting-danmaku')[0];\n const dWidth = 130;\n this.updateBar('danmaku', this.danOpacity, 'width');\n \n const danmakuMove = (event) => {\n const e = event || window.event;\n let percentage = (e.clientX - getElementViewLeft(danmakuBarWrap)) / dWidth;\n percentage = percentage > 0 ? percentage : 0;\n percentage = percentage < 1 ? percentage : 1;\n this.updateBar('danmaku', percentage, 'width');\n const items = this.element.getElementsByClassName('dplayer-danmaku-item');\n for (let i = 0; i < items.length; i++) {\n items[i].style.opacity = percentage;\n }\n this.danOpacity = percentage;\n };\n const danmakuUp = () => {\n document.removeEventListener('mouseup', danmakuUp);\n document.removeEventListener('mousemove', danmakuMove);\n danmakuSettingBox.classList.remove('dplayer-setting-danmaku-active');\n };\n \n danmakuBarWrapWrap.addEventListener('click', (event) => {\n const e = event || window.event;\n let percentage = (e.clientX - getElementViewLeft(danmakuBarWrap)) / dWidth;\n percentage = percentage > 0 ? percentage : 0;\n percentage = percentage < 1 ? percentage : 1;\n this.updateBar('danmaku', percentage, 'width');\n const items = this.element.getElementsByClassName('dplayer-danmaku-item');\n for (let i = 0; i < items.length; i++) {\n items[i].style.opacity = percentage;\n }\n this.danOpacity = percentage;\n });\n danmakuBarWrapWrap.addEventListener('mousedown', () => {\n document.addEventListener('mousemove', danmakuMove);\n document.addEventListener('mouseup', danmakuUp);\n danmakuSettingBox.classList.add('dplayer-setting-danmaku-active');\n });\n };\n settingEvent();\n \n \n /**\n * audio events\n */\n // show audio time: the metadata has loaded or changed\n this.audio.addEventListener('durationchange', () => {\n if (this.audio.duration !== 1) { // compatibility: Android browsers will output 1 at first\n this.element.getElementsByClassName('dplayer-dtime')[0].innerHTML = this.secondToTime(this.audio.duration);\n }\n });\n \n // show audio loaded bar: to inform interested parties of progress downloading the media\n this.audio.addEventListener('progress', () => {\n const percentage = this.audio.buffered.length ? this.audio.buffered.end(this.audio.buffered.length - 1) / this.audio.duration : 0;\n this.updateBar('loaded', percentage, 'width');\n });\n \n // audio download error: an error occurs\n this.audio.addEventListener('error', () => {\n this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = `Error happens ╥﹏╥`;\n this.trigger('pause');\n });\n \n // audio can play: enough data is available that the media can be played\n this.audio.addEventListener('canplay', () => {\n this.trigger('canplay');\n });\n \n // music end\n this.ended = false;\n this.audio.addEventListener('ended', () => {\n this.updateBar('played', 1, 'width');\n if (!this.loop) {\n this.ended = true;\n this.pause();\n this.trigger('ended');\n }\n });\n \n // control volume\n this.audio.volume = parseInt(this.element.getElementsByClassName('dplayer-volume-bar-inner')[0].style.width) / 100;\n \n // loop\n this.audio.loop = this.loop;\n \n // set duration time\n if (this.audio.duration !== 1) { // compatibility: Android browsers will output 1 at first\n this.element.getElementsByClassName('dplayer-dtime')[0].innerHTML = this.audio.duration ? this.secondToTime(this.audio.duration) : '00:00';\n }\n \n \n /**\n * danmaku display\n */\n const danContainer = this.element.getElementsByClassName('dplayer-danmaku')[0];\n const itemHeight = 30;\n let danWidth;\n let danHeight;\n let itemY;\n let danTunnel = {\n right: {},\n top: {},\n bottom: {}\n };\n \n const danItemRight = (ele) => {\n return danContainer.getBoundingClientRect().right - ele.getBoundingClientRect().right;\n };\n \n const danSpeed = (ele) => {\n return (danWidth + ele.offsetWidth) / 5;\n };\n \n const getTunnel = (ele, type) => {\n const tmp = danWidth / danSpeed(ele);\n \n for (let i = 0; ; i++) {\n let item = danTunnel[type][i + ''];\n if (item && item.length) {\n for (let j = 0; j < item.length; j++) {\n const danRight = danItemRight(item[j]) - 10;\n if (danRight <= 640 - (tmp * danSpeed(item[j])) || danRight <= 0) {\n break;\n }\n if (j === item.length - 1) {\n danTunnel[type][i + ''].push(ele);\n ele.addEventListener('animationend', () => {\n danTunnel[type][i + ''].splice(0, 1);\n });\n return i % itemY;\n }\n }\n }\n else {\n danTunnel[type][i + ''] = [ele];\n ele.addEventListener('animationend', () => {\n danTunnel[type][i + ''].splice(0, 1);\n });\n return i % itemY;\n }\n }\n };\n \n this.danmakuIn = (text, color, type) => {\n danWidth = danContainer.offsetWidth;\n danHeight = danContainer.offsetHeight;\n itemY = danHeight / itemHeight;\n let item = document.createElement(`div`);\n let content = document.createTextNode(text);\n item.classList.add(`dplayer-danmaku-item`);\n item.classList.add(`dplayer-danmaku-${type}`);\n item.appendChild(content);\n item.style.opacity = this.danOpacity;\n \n // insert\n danContainer.appendChild(item);\n \n // adjust\n item.style.color = color;\n switch (type) {\n case 'right':\n item.style.top = itemHeight * getTunnel(item, type) + 'px';\n item.style.width = (item.offsetWidth + 1) + 'px';\n item.style.transform = `translateX(-${danWidth}px)`;\n item.addEventListener('animationend', () => {\n danContainer.removeChild(item);\n });\n break;\n case 'top':\n item.style.top = itemHeight * getTunnel(item, type) + 'px';\n item.addEventListener('animationend', () => {\n danContainer.removeChild(item);\n });\n break;\n case 'bottom':\n item.style.bottom = itemHeight * getTunnel(item, type) + 'px';\n item.addEventListener('animationend', () => {\n danContainer.removeChild(item);\n });\n break;\n default:\n console.error(`Can't handled danmaku type: ${type}`);\n }\n \n // move\n item.classList.add(`dplayer-danmaku-move`);\n };\n \n // danmaku\n if (this.option.danmaku) {\n this.danIndex = 0;\n const xhr = new XMLHttpRequest();\n xhr.onreadystatechange = () => {\n if (xhr.readyState === 4) {\n if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {\n this.dan = JSON.parse(xhr.responseText).danmaku.sort((a, b) => a.time - b.time);\n console.log(this.dan);\n \n // autoplay\n if (this.option.autoplay && !this.isMobile) {\n this.play();\n }\n else if (this.isMobile) {\n this.pause();\n }\n }\n else {\n console.log('Request was unsuccessful: ' + xhr.status);\n }\n }\n };\n xhr.open('get', this.option.danmaku.api + '?id=' + this.option.danmaku.id, true);\n xhr.send(null);\n }\n else {\n // autoplay\n if (this.option.autoplay && !this.isMobile) {\n this.play();\n }\n else if (this.isMobile) {\n this.pause();\n }\n }\n \n \n /**\n * comment\n */\n const commentInput = this.element.getElementsByClassName('dplayer-comment-input')[0];\n const commentIcon = this.element.getElementsByClassName('dplayer-comment-icon')[0];\n const commentBox = this.element.getElementsByClassName('dplayer-comment-box')[0];\n const commentSettingIcon = this.element.getElementsByClassName('dplayer-comment-setting')[0];\n const commentSettingBox = this.element.getElementsByClassName('dplayer-comment-setting-box')[0];\n const commentSendIcon = this.element.getElementsByClassName('dplayer-send-icon')[0];\n\n const sendComment = () => {\n // text can't be empty\n if (!commentInput.value.replace(/^\\s+|\\s+$/g, '')) {\n alert('要输入弹幕内容啊喂!');\n return;\n }\n\n const danmakuData = {\n token: this.option.danmaku.token,\n player: this.option.danmaku.id,\n author: 'DIYgod',\n time: this.audio.currentTime,\n text: commentInput.value,\n color: this.element.querySelector('input[name=\"dplayer-danmaku-color\"]:checked').value,\n type: this.element.querySelector('input[name=\"dplayer-danmaku-type\"]:checked').value\n };\n const xhr = new XMLHttpRequest();\n xhr.onreadystatechange = () => {\n if (xhr.readyState === 4) {\n if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {\n console.log(JSON.parse(xhr.responseText));\n }\n else {\n console.log('Request was unsuccessful: ' + xhr.status);\n }\n }\n };\n xhr.open('post', this.option.danmaku.api, true);\n xhr.send(JSON.stringify(danmakuData));\n\n commentInput.value = '';\n closeComment();\n this.danmakuIn(danmakuData.text, danmakuData.color, danmakuData.type);\n };\n \n const closeCommentSetting = () => {\n if (commentSettingBox.classList.contains('dplayer-comment-setting-open')) {\n commentSettingBox.classList.remove('dplayer-comment-setting-open');\n }\n };\n const toggleCommentSetting = () => {\n if (commentSettingBox.classList.contains('dplayer-comment-setting-open')) {\n commentSettingBox.classList.remove('dplayer-comment-setting-open');\n }\n else {\n commentSettingBox.classList.add('dplayer-comment-setting-open');\n }\n };\n \n let disableHide = 0;\n const closeComment = () => {\n if (commentBox.classList.contains('dplayer-comment-box-open')) {\n commentBox.classList.remove('dplayer-comment-box-open');\n mask.classList.remove('dplayer-mask-show');\n clearInterval(disableHide);\n this.element.classList.remove('dplayer-show-controller');\n closeCommentSetting();\n }\n };\n const openComment = () => {\n commentBox.classList.add('dplayer-comment-box-open');\n mask.classList.add('dplayer-mask-show');\n disableHide = setInterval(() => {\n clearTimeout(hideTime);\n }, 1000);\n this.element.classList.add('dplayer-show-controller');\n };\n \n mask.addEventListener('click', () => {\n closeComment();\n });\n commentIcon.addEventListener('click', () => {\n openComment();\n setTimeout(() => {\n commentInput.focus();\n }, 300);\n });\n commentSettingIcon.addEventListener('click', () => {\n toggleCommentSetting();\n });\n \n // comment setting box\n this.element.getElementsByClassName('dplayer-comment-setting-color')[0].addEventListener('click', () => {\n const sele = this.element.querySelector('input[name=\"dplayer-danmaku-color\"]:checked+span');\n if (sele) {\n commentSettingIcon.setAttribute('style', sele.getAttribute('style'));\n }\n });\n \n commentInput.addEventListener('click', () => {\n closeCommentSetting();\n });\n commentInput.addEventListener('keydown', (e) => {\n const event = e || window.event;\n if (event.keyCode === 13) {\n sendComment();\n }\n });\n \n commentSendIcon.addEventListener('click', sendComment);\n \n \n /**\n * full screen\n */\n const resetAnimation = () => {\n danWidth = danContainer.offsetWidth;\n const items = this.element.getElementsByClassName('dplayer-danmaku-item');\n for (let i = 0; i < items.length; i++) {\n items[i].style.transform = `translateX(-${danWidth}px)`;\n }\n };\n \n this.element.addEventListener('fullscreenchange', () => {\n resetAnimation();\n });\n this.element.addEventListener('mozfullscreenchange', () => {\n resetAnimation();\n });\n this.element.addEventListener('webkitfullscreenchange', () => {\n resetAnimation();\n });\n this.element.getElementsByClassName('dplayer-full-icon')[0].addEventListener('click', () => {\n if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) {\n if (this.element.requestFullscreen) {\n this.element.requestFullscreen();\n }\n else if (this.element.mozRequestFullScreen) {\n this.element.mozRequestFullScreen();\n }\n else if (this.element.webkitRequestFullscreen) {\n this.element.webkitRequestFullscreen();\n }\n }\n else {\n if (document.cancelFullScreen) {\n document.cancelFullScreen();\n }\n else if (document.mozCancelFullScreen) {\n document.mozCancelFullScreen();\n }\n else if (document.webkitCancelFullScreen) {\n document.webkitCancelFullScreen();\n }\n }\n resetAnimation();\n });\n }\n \n /**\n * Play music\n */\n play() {\n if (this.audio.paused) {\n this.playButton.innerHTML = this.getSVG('pause');\n \n this.audio.play();\n if (this.playedTime) {\n clearInterval(this.playedTime);\n }\n this.setTime();\n this.element.classList.add('dplayer-playing');\n this.trigger('play');\n }\n }\n \n /**\n * Pause music\n */\n pause() {\n if (!this.audio.paused || this.ended) {\n this.ended = false;\n this.playButton.innerHTML = this.getSVG('play');\n this.audio.pause();\n clearInterval(this.playedTime);\n this.element.classList.remove('dplayer-playing');\n this.trigger('pause');\n }\n }\n \n /**\n * attach event\n */\n on(name, func) {\n if (typeof func === 'function') {\n this.event[name].push(func);\n }\n }\n }\n\n if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {\n module.exports = DPlayer;\n }\n else {\n window.DPlayer = DPlayer;\n }\n})();"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/nodejs/blacklist b/nodejs/blacklist new file mode 100644 index 0000000..1d38074 --- /dev/null +++ b/nodejs/blacklist @@ -0,0 +1,3 @@ +Can be username and IP +username +0.0.0.0 \ No newline at end of file diff --git a/nodejs/index.js b/nodejs/index.js index 4a8e762..3ae6eba 100644 --- a/nodejs/index.js +++ b/nodejs/index.js @@ -1,9 +1,39 @@ var url = require('url'); +var fs = require('fs'); var mongoose = require('mongoose'); -var mongodbUrl = 'mongodb://' + process.env.MONGODB_USERNAME + ':' + process.env.MONGODB_PASSWORD + '@' + process.env.MONGODB_PORT_27017_TCP_ADDR + ':' + process.env.MONGODB_PORT_27017_TCP_PORT + '/' + process.env.MONGODB_INSTANCE_NAME; var express = require('express'); var app = express(); +var log4js = require('log4js'); +log4js.configure({ + appenders: [ + { + type: "file", + filename: 'DPlayer.log', + maxLogSize: 20480, + backups: 3, + category: [ 'DPlayer','console' ] + }, + { + type: "console" + } + ], + replaceConsole: true +}); +var logger = log4js.getLogger('DPlayer'); +logger.setLevel('INFO'); +logger.info(`🍻 DPlayer start! Cheers!`); + +var postIP = []; + +var mongodbUrl; +if (process.env.MONGODB_USERNAME && process.env.MONGODB_PASSWORD && process.env.MONGODB_PORT_27017_TCP_ADDR && process.env.MONGODB_PORT_27017_TCP_PORT && process.env.MONGODB_INSTANCE_NAME) { + mongodbUrl = 'mongodb://' + process.env.MONGODB_USERNAME + ':' + process.env.MONGODB_PASSWORD + '@' + process.env.MONGODB_PORT_27017_TCP_ADDR + ':' + process.env.MONGODB_PORT_27017_TCP_PORT + '/' + process.env.MONGODB_INSTANCE_NAME; +} +else { + mongodbUrl = 'mongodb://localhost:27017/danmaku'; +} + var danmakuSchema = new mongoose.Schema({ player: String, author: String, @@ -27,16 +57,23 @@ app.all('*', function(req, res, next) { } }); -app.get('/', function(req, res) { +app.get('/', function (req, res) { + var ip = req.headers['x-forwarded-for'] || + req.connection.remoteAddress || + req.socket.remoteAddress || + req.connection.socket.remoteAddress; + logger.info(`GET form IP: ${ip}`); + mongoose.connect(mongodbUrl); var db = mongoose.connection; - db.on('error',console.error); + db.on('error', errorListener); var id = url.parse(req.url,true).query.id; db.once('open', function() { + cleanListener(); danmaku.find({player: id}, function (err, data) { if (err) { - console.error(err); + logger.error(err); } var json = `{"code": 1,"danmaku":[`; @@ -47,32 +84,138 @@ app.get('/', function(req, res) { } } json += `]}`; - res.write(json); - res.end(); + res.send(json); db.close(); }) }); + + function errorListener (err) { + cleanListener(); + logger.error(err); + res.send(`{"code": 0, "msg": "Error happens, please contact system administrator."}`); + } + + function cleanListener () { + db.removeListener('error', errorListener); + } }); app.post('/', function (req, res) { - var body = '', jsonStr; - req.on('data', function (chunk) { + var body = ''; + var jsonStr; + var db; + var ip = req.headers['x-forwarded-for'] || + req.connection.remoteAddress || + req.socket.remoteAddress || + req.connection.socket.remoteAddress; + + // check black ip + var blanklist = fs.readFileSync('blacklist').toString().split('\n'); + if (blanklist.indexOf(ip) !== -1) { + logger.info(`Reject POST form ${ip} for black ip.`); + res.send(`{"code": -1, "msg": "Rejected for black ip."}`); + return; + } + + // frequency limitation + if (postIP.indexOf(ip) !== -1) { + logger.info(`Reject POST form ${ip} for frequent operation.`); + res.send(`{"code": -2, "msg": "Rejected for frequent operation."}`); + return; + } + else { + postIP.push(ip); + setTimeout(function () { + postIP.splice(0, 1); + }, 1000); + } + + req.on('data', dataListener); + req.on('end', endListener); + + function dataListener (chunk) { body += chunk; - }); - req.on('end', function () { + } + function endListener () { + cleanListener(); try { jsonStr = JSON.parse(body); } catch (err) { jsonStr = null; } - var dan = new danmaku({player: jsonStr.player, author: jsonStr.author, time: jsonStr.time, text: jsonStr.text, color: jsonStr.color, type: jsonStr.type}); - dan.save(function (err, d) { - if (err){ - console.error(err); - } + // check data + console.log(jsonStr.player, jsonStr.author, jsonStr.time, jsonStr.text, jsonStr.color, jsonStr.type); + if (jsonStr.player === undefined + || jsonStr.author === undefined + || jsonStr.time === undefined + || jsonStr.text === undefined + || jsonStr.color === undefined + || jsonStr.type === undefined) { + logger.info(`Reject POST form ${ip} for illegal data: ${JSON.stringify(jsonStr)}`); + res.send(`{"code": -3, "msg": "Rejected for illegal data"}`); + return; + } + + // check token: set it yourself + function checkToken (token) { + return true; + } + if (!checkToken(jsonStr.token)) { + logger.info(`Rejected POST form ${ip} for illegal token: ${jsonStr.token}`); + res.send(`{"code": -4, "msg": "Rejected for illegal token: ${jsonStr.token}"}`); + return; + } + + // check black username + if (blanklist.indexOf(jsonStr.author) !== -1) { + logger.info(`Reject POST form ${jsonStr.author} for black user.`); + res.send(`{"code": -5, "msg": "Rejected for black user."}`); + return; + } + + logger.info(`POST form ${ip}, data: ${JSON.stringify(jsonStr)}`); + + mongoose.connect(mongodbUrl); + db = mongoose.connection; + db.on('error', errorListener); + db.once('open', function() { + cleandbListener(); + + var dan = new danmaku({ + player: jsonStr.player, + author: jsonStr.author, + time: jsonStr.time, + text: jsonStr.text, + color: jsonStr.color, + type: jsonStr.type + }); + dan.save(function (err, d) { + if (err){ + logger.error(err); + res.send(`{"code": 0, "msg": "Error happens, please contact system administrator."}`); + } + else { + res.send(`{"code": 1, "data": ${JSON.stringify(d)}}`); + } + db.close(); + }); }); - }); + } + + function errorListener (err) { + cleandbListener(); + logger.error(err); + res.send(`{"code": 0, "msg": "Error happens, please contact system administrator."}`); + } + + function cleandbListener () { + db.removeListener('error', errorListener); + } + function cleanListener () { + req.removeListener('data', dataListener); + req.removeListener('end', endListener); + } }); app.listen(1207); \ No newline at end of file diff --git a/nodejs/package.json b/nodejs/package.json index 75309aa..e8f57a0 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -8,6 +8,7 @@ "devDependencies": {}, "dependencies": { "express": "^4.13.4", + "log4js": "^0.6.36", "mongoose": "^4.1.9" } } diff --git a/package.json b/package.json index 60a5f48..c0e52f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dplayer", - "version": "0.0.1", + "version": "0.0.2", "description": "Wow, such a lovely HTML5 danmaku video player", "main": "dist/DPlayer.min.js", "scripts": { diff --git a/src/DPlayer.js b/src/DPlayer.js index 86d5a3f..a83ccd1 100644 --- a/src/DPlayer.js +++ b/src/DPlayer.js @@ -1,993 +1,1003 @@ (() => { class DPlayer { - /** - * DPlayer constructor function - * - * @param {Object} option - See README - * @constructor - */ - constructor(option) { - this.svg = { - 'play': ['0 0 16 32', 'M15.552 15.168q0.448 0.32 0.448 0.832 0 0.448-0.448 0.768l-13.696 8.512q-0.768 0.512-1.312 0.192t-0.544-1.28v-16.448q0-0.96 0.544-1.28t1.312 0.192z'], - 'pause': ['0 0 17 32', 'M14.080 4.8q2.88 0 2.88 2.048v18.24q0 2.112-2.88 2.112t-2.88-2.112v-18.24q0-2.048 2.88-2.048zM2.88 4.8q2.88 0 2.88 2.048v18.24q0 2.112-2.88 2.112t-2.88-2.112v-18.24q0-2.048 2.88-2.048z'], - 'volume-up': ['0 0 21 32', 'M13.728 6.272v19.456q0 0.448-0.352 0.8t-0.8 0.32-0.8-0.32l-5.952-5.952h-4.672q-0.48 0-0.8-0.352t-0.352-0.8v-6.848q0-0.48 0.352-0.8t0.8-0.352h4.672l5.952-5.952q0.32-0.32 0.8-0.32t0.8 0.32 0.352 0.8zM20.576 16q0 1.344-0.768 2.528t-2.016 1.664q-0.16 0.096-0.448 0.096-0.448 0-0.8-0.32t-0.32-0.832q0-0.384 0.192-0.64t0.544-0.448 0.608-0.384 0.512-0.64 0.192-1.024-0.192-1.024-0.512-0.64-0.608-0.384-0.544-0.448-0.192-0.64q0-0.48 0.32-0.832t0.8-0.32q0.288 0 0.448 0.096 1.248 0.48 2.016 1.664t0.768 2.528zM25.152 16q0 2.72-1.536 5.056t-4 3.36q-0.256 0.096-0.448 0.096-0.48 0-0.832-0.352t-0.32-0.8q0-0.704 0.672-1.056 1.024-0.512 1.376-0.8 1.312-0.96 2.048-2.4t0.736-3.104-0.736-3.104-2.048-2.4q-0.352-0.288-1.376-0.8-0.672-0.352-0.672-1.056 0-0.448 0.32-0.8t0.8-0.352q0.224 0 0.48 0.096 2.496 1.056 4 3.36t1.536 5.056zM29.728 16q0 4.096-2.272 7.552t-6.048 5.056q-0.224 0.096-0.448 0.096-0.48 0-0.832-0.352t-0.32-0.8q0-0.64 0.704-1.056 0.128-0.064 0.384-0.192t0.416-0.192q0.8-0.448 1.44-0.896 2.208-1.632 3.456-4.064t1.216-5.152-1.216-5.152-3.456-4.064q-0.64-0.448-1.44-0.896-0.128-0.096-0.416-0.192t-0.384-0.192q-0.704-0.416-0.704-1.056 0-0.448 0.32-0.8t0.832-0.352q0.224 0 0.448 0.096 3.776 1.632 6.048 5.056t2.272 7.552z'], - 'volume-down': ['0 0 21 32', 'M13.728 6.272v19.456q0 0.448-0.352 0.8t-0.8 0.32-0.8-0.32l-5.952-5.952h-4.672q-0.48 0-0.8-0.352t-0.352-0.8v-6.848q0-0.48 0.352-0.8t0.8-0.352h4.672l5.952-5.952q0.32-0.32 0.8-0.32t0.8 0.32 0.352 0.8zM20.576 16q0 1.344-0.768 2.528t-2.016 1.664q-0.16 0.096-0.448 0.096-0.448 0-0.8-0.32t-0.32-0.832q0-0.384 0.192-0.64t0.544-0.448 0.608-0.384 0.512-0.64 0.192-1.024-0.192-1.024-0.512-0.64-0.608-0.384-0.544-0.448-0.192-0.64q0-0.48 0.32-0.832t0.8-0.32q0.288 0 0.448 0.096 1.248 0.48 2.016 1.664t0.768 2.528z'], - 'volume-off': ['0 0 21 32', 'M13.728 6.272v19.456q0 0.448-0.352 0.8t-0.8 0.32-0.8-0.32l-5.952-5.952h-4.672q-0.48 0-0.8-0.352t-0.352-0.8v-6.848q0-0.48 0.352-0.8t0.8-0.352h4.672l5.952-5.952q0.32-0.32 0.8-0.32t0.8 0.32 0.352 0.8z'], - 'loop': ['0 0 32 32', 'M1.882 16.941c0 4.152 3.221 7.529 7.177 7.529v1.882c-4.996 0-9.060-4.222-9.060-9.412s4.064-9.412 9.060-9.412h7.96l-3.098-3.098 1.331-1.331 5.372 5.37-5.37 5.372-1.333-1.333 3.1-3.098h-7.962c-3.957 0-7.177 3.377-7.177 7.529zM22.94 7.529v1.882c3.957 0 7.177 3.377 7.177 7.529s-3.221 7.529-7.177 7.529h-7.962l3.098-3.098-1.331-1.331-5.37 5.37 5.372 5.372 1.331-1.331-3.1-3.1h7.96c4.998 0 9.062-4.222 9.062-9.412s-4.064-9.412-9.060-9.412z'], - 'full': ['0 0 32 33', 'M6.667 28h-5.333c-0.8 0-1.333-0.533-1.333-1.333v-5.333c0-0.8 0.533-1.333 1.333-1.333s1.333 0.533 1.333 1.333v4h4c0.8 0 1.333 0.533 1.333 1.333s-0.533 1.333-1.333 1.333zM30.667 28h-5.333c-0.8 0-1.333-0.533-1.333-1.333s0.533-1.333 1.333-1.333h4v-4c0-0.8 0.533-1.333 1.333-1.333s1.333 0.533 1.333 1.333v5.333c0 0.8-0.533 1.333-1.333 1.333zM30.667 12c-0.8 0-1.333-0.533-1.333-1.333v-4h-4c-0.8 0-1.333-0.533-1.333-1.333s0.533-1.333 1.333-1.333h5.333c0.8 0 1.333 0.533 1.333 1.333v5.333c0 0.8-0.533 1.333-1.333 1.333zM1.333 12c-0.8 0-1.333-0.533-1.333-1.333v-5.333c0-0.8 0.533-1.333 1.333-1.333h5.333c0.8 0 1.333 0.533 1.333 1.333s-0.533 1.333-1.333 1.333h-4v4c0 0.8-0.533 1.333-1.333 1.333z'], - 'setting': ['0 0 32 28', 'M28.633 17.104c0.035 0.21 0.026 0.463-0.026 0.76s-0.14 0.598-0.262 0.904c-0.122 0.306-0.271 0.581-0.445 0.825s-0.367 0.419-0.576 0.524c-0.209 0.105-0.393 0.157-0.55 0.157s-0.332-0.035-0.524-0.105c-0.175-0.052-0.393-0.1-0.655-0.144s-0.528-0.052-0.799-0.026c-0.271 0.026-0.541 0.083-0.812 0.17s-0.502 0.236-0.694 0.445c-0.419 0.437-0.664 0.934-0.734 1.493s0.009 1.092 0.236 1.598c0.175 0.349 0.148 0.699-0.079 1.048-0.105 0.14-0.271 0.284-0.498 0.432s-0.476 0.284-0.747 0.406-0.555 0.218-0.851 0.288c-0.297 0.070-0.559 0.105-0.786 0.105-0.157 0-0.306-0.061-0.445-0.183s-0.236-0.253-0.288-0.393h-0.026c-0.192-0.541-0.52-1.009-0.982-1.402s-1-0.589-1.611-0.589c-0.594 0-1.131 0.197-1.611 0.589s-0.816 0.851-1.009 1.375c-0.087 0.21-0.218 0.362-0.393 0.458s-0.367 0.144-0.576 0.144c-0.244 0-0.52-0.044-0.825-0.131s-0.611-0.197-0.917-0.327c-0.306-0.131-0.581-0.284-0.825-0.458s-0.428-0.349-0.55-0.524c-0.087-0.122-0.135-0.266-0.144-0.432s0.057-0.397 0.197-0.694c0.192-0.402 0.266-0.86 0.223-1.375s-0.266-0.991-0.668-1.428c-0.244-0.262-0.541-0.432-0.891-0.511s-0.681-0.109-0.995-0.092c-0.367 0.017-0.742 0.087-1.127 0.21-0.244 0.070-0.489 0.052-0.734-0.052-0.192-0.070-0.371-0.231-0.537-0.485s-0.314-0.533-0.445-0.838c-0.131-0.306-0.231-0.62-0.301-0.943s-0.087-0.59-0.052-0.799c0.052-0.384 0.227-0.629 0.524-0.734 0.524-0.21 0.995-0.555 1.415-1.035s0.629-1.017 0.629-1.611c0-0.611-0.21-1.144-0.629-1.598s-0.891-0.786-1.415-0.996c-0.157-0.052-0.288-0.179-0.393-0.38s-0.157-0.406-0.157-0.616c0-0.227 0.035-0.48 0.105-0.76s0.162-0.55 0.275-0.812 0.244-0.502 0.393-0.72c0.148-0.218 0.31-0.38 0.485-0.485 0.14-0.087 0.275-0.122 0.406-0.105s0.275 0.052 0.432 0.105c0.524 0.21 1.070 0.275 1.637 0.197s1.070-0.327 1.506-0.747c0.21-0.209 0.362-0.467 0.458-0.773s0.157-0.607 0.183-0.904c0.026-0.297 0.026-0.568 0-0.812s-0.048-0.419-0.065-0.524c-0.035-0.105-0.066-0.227-0.092-0.367s-0.013-0.262 0.039-0.367c0.105-0.244 0.293-0.458 0.563-0.642s0.563-0.336 0.878-0.458c0.314-0.122 0.62-0.214 0.917-0.275s0.533-0.092 0.707-0.092c0.227 0 0.406 0.074 0.537 0.223s0.223 0.301 0.275 0.458c0.192 0.471 0.507 0.886 0.943 1.244s0.952 0.537 1.546 0.537c0.611 0 1.153-0.17 1.624-0.511s0.803-0.773 0.996-1.297c0.070-0.14 0.179-0.284 0.327-0.432s0.301-0.223 0.458-0.223c0.244 0 0.511 0.035 0.799 0.105s0.572 0.166 0.851 0.288c0.279 0.122 0.537 0.279 0.773 0.472s0.423 0.402 0.563 0.629c0.087 0.14 0.113 0.293 0.079 0.458s-0.070 0.284-0.105 0.354c-0.227 0.506-0.297 1.039-0.21 1.598s0.341 1.048 0.76 1.467c0.419 0.419 0.934 0.651 1.546 0.694s1.179-0.057 1.703-0.301c0.14-0.087 0.31-0.122 0.511-0.105s0.371 0.096 0.511 0.236c0.262 0.244 0.493 0.616 0.694 1.113s0.336 1 0.406 1.506c0.035 0.297-0.013 0.528-0.144 0.694s-0.266 0.275-0.406 0.327c-0.542 0.192-1.004 0.528-1.388 1.009s-0.576 1.026-0.576 1.637c0 0.594 0.162 1.113 0.485 1.559s0.747 0.764 1.27 0.956c0.122 0.070 0.227 0.14 0.314 0.21 0.192 0.157 0.323 0.358 0.393 0.602v0zM16.451 19.462c0.786 0 1.528-0.149 2.227-0.445s1.305-0.707 1.821-1.231c0.515-0.524 0.921-1.131 1.218-1.821s0.445-1.428 0.445-2.214c0-0.786-0.148-1.524-0.445-2.214s-0.703-1.292-1.218-1.808c-0.515-0.515-1.122-0.921-1.821-1.218s-1.441-0.445-2.227-0.445c-0.786 0-1.524 0.148-2.214 0.445s-1.292 0.703-1.808 1.218c-0.515 0.515-0.921 1.118-1.218 1.808s-0.445 1.428-0.445 2.214c0 0.786 0.149 1.524 0.445 2.214s0.703 1.297 1.218 1.821c0.515 0.524 1.118 0.934 1.808 1.231s1.428 0.445 2.214 0.445v0z'], - 'right': ['0 0 32 32', 'M22 16l-10.105-10.6-1.895 1.987 8.211 8.613-8.211 8.612 1.895 1.988 8.211-8.613z'], - 'comment': ['0 0 32 32', 'M27.128 0.38h-22.553c-2.336 0-4.229 1.825-4.229 4.076v16.273c0 2.251 1.893 4.076 4.229 4.076h4.229v-2.685h8.403l-8.784 8.072 1.566 1.44 7.429-6.827h9.71c2.335 0 4.229-1.825 4.229-4.076v-16.273c0-2.252-1.894-4.076-4.229-4.076zM28.538 19.403c0 1.5-1.262 2.717-2.819 2.717h-8.36l-0.076-0.070-0.076 0.070h-11.223c-1.557 0-2.819-1.217-2.819-2.717v-13.589c0-1.501 1.262-2.718 2.819-2.718h19.734c1.557 0 2.819-0.141 2.819 1.359v14.947zM9.206 10.557c-1.222 0-2.215 0.911-2.215 2.036s0.992 2.035 2.215 2.035c1.224 0 2.216-0.911 2.216-2.035s-0.992-2.036-2.216-2.036zM22.496 10.557c-1.224 0-2.215 0.911-2.215 2.036s0.991 2.035 2.215 2.035c1.224 0 2.215-0.911 2.215-2.035s-0.991-2.036-2.215-2.036zM15.852 10.557c-1.224 0-2.215 0.911-2.215 2.036s0.991 2.035 2.215 2.035c1.222 0 2.215-0.911 2.215-2.035s-0.992-2.036-2.215-2.036z'], - 'comment-off': ['0 0 32 32', 'M27.090 0.131h-22.731c-2.354 0-4.262 1.839-4.262 4.109v16.401c0 2.269 1.908 4.109 4.262 4.109h4.262v-2.706h8.469l-8.853 8.135 1.579 1.451 7.487-6.88h9.787c2.353 0 4.262-1.84 4.262-4.109v-16.401c0-2.27-1.909-4.109-4.262-4.109v0zM28.511 19.304c0 1.512-1.272 2.738-2.841 2.738h-8.425l-0.076-0.070-0.076 0.070h-11.311c-1.569 0-2.841-1.226-2.841-2.738v-13.696c0-1.513 1.272-2.739 2.841-2.739h19.889c1.569 0 2.841-0.142 2.841 1.37v15.064z'], - 'send': ['0 0 32 32', 'M13.725 30l3.9-5.325-3.9-1.125v6.45zM0 17.5l11.050 3.35 13.6-11.55-10.55 12.425 11.8 3.65 6.1-23.375-32 15.5z'] - }; - this.getSVG = (type) => { - return ` - - - - + /** + * DPlayer constructor function + * + * @param {Object} option - See README + * @constructor + */ + constructor(option) { + this.svg = { + 'play': ['0 0 16 32', 'M15.552 15.168q0.448 0.32 0.448 0.832 0 0.448-0.448 0.768l-13.696 8.512q-0.768 0.512-1.312 0.192t-0.544-1.28v-16.448q0-0.96 0.544-1.28t1.312 0.192z'], + 'pause': ['0 0 17 32', 'M14.080 4.8q2.88 0 2.88 2.048v18.24q0 2.112-2.88 2.112t-2.88-2.112v-18.24q0-2.048 2.88-2.048zM2.88 4.8q2.88 0 2.88 2.048v18.24q0 2.112-2.88 2.112t-2.88-2.112v-18.24q0-2.048 2.88-2.048z'], + 'volume-up': ['0 0 21 32', 'M13.728 6.272v19.456q0 0.448-0.352 0.8t-0.8 0.32-0.8-0.32l-5.952-5.952h-4.672q-0.48 0-0.8-0.352t-0.352-0.8v-6.848q0-0.48 0.352-0.8t0.8-0.352h4.672l5.952-5.952q0.32-0.32 0.8-0.32t0.8 0.32 0.352 0.8zM20.576 16q0 1.344-0.768 2.528t-2.016 1.664q-0.16 0.096-0.448 0.096-0.448 0-0.8-0.32t-0.32-0.832q0-0.384 0.192-0.64t0.544-0.448 0.608-0.384 0.512-0.64 0.192-1.024-0.192-1.024-0.512-0.64-0.608-0.384-0.544-0.448-0.192-0.64q0-0.48 0.32-0.832t0.8-0.32q0.288 0 0.448 0.096 1.248 0.48 2.016 1.664t0.768 2.528zM25.152 16q0 2.72-1.536 5.056t-4 3.36q-0.256 0.096-0.448 0.096-0.48 0-0.832-0.352t-0.32-0.8q0-0.704 0.672-1.056 1.024-0.512 1.376-0.8 1.312-0.96 2.048-2.4t0.736-3.104-0.736-3.104-2.048-2.4q-0.352-0.288-1.376-0.8-0.672-0.352-0.672-1.056 0-0.448 0.32-0.8t0.8-0.352q0.224 0 0.48 0.096 2.496 1.056 4 3.36t1.536 5.056zM29.728 16q0 4.096-2.272 7.552t-6.048 5.056q-0.224 0.096-0.448 0.096-0.48 0-0.832-0.352t-0.32-0.8q0-0.64 0.704-1.056 0.128-0.064 0.384-0.192t0.416-0.192q0.8-0.448 1.44-0.896 2.208-1.632 3.456-4.064t1.216-5.152-1.216-5.152-3.456-4.064q-0.64-0.448-1.44-0.896-0.128-0.096-0.416-0.192t-0.384-0.192q-0.704-0.416-0.704-1.056 0-0.448 0.32-0.8t0.832-0.352q0.224 0 0.448 0.096 3.776 1.632 6.048 5.056t2.272 7.552z'], + 'volume-down': ['0 0 21 32', 'M13.728 6.272v19.456q0 0.448-0.352 0.8t-0.8 0.32-0.8-0.32l-5.952-5.952h-4.672q-0.48 0-0.8-0.352t-0.352-0.8v-6.848q0-0.48 0.352-0.8t0.8-0.352h4.672l5.952-5.952q0.32-0.32 0.8-0.32t0.8 0.32 0.352 0.8zM20.576 16q0 1.344-0.768 2.528t-2.016 1.664q-0.16 0.096-0.448 0.096-0.448 0-0.8-0.32t-0.32-0.832q0-0.384 0.192-0.64t0.544-0.448 0.608-0.384 0.512-0.64 0.192-1.024-0.192-1.024-0.512-0.64-0.608-0.384-0.544-0.448-0.192-0.64q0-0.48 0.32-0.832t0.8-0.32q0.288 0 0.448 0.096 1.248 0.48 2.016 1.664t0.768 2.528z'], + 'volume-off': ['0 0 21 32', 'M13.728 6.272v19.456q0 0.448-0.352 0.8t-0.8 0.32-0.8-0.32l-5.952-5.952h-4.672q-0.48 0-0.8-0.352t-0.352-0.8v-6.848q0-0.48 0.352-0.8t0.8-0.352h4.672l5.952-5.952q0.32-0.32 0.8-0.32t0.8 0.32 0.352 0.8z'], + 'loop': ['0 0 32 32', 'M1.882 16.941c0 4.152 3.221 7.529 7.177 7.529v1.882c-4.996 0-9.060-4.222-9.060-9.412s4.064-9.412 9.060-9.412h7.96l-3.098-3.098 1.331-1.331 5.372 5.37-5.37 5.372-1.333-1.333 3.1-3.098h-7.962c-3.957 0-7.177 3.377-7.177 7.529zM22.94 7.529v1.882c3.957 0 7.177 3.377 7.177 7.529s-3.221 7.529-7.177 7.529h-7.962l3.098-3.098-1.331-1.331-5.37 5.37 5.372 5.372 1.331-1.331-3.1-3.1h7.96c4.998 0 9.062-4.222 9.062-9.412s-4.064-9.412-9.060-9.412z'], + 'full': ['0 0 32 33', 'M6.667 28h-5.333c-0.8 0-1.333-0.533-1.333-1.333v-5.333c0-0.8 0.533-1.333 1.333-1.333s1.333 0.533 1.333 1.333v4h4c0.8 0 1.333 0.533 1.333 1.333s-0.533 1.333-1.333 1.333zM30.667 28h-5.333c-0.8 0-1.333-0.533-1.333-1.333s0.533-1.333 1.333-1.333h4v-4c0-0.8 0.533-1.333 1.333-1.333s1.333 0.533 1.333 1.333v5.333c0 0.8-0.533 1.333-1.333 1.333zM30.667 12c-0.8 0-1.333-0.533-1.333-1.333v-4h-4c-0.8 0-1.333-0.533-1.333-1.333s0.533-1.333 1.333-1.333h5.333c0.8 0 1.333 0.533 1.333 1.333v5.333c0 0.8-0.533 1.333-1.333 1.333zM1.333 12c-0.8 0-1.333-0.533-1.333-1.333v-5.333c0-0.8 0.533-1.333 1.333-1.333h5.333c0.8 0 1.333 0.533 1.333 1.333s-0.533 1.333-1.333 1.333h-4v4c0 0.8-0.533 1.333-1.333 1.333z'], + 'setting': ['0 0 32 28', 'M28.633 17.104c0.035 0.21 0.026 0.463-0.026 0.76s-0.14 0.598-0.262 0.904c-0.122 0.306-0.271 0.581-0.445 0.825s-0.367 0.419-0.576 0.524c-0.209 0.105-0.393 0.157-0.55 0.157s-0.332-0.035-0.524-0.105c-0.175-0.052-0.393-0.1-0.655-0.144s-0.528-0.052-0.799-0.026c-0.271 0.026-0.541 0.083-0.812 0.17s-0.502 0.236-0.694 0.445c-0.419 0.437-0.664 0.934-0.734 1.493s0.009 1.092 0.236 1.598c0.175 0.349 0.148 0.699-0.079 1.048-0.105 0.14-0.271 0.284-0.498 0.432s-0.476 0.284-0.747 0.406-0.555 0.218-0.851 0.288c-0.297 0.070-0.559 0.105-0.786 0.105-0.157 0-0.306-0.061-0.445-0.183s-0.236-0.253-0.288-0.393h-0.026c-0.192-0.541-0.52-1.009-0.982-1.402s-1-0.589-1.611-0.589c-0.594 0-1.131 0.197-1.611 0.589s-0.816 0.851-1.009 1.375c-0.087 0.21-0.218 0.362-0.393 0.458s-0.367 0.144-0.576 0.144c-0.244 0-0.52-0.044-0.825-0.131s-0.611-0.197-0.917-0.327c-0.306-0.131-0.581-0.284-0.825-0.458s-0.428-0.349-0.55-0.524c-0.087-0.122-0.135-0.266-0.144-0.432s0.057-0.397 0.197-0.694c0.192-0.402 0.266-0.86 0.223-1.375s-0.266-0.991-0.668-1.428c-0.244-0.262-0.541-0.432-0.891-0.511s-0.681-0.109-0.995-0.092c-0.367 0.017-0.742 0.087-1.127 0.21-0.244 0.070-0.489 0.052-0.734-0.052-0.192-0.070-0.371-0.231-0.537-0.485s-0.314-0.533-0.445-0.838c-0.131-0.306-0.231-0.62-0.301-0.943s-0.087-0.59-0.052-0.799c0.052-0.384 0.227-0.629 0.524-0.734 0.524-0.21 0.995-0.555 1.415-1.035s0.629-1.017 0.629-1.611c0-0.611-0.21-1.144-0.629-1.598s-0.891-0.786-1.415-0.996c-0.157-0.052-0.288-0.179-0.393-0.38s-0.157-0.406-0.157-0.616c0-0.227 0.035-0.48 0.105-0.76s0.162-0.55 0.275-0.812 0.244-0.502 0.393-0.72c0.148-0.218 0.31-0.38 0.485-0.485 0.14-0.087 0.275-0.122 0.406-0.105s0.275 0.052 0.432 0.105c0.524 0.21 1.070 0.275 1.637 0.197s1.070-0.327 1.506-0.747c0.21-0.209 0.362-0.467 0.458-0.773s0.157-0.607 0.183-0.904c0.026-0.297 0.026-0.568 0-0.812s-0.048-0.419-0.065-0.524c-0.035-0.105-0.066-0.227-0.092-0.367s-0.013-0.262 0.039-0.367c0.105-0.244 0.293-0.458 0.563-0.642s0.563-0.336 0.878-0.458c0.314-0.122 0.62-0.214 0.917-0.275s0.533-0.092 0.707-0.092c0.227 0 0.406 0.074 0.537 0.223s0.223 0.301 0.275 0.458c0.192 0.471 0.507 0.886 0.943 1.244s0.952 0.537 1.546 0.537c0.611 0 1.153-0.17 1.624-0.511s0.803-0.773 0.996-1.297c0.070-0.14 0.179-0.284 0.327-0.432s0.301-0.223 0.458-0.223c0.244 0 0.511 0.035 0.799 0.105s0.572 0.166 0.851 0.288c0.279 0.122 0.537 0.279 0.773 0.472s0.423 0.402 0.563 0.629c0.087 0.14 0.113 0.293 0.079 0.458s-0.070 0.284-0.105 0.354c-0.227 0.506-0.297 1.039-0.21 1.598s0.341 1.048 0.76 1.467c0.419 0.419 0.934 0.651 1.546 0.694s1.179-0.057 1.703-0.301c0.14-0.087 0.31-0.122 0.511-0.105s0.371 0.096 0.511 0.236c0.262 0.244 0.493 0.616 0.694 1.113s0.336 1 0.406 1.506c0.035 0.297-0.013 0.528-0.144 0.694s-0.266 0.275-0.406 0.327c-0.542 0.192-1.004 0.528-1.388 1.009s-0.576 1.026-0.576 1.637c0 0.594 0.162 1.113 0.485 1.559s0.747 0.764 1.27 0.956c0.122 0.070 0.227 0.14 0.314 0.21 0.192 0.157 0.323 0.358 0.393 0.602v0zM16.451 19.462c0.786 0 1.528-0.149 2.227-0.445s1.305-0.707 1.821-1.231c0.515-0.524 0.921-1.131 1.218-1.821s0.445-1.428 0.445-2.214c0-0.786-0.148-1.524-0.445-2.214s-0.703-1.292-1.218-1.808c-0.515-0.515-1.122-0.921-1.821-1.218s-1.441-0.445-2.227-0.445c-0.786 0-1.524 0.148-2.214 0.445s-1.292 0.703-1.808 1.218c-0.515 0.515-0.921 1.118-1.218 1.808s-0.445 1.428-0.445 2.214c0 0.786 0.149 1.524 0.445 2.214s0.703 1.297 1.218 1.821c0.515 0.524 1.118 0.934 1.808 1.231s1.428 0.445 2.214 0.445v0z'], + 'right': ['0 0 32 32', 'M22 16l-10.105-10.6-1.895 1.987 8.211 8.613-8.211 8.612 1.895 1.988 8.211-8.613z'], + 'comment': ['0 0 32 32', 'M27.128 0.38h-22.553c-2.336 0-4.229 1.825-4.229 4.076v16.273c0 2.251 1.893 4.076 4.229 4.076h4.229v-2.685h8.403l-8.784 8.072 1.566 1.44 7.429-6.827h9.71c2.335 0 4.229-1.825 4.229-4.076v-16.273c0-2.252-1.894-4.076-4.229-4.076zM28.538 19.403c0 1.5-1.262 2.717-2.819 2.717h-8.36l-0.076-0.070-0.076 0.070h-11.223c-1.557 0-2.819-1.217-2.819-2.717v-13.589c0-1.501 1.262-2.718 2.819-2.718h19.734c1.557 0 2.819-0.141 2.819 1.359v14.947zM9.206 10.557c-1.222 0-2.215 0.911-2.215 2.036s0.992 2.035 2.215 2.035c1.224 0 2.216-0.911 2.216-2.035s-0.992-2.036-2.216-2.036zM22.496 10.557c-1.224 0-2.215 0.911-2.215 2.036s0.991 2.035 2.215 2.035c1.224 0 2.215-0.911 2.215-2.035s-0.991-2.036-2.215-2.036zM15.852 10.557c-1.224 0-2.215 0.911-2.215 2.036s0.991 2.035 2.215 2.035c1.222 0 2.215-0.911 2.215-2.035s-0.992-2.036-2.215-2.036z'], + 'comment-off': ['0 0 32 32', 'M27.090 0.131h-22.731c-2.354 0-4.262 1.839-4.262 4.109v16.401c0 2.269 1.908 4.109 4.262 4.109h4.262v-2.706h8.469l-8.853 8.135 1.579 1.451 7.487-6.88h9.787c2.353 0 4.262-1.84 4.262-4.109v-16.401c0-2.27-1.909-4.109-4.262-4.109v0zM28.511 19.304c0 1.512-1.272 2.738-2.841 2.738h-8.425l-0.076-0.070-0.076 0.070h-11.311c-1.569 0-2.841-1.226-2.841-2.738v-13.696c0-1.513 1.272-2.739 2.841-2.739h19.889c1.569 0 2.841-0.142 2.841 1.37v15.064z'], + 'send': ['0 0 32 32', 'M13.725 30l3.9-5.325-3.9-1.125v6.45zM0 17.5l11.050 3.35 13.6-11.55-10.55 12.425 11.8 3.65 6.1-23.375-32 15.5z'] + }; + this.getSVG = (type) => { + return ` + + + + + `; + }; + + this.isMobile = navigator.userAgent.match(/(iPad)|(iPhone)|(iPod)|(android)|(webOS)/i); + // compatibility: some mobile browsers don't suppose autoplay + if (this.isMobile) { + option.autoplay = false; + } + + // default options + const defaultOption = { + element: document.getElementsByClassName('dplayer')[0], + autoplay: false, + theme: '#b7daff', + loop: false + }; + for (let defaultKey in defaultOption) { + if (defaultOption.hasOwnProperty(defaultKey) && !option.hasOwnProperty(defaultKey)) { + option[defaultKey] = defaultOption[defaultKey]; + } + } + + this.option = option; + this.loop = option.loop; + + /** + * Parse second to 00:00 format + * + * @param {Number} second + * @return {String} 00:00 format + */ + this.secondToTime = (second) => { + const add0 = (num) => { + return num < 10 ? '0' + num : '' + num; + }; + const min = parseInt(second / 60); + const sec = parseInt(second - min * 60); + return add0(min) + ':' + add0(sec); + }; + + /** + * Update progress bar, including loading progress bar and play progress bar + * + * @param {String} type - Point out which bar it is, should be played loaded or volume + * @param {Number} percentage + * @param {String} direction - Point out the direction of this bar, Should be height or width + */ + this.updateBar = (type, percentage, direction) => { + percentage = percentage > 0 ? percentage : 0; + percentage = percentage < 1 ? percentage : 1; + this[type + 'Bar'].style[direction] = percentage * 100 + '%'; + }; + + // define DPlayer events + this.eventTypes = ['play', 'pause', 'canplay', 'playing', 'ended', 'error']; + this.event = {}; + for (let i = 0; i < this.eventTypes.length; i++) { + this.event[this.eventTypes[i]] = []; + } + this.trigger = (type) => { + for (let i = 0; i < this.event[type].length; i++) { + this.event[type][i](); + } + } + } + + /** + * AutoLink initialization function + */ + init() { + this.element = this.option.element; + + this.element.innerHTML = ` +
+
+ +
+
+
+
+
+ +
+ +
+
+
+ +
+
+
+
+ 0:00 / 0:00 +
+
+
+ +
+
+
+
+ + + +
+
+ + + + + + + + +
+
+ + +
+
+
+ +
+
+ +
+
+
+
+
+ +
+
+
+
`; - }; - - this.isMobile = navigator.userAgent.match(/(iPad)|(iPhone)|(iPod)|(android)|(webOS)/i); - // compatibility: some mobile browsers don't suppose autoplay - if (this.isMobile) { - option.autoplay = false; - } - - // default options - const defaultOption = { - element: document.getElementsByClassName('dplayer')[0], - autoplay: false, - theme: '#b7daff', - loop: false - }; - for (let defaultKey in defaultOption) { - if (defaultOption.hasOwnProperty(defaultKey) && !option.hasOwnProperty(defaultKey)) { - option[defaultKey] = defaultOption[defaultKey]; - } - } - - this.option = option; - this.loop = option.loop; - - /** - * Parse second to 00:00 format - * - * @param {Number} second - * @return {String} 00:00 format - */ - this.secondToTime = (second) => { - const add0 = (num) => { - return num < 10 ? '0' + num : '' + num; + + // get this audio object + this.audio = this.element.getElementsByClassName('dplayer-video')[0]; + + this.ptime = this.element.getElementsByClassName('dplayer-ptime')[0]; + + // play and pause button + this.playButton = this.element.getElementsByClassName('dplayer-play-icon')[0]; + this.toggle = () => { + if (this.audio.paused) { + this.play(); + } + else { + this.pause(); + } }; - const min = parseInt(second / 60); - const sec = parseInt(second - min * 60); - return add0(min) + ':' + add0(sec); - }; - - /** - * Update progress bar, including loading progress bar and play progress bar - * - * @param {String} type - Point out which bar it is, should be played loaded or volume - * @param {Number} percentage - * @param {String} direction - Point out the direction of this bar, Should be height or width - */ - this.updateBar = (type, percentage, direction) => { - percentage = percentage > 0 ? percentage : 0; - percentage = percentage < 1 ? percentage : 1; - this[type + 'Bar'].style[direction] = percentage * 100 + '%'; - }; - - // define DPlayer events - this.eventTypes = ['play', 'pause', 'canplay', 'playing', 'ended', 'error']; - this.event = {}; - for (let i = 0; i < this.eventTypes.length; i++) { - this.event[this.eventTypes[i]] = []; - } - this.trigger = (type) => { - for (let i = 0; i < this.event[type].length; i++) { - this.event[type][i](); - } - } - } - - /** - * AutoLink initialization function - */ - init() { - this.element = this.option.element; - - this.element.innerHTML = ` -
-
- -
-
-
-
-
- -
- -
-
-
- -
-
-
-
- 0:00 / 0:00 -
-
-
- -
-
-
-
- - - -
-
- - - - - - - - -
-
- - -
-
-
- -
-
- -
-
-
-
-
- -
-
-
-
- `; - - // get this audio object - this.audio = this.element.getElementsByClassName('dplayer-video')[0]; - - this.ptime = this.element.getElementsByClassName('dplayer-ptime')[0]; - - // play and pause button - this.playButton = this.element.getElementsByClassName('dplayer-play-icon')[0]; - this.toggle = () => { - if (this.audio.paused) { - this.play(); - } - else { - this.pause(); - } - }; - this.playButton.addEventListener('click', this.toggle); - this.element.getElementsByClassName('dplayer-video-wrap')[0].addEventListener('click', this.toggle); - this.element.getElementsByClassName('dplayer-controller-mask')[0].addEventListener('click', this.toggle); - - - /** - * control play progress - */ - // get element's view position - const getElementViewLeft = (element) => { - let actualLeft = element.offsetLeft; - let current = element.offsetParent; - let elementScrollLeft; - if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) { - while (current !== null) { - actualLeft += current.offsetLeft; - current = current.offsetParent; - } - } - else { - while (current !== null && current !== this.element) { - actualLeft += current.offsetLeft; - current = current.offsetParent; - } - } - elementScrollLeft = document.body.scrollLeft + document.documentElement.scrollLeft; - return actualLeft - elementScrollLeft; - }; - - const getElementViewTop = (element) => { - let actualTop = element.offsetTop; - let current = element.offsetParent; - let elementScrollTop; - if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) { - while (current !== null) { - actualTop += current.offsetTop; - current = current.offsetParent; - } - } - else { - while (current !== null && current !== this.element) { - actualTop += current.offsetTop; - current = current.offsetParent; - } - } - elementScrollTop = document.body.scrollTop + document.documentElement.scrollTop; - return actualTop - elementScrollTop; - }; - - this.playedBar = this.element.getElementsByClassName('dplayer-played')[0]; - this.loadedBar = this.element.getElementsByClassName('dplayer-loaded')[0]; - this.bar = this.element.getElementsByClassName('dplayer-bar-wrap')[0]; - let barWidth; - - this.audio.addEventListener('seeked', () => { - if (this.option.danmaku) { - for (let i = 0; i < this.dan.length; i++) { - if (this.dan[i].time >= this.audio.currentTime) { - this.danIndex = i; - return; + this.playButton.addEventListener('click', this.toggle); + this.element.getElementsByClassName('dplayer-video-wrap')[0].addEventListener('click', this.toggle); + this.element.getElementsByClassName('dplayer-controller-mask')[0].addEventListener('click', this.toggle); + + + /** + * control play progress + */ + // get element's view position + const getElementViewLeft = (element) => { + let actualLeft = element.offsetLeft; + let current = element.offsetParent; + let elementScrollLeft; + if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) { + while (current !== null) { + actualLeft += current.offsetLeft; + current = current.offsetParent; } } - } - }); - - this.setTime = () => { - if (this.option.danmaku) { - this.playedTime = setInterval(() => { - this.updateBar('played', this.audio.currentTime / this.audio.duration, 'width'); - this.ptime.innerHTML = this.secondToTime(this.audio.currentTime); - this.trigger('playing'); - - const item = this.dan[this.danIndex]; - if (item && this.audio.currentTime >= parseFloat(item.time)) { - this.danmakuIn(item.text, item.color, item.type); - this.danIndex++; + else { + while (current !== null && current !== this.element) { + actualLeft += current.offsetLeft; + current = current.offsetParent; } - }, 100); - } - else { - this.playedTime = setInterval(() => { - this.updateBar('played', this.audio.currentTime / this.audio.duration, 'width'); - this.ptime.innerHTML = this.secondToTime(this.audio.currentTime); - this.trigger('playing'); - }, 100); - } - }; - - this.bar.addEventListener('click', (event) => { - const e = event || window.event; - barWidth = this.bar.clientWidth; - let percentage = (e.clientX - getElementViewLeft(this.bar)) / barWidth; - percentage = percentage > 0 ? percentage : 0; - percentage = percentage < 1 ? percentage : 1; - this.updateBar('played', percentage, 'width'); - this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = this.secondToTime(percentage * this.audio.duration); - this.audio.currentTime = parseFloat(this.playedBar.style.width) / 100 * this.audio.duration; - }); - - const thumbMove = (event) => { - const e = event || window.event; - let percentage = (e.clientX - getElementViewLeft(this.bar)) / barWidth; - percentage = percentage > 0 ? percentage : 0; - percentage = percentage < 1 ? percentage : 1; - this.updateBar('played', percentage, 'width'); - this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = this.secondToTime(percentage * this.audio.duration); - }; - - const thumbUp = () => { - document.removeEventListener('mouseup', thumbUp); - document.removeEventListener('mousemove', thumbMove); - this.audio.currentTime = parseFloat(this.playedBar.style.width) / 100 * this.audio.duration; - this.setTime(); - }; - - this.bar.addEventListener('mousedown', () => { - barWidth = this.bar.clientWidth; - clearInterval(this.playedTime); - document.addEventListener('mousemove', thumbMove); - document.addEventListener('mouseup', thumbUp); - }); - - - /** - * control volume - */ - this.volumeBar = this.element.getElementsByClassName('dplayer-volume-bar-inner')[0]; - const volumeEle = this.element.getElementsByClassName('dplayer-volume')[0]; - const volumeBarWrapWrap = this.element.getElementsByClassName('dplayer-volume-bar-wrap')[0]; - const volumeBarWrap = this.element.getElementsByClassName('dplayer-volume-bar')[0]; - const volumeicon = this.element.getElementsByClassName('dplayer-volume-icon')[0]; - const vWidth = 35; - const switchVolumeIcon = () => { - if (this.audio.volume >= 0.8) { - volumeicon.innerHTML = this.getSVG('volume-up'); - } - else if (this.audio.volume > 0) { - volumeicon.innerHTML = this.getSVG('volume-down'); - } - else { - volumeicon.innerHTML = this.getSVG('volume-off'); - } - }; - const volumeMove = (event) => { - const e = event || window.event; - let percentage = (e.clientX - getElementViewLeft(volumeBarWrap) - 5.5) / vWidth; - percentage = percentage > 0 ? percentage : 0; - percentage = percentage < 1 ? percentage : 1; - this.updateBar('volume', percentage, 'width'); - this.audio.volume = percentage; - if (this.audio.muted) { - this.audio.muted = false; - } - switchVolumeIcon(); - }; - const volumeUp = () => { - document.removeEventListener('mouseup', volumeUp); - document.removeEventListener('mousemove', volumeMove); - volumeEle.classList.remove('dplayer-volume-active'); - }; - - volumeBarWrapWrap.addEventListener('click', (event) => { - const e = event || window.event; - let percentage = (e.clientX - getElementViewLeft(volumeBarWrap) - 5.5) / vWidth; - percentage = percentage > 0 ? percentage : 0; - percentage = percentage < 1 ? percentage : 1; - this.updateBar('volume', percentage, 'width'); - this.audio.volume = percentage; - if (this.audio.muted) { - this.audio.muted = false; - } - switchVolumeIcon(); - }); - volumeBarWrapWrap.addEventListener('mousedown', () => { - document.addEventListener('mousemove', volumeMove); - document.addEventListener('mouseup', volumeUp); - volumeEle.classList.add('dplayer-volume-active'); - }); - volumeicon.addEventListener('click', () => { - if (this.audio.muted) { - this.audio.muted = false; - switchVolumeIcon(); - this.updateBar('volume', this.audio.volume, 'width'); - } - else { - this.audio.muted = true; - volumeicon.innerHTML = this.getSVG('volume-off'); - this.updateBar('volume', 0, 'width'); - } - }); - - - /** - * auto hide controller - */ - let hideTime = 0; - const hideController = () => { - this.element.classList.remove('dplayer-hide-controller'); - clearTimeout(hideTime); - hideTime = setTimeout(() => { - if (this.audio.played.length) { - this.element.classList.add('dplayer-hide-controller'); - closeSetting(); - closeComment(); } - }, 2000); - }; - this.element.addEventListener('mousemove', hideController); - this.element.addEventListener('click', hideController); - - - /*** - * setting - */ - this.danOpacity = 0.7; - const settingHTML = { - 'original': ` -
- 洗脑循环 -
- - -
-
-
- 速度 -
` - + this.getSVG('right') - + `
-
-
- 弹幕透明度 -
-
-
- -
-
-
-
`, - 'speed': ` -
- 0.5 -
-
- 0.75 -
-
- 正常 -
-
- 1.25 -
-
- 1.5 -
-
- 2 -
` - }; - - // toggle setting box - const settingIcon = this.element.getElementsByClassName('dplayer-setting-icon')[0]; - const settingBox = this.element.getElementsByClassName('dplayer-setting-box')[0]; - const mask = this.element.getElementsByClassName('dplayer-mask')[0]; - settingBox.innerHTML = settingHTML.original; - - const closeSetting = () => { - if (settingBox.classList.contains('dplayer-setting-box-open')) { - settingBox.classList.remove('dplayer-setting-box-open'); - mask.classList.remove('dplayer-mask-show'); - setTimeout(() => { - settingBox.classList.remove('dplayer-setting-box-narrow'); - settingBox.innerHTML = settingHTML.original; - settingEvent(); - }, 300); - } - }; - const openSetting = () => { - settingBox.classList.add('dplayer-setting-box-open'); - mask.classList.add('dplayer-mask-show'); - }; - - mask.addEventListener('click', () => { - closeSetting(); - }); - settingIcon.addEventListener('click', () => { - openSetting(); - }); - - const settingEvent = () => { - // loop control - const loopEle = this.element.getElementsByClassName('dplayer-setting-loop')[0]; - const loopToggle = loopEle.getElementsByClassName('dplayer-toggle-setting-input')[0]; - - loopToggle.checked = this.loop; - - loopEle.addEventListener('click', () => { - loopToggle.checked = !loopToggle.checked; - if (loopToggle.checked) { - this.loop = true; - this.audio.loop = this.loop; + elementScrollLeft = document.body.scrollLeft + document.documentElement.scrollLeft; + return actualLeft - elementScrollLeft; + }; + + const getElementViewTop = (element) => { + let actualTop = element.offsetTop; + let current = element.offsetParent; + let elementScrollTop; + if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) { + while (current !== null) { + actualTop += current.offsetTop; + current = current.offsetParent; + } } else { - this.loop = false; - this.audio.loop = this.loop; + while (current !== null && current !== this.element) { + actualTop += current.offsetTop; + current = current.offsetParent; + } + } + elementScrollTop = document.body.scrollTop + document.documentElement.scrollTop; + return actualTop - elementScrollTop; + }; + + this.playedBar = this.element.getElementsByClassName('dplayer-played')[0]; + this.loadedBar = this.element.getElementsByClassName('dplayer-loaded')[0]; + this.bar = this.element.getElementsByClassName('dplayer-bar-wrap')[0]; + let barWidth; + + this.audio.addEventListener('seeked', () => { + if (this.option.danmaku) { + for (let i = 0; i < this.dan.length; i++) { + if (this.dan[i].time >= this.audio.currentTime) { + this.danIndex = i; + return; + } + } } - closeSetting(); }); - loopToggle.addEventListener('change', () => { - if (loopToggle.checked) { - this.loop = true; - this.audio.loop = this.loop; + + this.setTime = () => { + if (this.option.danmaku) { + this.playedTime = setInterval(() => { + this.updateBar('played', this.audio.currentTime / this.audio.duration, 'width'); + this.ptime.innerHTML = this.secondToTime(this.audio.currentTime); + this.trigger('playing'); + + const item = this.dan[this.danIndex]; + if (item && this.audio.currentTime >= parseFloat(item.time)) { + this.danmakuIn(item.text, item.color, item.type); + this.danIndex++; + } + }, 100); } else { - this.loop = false; - this.audio.loop = this.loop; + this.playedTime = setInterval(() => { + this.updateBar('played', this.audio.currentTime / this.audio.duration, 'width'); + this.ptime.innerHTML = this.secondToTime(this.audio.currentTime); + this.trigger('playing'); + }, 100); } - closeSetting(); - }); - - // speed control - const speedEle = this.element.getElementsByClassName('dplayer-setting-speed')[0]; - speedEle.addEventListener('click', () => { - settingBox.classList.add('dplayer-setting-box-narrow'); - settingBox.innerHTML = settingHTML.speed; - - const speedItem = settingBox.getElementsByClassName('dplayer-setting-speed-item'); - for (let i = 0; i < speedItem.length; i++) { - speedItem[i].addEventListener('click', () => { - this.audio.playbackRate = speedItem[i].dataset.speed; - closeSetting(); - }); - } - }); - - // danmaku opacity - this.danmakuBar = this.element.getElementsByClassName('dplayer-danmaku-bar-inner')[0]; - const danmakuBarWrapWrap = this.element.getElementsByClassName('dplayer-danmaku-bar-wrap')[0]; - const danmakuBarWrap = this.element.getElementsByClassName('dplayer-danmaku-bar')[0]; - const danmakuSettingBox = this.element.getElementsByClassName('dplayer-setting-danmaku')[0]; - const dWidth = 130; - this.updateBar('danmaku', this.danOpacity, 'width'); - - const danmakuMove = (event) => { + }; + + this.bar.addEventListener('click', (event) => { const e = event || window.event; - let percentage = (e.clientX - getElementViewLeft(danmakuBarWrap)) / dWidth; + barWidth = this.bar.clientWidth; + let percentage = (e.clientX - getElementViewLeft(this.bar)) / barWidth; percentage = percentage > 0 ? percentage : 0; percentage = percentage < 1 ? percentage : 1; - this.updateBar('danmaku', percentage, 'width'); - const items = this.element.getElementsByClassName('dplayer-danmaku-item'); - for (let i = 0; i < items.length; i++) { - items[i].style.opacity = percentage; - } - this.danOpacity = percentage; - }; - const danmakuUp = () => { - document.removeEventListener('mouseup', danmakuUp); - document.removeEventListener('mousemove', danmakuMove); - danmakuSettingBox.classList.remove('dplayer-setting-danmaku-active'); - }; - - danmakuBarWrapWrap.addEventListener('click', (event) => { + this.updateBar('played', percentage, 'width'); + this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = this.secondToTime(percentage * this.audio.duration); + this.audio.currentTime = parseFloat(this.playedBar.style.width) / 100 * this.audio.duration; + }); + + const thumbMove = (event) => { const e = event || window.event; - let percentage = (e.clientX - getElementViewLeft(danmakuBarWrap)) / dWidth; + let percentage = (e.clientX - getElementViewLeft(this.bar)) / barWidth; percentage = percentage > 0 ? percentage : 0; percentage = percentage < 1 ? percentage : 1; - this.updateBar('danmaku', percentage, 'width'); - const items = this.element.getElementsByClassName('dplayer-danmaku-item'); - for (let i = 0; i < items.length; i++) { - items[i].style.opacity = percentage; - } - this.danOpacity = percentage; - }); - danmakuBarWrapWrap.addEventListener('mousedown', () => { - document.addEventListener('mousemove', danmakuMove); - document.addEventListener('mouseup', danmakuUp); - danmakuSettingBox.classList.add('dplayer-setting-danmaku-active'); - }); - }; - settingEvent(); - - - /** - * audio events - */ - // show audio time: the metadata has loaded or changed - this.audio.addEventListener('durationchange', () => { - if (this.audio.duration !== 1) { // compatibility: Android browsers will output 1 at first - this.element.getElementsByClassName('dplayer-dtime')[0].innerHTML = this.secondToTime(this.audio.duration); - } - }); - - // show audio loaded bar: to inform interested parties of progress downloading the media - this.audio.addEventListener('progress', () => { - const percentage = this.audio.buffered.length ? this.audio.buffered.end(this.audio.buffered.length - 1) / this.audio.duration : 0; - this.updateBar('loaded', percentage, 'width'); - }); - - // audio download error: an error occurs - this.audio.addEventListener('error', () => { - this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = `Error happens ╥﹏╥`; - this.trigger('pause'); - }); - - // audio can play: enough data is available that the media can be played - this.audio.addEventListener('canplay', () => { - this.trigger('canplay'); - }); - - // music end - this.ended = false; - this.audio.addEventListener('ended', () => { - this.updateBar('played', 1, 'width'); - if (!this.loop) { - this.ended = true; - this.pause(); - this.trigger('ended'); - } - }); - - // control volume - this.audio.volume = parseInt(this.element.getElementsByClassName('dplayer-volume-bar-inner')[0].style.width) / 100; - - // loop - this.audio.loop = this.loop; - - // set duration time - if (this.audio.duration !== 1) { // compatibility: Android browsers will output 1 at first - this.element.getElementsByClassName('dplayer-dtime')[0].innerHTML = this.audio.duration ? this.secondToTime(this.audio.duration) : '00:00'; - } - - - /** - * danmaku display - */ - const danContainer = this.element.getElementsByClassName('dplayer-danmaku')[0]; - const itemHeight = 30; - let danWidth; - let danHeight; - let itemY; - let danTunnel = { - right: {}, - top: {}, - bottom: {} - }; - - const danItemRight = (ele) => { - return danContainer.getBoundingClientRect().right - ele.getBoundingClientRect().right; - }; - - const danSpeed = (ele) => { - return (danWidth + ele.offsetWidth) / 5; - }; - - const getTunnel = (ele, type) => { - const tmp = danWidth / danSpeed(ele); - - for (let i = 0; ; i++) { - let item = danTunnel[type][i + '']; - if (item && item.length) { - for (let j = 0; j < item.length; j++) { - const danRight = danItemRight(item[j]) - 10; - if (danRight <= 640 - (tmp * danSpeed(item[j])) || danRight <= 0) { - break; - } - if (j === item.length - 1) { - danTunnel[type][i + ''].push(ele); - ele.addEventListener('animationend', () => { - danTunnel[type][i + ''].splice(0, 1); - }); - return i % itemY; - } - } - } - else { - danTunnel[type][i + ''] = [ele]; - ele.addEventListener('animationend', () => { - danTunnel[type][i + ''].splice(0, 1); - }); - return i % itemY; - } - } - }; - - this.danmakuIn = (text, color, type) => { - danWidth = danContainer.offsetWidth; - danHeight = danContainer.offsetHeight; - itemY = danHeight / itemHeight; - let item = document.createElement(`div`); - let content = document.createTextNode(text); - item.classList.add(`dplayer-danmaku-item`); - item.classList.add(`dplayer-danmaku-${type}`); - item.appendChild(content); - item.style.opacity = this.danOpacity; - - // insert - danContainer.appendChild(item); - - // adjust - item.style.color = color; - switch (type) { - case 'right': - item.style.top = itemHeight * getTunnel(item, type) + 'px'; - item.style.width = (item.offsetWidth + 1) + 'px'; - item.style.transform = `translateX(-${danWidth}px)`; - item.addEventListener('animationend', () => { - danContainer.removeChild(item); - }); - break; - case 'top': - item.style.top = itemHeight * getTunnel(item, type) + 'px'; - item.addEventListener('animationend', () => { - danContainer.removeChild(item); - }); - break; - case 'bottom': - item.style.bottom = itemHeight * getTunnel(item, type) + 'px'; - item.addEventListener('animationend', () => { - danContainer.removeChild(item); - }); - break; - default: - console.error(`Can't handled danmaku type: ${type}`); - } - - // move - item.classList.add(`dplayer-danmaku-move`); - }; - - // danmaku - if (this.option.danmaku) { - this.danIndex = 0; - const xhr = new XMLHttpRequest(); - xhr.onreadystatechange = () => { - if (xhr.readyState === 4) { - if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) { - this.dan = JSON.parse(xhr.responseText).danmaku.sort((a, b) => a.time - b.time); - console.log(this.dan); - - // autoplay - if (this.option.autoplay && !this.isMobile) { - this.play(); - } - else if (this.isMobile) { - this.pause(); - } - } - else { - console.log('Request was unsuccessful: ' + xhr.status); - } - } + this.updateBar('played', percentage, 'width'); + this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = this.secondToTime(percentage * this.audio.duration); }; - xhr.open('get', this.option.danmaku.api + '?id=' + this.option.danmaku.id, true); - xhr.send(null); - } - else { - // autoplay - if (this.option.autoplay && !this.isMobile) { - this.play(); - } - else if (this.isMobile) { - this.pause(); - } - } - - - /** - * comment - */ - const commentInput = this.element.getElementsByClassName('dplayer-comment-input')[0]; - const commentIcon = this.element.getElementsByClassName('dplayer-comment-icon')[0]; - const commentBox = this.element.getElementsByClassName('dplayer-comment-box')[0]; - const commentSettingIcon = this.element.getElementsByClassName('dplayer-comment-setting')[0]; - const commentSettingBox = this.element.getElementsByClassName('dplayer-comment-setting-box')[0]; - const commentSendIcon = this.element.getElementsByClassName('dplayer-send-icon')[0]; - - const closeCommentSetting = () => { - if (commentSettingBox.classList.contains('dplayer-comment-setting-open')) { - commentSettingBox.classList.remove('dplayer-comment-setting-open'); - } - }; - const toggleCommentSetting = () => { - if (commentSettingBox.classList.contains('dplayer-comment-setting-open')) { - commentSettingBox.classList.remove('dplayer-comment-setting-open'); - } - else { - commentSettingBox.classList.add('dplayer-comment-setting-open'); - } - }; - - let disableHide = 0; - const closeComment = () => { - if (commentBox.classList.contains('dplayer-comment-box-open')) { - commentBox.classList.remove('dplayer-comment-box-open'); - mask.classList.remove('dplayer-mask-show'); - clearInterval(disableHide); - this.element.classList.remove('dplayer-show-controller'); - closeCommentSetting(); - } - }; - const openComment = () => { - commentBox.classList.add('dplayer-comment-box-open'); - mask.classList.add('dplayer-mask-show'); - disableHide = setInterval(() => { - clearTimeout(hideTime); - }, 1000); - this.element.classList.add('dplayer-show-controller'); - }; - - mask.addEventListener('click', () => { - closeComment(); - }); - commentIcon.addEventListener('click', () => { - openComment(); - setTimeout(() => { - commentInput.focus(); - }, 300); - }); - commentSettingIcon.addEventListener('click', () => { - toggleCommentSetting(); - }); - - // comment setting box - this.element.getElementsByClassName('dplayer-comment-setting-color')[0].addEventListener('click', () => { - const sele = this.element.querySelector('input[name="dplayer-danmaku-color"]:checked+span'); - if (sele) { - commentSettingIcon.setAttribute('style', sele.getAttribute('style')); - } - }); - - commentInput.addEventListener('click', () => { - closeCommentSetting(); - }); - - commentSendIcon.addEventListener('click', () => { - // text can't be empty - if (!commentInput.value.replace(/^\s+|\s+$/g, '')) { - alert('要输入弹幕内容啊喂!'); - return; - } - - const danmakuData = { - player: this.option.danmaku.id, - time: this.audio.currentTime, - text: commentInput.value, - color: this.element.querySelector('input[name="dplayer-danmaku-color"]:checked').value, - type: this.element.querySelector('input[name="dplayer-danmaku-type"]:checked').value + + const thumbUp = () => { + document.removeEventListener('mouseup', thumbUp); + document.removeEventListener('mousemove', thumbMove); + this.audio.currentTime = parseFloat(this.playedBar.style.width) / 100 * this.audio.duration; + this.setTime(); }; - const xhr = new XMLHttpRequest(); - xhr.onreadystatechange = () => { - if (xhr.readyState === 4) { - if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) { - console.log(JSON.parse(xhr.responseText)); - } - else { - console.log('Request was unsuccessful: ' + xhr.status); - } - } - }; - xhr.open('post', this.option.danmaku.api, true); - xhr.send(JSON.stringify(danmakuData)); - - commentInput.value = ''; - closeComment(); - this.danmakuIn(danmakuData.text, danmakuData.color, danmakuData.type); - }); - - - /** - * full screen - */ - const resetAnimation = () => { - danWidth = danContainer.offsetWidth; - const items = this.element.getElementsByClassName('dplayer-danmaku-item'); - for (let i = 0; i < items.length; i++) { - items[i].style.transform = `translateX(-${danWidth}px)`; - } - }; - - this.element.addEventListener('fullscreenchange', () => { - resetAnimation(); - }); - this.element.addEventListener('mozfullscreenchange', () => { - resetAnimation(); - }); - this.element.addEventListener('webkitfullscreenchange', () => { - resetAnimation(); - }); - this.element.getElementsByClassName('dplayer-full-icon')[0].addEventListener('click', () => { - if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) { - if (this.element.requestFullscreen) { - this.element.requestFullscreen(); - } - else if (this.element.mozRequestFullScreen) { - this.element.mozRequestFullScreen(); - } - else if (this.element.webkitRequestFullscreen) { - this.element.webkitRequestFullscreen(); - } - } - else { - if (document.cancelFullScreen) { - document.cancelFullScreen(); - } - else if (document.mozCancelFullScreen) { - document.mozCancelFullScreen(); - } - else if (document.webkitCancelFullScreen) { - document.webkitCancelFullScreen(); - } - } - resetAnimation(); - }); - } - - /** - * Play music - */ - play() { - if (this.audio.paused) { - this.playButton.innerHTML = this.getSVG('pause'); - - this.audio.play(); - if (this.playedTime) { + + this.bar.addEventListener('mousedown', () => { + barWidth = this.bar.clientWidth; clearInterval(this.playedTime); - } - this.setTime(); - this.element.classList.add('dplayer-playing'); - this.trigger('play'); - } - } - - /** - * Pause music - */ - pause() { - if (!this.audio.paused || this.ended) { + document.addEventListener('mousemove', thumbMove); + document.addEventListener('mouseup', thumbUp); + }); + + + /** + * control volume + */ + this.volumeBar = this.element.getElementsByClassName('dplayer-volume-bar-inner')[0]; + const volumeEle = this.element.getElementsByClassName('dplayer-volume')[0]; + const volumeBarWrapWrap = this.element.getElementsByClassName('dplayer-volume-bar-wrap')[0]; + const volumeBarWrap = this.element.getElementsByClassName('dplayer-volume-bar')[0]; + const volumeicon = this.element.getElementsByClassName('dplayer-volume-icon')[0]; + const vWidth = 35; + const switchVolumeIcon = () => { + if (this.audio.volume >= 0.8) { + volumeicon.innerHTML = this.getSVG('volume-up'); + } + else if (this.audio.volume > 0) { + volumeicon.innerHTML = this.getSVG('volume-down'); + } + else { + volumeicon.innerHTML = this.getSVG('volume-off'); + } + }; + const volumeMove = (event) => { + const e = event || window.event; + let percentage = (e.clientX - getElementViewLeft(volumeBarWrap) - 5.5) / vWidth; + percentage = percentage > 0 ? percentage : 0; + percentage = percentage < 1 ? percentage : 1; + this.updateBar('volume', percentage, 'width'); + this.audio.volume = percentage; + if (this.audio.muted) { + this.audio.muted = false; + } + switchVolumeIcon(); + }; + const volumeUp = () => { + document.removeEventListener('mouseup', volumeUp); + document.removeEventListener('mousemove', volumeMove); + volumeEle.classList.remove('dplayer-volume-active'); + }; + + volumeBarWrapWrap.addEventListener('click', (event) => { + const e = event || window.event; + let percentage = (e.clientX - getElementViewLeft(volumeBarWrap) - 5.5) / vWidth; + percentage = percentage > 0 ? percentage : 0; + percentage = percentage < 1 ? percentage : 1; + this.updateBar('volume', percentage, 'width'); + this.audio.volume = percentage; + if (this.audio.muted) { + this.audio.muted = false; + } + switchVolumeIcon(); + }); + volumeBarWrapWrap.addEventListener('mousedown', () => { + document.addEventListener('mousemove', volumeMove); + document.addEventListener('mouseup', volumeUp); + volumeEle.classList.add('dplayer-volume-active'); + }); + volumeicon.addEventListener('click', () => { + if (this.audio.muted) { + this.audio.muted = false; + switchVolumeIcon(); + this.updateBar('volume', this.audio.volume, 'width'); + } + else { + this.audio.muted = true; + volumeicon.innerHTML = this.getSVG('volume-off'); + this.updateBar('volume', 0, 'width'); + } + }); + + + /** + * auto hide controller + */ + let hideTime = 0; + const hideController = () => { + this.element.classList.remove('dplayer-hide-controller'); + clearTimeout(hideTime); + hideTime = setTimeout(() => { + if (this.audio.played.length) { + this.element.classList.add('dplayer-hide-controller'); + closeSetting(); + closeComment(); + } + }, 2000); + }; + this.element.addEventListener('mousemove', hideController); + this.element.addEventListener('click', hideController); + + + /*** + * setting + */ + this.danOpacity = 0.7; + const settingHTML = { + 'original': ` +
+ 洗脑循环 +
+ + +
+
+
+ 速度 +
` + + this.getSVG('right') + + `
+
+
+ 弹幕透明度 +
+
+
+ +
+
+
+
`, + 'speed': ` +
+ 0.5 +
+
+ 0.75 +
+
+ 正常 +
+
+ 1.25 +
+
+ 1.5 +
+
+ 2 +
` + }; + + // toggle setting box + const settingIcon = this.element.getElementsByClassName('dplayer-setting-icon')[0]; + const settingBox = this.element.getElementsByClassName('dplayer-setting-box')[0]; + const mask = this.element.getElementsByClassName('dplayer-mask')[0]; + settingBox.innerHTML = settingHTML.original; + + const closeSetting = () => { + if (settingBox.classList.contains('dplayer-setting-box-open')) { + settingBox.classList.remove('dplayer-setting-box-open'); + mask.classList.remove('dplayer-mask-show'); + setTimeout(() => { + settingBox.classList.remove('dplayer-setting-box-narrow'); + settingBox.innerHTML = settingHTML.original; + settingEvent(); + }, 300); + } + }; + const openSetting = () => { + settingBox.classList.add('dplayer-setting-box-open'); + mask.classList.add('dplayer-mask-show'); + }; + + mask.addEventListener('click', () => { + closeSetting(); + }); + settingIcon.addEventListener('click', () => { + openSetting(); + }); + + const settingEvent = () => { + // loop control + const loopEle = this.element.getElementsByClassName('dplayer-setting-loop')[0]; + const loopToggle = loopEle.getElementsByClassName('dplayer-toggle-setting-input')[0]; + + loopToggle.checked = this.loop; + + loopEle.addEventListener('click', () => { + loopToggle.checked = !loopToggle.checked; + if (loopToggle.checked) { + this.loop = true; + this.audio.loop = this.loop; + } + else { + this.loop = false; + this.audio.loop = this.loop; + } + closeSetting(); + }); + loopToggle.addEventListener('change', () => { + if (loopToggle.checked) { + this.loop = true; + this.audio.loop = this.loop; + } + else { + this.loop = false; + this.audio.loop = this.loop; + } + closeSetting(); + }); + + // speed control + const speedEle = this.element.getElementsByClassName('dplayer-setting-speed')[0]; + speedEle.addEventListener('click', () => { + settingBox.classList.add('dplayer-setting-box-narrow'); + settingBox.innerHTML = settingHTML.speed; + + const speedItem = settingBox.getElementsByClassName('dplayer-setting-speed-item'); + for (let i = 0; i < speedItem.length; i++) { + speedItem[i].addEventListener('click', () => { + this.audio.playbackRate = speedItem[i].dataset.speed; + closeSetting(); + }); + } + }); + + // danmaku opacity + this.danmakuBar = this.element.getElementsByClassName('dplayer-danmaku-bar-inner')[0]; + const danmakuBarWrapWrap = this.element.getElementsByClassName('dplayer-danmaku-bar-wrap')[0]; + const danmakuBarWrap = this.element.getElementsByClassName('dplayer-danmaku-bar')[0]; + const danmakuSettingBox = this.element.getElementsByClassName('dplayer-setting-danmaku')[0]; + const dWidth = 130; + this.updateBar('danmaku', this.danOpacity, 'width'); + + const danmakuMove = (event) => { + const e = event || window.event; + let percentage = (e.clientX - getElementViewLeft(danmakuBarWrap)) / dWidth; + percentage = percentage > 0 ? percentage : 0; + percentage = percentage < 1 ? percentage : 1; + this.updateBar('danmaku', percentage, 'width'); + const items = this.element.getElementsByClassName('dplayer-danmaku-item'); + for (let i = 0; i < items.length; i++) { + items[i].style.opacity = percentage; + } + this.danOpacity = percentage; + }; + const danmakuUp = () => { + document.removeEventListener('mouseup', danmakuUp); + document.removeEventListener('mousemove', danmakuMove); + danmakuSettingBox.classList.remove('dplayer-setting-danmaku-active'); + }; + + danmakuBarWrapWrap.addEventListener('click', (event) => { + const e = event || window.event; + let percentage = (e.clientX - getElementViewLeft(danmakuBarWrap)) / dWidth; + percentage = percentage > 0 ? percentage : 0; + percentage = percentage < 1 ? percentage : 1; + this.updateBar('danmaku', percentage, 'width'); + const items = this.element.getElementsByClassName('dplayer-danmaku-item'); + for (let i = 0; i < items.length; i++) { + items[i].style.opacity = percentage; + } + this.danOpacity = percentage; + }); + danmakuBarWrapWrap.addEventListener('mousedown', () => { + document.addEventListener('mousemove', danmakuMove); + document.addEventListener('mouseup', danmakuUp); + danmakuSettingBox.classList.add('dplayer-setting-danmaku-active'); + }); + }; + settingEvent(); + + + /** + * audio events + */ + // show audio time: the metadata has loaded or changed + this.audio.addEventListener('durationchange', () => { + if (this.audio.duration !== 1) { // compatibility: Android browsers will output 1 at first + this.element.getElementsByClassName('dplayer-dtime')[0].innerHTML = this.secondToTime(this.audio.duration); + } + }); + + // show audio loaded bar: to inform interested parties of progress downloading the media + this.audio.addEventListener('progress', () => { + const percentage = this.audio.buffered.length ? this.audio.buffered.end(this.audio.buffered.length - 1) / this.audio.duration : 0; + this.updateBar('loaded', percentage, 'width'); + }); + + // audio download error: an error occurs + this.audio.addEventListener('error', () => { + this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = `Error happens ╥﹏╥`; + this.trigger('pause'); + }); + + // audio can play: enough data is available that the media can be played + this.audio.addEventListener('canplay', () => { + this.trigger('canplay'); + }); + + // music end this.ended = false; - this.playButton.innerHTML = this.getSVG('play'); - this.audio.pause(); - clearInterval(this.playedTime); - this.element.classList.remove('dplayer-playing'); - this.trigger('pause'); + this.audio.addEventListener('ended', () => { + this.updateBar('played', 1, 'width'); + if (!this.loop) { + this.ended = true; + this.pause(); + this.trigger('ended'); + } + }); + + // control volume + this.audio.volume = parseInt(this.element.getElementsByClassName('dplayer-volume-bar-inner')[0].style.width) / 100; + + // loop + this.audio.loop = this.loop; + + // set duration time + if (this.audio.duration !== 1) { // compatibility: Android browsers will output 1 at first + this.element.getElementsByClassName('dplayer-dtime')[0].innerHTML = this.audio.duration ? this.secondToTime(this.audio.duration) : '00:00'; + } + + + /** + * danmaku display + */ + const danContainer = this.element.getElementsByClassName('dplayer-danmaku')[0]; + const itemHeight = 30; + let danWidth; + let danHeight; + let itemY; + let danTunnel = { + right: {}, + top: {}, + bottom: {} + }; + + const danItemRight = (ele) => { + return danContainer.getBoundingClientRect().right - ele.getBoundingClientRect().right; + }; + + const danSpeed = (ele) => { + return (danWidth + ele.offsetWidth) / 5; + }; + + const getTunnel = (ele, type) => { + const tmp = danWidth / danSpeed(ele); + + for (let i = 0; ; i++) { + let item = danTunnel[type][i + '']; + if (item && item.length) { + for (let j = 0; j < item.length; j++) { + const danRight = danItemRight(item[j]) - 10; + if (danRight <= 640 - (tmp * danSpeed(item[j])) || danRight <= 0) { + break; + } + if (j === item.length - 1) { + danTunnel[type][i + ''].push(ele); + ele.addEventListener('animationend', () => { + danTunnel[type][i + ''].splice(0, 1); + }); + return i % itemY; + } + } + } + else { + danTunnel[type][i + ''] = [ele]; + ele.addEventListener('animationend', () => { + danTunnel[type][i + ''].splice(0, 1); + }); + return i % itemY; + } + } + }; + + this.danmakuIn = (text, color, type) => { + danWidth = danContainer.offsetWidth; + danHeight = danContainer.offsetHeight; + itemY = danHeight / itemHeight; + let item = document.createElement(`div`); + let content = document.createTextNode(text); + item.classList.add(`dplayer-danmaku-item`); + item.classList.add(`dplayer-danmaku-${type}`); + item.appendChild(content); + item.style.opacity = this.danOpacity; + + // insert + danContainer.appendChild(item); + + // adjust + item.style.color = color; + switch (type) { + case 'right': + item.style.top = itemHeight * getTunnel(item, type) + 'px'; + item.style.width = (item.offsetWidth + 1) + 'px'; + item.style.transform = `translateX(-${danWidth}px)`; + item.addEventListener('animationend', () => { + danContainer.removeChild(item); + }); + break; + case 'top': + item.style.top = itemHeight * getTunnel(item, type) + 'px'; + item.addEventListener('animationend', () => { + danContainer.removeChild(item); + }); + break; + case 'bottom': + item.style.bottom = itemHeight * getTunnel(item, type) + 'px'; + item.addEventListener('animationend', () => { + danContainer.removeChild(item); + }); + break; + default: + console.error(`Can't handled danmaku type: ${type}`); + } + + // move + item.classList.add(`dplayer-danmaku-move`); + }; + + // danmaku + if (this.option.danmaku) { + this.danIndex = 0; + const xhr = new XMLHttpRequest(); + xhr.onreadystatechange = () => { + if (xhr.readyState === 4) { + if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) { + this.dan = JSON.parse(xhr.responseText).danmaku.sort((a, b) => a.time - b.time); + console.log(this.dan); + + // autoplay + if (this.option.autoplay && !this.isMobile) { + this.play(); + } + else if (this.isMobile) { + this.pause(); + } + } + else { + console.log('Request was unsuccessful: ' + xhr.status); + } + } + }; + xhr.open('get', this.option.danmaku.api + '?id=' + this.option.danmaku.id, true); + xhr.send(null); + } + else { + // autoplay + if (this.option.autoplay && !this.isMobile) { + this.play(); + } + else if (this.isMobile) { + this.pause(); + } + } + + + /** + * comment + */ + const commentInput = this.element.getElementsByClassName('dplayer-comment-input')[0]; + const commentIcon = this.element.getElementsByClassName('dplayer-comment-icon')[0]; + const commentBox = this.element.getElementsByClassName('dplayer-comment-box')[0]; + const commentSettingIcon = this.element.getElementsByClassName('dplayer-comment-setting')[0]; + const commentSettingBox = this.element.getElementsByClassName('dplayer-comment-setting-box')[0]; + const commentSendIcon = this.element.getElementsByClassName('dplayer-send-icon')[0]; + + const sendComment = () => { + // text can't be empty + if (!commentInput.value.replace(/^\s+|\s+$/g, '')) { + alert('要输入弹幕内容啊喂!'); + return; + } + + const danmakuData = { + token: this.option.danmaku.token, + player: this.option.danmaku.id, + author: 'DIYgod', + time: this.audio.currentTime, + text: commentInput.value, + color: this.element.querySelector('input[name="dplayer-danmaku-color"]:checked').value, + type: this.element.querySelector('input[name="dplayer-danmaku-type"]:checked').value + }; + const xhr = new XMLHttpRequest(); + xhr.onreadystatechange = () => { + if (xhr.readyState === 4) { + if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) { + console.log(JSON.parse(xhr.responseText)); + } + else { + console.log('Request was unsuccessful: ' + xhr.status); + } + } + }; + xhr.open('post', this.option.danmaku.api, true); + xhr.send(JSON.stringify(danmakuData)); + + commentInput.value = ''; + closeComment(); + this.danmakuIn(danmakuData.text, danmakuData.color, danmakuData.type); + }; + + const closeCommentSetting = () => { + if (commentSettingBox.classList.contains('dplayer-comment-setting-open')) { + commentSettingBox.classList.remove('dplayer-comment-setting-open'); + } + }; + const toggleCommentSetting = () => { + if (commentSettingBox.classList.contains('dplayer-comment-setting-open')) { + commentSettingBox.classList.remove('dplayer-comment-setting-open'); + } + else { + commentSettingBox.classList.add('dplayer-comment-setting-open'); + } + }; + + let disableHide = 0; + const closeComment = () => { + if (commentBox.classList.contains('dplayer-comment-box-open')) { + commentBox.classList.remove('dplayer-comment-box-open'); + mask.classList.remove('dplayer-mask-show'); + clearInterval(disableHide); + this.element.classList.remove('dplayer-show-controller'); + closeCommentSetting(); + } + }; + const openComment = () => { + commentBox.classList.add('dplayer-comment-box-open'); + mask.classList.add('dplayer-mask-show'); + disableHide = setInterval(() => { + clearTimeout(hideTime); + }, 1000); + this.element.classList.add('dplayer-show-controller'); + }; + + mask.addEventListener('click', () => { + closeComment(); + }); + commentIcon.addEventListener('click', () => { + openComment(); + setTimeout(() => { + commentInput.focus(); + }, 300); + }); + commentSettingIcon.addEventListener('click', () => { + toggleCommentSetting(); + }); + + // comment setting box + this.element.getElementsByClassName('dplayer-comment-setting-color')[0].addEventListener('click', () => { + const sele = this.element.querySelector('input[name="dplayer-danmaku-color"]:checked+span'); + if (sele) { + commentSettingIcon.setAttribute('style', sele.getAttribute('style')); + } + }); + + commentInput.addEventListener('click', () => { + closeCommentSetting(); + }); + commentInput.addEventListener('keydown', (e) => { + const event = e || window.event; + if (event.keyCode === 13) { + sendComment(); + } + }); + + commentSendIcon.addEventListener('click', sendComment); + + + /** + * full screen + */ + const resetAnimation = () => { + danWidth = danContainer.offsetWidth; + const items = this.element.getElementsByClassName('dplayer-danmaku-item'); + for (let i = 0; i < items.length; i++) { + items[i].style.transform = `translateX(-${danWidth}px)`; + } + }; + + this.element.addEventListener('fullscreenchange', () => { + resetAnimation(); + }); + this.element.addEventListener('mozfullscreenchange', () => { + resetAnimation(); + }); + this.element.addEventListener('webkitfullscreenchange', () => { + resetAnimation(); + }); + this.element.getElementsByClassName('dplayer-full-icon')[0].addEventListener('click', () => { + if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) { + if (this.element.requestFullscreen) { + this.element.requestFullscreen(); + } + else if (this.element.mozRequestFullScreen) { + this.element.mozRequestFullScreen(); + } + else if (this.element.webkitRequestFullscreen) { + this.element.webkitRequestFullscreen(); + } + } + else { + if (document.cancelFullScreen) { + document.cancelFullScreen(); + } + else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } + else if (document.webkitCancelFullScreen) { + document.webkitCancelFullScreen(); + } + } + resetAnimation(); + }); + } + + /** + * Play music + */ + play() { + if (this.audio.paused) { + this.playButton.innerHTML = this.getSVG('pause'); + + this.audio.play(); + if (this.playedTime) { + clearInterval(this.playedTime); + } + this.setTime(); + this.element.classList.add('dplayer-playing'); + this.trigger('play'); + } + } + + /** + * Pause music + */ + pause() { + if (!this.audio.paused || this.ended) { + this.ended = false; + this.playButton.innerHTML = this.getSVG('play'); + this.audio.pause(); + clearInterval(this.playedTime); + this.element.classList.remove('dplayer-playing'); + this.trigger('pause'); + } + } + + /** + * attach event + */ + on(name, func) { + if (typeof func === 'function') { + this.event[name].push(func); + } } } - /** - * attach event - */ - on(name, func) { - if (typeof func === 'function') { - this.event[name].push(func); - } + if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { + module.exports = DPlayer; + } + else { + window.DPlayer = DPlayer; } -} - -if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { - module.exports = DPlayer -} -else { - window.DPlayer = DPlayer; -} })(); \ No newline at end of file