Files
hexo-theme-shoka/source/js/_app/dom.js
amehime a296c2f795 update
2020-10-04 23:26:20 +08:00

559 lines
16 KiB
JavaScript

const $ = function(selector, element) {
element = element || document;
if(selector.indexOf('#') === 0) {
return element.getElementById(selector.replace('#', ''))
}
return element.querySelector(selector)
};
$.all = function(selector, element) {
element = element || document;
return element.querySelectorAll(selector)
};
$.each = function(selector, callback, element) {
return $.all(selector, element).forEach(callback)
}
Object.assign(HTMLElement.prototype, {
createChild: function(tag, obj) {
var child = document.createElement(tag);
Object.assign(child, obj)
this.appendChild(child)
return child
},
wrap: function (obj) {
var box = document.createElement('div');
Object.assign(box, obj)
this.parentNode.insertBefore(box, this);
this.parentNode.removeChild(this);
box.appendChild(this);
},
height: function(h) {
if(h) {
this.style.height = typeof h == 'number' ? h + 'rem' : h;
}
return this.getBoundingClientRect().height
},
width: function(w) {
if(w) {
this.style.width = typeof w == 'number' ? w + 'rem' : w;
}
return this.getBoundingClientRect().width
},
top: function() {
return this.getBoundingClientRect().top
},
left:function() {
return this.getBoundingClientRect().left
},
attr: function(type, value) {
if(value === null) {
return this.removeAttribute(type)
}
if(value) {
return this.setAttribute(type, value)
} else {
return this.getAttribute(type)
}
},
insertAfter: function(element) {
var parent = this.parentNode;
if(parent.lastChild == this){
parent.appendChild(element);
}else{
parent.insertBefore(element, this.nextSibling);
}
},
display: function(d) {
if(d == null) {
return this.style.display
} else {
this.style.display = d;
return this
}
},
child: function(selector) {
return $(selector, this)
},
find: function(selector) {
return $.all(selector, this)
},
_class: function(type, className, display) {
var classNames = className.indexOf(' ') ? className.split(' ') : [className];
var that = this;
classNames.forEach(function(name) {
if(type == 'toggle') {
that.classList.toggle(name, display)
} else {
that.classList[type](name)
}
})
},
addClass: function(className) {
this._class('add', className);
return this;
},
removeClass: function(className) {
this._class('remove', className);
return this;
},
toggleClass: function(className, display) {
this._class('toggle', className, display);
return this;
},
hasClass: function(className) {
return this.classList.contains(className)
}
});
const store = {
get: function(item) {
return localStorage.getItem(item);
},
set: function(item, str) {
localStorage.setItem(item, str);
return str;
},
del: function(item) {
localStorage.removeItem(item);
}
}
const mediaPlayer = function(config) {
var t = this,
option = {
type: 'audio',
mode: 'random',
btns: ['play-pause', 'music'],
events: {
"play-pause": function(event) {
if(t.media.source.paused) {
t.media.play()
} else {
t.media.pause()
}
},
"music": function(event) {
if(t.media.list.hasClass('show')) {
t.media.hideList()
} else {
t.media.list.addClass('show');
t.media.scroll();
t.media.changeTitle();
}
}
}
};
var
utils = {
random: function(len) {
return Math.floor((Math.random()*len))
},
parse: function(link) {
var result = [];
[
['music.163.com.*song.*id=(\\d+)', 'netease', 'song'],
['music.163.com.*album.*id=(\\d+)', 'netease', 'album'],
['music.163.com.*artist.*id=(\\d+)', 'netease', 'artist'],
['music.163.com.*playlist.*id=(\\d+)', 'netease', 'playlist'],
['music.163.com.*discover/toplist.*id=(\\d+)', 'netease', 'playlist'],
['y.qq.com.*song/(\\w+).html', 'tencent', 'song'],
['y.qq.com.*album/(\\w+).html', 'tencent', 'album'],
['y.qq.com.*singer/(\\w+).html', 'tencent', 'artist'],
['y.qq.com.*playsquare/(\\w+).html', 'tencent', 'playlist'],
['y.qq.com.*playlist/(\\w+).html', 'tencent', 'playlist'],
['xiami.com.*song/(\\w+)', 'xiami', 'song'],
['xiami.com.*album/(\\w+)', 'xiami', 'album'],
['xiami.com.*artist/(\\w+)', 'xiami', 'artist'],
['xiami.com.*collect/(\\w+)', 'xiami', 'playlist'],
].forEach(function(rule) {
var patt = new RegExp(rule[0])
var res = patt.exec(link)
if (res !== null) {
result = [rule[1], rule[2], res[1]]
}
})
return result
},
fetch: function(source, callback) {
var list = []
return new Promise(function(resolve, reject){
source.forEach(function(raw) {
var meta = utils.parse(raw)
var skey = JSON.stringify(meta)
var playlist = store.get(skey)
if(playlist) {
list.push.apply(list, JSON.parse(playlist));
resolve(list);
} else {
fetch('https://api.i-meto.com/meting/api?server='+meta[0]+'&type='+meta[1]+'&id='+meta[2]+'&r='+ Math.random())
.then(function(response) {
return response.json()
}).then(function(json) {
store.set(skey, JSON.stringify(json))
list.push.apply(list, json);
resolve(list);
}).catch(function(ex) {})
}
})
})
},
lrc: function(lrc_s) {
if (lrc_s) {
lrc_s = lrc_s.replace(/([^\]^\n])\[/g, function(match, p1){return p1 + '\n['});
const lyric = lrc_s.split('\n');
var lrc = [];
const lyricLen = lyric.length;
for (var i = 0; i < lyricLen; i++) {
// match lrc time
const lrcTimes = lyric[i].match(/\[(\d{2}):(\d{2})(\.(\d{2,3}))?]/g);
// match lrc text
const lrcText = lyric[i]
.replace(/.*\[(\d{2}):(\d{2})(\.(\d{2,3}))?]/g, '')
.replace(/<(\d{2}):(\d{2})(\.(\d{2,3}))?>/g, '')
.replace(/^\s+|\s+$/g, '');
if (lrcTimes) {
// handle multiple time tag
const timeLen = lrcTimes.length;
for (var j = 0; j < timeLen; j++) {
const oneTime = /\[(\d{2}):(\d{2})(\.(\d{2,3}))?]/.exec(lrcTimes[j]);
const min2sec = oneTime[1] * 60;
const sec2sec = parseInt(oneTime[2]);
const msec2sec = oneTime[4] ? parseInt(oneTime[4]) / ((oneTime[4] + '').length === 2 ? 100 : 1000) : 0;
const lrcTime = min2sec + sec2sec + msec2sec;
lrc.push([lrcTime, lrcText]);
}
}
}
// sort by time
lrc = lrc.filter(function(item){return item[1]});
lrc.sort(function(a, b){return a[0] - b[0]});
return lrc;
} else {
return [];
}
}
}
t.media = {
pointer: -1,
loaded: false,
source: null,
buttons: {},
playlist: [],
lrc: {},
fetch: function (callback) {
var that = this;
callback = callback || function() {}
if(!this.loaded) {
if(this.options.rawList)
utils.fetch(this.options.rawList).then(function(list) {
that.playlist = list;
create.list();
that.setMode(t.media.options.mode);
that.loaded = true;
callback();
});
} else {
callback()
}
},
load: function(newList) {
var d = "block"
if(newList && newList.length > 0) {
if(this.options.rawList !== newList) {
this.options.rawList = newList;
if(this.loaded) {
this.loaded = false;
this.fetch();
}
}
} else {
d = "none"
this.pause()
}
for(var el in this.buttons) {
this.buttons[el].display(d)
}
},
// 根据模式切换当前曲目pointer
setMode: function(mode) {
var total = this.playlist.length;
if(!total)
return;
var next = function(pointer) {
if((pointer + 1) == total) {
pointer = -1;
}
t.media.pointer = ++pointer;
}
switch (mode) {
case 'random':
var p = utils.random(total)
if(this.pointer !== p) {
this.pointer = p
} else {
next(this.pointer)
}
break;
case 'next':
next(this.pointer)
break;
}
this.setSource()
},
// 直接设置当前曲目pointer
setCurrent: function(pointer) {
if(typeof pointer == 'number' && pointer != this.pointer && this.playlist[pointer] && !this.playlist[pointer]['error']) {
this.pointer = pointer;
this.setSource()
}
},
// 更新source为当前曲目pointer
setSource: function() {
var item = this.playlist[this.pointer]
if(item['error']) {
this.setMode('next');
return;
}
var playing = false;
if(!this.source.paused) {
playing = true
this.stop()
}
this.source.attr('src', item.url);
this.source.attr('title', item.title + ' - ' + item.author);
create.progress()
create.preview()
if(playing == true) {
this.play()
}
},
play: function() {
if(this.playlist[this.pointer]['error']) {
this.setMode('next');
return;
}
var that = this
this.source.play().then(function() {
that.changeTitle()
}).catch(function(e) {});
},
pause: function() {
this.source.pause()
document.title = originTitle
},
stop: function() {
this.source.pause();
this.source.currentTime = 0;
document.title = originTitle;
},
scroll: function() {
var current = this.list.find('li')[this.pointer];
pageScroll(current, current.offsetTop)
},
scrollLrc: function(currentTime) {
var that = this
if(!this.lrc.data)
return
if (this.lrc.index > this.lrc.data.length - 1 || currentTime < this.lrc.data[this.lrc.index][0] || (!this.lrc.data[this.lrc.index + 1] || currentTime >= this.lrc.data[this.lrc.index + 1][0])) {
for (var i = 0; i < this.lrc.data.length; i++) {
if (currentTime >= this.lrc.data[i][0] && (!this.lrc.data[i + 1] || currentTime < this.lrc.data[i + 1][0])) {
that.lrc.index = i;
var y = -(that.lrc.index-1);
that.lrc.el.style.transform = 'translateY('+y+'rem)';
that.lrc.el.style.webkitTransform = 'translateY('+y+'rem)';
that.lrc.el.getElementsByClassName('current')[0].removeClass('current');
that.lrc.el.getElementsByTagName('p')[i].addClass('current');
}
}
}
},
hideList: function() {
var el = this.list
el.addClass('hide');
window.setTimeout(function() {
el.removeClass('show hide')
}, 300);
},
changeTitle: function() {
if(!this.source.paused)
document.title = 'Now Playing...' + this.playlist[this.pointer]['title'] + ' - ' + this.playlist[this.pointer]['author'] + ' | ' + originTitle;
}
};
var create = {
button: function(b) {
if(!t.media.buttons[b]) {
var el = document.createElement('div');
el.addClass(b + ' btn');
el.addEventListener('click', function(){
t.media.fetch(t.media.options.events[b])
});
t.appendChild(el);
t.media.buttons[b] = el;
}
},
audio: function() {
if(!t.media.source) {
var el = document.createElement('audio');
el.addEventListener('error', function() {
t.media.list.find('li')[t.media.pointer].addClass('error')
t.media.playlist[t.media.pointer]['error'] = true
t.media.setMode('next');
});
el.addEventListener('play', function() {
t.addClass('playing');
t.media.list.addClass('playing');
showtip(el.attr('title'))
});
el.addEventListener('pause', function() {
t.removeClass('playing');
t.media.list.removeClass('playing');
});
el.addEventListener('timeupdate', function() {
var percent = Math.floor((el.currentTime / el.duration * 100));
t.media.progress.width(percent + '%');
if(t.media.lrc) {
t.media.scrollLrc(el.currentTime)
}
if (percent == 100) { // 下一曲
t.media.setMode('next');
t.media.play();
}
});
t.appendChild(el);
t.media.source = el;
}
},
info: function() {
if(!t.media.list) {
var el = document.createElement('div');
el.addClass('play-list');
el.innerHTML = '<div class="preview"></div><ol></ol>';
t.media.list = el;
t.insertAfter(el);
$('#main').addEventListener('click', function() {
t.media.hideList()
})
}
},
list: function() {
var list = t.media.list.child("ol");
list.innerHTML = "";
t.media.playlist.forEach(function(item, index) {
var el = document.createElement('li');
el.innerHTML = '<span class="info"><span>'+item.title+'</span><span>'+item.author+'</span></span>';
el.title = item.title + ' - ' + item.author
el.addEventListener('click', function(event) {
var current = event.currentTarget;
if(t.media.pointer === index && t.media.progress) {
if(t.media.source.paused) {
t.media.play();
} else {
t.media.source.currentTime = t.media.source.duration * Math.floor((event.clientX - current.left()))/current.width();
}
return;
}
t.media.setCurrent(index);
t.media.play();
});
list.appendChild(el);
})
},
progress: function() {
if(t.media.progress) {
t.media.progress.parentNode.removeClass('current');
t.media.progress.remove();
}
var current = t.media.list.find('li')[t.media.pointer];
if(current) {
var progress = document.createElement('div');
progress.addClass('progress')
current.appendChild(progress);
t.media.progress = progress;
current.addClass('current');
t.media.scroll()
}
},
preview: function() {
var preview = t.media.list.child('.preview')
var current = t.media.playlist[t.media.pointer]
preview.innerHTML = '<div class="cover"><div class="disc"><img src="'+(current.pic)+'" class="blur" /></div></div>'
+ '<div class="info"><h4 class="title">'+current.title+'</h4><span>'+current.author+'</span><div class="lrc"></div></div>'
var lrc = '';
fetch(current.lrc)
.then(function(response) {
return response.text()
}).then(function(body) {
if(current !== t.media.playlist[t.media.pointer])
return;
t.media.lrc.data = utils.lrc(body)
var result = ''
t.media.lrc.data.forEach(function(line, index) {
lrc += '<p'+(index===0?' class="current"':'')+'>'+line[1]+'</p>';
})
var el = document.createElement('div');
el.addClass('inner');
el.innerHTML = lrc;
preview.child('.lrc').innerHTML = '';
preview.child('.lrc').appendChild(el);
t.media.lrc.el = el;
t.media.lrc.index = 0;
}).catch(function(ex) {})
preview.child('.cover').addEventListener('click', t.media.options.events['play-pause'])
}
},
init = function(config) {
if(t.media.created)
return;
t.media.options = Object.assign(option, config);
// 初始化button以及click事件
t.media.options.btns.forEach(create.button);
// 初始化audio
create[t.media.options.type]();
// 初始化播放列表等
create.info();
t.media.created = true;
}
init(config);
}
Object.assign(HTMLElement.prototype, {
player: mediaPlayer
})