require('whatwg-fetch');
require('element-closest');
require('classlist.js');
require('url-search-params-polyfill');
require('formdata-polyfill');
import { embedLinksToX, embedAll, format } from './embed';
if (!('remove' in Element.prototype)) { // Firefox <23
Element.prototype.remove = function() {
if (this.parentNode) {
this.parentNode.removeChild(this);
}
};
}
NodeList.prototype.forEach = Array.prototype.forEach;
HTMLCollection.prototype.forEach = Array.prototype.forEach;
NodeList.prototype.filter = Array.prototype.filter;
HTMLCollection.prototype.filter = Array.prototype.filter;
Element.prototype.selectText = function() {
let d = document;
if (d.body.createTextRange) {
let range = d.body.createTextRange();
range.moveToElementText(this);
range.select();
} else if (window.getSelection) {
let selection = window.getSelection();
let rangeSel = d.createRange();
rangeSel.selectNodeContents(this);
selection.removeAllRanges();
selection.addRange(rangeSel);
}
};
function autosize(el) {
let offset = (!window.opera)
? (el.offsetHeight - el.clientHeight)
: (el.offsetHeight + parseInt(window.getComputedStyle(el, null).getPropertyValue('border-top-width')));
let resize = function(el) {
el.style.height = 'auto';
el.style.height = (el.scrollHeight + offset) + 'px';
};
if (el.addEventListener) {
el.addEventListener('input', () => resize(el));
} else if (el.attachEvent) {
el.attachEvent('onkeyup', () => resize(el));
}
}
function evilIcon(name) {
return `
`;
}
/* eslint-disable only-ascii/only-ascii */
const translations = {
'en': {
'message.inReplyTo': 'in reply to',
'message.reply': 'Reply',
'message.likeThisMessage?': 'Recommend this message?',
'postForm.pleaseInputMessageText': 'Please input message text',
'postForm.upload': 'Upload',
'postForm.newMessage': 'New message...',
'postForm.imageLink': 'Link to image',
'postForm.imageFormats': 'JPG/PNG, up to 10 MB',
'postForm.or': 'or',
'postForm.tags': 'Tags (space separated)',
'postForm.submit': 'Send',
'comment.writeComment': 'Write a comment...',
'shareDialog.linkToMessage': 'Link to message',
'shareDialog.messageNumber': 'Message number',
'shareDialog.share': 'Share',
'loginDialog.pleaseIntroduceYourself': 'Please introduce yourself',
'loginDialog.registeredAlready': 'Registered already?',
'loginDialog.username': 'Username',
'loginDialog.password': 'Password',
'loginDialog.facebook': 'Login with Facebook',
'loginDialog.vk': 'Login with VK',
'loginDialog.email': 'Registration',
'error.error': 'Error'
},
'ru': {
'message.inReplyTo': 'в ответ на',
'message.reply': 'Ответить',
'message.likeThisMessage?': 'Рекомендовать это сообщение?',
'postForm.pleaseInputMessageText': 'Пожалуйста, введите текст сообщения',
'postForm.upload': 'загрузить',
'postForm.newMessage': 'Новое сообщение...',
'postForm.imageLink': 'Ссылка на изображение',
'postForm.imageFormats': 'JPG/PNG, до 10Мб',
'postForm.or': 'или',
'postForm.tags': 'Теги (через пробел)',
'postForm.submit': 'Отправить',
'comment.writeComment': 'Написать комментарий...',
'shareDialog.linkToMessage': 'Ссылка на сообщение',
'shareDialog.messageNumber': 'Номер сообщения',
'shareDialog.share': 'Поделиться',
'loginDialog.pleaseIntroduceYourself': 'Пожалуйста, представьтесь',
'loginDialog.registeredAlready': 'Уже зарегистрированы?',
'loginDialog.username': 'Имя пользователя',
'loginDialog.password': 'Пароль',
'loginDialog.facebook': 'Войти через Facebook',
'loginDialog.vk': 'Войти через ВКонтакте',
'loginDialog.email': 'Регистрация',
'error.error': 'Ошибка'
}
};
/* eslint-enable only-ascii/only-ascii */
function getLang() {
return (window.navigator.languages && window.navigator.languages[0])
|| window.navigator.userLanguage
|| window.navigator.language;
}
function i18n(key, lang = undefined) {
const fallbackLang = 'ru';
lang = lang || getLang().split('-')[0];
return (translations[lang] && translations[lang][key])
|| translations[fallbackLang][key]
|| key;
}
var es, pageTitle;
function initES() {
let url = '/api/events';
let hash = document.getElementById('body').getAttribute('data-hash');
if (hash) {
url += '?hash=' + hash;
}
es = new EventSource(url);
es.onopen = function() {
console.log('online');
if (!document.querySelector('#wsthread')) {
var d = document.createElement('div');
d.id = 'wsthread';
d.addEventListener('click', nextReply);
document.querySelector('body').appendChild(d);
pageTitle = document.title;
}
};
es.addEventListener('msg', msg => {
try {
var jsonMsg = JSON.parse(msg.data);
console.log('data: ' + msg.data);
if (jsonMsg.service) {
return;
}
wsIncomingReply(jsonMsg);
} catch (err) {
console.log(err);
}
});
}
function wsIncomingReply(msg) {
let content = document.getElementById('content');
if (!content) { return; }
let pageMID = content.getAttribute('data-mid');
if (!pageMID || pageMID != msg.mid) { return; }
let msgNum = '/' + msg.rid;
if (msg.replyto > 0) {
msgNum += ` ${i18n('message.inReplyTo')} /${msg.replyto}`;
}
let photoDiv = (msg.attach == null) ? '' : `
`;
let msgContHtml = `
${format(msg.body, msg.mid, false)}
${photoDiv}
`;
let li = document.createElement('li');
li.setAttribute('class', 'msg reply-new');
li.setAttribute('id', msg.rid);
li.innerHTML = msgContHtml;
li.addEventListener('click', newReply);
li.addEventListener('mouseover', newReply);
li.querySelector('a.msg-reply-link').addEventListener('click', function(e) {
showCommentForm(msg.mid, msg.rid);
e.preventDefault();
});
embedLinksToX(li.querySelector('.msg-cont'), '.msg-links', '.msg-txt a');
document.getElementById('replies').appendChild(li);
updateRepliesCounter();
}
function newReply(e) {
var li = e.target;
li.classList.remove('reply-new');
li.removeEventListener('click', e);
li.removeEventListener('mouseover', e);
updateRepliesCounter();
}
function nextReply() {
var li = document.querySelector('#replies>li.reply-new');
if (li) {
li.classList.remove('reply-new');
li.removeEventListener('click', this);
li.children[0].scrollIntoView();
updateRepliesCounter();
}
}
function updateRepliesCounter() {
var replies = document.querySelectorAll('#replies>li.reply-new').length;
var wsthread = document.getElementById('wsthread');
if (replies) {
wsthread.textContent = replies;
wsthread.style.display = 'block';
document.title = '[' + replies + '] ' + pageTitle;
} else {
wsthread.style.display = 'none';
document.title = pageTitle;
}
}
/******************************************************************************/
/******************************************************************************/
/******************************************************************************/
function postformListener(formEl, ev) {
if (ev.ctrlKey && (ev.keyCode == 10 || ev.keyCode == 13)) {
let form = formEl.closest('form');
if (!form.onsubmit || form.onsubmit()) {
form.querySelector('input[type="submit"]').click();
}
}
}
function closeDialogListener(ev) {
ev = ev || window.event;
if (ev.keyCode == 27) {
closeDialog();
}
}
function newMessage(evt) {
document.querySelectorAll('#newmessage .dialogtxt').forEach(t => {
t.remove();
});
if (document.querySelector('#newmessage textarea').value.length == 0
&& document.querySelector('#newmessage .img').value.length == 0
&& !document.querySelector('#newmessage input[type="file"]')) {
document.querySelector('#newmessage').insertAdjacentHTML('afterbegin', `${i18n('postForm.pleaseInputMessageText')}
`);
evt.preventDefault();
}
}
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
function showCommentForm(mid, rid) {
let reply = document.getElementById(rid);
let formTarget = reply.querySelector('div.msg-cont .msg-comment-target');
if (formTarget) {
let formHtml = `
`;
formTarget.insertAdjacentHTML('afterend', formHtml);
formTarget.remove();
let form = reply.querySelector('form');
let submitButton = form.querySelector('input[type="submit"]');
let attachButton = form.querySelector('.msg-comment .attach-photo');
attachButton.addEventListener('click', e => attachCommentPhoto(e.target));
let textarea = form.querySelector('.msg-comment textarea');
textarea.addEventListener('keypress', e => postformListener(e.target, e));
autosize(textarea);
let validateMessage = () => {
let len = textarea.value.length;
if (len > 4096) { return 'Message is too long'; }
return '';
};
form.addEventListener('submit', e => {
let validationResult = validateMessage();
if (validationResult) {
e.preventDefault();
alert(validationResult);
return false;
}
submitButton.disabled = true;
let formData = new FormData(form);
fetch('/api/comment' + '?hash=' + document.getElementById('body').getAttribute('data-hash'), {
method: 'POST',
body: formData,
credentials: 'omit'
}).then(handleErrors)
.then(response => {
if (response.ok) {
response.json().then(result => {
if (result.newMessage) {
window.location.href = new URL(`${mid}#${result.newMessage.rid}`, window.location.href);
} else {
alert(result.text);
}
window.location.reload(true);
});
}
}).catch(error => {
alert(error.message);
});
e.preventDefault();
});
}
reply.querySelector('.msg-comment textarea').focus();
}
function attachInput() {
let inp = document.createElement('input');
inp.setAttribute('type', 'file');
inp.setAttribute('name', 'attach');
inp.setAttribute('accept', 'image/jpeg,image/png');
inp.style.visibility = 'hidden';
return inp;
}
function attachCommentPhoto(div) {
let input = div.querySelector('input');
if (input) {
input.remove();
div.classList.remove('attach-photo-active');
} else {
let newInput = attachInput();
newInput.addEventListener('change', function() {
div.classList.add('attach-photo-active');
});
newInput.click();
div.appendChild(newInput);
}
}
function attachMessagePhoto(div) {
var f = div.closest('form'),
finput = f.querySelector('input[type="file"]');
if (!finput) {
var inp = attachInput();
inp.style.float = 'left';
inp.style.width = 0;
inp.style.height = 0;
inp.addEventListener('change', function() {
div.textContent = i18n('postForm.upload') + ' (✓)';
});
f.appendChild(inp);
inp.click();
} else {
finput.remove();
div.textContent = i18n('postForm.upload');
}
}
function showMessageLinksDialog(mid, rid) {
let hlink = window.location.protocol + '//juick.com/' + mid;
let mlink = '#' + mid;
if (rid > 0) {
hlink += '#' + rid;
mlink += '/' + rid;
}
let hlinkenc = encodeURIComponent(hlink);
let html = `
${i18n('shareDialog.linkToMessage')}:
${hlink}
${i18n('shareDialog.messageNumber')}:
${mlink}
${i18n('shareDialog.share')}:
`;
openDialog(html);
}
function showPhotoDialog(fname) {
let width = window.innerWidth;
let height = window.innerHeight;
let minDimension = (width < height) ? width : height;
if (minDimension < 640) {
return true; // no dialog, open the link
} else if (minDimension < 1280) {
openDialog(``, true);
return false;
} else {
openDialog(``, true);
return false;
}
}
function openPostDialog() {
let newmessageTemplate = `
`;
return openDialog(newmessageTemplate);
}
function openDialog(html, image) {
var dialogHtml = `
`;
let body = document.querySelector('body');
body.classList.add('dialog-opened');
body.insertAdjacentHTML('afterbegin', dialogHtml);
if (image) {
let header = document.querySelector('#dialog_header');
header.classList.add('header_image');
}
document.addEventListener('keydown', closeDialogListener);
document.querySelector('#dialogb').addEventListener('click', closeDialog);
document.querySelector('#dialogc').addEventListener('click', closeDialog);
}
function closeDialog() {
let draft = document.querySelector('#newmessage textarea');
if (draft) {
window.draft = draft.value;
}
document.querySelector('body').classList.remove('dialog-opened');
document.querySelector('#dialogb').remove();
document.querySelector('#dialogt').remove();
}
function openSocialWindow(a) {
var w = window.open(a.href, 'juickshare', 'width=640,height=400');
if (window.focus) { w.focus(); }
return false;
}
function checkUsername() {
var uname = document.querySelector('#username').textContent,
style = document.querySelector('#username').style;
fetch('/api/users?uname=' + uname)
.then(handleErrors)
.then(function() {
style.background = '#FFCCCC';
})
.catch(function() {
style.background = '#CCFFCC';
});
}
/******************************************************************************/
function openDialogLogin() {
let html = `
`;
openDialog(html);
return false;
}
/******************************************************************************/
function resultMessage(str) {
var result = document.createElement('p');
result.textContent = str;
return result;
}
function likeMessage(e, mid) {
if (confirm(i18n('message.likeThisMessage?'))) {
fetch('/api/like?mid=' + mid
+ '&hash=' + document.getElementById('body').getAttribute('data-hash'), {
method: 'POST',
credentials: 'omit'
})
.then(handleErrors)
.then(function(response) {
if (response.ok) {
e.closest('article').appendChild(resultMessage('OK!'));
}
})
.catch(function() {
e.closest('article').appendChild(resultMessage(i18n('error.error')));
});
}
return false;
}
function subscribeMessage(e, mid) {
fetch('/api/subscribe?mid=' + mid
+ '&hash=' + document.getElementById('body').getAttribute('data-hash'), {
method: 'POST',
credentials: 'omit'
})
.then(handleErrors)
.then(function(response) {
if (response.ok) {
window.location.reload(true);
} else {
alert('Something went wrong :(');
}
})
.catch(error => {
alert(error.message);
});
return false;
}
/******************************************************************************/
function setPopular(e, mid, popular) {
fetch('/api/messages/set_popular?mid=' + mid
+ '&popular=' + popular
+ '&hash=' + document.getElementById('body').getAttribute('data-hash'), {
credentials: 'same-origin'
})
.then(handleErrors)
.then(function() {
e.closest('article').append(resultMessage('OK!'));
});
return false;
}
function setPrivacy(e, mid) {
fetch('/api/messages/set_privacy?mid=' + mid
+ '&hash=' + document.getElementById('body').getAttribute('data-hash'), {
credentials: 'same-origin'
})
.then(handleErrors)
.then(function() {
e.closest('article').append(resultMessage('OK!'));
});
return false;
}
function getTags() {
fetch('/api/tags?hash=' + document.getElementById('body').getAttribute('data-hash'), {
credentials: 'omit'
})
.then(handleErrors)
.then(response => {
return response.json();
})
.then(json => {
let tags = json.map(t => t.tag);
let input = document.getElementById('tags_input');
});
return false;
}
function addTag(tag) {
document.forms['postmsg'].body.value = '*' + tag + ' ' + document.forms['postmsg'].body.value;
return false;
}
var users = {};
function fetchUserUri(dataUri, callback) {
if (users[dataUri]) {
callback(users[dataUri]);
} else {
let data = new FormData();
data.append('uri', dataUri);
fetch('/u/', {
method: 'POST',
body: data
}).then(handleErrors)
.then(response => {
return response.json();
})
.then(json => {
users[dataUri] = json;
callback(json);
});
}
}
/******************************************************************************/
function ready(fn) {
if (document.readyState != 'loading') {
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
ready(function() {
document.querySelectorAll('textarea').forEach((ta) => {
autosize(ta);
});
var insertPMButtons = function(e) {
e.target.classList.add('narrowpm');
e.target.parentNode.insertAdjacentHTML('afterend', '');
e.target.removeEventListener('click', insertPMButtons);
e.preventDefault();
};
document.querySelectorAll('textarea.replypm').forEach(function(e) {
e.addEventListener('click', insertPMButtons);
e.addEventListener('keypress', function(e) {
postformListener(e.target, e);
});
});
document.querySelectorAll('#postmsg textarea').forEach(function(e) {
e.addEventListener('keypress', function(e) {
postformListener(e.target, e);
});
});
var content = document.getElementById('content');
if (content) {
var pageMID = content.getAttribute('data-mid');
if (pageMID > 0) {
document.querySelectorAll('li.msg').forEach(li => {
let showReplyFormBtn = li.querySelector('.a-thread-comment');
if (showReplyFormBtn) {
showReplyFormBtn.addEventListener('click', function(e) {
showCommentForm(pageMID, li.id);
e.preventDefault();
});
}
});
let opMessage = document.querySelector('.msgthread');
if (opMessage) {
let replyTextarea = opMessage.querySelector('textarea.reply');
if (replyTextarea) {
replyTextarea.addEventListener('focus', e => showCommentForm(pageMID, 0));
replyTextarea.addEventListener('keypress', e => postformListener(e.target, e));
if (!window.location.hash) {
replyTextarea.focus();
}
}
}
}
}
var postmsg = document.getElementById('postmsg');
if (postmsg) {
document.querySelectorAll('a').filter(t => t.href.indexOf('?') >= 0).forEach(t => {
t.addEventListener('click', e => {
let params = new URLSearchParams(t.href.slice(t.href.indexOf('?') + 1));
if (params.has('tag')) {
addTag(params.get('tag'));
e.preventDefault();
}
});
});
postmsg.addEventListener('submit', e => {
let formData = new FormData(postmsg);
fetch('/api/post' + '?hash=' + document.getElementById('body').getAttribute('data-hash'), {
method: 'POST',
body: formData,
credentials: 'omit'
}).then(handleErrors)
.then(response => {
if (response.ok) {
response.json().then(result => {
if (result.newMessage) {
window.location = new URL(`/m/${result.newMessage.mid}`, window.location.href);
} else {
alert(result.text);
}
});
} else {
alert('Something went wrong :(');
}
}).catch(error => {
alert(error.message);
});
e.preventDefault();
});
}
document.querySelectorAll('.pmmsg').forEach(pmmsg => {
pmmsg.addEventListener('submit', e => {
let formData = new FormData(pmmsg);
fetch('/api/pm' + '?hash=' + document.getElementById('body').getAttribute('data-hash'), {
method: 'POST',
body: formData,
credentials: 'omit'
}).then(handleErrors)
.then(response => {
if (response.ok) {
response.json().then(result => {
if (result.to) {
window.location = new URL('/pm/sent', window.location.href);
} else {
alert('Something went wrong :(');
}
});
} else {
alert('Something went wrong :(');
}
}).catch(error => {
alert(error.message);
});
e.preventDefault();
});
});
document.querySelectorAll('.msg-menu').forEach(function(el) {
el.addEventListener('click', function(e) {
var reply = e.target.closest('li');
var rid = reply ? parseInt(reply.id) : 0;
var message = e.target.closest('section');
var mid = message.getAttribute('data-mid') || e.target.closest('article').getAttribute('data-mid');
showMessageLinksDialog(mid, rid);
e.preventDefault();
});
});
document.querySelectorAll('.l .a-privacy').forEach(function(e) {
e.addEventListener('click', function(e) {
setPrivacy(
e.target,
e.target.closest('article').getAttribute('data-mid'));
e.preventDefault();
});
});
document.querySelectorAll('.ir a[data-fname], .msg-media a[data-fname]').forEach(function(el) {
el.addEventListener('click', function(e) {
let fname = e.target.closest('[data-fname]').getAttribute('data-fname');
if (!showPhotoDialog(fname)) {
e.preventDefault();
}
});
});
document.querySelectorAll('.social a').forEach(function(e) {
e.addEventListener('click', function(e) {
openSocialWindow(e.target);
e.preventDefault();
});
});
var username = document.getElementById('username');
if (username) {
username.addEventListener('blur', function() {
checkUsername();
});
}
document.querySelectorAll('.l .a-like').forEach(function(e) {
e.addEventListener('click', function(e) {
likeMessage(
e.target,
e.target.closest('article').getAttribute('data-mid'));
e.preventDefault();
});
});
document.querySelectorAll('.l .a-sub').forEach(function(e) {
e.addEventListener('click', function(e) {
subscribeMessage(
e.target,
document.getElementById('content').getAttribute('data-mid'));
e.preventDefault();
});
});
document.querySelectorAll('.a-login').forEach(function(el) {
el.addEventListener('click', function(e) {
openDialogLogin();
e.preventDefault();
});
});
var unfoldall = document.getElementById('unfoldall');
if (unfoldall) {
unfoldall.addEventListener('click', function(e) {
document.querySelectorAll('#replies>li').forEach(function(e) {
e.style.display = 'block';
});
document.querySelectorAll('#replies .msg-comments').forEach(function(e) {
e.style.display = 'none';
});
e.preventDefault();
});
}
document.querySelectorAll('article').forEach(function(article) {
if (Array.prototype.some.call(
article.querySelectorAll('.msg-tags a'),
function(a) {
return a.textContent === 'NSFW';
}
)) {
article.classList.add('nsfw');
}
});
document.querySelectorAll('[data-uri]').forEach(el => {
let dataUri = el.getAttribute('data-uri');
if (dataUri) {
setTimeout(() => fetchUserUri(dataUri, user => {
let header = el.closest('.msg-header');
header.querySelectorAll('.a-username').forEach(a => {
a.setAttribute('href', user.uri);
let img = a.querySelector('img');
if (img && user.avatar) {
img.setAttribute('src', user.avatar);
img.setAttribute('alt', user.uname);
}
let textNode = a.childNodes[0];
if (textNode.nodeType === Node.TEXT_NODE && textNode.nodeValue.trim().length > 0) {
let uname = document.createTextNode(user.uname);
a.replaceChild(uname, a.firstChild);
}
});
}), 100);
}
});
document.querySelectorAll('[data-user-uri]').forEach(el => {
let dataUri = el.getAttribute('href');
if (dataUri) {
setTimeout(() => fetchUserUri(dataUri, user => {
let textNode = el.childNodes[0];
if (textNode.nodeType === Node.TEXT_NODE && textNode.nodeValue.trim().length > 0) {
let uname = document.createTextNode(`@${user.uname}`);
el.replaceChild(uname, el.firstChild);
}
}), 100);
}
});
initES();
embedAll();
var elSelector = 'header',
elClassHidden = 'header--hidden',
elClassBackground = 'header--background',
throttleTimeout = 500,
element = document.querySelector(elSelector);
if (element) {
var dHeight = 0,
wHeight = 0,
wScrollCurrent = 0,
wScrollBefore = 0,
wScrollDiff = 0,
throttle = function(delay, fn) {
var last, deferTimer;
return function() {
var context = this, args = arguments, now = +new Date;
if (last && now < last + delay) {
clearTimeout(deferTimer);
deferTimer = setTimeout(
function() {
last = now;
fn.apply(context, args);
},
delay);
} else {
last = now;
fn.apply(context, args);
}
};
};
window.addEventListener('scroll', throttle(throttleTimeout, function() {
dHeight = document.body.offsetHeight;
wHeight = window.innerHeight;
wScrollCurrent = window.pageYOffset;
wScrollDiff = wScrollBefore - wScrollCurrent;
if (wScrollCurrent <= 0) {
// scrolled to the very top; element sticks to the top
element.classList.remove(elClassHidden);
element.classList.remove(elClassBackground);
} else if (wScrollDiff > 0 && element.classList.contains(elClassHidden)) {
// scrolled up; element slides in
element.classList.remove(elClassHidden);
element.classList.add(elClassBackground);
} else if (wScrollDiff < 0) {
// scrolled down
if (wScrollCurrent + wHeight >= dHeight && element.classList.contains(elClassHidden)) {
// scrolled to the very bottom; element slides in
element.classList.remove(elClassHidden);
element.classList.add(elClassBackground);
} else {
// scrolled down; element slides out
element.classList.add(elClassHidden);
}
}
wScrollBefore = wScrollCurrent;
}));
}
});