diff --git a/client/assets/app.css b/client/assets/app.css index 975d62f6..068c763a 100644 --- a/client/assets/app.css +++ b/client/assets/app.css @@ -1,4 +1,5 @@ @import url('./transitions.css'); +@import url('./draggable.css'); .page { width: 100%; diff --git a/client/assets/draggable.css b/client/assets/draggable.css new file mode 100644 index 00000000..f37683f0 --- /dev/null +++ b/client/assets/draggable.css @@ -0,0 +1,38 @@ +.flip-list-move { + transition: transform 0.5s; +} +.no-move { + transition: transform 0s; +} +.ghost { + opacity: 0.5; + background-color: rgba(255, 255, 255, 0.25); +} +.list-group { + min-height: 30px; +} +#librariesTable .item { + cursor: n-resize; +} +.list-group-item:not(.exclude) { + cursor: n-resize; +} +.list-group-item.exclude { + cursor: not-allowed; +} +.list-group-item:not(.ghost):not(.exclude):hover { + background-color: rgba(0, 0, 0, 0.1); +} +.list-group-item:nth-child(even):not(.ghost):not(.exclude) { + background-color: rgba(0, 0, 0, 0.25); +} +.list-group-item:nth-child(even):not(.ghost):not(.exclude):hover { + background-color: rgba(0, 0, 0, 0.1); +} + +.list-group-item.exclude:not(.ghost) { + background-color: rgba(255, 0, 0, 0.25); +} +.list-group-item.exclude:not(.ghost):hover { + background-color: rgba(223, 0, 0, 0.25); +} \ No newline at end of file diff --git a/client/assets/ebooks/basic.js b/client/assets/ebooks/basic.js new file mode 100644 index 00000000..16be6afd --- /dev/null +++ b/client/assets/ebooks/basic.js @@ -0,0 +1,506 @@ +/* +Calibres stylesheet +*/ + +export default ` +@charset "UTF-8"; + +/* + Calibre styles +*/ +.arabic { + display: block; + list-style-type: decimal; + margin-bottom: 1em; + margin-right: 0; + margin-top: 1em; + text-align: justify + } +.attribution { + display: block; + font-size: 1em; + line-height: 1.2; + text-align: left; + margin: 0.3em 0 + } +.big { + font-size: 1.375em; + line-height: 1.2 + } +.big1 { + font-size: 1em + } +.block { + display: block; + text-align: justify; + margin: 1em 1em 2em + } +.block1 { + display: block; + text-align: justify; + margin: 1em 4em + } +.block2 { + display: block; + text-align: justify; + margin: 1em 1em 1em 2em + } +.bullet { + display: block; + list-style-type: disc; + margin-bottom: 1em; + margin-right: 0; + margin-top: 1em; + text-align: disc + } +.calibre { + background-color: #000007; + display: block; + font-family: Charis, "Times New Roman", Verdana, Arial; + font-size: 1.125em; + line-height: 1.2; + padding-left: 0; + padding-right: 0; + text-align: center; + margin: 0 5pt + } +.calibre1 { + display: block + } +.calibre2 { + height: auto; + width: auto + } +.calibre3:not(strong) { + display: block; + font-family: Charis, "Times New Roman", Verdana, Arial; + font-size: 1.125em; + line-height: 1.2; + padding-left: 0; + padding-right: 0; + margin: 0 5pt + } +.calibre4 { + font-weight: bold + } +.calibre5 { + font-style: italic + } +.calibre6 { + background-color: #FFF; + display: block; + font-family: Charis, "Times New Roman", Verdana, Arial; + font-size: 1.125em; + line-height: 1.2; + padding-left: 0; + padding-right: 0; + text-align: center; + margin: 0 5pt + } +.calibre7 { + display: list-item + } +.calibre8 { + font-size: 1em; + line-height: 1.2; + vertical-align: super + } +.calibre9 { + border-collapse: separate; + border-spacing: 2px; + display: table; + margin-bottom: 0; + margin-top: 0; + text-indent: 0 + } +.calibre10 { + display: table-row; + vertical-align: middle + } +.calibre11 { + display: table-cell; + text-align: right; + vertical-align: inherit; + padding: 1px + } +.calibre12 { + display: table-cell; + text-align: left; + vertical-align: inherit; + padding: 1px + } +.calibre13 { + height: 1em; + width: auto + } +.calibre14 { + font-size: 0.88889em; + line-height: 1.2; + vertical-align: super + } +.calibre15 { + font-size: 1em; + line-height: 1.2; + vertical-align: sub + } +.calibre16 { + display: block; + list-style-type: decimal; + margin-bottom: 1em; + margin-right: 0; + margin-top: 1em + } +.calibre17 { + display: block; + font-size: 1.125em; + font-weight: bold; + line-height: 1.2; + margin: 0.83em 0 + } +.center { + display: block; + text-align: center; + margin: 1em 0 + } +.center1 { + display: block; + font-size: 1em; + font-weight: bold; + line-height: 1.2; + text-align: center; + margin: -2em 0 3em + } +.center2 { + display: block; + font-size: 1em; + font-weight: bold; + line-height: 1.2; + text-align: center; + margin: 2em 0 1em + } +.center3 { + display: block; + text-align: center; + margin: -1em 0 1em + } +.center4 { + display: block; + text-align: center; + text-indent: 3%; + margin: 1em 0 + } +.chapter { + display: block; + font-size: 1.125em; + font-weight: bold; + line-height: 2em; + text-align: center; + margin: 2em 0 1em + } +.chapter1 { + display: block; + font-size: 0.88889em; + line-height: 1.2; + margin-left: 0.5em; + margin-right: 0.5em; + margin-top: 2em + } +.chapter2 { + display: block; + font-size: 1.125em; + font-weight: bold; + line-height: 2em; + text-align: center; + margin: 2em 0 3em + } +.copyright { + display: block; + font-size: 0.88889em; + line-height: 1.2; + margin-top: 4em; + text-align: center + } +.dedication { + display: block; + font-size: 0.88889em; + line-height: 1.2; + margin-top: 4em + } +.dropcaps { + float: left; + font-size: 3.4375rem; + line-height: 50px; + margin-right: 0.09em; + margin-top: -0.05em; + padding-top: 1px + } +.dropcaps1 { + float: left; + font-size: 3.4375rem; + line-height: 50px; + margin-right: 0.09em; + margin-top: 0.15em; + padding-top: 1px + } +.extract { + display: block; + text-align: justify; + margin: 2em 0 0.3em + } +.extract1 { + display: block; + text-align: justify; + text-indent: 3%; + margin: 2em 0 0.3em + } +.extract2 { + display: block; + text-align: justify; + margin: 1em 0 0.3em + } +.footnote { + border-bottom-style: solid; + border-bottom-width: 0; + border-left-style: solid; + border-left-width: 0; + border-right-style: solid; + border-right-width: 0; + border-top-style: solid; + border-top-width: 1px; + display: block; + font-size: 1em; + line-height: 1.2; + margin-top: 2 em + } +.footnote1 { + display: block; + text-align: justify; + margin: 0.3em 0 0.3em 2 + } +.footnote2 { + border-bottom-style: solid; + border-bottom-width: 0; + border-left-style: solid; + border-left-width: 0; + border-right-style: solid; + border-right-width: 0; + border-top-style: solid; + border-top-width: 1px; + display: block; + font-size: 0.88889em; + line-height: 1.2; + margin-top: 2 em + } +.hanging { + display: block; + font-size: 0.88889em; + line-height: 1.2; + text-align: left; + text-indent: -1em; + margin: 0.5em 0 0.3em 1em + } +.hanging1 { + display: block; + font-size: 0.88889em; + line-height: 1.2; + text-align: left; + text-indent: -1em; + margin: 0.5em 0 0.3em 1.5em + } +.hanging2 { + display: block; + font-size: 1em; + line-height: 1.2; + text-indent: -1em; + margin: 0.5em 0 0.3em 1em + } +.hanging3 { + display: block; + font-size: 1em; + line-height: 1.2; + text-align: left; + text-indent: 1em; + margin: 0.1em 0 0.3em 1em + } +.hanging4 { + display: block; + font-size: 1em; + line-height: 1.2; + text-align: left; + text-indent: 0.1em; + margin: 0.1em 0 0.3em 1em + } +a.hlink { + text-decoration: none + } +.indent { + display: block; + text-align: justify; + text-indent: 1em; + margin: 0.3em 0 + } +.line { + border-top: currentColor solid 1px; + border-bottom: currentColor solid 1px + } +.loweralpha { + display: block; + list-style-type: lower-alpha; + margin-bottom: 1em; + margin-right: 0; + margin-top: 1em; + text-align: justify + } +.none { + display: block; + list-style-type: none; + margin-bottom: 1em; + margin-right: 0; + margin-top: 1em; + text-align: justify + } +.none1 { + display: block; + list-style-type: none; + margin-bottom: 0; + margin-right: 0; + margin-top: 0; + text-align: justify + } +.nonindent { + display: block; + text-align: justify; + margin: 0.3em 0 + } +.nonindent1 { + display: block; + font-size: 1.125em; + line-height: 1.2; + text-indent: -1em; + margin: 0.5em 0 0.3em 0.1em + } +.nonindent2 { + display: block; + font-size: 1.125em; + line-height: 1.2; + text-indent: -1em; + margin: 0.5em 0 0.3em -0.5em + } +.nonindent3 { + display: block; + text-align: justify; + text-indent: 3%; + margin: 0.3em 0 + } +.part { + display: block; + font-size: 1em; + font-weight: bold; + line-height: 2em; + text-align: center; + margin: 4em 0 1em + } +.preface { + display: block; + font-size: 0.88889em; + line-height: 1.2; + margin-left: 2em; + margin-right: 2em; + text-align: justify + } +.pubhlink { + color: green; + text-decoration: none + } +.right { + display: block; + text-align: right; + margin: 0.3em 0 + } +.section { + display: block; + font-size: 1.125em; + font-weight: bold; + line-height: 1.2; + text-align: center; + margin: 2em 0 0.5em + } +.section1 { + display: block; + font-size: 1.125em; + font-weight: bold; + line-height: 1.2; + text-align: left; + margin: 2em 0 0.3em + } +.section2 { + display: block; + font-size: 1em; + font-weight: bold; + line-height: 1.2; + text-align: left; + margin: 2em 0 0.3em 1em + } +.small { + font-size: 0.66667em + } +.small1 { + font-size: 0.75em + } +.subchapter { + display: block; + font-size: 1.125em; + font-weight: bold; + line-height: 1.2; + margin: 1em 0 + } +.textbox { + background-color: #E4E4E4; + display: block; + line-height: 1.5em; + margin-bottom: 2em; + margin-top: 2em; + text-align: justify; + border-top: currentColor double 2px; + border-bottom: currentColor double 2px + } +.textbox1 { + display: block; + text-align: justify; + margin: 0.3em 0.5em 0.3em 0.8em + } +.textbox2 { + display: block; + text-align: justify; + text-indent: 1em; + margin: 0.3em 0.5em + } +.textbox3 { + display: block; + text-align: justify; + text-indent: 3%; + margin: 0.3em 0.5em 0.3em 0.8em + } +.titlepage { + display: block; + margin-left: -0.4em; + margin-top: 1.2em + } +.toc { + display: block; + font-size: 1em; + line-height: 1.2; + text-align: center + } +.toc1 { + display: block; + font-size: 1em; + font-weight: bold; + line-height: 1.2; + text-align: center; + margin: 0.67em 0 3em + } +.underline { + text-decoration: underline + } +` \ No newline at end of file diff --git a/client/assets/ebooks/htmlParser.js b/client/assets/ebooks/htmlParser.js new file mode 100644 index 00000000..21bcc789 --- /dev/null +++ b/client/assets/ebooks/htmlParser.js @@ -0,0 +1,248 @@ +/* +This is borrowed from koodo-reader https://github.com/troyeguo/koodo-reader/tree/master/src +*/ + +export const isTitle = ( + line, + isContainDI = false, + isContainChapter = false, + isContainCHAPTER = false +) => { + return ( + line.length < 30 && + line.indexOf("[") === -1 && + line.indexOf("(") === -1 && + (line.startsWith("CHAPTER") || + line.startsWith("Chapter") || + line.startsWith("序章") || + line.startsWith("前言") || + line.startsWith("声明") || + line.startsWith("聲明") || + line.startsWith("写在前面的话") || + line.startsWith("后记") || + line.startsWith("楔子") || + line.startsWith("后序") || + line.startsWith("寫在前面的話") || + line.startsWith("後記") || + line.startsWith("後序") || + /(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/.test( + line + ) || + (line.startsWith("第") && startWithDI(line)) || + (line.startsWith("卷") && startWithJUAN(line)) || + startWithRomanNum(line) || + (!isContainDI && + !isContainChapter && + !isContainCHAPTER && + line.indexOf("第") > -1 && + (line[line.indexOf("第") - 1] === " " || + line[line.indexOf("第") - 1] === " " || + line[line.indexOf("第") - 1] === "、" || + line[line.indexOf("第") - 1] === ":" || + line[line.indexOf("第") - 1] === ":") && + startWithDI(line.substr(line.indexOf("第")))) || + (!isContainDI && + !isContainChapter && + !isContainCHAPTER && + line.indexOf(" ") && + startWithNumAndSpace(line)) || + (!isContainDI && + !isContainChapter && + !isContainCHAPTER && + line.indexOf(" ") && + startWithNumAndSpace(line)) || + (!isContainDI && + !isContainChapter && + !isContainCHAPTER && + line.indexOf("、") && + startWithNumAndPause(line)) || + (!isContainDI && + !isContainChapter && + !isContainCHAPTER && + line.indexOf(":") && + startWithNumAndColon(line)) || + (!isContainDI && + !isContainChapter && + !isContainCHAPTER && + line.indexOf(":") && + startWithNumAndColon(line))) + ); +}; +const startWithDI = (line) => { + let keywords = [ + "章", + "节", + "回", + "節", + "卷", + "部", + "輯", + "辑", + "話", + "集", + "话", + "篇", + ]; + let flag = false; + for (let i = 0; i < keywords.length; i++) { + if ( + (line.indexOf(keywords[i]) > -1 && + (line[line.indexOf(keywords[i]) + 1] === " " || + line[line.indexOf(keywords[i]) + 1] === " " || + line[line.indexOf(keywords[i]) + 1] === "、" || + line[line.indexOf(keywords[i]) + 1] === ":" || + line[line.indexOf(keywords[i]) + 1] === ":")) || + !line[line.indexOf(keywords[i]) + 1] + ) { + if ( + /^[\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u842c]+$/.test( + line.substring(1, line.indexOf(keywords[i])).trim() + ) || + /^\d+$/.test(line.substring(1, line.indexOf(keywords[i])).trim()) + ) { + flag = true; + } + if (flag) break; + } + } + return flag; +}; +const startWithJUAN = (line) => { + if ( + /^[\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u842c]+$/.test( + line.substring(1, line.indexOf(" ")) + ) || + /^\d+$/.test(line.substring(1, line.indexOf(" "))) + ) + return true; + if ( + /^[\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u842c]+$/.test( + line.substring(1, line.indexOf(" ")) + ) || + /^\d+$/.test(line.substring(1, line.indexOf(" "))) + ) + return true; + if ( + /^[\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u842c]+$/.test( + line.substring(1) + ) || + /^\d+$/.test(line.substring(1)) + ) + return true; + return false; +}; +const startWithRomanNum = (line) => { + if ( + /(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/.test( + line.substring(0, line.indexOf(" ")) + ) + ) + return true; + if ( + /(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/.test( + line.substring(0, line.indexOf(".")) + ) + ) + return true; + if ( + /(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/.test( + line.trim() + ) + ) + return true; + return false; +}; +const startWithNumAndSpace = (line) => { + if ( + /^[\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u842c]+$/.test( + line.substring(0, line.indexOf(" ")) + ) + ) + return true; + if ( + /^[\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u842c]+$/.test( + line.substring(0, line.indexOf(" ")) + ) + ) + return true; + + if (/^\d+$/.test(line.substring(0, line.indexOf(" ")))) return true; + if (/^\d+$/.test(line.substring(0, line.indexOf(" ")))) return true; + return false; +}; +const startWithNumAndColon = (line) => { + if ( + /^[\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u842c]+$/.test( + line.substring(0, line.indexOf(":")) + ) + ) + return true; + if ( + /^[\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u842c]+$/.test( + line.substring(0, line.indexOf(":")) + ) + ) + return true; + + if (/^\d+$/.test(line.substring(0, line.indexOf(":")))) return true; + if (/^\d+$/.test(line.substring(0, line.indexOf(":")))) return true; + return false; +}; +const startWithNumAndPause = (line) => { + if ( + /^[\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u842c]+$/.test( + line.substring(0, line.indexOf("、")) + ) + ) + return true; + + if (/^\d+$/.test(line.substring(0, line.indexOf("、")))) return true; + return false; +}; + + +class HtmlParser { + bookDoc; + contentList; + contentTitleList; + constructor(bookDoc) { + this.bookDoc = bookDoc; + this.contentList = []; + this.contentTitleList = []; + this.getContent(bookDoc); + } + getContent(bookDoc) { + this.contentList = Array.from( + bookDoc.querySelectorAll("h1,h2,h3,h4,h5,b,font") + ).filter((item, index) => { + return isTitle(item.innerText.trim()); + }); + + for (let i = 0; i < this.contentList.length; i++) { + let random = Math.floor(Math.random() * 900000) + 100000; + this.contentTitleList.push({ + label: this.contentList[i].innerText, + id: "title" + random, + href: "#title" + random, + subitems: [], + }); + } + for (let i = 0; i < this.contentList.length; i++) { + this.contentList[i].id = this.contentTitleList[i].id; + } + } + getAnchoredDoc() { + return this.bookDoc; + } + getContentList() { + return this.contentTitleList.filter((item, index) => { + if (index > 0) { + return item.label !== this.contentTitleList[index - 1].label; + } else { + return true; + } + }); + } +} + +export default HtmlParser; diff --git a/client/assets/mobi.js b/client/assets/ebooks/mobi.js similarity index 73% rename from client/assets/mobi.js rename to client/assets/ebooks/mobi.js index fd01a0f9..58c6779e 100644 --- a/client/assets/mobi.js +++ b/client/assets/ebooks/mobi.js @@ -1,3 +1,7 @@ +/* +This is borrowed from koodo-reader https://github.com/troyeguo/koodo-reader/tree/master/src +*/ + function ab2str(buf) { if (buf instanceof ArrayBuffer) { buf = new Uint8Array(buf); @@ -8,9 +12,14 @@ function ab2str(buf) { var domParser = new DOMParser(); class Buffer { + capacity; + fragment_list; + imageArray; + cur_fragment; constructor(capacity) { this.capacity = capacity; this.fragment_list = []; + this.imageArray = []; this.cur_fragment = new Fragment(capacity); this.fragment_list.push(this.cur_fragment); } @@ -57,23 +66,26 @@ class Buffer { } } -var combine_uint8array = function (buffers) { +var copagesne_uint8array = function (buffers) { var total_size = 0; - for (var i = 0; i < buffers.length; i++) { + for (let i = 0; i < buffers.length; i++) { var buffer = buffers[i]; total_size += buffer.length; } var total_buffer = new Uint8Array(total_size); var offset = 0; - for (var i = 0; i < buffers.length; i++) { - var buffer = buffers[i]; + for (let i = 0; i < buffers.length; i++) { + buffer = buffers[i]; total_buffer.set(buffer, offset); offset += buffer.length; } return total_buffer; -} +}; class Fragment { + buffer; + capacity; + size; constructor(capacity) { this.buffer = new Uint8Array(capacity); this.capacity = capacity; @@ -98,14 +110,14 @@ class Fragment { var uncompression_lz77 = function (data) { var length = data.length; - var offset = 0; // Current offset into data + var offset = 0; // Current offset into data var buffer = new Buffer(data.length); while (offset < length) { var char = data[offset]; offset += 1; - if (char == 0) { + if (char === 0) { buffer.write(char); } else if (char <= 8) { for (var i = offset; i < offset + char; i++) { @@ -117,12 +129,12 @@ var uncompression_lz77 = function (data) { } else if (char <= 0xbf) { var next = data[offset]; offset += 1; - var distance = ((char << 8 | next) >> 3) & 0x7ff; + var distance = (((char << 8) | next) >> 3) & 0x7ff; var lz_length = (next & 0x7) + 3; var buffer_size = buffer.size(); - for (var i = 0; i < lz_length; i++) { - buffer.write(buffer.get(buffer_size - distance)) + for (let i = 0; i < lz_length; i++) { + buffer.write(buffer.get(buffer_size - distance)); buffer_size += 1; } } else { @@ -134,6 +146,13 @@ var uncompression_lz77 = function (data) { }; class MobiFile { + view; + buffer; + offset; + header; + palm_header; + mobi_header; + reclist; constructor(data) { this.view = new DataView(data); this.buffer = this.view.buffer; @@ -141,9 +160,7 @@ class MobiFile { this.header = null; } - parse() { - - } + parse() { } getUint8() { var v = this.view.getUint8(this.offset); @@ -220,14 +237,14 @@ class MobiFile { } return [size, l, pos]; } - + // 读出文本内容 read_text() { var text_end = this.palm_header.record_count; var buffers = []; for (var i = 1; i <= text_end; i++) { buffers.push(this.read_text_record(i)); } - var all = combine_uint8array(buffers) + var all = copagesne_uint8array(buffers); return ab2str(all); } @@ -236,7 +253,7 @@ class MobiFile { var begin = this.reclist[i].offset; var end = this.reclist[i + 1].offset; - var data = new Uint8Array(this.buffer.slice(begin, end)) + var data = new Uint8Array(this.buffer.slice(begin, end)); var ex = this.get_record_extrasize(data, flags); data = new Uint8Array(this.buffer.slice(begin, end - ex)); @@ -247,12 +264,12 @@ class MobiFile { return data; } } - + // 从buffer中读出image read_image(idx) { var first_image_idx = this.mobi_header.first_image_idx; var begin = this.reclist[first_image_idx + idx].offset; var end = this.reclist[first_image_idx + idx + 1].offset; - var data = new Uint8Array(this.buffer.slice(begin, end)) + var data = new Uint8Array(this.buffer.slice(begin, end)); return new Blob([data.buffer]); } @@ -278,7 +295,6 @@ class MobiFile { header.uid = this.getUint32(); header.next_rec = this.getUint32(); header.record_num = this.getUint16(); - return header; } @@ -287,7 +303,7 @@ class MobiFile { for (var i = 0; i < this.header.record_num; i++) { var record = {}; record.offset = this.getUint32(); - // TODO(zz) change + // TODO(zz) change record.attr = this.getUint32(); reclist.push(record); } @@ -361,7 +377,7 @@ class MobiFile { mobi_header.extra_flags = this.getUint16(); - this.setoffset(start_offset + mobi_header.header_length) + this.setoffset(start_offset + mobi_header.header_length); return mobi_header; } @@ -369,37 +385,66 @@ class MobiFile { // TODO return {}; } - - render_to(id) { - this.load(); - var content = this.read_text(); - - var bookDom = document.getElementById(id); - while (bookDom.firstChild) { - bookDom.removeChild(bookDom.firstChild); - } - - var bookDoc = domParser.parseFromString(content, "text/html"); - bookDoc.body.childNodes.forEach(function (x) { - if (x instanceof Element) { - bookDom.appendChild(x); + extractContent(s) { + var span = document.createElement("span"); + span.innerHTML = s; + return span.textContent || span.innerText; + } + render(isElectron = false) { + return new Promise((resolve, reject) => { + this.load(); + var content = this.read_text(); + var bookDoc = domParser.parseFromString(content, "text/html") + .documentElement; + let lines = Array.from( + bookDoc.querySelectorAll("p,b,font,h3,h2,h1") + ); + let parseContent = []; + for (let i = 0, len = lines.length; i < len - 1; i++) { + lines[i].innerText && + lines[i].innerText !== parseContent[parseContent.length - 1] && + parseContent.push(lines[i].innerText); + let imgDoms = lines[i].getElementsByTagName("img"); + if (imgDoms.length > 0) { + for (let i = 0; i < imgDoms.length; i++) { + parseContent.push("#image"); + } + } } + const handleImage = async () => { + var imgDoms = bookDoc.getElementsByTagName("img"); + parseContent.push("~image"); + for (let i = 0; i < imgDoms.length; i++) { + const src = await this.render_image(imgDoms, i); + parseContent.push( + src + " " + imgDoms[i].width + " " + imgDoms[i].height + ); + } + if (imgDoms.length > 200 || !isElectron) { + resolve(bookDoc); + } else { + resolve(parseContent.join("\n \n")); + } + }; + handleImage(); }); - - var imgDoms = bookDom.getElementsByTagName("img"); - for (var i = 0; i < imgDoms.length; i++) { - this.render_image(imgDoms, i); - } - } - render_image(imgDoms, i) { - var imgDom = imgDoms[i]; - var idx = +imgDom.getAttribute("recindex"); - var blob = this.read_image(idx - 1); - var imgReader = new FileReader(); - imgReader.onload = function (e) { - imgDom.src = e.target.result; - }; - imgReader.readAsDataURL(blob); } + render_image = (imgDoms, i) => { + return new Promise((resolve, reject) => { + var imgDom = imgDoms[i]; + var idx = +imgDom.getAttribute("recindex"); + var blob = this.read_image(idx - 1); + var imgReader = new FileReader(); + imgReader.onload = (e) => { + imgDom.src = e.target?.result; + resolve(e.target?.result); + }; + imgReader.onerror = function (err) { + reject(err); + }; + imgReader.readAsDataURL(blob); + }); + }; } -module.exports = MobiFile \ No newline at end of file + +export default MobiFile; diff --git a/client/components/app/Appbar.vue b/client/components/app/Appbar.vue index 2d88f72b..4c3874cc 100644 --- a/client/components/app/Appbar.vue +++ b/client/components/app/Appbar.vue @@ -7,24 +7,15 @@ arrow_back

AudioBookshelf

- - - - -
+ +
diff --git a/client/components/app/Reader.vue b/client/components/app/Reader.vue index 516400eb..0d091594 100644 --- a/client/components/app/Reader.vue +++ b/client/components/app/Reader.vue @@ -12,10 +12,8 @@

{{ title || abTitle }}

by {{ author || abAuthor }}

-
-

Warning: Reading mobi & azw3 files is in the very early stages

-
+
chevron_left @@ -31,9 +29,10 @@ chevron_right
-
-
-
+ +
+
+
@@ -41,11 +40,14 @@ \ No newline at end of file + + + \ No newline at end of file diff --git a/client/components/modals/libraries/LibraryItem.vue b/client/components/modals/libraries/LibraryItem.vue index c6e6d3a8..2933474e 100644 --- a/client/components/modals/libraries/LibraryItem.vue +++ b/client/components/modals/libraries/LibraryItem.vue @@ -1,18 +1,18 @@