╔══════ PixeFan ══════╗
BLACKPINK // PIXELOG/BLACKPINKX
════════════════════════════════════════
Sejam todos bem vindos ao pixelog fan dedicado ao Grupo sul - coreano Blackpink.
════ Menu ══════════════
' +
'' +
avatar +
'' + escaparHtml(item.username) + '' +
'' +
botaoSeguirHtml +
'
';
}
function carregarLista() {
if (carregado) {
return;
}
carregado = true;
fetch(modal.getAttribute('data-url-curtidores') + '?post_id=' + encodeURIComponent(abrirBotao.getAttribute('data-post-id')), { credentials: 'same-origin' })
.then(function (resposta) { return resposta.json(); })
.then(function (dados) {
if (!dados || !dados.sucesso || !dados.itens.length) {
lista.innerHTML = '' + m('curtidores.ninguem_curtiu', 'Ninguém curtiu ainda.') + '
'; return; } lista.innerHTML = dados.itens.map(linhaCurtidor).join(''); }) .catch(function () { lista.innerHTML = '' + m('curtidores.erro_carregar', 'Não foi possível carregar as curtidas.') + '
'; carregado = false; }); } lista.addEventListener('click', function (evento) { var alvo = evento.target.closest('[data-seguir-botao]'); if (!alvo) { return; } var jaSegue = alvo.getAttribute('data-segue') === '1'; alvo.disabled = true; fetch(modal.getAttribute('data-url-seguir'), { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: [ 'ajax=1', 'acao=' + (jaSegue ? 'deixar_de_seguir' : 'seguir'), 'alvo_id=' + encodeURIComponent(alvo.getAttribute('data-alvo-id')), 'username_alvo=' + encodeURIComponent(alvo.getAttribute('data-username')), 'csrf_token=' + encodeURIComponent(modal.getAttribute('data-csrf')) ].join('&') }) .then(function (resposta) { return resposta.json(); }) .then(function (dados) { if (dados && dados.sucesso) { var segueAgora = !jaSegue; alvo.setAttribute('data-segue', segueAgora ? '1' : '0'); alvo.classList.toggle('botao-primario', !segueAgora); alvo.classList.toggle('botao-contorno', segueAgora); alvo.textContent = segueAgora ? m('curtidores.deixar_seguir', 'Deixar de seguir') : m('curtidores.seguir', 'Seguir'); } }) .finally(function () { alvo.disabled = false; }); }); var TRANSICAO_MODAL_CURTIDORES_MS = 220; function abrirModal() { modal.hidden = false; document.body.style.overflow = 'hidden'; carregarLista(); requestAnimationFrame(function () { requestAnimationFrame(function () { modal.classList.add('modal-curtidores--visivel'); }); }); } function fecharModal() { modal.classList.remove('modal-curtidores--visivel'); document.body.style.overflow = ''; window.setTimeout(function () { modal.hidden = true; }, TRANSICAO_MODAL_CURTIDORES_MS); } abrirBotao.addEventListener('click', abrirModal); modal.querySelectorAll('[data-fechar-curtidores]').forEach(function (el) { el.addEventListener('click', fecharModal); }); document.addEventListener('keydown', function (evento) { if (evento.key === 'Escape' && !modal.hidden) { fecharModal(); } }); } // modal de denúncia var botaoAbrirDenuncia = document.querySelector('[data-abrir-denuncia]'); var modalDenuncia = document.getElementById('modalDenuncia'); if (botaoAbrirDenuncia && modalDenuncia) { var formDenuncia = modalDenuncia.querySelector('[data-form-denuncia]'); var TRANSICAO_MODAL_DENUNCIA_MS = 220; function abrirModalDenuncia() { modalDenuncia.hidden = false; document.body.style.overflow = 'hidden'; requestAnimationFrame(function () { requestAnimationFrame(function () { modalDenuncia.classList.add('modal-denuncia--visivel'); }); }); } function fecharModalDenuncia() { modalDenuncia.classList.remove('modal-denuncia--visivel'); document.body.style.overflow = ''; window.setTimeout(function () { modalDenuncia.hidden = true; }, TRANSICAO_MODAL_DENUNCIA_MS); } botaoAbrirDenuncia.addEventListener('click', abrirModalDenuncia); modalDenuncia.querySelectorAll('[data-fechar-denuncia]').forEach(function (el) { el.addEventListener('click', fecharModalDenuncia); }); document.addEventListener('keydown', function (evento) { if (evento.key === 'Escape' && !modalDenuncia.hidden) { fecharModalDenuncia(); } }); formDenuncia.addEventListener('submit', function (evento) { evento.preventDefault(); var botaoEnviar = formDenuncia.querySelector('.modal-denuncia__botao--enviar'); var tipoAlvo = formDenuncia.querySelector('input[name="tipo_alvo"]:checked').value; var motivo = formDenuncia.querySelector('select[name="motivo"]').value; var descricao = formDenuncia.querySelector('textarea[name="descricao"]').value.trim(); if (!motivo || descricao.length < 20) { window.alert(m('denuncia.erro_selecione_descreva', 'Selecione o motivo e descreva com pelo menos 20 caracteres.')); return; } botaoEnviar.disabled = true; fetch(modalDenuncia.getAttribute('data-url'), { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: [ 'tipo_alvo=' + encodeURIComponent(tipoAlvo), 'target_user_id=' + encodeURIComponent(modalDenuncia.getAttribute('data-target-user-id')), 'post_id=' + encodeURIComponent(modalDenuncia.getAttribute('data-post-id')), 'motivo=' + encodeURIComponent(motivo), 'descricao=' + encodeURIComponent(descricao), 'url_pagina=' + encodeURIComponent(window.location.href), 'csrf_token=' + encodeURIComponent(modalDenuncia.getAttribute('data-csrf')) ].join('&') }) .then(function (resposta) { return resposta.json(); }) .then(function (dados) { window.alert((dados && dados.mensagem) || m('denuncia.erro_enviar', 'Não foi possível enviar a denúncia.')); if (dados && dados.sucesso) { formDenuncia.reset(); fecharModalDenuncia(); } }) .catch(function () { window.alert(m('denuncia.erro_enviar_retry', 'Não foi possível enviar a denúncia. Tente de novo.')); }) .finally(function () { botaoEnviar.disabled = false; }); }); } // carrossel: sem loop, translateX no trilho var carrossel = document.querySelector('[data-carrossel]'); if (carrossel) { var trilho = carrossel.querySelector('[data-carrossel-trilho]'); var pontos = Array.prototype.slice.call(carrossel.querySelectorAll('[data-carrossel-ponto]')); var contador = carrossel.querySelector('[data-carrossel-contador]'); var setaAnterior = carrossel.querySelector('[data-carrossel-anterior]'); var setaProxima = carrossel.querySelector('[data-carrossel-proxima]'); var total = pontos.length; var indiceAtual = 0; function atualizar() { trilho.style.transform = 'translateX(-' + (indiceAtual * 100) + '%)'; pontos.forEach(function (p, i) { p.classList.toggle('perfil__carrossel-ponto--ativo', i === indiceAtual); }); if (contador) { contador.textContent = (indiceAtual + 1) + ' / ' + total; } if (setaAnterior) { setaAnterior.classList.toggle('perfil__carrossel-seta--oculta', indiceAtual === 0); } if (setaProxima) { setaProxima.classList.toggle('perfil__carrossel-seta--oculta', indiceAtual === total - 1); } } function irPara(indice) { indiceAtual = Math.max(0, Math.min(total - 1, indice)); atualizar(); } pontos.forEach(function (ponto, i) { ponto.addEventListener('click', function () { irPara(i); }); }); if (setaAnterior) { setaAnterior.addEventListener('click', function (evento) { evento.stopPropagation(); irPara(indiceAtual - 1); }); } if (setaProxima) { setaProxima.addEventListener('click', function (evento) { evento.stopPropagation(); irPara(indiceAtual + 1); }); } // Setas do teclado quando o carrossel está visível na tela. document.addEventListener('keydown', function (evento) { var retangulo = carrossel.getBoundingClientRect(); var visivel = retangulo.top < window.innerHeight && retangulo.bottom > 0; if (!visivel) { return; } if (evento.key === 'ArrowLeft') { irPara(indiceAtual - 1); } else if (evento.key === 'ArrowRight') { irPara(indiceAtual + 1); } }); // Swipe no touch (celular) -- também não passa dos limites. var toqueX = null; carrossel.addEventListener('touchstart', function (evento) { toqueX = evento.touches[0].clientX; }, { passive: true }); carrossel.addEventListener('touchend', function (evento) { if (toqueX === null) { return; } var deltaX = evento.changedTouches[0].clientX - toqueX; if (Math.abs(deltaX) > 40) { irPara(indiceAtual + (deltaX < 0 ? 1 : -1)); } toqueX = null; }); atualizar(); } // comentários: enviar/responder/curtir/paginar tudo via fetch var listaComentarios = document.querySelector('[data-comentarios-lista]'); var formComentario = document.querySelector('[data-form-comentario]'); var paginacaoComentarios = document.querySelector('[data-comentarios-paginacao]'); if (listaComentarios) { var podeInteragirComentarios = listaComentarios.getAttribute('data-pode-interagir') === '1'; var baseUrlComentarios = '/'; var fotoPadraoUrlComentarios = '/assets/img/sem-foto.png'; var viewerIdComentarios = parseInt(listaComentarios.getAttribute('data-viewer-id'), 10) || 0; var donoIdComentarios = parseInt(listaComentarios.getAttribute('data-dono-id'), 10) || 0; // Mesma lista que Emoticons::listar() usa pra montar o seletor no PHP // (ver perfil_comentario_html) -- precisa dela aqui pra montar o MESMO // seletor via JS quando uma resposta nova é criada sem reload de página. var emoticonsListaComentarios = [{"numero":1,"shortcode":":e1:","src":"/assets/img/emoticons/1.png"},{"numero":2,"shortcode":":e2:","src":"/assets/img/emoticons/2.png"},{"numero":3,"shortcode":":e3:","src":"/assets/img/emoticons/3.png"},{"numero":4,"shortcode":":e4:","src":"/assets/img/emoticons/4.png"},{"numero":5,"shortcode":":e5:","src":"/assets/img/emoticons/5.png"},{"numero":6,"shortcode":":e6:","src":"/assets/img/emoticons/6.png"},{"numero":7,"shortcode":":e7:","src":"/assets/img/emoticons/7.png"},{"numero":8,"shortcode":":e8:","src":"/assets/img/emoticons/8.png"},{"numero":9,"shortcode":":e9:","src":"/assets/img/emoticons/9.png"},{"numero":10,"shortcode":":e10:","src":"/assets/img/emoticons/10.png"},{"numero":11,"shortcode":":e11:","src":"/assets/img/emoticons/11.png"},{"numero":12,"shortcode":":e12:","src":"/assets/img/emoticons/12.png"},{"numero":13,"shortcode":":e13:","src":"/assets/img/emoticons/13.png"},{"numero":14,"shortcode":":e14:","src":"/assets/img/emoticons/14.png"},{"numero":15,"shortcode":":e15:","src":"/assets/img/emoticons/15.png"},{"numero":16,"shortcode":":e16:","src":"/assets/img/emoticons/16.png"},{"numero":17,"shortcode":":e17:","src":"/assets/img/emoticons/17.png"},{"numero":18,"shortcode":":e18:","src":"/assets/img/emoticons/18.png"},{"numero":19,"shortcode":":e19:","src":"/assets/img/emoticons/19.png"}]; // Espelha Emoticons::seletorHtml() (nucleo/Emoticons.php) em JS -- mesmo // markup, pra caixa de resposta criada dinamicamente (ver caixaRespostaHtml // em comentarioHtml abaixo) ficar idêntica à renderizada no PHP. function emojiSeletorHtml(idAlvo, ariaLabel) { var idAlvoEscapado = comentarioEscaparHtml(idAlvo); var ariaLabelEscapado = comentarioEscaparHtml(ariaLabel); var grade = emoticonsListaComentarios.map(function (item) { var codigo = comentarioEscaparHtml(item.shortcode); return ''; }).join(''); return '' +
'' +
'' +
'
';
}
// Painel de emoji (comentário principal e respostas): por padrão
// .emoticon-seletor__painel é position:absolute, e .conteudo tem
// overflow:hidden (ver assets/css/base.css) -- perto do fim do
// conteúdo do flog (onde o form de comentário e a última
// resposta normalmente ficam), o painel era cortado por esse
// overflow ao abrir ("engolido"). Sem nenhum ancestral com
// transform aqui (página normal, não modal com animação de
// abrir/fechar), position:fixed simples já resolve, sem
// precisar mover o painel de lugar no DOM feito na versão do
// postar-modal (ver assets/js/editor_rico.js, que precisa disso
// por causa do transform de .postar-modal__janela).
var painelEmojiComentarioAberto = null;
var gatilhoEmojiComentarioAberto = null;
function reposicionarEmojiComentario() {
if (!painelEmojiComentarioAberto || !gatilhoEmojiComentarioAberto) {
return;
}
var rect = gatilhoEmojiComentarioAberto.getBoundingClientRect();
painelEmojiComentarioAberto.style.top = (rect.bottom + 6) + 'px';
painelEmojiComentarioAberto.style.left = rect.left + 'px';
}
document.addEventListener('click', function (evento) {
var gatilho = evento.target.closest('[data-emoticon-gatilho]');
if (!gatilho) {
return;
}
var wrap = gatilho.closest('.perfil__form-comentario-campo-wrap, .perfil__comentario-resposta-campo-wrap');
if (!wrap) {
return;
}
var painel = wrap.querySelector('.emoticon-seletor__painel');
if (!painel) {
return;
}
painel.style.position = 'fixed';
painelEmojiComentarioAberto = painel;
gatilhoEmojiComentarioAberto = gatilho;
reposicionarEmojiComentario();
});
window.addEventListener('scroll', reposicionarEmojiComentario, { passive: true });
window.addEventListener('resize', reposicionarEmojiComentario);
// Autocomplete de @menção nos comentários -- mesmo esquema do
// editor (assets/js/editor_rico.js), mas pra ' + grade + '
