/*
* @title backup Hatena Space
* @description backup Hatena Space. see http://a-kuma3.hatenablog.com/entry/hatena_space_backup
* @include http://space.hatena.ne.jp/*
* @license MIT License
*/
(function(){
/*
[OPTIONS]
only_my_entry:
remove other users posts.
need login.
expand_hatena_star:
slower 10-50% to exract.
larger 10% or more.
embed_stylesheet:
need 218 KB more.
*/
var opt = {
only_my_entry: false,
expand_hatena_star: false,
embed_stylesheet: true,
};
// check location
re_check = new RegExp("^http://space\.hatena\.ne\.jp/~/\\d+/\\d+#?$");
if (! re_check.test(location.href)) {
console.log('"' + location.href + '"');
alert("use at topics page @ space.hatena.ne.jp.");
return;
}
var hspace_pages = [];
var hspace_stylesheet = "";
var hspace_title = "";
var need_break = false;
var is_last_page = false;
var page = 1;
var max_page = -1;
var last_entry_number = -1;
var d_ = document;
var b_ = d_.body;
var style_url = "http://space.hatena.ne.jp/css/master.css?47ab9942";
var t_start = new Date();
function setStyle(target, prop) {
for (k in prop) {
target.style[k] = prop[k];
}
}
function removeNode(e) {
e.parentNode.removeChild(e);
}
function $id(id) {
return d_.getElementById(id);
}
function createProgressView() {
if ($id("progress")) {
removeNode($id("progress").parentNode);
}
var panel = d_.createElement("DIV");
setStyle(panel, {
display: "inline-block",
position: "fixed",
top: "1ex",
right: "1ex",
backgroundColor: "green",
border: "2px inset darkgreen",
color: "white",
padding: "0.5ex 2ex",
zIndex: 1000,
});
var m = d_.createElement("DIV");
m.id = "progress";
m.innerHTML = " ";
panel.appendChild(m);
var btn = d_.createElement("BUTTON");
btn.innerHTML = "BREAK";
btn.style.marginTop = "0.5em";
btn.onclick = function() {
need_break = true;
};
panel.appendChild(btn);
b_.appendChild(panel);
}
var xhr = new XMLHttpRequest();
xhr.onload = function(e) {
if (e.target.status <= 200) {
treatResponse(e.target.response);
}
};
function is_spam(entry) {
/*
TODO:
another spam pattern
*/
var re = new RegExp("^https?://\\S+$");
if (re.test(entry.textContent)) {
return true;
}
return false;
}
function treatResponse(resp) {
hspace_title = resp.querySelector("head title").innerHTML;
var entries = resp.querySelector("div#entries");
if (max_page == -1) {
var last_entry = entries.querySelector("div.entry");
last_entry_number = parseInt(last_entry.dataset.entryNumber, 10);
max_page = Math.ceil(last_entry_number / 20.0);
}
// remove entries of other users
if (opt.only_my_entry) {
try {
var myid = resp.documentElement.dataset.userName;
var re = new RegExp("/" + myid + "/$");
Array.from(entries.querySelectorAll("div.entry div.author > a[href]")).forEach(function(e) {
if (! re.test(e.href)) {
removeNode(e.parentNode.parentNode.parentNode);
}
});
} catch (ex) {
console.error("ERROR: " + e.target.responseURL);
console.error(ex);
}
}
// remove spam entries
Array.from(entries.querySelectorAll("div.entry > div.body > div.plain")).forEach(function(e) {
if (is_spam(e)) {
console.log("maybe spam: " + e.parentNode.parentNode.querySelector("div.author").textContent.replace(/\s/g, ""));
removeNode(e.parentNode.parentNode);
}
});
// change user link space to profile of user
{
var sel = [
"a.author-image-link",
"div.meta > .author > a",
"ul.comment-list > li > a",
"ul.comment-list > li > .user-comment > a",
"a.id-call",
].join(",");
Array.from(entries.querySelectorAll(sel)).forEach(function(e) {
e.href = e.href.replace(/space\.hatena\.ne\.jp/, "profile.hatena.ne.jp");
});
}
// display all comments
Array.from(entries.querySelectorAll("div.comments > ul.comment-list li.hide")).forEach(function(e) {
e.classList.remove("hide");
});
// remove node (reduce size)
{
var sel = [
// post form
"div.comments > form.post-comment",
"div.reply-action > form.quick-reply",
// action element
"div.comments > a.expand-comments",
"ul.comment-list li div.comment-data > a.delete-comment",
"div.reply-action > a.open-entry-menu",
"div.reply-action > ul.entry-menu",
// star button
"div.reply-action button.star-add-button",
].join(",");
Array.from(entries.querySelectorAll(sel)).forEach(function(e) {
removeNode(e);
});
}
// Hatena Star
if (opt.expand_hatena_star) {
var keys = [];
Array.from(entries.querySelectorAll(".star-list-container")).forEach(function(e) {
keys.push(e.dataset.resourceKey);
});
var url = "http://space.hatena.ne.jp/-/api/star?" + keys.map(function(e) {
return "resource_key[]=" + e;
}).join("&");
var xx = new XMLHttpRequest();
xx.open('GET', url, true);
xx.onload = (function() {
var ec = entries;
return function(ev) {
var a;
eval("a=" + ev.target.responseText);
a.resources.forEach(function(e) {
//console.log(e);
e.stars.forEach(function(ee) {
var stcn = ec.querySelector('span.star-list-container[data-resource-key="' + ee.resource_key + '"]');
var box = d_.createElement("span");
box.className = "star-box";
box.style.backgroundImage="url(" + ee.user.profile_image + ")";
var img = d_.createElement("a");
img.className = "star-image";
img.href = "http://s.hatena.ne.jp" + ee.user.path;
img.innerHTML = ee.user.name;
box.appendChild(img);
stcn.appendChild(box);
});
});
if (is_last_page) {
finishLoading();
}
};
})();
xx.send(null);
} else {
var sel = [
"div.reply-action div.star-container",
].join(",");
Array.from(entries.querySelectorAll(sel)).forEach(function(e) {
removeNode(e);
});
}
// post time
Array.from(entries.querySelectorAll("time")).forEach(function(e) {
function d2(n) {
return n < 10 ? "0"+n : n;
}
var t = new Date(parseInt(e.dataset.epochMilliseconds));
e.innerHTML = t.getFullYear() + "/" + d2(t.getMonth()+1) + "/" + d2(t.getDate()) + " " +
d2(t.getHours()) + ":" + d2(t.getMinutes()) + ":" + d2(t.getSeconds());
});
hspace_pages.push(entries);
if (need_break) {
$id("progress").innerHTML = "BREAK Loading !";
return;
}
is_last_page = !resp.querySelector("#timeline-pager-loading");
if (is_last_page) {
$id("progress").innerHTML = "FINISH Loading";
if (! opt.expand_hatena_star) {
setTimeout(finishLoading, 0);
}
} else {
page += 1;
loadEntries();
}
}
function finishLoading() {
console.log("*** FINISH ***");
displayResultView();
console.log("display initialized");
displaySource();
console.log("source displayed");
var lapse = (new Date().getTime() - t_start.getTime()) / 1000;
console.log("=== COMPLETE === " + [
lapse + " sec",
max_page + " pages",
last_entry_number + " posts",
].join(", "));
}
function displayResultView() {
b_.innerHTML = "";
b_.classList.remove("new-space");
// source area
var sourceArea = d_.createElement("div");
setStyle(sourceArea, {
display: "inline-block",
width: "20em",
padding: "0.5ex 2ex",
backgroundColor: "palegoldenrod",
verticalAlign: "top",
});
function createUnitArea(dest, title, id, viewtype) {
var e_txt = d_.createElement("textarea");
e_txt.id = "source-" + id;
e_txt.rows = 5;
setStyle(e_txt, {
width: "100%",
display: "block",
});
var e_title = d_.createElement("a");
e_title.innerHTML = title;
e_title.href = "#";
setStyle(e_title, {
display: "block",
textDecoration: "underline",
borderLeft: "1em solid saddlebrown",
paddingLeft: "1ex",
marginTop: "0.5ex",
});
e_title.onclick = function() {
var viewArea = $id("view-area");
var dd_ = viewArea.contentWindow.document;
dd_.open(viewtype, "replace");
// dd_.charset = "Shift_JIS";
dd_.write(this.nextSibling.value);
dd_.close();
return false;
};
dest.appendChild(e_title);
dest.appendChild(e_txt);
}
createUnitArea(sourceArea, "entries HTML" , "entries" , "text/html");
createUnitArea(sourceArea, "style sheet" , "stylesheet", "text/plain");
createUnitArea(sourceArea, "images URL list", "imagelist" , "text/plain");
createUnitArea(sourceArea, "images HTML" , "imagepage" , "text/html");
b_.appendChild(sourceArea);
// preview frame
var viewArea = d_.createElement("iframe");
viewArea.id = "view-area";
setStyle(viewArea, {
width: "600px",
height: window.innerHeight + "px",
display: "inline-block",
verticalAlign: "top",
});
b_.appendChild(viewArea);
}
function code_stylesheet() {
var code = [
'<!-- need replace -->',
'<link rel="stylesheet" href="' + style_url + '" />',
].join("\n");
if (opt.embed_stylesheet) {
code = [
'<style>',
hspace_stylesheet,
'</style>',
].join("\n");
}
var star_image = [
"<style>",
".star-image {",
"background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAMAAADW3miqAAAABGdBTUEAALGPC/xhBQAAAh9QTFRF1dXVlZWV3d3dxcXFyMjI/v7+9PT04+PjZGRk/f399fX1+/v76urqzc3N/f396enp/v7+/Pz8/v7+xMTE/v7+YWFhSUlJ4uLi+Pj4xcXF7+/vmJiYBgYG/Pz8AAAAtbW1+fn56Ojo3t7e/f39/v7+OTk57u7uq6urjY2NbGxs+/v7IyMj/Pz8+fn59vb2/f39v7+/9/f30tLSwcHB8fHx4uLij4+P1dXV9fX1urq6+fn5i4uL9vb24eHh+fn57u7uwcHB6+vrwcHB6enpX19fUlJS7u7udHR09PT07+/v+Pj4Ly8v5+fnycnJ/Pz81dXV6Ojo/v7++vr6+fn5sbGx8/PzFhYWpaWl39/fBwcH3t7es7OzycnJzc3N1dXV6enp/v7+zs7OcHBw8/Pz/f39/Pz8/f39/f39/f39/Pz8+vr6wMDAZmZm/f39u7u79PT02tra29vb+vr6+Pj48fHxeHh49vb23d3d/v7+////+Pj4R0dHtLS09vb2/Pz8////9vb2/Pz89/f3/v7+3d3d7Ozs/v7+/v7+AAAA////////AAAA/Pz8AAAAAAAAAAAAAAAA+/v7tLS0AAAAAAAA/v7+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////gMlA/AAAALR0Uk5TPAxMLBznb1sX7pXISUfmafjg+l/8OiqD1XuwNCjuGEzno3zg9BvGT0E06iTw5Mr3W9hmTsdxUDazMKhL1xGJeW+2XpQrOMU5uZzQG6ty3pN73mLYW7sXR58mXApMLhiv8kRA0/Wx4fPMvpJlQfRtiJib6XHORDgm8P6aNmbcrPpUmn/YDxvk9R78/RzxECknJO5pGSX7FQ0XDh0bIBQTEQkjFhoPHyILBxIIBQwKBgQDAgEAUjpwbwAAAfJJREFUOMuN1PVfFFEQAPATFLERsYMQUMHuRERs7MLu7pYQFVQaBa77brterX+ge7vqce/27pjfdj/fz8zszNvn+D2OcKS9WV6ZG7lK5+dG5fy8BblQ2Vyem1OSAy109jDMouyoegkXj0iLl2ZDd7fyy1hNkCqyoZfOfkkDQK1amxl9agvGVYCQyG5yZULD7bHRQRkRAjVpv8sGvX77auRdLCBpCtF1DNQLF4837R5Iot7hgydHng79/BVkwirAuq4TJPvi/bxzV33znQETnT93wAA85x91RwQRGYkMBWWP5A34g7FLt58n0NlDMd7PeEMRVo0Cy+gEQ6B5WMntDXBPzHJHG5kQq2qyqCBsGYspYlTwDF59YDV++JgvCqAh/hOTEYygeO3+v6874vvbCxUEX3+YHMEpQcF26Mq9MXPqu6xBbJPoccowb96KwrSC+A018RM7ZLot8uUzvbst1rDHGNSRtuCNESoVga006lsf0lK7IsoLGq1bEVeVFITBXhqtXpmOZtGoZpVboMqByTRaUxsSoLmx79+wuWcC8noptIELG40T/P7rj67OD9A4w0RxdFNocwLtOfOoJfHwcef0CQiB/CkU2tcT3lZ3Onn2pxVMlAtnUuhGw/bu1D9y0tQZsyn0zObuKirOeImN66aziT+yRUV0UfcynAAAAABJRU5ErkJggg==) !important;",
"}",
"</style>",
].join("\n");
return code + star_image;
}
function displaySource() {
// contents
var e = $id("source-entries");
e.value = [].concat([
"<!DOCTYPE html>",
"<html>",
'<head>',
'<meta charset="utf-8">',
'<title>' + hspace_title + "(アーカイブ)" + '</title>',
code_stylesheet(),
'</head>',
'<body>',
'<div id="main-container" style="width: 538px;">',
'<div class="container">',
'<div id="entry-container" class="entry-box">',
'<div id="entries">',
],
hspace_pages.map(function(e) {
return e.innerHTML;
}),
[
'</div>',
'</div>',
'</div>',
'</div>',
'</body>',
"</html>"
]).join("\n");
// image list
var image_list = [];
hspace_pages.forEach(function(e) {
Array.from(e.querySelectorAll('a[href^="http://cdn.mogile.archive.st-hatena.com/v1/image/"]')).forEach(function(a) {
image_list.push(a.href);
});
});
if (image_list.length > 0) {
// only url
e = $id("source-imagelist");
e.value = image_list.join("\n");
// thumbnail page
e = $id("source-imagepage");
e.value = [
"<!DOCTYPE html>",
"<html>",
"<head>",
"<title>",
hspace_title + "(アーカイブ:画像)",
"</title>",
"<style>",
"body {padding: 1ex;}",
"img {padding: 1ex;border: 1px solid gray; max-width: 100%;}",
"</style>",
"</head>",
"<body>",
image_list.map(function(e) {
return '<img src="' + e + '">';
}).join("\n"),
"</body>",
"</html>"].join("\n");
}
// stylesheet
var e = $id("source-stylesheet");
e.value = hspace_stylesheet;
}
function loadStyleSheet() {
var xx = new XMLHttpRequest();
xx.open('GET', style_url, true);
xx.onload = function(ev) {
hspace_stylesheet = ev.target.responseText;
};
xx.send(null);
}
function loadEntries() {
var msg = "Loading page:" + page + " / " + (max_page == -1 ? "?" : max_page) + " ...";
console.log(msg);
$id("progress").innerHTML = msg;
xhr.open('GET', location.pathname + "?p=" + page, true);
xhr.responseType = 'document';
xhr.send(null);
};
// opt.only_my_entry = confirm([
// "Only my POST ?",
// "",
// " OK : only my posts (need login)",
// " Cancel : all posts"
// ].join("\n"));
createProgressView();
loadEntries();
loadStyleSheet();
}());