// ==UserScript==
// @name Zelluloza Dumper
// @version 0.4
// @description Сохраняет фрагмент книги, доступный для чтения в формате fb2. Книги защищенные максимальной защито сохранет в виде картинок
// @author MadDAD
// @include https://zelluloza.ru/books/*
// @namespace https://greasyfork.org/users/38856
// ==/UserScript==
//*************************************
var saveAs = saveAs || (function(view) {
"use strict";
// IE <10 is explicitly unsupported
if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
return;
}
var
doc = view.document
// only get URL when necessary in case Blob.js hasn't overridden it yet
,
get_URL = function() {
return view.URL || view.webkitURL || view;
},
save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a"),
can_use_save_link = "download" in save_link,
click = function(node) {
var event = new MouseEvent("click");
node.dispatchEvent(event);
},
is_safari = /constructor/i.test(view.HTMLElement),
is_chrome_ios = /CriOS\/[\d]+/.test(navigator.userAgent),
throw_outside = function(ex) {
(view.setImmediate || view.setTimeout)(function() {
throw ex;
}, 0);
},
force_saveable_type = "application/octet-stream"
// the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
,
arbitrary_revoke_timeout = 1000 * 40 // in ms
,
revoke = function(file) {
var revoker = function() {
if (typeof file === "string") { // file is an object URL
get_URL().revokeObjectURL(file);
} else { // file is a File
file.remove();
}
};
setTimeout(revoker, arbitrary_revoke_timeout);
},
dispatch = function(filesaver, event_types, event) {
event_types = [].concat(event_types);
var i = event_types.length;
while (i--) {
var listener = filesaver["on" + event_types[i]];
if (typeof listener === "function") {
try {
listener.call(filesaver, event || filesaver);
} catch (ex) {
throw_outside(ex);
}
}
}
},
auto_bom = function(blob) {
// prepend BOM for UTF-8 XML and text/* types (including HTML)
// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
return new Blob([String.fromCharCode(0xFEFF), blob], {
type: blob.type
});
}
return blob;
},
FileSaver = function(blob, name, no_auto_bom) {
if (!no_auto_bom) {
blob = auto_bom(blob);
}
// First try a.download, then web filesystem, then object URLs
var
filesaver = this,
type = blob.type,
force = type === force_saveable_type,
object_url, dispatch_all = function() {
dispatch(filesaver, "writestart progress write writeend".split(" "));
}
// on any filesys errors revert to saving with object URLs
,
fs_error = function() {
if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
// Safari doesn't allow downloading of blob urls
var reader = new FileReader();
reader.onloadend = function() {
var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
var popup = view.open(url, '_blank');
if (!popup) view.location.href = url;
url = undefined; // release reference before dispatching
filesaver.readyState = filesaver.DONE;
dispatch_all();
};
reader.readAsDataURL(blob);
filesaver.readyState = filesaver.INIT;
return;
}
// don't create more object URLs than needed
if (!object_url) {
object_url = get_URL().createObjectURL(blob);
}
if (force) {
view.location.href = object_url;
} else {
var opened = view.open(object_url, "_blank");
if (!opened) {
// Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
view.location.href = object_url;
}
}
filesaver.readyState = filesaver.DONE;
dispatch_all();
revoke(object_url);
};
filesaver.readyState = filesaver.INIT;
if (can_use_save_link) {
object_url = get_URL().createObjectURL(blob);
setTimeout(function() {
save_link.href = object_url;
save_link.download = name;
click(save_link);
dispatch_all();
revoke(object_url);
filesaver.readyState = filesaver.DONE;
});
return;
}
fs_error();
},
FS_proto = FileSaver.prototype,
saveAs = function(blob, name, no_auto_bom) {
return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
};
// IE 10+ (native saveAs)
if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
return function(blob, name, no_auto_bom) {
name = name || blob.name || "download";
if (!no_auto_bom) {
blob = auto_bom(blob);
}
return navigator.msSaveOrOpenBlob(blob, name);
};
}
FS_proto.abort = function() {};
FS_proto.readyState = FS_proto.INIT = 0;
FS_proto.WRITING = 1;
FS_proto.DONE = 2;
FS_proto.error =
FS_proto.onwritestart =
FS_proto.onprogress =
FS_proto.onwrite =
FS_proto.onabort =
FS_proto.onerror =
FS_proto.onwriteend =
null;
return saveAs;
}(
typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content
));
// `self` is undefined in Firefox for Android content script context
// while `this` is nsIContentFrameMessageManager
// with an attribute `content` that corresponds to the window
if (typeof module !== "undefined" && module.exports) {
module.exports.saveAs = saveAs;
} else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) {
define([], function() {
return saveAs;
});
}
//*************************************
function ImagePreloader(images, callback) {
// store the call-back
this.callback = callback;
// initialize internal state.
this.nLoaded = 0;
this.nProcessed = 0;
this.aImages = new Array();
// record the number of images.
this.nImages = images.length;
// for each image, call preload()
for (var i = 0; i < images.length; i++)
this.preload(images[i]);
}
ImagePreloader.prototype.preload = function(image) {
// create new Image object and add to array
var oImage = new Image();
this.aImages.push(oImage);
// set up event handlers for the Image object
oImage.onload = ImagePreloader.prototype.onload;
oImage.onerror = ImagePreloader.prototype.onerror;
oImage.onabort = ImagePreloader.prototype.onabort;
// assign pointer back to this.
oImage.oImagePreloader = this;
oImage.bLoaded = false;
// assign the .src property of the Image object
oImage.src = image;
};
ImagePreloader.prototype.onComplete = function() {
this.nProcessed++;
my_getbyid("bookpg").innerHTML = 'Загружено страниц: ' + this.nProcessed + ' из ' + this.nImages;
if (this.nProcessed == this.nImages) {
this.callback(this.aImages, this.nLoaded);
}
};
ImagePreloader.prototype.onload = function() {
this.bLoaded = true;
this.oImagePreloader.nLoaded++;
this.oImagePreloader.onComplete();
};
ImagePreloader.prototype.onerror = function() {
this.bError = true;
this.oImagePreloader.onComplete();
};
ImagePreloader.prototype.onabort = function() {
this.bAbort = true;
this.oImagePreloader.onComplete();
};
//*************************************
function GetUserToken() {
var UserToken = eval(document.getElementsByTagName("script")[8].text.split(';')[1].split(',')[5]);
if (UserToken === undefined) {
UserToken = eval(document.getElementsByTagName("script")[9].text.split(';')[1].split(',')[5]);
}
if (UserToken === undefined) {
var URL = document.getElementsByClassName("taglnk2");
for (var i = 0; i < URL.length; i++) {
var xmlhttp = getXmlHttp();
xmlhttp.open("GET", URL[i], false);
if (xmlhttp.readyState == 1) {
xmlhttp.setRequestHeader("Content-Type", "text/html");
xmlhttp.send();
}
if (xmlhttp.status != 200) {
alert(xmlhttp.status + ': ' + xmlhttp.statusText); // пример вывода: 404: Not Found
} else {
var doc = new DOMParser().parseFromString(xmlhttp.responseText, "text/html");
UserToken = eval(doc.getElementsByTagName("script")[6].text.split(';')[1].split(',')[5]);
if (UserToken === undefined)
UserToken = eval(doc.getElementsByTagName("script")[5].text.split(';')[1].split(',')[5]);
if (UserToken !== undefined)
return UserToken;
}
}
}
return UserToken;
}
function MyAjax(obj, notused, par1, par2, par3, par4, append) {
var xmlhttp = getXmlHttp();
var params = "op=" + encodeURIComponent(par1) + "&par1=" + encodeURIComponent(par2) + "&par2=" + encodeURIComponent(par3) + "&par4=" + encodeURIComponent(par4);
ajaxDone = 0;
if (xmlhttp.readyState == 0) {
xmlhttp.open("POST", "/ajaxcall/", false);
}
if (xmlhttp.readyState == 1) {
xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlhttp.send(params);
}
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == 200) {
ajaxDone = 1;
if (obj != "") {
var str = xmlhttp.responseText;
if (par1 == "getbook") {
chp = str.split("<END>");
return chp;
}
}
}
}
}
function AllBook(book) {
//var MaxProtection = my_getbyid("protected1").src !== '';
var MaxProtection = false;
if (!MaxProtection) {
var pageHtml = "";
var BookId = document.getElementById("gotobook").value;
var PartId = document.getElementsByClassName("taglnk2")[0].href.split('/')[4];
var UserToken = GetUserToken();
var BookTitle = document.getElementsByClassName("booklnk4")[1].childNodes[0].innerHTML;
var PartTitle = document.getElementsByClassName("taglnk2")[0].text;
var series = "";
var res = new RegExp('.*Серия: (.*)$', 'm').exec(document.getElementsByClassName('serie')[1].outerText);
if (res !== null)
series = res[1];
pageHtml = '<?xml version="1.0" encoding="utf-8"?><FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:xlink="http://www.w3.org/1999/xlink">';
pageHtml += '<description><title-info><genre/><author><first-name>' + document.getElementsByClassName("txt")[5].text + '</first-name><last-name/></author>';
pageHtml += '<book-title>' + BookTitle + '</book-title>';
pageHtml += '<annotation><p>' + document.getElementsByTagName("meta", "")[4].content + '</p></annotation>';
pageHtml += '<lang>ru</lang><src-lang>ru</src-lang>';
pageHtml += '<sequence name="' + series + '" number="" />';
pageHtml += '</title-info><document-info><author><nickname/><email/></author><version>2.0</version></document-info>';
pageHtml += '<publish-info><book-name>' + BookTitle + '</book-name></publish-info></description>';
pageHtml += '<body>';
pageHtml += '<title>';
pageHtml += '<p>' + BookTitle + '</p>';
pageHtml += '</title>';
//var chp = new Array();
var links = document.getElementsByClassName("taglnk2");
for (var i = 0; i < links.length; i++) {
book.childNodes[0].innerHTML = BookTitle + "(" + (i / links.length * 100) + "% загружено...)"
var item = links[i];
PartId = item.href.split('/')[5];
PartTitle = item.text;
var chp = MyAjax('booktext', '', 'getbook', BookId, PartId + ':0.0.1::0', UserToken, 0);
pageHtml += '<section><title><p>' + PartTitle + '</p></title>';
var Z = chp[0].split(/\n/);
for (var j in Z) {
pageHtml += '<p>' + DS('', Z[j]).replace('\r', '').replace('\n', '').replace('[ctr][gry]- Конец фрагмента -[/][/]', '') + '</p>\n';
}
pageHtml += '</section>';
};
pageHtml += '</body></FictionBook>';
var file = new File([pageHtml], BookTitle + '(' + PartTitle + ').fb2', {
type: "text/plain;charset=utf-8"
});
saveAs(file);
}
}
function unprotect(text) {
var pageHtml = null;
if (document.location.hostname == "zelluloza.ru") {
var BookId = document.getElementById("gotobook").value;
var PartId = document.getElementsByClassName("taglnk2")[0].href.split('/')[4];
var UserToken = GetUserToken();
var BookTitle = document.getElementsByClassName("booklnk4")[1].childNodes[0].innerHTML;
var PartTitle = document.getElementsByClassName("taglnk2")[0].text;
if (document.getElementsByClassName("booklnk4")) {
var series = "";
var res = new RegExp('.*Серия: (.*)$', 'm').exec(document.getElementsByClassName('serie')[1].outerText);
if (res !== null)
series = res[1];
pageHtml = '<?xml version="1.0" encoding="utf-8"?><FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:xlink="http://www.w3.org/1999/xlink">';
pageHtml += '<description><title-info><genre/><author><first-name>' + document.getElementsByClassName("txt")[5].text + '</first-name><last-name/></author>';
pageHtml += '<book-title>' + BookTitle + '</book-title>';
pageHtml += '<annotation><p>' + document.getElementsByTagName("meta", "")[4].content + '</p></annotation>';
pageHtml += '<lang>ru</lang><src-lang>ru</src-lang>';
pageHtml += '<sequence name="' + series + '" number="" />';
pageHtml += '</title-info><document-info><author><nickname/><email/></author><version>2.0</version></document-info>';
pageHtml += '<publish-info><book-name>' + BookTitle + '</book-name></publish-info></description>';
pageHtml += '<body>';
pageHtml += '<title>';
pageHtml += '<p>' + BookTitle + '</p>';
pageHtml += '</title>';
pageHtml += '<section><title><p>' + PartTitle + '</p></title>';
}
var MaxProtection = my_getbyid("protected1").src !== '';
if (!MaxProtection) {
var Z = chp[0].split(/\n/);
for (var j in Z) {
pageHtml += '<p>' + DS('', Z[j]).replace('\r', '').replace('\n', '').replace('[ctr][gry]- Конец фрагмента -[/][/]', '') + '</p>\n';
}
pageHtml += '</section></body></FictionBook>';
var file = new File([pageHtml], BookTitle + '(' + PartTitle + ').fb2', {
type: "text/plain;charset=utf-8"
});
saveAs(file);
} else {
var pages = new Array();
oCanvas = my_getbyid("cnv1");
oCtx = oCanvas.getContext("2d");
var page = 0;
var loaded = false;
for (page = 0; page <= maxpg; page += pagessize) {
pages[page] = "/get/" + base64_encode(bookid + ":" + chapterid + ":" + pagessize + ":" + page);
}
var binaryImages = "";
var pp = new ImagePreloader(pages, function(aImages, nLoaded) {
aImages.forEach(function(item, i, aImages) {
if (item.bLoaded) {
oCtx.drawImage(item, 0, 0);
pageHtml += '<image xlink:href="#page_' + i + '.jpg"/>\n';
binaryImages += '<binary content-type="image/png" id="page_' + i + '.jpg">\n' + oCanvas.toDataURL("image/png", 1.0).replace('data:image/png;base64,', '') + '\n</binary>';
}
});
pageHtml += '</section></body>';
pageHtml += binaryImages;
pageHtml += '</FictionBook>';
var file = new File([pageHtml], BookTitle + '(' + PartTitle + ').fb2', {
type: "text/plain;charset=utf-8"
});
saveAs(file);
});
}
}
}
unsafeWindow.wait_for_text = function() {
var text = document.getElementById("bookpgm");
if (null === text) {
var book = document.getElementsByClassName("booklnk4")[1];
if (null === book)
alert('No copyable text found!');
else {
var button = document.createElement("BUTTON");
button.innerHTML = 'Get fb2';
button.addEventListener('click', function() {
AllBook(book);
}, true);
book.parentNode.insertBefore(button, book);
}
return;
}
if (text.innerHTML.length > 100) {
var button = document.createElement("BUTTON");
button.innerHTML = 'Get fb2';
button.addEventListener('click', function() {
unprotect(text);
}, true);
text.parentNode.insertBefore(button, text);
} else
window.setTimeout("wait_for_text()", 1000);
};
window.setTimeout("wait_for_text()", 1000);