From cfc412399b3589868bb7060a7636d0b63ce1f1df Mon Sep 17 00:00:00 2001 From: dteviot Date: Thu, 29 Sep 2016 10:24:54 +1300 Subject: [PATCH] Changed assorted files to class syntax. * EpubItem * EpubItemSupplier * Parser * BakaTsuki Parser * Sonako parser --- plugin/js/EpubItem.js | 55 ++- plugin/js/EpubItemSupplier.js | 61 +-- plugin/js/Parser.js | 282 +++++++------- plugin/js/parsers/BakaTsukiParser.js | 554 +++++++++++++-------------- plugin/js/parsers/SonakoParser.js | 74 ++-- 5 files changed, 512 insertions(+), 514 deletions(-) diff --git a/plugin/js/EpubItem.js b/plugin/js/EpubItem.js index 7eb8572c..286b9737 100644 --- a/plugin/js/EpubItem.js +++ b/plugin/js/EpubItem.js @@ -56,19 +56,19 @@ class EpubItem { // ToDo: assert that tagName in range

...

return tagName[1] - "1"; } -} -EpubItem.prototype.chapterInfo = function*() { - let that = this; - for(let element of that.elements) { - if (util.isHeaderTag(element)) { - yield { - depth: this.tagNameToTocDepth(element.tagName), - title: element.textContent, - src: that.getZipHref() + *chapterInfo() { + let that = this; + for(let element of that.elements) { + if (util.isHeaderTag(element)) { + yield { + depth: this.tagNameToTocDepth(element.tagName), + title: element.textContent, + src: that.getZipHref() + }; }; }; - }; + } } //============================================================== @@ -82,23 +82,23 @@ class ChapterEpubItem extends EpubItem { this.chapterTitle = chapter.title; this.newArc = chapter.newArc; } -} -ChapterEpubItem.prototype.chapterInfo = function*() { - let that = this; - if (that.newArc !== null) { - yield { - depth: 0, - title: that.newArc, - src: that.getZipHref() + *chapterInfo() { + let that = this; + if (that.newArc !== null) { + yield { + depth: 0, + title: that.newArc, + src: that.getZipHref() + } } - } - if (typeof (that.chapterTitle) !== "undefined") { - yield { - depth: 1, - title: that.chapterTitle, - src: that.getZipHref() + if (typeof (that.chapterTitle) !== "undefined") { + yield { + depth: 1, + title: that.chapterTitle, + src: that.getZipHref() + } } } } @@ -259,9 +259,8 @@ class ImageInfo extends EpubItem { div.appendChild(util.createComment(doc, origin)); return div; } -} -ImageInfo.prototype.chapterInfo = function*() { - // images do not appear in table of contents + *chapterInfo() { + // images do not appear in table of contents + } } - diff --git a/plugin/js/EpubItemSupplier.js b/plugin/js/EpubItemSupplier.js index fbd2b739..6b6bc42e 100644 --- a/plugin/js/EpubItemSupplier.js +++ b/plugin/js/EpubItemSupplier.js @@ -16,40 +16,41 @@ class EpubItemSupplier { let that = this; this.coverImageId = () => that.coverImageInfo.getId(); }; -} -// used to populate manifest -EpubItemSupplier.prototype.manifestItems = function() { - return this.epubItems; -} -// used to populate spine -EpubItemSupplier.prototype.spineItems = function() { - return this.epubItems.filter(item => item.isInSpine); -} + // used to populate manifest + manifestItems() { + return this.epubItems; + } -// used to populate Zip file itself -EpubItemSupplier.prototype.files = function() { - return this.epubItems; -} + // used to populate spine + spineItems() { + return this.epubItems.filter(item => item.isInSpine); + } -// used to populate table of contents -EpubItemSupplier.prototype.chapterInfo = function*() { - let that = this; - for(let epubItem of that.epubItems) { - yield* epubItem.chapterInfo(); - }; -} + // used to populate Zip file itself + files() { + return this.epubItems; + } -EpubItemSupplier.prototype.makeCoverImageXhtmlFile = function() { - let that = this; - let doc = util.createEmptyXhtmlDoc(); - let body = doc.getElementsByTagName("body")[0]; - let userPreferences = that.imageCollector.userPreferences; - body.appendChild(that.coverImageInfo.createImageElement(userPreferences)); - return util.xmlToString(doc); -} + // used to populate table of contents + *chapterInfo() { + let that = this; + for(let epubItem of that.epubItems) { + yield* epubItem.chapterInfo(); + }; + } -EpubItemSupplier.prototype.hasCoverImageFile = function() { - return (this.coverImageInfo != null); + makeCoverImageXhtmlFile() { + let that = this; + let doc = util.createEmptyXhtmlDoc(); + let body = doc.getElementsByTagName("body")[0]; + let userPreferences = that.imageCollector.userPreferences; + body.appendChild(that.coverImageInfo.createImageElement(userPreferences)); + return util.xmlToString(doc); + } + + hasCoverImageFile() { + return (this.coverImageInfo != null); + } } diff --git a/plugin/js/Parser.js b/plugin/js/Parser.js index 73991666..a2b337e4 100644 --- a/plugin/js/Parser.js +++ b/plugin/js/Parser.js @@ -129,169 +129,169 @@ class Parser { */ extractSeriesInfo(dom, metaInfo) { // eslint-disable-line no-unused-vars } -} -Parser.prototype.getEpubMetaInfo = function (dom){ - let that = this; - let metaInfo = new EpubMetaInfo(); - metaInfo.uuid = dom.baseURI; - metaInfo.title = that.extractTitle(dom); - metaInfo.author = that.extractAuthor(dom); - metaInfo.language = that.extractLanguage(dom); - metaInfo.fileName = that.makeFileName(metaInfo.title); - that.extractSeriesInfo(dom, metaInfo); - return metaInfo; -} + getEpubMetaInfo(dom){ + let that = this; + let metaInfo = new EpubMetaInfo(); + metaInfo.uuid = dom.baseURI; + metaInfo.title = that.extractTitle(dom); + metaInfo.author = that.extractAuthor(dom); + metaInfo.language = that.extractLanguage(dom); + metaInfo.fileName = that.makeFileName(metaInfo.title); + that.extractSeriesInfo(dom, metaInfo); + return metaInfo; + } -Parser.prototype.singleChapterStory = function (baseUrl, dom) { - return [{ - sourceUrl: baseUrl, - title: this.extractTitle(dom) - }]; -} + singleChapterStory(baseUrl, dom) { + return [{ + sourceUrl: baseUrl, + title: this.extractTitle(dom) + }]; + } -Parser.prototype.getBaseUrl = function (dom) { - return Array.prototype.slice.apply(dom.getElementsByTagName("base"))[0].href; -} + getBaseUrl(dom) { + return Array.prototype.slice.apply(dom.getElementsByTagName("base"))[0].href; + } -// Name to save EPUB file as. -Parser.prototype.makeFileName = function(title) { - if (title == null) { - return "web.epub"; - } else { - // allow only legal characters within the file name - title = util.safeForFileName(title); + // Name to save EPUB file as. + makeFileName(title) { + if (title == null) { + return "web.epub"; + } else { + // allow only legal characters within the file name + title = util.safeForFileName(title); - // append suffix - return title + ".epub"; + // append suffix + return title + ".epub"; + } } -} - -Parser.prototype.epubItemSupplier = function () { - let that = this; - let epubItems = that.chaptersToEpubItems(that.chapters); - let supplier = new EpubItemSupplier(this, epubItems, that.imageCollector); - return supplier; -} -Parser.prototype.chaptersToEpubItems = function (chapters) { - let that = this; - let epubItems = []; - let index = 0; - for(let chapter of chapters.filter(c => that.isChapterPackable(c))) { - let newItems = that.chapterToEpubItems(chapter, index); - epubItems = epubItems.concat(newItems); - index += newItems.length; + epubItemSupplier() { + let that = this; + let epubItems = that.chaptersToEpubItems(that.chapters); + let supplier = new EpubItemSupplier(this, epubItems, that.imageCollector); + return supplier; } - return epubItems; -} -// called when plugin has obtained the first web page -Parser.prototype.onLoadFirstPage = function (url, firstPageDom) { - let that = this; - - // returns promise, because may need to fetch additional pages to find list of chapters - that.getChapterUrls(firstPageDom).then(function(chapters) { - let chapterUrlsUI = new ChapterUrlsUI(that); - chapterUrlsUI.populateChapterUrlsTable(chapters, that.userPreferences); - if (0 < chapters.length) { - if (chapters[0].sourceUrl === url) { - chapters[0].rawDom = firstPageDom; - that.updateLoadState(chapters[0]); - } - that.getProgressBar().value = 0; + chaptersToEpubItems(chapters) { + let that = this; + let epubItems = []; + let index = 0; + for(let chapter of chapters.filter(c => that.isChapterPackable(c))) { + let newItems = that.chapterToEpubItems(chapter, index); + epubItems = epubItems.concat(newItems); + index += newItems.length; } - that.chapters = chapters; - chapterUrlsUI.connectButtonHandlers(); - }).catch(function (err) { - window.showErrorMessage(err); - }); -} - -Parser.prototype.onFetchChaptersClicked = function () { - if (0 == this.chapters.length) { - window.showErrorMessage(chrome.i18n.getMessage("noChaptersFoundAndFetchClicked")); - } else { - this.getFetchContentButton().disabled = true; - this.fetchChapters(); + return epubItems; } -} -Parser.prototype.fetchContent = function () { - return this.fetchChapters(); -} - -Parser.prototype.setUiToShowLoadingProgress = function(length) { - main.getPackEpubButton().disabled = true; - this.getProgressBar().max = length + 1; - this.getProgressBar().value = 1; -} + // called when plugin has obtained the first web page + onLoadFirstPage(url, firstPageDom) { + let that = this; + + // returns promise, because may need to fetch additional pages to find list of chapters + that.getChapterUrls(firstPageDom).then(function(chapters) { + let chapterUrlsUI = new ChapterUrlsUI(that); + chapterUrlsUI.populateChapterUrlsTable(chapters, that.userPreferences); + if (0 < chapters.length) { + if (chapters[0].sourceUrl === url) { + chapters[0].rawDom = firstPageDom; + that.updateLoadState(chapters[0]); + } + that.getProgressBar().value = 0; + } + that.chapters = chapters; + chapterUrlsUI.connectButtonHandlers(); + }).catch(function (err) { + window.showErrorMessage(err); + }); + } -Parser.prototype.fetchChapters = function() { - let that = this; + onFetchChaptersClicked() { + if (0 == this.chapters.length) { + window.showErrorMessage(chrome.i18n.getMessage("noChaptersFoundAndFetchClicked")); + } else { + this.getFetchContentButton().disabled = true; + this.fetchChapters(); + } + } - let pagesToFetch = that.chapters.filter(c => c.isIncludeable); - if (pagesToFetch.length === 0) { - return Promise.reject(new Error("No chapters found.")); + fetchContent() { + return this.fetchChapters(); } - this.setUiToShowLoadingProgress(pagesToFetch.length); + setUiToShowLoadingProgress(length) { + main.getPackEpubButton().disabled = true; + this.getProgressBar().max = length + 1; + this.getProgressBar().value = 1; + } - var sequence = Promise.resolve(); + fetchChapters() { + let that = this; - that.imageCollector.reset(); - that.imageCollector.setCoverImageUrl(CoverImageUI.getCoverImageUrl()); + let pagesToFetch = that.chapters.filter(c => c.isIncludeable); + if (pagesToFetch.length === 0) { + return Promise.reject(new Error("No chapters found.")); + } - pagesToFetch.forEach(function(chapter) { - sequence = sequence.then(function () { - return HttpClient.wrapFetch(chapter.sourceUrl); - }).then(function (xhr) { - chapter.rawDom = xhr.responseXML; - that.updateLoadState(chapter); - let content = that.findContent(chapter.rawDom); - if (content == null) { - chapter.isIncludeable = false; - let errorMsg = chrome.i18n.getMessage("errorContentNotFound", [chapter.sourceUrl]); - throw new Error(errorMsg); - } - that.imageCollector.findImagesUsedInDocument(content); - return that.imageCollector.fetchImages(() => { }); - }); - }); - sequence = sequence.then(function() { - that.getFetchContentButton().disabled = false; - main.getPackEpubButton().disabled = false; - }).catch(function (err) { - util.logError(err); - }) - return sequence; -} + this.setUiToShowLoadingProgress(pagesToFetch.length); + + var sequence = Promise.resolve(); + + that.imageCollector.reset(); + that.imageCollector.setCoverImageUrl(CoverImageUI.getCoverImageUrl()); + + pagesToFetch.forEach(function(chapter) { + sequence = sequence.then(function () { + return HttpClient.wrapFetch(chapter.sourceUrl); + }).then(function (xhr) { + chapter.rawDom = xhr.responseXML; + that.updateLoadState(chapter); + let content = that.findContent(chapter.rawDom); + if (content == null) { + chapter.isIncludeable = false; + let errorMsg = chrome.i18n.getMessage("errorContentNotFound", [chapter.sourceUrl]); + throw new Error(errorMsg); + } + that.imageCollector.findImagesUsedInDocument(content); + return that.imageCollector.fetchImages(() => { }); + }); + }); + sequence = sequence.then(function() { + that.getFetchContentButton().disabled = false; + main.getPackEpubButton().disabled = false; + }).catch(function (err) { + util.logError(err); + }) + return sequence; + } -Parser.prototype.updateLoadState = function(chapter) { - chapter.stateColumn.innerText = "Yes"; - this.getProgressBar().value += 1; -} + updateLoadState(chapter) { + chapter.stateColumn.innerText = "Yes"; + this.getProgressBar().value += 1; + } -Parser.prototype.getProgressBar = function() { - return document.getElementById("fetchProgress"); -} + getProgressBar() { + return document.getElementById("fetchProgress"); + } -Parser.prototype.getFetchContentButton = function() { - return document.getElementById("fetchChaptersButton") -} + getFetchContentButton() { + return document.getElementById("fetchChaptersButton") + } -// pack the raw chapter HTML into a zip file (for later manual analysis) -Parser.prototype.packRawChapters = function() { - let that = this; - let zipFile = new JSZip(); - for (let i = 0; i < that.chapters.length; ++i) { - if (that.chapters[i].rawDom != null) { - zipFile.file("chapter" + i + ".html", that.chapters[i].rawDom.documentElement.outerHTML, { compression: "DEFLATE" }); - }; + // pack the raw chapter HTML into a zip file (for later manual analysis) + packRawChapters() { + let that = this; + let zipFile = new JSZip(); + for (let i = 0; i < that.chapters.length; ++i) { + if (that.chapters[i].rawDom != null) { + zipFile.file("chapter" + i + ".html", that.chapters[i].rawDom.documentElement.outerHTML, { compression: "DEFLATE" }); + }; + } + zipFile.generateAsync({ type: "blob" }).then(function(content) { + EpubPacker.save(content, "raw.zip"); + }).catch(function(error) { + window.showErrorMessage(error); + }); } - zipFile.generateAsync({ type: "blob" }).then(function(content) { - EpubPacker.save(content, "raw.zip"); - }).catch(function(error) { - window.showErrorMessage(error); - }); } diff --git a/plugin/js/parsers/BakaTsukiParser.js b/plugin/js/parsers/BakaTsukiParser.js index e282181c..f98a7647 100644 --- a/plugin/js/parsers/BakaTsukiParser.js +++ b/plugin/js/parsers/BakaTsukiParser.js @@ -110,329 +110,327 @@ class BakaTsukiParser extends Parser{ ++index; }; } -} -BakaTsukiParser.prototype.extractTitle = function(dom) { - return util.getElement(dom, "h1", e => (e.className === "firstHeading") ).textContent.trim(); -}; - -BakaTsukiParser.prototype.extractLanguage = function(dom) { - return util.getElement(dom, "html").getAttribute("lang"); -}; - -BakaTsukiParser.prototype.extractSeriesInfo = function(dom, metaInfo) { - // assumes element text is "<series name>:Volume <series index> - Baka Tsuki" - let that = this; - let title = util.getElement(dom, "title").innerText.trim(); - let splitIndex = title.indexOf(":"); - if (0 < splitIndex) { - metaInfo.seriesName = title.substring(0, splitIndex); - metaInfo.seriesIndex = that.extractVolumeIndex(title.substring(splitIndex)); - }; -} + extractTitle(dom) { + return util.getElement(dom, "h1", e => (e.className === "firstHeading") ).textContent.trim(); + } -BakaTsukiParser.prototype.extractVolumeIndex = function(volumeString) { - let volumeIndex = ""; - for(let ch of volumeString) { - if (("0" <= ch) && (ch <= "9")) { - volumeIndex += ch; - }; - }; - return volumeIndex; -} + extractLanguage(dom) { + return util.getElement(dom, "html").getAttribute("lang"); + } -// find the node(s) holding the story content -BakaTsukiParser.prototype.findContent = function (dom) { - return util.getElement(dom, "div", e => (e.className === "mw-content-ltr") ); -}; - -// called when plugin has obtained the first web page -BakaTsukiParser.prototype.onLoadFirstPage = function (url, firstPageDom) { - let that = this; - that.firstPageDom = firstPageDom; - - let content = that.findContent(that.firstPageDom).cloneNode(true); - that.removeUnwantedElementsFromContentElement(content); - that.imageCollector.findImagesUsedInDocument(content); - that.populateImageTable(); -}; - -BakaTsukiParser.prototype.populateUI = function (dom) { // eslint-disable-line no-unused-vars - document.getElementById("higestResolutionImagesRow").hidden = false; - document.getElementById("imageSection").hidden = false; - document.getElementById("outputSection").hidden = true; - document.getElementById("translatorRow").hidden = false; - document.getElementById("fileAuthorAsRow").hidden = false; - this.getFetchContentButton().onclick = this.onFetchImagesClicked.bind(this); - document.getElementById("coverFromUrlCheckboxInput").onclick = this.populateImageTable.bind(this); -}; - -BakaTsukiParser.prototype.epubItemSupplier = function () { - let that = this; - let content = that.findContent(that.firstPageDom).cloneNode(true); - that.removeUnwantedElementsFromContentElement(content); - util.fixBlockTagsNestedInInlineTags(content); - that.replaceImageTags(content); - util.removeUnusedHeadingLevels(content); - util.prepForConvertToXhtml(content); - util.removeEmptyDivElements(content); - let epubItems = that.splitContentIntoSections(content, that.firstPageDom.baseURI); - that.fixupInternalHyperLinks(epubItems); - return new EpubItemSupplier(that, epubItems, that.imageCollector); -} + extractSeriesInfo(dom, metaInfo) { + // assumes <title> element text is "<series name>:Volume <series index> - Baka Tsuki" + let that = this; + let title = util.getElement(dom, "title").innerText.trim(); + let splitIndex = title.indexOf(":"); + if (0 < splitIndex) { + metaInfo.seriesName = title.substring(0, splitIndex); + metaInfo.seriesIndex = that.extractVolumeIndex(title.substring(splitIndex)); + }; + } -BakaTsukiParser.prototype.removeUnwantedElementsFromContentElement = function (element) { - let that = this; - util.removeScriptableElements(element); - - // discard table of contents (will generate one from tags later) - util.removeElements(util.getElements(element, "div", e => (e.id === "toc"))); - - // remove "Jump Up" text that appears beside the up arrow from translator notes - util.removeElements(util.getElements(element, "span", e => (e.className === "cite-accessibility-label"))); - - util.removeUnneededIds(element); - - util.removeComments(element); - that.removeUnwantedTable(element); - - // hyperlinks that allow editing text - util.removeElements(util.getElements(element, "span", e => (e.className === "mw-editsection"))); -}; - -// There's a table at end of content, with links to other stories on Baka Tsuki. -// It's not wanted in the EPUB -BakaTsukiParser.prototype.removeUnwantedTable = function (element) { - // sometimes the target table has other tables nested in it. - let that = this; - let tables = util.getElements(element, "table"); - if (0 < tables.length) { - let endTable = tables[tables.length - 1]; - let node = endTable; - while (node.parentNode != null) { - node = node.parentNode; - if (node.tagName === "TABLE") { - endTable = node; + extractVolumeIndex(volumeString) { + let volumeIndex = ""; + for(let ch of volumeString) { + if (("0" <= ch) && (ch <= "9")) { + volumeIndex += ch; }; - }; - if (that.isTableContainsHyperLinks(endTable)) { - util.removeNode(endTable); - }; + }; + return volumeIndex; } -} -BakaTsukiParser.prototype.isTableContainsHyperLinks = function(tableElement) { - return util.getElement(tableElement, "a") !== null; -} + findContent(dom) { + return util.getElement(dom, "div", e => (e.className === "mw-content-ltr") ); + } -BakaTsukiParser.prototype.replaceImageTags = function (element) { - let that = this; - that.stripGalleryBox(element); - that.imageCollector.replaceImageTags(element); -} + onLoadFirstPage(url, firstPageDom) { + let that = this; + that.firstPageDom = firstPageDom; + + let content = that.findContent(that.firstPageDom).cloneNode(true); + that.removeUnwantedElementsFromContentElement(content); + that.imageCollector.findImagesUsedInDocument(content); + that.populateImageTable(); + } + + populateUI(dom) { // eslint-disable-line no-unused-vars + document.getElementById("higestResolutionImagesRow").hidden = false; + document.getElementById("imageSection").hidden = false; + document.getElementById("outputSection").hidden = true; + document.getElementById("translatorRow").hidden = false; + document.getElementById("fileAuthorAsRow").hidden = false; + this.getFetchContentButton().onclick = this.onFetchImagesClicked.bind(this); + document.getElementById("coverFromUrlCheckboxInput").onclick = this.populateImageTable.bind(this); + } + + epubItemSupplier() { + let that = this; + let content = that.findContent(that.firstPageDom).cloneNode(true); + that.removeUnwantedElementsFromContentElement(content); + util.fixBlockTagsNestedInInlineTags(content); + that.replaceImageTags(content); + util.removeUnusedHeadingLevels(content); + util.prepForConvertToXhtml(content); + util.removeEmptyDivElements(content); + let epubItems = that.splitContentIntoSections(content, that.firstPageDom.baseURI); + that.fixupInternalHyperLinks(epubItems); + return new EpubItemSupplier(that, epubItems, that.imageCollector); + } + + removeUnwantedElementsFromContentElement(element) { + let that = this; + util.removeScriptableElements(element); + + // discard table of contents (will generate one from tags later) + util.removeElements(util.getElements(element, "div", e => (e.id === "toc"))); -// remove gallery text and move images out of the gallery box so images can take full screen. -BakaTsukiParser.prototype.stripGalleryBox = function (element) { + // remove "Jump Up" text that appears beside the up arrow from translator notes + util.removeElements(util.getElements(element, "span", e => (e.className === "cite-accessibility-label"))); - // move images out of the <ul> gallery - let garbage = new Set(); - for(let listItem of util.getElements(element, "li", e => (e.className === "gallerybox"))) { - util.removeElements(util.getElements(listItem, "div", e => (e.className === "gallerytext"))); + util.removeUnneededIds(element); - let gallery = listItem.parentNode; - garbage.add(gallery); - gallery.parentNode.insertBefore(listItem.firstChild, gallery); + util.removeComments(element); + that.removeUnwantedTable(element); + + // hyperlinks that allow editing text + util.removeElements(util.getElements(element, "span", e => (e.className === "mw-editsection"))); } - // throw away rest of gallery (note sometimes there are multiple galleries) - for(let node of garbage) { - util.removeNode(node); + // There's a table at end of content, with links to other stories on Baka Tsuki. + // It's not wanted in the EPUB + removeUnwantedTable(element) { + // sometimes the target table has other tables nested in it. + let that = this; + let tables = util.getElements(element, "table"); + if (0 < tables.length) { + let endTable = tables[tables.length - 1]; + let node = endTable; + while (node.parentNode != null) { + node = node.parentNode; + if (node.tagName === "TABLE") { + endTable = node; + }; + }; + if (that.isTableContainsHyperLinks(endTable)) { + util.removeNode(endTable); + }; + } } -} -BakaTsukiParser.prototype.splitContentIntoSections = function (content, sourceUrl) { - let that = this; - that.flattenContent(content); - let epubItems = BakaTsukiParser.splitContentOnHeadingTags(content, sourceUrl); - epubItems = that.consolidateEpubItems(epubItems); - epubItems = that.discardEpubItemsWithNoVisibleContent(epubItems); - BakaTsukiParser.indexEpubItems(epubItems, 0); - return epubItems; -} + isTableContainsHyperLinks(tableElement) { + return util.getElement(tableElement, "a") !== null; + } -BakaTsukiParser.prototype.flattenContent = function (content) { - // most pages have all header tags as immediate children of the content element - // where this is not the case, flatten them so that they are. - let that = this; - for(let i = 0; i < content.childNodes.length; ++i) { - let node = content.childNodes[i]; - if (that.nodeNeedsToBeFlattened(node)) { - for(let j = node.childNodes.length - 1; 0 <= j; --j) { - that.insertAfter(node, node.childNodes[j]); - } + replaceImageTags(element) { + let that = this; + that.stripGalleryBox(element); + that.imageCollector.replaceImageTags(element); + } + + // remove gallery text and move images out of the gallery box so images can take full screen. + stripGalleryBox(element) { + + // move images out of the <ul> gallery + let garbage = new Set(); + for(let listItem of util.getElements(element, "li", e => (e.className === "gallerybox"))) { + util.removeElements(util.getElements(listItem, "div", e => (e.className === "gallerytext"))); + + let gallery = listItem.parentNode; + garbage.add(gallery); + gallery.parentNode.insertBefore(listItem.firstChild, gallery); + } + + // throw away rest of gallery (note sometimes there are multiple galleries) + for(let node of garbage) { util.removeNode(node); - --i; } } -} -BakaTsukiParser.prototype.nodeNeedsToBeFlattened = function (node) { - let that = this; - let numHeaders = that.numberOfHeaderTags(node); - return ((1 < numHeaders) || ((numHeaders === 1) && !BakaTsukiParser.isChapterStart(node))); -} + splitContentIntoSections(content, sourceUrl) { + let that = this; + that.flattenContent(content); + let epubItems = BakaTsukiParser.splitContentOnHeadingTags(content, sourceUrl); + epubItems = that.consolidateEpubItems(epubItems); + epubItems = that.discardEpubItemsWithNoVisibleContent(epubItems); + BakaTsukiParser.indexEpubItems(epubItems, 0); + return epubItems; + } -BakaTsukiParser.prototype.numberOfHeaderTags = function (node) { - let walker = document.createTreeWalker(node); - let count = 0; - do { - if (BakaTsukiParser.isChapterStart(walker.currentNode)) { - ++count; - }; - } while (walker.nextNode()); - return count; -} + flattenContent(content) { + // most pages have all header tags as immediate children of the content element + // where this is not the case, flatten them so that they are. + let that = this; + for(let i = 0; i < content.childNodes.length; ++i) { + let node = content.childNodes[i]; + if (that.nodeNeedsToBeFlattened(node)) { + for(let j = node.childNodes.length - 1; 0 <= j; --j) { + that.insertAfter(node, node.childNodes[j]); + } + util.removeNode(node); + --i; + } + } + } -BakaTsukiParser.prototype.insertAfter = function (atNode, nodeToInsert) { - let nextSibling = atNode.nextSibling; - if (nextSibling != null) { - atNode.parentNode.insertBefore(nodeToInsert, nextSibling); - } else { - atNode.parentNode.appendChild(nodeToInsert); + nodeNeedsToBeFlattened(node) { + let that = this; + let numHeaders = that.numberOfHeaderTags(node); + return ((1 < numHeaders) || ((numHeaders === 1) && !BakaTsukiParser.isChapterStart(node))); } -} -// If a epubItem only holds a heading element, combine with following epubItem. -// e.g. We're dealing with <h1> followed by <h2> -BakaTsukiParser.prototype.consolidateEpubItems = function (epubItems) { - let newEpubItems = [ epubItems[epubItems.length - 1] ]; - let i = epubItems.length - 2; - while (0 <= i) { - let epubItem = epubItems[i]; - if (epubItem.elements.length === 1) { - newEpubItems[0].elements.unshift(epubItem.elements[0]); + numberOfHeaderTags(node) { + let walker = document.createTreeWalker(node); + let count = 0; + do { + if (BakaTsukiParser.isChapterStart(walker.currentNode)) { + ++count; + }; + } while (walker.nextNode()); + return count; + } + + insertAfter(atNode, nodeToInsert) { + let nextSibling = atNode.nextSibling; + if (nextSibling != null) { + atNode.parentNode.insertBefore(nodeToInsert, nextSibling); } else { - newEpubItems.unshift(epubItem); + atNode.parentNode.appendChild(nodeToInsert); } - --i; } - return newEpubItems; -} -BakaTsukiParser.prototype.discardEpubItemsWithNoVisibleContent = function(epubItems) { - let that = this; - return epubItems.filter(item => that.hasVisibleContent(item.elements)); -} - -BakaTsukiParser.prototype.hasVisibleContent = function(elements) { - for (let element of elements) { - if (!util.isElementWhiteSpace(element)) { - return true; + // If a epubItem only holds a heading element, combine with following epubItem. + // e.g. We're dealing with <h1> followed by <h2> + consolidateEpubItems(epubItems) { + let newEpubItems = [ epubItems[epubItems.length - 1] ]; + let i = epubItems.length - 2; + while (0 <= i) { + let epubItem = epubItems[i]; + if (epubItem.elements.length === 1) { + newEpubItems[0].elements.unshift(epubItem.elements[0]); + } else { + newEpubItems.unshift(epubItem); + } + --i; } + return newEpubItems; } - // if get here, no visible content - return false; -} + discardEpubItemsWithNoVisibleContent(epubItems) { + let that = this; + return epubItems.filter(item => that.hasVisibleContent(item.elements)); + } -BakaTsukiParser.prototype.fixupInternalHyperLinks = function(epubItems) { - let targets = this.findLinkTargets(epubItems); - this.findAndFixHyperLinks(epubItems, targets); -} + hasVisibleContent(elements) { + for (let element of elements) { + if (!util.isElementWhiteSpace(element)) { + return true; + } + } -BakaTsukiParser.prototype.findLinkTargets = function(epubItems) { - let that = this; - let targets = new Map(); - that.walkEpubItemsWithElements( - epubItems, - targets, - that.recordTarget - ); - return targets; -} + // if get here, no visible content + return false; + } -BakaTsukiParser.prototype.findAndFixHyperLinks = function(epubItems, targets) { - let that = this; - that.walkEpubItemsWithElements( - epubItems, - targets, - that.fixHyperlink - ); -} + fixupInternalHyperLinks(epubItems) { + let targets = this.findLinkTargets(epubItems); + this.findAndFixHyperLinks(epubItems, targets); + } -BakaTsukiParser.prototype.walkEpubItemsWithElements = function(epubItems, targets, processFoundNode) { - let that = this; - for(let epubItem of epubItems) { - for(let element of epubItem.elements) { - let walker = document.createTreeWalker( - element, - NodeFilter.SHOW_ELEMENT - ); - - // assume first header tag we find is title of the chapter. - if(util.isHeaderTag(element) && (epubItem.chapterTitle === null)){ - epubItem.chapterTitle = element.textContent; - } - do { - processFoundNode.apply(that, [walker.currentNode, targets, util.makeRelative(epubItem.getZipHref())]); - } while (walker.nextNode()); + findLinkTargets(epubItems) { + let that = this; + let targets = new Map(); + that.walkEpubItemsWithElements( + epubItems, + targets, + that.recordTarget + ); + return targets; + } + + findAndFixHyperLinks(epubItems, targets) { + let that = this; + that.walkEpubItemsWithElements( + epubItems, + targets, + that.fixHyperlink + ); + } + + walkEpubItemsWithElements(epubItems, targets, processFoundNode) { + let that = this; + for(let epubItem of epubItems) { + for(let element of epubItem.elements) { + let walker = document.createTreeWalker( + element, + NodeFilter.SHOW_ELEMENT + ); + + // assume first header tag we find is title of the chapter. + if(util.isHeaderTag(element) && (epubItem.chapterTitle === null)){ + epubItem.chapterTitle = element.textContent; + } + do { + processFoundNode.apply(that, [walker.currentNode, targets, util.makeRelative(epubItem.getZipHref())]); + } while (walker.nextNode()); + }; }; - }; -} + } -BakaTsukiParser.prototype.recordTarget = function(node, targets, zipHref) { - if (node.id != "") { - targets.set(node.id, zipHref); - }; -} + recordTarget(node, targets, zipHref) { + if (node.id != "") { + targets.set(node.id, zipHref); + }; + } -BakaTsukiParser.prototype.fixHyperlink = function(node, targets, unused) { // eslint-disable-line no-unused-vars - if (node.tagName === "A") { - let targetId = util.extractHashFromUri(node.href); - let targetZipHref = targets.get(targetId); - if (targetZipHref != null) { - node.href = targetZipHref + "#" + targetId; + fixHyperlink(node, targets, unused) { // eslint-disable-line no-unused-vars + if (node.tagName === "A") { + let targetId = util.extractHashFromUri(node.href); + let targetZipHref = targets.get(targetId); + if (targetZipHref != null) { + node.href = targetZipHref + "#" + targetId; + } } } -} -BakaTsukiParser.prototype.onFetchImagesClicked = function () { - let that = this; - if (0 == that.imageCollector.imageInfoList.length) { - window.showErrorMessage(chrome.i18n.getMessage("noImagesFound")); - } else { - that.getFetchContentButton().disabled = true; - that.fetchContent(); + onFetchImagesClicked() { + let that = this; + if (0 == that.imageCollector.imageInfoList.length) { + window.showErrorMessage(chrome.i18n.getMessage("noImagesFound")); + } else { + that.getFetchContentButton().disabled = true; + that.fetchContent(); + } } -} -BakaTsukiParser.prototype.fetchContent = function () { - let that = this; - that.rebuildImagesToFetch(); - this.setUiToShowLoadingProgress(that.imageCollector.numberOfImagesToFetch()); - return that.imageCollector.fetchImages(() => that.updateProgressBarOneStep()) - .then(function() { - main.getPackEpubButton().disabled = false; - that.getFetchContentButton().disabled = false; - }).catch(function (err) { - util.logError(err); - }); -} + fetchContent() { + let that = this; + that.rebuildImagesToFetch(); + this.setUiToShowLoadingProgress(that.imageCollector.numberOfImagesToFetch()); + return that.imageCollector.fetchImages(() => that.updateProgressBarOneStep()) + .then(function() { + main.getPackEpubButton().disabled = false; + that.getFetchContentButton().disabled = false; + }).catch(function (err) { + util.logError(err); + }); + } -BakaTsukiParser.prototype.updateProgressBarOneStep = function() { - this.updateLoadState(); -} + updateProgressBarOneStep() { + this.updateLoadState(); + } -/* - Show progress, - finished true if have loaded all images, false if only loaded a single image -*/ -BakaTsukiParser.prototype.updateLoadState = function() { - let that = this; - that.getProgressBar().value += 1; -} + /* + Show progress, + finished true if have loaded all images, false if only loaded a single image + */ + updateLoadState() { + let that = this; + that.getProgressBar().value += 1; + } -BakaTsukiParser.prototype.getFetchContentButton = function() { - return document.getElementById("fetchImagesButton"); + getFetchContentButton() { + return document.getElementById("fetchImagesButton"); + } } diff --git a/plugin/js/parsers/SonakoParser.js b/plugin/js/parsers/SonakoParser.js index 537d6675..3b7533af 100644 --- a/plugin/js/parsers/SonakoParser.js +++ b/plugin/js/parsers/SonakoParser.js @@ -3,6 +3,8 @@ */ "use strict"; +parserFactory.register("sonako.wikia.com", function() { return new SonakoParser() }); + //----------------------------------------------------------------------------- // class SonakoImageCollector (derives from ImageCollector) //----------------------------------------------------------------------------- @@ -45,52 +47,50 @@ class SonakoParser extends BakaTsukiParser { constructor() { super(new SonakoImageCollector()); } -} -parserFactory.register("sonako.wikia.com", function() { return new SonakoParser() }); - -SonakoParser.prototype.extractTitle = function(dom) { - return util.getElement(dom, "title").innerText; -}; + extractTitle(dom) { + return util.getElement(dom, "title").innerText; + } -SonakoParser.prototype.extractLanguage = function(dom) { // eslint-disable-line no-unused-vars - // ToDo find language - return "vi-VN"; -}; + extractLanguage(dom) { // eslint-disable-line no-unused-vars + // ToDo find language + return "vi-VN"; + } -SonakoParser.prototype.extractSeriesInfo = function(dom, metaInfo) { // eslint-disable-line no-unused-vars - // This parser does not currently support this functionality -} + extractSeriesInfo(dom, metaInfo) { // eslint-disable-line no-unused-vars + // This parser does not currently support this functionality + } -// find the node(s) holding the story content -SonakoParser.prototype.findContent = function (dom) { - return util.getElement(dom, "div", e => (e.className.startsWith("mw-content-ltr"))); -}; + // find the node(s) holding the story content + findContent(dom) { + return util.getElement(dom, "div", e => (e.className.startsWith("mw-content-ltr"))); + } -SonakoParser.prototype.removeUnwantedElementsFromContentElement = function (element) { - util.removeElements(util.getElements(element, "script")); - util.removeElements(util.getElements(element, "noscript")); + removeUnwantedElementsFromContentElement(element) { + util.removeElements(util.getElements(element, "script")); + util.removeElements(util.getElements(element, "noscript")); - // discard table of contents (will generate one from tags later) - util.removeElements(util.getElements(element, "div", e => (e.id === "toc-wrapper"))); - util.removeElements(util.getElements(element, "a", e => (e.className === "toc-link"))); + // discard table of contents (will generate one from tags later) + util.removeElements(util.getElements(element, "div", e => (e.id === "toc-wrapper"))); + util.removeElements(util.getElements(element, "a", e => (e.className === "toc-link"))); - util.removeElements(util.getElements(element, "a", e => e.className.startsWith("wikia-photogallery-add"))); - util.removeElements(util.getElements(element, "div", e => (e.className ==="print-no"))); - util.removeElements(util.getElements(element, "div", e => (e.id.startsWith("INCONTENT")))); + util.removeElements(util.getElements(element, "a", e => e.className.startsWith("wikia-photogallery-add"))); + util.removeElements(util.getElements(element, "div", e => (e.className ==="print-no"))); + util.removeElements(util.getElements(element, "div", e => (e.id.startsWith("INCONTENT")))); - util.removeComments(element); - util.removeElements(util.getElements(element, "table")); + util.removeComments(element); + util.removeElements(util.getElements(element, "table")); - // hyperlinks that allow editing text - util.removeElements(util.getElements(element, "span", e => (e.className === "editsection"))); + // hyperlinks that allow editing text + util.removeElements(util.getElements(element, "span", e => (e.className === "editsection"))); - // fix source for delay loaded image tags - for(let img of util.getElements(element, "img", e => e.src.startsWith("data:image"))) { - let href = img.getAttribute("data-src"); - if (href != null) { - img.src = href; + // fix source for delay loaded image tags + for(let img of util.getElements(element, "img", e => e.src.startsWith("data:image"))) { + let href = img.getAttribute("data-src"); + if (href != null) { + img.src = href; + }; }; - }; -}; + } +}