misuzu/assets/js/misuzu/_main.js

458 lines
20 KiB
JavaScript

var Misuzu = function() {
timeago.render($qa('time'));
hljs.initHighlighting();
Misuzu.initQuickSubmit(); // only used by the forum posting form
Misuzu.Forum.Editor.init();
Misuzu.Events.dispatch();
Misuzu.initLoginPage();
Misuzu.handleEmbeds();
};
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();
}
});
};
Misuzu.handleEmbeds = function() {
const UIHARU_API = location.protocol + '//uiharu.' + location.host;
const embeds = Array.from($qa('.js-msz-embed-media'));
if(embeds.length > 0) {
$as(embeds);
const uiharu = new Uiharu(UIHARU_API),
elems = new Map;
for(const elem of embeds) {
let cleanUrl = elem.dataset.mszEmbedUrl.replace(/ /, '%20');
if(cleanUrl.indexOf('http://') !== 0 && cleanUrl.indexOf('https://') !== 0) {
elem.textContent = elem.dataset.mszEmbedUrl;
continue;
}
elem.textContent = 'Loading...';
if(elems.has(cleanUrl))
elems.get(cleanUrl).push(elem);
else
elems.set(cleanUrl, [elem]);
}
uiharu.lookupMany(Array.from(elems.keys()), function(resp) {
if(resp.results === undefined)
return; // rip
for(const result of resp.results) {
let elemList = elems.get(result.url);
const replaceWithUrl = function() {
for(let i = 0; i < elemList.length; ++i) {
let body = $e({
tag: 'a',
attrs: {
className: 'link',
href: result.url,
target: '_blank',
rel: 'noopener noreferrer',
},
child: result.url
});
$ib(elemList[i], body);
$r(elemList[i]);
elemList[i] = body;
}
};
if(result.error) {
replaceWithUrl();
console.error(result.error);
continue;
}
if(result.info.title === undefined) {
replaceWithUrl();
console.warn('Media is no longer available.');
continue;
}
(function(elemList, info) {
const replaceElement = function(bodyInfo) {
for(let i = 0; i < elemList.length; ++i) {
let body = $e(bodyInfo);
$ib(elemList[i], body);
$r(elemList[i]);
elemList[i] = 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 {
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({
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({
attrs: {
className: 'embed embed-nicovideo',
},
child: {
tag: 'script',
attrs: {
async: 'async',
src: embedUrl,
},
},
});
}));
} else if(info.type === 'media') {
// todo: proxying
// think uiharu will just serve as the camo system
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({
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) {
// need dedicated audio placeholder and a player frame that includes the cover art
replaceElement(createEmbedPH('external', info, function() {
replaceElement({
attrs: {
className: 'embed embed-external',
},
child: {
tag: 'audio',
attrs: {
autoplay: 'autoplay',
controls: 'controls',
src: info.url,
},
},
});
}, '300px', '300px'));
} else if(info.is_image) {
replaceElement({
tag: 'img',
attrs: {
src: info.url,
alt: info.url,
style: {
maxWidth: '100%',
maxHeight: '900px',
},
},
});
}
}
})(elemList, result.info);
}
});
}
};