var are_filters = false; var hide_word = new Set(); var hide_name = new Set(); var hide_id = new Set(); var shobon_ver = "v0.4+"; function shobon() { boardName = document.body.dataset.brd; var where = document.body.classList; var inThread = where.contains("threadpage"); if (!inThread) { /* Create settings link */ var box = document.getElementsByClassName("links")[0]; box.appendChild(document.createTextNode(" | ")); var slnk = document.createElement("a"); slnk.href = "#"; slnk.innerHTML = "Configurar"; slnk.addEventListener("click", shobonSettings); box.appendChild(slnk); } if (localStorage.getItem("shobon_on") == "false") return; else console.log("Running shobon " + shobon_ver); var newRepliesCounter = 0; var shobon_time = localStorage.getItem("shobon_time"); if (shobon_time != "false") { if (boardName == "world") week = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"]; else if (boardName == "2d") week = ["日", "月", "火", "水", "木", "金", "土"]; else week = ["dom", "lun", "mar", "mie", "jue", "vie", "sab"]; } if (where == "threads") { if (shobon_time != "false") { var dt = document.getElementsByClassName("date"); for (var i = 0; i < dt.length; i++) { dt[i].addEventListener("mouseover", function(e) { this.title = "Hace " + timeAgo(this.dataset.unix); }); dt[i].textContent = localTime(dt[i].dataset.unix); } } return; } if (localStorage.getItem("shobon_usefilters") != "false") { loadFilters(); } var threadList = document.getElementsByClassName("thread"); for (var i = 0; i < threadList.length; i++) { var threadId; var thread = threadList[i]; var replyList = thread.getElementsByClassName("reply"); if (inThread) { threadId = document.getElementsByName("parent")[0].value; } else { threadId = thread.getElementsByTagName("input").parent.value; } var lastReplyN = replyList[replyList.length - 1].attributes["data-n"].value; if ( localStorage.getItem("shobon_newposts") == "true" && localStorage.getItem(boardName + "_" + threadId) == null ) { localStorage.setItem(boardName + "_" + threadId, lastReplyN); } var lastSeen = localStorage.getItem(boardName + "_" + threadId); var newRepliesInThread = 0; for (var e = 0; e < replyList.length; e++) { var reply = replyList[e]; var message = reply.getElementsByClassName("msg")[0]; var id = 0; if (shobon_time != "false") { var date = reply.getElementsByClassName("date")[0]; date.addEventListener("mouseover", function(e) { this.title = "Hace " + timeAgo(this.dataset.unix); }); if (date.textContent.includes("ID:")) id = date.textContent.split(" ")[1]; date.textContent = localTime(date.dataset.unix, id); } if (localStorage.getItem("shobon_newposts") == "true") { var replyId = reply.attributes["data-n"].value; var isNewReply = parseInt(lastSeen) < parseInt(replyId); if (isNewReply) { newRepliesCounter++; newRepliesInThread++; reply.children[0].innerHTML += " Nuevo!"; } } // ocultar mensajes que coinciden con lista negra if (are_filters) { checkBlackList(reply); } // colorear IDs if (localStorage.getItem("shobon_ids") == "true") { paintIds(reply); } // insertar imágenes if (localStorage.getItem("shobon_embedimg") == "true") { embedImg(reply); } } if (newRepliesInThread > 0 && !inThread) { var btnNew = document.createElement("span"); btnNew.dataset.id = boardName + "_" + threadId; btnNew.dataset.last = lastReplyN; btnNew.textContent = "Marcar como leído"; btnNew.setAttribute( "style", "font-weight:bold; background:#81a2be; padding:5px; border-radius:5px; float:right; margin-bottom:10px;" ); btnNew.onclick = function() { localStorage.setItem(this.dataset.id, this.dataset.last); this.hidden = true; }; thread.getElementsByClassName("threadlinks")[0].appendChild(btnNew); } } if (newRepliesCounter > 0 && !inThread) { var banner = document.createElement("span"); banner.onclick = function() { this.hidden = true; }; banner.setAttribute( "style", "font-weight:bold; background:#8c9440; padding:8px; border-radius:30px; float:right; position:fixed; bottom:10px; right:10px" ); banner.textContent = "Nuevas respuestas: " + newRepliesCounter; document.body.appendChild(banner); } if (localStorage.getItem("shobon_newposts") == "true" && inThread) { localStorage.setItem(boardName + "_" + threadId, lastReplyN); } } function on_checked(e) { localStorage.setItem(e.target.id, e.target.checked); } function createCheckbox(name, label, def) { var lbl = document.createElement("label"); var chk = document.createElement("input"); chk.type = "checkbox"; chk.id = name; chk.onchange = on_checked; lbl.appendChild(chk); lbl.insertAdjacentHTML("beforeend", " " + label + " "); var checked = localStorage.getItem(name); if (checked !== null) { chk.checked = checked == "true"; } else { chk.checked = def; } return lbl; } function createOption(name, label) { var opt = document.createElement("option"); opt.value = name; opt.text = label; return opt; } function createButton(label, func) { var btn = document.createElement("button"); btn.type = "button"; btn.textContent = label; btn.onclick = func; return btn; } function createTh(label, w) { var th = document.createElement("th"); th.textContent = label; th.width = w; return th; } function timeAgo(timestamp) { var time = Math.round(Date.now() / 1000); var el = time - timestamp; if (el == 0) return "un instante"; else if (el == 1) return "un segundo"; else if (el < 60) return el + " segundos"; else if (el < 120) return "un minuto"; else if (el < 3600) return Math.round(el / 60) + " minutos"; else if (el < 7200) return "una hora"; else if (el < 86400) return Math.round(el / 3600) + " horas"; else if (el < 172800) return "un día"; else if (el < 2628000) return Math.round(el / 86400) + " días"; else if (el < 5256000) return "un mes"; else if (el < 31536000) return Math.round(el / 2628000) + " meses"; else if (el > 31535999) return "más de un año"; } function localTime(timestamp, id) { id = id || 0; var lcl = new Date(timestamp * 1000); lcl = ("0" + lcl.getDate()).slice(-2) + "/" + ("0" + (lcl.getMonth() + 1)).slice(-2) + "/" + lcl.getFullYear().toString().slice(-2) + "(" + week[lcl.getDay()] + ")" + ("0" + lcl.getHours()).slice(-2) + ":" + ("0" + lcl.getMinutes()).slice(-2) + ":" + ("0" + lcl.getSeconds()).slice(-2); if (id) lcl = lcl + " " + id; return lcl; } function loadFilters() { var filters = JSON.parse(localStorage.getItem("shobon_filters")); if (filters) { are_filters = true; hide_word = new Set(filters.word); hide_name = new Set(filters.name); hide_id = new Set(filters.id); } } function saveFilters() { var filters = { word: Array.from(hide_word), name: Array.from(hide_name), id: Array.from(hide_id) }; localStorage.setItem("shobon_filters", JSON.stringify(filters)); } function deleteFilter(e) { var tr = this.parentElement.parentElement; var name = tr.dataset.name; var type = tr.dataset.type; switch (type) { case "word": hide_word.delete(name); break; case "name": hide_name.delete(name); break; case "id": hide_id.delete(name); break; } saveFilters(); tr.remove(); } function addFilter(e) { var name = document.getElementById("txt_filter").value; var type = document.getElementById("lst_type").value; if (!name) { return; } switch (type) { case "word": hide_word.add(name); break; case "name": hide_name.add(name); break; case "id": hide_id.add(name); break; } addToFilterTable(name, type); saveFilters(); document.getElementById("txt_filter").value = ""; } function addToFilterTable(name, type) { var dict = { word: "Palabra", name: "Nombre/Tripcode", id: "ID" }; var table = document.getElementById("tbl_filters"); var td_type = document.createElement("td"); td_type.textContent = dict[type]; var td_name = document.createElement("td"); td_name.textContent = name; var td_btn = document.createElement("td"); td_btn.appendChild(createButton("X", deleteFilter)); var tr = document.createElement("tr"); tr.dataset.type = type; tr.dataset.name = name; tr.appendChild(td_type); tr.appendChild(td_name); tr.appendChild(td_btn); table.appendChild(tr); } function shobonSettings(e) { e.preventDefault(); var titlebox = document.getElementById("titlebox"); var box = document.getElementById("settings"); if (box) { box.hidden = !box.hidden; } else { box = document.createElement("div"); box.id = "settings"; box.className = "innerbox"; box.style.textAlign = "center"; var p = document.createElement("div"); p.appendChild(createCheckbox("shobon_on", "Activar extensión", true)); p.appendChild(createCheckbox("shobon_ids", "Colorear IDs", false)); p.appendChild(createCheckbox("shobon_embedimg", "Insertar imágenes miniatura", false)); p.appendChild(createCheckbox("shobon_newposts", "Destacar mensajes nuevos", false)); p.appendChild(createCheckbox("shobon_time", "Convertir fechas a hora local", true)); p.appendChild(createCheckbox("shobon_backlink", "Mostrar quién ha citado un post", true)); p.appendChild(createCheckbox("shobon_preview", "Previsualizar citas", true)); p.appendChild(createCheckbox("shobon_usefilters", "Activar filtros", false)); /*var a = document.createElement("a"); a.href = "#"; a.textContent = "[Editar filtros]"; a.addEventListener("click", function() { var x = document.getElementById("filters"); x.hidden = !x.hidden; }); p.appendChild(a);*/ box.appendChild(p); var title2 = document.createElement("h6"); title2.textContent = "Filtros"; title2.style.fontSize = "18px"; title2.style.margin = "0.5em 0"; box.appendChild(title2); box.appendChild(document.createTextNode("Filtrar mensajes por: ")); var lst_type = document.createElement("select"); lst_type.id = "lst_type"; lst_type.appendChild(createOption("word", "Palabra")); lst_type.appendChild(createOption("name", "Nombre/Tripcode")); lst_type.appendChild(createOption("id", "ID")); box.appendChild(lst_type); var txt_filter = document.createElement("input"); txt_filter.id = "txt_filter"; txt_filter.type = "text"; box.appendChild(txt_filter); box.appendChild(createButton("Agregar", addFilter)); var tbl_filters = document.createElement("table"); tbl_filters.id = "tbl_filters"; tbl_filters.border = "1"; tbl_filters.style.margin = "0 auto"; var row = document.createElement("tr"); row.appendChild(createTh("Tipo", 150)); row.appendChild(createTh("Filtro", 300)); row.appendChild(createTh("", 75)); tbl_filters.appendChild(row); box.appendChild(tbl_filters); var msg = document.createElement("a"); msg.style.display = "block"; msg.href = "#"; msg.textContent = "Actualizar para ver cambios"; msg.style.marginTop = "1em"; msg.addEventListener("click", function() { location.reload(); }); box.appendChild(msg); titlebox.appendChild(box); var i; hide_word.forEach(v => { addToFilterTable(v, "word"); }); hide_name.forEach(v => { addToFilterTable(v, "name"); }); hide_id.forEach(v => { addToFilterTable(v, "id"); }); } } function checkBlackList(reply) { var i; // Check words var low = reply.children[1].textContent.toLowerCase(); hide_word.forEach(v => { console.log(v); if (low.includes(v.toLowerCase())) { hidepost(reply); } }); // Check name/trip var msg_name = reply.firstElementChild .getElementsByClassName("name")[0] .textContent.toLowerCase(); hide_name.forEach(v => { if (msg_name.includes(hide_name[i])) { hidepost(reply); } }); // Check ID var date_div = reply.firstElementChild.getElementsByClassName("date")[0] .textContent; var id_index = date_div.indexOf("ID:"); if (id_index != -1) { var id = date_div.substr(id_index + 3); hide_id.forEach(v => { if (id.includes(v)) { hidepost(reply); } }); } } var currentSel = null; function paintIds(reply) { var dateId = reply.getElementsByClassName("date")[0]; if (dateId.textContent.includes("ID:")) { var postDate = dateId.textContent.split("ID:")[0]; var userId = dateId.textContent.split("ID:")[1]; var idColor = toHex(userId).substring(0, 6); var reverseColor = invertColor(idColor); var lastChar = userId.substring(userId.length - 1, userId.length); dateId.innerHTML = postDate + "ID:" + userId + ""; dateId.getElementsByClassName("uid")[0].addEventListener("click", markId, false); } } function markId(e) { var uid = this.textContent.slice(0, -1).replace(":", "_"); var sel = document.getElementsByClassName("sel"); var len = sel.length; for (i = 0; i < len; i++) { var prev = String(sel[0].className); sel[0].className = sel[0].className.toString().replace(/ sel/i, ""); } if (currentSel == uid) { currentSel = null; return; } currentSel = uid; var tosel = document.getElementsByClassName(uid); for (j = 0; j < tosel.length; j++) { tosel[j].className = tosel[j].className + " sel"; } } function embedImg(reply) { var links = reply.getElementsByTagName("a"); for (i = 0; i < links.length && i < 5; i++) { var url = links[i].href; if (url.startsWith("https://i.imgur.com")) { var ext = url.lastIndexOf("."); var thumburl = url.slice(0, ext) + "s" + url.slice(ext); } else if (links[i].href.startsWith("https://pbs.twimg.com")) { var thumburl = links[i].href + ":thumb"; } else continue; var thumb = document.createElement("img"); thumb.src = thumburl; links[i].appendChild(document.createElement("br")); links[i].appendChild(thumb); } } function toHex(str) { var hex = ""; for (var i = 0; i < str.length; i++) { hex += "" + (str.charCodeAt(i) + 125).toString(16); } return hex; } function invertColor(hex) { if (hex.indexOf("#") === 0) { hex = hex.slice(1); } // convert 3-digit hex to 6-digits. if (hex.length === 3) { hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; } if (hex.length !== 6) { throw new Error("Invalid HEX color."); } // invert color components var r = (255 - parseInt(hex.slice(0, 2), 16)).toString(16), g = (255 - parseInt(hex.slice(2, 4), 16)).toString(16), b = (255 - parseInt(hex.slice(4, 6), 16)).toString(16); // pad each with zeros and return return padZero(r) + padZero(g) + padZero(b); } function padZero(str, len) { len = len || 2; var zeros = new Array(len).join("0"); return (zeros + str).slice(-len); } document.addEventListener("DOMContentLoaded", shobon, false);