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); } }); } };