node(s) and append them into the `nodes` variable.
// Some articles' DOM structures might look like
//
var brNodes = doc.querySelectorAll("div > br");
if (brNodes.length) {
var set = new Set(nodes);
[].forEach.call(brNodes, function (node) {
set.add(node.parentNode);
});
nodes = Array.from(set);
}
var score = 0;
// This is a little cheeky, we use the accumulator 'score' to decide what to return from
// this callback:
return [].some.call(nodes, function (node) {
if (!options.visibilityChecker(node)) {
return false;
}
var matchString = node.className + " " + node.id;
if (REGEXPS.unlikelyCandidates.test(matchString) &&
!REGEXPS.okMaybeItsACandidate.test(matchString)) {
return false;
}
if (node.matches("li p")) {
return false;
}
var textContentLength = node.textContent.trim().length;
if (textContentLength < options.minContentLength) {
return false;
}
score += Math.sqrt(textContentLength - options.minContentLength);
if (score > options.minScore) {
return true;
}
return false;
});
}
if (true) {
module.exports = isProbablyReaderable;
}
/***/ }),
/***/ "./node_modules/@mozilla/readability/Readability.js":
/***/ ((module) => {
/*eslint-env es6:false*/
/*
* Copyright (c) 2010 Arc90 Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* This code is heavily based on Arc90's readability.js (1.7.1) script
* available at: http://code.google.com/p/arc90labs-readability
*/
/**
* Public constructor.
* @param {HTMLDocument} doc The document to parse.
* @param {Object} options The options object.
*/
function Readability(doc, options) {
// In some older versions, people passed a URI as the first argument. Cope:
if (options && options.documentElement) {
doc = options;
options = arguments[2];
} else if (!doc || !doc.documentElement) {
throw new Error("First argument to Readability constructor should be a document object.");
}
options = options || {};
this._doc = doc;
this._docJSDOMParser = this._doc.firstChild.__JSDOMParser__;
this._articleTitle = null;
this._articleByline = null;
this._articleDir = null;
this._articleSiteName = null;
this._attempts = [];
// Configurable options
this._debug = !!options.debug;
this._maxElemsToParse = options.maxElemsToParse || this.DEFAULT_MAX_ELEMS_TO_PARSE;
this._nbTopCandidates = options.nbTopCandidates || this.DEFAULT_N_TOP_CANDIDATES;
this._charThreshold = options.charThreshold || this.DEFAULT_CHAR_THRESHOLD;
this._classesToPreserve = this.CLASSES_TO_PRESERVE.concat(options.classesToPreserve || []);
this._keepClasses = !!options.keepClasses;
this._serializer = options.serializer || function(el) {
return el.innerHTML;
};
this._disableJSONLD = !!options.disableJSONLD;
// Start with all flags set
this._flags = this.FLAG_STRIP_UNLIKELYS |
this.FLAG_WEIGHT_CLASSES |
this.FLAG_CLEAN_CONDITIONALLY;
// Control whether log messages are sent to the console
if (this._debug) {
let logNode = function(node) {
if (node.nodeType == node.TEXT_NODE) {
return `${node.nodeName} ("${node.textContent}")`;
}
let attrPairs = Array.from(node.attributes || [], function(attr) {
return `${attr.name}="${attr.value}"`;
}).join(" ");
return `<${node.localName} ${attrPairs}>`;
};
this.log = function () {
if (typeof dump !== "undefined") {
var msg = Array.prototype.map.call(arguments, function(x) {
return (x && x.nodeName) ? logNode(x) : x;
}).join(" ");
dump("Reader: (Readability) " + msg + "\n");
} else if (typeof console !== "undefined") {
let args = Array.from(arguments, arg => {
if (arg && arg.nodeType == this.ELEMENT_NODE) {
return logNode(arg);
}
return arg;
});
args.unshift("Reader: (Readability)");
console.log.apply(console, args);
}
};
} else {
this.log = function () {};
}
}
Readability.prototype = {
FLAG_STRIP_UNLIKELYS: 0x1,
FLAG_WEIGHT_CLASSES: 0x2,
FLAG_CLEAN_CONDITIONALLY: 0x4,
// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
ELEMENT_NODE: 1,
TEXT_NODE: 3,
// Max number of nodes supported by this parser. Default: 0 (no limit)
DEFAULT_MAX_ELEMS_TO_PARSE: 0,
// The number of top candidates to consider when analysing how
// tight the competition is among candidates.
DEFAULT_N_TOP_CANDIDATES: 5,
// Element tags to score by default.
DEFAULT_TAGS_TO_SCORE: "section,h2,h3,h4,h5,h6,p,td,pre".toUpperCase().split(","),
// The default number of chars an article must have in order to return a result
DEFAULT_CHAR_THRESHOLD: 500,
// All of the regular expressions in use within readability.
// Defined up here so we don't instantiate them repeatedly in loops.
REGEXPS: {
// NOTE: These two regular expressions are duplicated in
// Readability-readerable.js. Please keep both copies in sync.
unlikelyCandidates: /-ad-|ai2html|banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|footer|gdpr|header|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote/i,
okMaybeItsACandidate: /and|article|body|column|content|main|shadow/i,
positive: /article|body|content|entry|hentry|h-entry|main|page|pagination|post|text|blog|story/i,
negative: /-ad-|hidden|^hid$| hid$| hid |^hid |banner|combx|comment|com-|contact|foot|footer|footnote|gdpr|masthead|media|meta|outbrain|promo|related|scroll|share|shoutbox|sidebar|skyscraper|sponsor|shopping|tags|tool|widget/i,
extraneous: /print|archive|comment|discuss|e[\-]?mail|share|reply|all|login|sign|single|utility/i,
byline: /byline|author|dateline|writtenby|p-author/i,
replaceFonts: /<(\/?)font[^>]*>/gi,
normalize: /\s{2,}/g,
videos: /\/\/(www\.)?((dailymotion|youtube|youtube-nocookie|player\.vimeo|v\.qq)\.com|(archive|upload\.wikimedia)\.org|player\.twitch\.tv)/i,
shareElements: /(\b|_)(share|sharedaddy)(\b|_)/i,
nextLink: /(next|weiter|continue|>([^\|]|$)|»([^\|]|$))/i,
prevLink: /(prev|earl|old|new|<|«)/i,
tokenize: /\W+/g,
whitespace: /^\s*$/,
hasContent: /\S$/,
hashUrl: /^#.+/,
srcsetUrl: /(\S+)(\s+[\d.]+[xw])?(\s*(?:,|$))/g,
b64DataUrl: /^data:\s*([^\s;,]+)\s*;\s*base64\s*,/i,
// See: https://schema.org/Article
jsonLdArticleTypes: /^Article|AdvertiserContentArticle|NewsArticle|AnalysisNewsArticle|AskPublicNewsArticle|BackgroundNewsArticle|OpinionNewsArticle|ReportageNewsArticle|ReviewNewsArticle|Report|SatiricalArticle|ScholarlyArticle|MedicalScholarlyArticle|SocialMediaPosting|BlogPosting|LiveBlogPosting|DiscussionForumPosting|TechArticle|APIReference$/
},
UNLIKELY_ROLES: [ "menu", "menubar", "complementary", "navigation", "alert", "alertdialog", "dialog" ],
DIV_TO_P_ELEMS: new Set([ "BLOCKQUOTE", "DL", "DIV", "IMG", "OL", "P", "PRE", "TABLE", "UL" ]),
ALTER_TO_DIV_EXCEPTIONS: ["DIV", "ARTICLE", "SECTION", "P"],
PRESENTATIONAL_ATTRIBUTES: [ "align", "background", "bgcolor", "border", "cellpadding", "cellspacing", "frame", "hspace", "rules", "style", "valign", "vspace" ],
DEPRECATED_SIZE_ATTRIBUTE_ELEMS: [ "TABLE", "TH", "TD", "HR", "PRE" ],
// The commented out elements qualify as phrasing content but tend to be
// removed by readability when put into paragraphs, so we ignore them here.
PHRASING_ELEMS: [
// "CANVAS", "IFRAME", "SVG", "VIDEO",
"ABBR", "AUDIO", "B", "BDO", "BR", "BUTTON", "CITE", "CODE", "DATA",
"DATALIST", "DFN", "EM", "EMBED", "I", "IMG", "INPUT", "KBD", "LABEL",
"MARK", "MATH", "METER", "NOSCRIPT", "OBJECT", "OUTPUT", "PROGRESS", "Q",
"RUBY", "SAMP", "SCRIPT", "SELECT", "SMALL", "SPAN", "STRONG", "SUB",
"SUP", "TEXTAREA", "TIME", "VAR", "WBR"
],
// These are the classes that readability sets itself.
CLASSES_TO_PRESERVE: [ "page" ],
// These are the list of HTML entities that need to be escaped.
HTML_ESCAPE_MAP: {
"lt": "<",
"gt": ">",
"amp": "&",
"quot": '"',
"apos": "'",
},
/**
* Run any post-process modifications to article content as necessary.
*
* @param Element
* @return void
**/
_postProcessContent: function(articleContent) {
// Readability cannot open relative uris so we convert them to absolute uris.
this._fixRelativeUris(articleContent);
this._simplifyNestedElements(articleContent);
if (!this._keepClasses) {
// Remove classes.
this._cleanClasses(articleContent);
}
},
/**
* Iterates over a NodeList, calls `filterFn` for each node and removes node
* if function returned `true`.
*
* If function is not passed, removes all the nodes in node list.
*
* @param NodeList nodeList The nodes to operate on
* @param Function filterFn the function to use as a filter
* @return void
*/
_removeNodes: function(nodeList, filterFn) {
// Avoid ever operating on live node lists.
if (this._docJSDOMParser && nodeList._isLiveNodeList) {
throw new Error("Do not pass live node lists to _removeNodes");
}
for (var i = nodeList.length - 1; i >= 0; i--) {
var node = nodeList[i];
var parentNode = node.parentNode;
if (parentNode) {
if (!filterFn || filterFn.call(this, node, i, nodeList)) {
parentNode.removeChild(node);
}
}
}
},
/**
* Iterates over a NodeList, and calls _setNodeTag for each node.
*
* @param NodeList nodeList The nodes to operate on
* @param String newTagName the new tag name to use
* @return void
*/
_replaceNodeTags: function(nodeList, newTagName) {
// Avoid ever operating on live node lists.
if (this._docJSDOMParser && nodeList._isLiveNodeList) {
throw new Error("Do not pass live node lists to _replaceNodeTags");
}
for (const node of nodeList) {
this._setNodeTag(node, newTagName);
}
},
/**
* Iterate over a NodeList, which doesn't natively fully implement the Array
* interface.
*
* For convenience, the current object context is applied to the provided
* iterate function.
*
* @param NodeList nodeList The NodeList.
* @param Function fn The iterate function.
* @return void
*/
_forEachNode: function(nodeList, fn) {
Array.prototype.forEach.call(nodeList, fn, this);
},
/**
* Iterate over a NodeList, and return the first node that passes
* the supplied test function
*
* For convenience, the current object context is applied to the provided
* test function.
*
* @param NodeList nodeList The NodeList.
* @param Function fn The test function.
* @return void
*/
_findNode: function(nodeList, fn) {
return Array.prototype.find.call(nodeList, fn, this);
},
/**
* Iterate over a NodeList, return true if any of the provided iterate
* function calls returns true, false otherwise.
*
* For convenience, the current object context is applied to the
* provided iterate function.
*
* @param NodeList nodeList The NodeList.
* @param Function fn The iterate function.
* @return Boolean
*/
_someNode: function(nodeList, fn) {
return Array.prototype.some.call(nodeList, fn, this);
},
/**
* Iterate over a NodeList, return true if all of the provided iterate
* function calls return true, false otherwise.
*
* For convenience, the current object context is applied to the
* provided iterate function.
*
* @param NodeList nodeList The NodeList.
* @param Function fn The iterate function.
* @return Boolean
*/
_everyNode: function(nodeList, fn) {
return Array.prototype.every.call(nodeList, fn, this);
},
/**
* Concat all nodelists passed as arguments.
*
* @return ...NodeList
* @return Array
*/
_concatNodeLists: function() {
var slice = Array.prototype.slice;
var args = slice.call(arguments);
var nodeLists = args.map(function(list) {
return slice.call(list);
});
return Array.prototype.concat.apply([], nodeLists);
},
_getAllNodesWithTag: function(node, tagNames) {
if (node.querySelectorAll) {
return node.querySelectorAll(tagNames.join(","));
}
return [].concat.apply([], tagNames.map(function(tag) {
var collection = node.getElementsByTagName(tag);
return Array.isArray(collection) ? collection : Array.from(collection);
}));
},
/**
* Removes the class="" attribute from every element in the given
* subtree, except those that match CLASSES_TO_PRESERVE and
* the classesToPreserve array from the options object.
*
* @param Element
* @return void
*/
_cleanClasses: function(node) {
var classesToPreserve = this._classesToPreserve;
var className = (node.getAttribute("class") || "")
.split(/\s+/)
.filter(function(cls) {
return classesToPreserve.indexOf(cls) != -1;
})
.join(" ");
if (className) {
node.setAttribute("class", className);
} else {
node.removeAttribute("class");
}
for (node = node.firstElementChild; node; node = node.nextElementSibling) {
this._cleanClasses(node);
}
},
/**
* Converts each
and
uri in the given element to an absolute URI,
* ignoring #ref URIs.
*
* @param Element
* @return void
*/
_fixRelativeUris: function(articleContent) {
var baseURI = this._doc.baseURI;
var documentURI = this._doc.documentURI;
function toAbsoluteURI(uri) {
// Leave hash links alone if the base URI matches the document URI:
if (baseURI == documentURI && uri.charAt(0) == "#") {
return uri;
}
// Otherwise, resolve against base URI:
try {
return new URL(uri, baseURI).href;
} catch (ex) {
// Something went wrong, just return the original:
}
return uri;
}
var links = this._getAllNodesWithTag(articleContent, ["a"]);
this._forEachNode(links, function(link) {
var href = link.getAttribute("href");
if (href) {
// Remove links with javascript: URIs, since
// they won't work after scripts have been removed from the page.
if (href.indexOf("javascript:") === 0) {
// if the link only contains simple text content, it can be converted to a text node
if (link.childNodes.length === 1 && link.childNodes[0].nodeType === this.TEXT_NODE) {
var text = this._doc.createTextNode(link.textContent);
link.parentNode.replaceChild(text, link);
} else {
// if the link has multiple children, they should all be preserved
var container = this._doc.createElement("span");
while (link.childNodes.length > 0) {
container.appendChild(link.childNodes[0]);
}
link.parentNode.replaceChild(container, link);
}
} else {
link.setAttribute("href", toAbsoluteURI(href));
}
}
});
var medias = this._getAllNodesWithTag(articleContent, [
"img", "picture", "figure", "video", "audio", "source"
]);
this._forEachNode(medias, function(media) {
var src = media.getAttribute("src");
var poster = media.getAttribute("poster");
var srcset = media.getAttribute("srcset");
if (src) {
media.setAttribute("src", toAbsoluteURI(src));
}
if (poster) {
media.setAttribute("poster", toAbsoluteURI(poster));
}
if (srcset) {
var newSrcset = srcset.replace(this.REGEXPS.srcsetUrl, function(_, p1, p2, p3) {
return toAbsoluteURI(p1) + (p2 || "") + p3;
});
media.setAttribute("srcset", newSrcset);
}
});
},
_simplifyNestedElements: function(articleContent) {
var node = articleContent;
while (node) {
if (node.parentNode && ["DIV", "SECTION"].includes(node.tagName) && !(node.id && node.id.startsWith("readability"))) {
if (this._isElementWithoutContent(node)) {
node = this._removeAndGetNext(node);
continue;
} else if (this._hasSingleTagInsideElement(node, "DIV") || this._hasSingleTagInsideElement(node, "SECTION")) {
var child = node.children[0];
for (var i = 0; i < node.attributes.length; i++) {
child.setAttribute(node.attributes[i].name, node.attributes[i].value);
}
node.parentNode.replaceChild(child, node);
node = child;
continue;
}
}
node = this._getNextNode(node);
}
},
/**
* Get the article title as an H1.
*
* @return string
**/
_getArticleTitle: function() {
var doc = this._doc;
var curTitle = "";
var origTitle = "";
try {
curTitle = origTitle = doc.title.trim();
// If they had an element with id "title" in their HTML
if (typeof curTitle !== "string")
curTitle = origTitle = this._getInnerText(doc.getElementsByTagName("title")[0]);
} catch (e) {/* ignore exceptions setting the title. */}
var titleHadHierarchicalSeparators = false;
function wordCount(str) {
return str.split(/\s+/).length;
}
// If there's a separator in the title, first remove the final part
if ((/ [\|\-\\\/>»] /).test(curTitle)) {
titleHadHierarchicalSeparators = / [\\\/>»] /.test(curTitle);
curTitle = origTitle.replace(/(.*)[\|\-\\\/>»] .*/gi, "$1");
// If the resulting title is too short (3 words or fewer), remove
// the first part instead:
if (wordCount(curTitle) < 3)
curTitle = origTitle.replace(/[^\|\-\\\/>»]*[\|\-\\\/>»](.*)/gi, "$1");
} else if (curTitle.indexOf(": ") !== -1) {
// Check if we have an heading containing this exact string, so we
// could assume it's the full title.
var headings = this._concatNodeLists(
doc.getElementsByTagName("h1"),
doc.getElementsByTagName("h2")
);
var trimmedTitle = curTitle.trim();
var match = this._someNode(headings, function(heading) {
return heading.textContent.trim() === trimmedTitle;
});
// If we don't, let's extract the title out of the original title string.
if (!match) {
curTitle = origTitle.substring(origTitle.lastIndexOf(":") + 1);
// If the title is now too short, try the first colon instead:
if (wordCount(curTitle) < 3) {
curTitle = origTitle.substring(origTitle.indexOf(":") + 1);
// But if we have too many words before the colon there's something weird
// with the titles and the H tags so let's just use the original title instead
} else if (wordCount(origTitle.substr(0, origTitle.indexOf(":"))) > 5) {
curTitle = origTitle;
}
}
} else if (curTitle.length > 150 || curTitle.length < 15) {
var hOnes = doc.getElementsByTagName("h1");
if (hOnes.length === 1)
curTitle = this._getInnerText(hOnes[0]);
}
curTitle = curTitle.trim().replace(this.REGEXPS.normalize, " ");
// If we now have 4 words or fewer as our title, and either no
// 'hierarchical' separators (\, /, > or ») were found in the original
// title or we decreased the number of words by more than 1 word, use
// the original title.
var curTitleWordCount = wordCount(curTitle);
if (curTitleWordCount <= 4 &&
(!titleHadHierarchicalSeparators ||
curTitleWordCount != wordCount(origTitle.replace(/[\|\-\\\/>»]+/g, "")) - 1)) {
curTitle = origTitle;
}
return curTitle;
},
/**
* Prepare the HTML document for readability to scrape it.
* This includes things like stripping javascript, CSS, and handling terrible markup.
*
* @return void
**/
_prepDocument: function() {
var doc = this._doc;
// Remove all style tags in head
this._removeNodes(this._getAllNodesWithTag(doc, ["style"]));
if (doc.body) {
this._replaceBrs(doc.body);
}
this._replaceNodeTags(this._getAllNodesWithTag(doc, ["font"]), "SPAN");
},
/**
* Finds the next node, starting from the given node, and ignoring
* whitespace in between. If the given node is an element, the same node is
* returned.
*/
_nextNode: function (node) {
var next = node;
while (next
&& (next.nodeType != this.ELEMENT_NODE)
&& this.REGEXPS.whitespace.test(next.textContent)) {
next = next.nextSibling;
}
return next;
},
/**
* Replaces 2 or more successive
elements with a single .
* Whitespace between
elements are ignored. For example:
*
foo
bar
abc
* will become:
*
*/
_replaceBrs: function (elem) {
this._forEachNode(this._getAllNodesWithTag(elem, ["br"]), function(br) {
var next = br.nextSibling;
// Whether 2 or more
elements have been found and replaced with a
// block.
var replaced = false;
// If we find a
chain, remove the
s until we hit another node
// or non-whitespace. This leaves behind the first
in the chain
// (which will be replaced with a
later).
while ((next = this._nextNode(next)) && (next.tagName == "BR")) {
replaced = true;
var brSibling = next.nextSibling;
next.parentNode.removeChild(next);
next = brSibling;
}
// If we removed a
chain, replace the remaining
with a
. Add
// all sibling nodes as children of the
until we hit another
// chain.
if (replaced) {
var p = this._doc.createElement("p");
br.parentNode.replaceChild(p, br);
next = p.nextSibling;
while (next) {
// If we've hit another
, we're done adding children to this
.
if (next.tagName == "BR") {
var nextElem = this._nextNode(next.nextSibling);
if (nextElem && nextElem.tagName == "BR")
break;
}
if (!this._isPhrasingContent(next))
break;
// Otherwise, make this node a child of the new
.
var sibling = next.nextSibling;
p.appendChild(next);
next = sibling;
}
while (p.lastChild && this._isWhitespace(p.lastChild)) {
p.removeChild(p.lastChild);
}
if (p.parentNode.tagName === "P")
this._setNodeTag(p.parentNode, "DIV");
}
});
},
_setNodeTag: function (node, tag) {
this.log("_setNodeTag", node, tag);
if (this._docJSDOMParser) {
node.localName = tag.toLowerCase();
node.tagName = tag.toUpperCase();
return node;
}
var replacement = node.ownerDocument.createElement(tag);
while (node.firstChild) {
replacement.appendChild(node.firstChild);
}
node.parentNode.replaceChild(replacement, node);
if (node.readability)
replacement.readability = node.readability;
for (var i = 0; i < node.attributes.length; i++) {
try {
replacement.setAttribute(node.attributes[i].name, node.attributes[i].value);
} catch (ex) {
/* it's possible for setAttribute() to throw if the attribute name
* isn't a valid XML Name. Such attributes can however be parsed from
* source in HTML docs, see https://github.com/whatwg/html/issues/4275,
* so we can hit them here and then throw. We don't care about such
* attributes so we ignore them.
*/
}
}
return replacement;
},
/**
* Prepare the article node for display. Clean out any inline styles,
* iframes, forms, strip extraneous
tags, etc.
*
* @param Element
* @return void
**/
_prepArticle: function(articleContent) {
this._cleanStyles(articleContent);
// Check for data tables before we continue, to avoid removing items in
// those tables, which will often be isolated even though they're
// visually linked to other content-ful elements (text, images, etc.).
this._markDataTables(articleContent);
this._fixLazyImages(articleContent);
// Clean out junk from the article content
this._cleanConditionally(articleContent, "form");
this._cleanConditionally(articleContent, "fieldset");
this._clean(articleContent, "object");
this._clean(articleContent, "embed");
this._clean(articleContent, "footer");
this._clean(articleContent, "link");
this._clean(articleContent, "aside");
// Clean out elements with little content that have "share" in their id/class combinations from final top candidates,
// which means we don't remove the top candidates even they have "share".
var shareElementThreshold = this.DEFAULT_CHAR_THRESHOLD;
this._forEachNode(articleContent.children, function (topCandidate) {
this._cleanMatchedNodes(topCandidate, function (node, matchString) {
return this.REGEXPS.shareElements.test(matchString) && node.textContent.length < shareElementThreshold;
});
});
this._clean(articleContent, "iframe");
this._clean(articleContent, "input");
this._clean(articleContent, "textarea");
this._clean(articleContent, "select");
this._clean(articleContent, "button");
this._cleanHeaders(articleContent);
// Do these last as the previous stuff may have removed junk
// that will affect these
this._cleanConditionally(articleContent, "table");
this._cleanConditionally(articleContent, "ul");
this._cleanConditionally(articleContent, "div");
// replace H1 with H2 as H1 should be only title that is displayed separately
this._replaceNodeTags(this._getAllNodesWithTag(articleContent, ["h1"]), "h2");
// Remove extra paragraphs
this._removeNodes(this._getAllNodesWithTag(articleContent, ["p"]), function (paragraph) {
var imgCount = paragraph.getElementsByTagName("img").length;
var embedCount = paragraph.getElementsByTagName("embed").length;
var objectCount = paragraph.getElementsByTagName("object").length;
// At this point, nasty iframes have been removed, only remain embedded video ones.
var iframeCount = paragraph.getElementsByTagName("iframe").length;
var totalCount = imgCount + embedCount + objectCount + iframeCount;
return totalCount === 0 && !this._getInnerText(paragraph, false);
});
this._forEachNode(this._getAllNodesWithTag(articleContent, ["br"]), function(br) {
var next = this._nextNode(br.nextSibling);
if (next && next.tagName == "P")
br.parentNode.removeChild(br);
});
// Remove single-cell tables
this._forEachNode(this._getAllNodesWithTag(articleContent, ["table"]), function(table) {
var tbody = this._hasSingleTagInsideElement(table, "TBODY") ? table.firstElementChild : table;
if (this._hasSingleTagInsideElement(tbody, "TR")) {
var row = tbody.firstElementChild;
if (this._hasSingleTagInsideElement(row, "TD")) {
var cell = row.firstElementChild;
cell = this._setNodeTag(cell, this._everyNode(cell.childNodes, this._isPhrasingContent) ? "P" : "DIV");
table.parentNode.replaceChild(cell, table);
}
}
});
},
/**
* Initialize a node with the readability object. Also checks the
* className/id for special names to add to its score.
*
* @param Element
* @return void
**/
_initializeNode: function(node) {
node.readability = {"contentScore": 0};
switch (node.tagName) {
case "DIV":
node.readability.contentScore += 5;
break;
case "PRE":
case "TD":
case "BLOCKQUOTE":
node.readability.contentScore += 3;
break;
case "ADDRESS":
case "OL":
case "UL":
case "DL":
case "DD":
case "DT":
case "LI":
case "FORM":
node.readability.contentScore -= 3;
break;
case "H1":
case "H2":
case "H3":
case "H4":
case "H5":
case "H6":
case "TH":
node.readability.contentScore -= 5;
break;
}
node.readability.contentScore += this._getClassWeight(node);
},
_removeAndGetNext: function(node) {
var nextNode = this._getNextNode(node, true);
node.parentNode.removeChild(node);
return nextNode;
},
/**
* Traverse the DOM from node to node, starting at the node passed in.
* Pass true for the second parameter to indicate this node itself
* (and its kids) are going away, and we want the next node over.
*
* Calling this in a loop will traverse the DOM depth-first.
*/
_getNextNode: function(node, ignoreSelfAndKids) {
// First check for kids if those aren't being ignored
if (!ignoreSelfAndKids && node.firstElementChild) {
return node.firstElementChild;
}
// Then for siblings...
if (node.nextElementSibling) {
return node.nextElementSibling;
}
// And finally, move up the parent chain *and* find a sibling
// (because this is depth-first traversal, we will have already
// seen the parent nodes themselves).
do {
node = node.parentNode;
} while (node && !node.nextElementSibling);
return node && node.nextElementSibling;
},
// compares second text to first one
// 1 = same text, 0 = completely different text
// works the way that it splits both texts into words and then finds words that are unique in second text
// the result is given by the lower length of unique parts
_textSimilarity: function(textA, textB) {
var tokensA = textA.toLowerCase().split(this.REGEXPS.tokenize).filter(Boolean);
var tokensB = textB.toLowerCase().split(this.REGEXPS.tokenize).filter(Boolean);
if (!tokensA.length || !tokensB.length) {
return 0;
}
var uniqTokensB = tokensB.filter(token => !tokensA.includes(token));
var distanceB = uniqTokensB.join(" ").length / tokensB.join(" ").length;
return 1 - distanceB;
},
_checkByline: function(node, matchString) {
if (this._articleByline) {
return false;
}
if (node.getAttribute !== undefined) {
var rel = node.getAttribute("rel");
var itemprop = node.getAttribute("itemprop");
}
if ((rel === "author" || (itemprop && itemprop.indexOf("author") !== -1) || this.REGEXPS.byline.test(matchString)) && this._isValidByline(node.textContent)) {
this._articleByline = node.textContent.trim();
return true;
}
return false;
},
_getNodeAncestors: function(node, maxDepth) {
maxDepth = maxDepth || 0;
var i = 0, ancestors = [];
while (node.parentNode) {
ancestors.push(node.parentNode);
if (maxDepth && ++i === maxDepth)
break;
node = node.parentNode;
}
return ancestors;
},
/***
* grabArticle - Using a variety of metrics (content score, classname, element types), find the content that is
* most likely to be the stuff a user wants to read. Then return it wrapped up in a div.
*
* @param page a document to run upon. Needs to be a full document, complete with body.
* @return Element
**/
_grabArticle: function (page) {
this.log("**** grabArticle ****");
var doc = this._doc;
var isPaging = page !== null;
page = page ? page : this._doc.body;
// We can't grab an article if we don't have a page!
if (!page) {
this.log("No body found in document. Abort.");
return null;
}
var pageCacheHtml = page.innerHTML;
while (true) {
this.log("Starting grabArticle loop");
var stripUnlikelyCandidates = this._flagIsActive(this.FLAG_STRIP_UNLIKELYS);
// First, node prepping. Trash nodes that look cruddy (like ones with the
// class name "comment", etc), and turn divs into P tags where they have been
// used inappropriately (as in, where they contain no other block level elements.)
var elementsToScore = [];
var node = this._doc.documentElement;
let shouldRemoveTitleHeader = true;
while (node) {
var matchString = node.className + " " + node.id;
if (!this._isProbablyVisible(node)) {
this.log("Removing hidden node - " + matchString);
node = this._removeAndGetNext(node);
continue;
}
// Check to see if this node is a byline, and remove it if it is.
if (this._checkByline(node, matchString)) {
node = this._removeAndGetNext(node);
continue;
}
if (shouldRemoveTitleHeader && this._headerDuplicatesTitle(node)) {
this.log("Removing header: ", node.textContent.trim(), this._articleTitle.trim());
shouldRemoveTitleHeader = false;
node = this._removeAndGetNext(node);
continue;
}
// Remove unlikely candidates
if (stripUnlikelyCandidates) {
if (this.REGEXPS.unlikelyCandidates.test(matchString) &&
!this.REGEXPS.okMaybeItsACandidate.test(matchString) &&
!this._hasAncestorTag(node, "table") &&
!this._hasAncestorTag(node, "code") &&
node.tagName !== "BODY" &&
node.tagName !== "A") {
this.log("Removing unlikely candidate - " + matchString);
node = this._removeAndGetNext(node);
continue;
}
if (this.UNLIKELY_ROLES.includes(node.getAttribute("role"))) {
this.log("Removing content with role " + node.getAttribute("role") + " - " + matchString);
node = this._removeAndGetNext(node);
continue;
}
}
// Remove DIV, SECTION, and HEADER nodes without any content(e.g. text, image, video, or iframe).
if ((node.tagName === "DIV" || node.tagName === "SECTION" || node.tagName === "HEADER" ||
node.tagName === "H1" || node.tagName === "H2" || node.tagName === "H3" ||
node.tagName === "H4" || node.tagName === "H5" || node.tagName === "H6") &&
this._isElementWithoutContent(node)) {
node = this._removeAndGetNext(node);
continue;
}
if (this.DEFAULT_TAGS_TO_SCORE.indexOf(node.tagName) !== -1) {
elementsToScore.push(node);
}
// Turn all divs that don't have children block level elements into p's
if (node.tagName === "DIV") {
// Put phrasing content into paragraphs.
var p = null;
var childNode = node.firstChild;
while (childNode) {
var nextSibling = childNode.nextSibling;
if (this._isPhrasingContent(childNode)) {
if (p !== null) {
p.appendChild(childNode);
} else if (!this._isWhitespace(childNode)) {
p = doc.createElement("p");
node.replaceChild(p, childNode);
p.appendChild(childNode);
}
} else if (p !== null) {
while (p.lastChild && this._isWhitespace(p.lastChild)) {
p.removeChild(p.lastChild);
}
p = null;
}
childNode = nextSibling;
}
// Sites like http://mobile.slate.com encloses each paragraph with a DIV
// element. DIVs with only a P element inside and no text content can be
// safely converted into plain P elements to avoid confusing the scoring
// algorithm with DIVs with are, in practice, paragraphs.
if (this._hasSingleTagInsideElement(node, "P") && this._getLinkDensity(node) < 0.25) {
var newNode = node.children[0];
node.parentNode.replaceChild(newNode, node);
node = newNode;
elementsToScore.push(node);
} else if (!this._hasChildBlockElement(node)) {
node = this._setNodeTag(node, "P");
elementsToScore.push(node);
}
}
node = this._getNextNode(node);
}
/**
* Loop through all paragraphs, and assign a score to them based on how content-y they look.
* Then add their score to their parent node.
*
* A score is determined by things like number of commas, class names, etc. Maybe eventually link density.
**/
var candidates = [];
this._forEachNode(elementsToScore, function(elementToScore) {
if (!elementToScore.parentNode || typeof(elementToScore.parentNode.tagName) === "undefined")
return;
// If this paragraph is less than 25 characters, don't even count it.
var innerText = this._getInnerText(elementToScore);
if (innerText.length < 25)
return;
// Exclude nodes with no ancestor.
var ancestors = this._getNodeAncestors(elementToScore, 5);
if (ancestors.length === 0)
return;
var contentScore = 0;
// Add a point for the paragraph itself as a base.
contentScore += 1;
// Add points for any commas within this paragraph.
contentScore += innerText.split(",").length;
// For every 100 characters in this paragraph, add another point. Up to 3 points.
contentScore += Math.min(Math.floor(innerText.length / 100), 3);
// Initialize and score ancestors.
this._forEachNode(ancestors, function(ancestor, level) {
if (!ancestor.tagName || !ancestor.parentNode || typeof(ancestor.parentNode.tagName) === "undefined")
return;
if (typeof(ancestor.readability) === "undefined") {
this._initializeNode(ancestor);
candidates.push(ancestor);
}
// Node score divider:
// - parent: 1 (no division)
// - grandparent: 2
// - great grandparent+: ancestor level * 3
if (level === 0)
var scoreDivider = 1;
else if (level === 1)
scoreDivider = 2;
else
scoreDivider = level * 3;
ancestor.readability.contentScore += contentScore / scoreDivider;
});
});
// After we've calculated scores, loop through all of the possible
// candidate nodes we found and find the one with the highest score.
var topCandidates = [];
for (var c = 0, cl = candidates.length; c < cl; c += 1) {
var candidate = candidates[c];
// Scale the final candidates score based on link density. Good content
// should have a relatively small link density (5% or less) and be mostly
// unaffected by this operation.
var candidateScore = candidate.readability.contentScore * (1 - this._getLinkDensity(candidate));
candidate.readability.contentScore = candidateScore;
this.log("Candidate:", candidate, "with score " + candidateScore);
for (var t = 0; t < this._nbTopCandidates; t++) {
var aTopCandidate = topCandidates[t];
if (!aTopCandidate || candidateScore > aTopCandidate.readability.contentScore) {
topCandidates.splice(t, 0, candidate);
if (topCandidates.length > this._nbTopCandidates)
topCandidates.pop();
break;
}
}
}
var topCandidate = topCandidates[0] || null;
var neededToCreateTopCandidate = false;
var parentOfTopCandidate;
// If we still have no top candidate, just use the body as a last resort.
// We also have to copy the body node so it is something we can modify.
if (topCandidate === null || topCandidate.tagName === "BODY") {
// Move all of the page's children into topCandidate
topCandidate = doc.createElement("DIV");
neededToCreateTopCandidate = true;
// Move everything (not just elements, also text nodes etc.) into the container
// so we even include text directly in the body:
var kids = page.childNodes;
while (kids.length) {
this.log("Moving child out:", kids[0]);
topCandidate.appendChild(kids[0]);
}
page.appendChild(topCandidate);
this._initializeNode(topCandidate);
} else if (topCandidate) {
// Find a better top candidate node if it contains (at least three) nodes which belong to `topCandidates` array
// and whose scores are quite closed with current `topCandidate` node.
var alternativeCandidateAncestors = [];
for (var i = 1; i < topCandidates.length; i++) {
if (topCandidates[i].readability.contentScore / topCandidate.readability.contentScore >= 0.75) {
alternativeCandidateAncestors.push(this._getNodeAncestors(topCandidates[i]));
}
}
var MINIMUM_TOPCANDIDATES = 3;
if (alternativeCandidateAncestors.length >= MINIMUM_TOPCANDIDATES) {
parentOfTopCandidate = topCandidate.parentNode;
while (parentOfTopCandidate.tagName !== "BODY") {
var listsContainingThisAncestor = 0;
for (var ancestorIndex = 0; ancestorIndex < alternativeCandidateAncestors.length && listsContainingThisAncestor < MINIMUM_TOPCANDIDATES; ancestorIndex++) {
listsContainingThisAncestor += Number(alternativeCandidateAncestors[ancestorIndex].includes(parentOfTopCandidate));
}
if (listsContainingThisAncestor >= MINIMUM_TOPCANDIDATES) {
topCandidate = parentOfTopCandidate;
break;
}
parentOfTopCandidate = parentOfTopCandidate.parentNode;
}
}
if (!topCandidate.readability) {
this._initializeNode(topCandidate);
}
// Because of our bonus system, parents of candidates might have scores
// themselves. They get half of the node. There won't be nodes with higher
// scores than our topCandidate, but if we see the score going *up* in the first
// few steps up the tree, that's a decent sign that there might be more content
// lurking in other places that we want to unify in. The sibling stuff
// below does some of that - but only if we've looked high enough up the DOM
// tree.
parentOfTopCandidate = topCandidate.parentNode;
var lastScore = topCandidate.readability.contentScore;
// The scores shouldn't get too low.
var scoreThreshold = lastScore / 3;
while (parentOfTopCandidate.tagName !== "BODY") {
if (!parentOfTopCandidate.readability) {
parentOfTopCandidate = parentOfTopCandidate.parentNode;
continue;
}
var parentScore = parentOfTopCandidate.readability.contentScore;
if (parentScore < scoreThreshold)
break;
if (parentScore > lastScore) {
// Alright! We found a better parent to use.
topCandidate = parentOfTopCandidate;
break;
}
lastScore = parentOfTopCandidate.readability.contentScore;
parentOfTopCandidate = parentOfTopCandidate.parentNode;
}
// If the top candidate is the only child, use parent instead. This will help sibling
// joining logic when adjacent content is actually located in parent's sibling node.
parentOfTopCandidate = topCandidate.parentNode;
while (parentOfTopCandidate.tagName != "BODY" && parentOfTopCandidate.children.length == 1) {
topCandidate = parentOfTopCandidate;
parentOfTopCandidate = topCandidate.parentNode;
}
if (!topCandidate.readability) {
this._initializeNode(topCandidate);
}
}
// Now that we have the top candidate, look through its siblings for content
// that might also be related. Things like preambles, content split by ads
// that we removed, etc.
var articleContent = doc.createElement("DIV");
if (isPaging)
articleContent.id = "readability-content";
var siblingScoreThreshold = Math.max(10, topCandidate.readability.contentScore * 0.2);
// Keep potential top candidate's parent node to try to get text direction of it later.
parentOfTopCandidate = topCandidate.parentNode;
var siblings = parentOfTopCandidate.children;
for (var s = 0, sl = siblings.length; s < sl; s++) {
var sibling = siblings[s];
var append = false;
this.log("Looking at sibling node:", sibling, sibling.readability ? ("with score " + sibling.readability.contentScore) : "");
this.log("Sibling has score", sibling.readability ? sibling.readability.contentScore : "Unknown");
if (sibling === topCandidate) {
append = true;
} else {
var contentBonus = 0;
// Give a bonus if sibling nodes and top candidates have the example same classname
if (sibling.className === topCandidate.className && topCandidate.className !== "")
contentBonus += topCandidate.readability.contentScore * 0.2;
if (sibling.readability &&
((sibling.readability.contentScore + contentBonus) >= siblingScoreThreshold)) {
append = true;
} else if (sibling.nodeName === "P") {
var linkDensity = this._getLinkDensity(sibling);
var nodeContent = this._getInnerText(sibling);
var nodeLength = nodeContent.length;
if (nodeLength > 80 && linkDensity < 0.25) {
append = true;
} else if (nodeLength < 80 && nodeLength > 0 && linkDensity === 0 &&
nodeContent.search(/\.( |$)/) !== -1) {
append = true;
}
}
}
if (append) {
this.log("Appending node:", sibling);
if (this.ALTER_TO_DIV_EXCEPTIONS.indexOf(sibling.nodeName) === -1) {
// We have a node that isn't a common block level element, like a form or td tag.
// Turn it into a div so it doesn't get filtered out later by accident.
this.log("Altering sibling:", sibling, "to div.");
sibling = this._setNodeTag(sibling, "DIV");
}
articleContent.appendChild(sibling);
// siblings is a reference to the children array, and
// sibling is removed from the array when we call appendChild().
// As a result, we must revisit this index since the nodes
// have been shifted.
s -= 1;
sl -= 1;
}
}
if (this._debug)
this.log("Article content pre-prep: " + articleContent.innerHTML);
// So we have all of the content that we need. Now we clean it up for presentation.
this._prepArticle(articleContent);
if (this._debug)
this.log("Article content post-prep: " + articleContent.innerHTML);
if (neededToCreateTopCandidate) {
// We already created a fake div thing, and there wouldn't have been any siblings left
// for the previous loop, so there's no point trying to create a new div, and then
// move all the children over. Just assign IDs and class names here. No need to append
// because that already happened anyway.
topCandidate.id = "readability-page-1";
topCandidate.className = "page";
} else {
var div = doc.createElement("DIV");
div.id = "readability-page-1";
div.className = "page";
var children = articleContent.childNodes;
while (children.length) {
div.appendChild(children[0]);
}
articleContent.appendChild(div);
}
if (this._debug)
this.log("Article content after paging: " + articleContent.innerHTML);
var parseSuccessful = true;
// Now that we've gone through the full algorithm, check to see if
// we got any meaningful content. If we didn't, we may need to re-run
// grabArticle with different flags set. This gives us a higher likelihood of
// finding the content, and the sieve approach gives us a higher likelihood of
// finding the -right- content.
var textLength = this._getInnerText(articleContent, true).length;
if (textLength < this._charThreshold) {
parseSuccessful = false;
page.innerHTML = pageCacheHtml;
if (this._flagIsActive(this.FLAG_STRIP_UNLIKELYS)) {
this._removeFlag(this.FLAG_STRIP_UNLIKELYS);
this._attempts.push({articleContent: articleContent, textLength: textLength});
} else if (this._flagIsActive(this.FLAG_WEIGHT_CLASSES)) {
this._removeFlag(this.FLAG_WEIGHT_CLASSES);
this._attempts.push({articleContent: articleContent, textLength: textLength});
} else if (this._flagIsActive(this.FLAG_CLEAN_CONDITIONALLY)) {
this._removeFlag(this.FLAG_CLEAN_CONDITIONALLY);
this._attempts.push({articleContent: articleContent, textLength: textLength});
} else {
this._attempts.push({articleContent: articleContent, textLength: textLength});
// No luck after removing flags, just return the longest text we found during the different loops
this._attempts.sort(function (a, b) {
return b.textLength - a.textLength;
});
// But first check if we actually have something
if (!this._attempts[0].textLength) {
return null;
}
articleContent = this._attempts[0].articleContent;
parseSuccessful = true;
}
}
if (parseSuccessful) {
// Find out text direction from ancestors of final top candidate.
var ancestors = [parentOfTopCandidate, topCandidate].concat(this._getNodeAncestors(parentOfTopCandidate));
this._someNode(ancestors, function(ancestor) {
if (!ancestor.tagName)
return false;
var articleDir = ancestor.getAttribute("dir");
if (articleDir) {
this._articleDir = articleDir;
return true;
}
return false;
});
return articleContent;
}
}
},
/**
* Check whether the input string could be a byline.
* This verifies that the input is a string, and that the length
* is less than 100 chars.
*
* @param possibleByline {string} - a string to check whether its a byline.
* @return Boolean - whether the input string is a byline.
*/
_isValidByline: function(byline) {
if (typeof byline == "string" || byline instanceof String) {
byline = byline.trim();
return (byline.length > 0) && (byline.length < 100);
}
return false;
},
/**
* Converts some of the common HTML entities in string to their corresponding characters.
*
* @param str {string} - a string to unescape.
* @return string without HTML entity.
*/
_unescapeHtmlEntities: function(str) {
if (!str) {
return str;
}
var htmlEscapeMap = this.HTML_ESCAPE_MAP;
return str.replace(/&(quot|amp|apos|lt|gt);/g, function(_, tag) {
return htmlEscapeMap[tag];
}).replace(/(?:x([0-9a-z]{1,4})|([0-9]{1,4}));/gi, function(_, hex, numStr) {
var num = parseInt(hex || numStr, hex ? 16 : 10);
return String.fromCharCode(num);
});
},
/**
* Try to extract metadata from JSON-LD object.
* For now, only Schema.org objects of type Article or its subtypes are supported.
* @return Object with any metadata that could be extracted (possibly none)
*/
_getJSONLD: function (doc) {
var scripts = this._getAllNodesWithTag(doc, ["script"]);
var jsonLdElement = this._findNode(scripts, function(el) {
return el.getAttribute("type") === "application/ld+json";
});
if (jsonLdElement) {
try {
// Strip CDATA markers if present
var content = jsonLdElement.textContent.replace(/^\s*\s*$/g, "");
var parsed = JSON.parse(content);
var metadata = {};
if (
!parsed["@context"] ||
!parsed["@context"].match(/^https?\:\/\/schema\.org$/)
) {
return metadata;
}
if (!parsed["@type"] && Array.isArray(parsed["@graph"])) {
parsed = parsed["@graph"].find(function(it) {
return (it["@type"] || "").match(
this.REGEXPS.jsonLdArticleTypes
);
});
}
if (
!parsed ||
!parsed["@type"] ||
!parsed["@type"].match(this.REGEXPS.jsonLdArticleTypes)
) {
return metadata;
}
if (typeof parsed.name === "string") {
metadata.title = parsed.name.trim();
} else if (typeof parsed.headline === "string") {
metadata.title = parsed.headline.trim();
}
if (parsed.author) {
if (typeof parsed.author.name === "string") {
metadata.byline = parsed.author.name.trim();
} else if (Array.isArray(parsed.author) && parsed.author[0] && typeof parsed.author[0].name === "string") {
metadata.byline = parsed.author
.filter(function(author) {
return author && typeof author.name === "string";
})
.map(function(author) {
return author.name.trim();
})
.join(", ");
}
}
if (typeof parsed.description === "string") {
metadata.excerpt = parsed.description.trim();
}
if (
parsed.publisher &&
typeof parsed.publisher.name === "string"
) {
metadata.siteName = parsed.publisher.name.trim();
}
return metadata;
} catch (err) {
this.log(err.message);
}
}
return {};
},
/**
* Attempts to get excerpt and byline metadata for the article.
*
* @param {Object} jsonld — object containing any metadata that
* could be extracted from JSON-LD object.
*
* @return Object with optional "excerpt" and "byline" properties
*/
_getArticleMetadata: function(jsonld) {
var metadata = {};
var values = {};
var metaElements = this._doc.getElementsByTagName("meta");
// property is a space-separated list of values
var propertyPattern = /\s*(dc|dcterm|og|twitter)\s*:\s*(author|creator|description|title|site_name)\s*/gi;
// name is a single value
var namePattern = /^\s*(?:(dc|dcterm|og|twitter|weibo:(article|webpage))\s*[\.:]\s*)?(author|creator|description|title|site_name)\s*$/i;
// Find description tags.
this._forEachNode(metaElements, function(element) {
var elementName = element.getAttribute("name");
var elementProperty = element.getAttribute("property");
var content = element.getAttribute("content");
if (!content) {
return;
}
var matches = null;
var name = null;
if (elementProperty) {
matches = elementProperty.match(propertyPattern);
if (matches) {
// Convert to lowercase, and remove any whitespace
// so we can match below.
name = matches[0].toLowerCase().replace(/\s/g, "");
// multiple authors
values[name] = content.trim();
}
}
if (!matches && elementName && namePattern.test(elementName)) {
name = elementName;
if (content) {
// Convert to lowercase, remove any whitespace, and convert dots
// to colons so we can match below.
name = name.toLowerCase().replace(/\s/g, "").replace(/\./g, ":");
values[name] = content.trim();
}
}
});
// get title
metadata.title = jsonld.title ||
values["dc:title"] ||
values["dcterm:title"] ||
values["og:title"] ||
values["weibo:article:title"] ||
values["weibo:webpage:title"] ||
values["title"] ||
values["twitter:title"];
if (!metadata.title) {
metadata.title = this._getArticleTitle();
}
// get author
metadata.byline = jsonld.byline ||
values["dc:creator"] ||
values["dcterm:creator"] ||
values["author"];
// get description
metadata.excerpt = jsonld.excerpt ||
values["dc:description"] ||
values["dcterm:description"] ||
values["og:description"] ||
values["weibo:article:description"] ||
values["weibo:webpage:description"] ||
values["description"] ||
values["twitter:description"];
// get site name
metadata.siteName = jsonld.siteName ||
values["og:site_name"];
// in many sites the meta value is escaped with HTML entities,
// so here we need to unescape it
metadata.title = this._unescapeHtmlEntities(metadata.title);
metadata.byline = this._unescapeHtmlEntities(metadata.byline);
metadata.excerpt = this._unescapeHtmlEntities(metadata.excerpt);
metadata.siteName = this._unescapeHtmlEntities(metadata.siteName);
return metadata;
},
/**
* Check if node is image, or if node contains exactly only one image
* whether as a direct child or as its descendants.
*
* @param Element
**/
_isSingleImage: function(node) {
if (node.tagName === "IMG") {
return true;
}
if (node.children.length !== 1 || node.textContent.trim() !== "") {
return false;
}
return this._isSingleImage(node.children[0]);
},
/**
* Find all
");
return doc.querySelector("#content");
},
contentPatch: (dom) => {
(0,_lib_misc__WEBPACK_IMPORTED_MODULE_1__.rm)("h1", true, dom);
(0,_lib_misc__WEBPACK_IMPORTED_MODULE_1__.rm)("div[id^=BookSee]", true, dom);
return dom;
},
});
};
/***/ }),
/***/ "./src/rules/twoPage/tempate.ts":
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "x": () => (/* binding */ mkRuleClass)
/* harmony export */ });
/* harmony import */ var _lib_attachments__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__("./src/lib/attachments.ts");
/* harmony import */ var _lib_cleanDOM__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__("./src/lib/cleanDOM.ts");
/* harmony import */ var _lib_http__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("./src/lib/http.ts");
/* harmony import */ var _lib_rule__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__("./src/lib/rule.ts");
/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__("loglevel");
/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_log__WEBPACK_IMPORTED_MODULE_4__);
/* harmony import */ var _main__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__("./src/main.ts");
/* harmony import */ var _rules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/rules.ts");
function mkRuleClass({ bookUrl, anotherPageUrl, getBookname, getAuthor, getIntroDom, introDomPatch, getCoverUrl, getAList, getAName, getSections, getSName: _getSectionName, postHook, getContentFromUrl, getContent, contentPatch, concurrencyLimit, }) {
return class extends _rules__WEBPACK_IMPORTED_MODULE_0__/* .BaseRuleClass */ .c {
constructor() {
super();
this.imageMode = "TM";
if (concurrencyLimit) {
this.concurrencyLimit = concurrencyLimit;
}
}
async bookParse() {
const doc = await (0,_lib_http__WEBPACK_IMPORTED_MODULE_1__/* .getHtmlDOM */ .dL)(anotherPageUrl, this.charset);
const base = document.createElement("base");
base.href = anotherPageUrl;
doc.head.appendChild(base);
const bookname = getBookname(doc);
const author = getAuthor(doc);
const introDom = getIntroDom(doc);
const [introduction, introductionHTML, introCleanimages] = await (0,_lib_rule__WEBPACK_IMPORTED_MODULE_2__/* .introDomHandle */ .SN)(introDom, introDomPatch);
const coverUrl = getCoverUrl(doc);
const additionalMetadate = {};
if (coverUrl) {
(0,_lib_attachments__WEBPACK_IMPORTED_MODULE_3__/* .getImageAttachment */ .CE)(coverUrl, this.imageMode, "cover-")
.then((coverClass) => {
additionalMetadate.cover = coverClass;
})
.catch((error) => _log__WEBPACK_IMPORTED_MODULE_4___default().error(error));
}
let sections;
if (typeof getSections === "function") {
sections = getSections(doc);
}
const chapters = [];
let chapterNumber = 0;
let sectionNumber = 0;
let sectionChapterNumber = 0;
let sectionName = null;
let hasSection = false;
if (sections &&
sections instanceof NodeList &&
typeof _getSectionName === "function") {
hasSection = true;
}
const aList = getAList(doc);
for (const aElem of Array.from(aList)) {
let chapterName;
if (getAName) {
chapterName = getAName(aElem);
}
else {
chapterName = aElem.innerText;
}
const chapterUrl = aElem.href;
if (hasSection && sections && _getSectionName) {
const _sectionName = (0,_lib_rule__WEBPACK_IMPORTED_MODULE_2__/* .getSectionName */ .$d)(aElem, sections, _getSectionName);
if (_sectionName !== sectionName) {
sectionName = _sectionName;
sectionNumber++;
sectionChapterNumber = 0;
}
}
chapterNumber++;
sectionChapterNumber++;
const isVIP = false;
const isPaid = false;
let chapter = new _main__WEBPACK_IMPORTED_MODULE_5__/* .Chapter */ .WC(bookUrl, bookname, chapterUrl, chapterNumber, chapterName, isVIP, isPaid, sectionName, hasSection ? sectionNumber : null, hasSection ? sectionChapterNumber : null, this.chapterParse, this.charset, { bookname });
if (typeof postHook === "function") {
chapter = postHook(chapter);
}
if (chapter) {
chapters.push(chapter);
}
}
const book = new _main__WEBPACK_IMPORTED_MODULE_5__/* .Book */ .fy(bookUrl, bookname, author, introduction, introductionHTML, additionalMetadate, chapters);
return book;
}
async chapterParse(chapterUrl, chapterName, isVIP, isPaid, charset, options) {
let content;
if (getContentFromUrl !== undefined) {
content = await getContentFromUrl(chapterUrl, chapterName, charset);
}
else if (getContent !== undefined) {
const doc = await (0,_lib_http__WEBPACK_IMPORTED_MODULE_1__/* .getHtmlDOM */ .dL)(chapterUrl, charset);
content = getContent(doc);
}
if (content) {
content = contentPatch(content);
const { dom, text, images } = await (0,_lib_cleanDOM__WEBPACK_IMPORTED_MODULE_6__/* .cleanDOM */ .z)(content, "TM");
return {
chapterName,
contentRaw: content,
contentText: text,
contentHTML: dom,
contentImages: images,
additionalMetadate: null,
};
}
return {
chapterName,
contentRaw: null,
contentText: null,
contentHTML: null,
contentImages: null,
additionalMetadate: null,
};
}
};
}
/***/ }),
/***/ "./src/rules/twoPage/ujxs.ts":
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "ujxs": () => (/* binding */ ujxs)
/* harmony export */ });
/* harmony import */ var _lib_misc__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("./src/lib/misc.ts");
/* harmony import */ var _tempate__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/rules/twoPage/tempate.ts");
const ujxs = () => {
const bookUrl = document.location.origin +
document.location.pathname.replace(/^\/read/, "/book");
return (0,_tempate__WEBPACK_IMPORTED_MODULE_0__/* .mkRuleClass */ .x)({
bookUrl,
anotherPageUrl: bookUrl,
getBookname: (doc) => document.querySelector("#smallcons > h1").innerText.trim(),
getAuthor: (doc) => document.querySelector("#smallcons > span:nth-child(3) > a").innerText.trim(),
getIntroDom: (doc) => doc.querySelector("#bookintro"),
introDomPatch: (introDom) => introDom,
getCoverUrl: (doc) => doc.querySelector(".img > img")?.src,
getAList: (doc) => document.querySelectorAll("#readerlist li > a"),
getSections: (doc) => document.querySelectorAll("#readerlist li.fj > h3"),
getSName: (sElem) => sElem.innerText,
postHook: (chapter) => {
chapter.sectionName =
chapter.sectionName?.replace(chapter.bookname, "") ?? null;
return chapter;
},
getContent: (doc) => doc.querySelector(".read-content"),
contentPatch: (content) => {
(0,_lib_misc__WEBPACK_IMPORTED_MODULE_1__.rm)("script", true, content);
const ads = ["免费小说无弹窗免费阅读!", "全集TXT电子书免费下载!"];
(0,_lib_misc__WEBPACK_IMPORTED_MODULE_1__/* .rm2 */ .vS)(content, ads);
return content;
},
});
};
/***/ }),
/***/ "./src/rules/twoPage/viviyzw.ts":
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "viviyzw": () => (/* binding */ viviyzw)
/* harmony export */ });
/* harmony import */ var _tempate__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/rules/twoPage/tempate.ts");
const viviyzw = () => {
const bookUrl = document.location.href.replace("/book", "/info");
return (0,_tempate__WEBPACK_IMPORTED_MODULE_0__/* .mkRuleClass */ .x)({
bookUrl,
anotherPageUrl: bookUrl,
getBookname: (doc) => doc.querySelector("article.info > header > h1").innerText.trim(),
getAuthor: (doc) => doc.querySelector("article.info > p.detail.pt20 > i:nth-child(1) > a").innerText.trim(),
getIntroDom: (doc) => doc.querySelector("article.info > p.desc"),
introDomPatch: (content) => content,
getCoverUrl: (doc) => doc.querySelector("article.info > div.cover > img")
.src,
getAList: (doc) => document.querySelectorAll("ul.mulu > li.col3 > a"),
getSections: (doc) => document.querySelectorAll("li.col1.volumn"),
getSName: (sElem) => sElem.innerText,
postHook: (chapter) => {
if (chapter.sectionName?.includes("最新九章")) {
return;
}
return chapter;
},
getContent: (doc) => doc.querySelector("#content"),
contentPatch: (content) => content,
});
};
/***/ }),
/***/ "./src/rules/twoPage/washuge.ts":
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "washuge": () => (/* binding */ washuge)
/* harmony export */ });
/* harmony import */ var _tempate__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/rules/twoPage/tempate.ts");
const washuge = () => {
const bookUrl = document.location.href;
const bookId = /(\d+)\/?$/.exec(bookUrl)?.[1];
if (!bookId) {
throw Error("获取书籍信息出错!");
}
const anotherPageUrl = `${document.location.origin}/books/book${bookId}.html`;
return (0,_tempate__WEBPACK_IMPORTED_MODULE_0__/* .mkRuleClass */ .x)({
bookUrl,
anotherPageUrl,
getBookname: (doc) => doc.querySelector("#content > dd > h1")?.innerText
.replace("全文阅读", "")
.trim(),
getAuthor: (doc) => doc.querySelector("#at > tbody:nth-child(1) > tr:nth-child(1) > td:nth-child(4)")?.innerText.trim(),
getIntroDom: (doc) => doc.querySelector("#content > dd:nth-child(7) > p:nth-child(3)"),
introDomPatch: (dom) => dom,
getCoverUrl: (doc) => doc.querySelector(".hst > img").src,
getAList: (doc) => document.querySelectorAll("#at > tbody td > a"),
getContent: (doc) => doc.querySelector("#contents"),
contentPatch: (dom) => dom,
concurrencyLimit: 1,
});
};
/***/ }),
/***/ "./src/rules/twoPage/yibige.ts":
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "yibige": () => (/* binding */ yibige)
/* harmony export */ });
/* harmony import */ var _lib_cleanDOM__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__("./src/lib/cleanDOM.ts");
/* harmony import */ var _lib_misc__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__("./src/lib/misc.ts");
/* harmony import */ var _lib_rule__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("./src/lib/rule.ts");
/* harmony import */ var _tempate__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/rules/twoPage/tempate.ts");
const yibige = () => (0,_tempate__WEBPACK_IMPORTED_MODULE_0__/* .mkRuleClass */ .x)({
bookUrl: document.location.href,
anotherPageUrl: document.location.href + "index.html",
getBookname: (doc) => document.querySelector("#info h1:nth-of-type(1)").innerText.trim(),
getAuthor: (doc) => document.querySelector("#info > p:nth-child(2)").innerText
.replace(/作(\s+)?者[::]/, "")
.trim(),
getIntroDom: (doc) => document.querySelector("#intro > p:nth-child(1)"),
introDomPatch: (introDom) => introDom,
getCoverUrl: (doc) => document.querySelector("#fmimg > img")?.src ?? "",
getAList: (doc) => doc.querySelectorAll("#list dd > a"),
getContent: (doc) => doc.querySelector("#content"),
getContentFromUrl: async (chapterUrl, chapterName, charset) => {
const { chapterName: _chapterName, contentRaw, contentText, contentHTML, contentImages, additionalMetadate, } = await (0,_lib_rule__WEBPACK_IMPORTED_MODULE_1__/* .nextPageParse */ .I2)({
chapterName,
chapterUrl,
charset,
selector: "#content",
contentPatch: (content, doc) => {
(0,_lib_misc__WEBPACK_IMPORTED_MODULE_2__.rm)("script", true, content);
(0,_lib_misc__WEBPACK_IMPORTED_MODULE_2__.rm)("div[style]", true, content);
(0,_lib_cleanDOM__WEBPACK_IMPORTED_MODULE_3__/* .htmlTrim */ .i)(content);
return content;
},
getNextPage: (doc) => doc.querySelector(".bottem1 > a:nth-child(4)")
.href,
continueCondition: (_content, nextLink) => {
const pathname = nextLink.split("/").slice(-1)[0];
return pathname.includes("_");
},
enableCleanDOM: false,
});
return contentRaw;
},
contentPatch: (content) => content,
});
/***/ }),
/***/ "./src/save/save.ts":
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
// EXPORTS
__webpack_require__.d(__webpack_exports__, {
"Zd": () => (/* binding */ getSaveBookObj),
"f0": () => (/* binding */ getSectionsObj),
"hh": () => (/* binding */ saveOptionsValidate)
});
// UNUSED EXPORTS: SaveBook
// EXTERNAL MODULE: ./node_modules/file-saver/dist/FileSaver.min.js
var FileSaver_min = __webpack_require__("./node_modules/file-saver/dist/FileSaver.min.js");
;// CONCATENATED MODULE: external "fflate"
const external_fflate_namespaceObject = fflate;
// EXTERNAL MODULE: external "log"
var external_log_ = __webpack_require__("loglevel");
var external_log_default = /*#__PURE__*/__webpack_require__.n(external_log_);
// EXTERNAL MODULE: ./src/lib/misc.ts
var misc = __webpack_require__("./src/lib/misc.ts");
;// CONCATENATED MODULE: ./src/lib/zip.ts
class FflateZip {
constructor(memlimit = false) {
this.count = 0;
this.zcount = 0;
this.tcount = 0;
this.memlimit = memlimit;
this.filenameList = [];
this.zipOut = [];
const self = this;
this.savedZip = new external_fflate_namespaceObject.Zip((err, dat, final) => {
if (err) {
external_log_default().error(err);
external_log_default().trace(err);
throw err;
}
self.zipOut.push(dat);
self.zcount++;
if (final) {
const zipBlob = new Blob(self.zipOut, { type: "application/zip" });
external_log_default().debug("[fflateZip][debug][zcount]" + self.zcount);
external_log_default().debug("[fflateZip][debug][count]" + self.count);
external_log_default().info("[fflateZip] ZIP生成完毕,文件大小:" + zipBlob.size);
self.zipOut = [];
if (typeof self.onFinal === "function") {
if (typeof self.onUpdateId !== "undefined") {
clearInterval(self.onUpdateId);
}
try {
self.onFinal(zipBlob);
}
catch (error) {
if (typeof self.onFinalError === "function") {
self.onFinalError(error);
}
}
}
else {
throw new Error("[fflateZip] 完成函数出错");
}
}
});
}
file(filename, file) {
if (this.filenameList.includes(filename)) {
external_log_default().error(`filename ${filename} has existed on zip.`);
return;
}
this.count++;
this.filenameList.push(filename);
file
.arrayBuffer()
.then((buffer) => new Uint8Array(buffer))
.then((chunk) => {
if (this.memlimit || file.type.includes("image/")) {
const nonStreamingFile = new external_fflate_namespaceObject.ZipPassThrough(filename);
this.addToSavedZip(this.savedZip, nonStreamingFile, chunk);
this.tcount++;
}
else {
const nonStreamingFile = new external_fflate_namespaceObject.AsyncZipDeflate(filename, {
level: 6,
});
this.addToSavedZip(this.savedZip, nonStreamingFile, chunk);
this.tcount++;
}
})
.catch((error) => external_log_default().error(error));
}
addToSavedZip(savedZip, nonStreamingFile, chunk) {
savedZip.add(nonStreamingFile);
nonStreamingFile.push(chunk, true);
}
async generateAsync(onUpdate) {
while (this.tcount !== this.count) {
await (0,misc/* sleep */._v)(500);
}
const self = this;
this.onUpdateId = window.setInterval(() => {
const percent = (self.zcount / 3 / self.count) * 100;
if (typeof onUpdate === "function") {
onUpdate(percent);
}
}, 100);
this.savedZip.end();
}
}
// EXTERNAL MODULE: ./src/log.ts
var log = __webpack_require__("./src/log.ts");
// EXTERNAL MODULE: ./src/main.ts
var main = __webpack_require__("./src/main.ts");
// EXTERNAL MODULE: ./src/setting.ts
var setting = __webpack_require__("./src/setting.ts");
// EXTERNAL MODULE: ./src/ui/progress.ts + 1 modules
var progress = __webpack_require__("./src/ui/progress.ts");
// EXTERNAL MODULE: ./src/save/main.css
var save_main = __webpack_require__("./src/save/main.css");
;// CONCATENATED MODULE: ./src/save/chapter.html.j2
// Module
var code = " {{ chapterName }}
{{ chapterName }}
{{ outerHTML }} ";
// Exports
/* harmony default export */ const chapter_html = (code);
;// CONCATENATED MODULE: ./src/save/index.html.j2
// Module
var index_html_code = " {{ bookname }} {{ bookname }}
{{ author }}
{% if cover -%}
![]()
{%- endif %} {% if introductionHTML -%}
简介
{{ introductionHTML }}
{%- endif %}
{% for sectionObj in sectionsObj -%}
{% if sectionObj.sectionName %}
{{ sectionObj.sectionName }}
{% endif %} {% for chapter in sectionObj.chpaters -%}
{%- endfor %}
{%- endfor %}
";
// Exports
/* harmony default export */ const index_html = (index_html_code);
;// CONCATENATED MODULE: ./src/save/section.html.j2
// Module
var section_html_code = " {{ sectionName }} {{ sectionName }}
";
// Exports
/* harmony default export */ const section_html = (section_html_code);
;// CONCATENATED MODULE: external "nunjucks"
const external_nunjucks_namespaceObject = nunjucks;
;// CONCATENATED MODULE: ./src/save/template.ts
const env = new external_nunjucks_namespaceObject.Environment(undefined, { autoescape: false });
const section = new external_nunjucks_namespaceObject.Template(section_html, env, undefined, true);
const chapter = new external_nunjucks_namespaceObject.Template(chapter_html, env, undefined, true);
const index = new external_nunjucks_namespaceObject.Template(index_html, env, undefined, true);
// EXTERNAL MODULE: ./src/save/toc.css
var toc = __webpack_require__("./src/save/toc.css");
;// CONCATENATED MODULE: ./src/save/save.ts
class SaveBook {
constructor(book) {
this.book = book;
this.chapters = book.chapters;
this.savedZip = new FflateZip();
this._sections = [];
this.savedTextArray = [];
this.saveFileNameBase = `[${this.book.author}]${this.book.bookname}`;
this.mainStyleText = save_main/* default */.Z;
this.tocStyleText = toc/* default */.Z;
}
saveTxt() {
const metaDateText = this.genMetaDateTxt();
this.savedTextArray.push(metaDateText);
external_log_default().debug("[save]对 chapters 排序");
this.chapters.sort(this.chapterSort);
const sections = [];
for (const chapterTemp of this.chapters) {
const chapterName = this.getchapterName(chapterTemp);
if (chapterTemp.sectionName &&
!sections.includes(chapterTemp.sectionName)) {
sections.push(chapterTemp.sectionName);
const sectionText = this.genSectionText(chapterTemp.sectionName);
this.savedTextArray.push(sectionText);
}
const chapterText = this.genChapterText(chapterName, chapterTemp.contentText ?? "");
this.savedTextArray.push(chapterText);
if (!setting/* enableDebug.value */.Cy.value) {
chapterTemp.contentText = null;
}
}
external_log_default().info("[save]保存TXT文件");
const savedText = this.savedTextArray.join("\n").replaceAll("\n", "\r\n");
(0,FileSaver_min.saveAs)(new Blob([savedText], { type: "text/plain;charset=utf-8" }), `${this.saveFileNameBase}.txt`);
}
saveLog() {
this.savedZip.file("debug.log", new Blob([log/* logText */.KC], { type: "text/plain; charset=UTF-8" }));
}
saveZip(runSaveChapters = false) {
external_log_default().debug("[save]保存元数据文本");
const metaDateText = this.genMetaDateTxt();
this.savedZip.file("info.txt", new Blob([metaDateText], { type: "text/plain;charset=utf-8" }));
external_log_default().debug("[save]保存样式");
this.savedZip.file("style.css", new Blob([this.mainStyleText], { type: "text/css;charset=utf-8" }));
if (this.book.additionalMetadate.cover) {
external_log_default().debug("[save]保存封面");
this.addImageToZip(this.book.additionalMetadate.cover, this.savedZip);
}
if (this.book.additionalMetadate.attachments) {
external_log_default().debug("[save]保存书籍附件");
for (const bookAttachment of this.book.additionalMetadate.attachments) {
this.addImageToZip(bookAttachment, this.savedZip);
}
}
external_log_default().debug("[save]开始生成并保存卷文件");
this.saveSections();
external_log_default().debug("[save]开始生成并保存 index.html");
this.saveToC();
if (runSaveChapters) {
external_log_default().debug("[save]开始保存章节文件");
this.saveChapters();
}
else {
external_log_default().debug("[save]保存仅标题章节文件");
this.chapters
.filter((c) => c.status !== main/* Status.saved */.qb.saved)
.forEach((c) => {
if (c.status === main/* Status.finished */.qb.finished) {
this.addChapter(c);
}
else {
this.addChapter(c, "Stub");
}
});
}
external_log_default().info("[save]开始保存ZIP文件");
const self = this;
if (setting/* enableDebug.value */.Cy.value) {
self.saveLog();
}
return new Promise((resolve, reject) => {
const finalHandle = (blob) => {
(0,FileSaver_min.saveAs)(blob, `${self.saveFileNameBase}.zip`);
resolve();
};
const finalErrorHandle = (err) => {
external_log_default().error("saveZip: " + err);
external_log_default().trace(err);
reject(err);
};
this.savedZip.onFinal = finalHandle;
this.savedZip.onFinalError = finalErrorHandle;
this.savedZip.generateAsync((percent) => {
progress.vm.zipPercent = percent;
});
});
}
saveToC() {
external_log_default().debug("[save]对 chapters 排序");
this.chapters.sort(this.chapterSort);
const self = this;
const sectionsListObj = getSectionsObj(self.chapters);
modifyTocStyleText();
const indexHtmlText = index.render({
creationDate: Date.now(),
bookname: self.book.bookname,
tocStyleText: self.tocStyleText,
author: self.book.author,
cover: self.book.additionalMetadate.cover,
introductionHTML: self.book.introductionHTML?.outerHTML,
bookUrl: self.book.bookUrl,
sectionsObj: Object.values(sectionsListObj),
Status: main/* Status */.qb,
});
this.savedZip.file("index.html", new Blob([indexHtmlText.replaceAll("data-src-address", "src")], {
type: "text/html; charset=UTF-8",
}));
function modifyTocStyleText() {
if (self.book.additionalMetadate.cover) {
self.tocStyleText = `${self.tocStyleText}
.info {
display: grid;
grid-template-columns: 30% 70%;
}`;
}
else {
self.tocStyleText = `${self.tocStyleText}
.info {
display: grid;
grid-template-columns: 100%;
}`;
}
}
}
saveChapters() {
for (const chapter of this.chapters) {
this.addChapter(chapter);
}
}
saveSections() {
external_log_default().debug("[save]对 chapters 排序");
this.chapters.sort(this.chapterSort);
for (const chapter of this.chapters) {
const chapterNumberToSave = this.getChapterNumberToSave(chapter);
const sectionHtmlFileName = `No${chapterNumberToSave}Section.html`;
if (chapter.sectionName) {
if (!this._sections.includes(chapter.sectionName)) {
this._sections.push(chapter.sectionName);
external_log_default().debug(`[save]保存卷HTML文件:${chapter.sectionName}`);
const sectionHTMLBlob = this.genSectionHtmlFile(chapter);
this.savedZip.file(sectionHtmlFileName, sectionHTMLBlob);
}
}
}
}
getChapterNumberToSave(chapter) {
return `${"0".repeat(this.chapters.length.toString().length -
chapter.chapterNumber.toString().length)}${chapter.chapterNumber.toString()}`;
}
addChapter(chapter, suffix = "") {
const chapterName = this.getchapterName(chapter);
const chapterNumberToSave = this.getChapterNumberToSave(chapter);
const chapterHtmlFileName = `No${chapterNumberToSave}Chapter${suffix}.html`;
external_log_default().debug(`[save]保存章HTML文件:${chapterName}`);
const chapterHTMLBlob = this.genChapterHtmlFile(chapter);
if (!setting/* enableDebug.value */.Cy.value) {
chapter.contentRaw = null;
chapter.contentHTML = null;
}
this.savedZip.file(chapterHtmlFileName, chapterHTMLBlob);
chapter.chapterHtmlFileName = chapterHtmlFileName;
chapter.status = main/* Status.saved */.qb.saved;
if (chapter.contentImages && chapter.contentImages.length !== 0) {
external_log_default().debug(`[save]保存章节附件:${chapterName}`);
for (const attachment of chapter.contentImages) {
this.addImageToZip(attachment, this.savedZip);
}
if (!setting/* enableDebug.value */.Cy.value) {
chapter.contentImages = null;
}
}
}
getchapterName(chapter) {
if (chapter.chapterName) {
return chapter.chapterName;
}
else {
return chapter.chapterNumber.toString();
}
}
genMetaDateTxt() {
let metaDateText = `题名:${this.book.bookname}\n作者:${this.book.author}`;
if (this.book.additionalMetadate.tags) {
metaDateText += `\nTag列表:${this.book.additionalMetadate.tags.join("、")}`;
}
metaDateText += `\n原始网址:${this.book.bookUrl}`;
if (this.book.additionalMetadate.cover) {
metaDateText += `\n封面图片地址:${this.book.additionalMetadate.cover.url}`;
}
if (this.book.introduction) {
metaDateText += `\n简介:${this.book.introduction}`;
}
metaDateText += `\n下载时间:${new Date().toISOString()}\n本文件由小说下载器生成,软件地址:https://github.com/yingziwu/novel-downloader\n\n`;
return metaDateText;
}
addImageToZip(attachment, zip) {
if (attachment.status === main/* Status.finished */.qb.finished && attachment.imageBlob) {
external_log_default().debug(`[save]添加附件,文件名:${attachment.name},对象`, attachment.imageBlob);
zip.file(attachment.name, attachment.imageBlob);
attachment.status = main/* Status.saved */.qb.saved;
if (!setting/* enableDebug.value */.Cy.value) {
attachment.imageBlob = null;
}
}
else if (attachment.status === main/* Status.saved */.qb.saved) {
external_log_default().debug(`[save]附件${attachment.name}已添加`);
}
else {
external_log_default().warn(`[save]添加附件${attachment.name}失败,该附件未完成或内容为空。`);
external_log_default().warn(attachment);
}
}
genSectionText(sectionName) {
return (`${"=".repeat(20)}\n\n\n\n# ${sectionName}\n\n\n\n${"=".repeat(20)}` +
"\n\n");
}
genChapterText(chapterName, contentText) {
return `## ${chapterName}\n\n${contentText}\n\n`;
}
genSectionHtmlFile(chapterObj) {
const htmlText = section.render({ sectionName: chapterObj.sectionName });
return new Blob([htmlText.replaceAll("data-src-address", "src")], {
type: "text/html; charset=UTF-8",
});
}
genChapterHtmlFile(chapterObj) {
const htmlText = chapter.render({
chapterUrl: chapterObj.chapterUrl,
chapterName: chapterObj.chapterName,
outerHTML: chapterObj.contentHTML?.outerHTML ?? "",
});
return new Blob([htmlText.replaceAll("data-src-address", "src")], {
type: "text/html; charset=UTF-8",
});
}
chapterSort(a, b) {
if (a.chapterNumber > b.chapterNumber) {
return 1;
}
if (a.chapterNumber === b.chapterNumber) {
return 0;
}
if (a.chapterNumber < b.chapterNumber) {
return -1;
}
return 0;
}
}
function getSectionsObj(chapters) {
const _sectionsObj = {};
for (const chapter of chapters) {
let sectionNumber = null;
const sectionName = null;
if (chapter.sectionNumber && chapter.sectionName) {
sectionNumber = chapter.sectionNumber;
}
else {
sectionNumber = -99999999;
}
if (_sectionsObj[sectionNumber]) {
_sectionsObj[sectionNumber].chpaters.push(chapter);
}
else {
_sectionsObj[sectionNumber] = {
sectionName: chapter.sectionName,
sectionNumber: chapter.sectionNumber,
chpaters: [chapter],
};
}
}
const _sectionsListObj = Object.entries(_sectionsObj);
function sectionListSort(a, b) {
const aKey = Number(a[0]);
const bKey = Number(b[0]);
if (aKey > bKey) {
return 1;
}
if (aKey === bKey) {
return 0;
}
if (aKey < bKey) {
return -1;
}
return 0;
}
_sectionsListObj.sort(sectionListSort);
const sectionsListObj = _sectionsListObj.map((s) => s[1]);
return sectionsListObj;
}
function saveOptionsValidate(data) {
const keyNamesS = ["mainStyleText", "tocStyleText"];
const keyNamesF = [
"getchapterName",
"genSectionText",
"genChapterText",
"genSectionHtmlFile",
"genChapterHtmlFile",
"chapterSort",
];
function keyNametest(keyname) {
const keyList = new Array().concat(keyNamesS).concat(keyNamesF);
if (keyList.includes(keyname)) {
return true;
}
return false;
}
function keyNamesStest(keyname) {
if (keyNamesS.includes(keyname)) {
if (typeof data[keyname] === "string") {
return true;
}
}
return false;
}
function keyNamesFtest(keyname) {
if (keyNamesF.includes(keyname)) {
if (typeof data[keyname] === "function") {
return true;
}
}
return false;
}
if (typeof data !== "object") {
return false;
}
if (Object.keys(data).length === 0) {
return false;
}
for (const keyname in data) {
if (Object.prototype.hasOwnProperty.call(data, keyname)) {
if (!keyNametest(keyname)) {
return false;
}
if (!(keyNamesStest(keyname) || keyNamesFtest(keyname))) {
return false;
}
}
}
return true;
}
function getSaveBookObj(book, options) {
const saveBookObj = new SaveBook(book);
if (setting/* enableCustomSaveOptions */.EI && saveOptionsValidate(options)) {
Object.assign(saveBookObj, options);
}
if (book.saveOptions !== undefined) {
Object.assign(saveBookObj, book.saveOptions);
}
return saveBookObj;
}
/***/ }),
/***/ "./src/setting.ts":
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "o5": () => (/* binding */ retryLimit),
/* harmony export */ "Cy": () => (/* binding */ enableDebug),
/* harmony export */ "Vo": () => (/* binding */ enableCustomFinishCallback),
/* harmony export */ "Td": () => (/* binding */ enableCustomChapterFilter),
/* harmony export */ "EI": () => (/* binding */ enableCustomSaveOptions),
/* harmony export */ "Z3": () => (/* binding */ enableJjwxcRemoteFont),
/* harmony export */ "cl": () => (/* binding */ iconStart0),
/* harmony export */ "wE": () => (/* binding */ iconStart1),
/* harmony export */ "d7": () => (/* binding */ iconSetting),
/* harmony export */ "y6": () => (/* binding */ iconJump)
/* harmony export */ });
const retryLimit = 5;
const enableDebug = {
value: false,
};
const enableCustomFinishCallback = true;
const enableCustomChapterFilter = true;
const enableCustomSaveOptions = true;
const enableJjwxcRemoteFont = true;
const iconStart0 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAFYElEQVR4nO2dIUxkORyHP4XD4E6RYNZgUGvWonAnVqxDbbJiNWLNOsQ65Oo1CMQIFAnJJiQIcgY7YhIEbgTJiEkm4USPuyNh3pv2tf33tb9f8kl4fe3H0Pm37xXi50/gHJgBC+C5YB6Bv4AL4CuwH7872skBcI/9oA5lBpwAO1F7p/IcUf5fuy8L4AzYjthPVWYfeMJ+wFLxABxG660K8xv7QcrBWawOqykfsB+YnEzQv4RXOcV+UHJzD+zF6LwaMsF+QCyYo3kBALfYD4YVK+DL8C4cd+6wHwhrfgJbQztyrJEAjhvgj4F9OcrUKMA33Me778/NaLCUXKMA27ivt48BP7vArYU0k1oFAPeRHjrJPQ3u0ZGlZgHATe5+Bv6ecxooGtUuwEuOCVvsugd2vXp0ZGlFAHDL3bOA3zfHzSmqTEsCgNsjcBXwO5e4T5Hq0poA4OYFoWsg1RWNWhTgJZ8ImxdcUdFuo5YFADcvmAZcY0olRaPWBQD313wZcJ0n3Fa6UUcC/JfvAdda4TagjjYS4HWOcF/7fK/5i5FODmvcDzC0eveOsO3xt4xwRVECvJ1t3MMmvtd+AN5HuH62SIDunOC/tLxgREUjCdCf0HnBKFYUJcBm2SNsXnCZqD3RIgE2zzZuidi3PVPcxLLISAD/fMYtDvm0qdht6BIgLIf4zwuWOHmKigQIzy5hhbSiKocSYFi2cFVA3zZ+ytjGztQogMVS7Vf85gVPFLLVrEYBrGbcvlvRJzbNfJ0aBbDc1++7Fd28bFyjAOdRe8g/PlvOfhm18d/UKMCKMjZqHNM/L1hiXCmsUYBn3ILMZ+zX6N/jVgi72mr6KFqtArzwiJtsneE+li3oezLJdNGodgHGgOm3AQlgz03vKCWMBLDnrneUEkYC2CMBGkcCNI4EaBwJ0DgSYEMecE/mbkLIA59NCnCzplElEbqfLvTJXwlQGEN2z+zjv4GzKQFK/xewZPiCTumS6xOgg4cI9xiyZ08CFIIESBwJYI8E6EACJI4EsEcCdCABEkcC2CMBOpAAiSMB7JEAHUiAxJEA9kiADiRA4kgAeyRABxIgcSSAPRKgAwmQOBLAHgnQgQRIHAlgjwToQAIkjgSwRwJ0IAESRwLYYyrA7zWNKgUJkDgSwB4J0IEESBwJYE8zAqxwr0T7webv2Ivxbv2PHtc7xb1qNucDpc0I8DHTPcXIB/yPi5MAHcT4KM+dXH3ThADzXDcUMSHHxEmADr5kuqcYOSJfvzQjwIKCz8/7X3bof8O3BAjkDvtXuPcl5HBICeDB9yx3FpZj8vdHcwKsKOCsnDeyhzvNSwJkYEp5hypfY9MXTQrwjDtJo5ScYNcPzQrwTBmHOx1g+y7BpgV4xJ21Z5Ut8hV8JMAaLpPf5fqcdbRLAmTE4lj1wwHtlQCRyV0l3MHvnF8JkIGcVcLc1T4JsCE5qoQW1T4JsCGpq4RW1b5iBbhe0yhLUlYJS7xfCfAGKaqE3wq4LwngQcxTta2rfRIggDlxqoQlVPskQCAxqoQlVPskwACG7CUspdonAQYQWiUsqdonAQYSUiUsqdonASLgUyUsrdonASKwwj2y1ZcSq30SIBKbVAlLK29LgMh0VQlLrfZJgMi89aRxydU+CRCZOe5g6JfsMo6TwiVARJbABe7r3pgmfRJASAAhAQQSQCABmsdUgKs1jRL5uO0dpYSRAPZMekcpYS7WNErk47R3lBLmx5pGiXyYvi1lDFumaua6f4jS5w77jmiRBa/XM8zyjnHX0sfIkrjPPQzOAeNdTRsbUzbb2ZQ9W7i9dBNghltyjUHrny4r3JtHJ//0b9RH4P8GSxsCzEN/51YAAAAASUVORK5CYII=";
const iconStart1 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAESElEQVR4nO2cLUxcQRSFv4QgEBiSKgQCh6pCouvQlbVVdaRuTFUNoqaqEkktCoVD4HBITBMMosmaVsxu+kL3l3lv7p13z5ccyc68OSf3sLtvHwghhBBCjJM/hRKNowAERwEIjgIQHAUgOApAcBSA4CgAwVEAgqMABEcBCI4CEBwFIDgKQHAUgOAoAMFRAIKjAARHAQiOAhAcBSA4CkBwFIDgKADBUQCCowAERwEIjgIQHAUgOApAcBSA4CgAzkmUm9SqUvHpjYSEvRky35iEvSky35iEvTky35iEvUky35iEvVky35iEvWky35iEvXky35iEvYky35iEvZky35iEvaky35iEvbky35iEvcky35iEvdky35iEveky35iEzA9PQuaHJyHzm2e78O8T7Zhfeq2j4i1wDvyi/GAT/s1P5Gs9J197SN4An4A7hjlgz+a/fM078lm8KXxt92wDp8BPYEL9g/ZoflcT8tmcMrKK6I54TwfueS/NV8SyEe/54D3uoZmK2GTEt2KA5dov5bYiXjvivRthsea6Mq+Ivka8V0NqrlWqahUx1IjfRGeF15DWWCMVrnG2xhpDaLCKqDHiV+ka+ADs9nA9ack6qYfX3yXv9XrJOkOruCIsRvxLPZANOXztRSwhzVkvDbDO4fR1H+asV0trV4SHEf8M/ABOVm22B1Jn3VRhvRPytT1jc7YLK8LTiN/Z/FyLSNT/Vm8HZxVhtYnZiD8oOc3GOcC+Iqou9gx8p86Ib40T8tnUrogqi1wB76k/4ltkh3xWVzQegHvgM7Df6/HEYp98hvc0EoAn8hg7HuAwonNMPtsnnAVggkZ8TboV0cfb9aIRf4ZGvCX7ZA9KKmLjEf8NjXiPHJO92bQiFICRUCUAqgBfVK+AedI/gXVx80/goorQ28BhcPs2cFlF6IOgMpr7IGiRVBHrM5qPguep5vf9rWF1v0DVxbrS18EBvw5epGv6u+fPOx7uGXQXgJnGXBHWt4Q1EYCuhrwptBYebgptNgBd3dBORcxG/A325zaaAMz0G7gA3gFbaxpSgy3yni7Ie7Q+p9EGoKtH4AtwtNqfwTia7uER+/MIF4CuboCPwN5Su/phb7pWKyM+RABmGqoiWh7xoQLQ1SPwlbKKOJq+RssjPmwAurpl/YqYjfhbB/tWAHrWBLjk/9/HzX4XeYnd7yIVgMqa/T7O+neR1jLfgKQASIYy34CkAEiGcvGACKmu5j5DKPJboQha9BZ4Lh4eEiX1o+LnCKoi2tMgTxJVRfjWRiO+FFWEH5k/TVwVUV/mD4ueh4cHTY5ZVUd8KaqI/mQ+4ktRRWwulyO+FFXEcjU14ktRRfxT8yO+lIgVMcoRX8rYP2gKNeJLGVNFhB/xpbRYERrxA+C9IjTiK+KpIjTijbGoCI14hwxdERrxDdFnRWjEN85rKkIjfoSsqgiN+EB0K0IjXgghhBDh+Avri3imoU6g/AAAAABJRU5ErkJggg==";
const iconSetting = "data:image/png;base64,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";
const iconJump = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAG6wAABusBTDGeSgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAeVSURBVHic7VpvTBNpHv6901L+yQU3nCfrByU9IRYrWrCVGkVzgu4ZCSFSMBDJrhcTP+iqMdlcLvFyl7sPftn9diZsiHterpYz8TxuCXsauBNy2VVoXIEIXV2hAYt/WqF/mMGZ6fzugwxXSjudlnYHWJ5kMm+n7/zmeZ55531/7ztDEBF+zKCUJqA01gxQmoDSWDNAaQJKY80ApQkojTUDlCawFNTU1Pzk3LlzzYQQkmiMFW0AABzRarVfXLx40UYISUjLijaA4zjNunXrQK/XWy5dutS5b98+dbwxVrQB4jwmIyMDtm/fXrV3796eQ4cOZcYTY0UbEAqNRgM6na7caDTeP3r0aI7c81aNAQAAaWlpsG3bNr3BYHhYW1v7npxzVpUBAABqtRoKCwu1er1+sK6uLj9W/RVtQLS1DJVKBVu3bn1fr9cPWCyWAqkYK9oAKVAUBVqtNq+kpORRQ0ODLlo9kooVoVOnTv0yOzv7F1J1Yl1XDi+e53++e/fu6uzs7AX1w8sTExPMo0eP9lmtVnt4jKQbQAhJv3z58rDBYJhveuI1pEjKLSd6nsvlYh8+fFhptVp7Qvmm4hFQAUDCqWmqkJ+frykrK+tubGz8IPR4SvqA5brQumHDBpXJZPqyqampTjy2KjvBYDAILMsCwzAQCATA5/OB1+uFqakpAACqsLDQ1tzcfAoAIO7cWQ5S3QIEQYDZ2VngOA6CwSDwPA88z8+XAQDECWKkfVZWFqXVaj9vaGiYTYkByQbP80DTNDAMAwzDAMuyABBZnJRwcY+IMDMz4x8eHuYTNoAQQk6fPn1yYmLi7x0dHb7Q/5baAliWBZqm5zc5d1WOcJGby+V6cevWrY+dTudXCRlACKHOnj17o6ioyPL8+XMGAP6WSBwRgiCAz+cDn88HDMNAMBiUJSYe4YQQCAaD4HQ6v2tra/vY7Xb3ICIdtwFms1l9/vz5DpPJVMXzPLAsqwmvg4goipj7vWAP8K6jCgQCMD09DYFAICJpKcQjnBACLMvCkydPHlit1osMw/Qj4luAODvBAwcOZJrN5i6j0Vienp4OPp8vUjX+7t27nz5+/NgoFSsZmWBWVtZPi4uLj6Snp0u2jtnZWRgaGuq02Wy/EQRhEBF5MYZsAw4fPpxjMpn+W1paqtdoNICIEUkiIksI+UtfX9+SHgs5OHjw4NHi4uIj4Y9CaDkQCEBfX9+f29vbrwCAAxGF0BiyDDh27Nh7ZWVlD0pKSrRpaWnz4qPdJUT0LkWYXFRWVnKi4Ej9gdfrFXp6ej7r7u7+EwCMYgTCMQ2oqanJ37lzZ79er39frVaDIPzfwNCyUiCEAEVR82URr1+/Zru6un53//79LxDRFe18SQNqa2sLdu3a9UCn0+VRFDUveLmkuiKP8DF+cnLS39nZ+cng4OBNRHRLxYhqwPHjx3UGg+GboqKiHACIKH65tADRAEEQYHx8/OXt27cvjI6OdiBixF46FBENsFgspaWlpb1arTYz1jR0y5YtdWfOnNkVSihSWeo/OWW/3/+v1tbWO+FcRQM4joPR0dHvb968ee7Vq1f/QUQ6suSFWGRAfX39/rKysrubN2/WSIlHRMjMzASz2Vw9PT0NDMNEHMfjGa+l9uPj45WEkN3i+B0an2VZcDgc9ra2tgt+v/9BeB3ZBtTX139gNBr/uWnTJlV4Lx9eRkSYmpoCv98fNXGJJSoezF1fFXYMaZqGp0+f3rHZbL8WBGEgdIyXg3kDTpw4Ubdnzx7bxo0bqdBnO5IJHMeBx+MBlmWXJE7u3Y8Gu93+Lcdxf+zt7bUCwEj4GC8HagCAxsbGX5WXl7fk5eWRaOJF0DQNHo8n5kiQzLsf7Ryv1+vo7e29AgCBSGO8HKgtFkvj/v37P8/NzY3Zq79582ZBk0+GqKXEmmvu/rgvHgJqeHiY93g8tJT4YDAILpcL/H7pay21SUvFEpOdZIMaGhrqvHbt2ocOh8MTOoMTEQwG4cWLF8BxXERiSt39ZIFCRJ/T6fyypaWlaWBgYEwUCrBYvBRS8cz/EMZQAACISLvd7n9fvXq1yW63f8uy7ALxUoRCJ0ZSm1g31h4RQRCERVuq0u8FL0YIIWqKovQnT568UlBQUBk+yQjfcxwHIyMjXzEM8zpicIlMMNrxaHXGxsa+7u/vb0VEVpYymVj0Zoi8+9SkqLq6+pMdO3Y0Z2RkLCAWSpDjOLh3795H3d3dHckkFQVvUzHNXpQKI6JACBlpb2///czMjNtkMl3Iyspa1AWLRqhUKg4RXyWb2A+FiJOhuaTiGSHk05mZmTcVFRW/zcnJ0SjRS6cakusBiOgihLTQND1VVVV1Zf369TkAyg5byUbMFSFEdBNC/hoIBKarq6s/y8vL+5lKpYp12oqBrPQKEX3Pnj37x40bNz6anJz8XhCEVXH3AeJ4OYqI9MuXL7uuX7/+4djYmF1OcrQSEFeCjYhv/X7/162tracdDseduXd0y2OBMFHIzeTCsjoKAHQVFRV/yM3NLU4kxnLZEv5EhrzrBNYBABPvKsxyQko+klpJWJVfiMSDNQOUJqA01gxQmoDSWDNAaQJK40dvwP8AKk+/HC2PJW8AAAAASUVORK5CYII=";
/***/ }),
/***/ "./src/ui/progress.ts":
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
// EXPORTS
__webpack_require__.d(__webpack_exports__, {
"el": () => (/* binding */ el),
"o": () => (/* binding */ style),
"vm": () => (/* binding */ vm)
});
// EXTERNAL MODULE: external "Vue"
var external_Vue_ = __webpack_require__("vue");
// EXTERNAL MODULE: ./src/lib/createEl.ts
var createEl = __webpack_require__("./src/lib/createEl.ts");
// EXTERNAL MODULE: ./src/ui/progress.css
var progress = __webpack_require__("./src/ui/progress.css");
;// CONCATENATED MODULE: ./src/ui/progress.html
// Module
var code = " ";
// Exports
/* harmony default export */ const ui_progress = (code);
;// CONCATENATED MODULE: ./src/ui/progress.ts
const style = (0,createEl/* createStyle */.w)(progress/* default */.Z);
const el = (0,createEl/* createEl */.u)(``);
const vm = (0,external_Vue_.createApp)({
data() {
return {
totalChapterNumber: 0,
finishedChapterNumber: 0,
zipPercent: 0,
};
},
computed: {
chapterPercent() {
if (this.totalChapterNumber !== 0 && this.finishedChapterNumber !== 0) {
return (this.finishedChapterNumber / this.totalChapterNumber) * 100;
}
else {
return 0;
}
},
chapterProgressSeen() {
return this.chapterPercent !== 0;
},
zipProgressSeen() {
return this.zipPercent !== 0;
},
ntProgressSeen() {
if (this.chapterProgressSeen || this.zipProgressSeen) {
return true;
}
else {
return false;
}
},
chapterProgressTitle() {
return `章节:${this.finishedChapterNumber}/${this.totalChapterNumber}`;
},
},
methods: {
reset() {
this.totalChapterNumber = 0;
this.finishedChapterNumber = 0;
this.zipPercent = 0;
},
},
template: ui_progress,
}).mount(el);
/***/ }),
/***/ "crypto-js":
/***/ ((module) => {
"use strict";
module.exports = CryptoJS;
/***/ }),
/***/ "vue":
/***/ ((module) => {
"use strict";
module.exports = Vue;
/***/ }),
/***/ "loglevel":
/***/ ((module) => {
"use strict";
module.exports = log;
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ id: moduleId,
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ (() => {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = (module) => {
/******/ var getter = module && module.__esModule ?
/******/ () => (module['default']) :
/******/ () => (module);
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/global */
/******/ (() => {
/******/ __webpack_require__.g = (function() {
/******/ if (typeof globalThis === 'object') return globalThis;
/******/ try {
/******/ return this || new Function('return this')();
/******/ } catch (e) {
/******/ if (typeof window === 'object') return window;
/******/ }
/******/ })();
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ })();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be in strict mode.
(() => {
"use strict";
// EXTERNAL MODULE: ./node_modules/file-saver/dist/FileSaver.min.js
var FileSaver_min = __webpack_require__("./node_modules/file-saver/dist/FileSaver.min.js");
;// CONCATENATED MODULE: ./src/router/download.ts
async function getRule() {
const host = document.location.host;
let ruleClass;
switch (host) {
case "www.ciweimao.com": {
const { Ciweimao } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/ciweimao.ts"));
ruleClass = Ciweimao;
break;
}
case "www.uukanshu.com": {
const { Uukanshu } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/reprint/uukanshu.ts"));
ruleClass = Uukanshu;
break;
}
case "www.yruan.com": {
const { yruan } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type1.ts"));
ruleClass = yruan();
break;
}
case "www.shuquge.com":
case "www.sizhicn.com": {
const { shuquge } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type2.ts"));
ruleClass = shuquge();
break;
}
case "www.dingdiann.net": {
const { dingdiann } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type3.ts"));
ruleClass = dingdiann();
break;
}
case "www.biquge66.com":
case "www.lewenn.com":
case "www.klxs.la":
case "www.xkzw.org": {
const { Xkzw } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/reprint/xkzw.ts"));
ruleClass = Xkzw;
break;
}
case "www.266ks.com": {
const { c226ks } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/onePageWithMultiIndexPage/226ks.ts"));
ruleClass = c226ks();
break;
}
case "book.sfacg.com": {
const { Sfacg } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/sfacg.ts"));
ruleClass = Sfacg;
break;
}
case "www.hetushu.com":
case "hetushu.com": {
const { Hetushu } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/reprint/hetushu.ts"));
ruleClass = Hetushu;
break;
}
case "www.shouda88.com": {
const { shouda8 } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/onePage/shouda8.ts"));
ruleClass = shouda8();
break;
}
case "www.gebiqu.com": {
const { gebiqu } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type1.ts"));
ruleClass = gebiqu();
break;
}
case "www.viviyzw.com": {
const { viviyzw } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/twoPage/viviyzw.ts"));
ruleClass = viviyzw();
break;
}
case "www.1pwx.com": {
const { xiaoshuodaquan } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/twoPage/1pwx.ts"));
ruleClass = xiaoshuodaquan();
break;
}
case "book.qidian.com": {
const { Qidian } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/qidian.ts"));
ruleClass = Qidian;
break;
}
case "www.jjwxc.net": {
const { Jjwxc } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/jjwxc.ts"));
ruleClass = Jjwxc;
break;
}
case "www.aixiawx.com":
case "www.banzhuer.org":
case "www.biquwoo.com":
case "www.biquwo.org":
case "www.hongyeshuzhai.com": {
const { common } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type1.ts"));
ruleClass = common();
break;
}
case "www.fuguoduxs.com":
case "www.shubaowa.org":
case "www.bz01.org": {
const { common1 } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type1.ts"));
ruleClass = common1();
break;
}
case "www.81book.com":
case "www.81zw.com": {
const { c81book } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type1.ts"));
ruleClass = c81book();
break;
}
case "book.zongheng.com":
case "huayu.zongheng.com": {
const { Zongheng } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/zongheng.ts"));
ruleClass = Zongheng;
break;
}
case "www.17k.com": {
const { C17k } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/17k.ts"));
ruleClass = C17k;
break;
}
case "www.shuhai.com":
case "mm.shuhai.com": {
const { Shuhai } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/shuhai.ts"));
ruleClass = Shuhai;
break;
}
case "www.gongzicp.com":
case "gongzicp.com": {
const { Gongzicp } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/gongzicp.ts"));
ruleClass = Gongzicp;
break;
}
case "www.linovel.net": {
const { Linovel } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/linovel.ts"));
ruleClass = Linovel;
break;
}
case "www.xinwanben.com": {
const { xinwanben } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type3.ts"));
ruleClass = xinwanben();
break;
}
case "www.tadu.com": {
const { Tadu } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/tadu.ts"));
ruleClass = Tadu;
break;
}
case "www.idejian.com": {
const { Idejian } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/reprint/idejian.ts"));
ruleClass = Idejian;
break;
}
case "www.qimao.com": {
const { Qimao } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/qimao.ts"));
ruleClass = Qimao;
break;
}
case "www.wenku8.net": {
const { Wenku8 } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/reprint/wenku8.ts"));
ruleClass = Wenku8;
break;
}
case "manhua.dmzj.com":
case "www.dmzj.com": {
const { Dmzj } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/reprint/dmzj.ts"));
ruleClass = Dmzj;
break;
}
case "sosad.fun":
case "www.sosad.fun":
case "wenzhan.org":
case "www.wenzhan.org":
case "sosadfun.com":
case "www.sosadfun.com":
case "xn--pxtr7m5ny.com":
case "www.xn--pxtr7m5ny.com":
case "xn--pxtr7m.com":
case "www.xn--pxtr7m.com":
case "xn--pxtr7m5ny.net":
case "www.xn--pxtr7m5ny.net":
case "xn--pxtr7m.net":
case "www.xn--pxtr7m.net":
case "sosadfun.link":
case "www.sosadfun.link": {
const { Sosadfun } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/sosadfun.ts"));
ruleClass = Sosadfun;
break;
}
case "www.westnovel.com": {
const { westnovel } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/onePage/westnovel.ts"));
ruleClass = westnovel();
break;
}
case "www.mht.tw":
case "www.mht99.com": {
const { mht } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type3.ts"));
ruleClass = mht();
break;
}
case "www.xbiquge.so": {
const { xbiquge } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type1.ts"));
ruleClass = xbiquge();
break;
}
case "www.linovelib.com": {
const { Linovelib } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/reprint/linovelib.ts"));
ruleClass = Linovelib;
break;
}
case "www.luoqiuzw.com": {
const { luoqiuzw } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type1.ts"));
ruleClass = luoqiuzw();
break;
}
case "www.yibige.cc": {
const { yibige } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/twoPage/yibige.ts"));
ruleClass = yibige();
break;
}
case "www.fushuwang.org": {
const { Fushuwang } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/reprint/fushuwang.ts"));
ruleClass = Fushuwang;
break;
}
case "www.soxscc.net":
case "www.soxscc.org":
case "www.soxs.cc":
case "www.soshuw.com":
case "www.soshuwu.org":
case "www.soxscc.cc":
case "www.soshuwu.com":
case "www.kubiji.net": {
const { Soxscc } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/reprint/soxscc.ts"));
ruleClass = Soxscc;
break;
}
case "www.xyqxs.cc": {
const { xyqxs } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type2.ts"));
ruleClass = xyqxs();
break;
}
case "www.630shu.net": {
const { c630shu } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/onePage/630shu.ts"));
ruleClass = c630shu;
break;
}
case "www.qingoo.cn": {
const { Qingoo } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/qingoo.ts"));
ruleClass = Qingoo;
break;
}
case "www.trxs.cc":
case "www.trxs123.com":
case "www.jpxs123.com":
case "trxs.cc":
case "trxs123.com":
case "jpxs123.com": {
const { trxs } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/onePage/trxs.ts"));
ruleClass = trxs();
break;
}
case "www.tongrenquan.org":
case "www.tongrenquan.me":
case "tongrenquan.me":
case "tongrenquan.org": {
const { tongrenquan } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/onePage/trxs.ts"));
ruleClass = tongrenquan();
break;
}
case "www.imiaobige.com": {
const { imiaobige } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/twoPage/imiaobige.ts"));
ruleClass = imiaobige();
break;
}
case "www.256wxc.com":
case "www.256wenku.com": {
const { c256wxc } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/onePage/256wxc.ts"));
ruleClass = c256wxc;
break;
}
case regExpMatch(/lofter\.com$/): {
const { Lofter } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/lofter.ts"));
ruleClass = Lofter;
break;
}
case "www.lwxs9.org": {
const { lwxs9 } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type1.ts"));
ruleClass = lwxs9();
break;
}
case "www.shubl.com": {
const { Shubl } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/shubl.ts"));
ruleClass = Shubl;
break;
}
case "www.ujxs.net": {
const { ujxs } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/twoPage/ujxs.ts"));
ruleClass = ujxs();
break;
}
case "m.haitangtxt.net": {
const { haitangtxt } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/duplicate/haitangtxt.ts"));
ruleClass = haitangtxt();
break;
}
case "m.yuzhaige.cc":
case "m.yushuge123.com": {
const { yuzhaige } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/duplicate/haitangtxt.ts"));
ruleClass = yuzhaige();
break;
}
case "ebook.longmabook.com":
case "www.longmabookcn.com":
case "ebook.lmbooks.com":
case "www.lmebooks.com":
case "www.haitbook.com":
case "www.htwhbook.com":
case "www.myhtebook.com":
case "www.lovehtbooks.com":
case "www.myhtebooks.com":
case "www.myhtlmebook.com":
case "jp.myhtebook.com":
case "jp.myhtlmebook.com":
case "ebook.urhtbooks.com":
case "www.urhtbooks.com":
case "www.newhtbook.com":
case "www.lvhtebook.com":
case "jp.lvhtebook.com":
case "www.htlvbooks.com": {
const { Longmabook } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/longmabook.ts"));
ruleClass = Longmabook;
break;
}
case "dijiubook.net": {
const { dijiubook } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type1.ts"));
ruleClass = dijiubook();
break;
}
case "www.biquwx.la": {
const { biquwx } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type1.ts"));
ruleClass = biquwx();
break;
}
case "www.25zw.com": {
const { c25zw } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type1.ts"));
ruleClass = c25zw();
break;
}
case "www.tycqxs.com": {
const { tycqxs } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type1.ts"));
ruleClass = tycqxs();
break;
}
case "www.kanunu8.com": {
const { Kanunu8 } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/reprint/kanunu8.ts"));
ruleClass = Kanunu8;
break;
}
case "www.ciyuanji.com": {
const { Ciyuanji } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/ciyuanji.ts"));
ruleClass = Ciyuanji;
break;
}
case "www.wanben.org": {
const { wanben } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/onePage/wanben.ts"));
ruleClass = wanben();
break;
}
case "m.wanben.org": {
const { wanben } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/onePageWithMultiIndexPage/wanben.ts"));
ruleClass = wanben();
break;
}
case "www.ranwen.la": {
const { ranwen } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type1.ts"));
ruleClass = ranwen();
break;
}
case "www.washuge.com": {
const { washuge } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/twoPage/washuge.ts"));
ruleClass = washuge();
break;
}
case "m.baihexs.com": {
const { baihexs } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/onePageWithMultiIndexPage/baihexs.ts"));
ruleClass = baihexs();
break;
}
case "www.quanshuzhai.com": {
const { quanshuzhai } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/onePage/quanshuzhai.ts"));
ruleClass = quanshuzhai();
break;
}
case "masiro.me": {
const { masiro } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/onePage/masiro.ts"));
ruleClass = masiro();
break;
}
case "www.pixiv.net": {
const { Pixiv } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/pixiv.ts"));
ruleClass = Pixiv;
break;
}
case "kakuyomu.jp": {
const { kakuyomu } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/onePage/kakuyomu.ts"));
ruleClass = kakuyomu();
break;
}
case "ncode.syosetu.com": {
const { syosetu } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/onePage/syosetu.ts"));
ruleClass = syosetu();
break;
}
case "syosetu.org": {
const { syosetuOrg } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/onePage/syosetu.ts"));
ruleClass = syosetuOrg();
break;
}
case "zhaoze.art":
case "houhuayuan.xyz": {
const { houhuayuan } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/onePage/houhuayuan.ts"));
ruleClass = houhuayuan();
break;
}
case "www.myrics.com": {
const { Myrics } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/myrics.ts"));
ruleClass = Myrics;
break;
}
case "www.lstxt.cc": {
const { lusetxt } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type2.ts"));
ruleClass = lusetxt();
break;
}
case "www.a7xs.com": {
const { a7xs } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/onePage/a7xs.ts"));
ruleClass = a7xs();
break;
}
case "www.shencou.com": {
const { shencou } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/twoPage/shencou.ts"));
ruleClass = shencou();
break;
}
case "www.tianyabooks.com": {
const { tianyabooks } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/onePage/tianyabooks.ts"));
ruleClass = tianyabooks();
break;
}
case "jingcaiyuedu6.com": {
const { jingcaiyuedu6 } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/twoPage/jingcaiyuedu6.ts"));
ruleClass = jingcaiyuedu6();
break;
}
case "www.hanwujinian.com": {
const { Hanwujinian } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/special/original/hanwujinian.ts"));
ruleClass = Hanwujinian;
break;
}
case "www.biqu55.com": {
const { biqu55 } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/rules/biquge/type3.ts"));
ruleClass = biqu55();
break;
}
default: {
throw new Error("Not Found Rule!");
}
}
const rule = new ruleClass();
return rule;
function regExpMatch(regexp) {
if (regexp.test(host)) {
return host;
}
}
}
;// CONCATENATED MODULE: ./src/debug.ts
async function debug() {
try {
const rule = await getRule();
let book;
if (typeof window._book !== "undefined") {
book = window._book;
}
else {
book = await rule.bookParse();
}
unsafeWindow.rule = rule;
unsafeWindow.book = book;
window._book = book;
}
catch (error) { }
unsafeWindow.saveAs = FileSaver_min.saveAs;
const { parse, fetchAndParse, gfetchAndParse } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/lib/readability.ts"));
const readability = {
parse,
fetchAndParse,
gfetchAndParse,
};
unsafeWindow.readability = readability;
return;
}
// EXTERNAL MODULE: ./src/lib/GM.ts
var GM = __webpack_require__("./src/lib/GM.ts");
// EXTERNAL MODULE: ./src/lib/misc.ts
var misc = __webpack_require__("./src/lib/misc.ts");
// EXTERNAL MODULE: ./src/setting.ts
var src_setting = __webpack_require__("./src/setting.ts");
;// CONCATENATED MODULE: ./src/detect.ts
function check(name) {
const target = window[name];
const targetLength = target.toString().length;
const targetPrototype = target.prototype;
const nativeFunctionRe = /function \w+\(\) {\n?(\s+)?\[native code\]\n?(\s+)?}/;
try {
if (targetPrototype === undefined ||
Boolean(target.toString().match(nativeFunctionRe))) {
return [true, targetLength].join(", ");
}
}
catch {
return [true, targetLength].join(", ");
}
return [false, targetLength].join(", ");
}
const environments = {
当前时间: new Date().toISOString(),
当前页URL: document.location.href,
当前页Referrer: document.referrer,
浏览器UA: navigator.userAgent,
浏览器语言: navigator.languages,
设备运行平台: navigator.platform,
设备内存: navigator.deviceMemory ?? "",
CPU核心数: navigator.hardwareConcurrency,
eval: check("eval"),
fetch: check("fetch"),
XMLHttpRequest: check("XMLHttpRequest"),
window: Object.keys(window).length,
localStorage: (0,misc/* storageAvailable */.oZ)("localStorage"),
sessionStorage: (0,misc/* storageAvailable */.oZ)("sessionStorage"),
Cookie: navigator.cookieEnabled,
doNotTrack: navigator.doNotTrack ?? 0,
ScriptHandler: GM/* _GM_info.scriptHandler */._p.scriptHandler,
"ScriptHandler version": GM/* _GM_info.version */._p.version,
"Novel-downloader version": GM/* _GM_info.script.version */._p.script.version,
enableDebug: src_setting/* enableDebug.value */.Cy.value,
};
;// CONCATENATED MODULE: ./src/global.ts
function init() {
window.downloading = false;
window.customStorage = new misc/* LocalStorageExpired */.Z2();
window.stopFlag = false;
}
// EXTERNAL MODULE: external "log"
var external_log_ = __webpack_require__("loglevel");
var external_log_default = /*#__PURE__*/__webpack_require__.n(external_log_);
// EXTERNAL MODULE: external "Vue"
var external_Vue_ = __webpack_require__("vue");
;// CONCATENATED MODULE: ./src/ui/fixVue.ts
globalThis.Vue = external_Vue_;
globalThis.Function = new Proxy(Function, {
construct(target, args) {
const code = args[args.length - 1];
if (code.includes("Vue") && code.includes("_Vue")) {
external_log_default().debug("Function hook:" + code);
return hook();
}
else {
return new target(...args);
}
function hook() {
function getGlobalObjectKeys() {
const _get = () => {
return Object.getOwnPropertyNames(window).filter((key) => window[key] === window);
};
const _f = new target(`return (${_get.toString()})()`);
return _f();
}
const globalObjectKeys = getGlobalObjectKeys();
const newArgs = [];
newArgs.push(...globalObjectKeys);
args[args.length - 1] = "with (window) {" + code + "}";
newArgs.push(...args);
const _newTarget = new target(...newArgs);
const newTarget = new Proxy(_newTarget, {
apply(targetI, thisArg, argumentsList) {
const newArgumentsList = [];
globalObjectKeys.forEach(() => newArgumentsList.push(window));
newArgumentsList.push(...argumentsList);
return Reflect.apply(targetI, window, newArgumentsList);
},
});
return newTarget;
}
},
});
// EXTERNAL MODULE: ./src/lib/createEl.ts
var createEl = __webpack_require__("./src/lib/createEl.ts");
;// CONCATENATED MODULE: ./src/router/ui.ts
const defaultObject = {
type: "download",
};
const errorObject = {
type: "error",
};
function getUI() {
const host = document.location.host;
switch (host) {
case "wap.shuquge.com": {
return () => {
const id = /(\d+)\.html$/.exec(document.location.pathname)?.[1];
if (!id) {
return errorObject;
}
return {
type: "jump",
jumpFunction() {
document.location.href = `https://www.shuquge.com/txt/${id}/index.html`;
},
};
};
}
case "m.xinwanben.com": {
return () => ({
type: "jump",
jumpFunction() {
document.location.host = "www.xinwanben.com";
},
});
}
case "www.tadu.com": {
return () => {
const re = /^\/book\/\d+\/?$/;
if (re.test(document.location.pathname)) {
return defaultObject;
}
return errorObject;
};
}
case "www.kanunu8.com": {
return () => {
if (document.body.innerHTML.includes("作者:") ||
document.body.innerHTML.includes("作者:") ||
document.body.innerHTML.includes("内容简介")) {
return defaultObject;
}
return errorObject;
};
}
case "www.ciyuanji.com": {
return () => {
if (document.location.pathname === "/bookDetails/info") {
return {
type: "jump",
jumpFunction: () => (document.location.pathname = "/bookDetails/catalog"),
};
}
return defaultObject;
};
}
case "ebook.longmabook.com":
case "www.longmabookcn.com":
case "ebook.lmbooks.com":
case "www.lmebooks.com":
case "www.haitbook.com":
case "www.htwhbook.com":
case "www.myhtebook.com":
case "www.lovehtbooks.com":
case "www.myhtebooks.com":
case "www.myhtlmebook.com":
case "jp.myhtebook.com":
case "jp.myhtlmebook.com":
case "ebook.urhtbooks.com":
case "www.urhtbooks.com":
case "www.newhtbook.com":
case "www.lvhtebook.com":
case "jp.lvhtebook.com":
case "www.htlvbooks.com": {
return () => {
const params = new URLSearchParams(document.location.search);
if (params.get("act") === "showinfo" &&
params.has("bookwritercode") &&
params.has("bookid")) {
return defaultObject;
}
return errorObject;
};
}
case "m.sfacg.com": {
return () => {
const bookId = /(\d+)\/?$/.exec(document.location.pathname)?.[1];
if (bookId) {
return {
type: "jump",
jumpFunction: () => (document.location.href = `https://book.sfacg.com/Novel/${bookId}/MainIndex/`),
};
}
else {
return errorObject;
}
};
}
case "book.sfacg.com": {
return () => {
const jump = /^\/Novel\/\d+\/?$/.test(document.location.pathname);
if (jump) {
const bookId = /(\d+)\/?$/.exec(document.location.pathname)?.[1];
if (bookId) {
return {
type: "jump",
jumpFunction: () => (document.location.href = `https://book.sfacg.com/Novel/${bookId}/MainIndex/`),
};
}
else {
return errorObject;
}
}
else {
return defaultObject;
}
};
}
case "www.ciweimao.com": {
return () => {
const jump = /^\/book\/\d+\/?$/.test(document.location.pathname);
if (jump) {
const bookId = /(\d+)\/?$/.exec(document.location.pathname)?.[1];
if (bookId) {
return {
type: "jump",
jumpFunction: () => (document.location.href = `https://www.ciweimao.com/chapter-list/${bookId}/book_detail`),
};
}
else {
return errorObject;
}
}
else {
return defaultObject;
}
};
}
case "m.lusetxt.com": {
return () => ({
type: "jump",
jumpFunction: () => (document.location.host = "www.lusetxt.com"),
});
}
default: {
return () => defaultObject;
}
}
}
;// CONCATENATED MODULE: ./src/ui/button.html
// Module
var code = " ";
// Exports
/* harmony default export */ const ui_button = (code);
// EXTERNAL MODULE: ./src/ui/button.less
var src_ui_button = __webpack_require__("./src/ui/button.less");
// EXTERNAL MODULE: ./src/main.ts
var main = __webpack_require__("./src/main.ts");
// EXTERNAL MODULE: ./src/save/save.ts + 7 modules
var save = __webpack_require__("./src/save/save.ts");
;// CONCATENATED MODULE: ./src/ui/ChapterList.html
// Module
var ChapterList_code = "
加载章节失败!
正在载入章节列表中,请耐心等待……
{{ sectionObj.sectionName }}
";
// Exports
/* harmony default export */ const ChapterList = (ChapterList_code);
// EXTERNAL MODULE: ./src/ui/ChapterList.less
var ui_ChapterList = __webpack_require__("./src/ui/ChapterList.less");
;// CONCATENATED MODULE: ./src/ui/ChapterList.ts
async function getSections() {
if (window._sections &&
window._url === document.location.href) {
return window._sections;
}
else {
const rule = await getRule();
const book = await rule.bookParse();
window._book = book;
window._url = document.location.href;
window._sections = (0,save/* getSectionsObj */.f0)(book.chapters);
return window._sections;
}
}
const style = (0,createEl/* createStyle */.w)(ui_ChapterList/* default */.Z);
/* harmony default export */ const src_ui_ChapterList = ((0,external_Vue_.defineComponent)({
name: "ChapterList",
setup(props, context) {
const sectionsObj = (0,external_Vue_.reactive)([]);
const loading = (0,external_Vue_.ref)(true);
const failed = (0,external_Vue_.ref)(false);
(0,external_Vue_.onMounted)(async () => {
if (sectionsObj.length === 0) {
try {
const _sectionsObj = await getSections();
Object.assign(sectionsObj, _sectionsObj);
loading.value = false;
}
catch (error) {
external_log_default().error(error);
failed.value = true;
}
}
});
const filterSetting = (0,external_Vue_.inject)("filterSetting");
const filter = (chapter) => {
if (chapter.status === main/* Status.aborted */.qb.aborted) {
return false;
}
if (filterSetting.value) {
const filterFunction = getFilterFunction(filterSetting.value.arg, filterSetting.value.functionBody);
if (typeof filterFunction === "function") {
return filterFunction(chapter);
}
}
return true;
};
const warningFilter = (chapter) => {
if (chapter.isVIP === true && chapter.isPaid !== true) {
return true;
}
return false;
};
const isChapterDisabled = (chapter) => {
if (!chapter?.chapterUrl) {
return true;
}
return false;
};
const isChapterSeen = (chapter) => {
if (filterSetting.value.hiddenBad && filter(chapter) === false) {
return false;
}
else {
return true;
}
};
const isSectionSeen = (sectionObj) => {
const chapters = sectionObj.chpaters;
return chapters.some((chapter) => isChapterSeen(chapter) === true);
};
return {
sectionsObj,
loading,
failed,
filter,
warningFilter,
isChapterDisabled,
isChapterSeen,
isSectionSeen,
};
},
template: ChapterList,
}));
// EXTERNAL MODULE: ./src/ui/FilterTab.css
var FilterTab = __webpack_require__("./src/ui/FilterTab.css");
;// CONCATENATED MODULE: ./src/ui/FilterTab.html
// Module
var FilterTab_code = " 当前过滤方法:
";
// Exports
/* harmony default export */ const ui_FilterTab = (FilterTab_code);
;// CONCATENATED MODULE: ./src/ui/FilterTab.ts
const filterOptionDict = {
null: {
raw: (arg) => {
return (chapter) => true;
},
description: "不应用任何过滤器(默认)
",
abbreviation: "无",
},
number: {
raw: (arg) => {
function characterCheck() {
return /^[\s\d\-,,]+$/.test(arg);
}
function match(s, n) {
switch (true) {
case /^\d+$/.test(s): {
const _m = s.match(/^(\d+)$/);
if (_m?.length === 2) {
const m = Number(_m[1]);
if (m === n) {
return true;
}
}
return false;
}
case /^\d+\-\d+$/.test(s): {
const _m = s.match(/^(\d+)\-(\d+)$/);
if (_m?.length === 3) {
const m = _m.map((_s) => Number(_s));
if (n >= m[1] && n <= m[2]) {
return true;
}
}
return false;
}
case /^\d+\-$/.test(s): {
const _m = s.match(/^(\d+)\-$/);
if (_m?.length === 2) {
const m = Number(_m[1]);
if (n >= m) {
return true;
}
}
return false;
}
case /^\-\d+$/.test(s): {
const _m = s.match(/^\-(\d+)$/);
if (_m?.length === 2) {
const m = Number(_m[1]);
if (n <= m) {
return true;
}
}
return false;
}
default: {
return false;
}
}
}
if (!characterCheck()) {
return;
}
return (chapter) => {
const n = chapter.chapterNumber;
const ss = arg.split(/,|,/).map((s) => s.replace(/\s/g, "").trim());
return ss.map((s) => match(s, n)).some((b) => b === true);
};
},
description: "基于章节序号过滤,章节序号可通过章节标题悬停查看。
支持以下格式:13, 1-5, 2-, -89。可通过分号(,)使用多个表达式。
",
abbreviation: "章节序号",
},
baseOnString: {
raw: (arg) => {
return (chapter) => {
return (chapter && chapter.chapterName?.includes(arg)) || false;
};
},
description: "过滤出所有包含过滤条件字符的章节
",
abbreviation: "章节标题",
},
};
function getFunctionBody(fn) {
return `return (${fn.toString()})(arg)`;
}
function getFilterFunction(arg, functionBody) {
const filterFunctionFactor = new Function("arg", functionBody);
const filterFunction = filterFunctionFactor(arg);
if (typeof filterFunction === "function") {
return filterFunction;
}
else {
return undefined;
}
}
/* harmony default export */ const src_ui_FilterTab = ((0,external_Vue_.defineComponent)({
components: { "chapter-list": src_ui_ChapterList },
emits: ["filterupdate"],
setup(props, { emit }) {
const arg = (0,external_Vue_.ref)("");
const hiddenBad = (0,external_Vue_.ref)(true);
const filterType = (0,external_Vue_.ref)("null");
const filterOptionList = Object.entries(filterOptionDict);
const functionBody = (0,external_Vue_.computed)(() => getFunctionBody(filterOptionDict[filterType.value].raw));
const filterDescription = (0,external_Vue_.computed)(() => filterOptionDict[filterType.value].description);
const filterSetting = (0,external_Vue_.computed)(() => ({
arg: arg.value,
hiddenBad: hiddenBad.value,
filterType: filterType.value,
functionBody: functionBody.value,
}));
(0,external_Vue_.provide)("filterSetting", filterSetting);
(0,external_Vue_.watch)(filterSetting, () => {
emit("filterupdate", filterSetting.value);
}, {
deep: true,
});
const getFilterSetting = (0,external_Vue_.inject)("getFilterSetting");
(0,external_Vue_.onMounted)(() => {
const faterFilterSetting = getFilterSetting();
if (faterFilterSetting) {
arg.value = faterFilterSetting.arg;
hiddenBad.value = faterFilterSetting.hiddenBad;
filterType.value = faterFilterSetting.filterType;
}
});
return {
arg,
hiddenBad,
filterType,
filterOptionList,
filterDescription,
};
},
template: ui_FilterTab,
}));
const FilterTab_style = (0,createEl/* createStyle */.w)(FilterTab/* default */.Z);
// EXTERNAL MODULE: ./src/log.ts
var log = __webpack_require__("./src/log.ts");
;// CONCATENATED MODULE: ./src/ui/LogUI.ts
/* harmony default export */ const LogUI = ((0,external_Vue_.defineComponent)({
name: "LogUI",
setup(props, context) {
const logText = (0,external_Vue_.ref)("");
let requestID;
(0,external_Vue_.onMounted)(() => {
logText.value = (0,log/* getLogText */.mZ)();
function step() {
logText.value = (0,log/* getLogText */.mZ)();
requestID = globalThis.requestAnimationFrame(step);
}
requestID = globalThis.requestAnimationFrame(step);
});
(0,external_Vue_.onUnmounted)(() => {
if (requestID) {
globalThis.cancelAnimationFrame(requestID);
}
});
return { logText };
},
template: ``,
}));
;// CONCATENATED MODULE: ./src/ui/setting.html
// Module
var setting_code = " ";
// Exports
/* harmony default export */ const setting = (setting_code);
// EXTERNAL MODULE: ./src/ui/setting.less
var ui_setting = __webpack_require__("./src/ui/setting.less");
;// CONCATENATED MODULE: ./src/ui/TestUI.html
// Module
var TestUI_code = " ";
// Exports
/* harmony default export */ const TestUI = (TestUI_code);
// EXTERNAL MODULE: ./src/ui/TestUI.less
var ui_TestUI = __webpack_require__("./src/ui/TestUI.less");
;// CONCATENATED MODULE: ./src/ui/TestUI.ts
/* harmony default export */ const src_ui_TestUI = ((0,external_Vue_.defineComponent)({
name: "TestUI",
setup(props, context) {
const book = (0,external_Vue_.reactive)({});
async function waitBook() {
while (true) {
await (0,misc/* sleep */._v)(500);
if (window._book) {
return window._book;
}
}
}
const metaData = (0,external_Vue_.reactive)({});
function getData(key, value) {
if (key === "封面") {
return `
`;
}
if (key === "简介") {
return value.outerHTML;
}
if (key === "网址") {
return `${value}`;
}
return value;
}
const chapter = (0,external_Vue_.reactive)({});
const chapterNumber = (0,external_Vue_.ref)(-99);
function getInitChapterNumber() {
if (book) {
const chapters = book.chapters;
const cns = chapters
.filter((c) => c.status !== main/* Status.aborted */.qb.aborted)
.map((c) => c.chapterNumber);
cns.sort();
return cns.slice(-3)[0];
}
}
async function initChapter(n) {
const chapters = book.chapters;
const _chapter = chapters.filter((c) => c.chapterNumber === n)[0];
if (_chapter) {
if (_chapter.status === main/* Status.pending */.qb.pending) {
await _chapter.init();
Object.assign(chapter, _chapter);
}
else {
Object.assign(chapter, _chapter);
}
}
}
(0,external_Vue_.watch)(chapterNumber, (value, oldValue) => {
if (typeof value === "string") {
value = parseInt(value, 10);
}
if (typeof oldValue === "string") {
oldValue = parseInt(oldValue, 10);
}
if (oldValue !== value) {
if (value !== -99) {
initChapter(value);
}
}
});
function isSeenChapter(_chapter) {
return _chapter.status === main/* Status.finished */.qb.finished;
}
function isChapterFailed(_chapter) {
return (_chapter.status === main/* Status.failed */.qb.failed || _chapter.status === main/* Status.aborted */.qb.aborted);
}
function getChapterHtml(_chapter) {
const imgs = _chapter.contentHTML?.querySelectorAll("img");
if (imgs) {
Array.from(imgs).forEach((img) => {
img.src = img.alt;
});
}
return _chapter.contentHTML?.outerHTML;
}
(0,external_Vue_.onMounted)(async () => {
const _book = await waitBook();
Object.assign(book, _book);
const _metaData = {
封面: book.additionalMetadate?.cover?.url ?? "",
题名: book.bookname ?? "None",
作者: book.author ?? "None",
网址: book.bookUrl,
简介: book.introductionHTML ?? "",
};
Object.assign(metaData, _metaData);
const cn = getInitChapterNumber();
if (cn) {
chapterNumber.value = cn;
}
});
return {
metaData,
getData,
chapter,
isSeenChapter,
isChapterFailed,
getChapterHtml,
chapterNumber,
};
},
template: TestUI,
}));
const TestUI_style = (0,createEl/* createStyle */.w)(ui_TestUI/* default */.Z);
;// CONCATENATED MODULE: ./src/ui/setting.ts
const setting_style = (0,createEl/* createStyle */.w)(ui_setting/* default */.Z);
const el = (0,createEl/* createEl */.u)(``);
const vm = (0,external_Vue_.createApp)({
name: "nd-setting",
components: { "filter-tab": src_ui_FilterTab, "log-ui": LogUI, "test-ui": src_ui_TestUI },
setup(props, context) {
const setting = (0,external_Vue_.reactive)({});
let settingBackup = {};
const saveOptions = [
{ key: "null", value: "不使用自定义保存参数", options: {} },
{
key: "chapter_name",
value: "将章节名称格式修改为 第xx章 xxxx",
options: {
getchapterName: (chapter) => {
if (chapter.chapterName) {
return `第${chapter.chapterNumber.toString()}章 ${chapter.chapterName}`;
}
else {
return `第${chapter.chapterNumber.toString()}章`;
}
},
},
},
{
key: "txt_space",
value: "txt文档每个自然段前加两个空格",
options: {
genChapterText: (chapterName, contentText) => {
contentText = contentText
.split("\n")
.map((line) => {
if (line.trim() === "") {
return line;
}
else {
return line.replace(/^/, " ");
}
})
.join("\n");
return `## ${chapterName}\n\n${contentText}\n\n`;
},
},
},
{
key: "reverse_chapters",
value: "保存章节时倒序排列",
options: {
chapterSort: (a, b) => {
if (a.chapterNumber > b.chapterNumber) {
return -1;
}
if (a.chapterNumber === b.chapterNumber) {
return 0;
}
if (a.chapterNumber < b.chapterNumber) {
return 1;
}
return 0;
},
},
},
];
setting.enableDebug = src_setting/* enableDebug.value */.Cy.value;
setting.chooseSaveOption = "null";
setting.enableTestPage = false;
setting.currentTab = "tab-1";
const curSaveOption = () => {
const _s = saveOptions.find((s) => s.key === setting.chooseSaveOption);
if (_s) {
return _s.options;
}
else {
return saveOptions[0].options;
}
};
const saveFilter = (filterSetting) => {
setting.filterSetting = (0,misc/* deepcopy */.X8)(filterSetting);
};
const getFilterSetting = () => {
if (setting.filterSetting) {
return setting.filterSetting;
}
else {
return;
}
};
(0,external_Vue_.provide)("getFilterSetting", getFilterSetting);
const setConfig = (config) => {
setEnableDebug();
setCustomSaveOption();
setCustomFilter();
function setEnableDebug() {
if (typeof config.enableDebug === "boolean") {
config.enableDebug ? external_log_default().setLevel("trace") : external_log_default().setLevel("info");
src_setting/* enableDebug.value */.Cy.value = config.enableDebug;
if (config.enableDebug) {
debug();
}
external_log_default().info(`[Init]enableDebug: ${src_setting/* enableDebug.value */.Cy.value}`);
}
}
function setCustomSaveOption() {
unsafeWindow.saveOptions = curSaveOption();
}
function setCustomFilter() {
if (config.filterSetting) {
if (config.filterSetting.filterType === "null") {
unsafeWindow.chapterFilter = undefined;
}
else {
const filterFunction = getFilterFunction(config.filterSetting.arg, config.filterSetting.functionBody);
if (filterFunction) {
const chapterFilter = (chapter) => {
if (chapter.status === main/* Status.aborted */.qb.aborted) {
return false;
}
return filterFunction(chapter);
};
unsafeWindow.chapterFilter = chapterFilter;
}
}
}
}
};
const openStatus = (0,external_Vue_.ref)("false");
const openSetting = () => {
settingBackup = (0,misc/* deepcopy */.X8)(setting);
openStatus.value = "true";
};
const closeSetting = (keep) => {
if (keep === true) {
settingBackup = (0,misc/* deepcopy */.X8)(setting);
}
else {
Object.assign(setting, settingBackup);
}
openStatus.value = "false";
};
const closeAndSaveSetting = async () => {
closeSetting(true);
await (0,misc/* sleep */._v)(30);
setConfig((0,misc/* deepcopy */.X8)(setting));
external_log_default().info("[Init]自定义设置:" + JSON.stringify(setting));
};
return {
openStatus,
openSetting,
closeSetting,
closeAndSaveSetting,
saveFilter,
setting,
saveOptions,
};
},
template: setting,
}).mount(el);
;// CONCATENATED MODULE: ./src/ui/button.ts
const button_style = (0,createEl/* createStyle */.w)(src_ui_button/* default */.Z, "button-div-style");
const button_el = (0,createEl/* createEl */.u)('');
const button_vm = (0,external_Vue_.createApp)({
data() {
return {
imgStart: src_setting/* iconStart0 */.cl,
imgSetting: src_setting/* iconSetting */.d7,
isSettingSeen: GM/* _GM_info.scriptHandler */._p.scriptHandler !== "Greasemonkey",
imgJump: src_setting/* iconJump */.y6,
uiObj: {},
};
},
methods: {
startButtonClick() {
if (window.downloading) {
alert("正在下载中,请耐心等待……");
return;
}
const self = this;
self.imgStart = src_setting/* iconStart1 */.wE;
async function run() {
const ruleClass = await getRule();
await ruleClass.run();
}
run()
.then(() => {
self.imgStart = src_setting/* iconStart0 */.cl;
})
.catch((error) => external_log_default().error(error));
},
settingButtonClick() {
vm.openSetting();
},
jumpButtonClick() {
this.uiObj.jumpFunction();
},
},
mounted() {
Object.assign(this.uiObj, getUI()());
},
template: ui_button,
});
// EXTERNAL MODULE: ./src/ui/dialog.css
var dialog = __webpack_require__("./src/ui/dialog.css");
;// CONCATENATED MODULE: ./src/ui/dialog.html
// Module
var dialog_code = " ";
// Exports
/* harmony default export */ const ui_dialog = (dialog_code);
;// CONCATENATED MODULE: ./src/ui/dialog.ts
/* harmony default export */ const src_ui_dialog = ((0,external_Vue_.defineCustomElement)({
name: "Dialog",
props: {
dialogTitle: String,
status: String,
},
emits: ["dialogclose"],
data() {
return {
myPrivateStatus: this.status === "true",
};
},
methods: {
dialogClose() {
this.myPrivateStatus = false;
this.$emit("dialogclose");
},
},
mounted() {
this.myPrivateStatus = this.status === "true";
},
watch: {
status() {
this.myPrivateStatus = this.status === "true";
},
},
template: ui_dialog,
styles: [dialog/* default */.Z],
}));
// EXTERNAL MODULE: ./src/ui/progress.ts + 1 modules
var progress = __webpack_require__("./src/ui/progress.ts");
;// CONCATENATED MODULE: ./src/ui/ui.ts
function register() {
customElements.define("dialog-ui", src_ui_dialog);
}
function ui_init() {
register();
button_vm.mount(button_el);
document.body.appendChild(button_el);
document.body.appendChild(progress.el);
document.body.appendChild(el);
document.head.appendChild(button_style);
document.head.appendChild(progress/* style */.o);
document.head.appendChild(setting_style);
document.head.appendChild(FilterTab_style);
document.head.appendChild(style);
document.head.appendChild(TestUI_style);
}
;// CONCATENATED MODULE: ./src/index.ts
function printEnvironments() {
external_log_default().info("[Init]开始载入小说下载器……");
Object.entries(environments).forEach((kv) => external_log_default().info("[Init]" + kv.join(":")));
}
function src_main() {
printEnvironments();
init();
ui_init();
if (src_setting/* enableDebug.value */.Cy.value) {
setTimeout(debug, 3000);
}
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", (event) => {
src_main();
});
}
else {
src_main();
}
})();
/******/ })()
;