/*
* @title H::H lightni.ng filter
* @description Hatena Haiku spam filtering w/ lightni.ng
* @include *://h.hatena.ne.jp/*
* @license The MIT License https://opensource.org/licenses/MIT
* @javascript_url
*/
// nitpicking
// via
// http://let.hatelabo.jp/austinburk/let/hLHU6PPgg9Ya
// e.g.
// http://h.hatena.ne.jp
(() => {
'use strict';
if (window.self !== window.top) return; // @noframes in Vanilla
// TODO async use w/ await, Promise and .all/.race for timeout
let blacklist;
const syncBlockingFetch = () => {
const xhr = new XMLHttpRequest();
/* XXX asynchronous only
xhr.timeout = 3000;
xhr.responseType = 'json';
*/
// TODO unreachable?
const cancel = (evt) => {
throw Error(evt.type, xhr.status, xhr.readyState, evt);
};
xhr.addEventListener('load', (evt) => {
if (xhr.status !== 200) {
cancel(evt);
}
const THRESHOULD = 5;
// TBD omit JSON.parse
/*/
blacklist = Array.prototype.concat(...
Object.entries(JSON.parse(document.body.textContent))
.filter(([id, score]) => score >= THRESHOULD)
);
// .includes('HATENA_ID')
/*/
//const blacklist = new Map(Object.entries(JSON.parse(document.body.textContent))
blacklist = new Map(Object.entries(JSON.parse(xhr.response))
.filter(([, score]) => score >= THRESHOULD)
);
// .has('HATENA_ID')
//*/
});
[
'abort',
'timeout',
'error',
].forEach(type => {
xhr.addEventListener(type, evt => {
cancel(evt);
});
});
// Access-Control-Allow-Origin: http://h.hatena.ne.jp
const FILTER_URL = 'https://haikuantispam.lightni.ng/api/recent_scores.json';
// XXX synchronize
xhr.open('GET',FILTER_URL, false);
xhr.send();
};
syncBlockingFetch();
const wipeout = (ctx) => {
if (!ctx.querySelectorAll) return;
ctx.querySelectorAll([
'.entry.tl-entry'
]).forEach(entry => {
const poster = entry.querySelector('.username a[href][title]');
if (!poster) return;
const id = poster.title.slice('id:'.length);
if (blacklist.has(id)) {
//*/
entry.style.backgroundColor = 'red';
//entry.innerHTML = `
//<p><a href="https://haikuantispam.lightni.ng/id/${id}" title="spam-detail">
//https://haikuantispam.lightni.ng/id/${id}</a></p>`;
/*/
//encty.display = 'none';
//*/
}
});
};
const timeline = document.body.querySelector('.entries');
if (!timeline) return;
wipeout(timeline);
new MutationObserver(records => {
records.forEach(record => {
wipeout(record.target);
});
}).observe(timeline, { childList: true, subtree: true, });
})();