require('whatwg-fetch'); require('element-closest'); require('classlist.js'); let killy = require('killy'); let Awesomplete = require('awesomplete'); 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; 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', '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': 'Войти через ВКонтакте', '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 ws, pageTitle; function initWS() { var content = document.getElementById('content'); if (!content) { return; } var pageMID = content.getAttribute('data-mid'); if (!pageMID) { return; } var url = (window.location.protocol === 'https:' ? 'wss' : 'ws') + ':' + (typeof juickDebug !== 'undefined' ? '//ws.juick.com/_replies' : ('//ws.juick.com/' + pageMID)), hash = document.getElementById('body').getAttribute('data-hash'); if (hash) { url += '?hash=' + hash; } ws = new WebSocket(url); ws.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; } }; ws.onclose = function () { console.log('offline'); ws = false; setTimeout(function () { initWS(); }, 2000); }; ws.onmessage = function (msg) { if (msg.data == ' ') { ws.send(' '); } else { try { var jsonMsg = JSON.parse(msg.data); console.log('data: ' + msg.data); wsIncomingReply(jsonMsg); } catch (err) { console.log(err); } } }; setInterval(wsSendKeepAlive, 90000); } function wsSendKeepAlive() { if (ws) { ws.send(' '); } } function wsShutdown() { if (ws) { ws.onclose = function () { }; ws.close(); } } function isTreeMode() { // relies on UserThread.printReplies implementation TODO keep this in cookie or something return document.querySelector('.title2-right > a').href.match(/\?view=(\w+)/)[1] == 'list'; } function wsIncomingReply(msg) { let msgNum = '/' + msg.rid; if (msg.replyto > 0) { msgNum += ` ${i18n('message.inReplyTo')} /${msg.replyto}`; } let photoDiv = (msg.attach === undefined) ? '' : `
`; let msgContHtml = `
${msg.user.uname}:
${msg.user.uname}
${killy.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(); }); killy.embedLinksToX(li.querySelector('.msg-cont'), '.msg-links', '.msg-txt a'); if (isTreeMode() && (msg.replyto > 0)) { let p = document.getElementById(msg.replyto); let m = parseInt(p.style.marginLeft) + 20; while (p.nextElementSibling && (parseInt(p.nextElementSibling.style.marginLeft) >= m)) { p = p.nextElementSibling; } li.style.marginLeft = m + 'px'; p.parentNode.insertBefore(li, p.nextSibling); } else { 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.submit(); } } } 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').insertAdjacentHTML('afterbegin', `

${i18n('postForm.pleaseInputMessageText')}

`); evt.preventDefault(); } } function showMoreReplies(el, id) { var foldedReplies = el.closest('li').querySelector('.msg-comments'); if (!foldedReplies) { return; } foldedReplies.style.display = 'none'; var replies = document.querySelectorAll('#replies>li'), flagshow = 0, i = 0; for (; i < replies.length; i += 1) { if (flagshow == 1) { if (replies[i].style.display == 'none') { replies[i].style.display = 'block'; } else { break; } } if (replies[i].id == id) { flagshow = 1; } } return false; } function showCommentForm(mid, rid) { let reply = document.getElementById(rid); let formTarget = reply.querySelector('div.msg-cont .msg-comment-target'); if (formTarget) { let formHtml = `
${evilIcon('ei-camera')}
`; 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 < 1) { return 'Empty message'; } 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; }); } 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) { if (div.children.length === 1) { let inp = attachInput(); inp.addEventListener('change', function () { div.classList.add('attach-photo-active'); }); inp.click(); div.appendChild(inp); } else { div.innerHTML = null; div.classList.remove('attach-photo-active'); } } 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 unfoldReply() { var anchor = window.location.hash.substring(1); if ((0 + anchor) > 0) { var el = document.getElementById(anchor); if (!el) { return; } while (el.style.display === 'none') { el = el.previousElementSibling; } showMoreReplies(el, el.getAttribute('id')); window.location.replace(window.location.hash); } } 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(``); return false; } else { openDialog(``); return false; } } function openPostDialog() { let newmessageTemplate = `
${i18n('postForm.or')} ${i18n('postForm.upload')}

`; return openDialog(newmessageTemplate); } function openDialog(html) { var dialogHtml = `
${html}
`; let body = document.querySelector('body'); body.classList.add('dialog-opened'); body.insertAdjacentHTML('afterbegin', dialogHtml); 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.juick.com/users?uname=' + uname) .then(function () { style.background = '#FFCCCC'; }) .catch(function () { style.background = '#CCFFCC'; }); } /******************************************************************************/ function openDialogLogin() { let html = `

${i18n('loginDialog.pleaseIntroduceYourself')}:

${evilIcon('ei-sc-facebook')}${i18n('loginDialog.facebook')} ${evilIcon('ei-sc-vk')}${i18n('loginDialog.vk')}

${i18n('loginDialog.registeredAlready')}



`; 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('//juick.com/like?mid=' + mid, { method: 'POST', credentials: 'same-origin' }) .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 setPopular(e, mid, popular) { fetch('//api.juick.com/messages/set_popular?mid=' + mid + '&popular=' + popular + '&hash=' + document.getElementById('body').getAttribute('data-hash'), { credentials: 'same-origin' }) .then(function () { e.closest('article').append(resultMessage('OK!')); }); return false; } function setPrivacy(e, mid) { fetch('//api.juick.com/messages/set_privacy?mid=' + mid + '&hash=' + document.getElementById('body').getAttribute('data-hash'), { credentials: 'same-origin' }) .then(function () { e.closest('article').append(resultMessage('OK!')); }); return false; } function getTags() { fetch('//api.juick.com/tags?hash=' + document.getElementById('body').getAttribute('data-hash'), { credentials: 'same-origin' }) .then(response => { return response.json(); }) .then(json => { let tags = json.map(t => t.tag); let input = document.getElementById('tags_input'); new Awesomplete(input, {list : tags}); }); return false; } /******************************************************************************/ 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); }); }); var content = document.getElementById('content'); if (content) { var pageMID = content.getAttribute('data-mid'); if (pageMID > 0) { document.querySelectorAll('li.msg').forEach(li => { let showMoreBtn = li.querySelector('.msg-comments'); if (showMoreBtn) { showMoreBtn.addEventListener('click', function (e) { showMoreReplies(e.target, li.id); e.preventDefault(); }); } 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(); } } } } } 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('.l .a-popular-plus').forEach(function (e) { e.addEventListener('click', function (e) { setPopular( e.target, e.target.closest('article').getAttribute('data-mid'), 2); e.preventDefault(); }); }); document.querySelectorAll('.l .a-popular-minus').forEach(function (e) { e.addEventListener('click', function (e) { setPopular( e.target, e.target.closest('article').getAttribute('data-mid'), -1); e.preventDefault(); }); }); document.querySelectorAll('.l .a-popular-delete').forEach(function (e) { e.addEventListener('click', function (e) { setPopular( e.target, e.target.closest('article').getAttribute('data-mid'), -2); 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('.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(); }); } var post = document.getElementById('post'); if (post) { post.addEventListener('click', (e) => { openPostDialog(); getTags(); var newMessageBlock = document.getElementById('newmessage'); if (newMessageBlock) { newMessageBlock.addEventListener('submit', newMessage); let ta = newMessageBlock.querySelector('textarea'); if (window.draft) { ta.innerHTML = window.draft; } ta.addEventListener('keypress', function (e) { postformListener(e.target, e); }); ta.style.minHeight = '70px'; autosize(ta); ta.focus(); newMessageBlock.querySelector('#tags_input') .addEventListener('keypress', function (e) { postformListener(e.target, e); }); newMessageBlock.querySelector('a').addEventListener('click', function (e) { attachMessagePhoto(e.target); }); } 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'); } }); unfoldReply(); initWS(); window.addEventListener('hashchange', unfoldReply); window.addEventListener('pagehide', wsShutdown); killy.embedAll(); });