misuzu/assets/js/misuzu/_main.js
flash d5bb0bb475 Complete revamp of the forum video bbcode.
Rather than blindly embedding everything, video metadata is first requested through the URL metadata lookup service.
This slightly protects you from automatically connecting to third party servers and also vastly improves page loading performance in tandem with caching on the server.
A similar implementation will eventually make its way to the audio bbcode and will also be worked in the img bbcode somehow.
This will then eventually make it possible to embed audio and video in markdown the same way you'd embed an image.
2023-01-25 22:33:59 +00:00

388 lines
17 KiB
JavaScript

var Misuzu = function() {
const UIHARU_API = location.protocol + '//uiharu.' + location.host;
timeago.render($qa('time'));
hljs.initHighlighting();
Misuzu.initQuickSubmit(); // only used by the forum posting form
Misuzu.Forum.Editor.init();
Misuzu.Events.dispatch();
Misuzu.initLoginPage();
const embeds = Array.from($qa('.js-msz-embed-media'));
if(embeds.length > 0) {
$as(embeds);
const uiharu = new Uiharu(UIHARU_API)
elems = new Map(embeds.map(function(elem) { return [elem.dataset.mszEmbedUrl, elem]; }));
uiharu.lookupMany(Array.from(elems.keys()), function(resp) {
if(resp.results === undefined)
return; // rip
for(const result of resp.results) {
if(result.error) {
console.error(result.error);
continue;
}
if(result.info.title === undefined) {
console.warn('Media is no longer available.');
continue;
}
let elem = elems.get(result.url);
(function(elem, info) {
const replaceElement = function(body) {
$ib(elem, body);
$r(elem);
elem = body;
};
const createEmbedPH = function(type, info, onclick, width, height) {
let infoChildren = [];
infoChildren.push({
tag: 'h1',
attrs: {
className: 'embedph-info-title',
},
child: info.title,
});
if(info.description) {
let firstLine = info.description.split("\n")[0].trim();
if(firstLine.length > 300)
firstLine = firstLine.substring(0, 300).trim() + '...';
infoChildren.push({
tag: 'div',
attrs: {
className: 'embedph-info-desc',
},
child: firstLine,
});
}
infoChildren.push({
tag: 'div',
attrs: {
className: 'embedph-info-site',
},
child: info.site_name,
});
let style = info.color === undefined ? '' : ('--embedph-colour: ' + info.color);
if(width !== undefined)
style += 'width: ' + width.toString() + ';';
if(height !== undefined)
style += 'height: ' + height.toString() + ';';
let bgElem;
if(info.image !== undefined) {
bgElem = {
tag: 'img',
attrs: {
src: info.image,
},
};
} else {
bgElem = {};
}
return $e({
attrs: {
href: 'javascript:void(0);',
className: ('embedph embedph-' + type),
style: style,
},
child: [
{
attrs: {
className: 'embedph-bg',
},
child: bgElem,
},
{
attrs: {
className: 'embedph-fg',
},
child: [
{
attrs: {
className: 'embedph-info',
},
child: {
attrs: {
className: 'embedph-info-wrap',
},
child: [
{
attrs: {
className: 'embedph-info-bar',
},
},
{
attrs: {
className: 'embedph-info-body',
},
child: infoChildren,
}
],
},
},
{
attrs: {
className: 'embedph-play',
onclick: function(ev) {
if(ev.target.tagName.toLowerCase() !== 'a')
onclick(ev);
},
},
child: [
{
attrs: {
className: 'embedph-play-internal',
},
child: {
tag: 'i',
attrs: {
className: 'fas fa-play fa-4x fa-fw',
},
},
},
{
tag: 'a',
attrs: {
className: 'embedph-play-external',
href: info.url,
target: '_blank',
rel: 'noopener',
},
child: ('or watch on ' + info.site_name + '?'),
}
],
}
],
},
],
});
};
if(info.type === 'youtube:video') {
let embedUrl = 'https://www.youtube.com/embed/' + info.youtube_video_id + '?rel=0&autoplay=1';
if(info.youtube_start_time)
embedUrl += '&t=' + encodeURIComponent(info.youtube_start_time);
if(info.youtube_playlist) {
embedUrl += '&list=' + encodeURIComponent(info.youtube_playlist);
if(info.youtube_playlist_index)
embedUrl += '&index=' + encodeURIComponent(info.youtube_playlist_index);
}
replaceElement(createEmbedPH('youtube', info, function() {
replaceElement($e({
attrs: {
className: 'embed embed-youtube',
},
child: {
tag: 'iframe',
attrs: {
frameborder: 0,
allow: 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture',
allowfullscreen: 'allowfullscreen',
src: embedUrl,
},
},
}));
}));
} else if(info.type === 'niconico:video') {
let embedUrl = 'https://embed.nicovideo.jp/watch/' + info.nicovideo_video_id + '/script?w=100%25&h=100%25&autoplay=1';
if(info.nicovideo_start_time)
embedUrl += '&from=' + encodeURIComponent(info.nicovideo_start_time);
replaceElement(createEmbedPH('nicovideo', info, function() {
replaceElement($e({
attrs: {
className: 'embed embed-nicovideo',
},
child: {
tag: 'script',
attrs: {
async: 'async',
src: embedUrl,
},
},
}));
}));
} else if(info.type === 'media') {
if(info.is_video) {
let width = info.width,
height = info.height;
const gcd = function(a, b) {
return (b == 0) ? a : gcd(b, a % b);
};
let ratio = gcd(width, height),
widthRatio = width / ratio,
heightRatio = height / ratio;
if(width > height) {
width = Math.min(640, width);
height = Math.ceil((width / widthRatio) * heightRatio).toString() + 'px';
width = width.toString() + 'px';
} else {
height = Math.min(360, height);
width = Math.ceil((height / heightRatio) * widthRatio).toString() + 'px';
height = height.toString() + 'px';
}
replaceElement(createEmbedPH('external', info, function() {
replaceElement($e({
attrs: {
className: 'embed embed-external',
},
child: {
tag: 'video',
attrs: {
autoplay: 'autoplay',
controls: 'controls',
src: info.url,
style: {
width: width,
height: height,
},
},
},
}));
}, width, height));
} else if(info.is_audio) {
// coming someday
}
}
})(elem, result.info);
}
});
}
};
Misuzu.showMessageBox = function(text, title, buttons) {
if($q('.messagebox'))
return false;
text = text || '';
title = title || '';
buttons = buttons || [];
var element = document.createElement('div');
element.className = 'messagebox';
var container = element.appendChild(document.createElement('div'));
container.className = 'container messagebox__container';
var titleElement = container.appendChild(document.createElement('div')),
titleBackground = titleElement.appendChild(document.createElement('div')),
titleText = titleElement.appendChild(document.createElement('div'));
titleElement.className = 'container__title';
titleBackground.className = 'container__title__background';
titleText.className = 'container__title__text';
titleText.textContent = title || 'Information';
var textElement = container.appendChild(document.createElement('div'));
textElement.className = 'container__content';
textElement.textContent = text;
var buttonsContainer = container.appendChild(document.createElement('div'));
buttonsContainer.className = 'messagebox__buttons';
var firstButton = null;
if(buttons.length < 1) {
firstButton = buttonsContainer.appendChild(document.createElement('button'));
firstButton.className = 'input__button';
firstButton.textContent = 'OK';
firstButton.addEventListener('click', function() { element.remove(); });
} else {
for(var i = 0; i < buttons.length; i++) {
var button = buttonsContainer.appendChild(document.createElement('button'));
button.className = 'input__button';
button.textContent = buttons[i].text;
button.addEventListener('click', function() {
element.remove();
buttons[i].callback();
});
if(firstButton === null)
firstButton = button;
}
}
document.body.appendChild(element);
firstButton.focus();
return true;
};
Misuzu.initLoginPage = function() {
var updateForm = function(avatarElem, usernameElem) {
var xhr = new XMLHttpRequest;
xhr.addEventListener('readystatechange', function() {
if(xhr.readyState !== 4)
return;
var json = JSON.parse(xhr.responseText);
if(!json)
return;
if(json.name)
usernameElem.value = json.name;
avatarElem.src = json.avatar;
});
// need to figure out a url registry system again, current one is too much overhead so lets just do this for now
xhr.open('GET', '/auth/login.php?resolve=1&name=' + encodeURIComponent(usernameElem.value));
xhr.send();
};
var loginForms = $c('js-login-form');
for(var i = 0; i < loginForms.length; ++i)
(function(form) {
var loginTimeOut = 0,
loginAvatar = form.querySelector('.js-login-avatar'),
loginUsername = form.querySelector('.js-login-username');
updateForm(loginAvatar, loginUsername);
loginUsername.addEventListener('keyup', function() {
if(loginTimeOut)
return;
loginTimeOut = setTimeout(function() {
updateForm(loginAvatar, loginUsername);
clearTimeout(loginTimeOut);
loginTimeOut = 0;
}, 750);
});
})(loginForms[i]);
};
Misuzu.initQuickSubmit = function() {
var ctrlSubmit = Array.from($qa('.js-quick-submit, .js-ctrl-enter-submit'));
if(!ctrlSubmit)
return;
for(var i = 0; i < ctrlSubmit.length; ++i)
ctrlSubmit[i].addEventListener('keydown', function(ev) {
if((ev.code === 'Enter' || ev.code === 'NumpadEnter') // i hate this fucking language so much
&& ev.ctrlKey && !ev.altKey && !ev.shiftKey && !ev.metaKey) {
// hack: prevent forum editor from screaming when using this keycombo
// can probably be done in a less stupid manner
Misuzu.Forum.Editor.allowWindowClose = true;
this.form.submit();
ev.preventDefault();
}
});
};