/*
* @title (仮)Wizball の詳細ページを保存用に、ちょっといじる
* @description (仮)Wizball の詳細ページを保存用に、ちょっといじる
* @include https://m.wizball.io/questions/*
* @license MIT License http://opensource.org/licenses/MIT
* @javascript_url *
*/
/*
・コメントを全て展開して、全文表示されるようにした
・コメントにアイコンをつけた
・Voter のアイコンを表示するようにした
・投稿時刻を日時にした
・下部の「人気の質問」を消した
・表示幅を、最大 640px にした
*/
(async _ => {
if (location.hostname != "m.wizball.io") {
alert("Please use at m.wizball.io !");
return;
}
const d_ = document;
const ts = Date.now();
const msg = d_.querySelector(".question_logo");
async function display_comments() {
// console.log("!!! load comments !!!");
const list = d_.querySelectorAll(".comment_list");
for (let i = 0 ; i < list.length ; ++i) {
const first_item = list[i].querySelector(".comment_txt");
if (! first_item) { // 質問はコメントがなくても UL 要素がある
continue;
}
const orig_url = first_item.href;
const url_base = orig_url.replace(/questions/, "apis/v1/questions");
const comments = [];
let page = 1;
while (true) {
const url = url_base + "?page=" + page + "&size=50&sort=LATEST";
const resp = await fetch(url);
const json = await resp.json();
if (json.length > 0) {
comments.push(... json);
page += 1;
} else {
break;
}
}
// console.log(comments);
list[i].innerHTML = "";
comments.forEach(c => {
let img;
if (c.author.image) {
img = [
'<img src="https://obs.line-scdn.net/',
c.author.image.hash,
'/s150" class="user_20" title="',
c.author.nickname,
'">'
].join("");
} else {
img = '<img src="" class="user_20" title="' + c.author.nickname + '">';
}
const item = Object.assign(d_.createElement("li"), {
className: "comment_item",
innerHTML: [
'<div class="comment_item_inner"><div class="profile_img comment_icon">',
img,
'</div><a href="/users/',
c.author.id,
'" class="user_id">',
c.author.nickname,
'</a> <div class="comment_txt type_space">',
c.text,
'</a></div>',
].join(""),
});
list[i].appendChild(item);
});
const comment_value = list[i].parentNode.querySelector(".comment_list ~ .comment_value");
// console.log(comment_value);
list[i].parentNode.insertBefore(comment_value, list[i]);
}
}
async function load_q(qid) {
const resp = await fetch("/apis/v1/questions/" + qid);
const json = await resp.json();
return json;
}
async function load_a(qid) {
const map = {};
for (let p = 1 ; p <= 300 ; ++p) {
const resp = await fetch("/apis/v1/questions/" + qid + "/answers?page=" + p + "&size=50&sort=POPULAR");
const json = await resp.json();
if (json.length == 0) {
break;
}
json.forEach(a => {
map[a.id] = a;
});
}
return map;
}
async function load_v(a) {
let ub;
if (a.questionId) { // answer
ub = "/apis/v1/questions/" + a.questionId + "/answers/" + a.id + "/voters?size=50&sort=LATEST&";
} else { // question
ub = "/apis/v1/questions/" + a.id + "/votes?size=50&sort=LATEST&";
}
const list = [];
for (let p = 1 ; p <= 300 ; ++p) {
const resp = await fetch(ub + "page=" + p);
const json = await resp.json();
if (json.length == 0) {
break;
}
list.push(... json);
}
return list;
}
function modify_time(e, q) {
const qt = e.querySelector(".user_info_detail.date");
qt.innerHTML = (new Date(q.createdAt)).toLocaleString();
}
async function display_voters(e, a) {
const eopt = e.querySelector(".qna_option_area");
const ea = d_.createElement("div");
ea.className = "vote_area";
eopt.parentNode.insertBefore(ea, eopt);
// eopt.parentNode.insertBefore(eul, eopt);
const v = await load_v(a);
ea.innerHTML = '<div class="vote_value">Vote : ' + v.length + '</div>';
const eul = d_.createElement("ul");
ea.appendChild(eul);
eul.className = "voter_list";
v.forEach(v => {
const li = eul.appendChild(d_.createElement("li"));
li.className = "voter_item profile_img";
// li.innerHTML = v.author.nickname + ", ";
if (v.author.image) {
li.innerHTML = '<img src="https://obs.line-scdn.net/' + v.author.image.hash + '/s150" class="user_20" title="' + v.author.nickname + '">';
} else {
li.innerHTML = '<img src="" class="user_20" title="' + v.author.nickname + '">';
}
});
}
async function modify_data() {
msg.innerHTML = "質問データを取得中...";
const m = /\/questions\/(\d+)/.exec(location.href);
const qid = m[1];
const q = await load_q(qid);
const qe = d_.querySelector(".qna_area.question");
modify_time(qe, q);
msg.innerHTML = "質問のVoterを取得中...";
// const qv = await load_v(q);
await display_voters(qe, q);
msg.innerHTML = "回答データを取得中...";
const a = await load_a(qid);
const ae = d_.querySelectorAll(".qna_area.answer .answer_item");
// console.log(ae);
msg.innerHTML = "回答のVoterを取得中...";
for (let i = 0 ; i < ae.length ; ++i) {
const bc = ae[i].querySelector(".btn_comment");
if (bc) {
const m = /answers\/(\d+)/.exec(bc.href);
const aid = m[1];
modify_time(ae[i], a[aid]);
// const av = await load_v(a);
await display_voters(ae[i], a[aid]);
}
// else PENALTY
}
}
// function x() {
// const imgs = d_.querySelectorAll("img");
// const img = imgs[1];
// console.log(img);
// let canvas = Object.assign(document.createElement("canvas"), {
// width: img.width,
// height: img.height,
// });
// let ctx = canvas.getContext('2d');
// ctx.drawImage(img, 0, 0);
// const uri = canvas.toDataURL(); // The operation is insecure.
// console.log(uri);
// img.src = uri;
// }
async function modify_window() {
msg.innerHTML = "コメントを読み込み中...";
await display_comments();
await modify_data();
// x();
msg.innerHTML = "★完了★";
setTimeout(function() {
msg.innerHTML = "";
}, 3000);
console.log("!!! " + ((Date.now() - ts)/1000.0) + " sec");
}
const more_btn = d_.querySelector(".qna_area.answer .btn_more_answer");
if (more_btn) {
d_.head.appendChild(Object.assign(d_.createElement("style"), {
innerHTML: [
'body {',
' max-width: 640px;',
'}',
'.wrap.home_end .content .qna_area_inner .comment_area .comment_item_inner .comment_txt {',
' max-height: none;',
'}',
'.comment_item_inner .comment_icon, .voter_list .voter_item {',
' width: 20px;',
' height: 20px;',
' margin-right: 0.5ex;',
'}',
'.comment_item_inner .comment_icon {',
' float: left;',
'}',
'.user_20 {',
' border-radius: 50%;',
' width: 100%;',
'}',
'.wrap.home_end .content .question_logo {',
' font-size: inherit;',
' color: inherit;',
' font-size: 15px;',
' color: #959aa5;',
'}',
'.wrap.home_end .content .qna_area_inner .comment_area .comment .comment_value {',
' padding-top: 0;',
' padding-bottom: 10px;',
'}',
'.qna_area.question .vote_area {',
' padding-left: 22px;',
'}',
'.vote_area {',
' padding-top: 14px;',
'}',
'.vote_value {',
' padding-bottom: 10px;',
'}',
'.vote_area {',
' color: #959aa5;',
' font-size: 15px;',
'}',
'.voter_item {',
' display: inline-block;',
'}',
'.related_question_area {',
' display: none;',
'}',
].join(""),
}));
let n = 0;
const answer_list = d_.querySelector(".answer_list");
// https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
const MutationObserver = window.MutationObserver || window.WebkitMutationObserver;
const observer = new MutationObserver(async function (records) {
n += 1;
// console.log(n);
if (more_btn.style.display != "none") {
more_btn.click();
if (n > 10) {
observer.disconnect();
}
} else {
await modify_window();
}
});
observer.observe(answer_list, { childList: true, /* subtree: true */ });
if (more_btn.style.display != "none") {
msg.innerHTML = "回答を読み込み中...";
more_btn.click();
} else {
await modify_window();
}
}
})();