(function (){
'use strict';
var root=document.getElementById('hwpfc-comments');
if(!root){ return; }
var CFG={
ajax: root.getAttribute('data-ajax')||(typeof HWPFC!=='undefined' ? HWPFC.ajax:''),
postId: root.getAttribute('data-post')||(typeof HWPFC!=='undefined' ? HWPFC.postId:''),
forum: root.getAttribute('data-forum')||(typeof HWPFC!=='undefined' ? HWPFC.forum:'')
};
if(!CFG.ajax||!CFG.postId){ return; }
var box=root.querySelector('.hwpfc-box');
var state={ user: null, csrf: '', login_url: '', logout_url: '', topic_url: '', topic_id: 0 };
var commentsById={};
function esc(s){
var d=document.createElement('div');
d.textContent=(s==null ? '':String(s));
return d.innerHTML;
}
function fmtDate(iso){
if(!iso){ return ''; }
var d=new Date(iso);
if(isNaN(d.getTime())){ return ''; }
try {
return d.toLocaleDateString('es-ES', { day: 'numeric', month: 'long', year: 'numeric' }) +
' ' + d.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' });
} catch (e){
return d.toLocaleString();
}}
function avatar(url, name){
if(url){
return '<img src="' + esc(url) + '" alt="' + esc(name) + '" loading="lazy">';
}
return '<img src="data:image/svg+xml;utf8,' +
encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44"><rect width="44" height="44" fill="#cfcfcf"/></svg>') +
'" alt="">';
}
var ICON_LIKE='<svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true"><path fill="currentColor" d="M2 21h2.5V9H2v12zm20-9.6c0-.9-.8-1.7-1.7-1.7h-5.3l.8-3.9v-.3c0-.4-.2-.7-.4-1l-.9-.9-5.6 5.6c-.3.3-.5.7-.5 1.2v8.1c0 .9.8 1.7 1.7 1.7h7.5c.7 0 1.3-.4 1.6-1l2.5-5.9c.1-.2.1-.4.1-.6v-1.6z"/></svg>';
var ICON_DISLIKE='<svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true"><path fill="currentColor" d="M22 3h-2.5v12H22V3zM2 12.6c0 .9.8 1.7 1.7 1.7H9l-.8 3.9v.3c0 .4.2.7.4 1l.9.9 5.6-5.6c.3-.3.5-.7.5-1.2V5.5c0-.9-.8-1.7-1.7-1.7H6.4c-.7 0-1.3.4-1.6 1L2.3 10.7c-.1.2-.1.4-.1.6v1.3z"/></svg>';
function reactBtns(c){
var likeOn=c.my==='like' ? ' is-on':'';
var disOn=c.my==='dislike' ? ' is-on':'';
return '<div class="hwpfc-react">' +
'<button type="button" class="hwpfc-vote hwpfc-like' + likeOn + '" data-id="' + esc(c.id) + '" data-type="like" title="Me gusta">' +
ICON_LIKE + '<span class="hwpfc-n">' + esc(c.likes||0) + '</span></button>' +
'<button type="button" class="hwpfc-vote hwpfc-dislike' + disOn + '" data-id="' + esc(c.id) + '" data-type="dislike" title="No me gusta">' +
ICON_DISLIKE + '<span class="hwpfc-n">' + esc(c.dislikes||0) + '</span></button>' +
'</div>';
}
function commentHTML(c){
commentsById[c.id]=c;
var name=c.url
? '<a class="hwpfc-c-name" href="' + esc(c.url) + '" target="_blank" rel="noopener">' + esc(c.name) + '</a>'
: '<span class="hwpfc-c-name">' + esc(c.name) + '</span>';
return '<li class="hwpfc-item" data-id="' + esc(c.id) + '">' +
'<div class="hwpfc-c-avatar">' + avatar(c.photo, c.name) + '</div>' +
'<div class="hwpfc-c-body">' +
'<div class="hwpfc-c-head">' + name + '<span class="hwpfc-c-date">' + esc(fmtDate(c.date)) + '</span></div>' +
'<div class="hwpfc-c-text">' + (c.html||'') + '</div>' +
'<div class="hwpfc-c-foot">' + reactBtns(c) +
'<button type="button" class="hwpfc-reply" data-id="' + esc(c.id) + '">' +
'<span class="hwpfc-reply-ico">&#8617;</span> Responder</button>' +
'</div></div></li>';
}
function buildQuote(c){
var contents=c.html||'';
return '<blockquote class="ipsQuote" data-ipsquote="" ' +
'data-ipsquote-userid="' + esc(c.author_id||0) + '" ' +
'data-ipsquote-username="' + esc(c.name) + '" ' +
'data-ipsquote-contentapp="forums" data-ipsquote-contenttype="forums" ' +
'data-ipsquote-contentclass="forums_Topic" ' +
'data-ipsquote-contentid="' + esc(state.topic_id||0) + '" ' +
'data-ipsquote-contentcommentid="' + esc(c.id) + '">' +
'<header class="ipsQuote_citation">' + esc(c.name) + ' dijo:</header>' +
'<div class="ipsQuote_contents">' + contents + '</div>' +
'</blockquote><p><br></p>';
}
function quoteComment(id){
var c=commentsById[id];
if(!c){ return; }
if(!state.user){
window.location.href=state.login_url;
return;
}
var ed=editor();
if(!ed){ return; }
insertHtmlAtCursor(buildQuote(c));
ed.focus();
var form=box.querySelector('.hwpfc-form');
if(form){ form.scrollIntoView({ behavior: 'smooth', block: 'center' });}}
var SVG={
ul: '<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="9" y1="6" x2="20" y2="6"/><line x1="9" y1="12" x2="20" y2="12"/><line x1="9" y1="18" x2="20" y2="18"/><circle cx="4" cy="6" r="1.4" fill="currentColor" stroke="none"/><circle cx="4" cy="12" r="1.4" fill="currentColor" stroke="none"/><circle cx="4" cy="18" r="1.4" fill="currentColor" stroke="none"/></svg>',
ol: '<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="10" y1="6" x2="20" y2="6"/><line x1="10" y1="12" x2="20" y2="12"/><line x1="10" y1="18" x2="20" y2="18"/><text x="2" y="8" font-size="7" fill="currentColor" stroke="none">1</text><text x="2" y="14" font-size="7" fill="currentColor" stroke="none">2</text><text x="2" y="20" font-size="7" fill="currentColor" stroke="none">3</text></svg>',
quote: '<svg viewBox="0 0 24 24" width="17" height="17" fill="currentColor"><path d="M6 7C4.3 7 3 8.3 3 10s1.3 3 3 3c.2 0 .4 0 .5-.1-.3 1.3-1.4 2.3-2.5 2.6v2c2.8-.5 5-3 5-6.5V10c0-1.7-1.3-3-3-3zm9 0c-1.7 0-3 1.3-3 3s1.3 3 3 3c.2 0 .4 0 .5-.1-.3 1.3-1.4 2.3-2.5 2.6v2c2.8-.5 5-3 5-6.5V10c0-1.7-1.3-3-3-3z"/></svg>',
link: '<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.5.5l3-3a5 5 0 0 0-7-7l-1.5 1.5"/><path d="M14 11a5 5 0 0 0-7.5-.5l-3 3a5 5 0 0 0 7 7l1.5-1.5"/></svg>',
img: '<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.6"/><path d="M21 15l-5-5L4 21"/></svg>',
upload: '<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>'
};
var TOOLBAR =
'<div class="hwpfc-toolbar">' +
'<button type="button" class="hwpfc-tool hwpfc-tool--txt" data-cmd="bold" title="Negrita"><b>B</b></button>' +
'<button type="button" class="hwpfc-tool hwpfc-tool--txt" data-cmd="italic" title="Cursiva"><i>I</i></button>' +
'<button type="button" class="hwpfc-tool hwpfc-tool--txt" data-cmd="underline" title="Subrayado"><u>U</u></button>' +
'<button type="button" class="hwpfc-tool hwpfc-tool--txt" data-cmd="strikeThrough" title="Tachado"><s>S</s></button>' +
'<span class="hwpfc-tool-sep"></span>' +
'<button type="button" class="hwpfc-tool" data-cmd="insertUnorderedList" title="Lista de puntos">' + SVG.ul + '</button>' +
'<button type="button" class="hwpfc-tool" data-cmd="insertOrderedList" title="Lista numerada">' + SVG.ol + '</button>' +
'<button type="button" class="hwpfc-tool" data-cmd="formatBlock" data-val="blockquote" title="Cita">' + SVG.quote + '</button>' +
'<button type="button" class="hwpfc-tool" data-act="link" title="Enlace">' + SVG.link + '</button>' +
'<span class="hwpfc-tool-sep"></span>' +
'<button type="button" class="hwpfc-tool" data-act="imgurl" title="Insertar imagen por URL">' + SVG.img + '</button>' +
'<button type="button" class="hwpfc-tool" data-act="upload" title="Subir imagen">' + SVG.upload + '</button>' +
'</div>';
function topComposer(){
if(state.user){
var ph=avatar(state.user.photo, state.user.name);
return '<div class="hwpfc-form">' +
'<div class="hwpfc-avatar">' + ph + '</div>' +
'<div class="hwpfc-fields">' +
'<div class="hwpfc-me">Comentando como <strong>' + esc(state.user.name) + '</strong>. ' +
'(<a class="hwpfc-logout">Salir</a>)</div>' +
TOOLBAR +
'<div class="hwpfc-editor" contenteditable="true" data-placeholder="Escribe un comentario…"></div>' +
'<input type="file" class="hwpfc-file" accept="image/png,image/jpeg,image/webp,image/gif" style="display:none">' +
'<div class="hwpfc-msg-slot"></div>' +
'<div class="hwpfc-actions">' +
'<button type="button" class="hwpfc-btn hwpfc-send">Publicar comentario</button></div>' +
'</div></div>';
}
return '<div class="hwpfc-login">' +
'<p>Para comentar necesitas tu cuenta del foro. Cualquiera puede leer los comentarios.</p>' +
'<a class="hwpfc-btn hwpfc-do-login" href="' + esc(state.login_url) + '">Iniciar sesión con HardwarePremium</a>' +
'</div>';
}
function render(comments){
commentsById={};
var html=topComposer();
if(comments&&comments.length){
html +='<ul class="hwpfc-list">';
comments.forEach(function (c){ html +=commentHTML(c); });
html +='</ul>';
}else{
html +='<div class="hwpfc-empty">Aún no hay comentarios. ¡Sé el primero!</div>';
}
if(state.topic_url){
html +='<div class="hwpfc-foot"><a href="' + esc(state.topic_url) + '" target="_blank" rel="noopener">Ver el hilo en el foro »</a></div>';
}
box.innerHTML=html;
box.setAttribute('data-state', 'ready');
wire();
}
function editor(){ return box.querySelector('.hwpfc-editor'); }
function insertHtmlAtCursor(html){
var ed=editor();
if(!ed){ return; }
ed.focus();
var sel=window.getSelection();
if(sel&&sel.rangeCount&&ed.contains(sel.anchorNode)){
var range=sel.getRangeAt(0);
range.deleteContents();
var el=document.createElement('div');
el.innerHTML=html;
var frag=document.createDocumentFragment(), node, last;
while ((node=el.firstChild)){ last=frag.appendChild(node); }
range.insertNode(frag);
if(last){
range=range.cloneRange();
range.setStartAfter(last);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}}else{
ed.innerHTML +=html;
}}
function wire(){
var logout=box.querySelector('.hwpfc-logout');
if(logout){
logout.addEventListener('click', function (){ window.location.href=state.logout_url; });
}
var send=box.querySelector('.hwpfc-send');
if(send){ send.addEventListener('click', submit); }
box.querySelectorAll('.hwpfc-tool').forEach(function (btn){
btn.addEventListener('mousedown', function (e){ e.preventDefault(); });
btn.addEventListener('click', function (){
var cmd=btn.getAttribute('data-cmd');
var act=btn.getAttribute('data-act');
var ed=editor();
if(ed){ ed.focus(); }
if(cmd){
var val=btn.getAttribute('data-val')||null;
try { document.execCommand (cmd, false, val); } catch (e){}}else if(act==='link'){
var url=prompt('Dirección del enlace (https://…):', 'https://');
if(url&&/^https?:\/\//i.test(url)){
try { document.execCommand ('createLink', false, url); } catch (e){}}
}else if(act==='imgurl'){
var iu=prompt('URL de la imagen o GIF (https://…):', 'https://');
if(iu&&/^https?:\/\//i.test(iu)){
insertHtmlAtCursor('<img src="' + esc(iu) + '" alt="">');
}}else if(act==='upload'){
var f=box.querySelector('.hwpfc-file');
if(f){ f.click(); }}
});
});
var file=box.querySelector('.hwpfc-file');
if(file){
file.addEventListener('change', function (){
if(file.files&&file.files[0]){ uploadImage(file.files[0]); file.value=''; }});
}}
function showMsg(kind, text){
var slot=box.querySelector('.hwpfc-msg-slot');
if(!slot){ return; }
slot.innerHTML='<div class="hwpfc-msg hwpfc-msg--' + (kind==='ok' ? 'ok':'err') + '">' + esc(text) + '</div>';
}
function uploadImage(f){
if(f.size > 8 * 1024 * 1024){ showMsg('err', 'La imagen supera el máximo de 8 MB.'); return; }
showMsg('ok', 'Subiendo imagen…');
var fd=new FormData();
fd.append('file', f);
fd.append('csrf', state.csrf);
fetch(CFG.ajax + '?action=hwpfc_upload', {
method: 'POST', credentials: 'same-origin', body: fd
}).then(function (r){ return r.json().then(function (j){ return { ok: r.ok, j: j };});})
.then(function (res){
if(!res.ok||!res.j||!res.j.success||!res.j.data||!res.j.data.url){
var m=(res.j&&res.j.data&&res.j.data.message) ? res.j.data.message:'No se pudo subir la imagen.';
showMsg('err', m);
return;
}
insertHtmlAtCursor('<img src="' + esc(res.j.data.url) + '" alt="">');
showMsg('ok', 'Imagen añadida.');
})
.catch(function (){ showMsg('err', 'Error de red al subir la imagen.'); });
}
function submit(){
var ed=editor();
var btn=box.querySelector('.hwpfc-send');
if(!ed){ return; }
var html=ed.innerHTML.trim();
var plain=(ed.textContent||'').trim();
if(!plain&&html.indexOf('<img')===-1){ showMsg('err', 'Escribe algo antes de publicar.'); return; }
btn.setAttribute('disabled', 'disabled');
btn.textContent='Publicando…';
var body=new URLSearchParams();
body.set('post', CFG.postId);
body.set('text', html);
body.set('csrf', state.csrf);
fetch(CFG.ajax + '?action=hwpfc_post', {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: body.toString()
}).then(function (r){
return r.json().then(function (j){ return { ok: r.ok, j: j };});
}).then(function (res){
btn.removeAttribute('disabled');
btn.textContent='Publicar comentario';
if(!res.ok||!res.j||!res.j.success){
var m=(res.j&&res.j.data&&res.j.data.message) ? res.j.data.message:'No se pudo publicar.';
showMsg('err', m);
return;
}
ed.innerHTML='';
showMsg('ok', 'Comentario publicado.');
var list=box.querySelector('.hwpfc-list');
if(!list){
var empty=box.querySelector('.hwpfc-empty');
if(empty){ empty.remove(); }
list=document.createElement('ul');
list.className='hwpfc-list';
var foot=box.querySelector('.hwpfc-foot');
if(foot){ box.insertBefore(list, foot); }else{ box.appendChild(list); }}
list.insertAdjacentHTML('beforeend', commentHTML(res.j.data.comment));
}).catch(function (){
btn.removeAttribute('disabled');
btn.textContent='Publicar comentario';
showMsg('err', 'Error de red, inténtalo de nuevo.');
});
}
function load(){
fetch(CFG.ajax + '?action=hwpfc_load&post=' + encodeURIComponent(CFG.postId), {
credentials: 'same-origin'
}).then(function (r){ return r.json(); })
.then(function (data){
state.user=data.user;
state.csrf=data.csrf||'';
state.login_url=data.login_url||'';
state.logout_url=data.logout_url||'';
state.topic_url=data.topic_url||'';
state.topic_id=data.topic_id||0;
render(data.comments||[]);
var hash=window.location.hash;
if(hash.indexOf('hwpfc')!==-1){
root.scrollIntoView({ behavior: 'smooth' });
}})
.catch(function (){
box.innerHTML='<div class="hwpfc-msg hwpfc-msg--err">No se pudieron cargar los comentarios.</div>';
});
}
box.addEventListener('click', function (e){
if(!e.target.closest){ return; }
var rep=e.target.closest('.hwpfc-reply');
if(rep){ quoteComment(rep.getAttribute('data-id')); return; }
var vote=e.target.closest('.hwpfc-vote');
if(vote){ doVote(vote); }});
function doVote(btn){
if(!state.user){ window.location.href=state.login_url; return; }
var id=btn.getAttribute('data-id');
var type=btn.getAttribute('data-type');
var foot=btn.closest('.hwpfc-c-foot');
if(foot.getAttribute('data-busy')==='1'){ return; }
foot.setAttribute('data-busy', '1');
var body=new URLSearchParams();
body.set('post', id);
body.set('type', type);
body.set('csrf', state.csrf);
fetch(CFG.ajax + '?action=hwpfc_react', {
method: 'POST', credentials: 'same-origin',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: body.toString()
}).then(function (r){ return r.json().then(function (j){ return { ok: r.ok, j: j };});})
.then(function (res){
foot.removeAttribute('data-busy');
if(!res.ok||!res.j||!res.j.success||!res.j.data){ return; }
var d=res.j.data;
var likeB=foot.querySelector('.hwpfc-like');
var disB=foot.querySelector('.hwpfc-dislike');
if(likeB){ likeB.querySelector('.hwpfc-n').textContent=d.likes; likeB.classList.toggle('is-on', d.my==='like'); }
if(disB){ disB.querySelector('.hwpfc-n').textContent=d.dislikes; disB.classList.toggle('is-on', d.my==='dislike'); }
if(commentsById[id]){ commentsById[id].likes=d.likes; commentsById[id].dislikes=d.dislikes; commentsById[id].my=d.my; }})
.catch(function (){ foot.removeAttribute('data-busy'); });
}
load();
})();