From 9dea01580a827edbed53f20cf90fb2ed43f4ff9d Mon Sep 17 00:00:00 2001 From: gedoor Date: Sun, 18 Jul 2021 22:22:39 +0800 Subject: [PATCH 01/40] HtmlFormatter.kt --- .../java/io/legado/app/utils/HtmlFormatter.kt | 90 ++++++++++++------- 1 file changed, 59 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt index 4ba022a73..8d086b55c 100644 --- a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt +++ b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt @@ -22,20 +22,21 @@ object HtmlFormatter { fun formatKeepImg(html: String?, redirectUrl: URL?): String { html ?: return "" - val keepImgHtml = html.replace(wrapHtmlRegex, "\n") - .replace(notImgHtmlRegex, "") - .replace("\\n\\s*$|^\\s*\\n".toRegex(), "") - .replace("\\n\\s*\\n".toRegex(), "\n") + val keepImgHtml = formatKeepImg(html) var str = StringBuffer() var endPos = 0 var hasMatch = true //普通图片标签是否还未处理过 var hasMatchX = false //是否存在带参数或带数据属性的图片标签 var pos = -1 - val list = listOf("]*src *= *\"([^\"{]*\\{(?:[^{}]|\\{[^}]+\\})+\\})\"[^>]*>","]*data-[^=]*= *\"([^\"]*)\"[^>]*>","]*src *= *\"([^\"]*)\"[^>]*>") //优先匹配用户处理过所以带参数的图片标签,其次匹配带数据属性的图片标签 - while(++pos<3){ - if(pos == 2) { - if(hasMatchX)break //普通图片标签只在不存在存在带参数或带数据属性的图片标签的时候匹配 + val list = listOf( + "]*src *= *\"([^\"{]*\\{(?:[^{}]|\\{[^}]+\\})+\\})\"[^>]*>", + "]*data-[^=]*= *\"([^\"]*)\"[^>]*>", + "]*src *= *\"([^\"]*)\"[^>]*>" + ) //优先匹配用户处理过所以带参数的图片标签,其次匹配带数据属性的图片标签 + while (++pos < 3) { + if (pos == 2) { + if (hasMatchX) break //普通图片标签只在不存在存在带参数或带数据属性的图片标签的时候匹配 else hasMatch = false //匹配带参数或带数据属性的图片标签同时也会格式化普通图片标签 } var appendPos = 0 @@ -48,22 +49,38 @@ object HtmlFormatter { if (hasMatch) { //格式化不带参数和数据属性的普通图片标签 var appendPos0 = 0 - val matcher0 = Pattern.compile("]*src *= *\"([^\"]*)\"[^>]*>", Pattern.CASE_INSENSITIVE).matcher(strBefore) //格式化普通图片标签 + val matcher0 = Pattern.compile( + "]*src *= *\"([^\"]*)\"[^>]*>", + Pattern.CASE_INSENSITIVE + ).matcher(strBefore) //格式化普通图片标签 while (matcher0.find()) { - sb.append(strBefore.substring(appendPos0, matcher0.start()),"") + sb.append( + strBefore.substring(appendPos0, matcher0.start()), + "" + ) appendPos0 = matcher0.end() } strBefore = strBefore.substring(appendPos0, strBefore.length) } - sb.append( strBefore,"") + sb.append( + strBefore, "" + ) appendPos = matcher.end() } while (matcher.find()) @@ -79,19 +96,30 @@ object HtmlFormatter { if (endPos < keepImgHtml.length) { str.append( - if(hasMatchX){ //处理末尾的普通图片标签 - var appendPos0 = 0 - val strBefore = keepImgHtml.substring(endPos, keepImgHtml.length) - val matcher0 = - Pattern.compile("]*src *= *\"([^\"]*)\"[^>]*>", Pattern.CASE_INSENSITIVE) - .matcher(strBefore) //格式化普通图片标签 - while (matcher0.find()) { - str.append(strBefore.substring(appendPos0, matcher0.start()),"") - appendPos0 = matcher0.end() - } - strBefore.substring( appendPos0, strBefore.length ) - }else keepImgHtml.substring( endPos, keepImgHtml.length ) - ) //缩进图片之后的非空白段落 + if (hasMatchX) { //处理末尾的普通图片标签 + var appendPos0 = 0 + val strBefore = keepImgHtml.substring(endPos, keepImgHtml.length) + val matcher0 = + Pattern.compile( + "]*src *= *\"([^\"]*)\"[^>]*>", + Pattern.CASE_INSENSITIVE + ) + .matcher(strBefore) //格式化普通图片标签 + while (matcher0.find()) { + str.append( + strBefore.substring(appendPos0, matcher0.start()), + "" + ) + appendPos0 = matcher0.end() + } + strBefore.substring(appendPos0, strBefore.length) + } else keepImgHtml.substring(endPos, keepImgHtml.length) + ) //缩进图片之后的非空白段落 } return str.toString() } From 2cfec7e245c65e424c8486f0c00cb96175058695 Mon Sep 17 00:00:00 2001 From: gedoor Date: Sun, 18 Jul 2021 23:00:29 +0800 Subject: [PATCH 02/40] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/help/ContentProcessor.kt | 27 ++++++++++++++++++- .../io/legado/app/service/help/ReadBook.kt | 2 +- .../association/ImportBookSourceViewModel.kt | 2 ++ .../app/ui/book/cache/CacheViewModel.kt | 4 +-- .../searchContent/SearchContentViewModel.kt | 2 +- .../app/ui/replace/ReplaceRuleActivity.kt | 4 +-- 6 files changed, 34 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/ContentProcessor.kt b/app/src/main/java/io/legado/app/help/ContentProcessor.kt index af19c11ca..5dbb39297 100644 --- a/app/src/main/java/io/legado/app/help/ContentProcessor.kt +++ b/app/src/main/java/io/legado/app/help/ContentProcessor.kt @@ -6,8 +6,33 @@ import io.legado.app.data.entities.Book import io.legado.app.data.entities.ReplaceRule import io.legado.app.utils.toastOnUi import splitties.init.appCtx +import java.lang.ref.WeakReference -class ContentProcessor(private val bookName: String, private val bookOrigin: String) { +class ContentProcessor private constructor( + private val bookName: String, + private val bookOrigin: String +) { + + companion object { + private val processors = hashMapOf>() + + fun get(bookName: String, bookOrigin: String): ContentProcessor { + val processorWr = processors[bookName + bookOrigin] + var processor: ContentProcessor? = processorWr?.get() + if (processor == null) { + processor = ContentProcessor(bookName, bookOrigin) + processors[bookName + bookOrigin] = WeakReference(processor) + } + return processor + } + + fun upReplaceRules() { + processors.forEach { + it.value.get()?.upReplaceRules() + } + } + + } private var replaceRules = arrayListOf() diff --git a/app/src/main/java/io/legado/app/service/help/ReadBook.kt b/app/src/main/java/io/legado/app/service/help/ReadBook.kt index c1d6fc2ca..b0202682b 100644 --- a/app/src/main/java/io/legado/app/service/help/ReadBook.kt +++ b/app/src/main/java/io/legado/app/service/help/ReadBook.kt @@ -46,7 +46,7 @@ object ReadBook { fun resetData(book: Book) { this.book = book - contentProcessor = ContentProcessor(book.name, book.origin) + contentProcessor = ContentProcessor.get(book.name, book.origin) readRecord.bookName = book.name readRecord.readTime = appDb.readRecordDao.getReadTime(book.name) ?: 0 durChapterIndex = book.durChapterIndex diff --git a/app/src/main/java/io/legado/app/ui/association/ImportBookSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/association/ImportBookSourceViewModel.kt index 4cadba770..0659487ad 100644 --- a/app/src/main/java/io/legado/app/ui/association/ImportBookSourceViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/association/ImportBookSourceViewModel.kt @@ -8,6 +8,7 @@ import io.legado.app.base.BaseViewModel import io.legado.app.data.appDb import io.legado.app.data.entities.BookSource import io.legado.app.help.AppConfig +import io.legado.app.help.ContentProcessor import io.legado.app.help.SourceHelp import io.legado.app.help.http.newCall import io.legado.app.help.http.okHttpClient @@ -67,6 +68,7 @@ class ImportBookSourceViewModel(app: Application) : BaseViewModel(app) { } } SourceHelp.insertBookSource(*selectSource.toTypedArray()) + ContentProcessor.upReplaceRules() }.onFinally { finally.invoke() } diff --git a/app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt b/app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt index 541200142..8e6019c1c 100644 --- a/app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt @@ -122,7 +122,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { private fun getAllContents(book: Book, append: (text: String) -> Unit) { val useReplace = AppConfig.exportUseReplace - val contentProcessor = ContentProcessor(book.name, book.origin) + val contentProcessor = ContentProcessor.get(book.name, book.origin) append( "${book.name}\n${ context.getString( @@ -386,7 +386,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { private fun setEpubContent(contentModel: String, book: Book, epubBook: EpubBook) { //正文 val useReplace = AppConfig.exportUseReplace - val contentProcessor = ContentProcessor(book.name, book.origin) + val contentProcessor = ContentProcessor.get(book.name, book.origin) appDb.bookChapterDao.getChapterList(book.bookUrl).forEachIndexed { index, chapter -> BookHelp.getContent(book, chapter).let { content -> var content1 = fixPic(epubBook, book, content ?: "null", chapter) diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentViewModel.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentViewModel.kt index 94cb50d51..121248738 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentViewModel.kt @@ -18,7 +18,7 @@ class SearchContentViewModel(application: Application) : BaseViewModel(applicati execute { book = appDb.bookDao.getBook(bookUrl) book?.let { - contentProcessor = ContentProcessor(it.name, it.origin) + contentProcessor = ContentProcessor.get(it.name, it.origin) } }.onSuccess { success.invoke() diff --git a/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt b/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt index 2fc737bd9..56ff1257e 100644 --- a/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt +++ b/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt @@ -21,12 +21,12 @@ import io.legado.app.data.appDb import io.legado.app.data.entities.ReplaceRule import io.legado.app.databinding.ActivityReplaceRuleBinding import io.legado.app.databinding.DialogEditTextBinding +import io.legado.app.help.ContentProcessor import io.legado.app.help.IntentDataHelp import io.legado.app.help.coroutine.Coroutine import io.legado.app.lib.dialogs.alert import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.primaryTextColor -import io.legado.app.service.help.ReadBook import io.legado.app.ui.association.ImportReplaceRuleActivity import io.legado.app.ui.document.FilePicker import io.legado.app.ui.document.FilePickerParam @@ -294,7 +294,7 @@ class ReplaceRuleActivity : VMBaseActivity Date: Sun, 18 Jul 2021 23:17:09 +0800 Subject: [PATCH 03/40] Update HtmlFormatter.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 大幅简化 --- .../java/io/legado/app/utils/HtmlFormatter.kt | 86 ++++--------------- 1 file changed, 19 insertions(+), 67 deletions(-) diff --git a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt index 4ba022a73..664525973 100644 --- a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt +++ b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt @@ -26,73 +26,25 @@ object HtmlFormatter { .replace(notImgHtmlRegex, "") .replace("\\n\\s*$|^\\s*\\n".toRegex(), "") .replace("\\n\\s*\\n".toRegex(), "\n") - - var str = StringBuffer() - var endPos = 0 - var hasMatch = true //普通图片标签是否还未处理过 - var hasMatchX = false //是否存在带参数或带数据属性的图片标签 - var pos = -1 - val list = listOf("]*src *= *\"([^\"{]*\\{(?:[^{}]|\\{[^}]+\\})+\\})\"[^>]*>","]*data-[^=]*= *\"([^\"]*)\"[^>]*>","]*src *= *\"([^\"]*)\"[^>]*>") //优先匹配用户处理过所以带参数的图片标签,其次匹配带数据属性的图片标签 - while(++pos<3){ - if(pos == 2) { - if(hasMatchX)break //普通图片标签只在不存在存在带参数或带数据属性的图片标签的时候匹配 - else hasMatch = false //匹配带参数或带数据属性的图片标签同时也会格式化普通图片标签 - } - var appendPos = 0 - val matcher = - Pattern.compile(list[pos], Pattern.CASE_INSENSITIVE).matcher(keepImgHtml) - val sb = StringBuffer() - if (matcher.find()) { - do { - var strBefore = keepImgHtml.substring(appendPos, matcher.start()) - - if (hasMatch) { //格式化不带参数和数据属性的普通图片标签 - var appendPos0 = 0 - val matcher0 = Pattern.compile("]*src *= *\"([^\"]*)\"[^>]*>", Pattern.CASE_INSENSITIVE).matcher(strBefore) //格式化普通图片标签 - while (matcher0.find()) { - sb.append(strBefore.substring(appendPos0, matcher0.start()),"") - appendPos0 = matcher0.end() - } - strBefore = strBefore.substring(appendPos0, strBefore.length) - } - - sb.append( strBefore,"") - - appendPos = matcher.end() - } while (matcher.find()) - hasMatch = false //普通图片标签已经处理过 - } - - if (appendPos != 0) { - hasMatchX = true //存在带参数或带数据属性的图片标签 - endPos = appendPos //存在匹配,更新位置 - str = sb //存在匹配,更新字符串 - } - } - - if (endPos < keepImgHtml.length) { - str.append( - if(hasMatchX){ //处理末尾的普通图片标签 - var appendPos0 = 0 - val strBefore = keepImgHtml.substring(endPos, keepImgHtml.length) - val matcher0 = - Pattern.compile("]*src *= *\"([^\"]*)\"[^>]*>", Pattern.CASE_INSENSITIVE) - .matcher(strBefore) //格式化普通图片标签 - while (matcher0.find()) { - str.append(strBefore.substring(appendPos0, matcher0.start()),"") - appendPos0 = matcher0.end() - } - strBefore.substring( appendPos0, strBefore.length ) - }else keepImgHtml.substring( endPos, keepImgHtml.length ) - ) //缩进图片之后的非空白段落 + //正则的“|”处于顶端而不处于()中时,具有类似||的熔断效果,故以此机制简化原来的代码 + val matcher = Pattern.compile("]*src *= *\"([^\"{]*\\{(?:[^{}]|\\{[^}]+\\})+\\})\"[^>]*>|]*data-[^=]*= *\"([^\"]*)\"[^>]*>|]*src *= *\"([^\"]*)\"[^>]*>", Pattern.CASE_INSENSITIVE).matcher(keepImgHtml) + var appendPos = 0 + val sb = StringBuffer() + while (matcher.find()){ + var param = "" + sb.append( keepImgHtml.substring(appendPos, matcher.start()),"") + appendPos = matcher.end() } - return str.toString() + if (appendPos < keepImgHtml.length) sb.append(keepImgHtml.substring( appendPos, keepImgHtml.length )) + return sb.toString() } } From d5e49b313b3bb3dea8deb3584521e70628296682 Mon Sep 17 00:00:00 2001 From: gedoor Date: Sun, 18 Jul 2021 23:55:48 +0800 Subject: [PATCH 04/40] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/utils/HtmlFormatter.kt | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt index 664525973..a88a59995 100644 --- a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt +++ b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt @@ -8,6 +8,10 @@ object HtmlFormatter { private val wrapHtmlRegex = "]*>".toRegex() private val notImgHtmlRegex = "])[^<>]*>".toRegex() private val otherHtmlRegex = "])[^<>]*>".toRegex() + private val formatImagePattern = Pattern.compile( + "]*src *= *\"([^\"{]*\\{(?:[^{}]|\\{[^}]+\\})+\\})\"[^>]*>|]*data-[^=]*= *\"([^\"]*)\"[^>]*>|]*src *= *\"([^\"]*)\"[^>]*>", + Pattern.CASE_INSENSITIVE + ) fun format(html: String?, otherRegex: Regex = otherHtmlRegex): String { html ?: return "" @@ -27,24 +31,32 @@ object HtmlFormatter { .replace("\\n\\s*$|^\\s*\\n".toRegex(), "") .replace("\\n\\s*\\n".toRegex(), "\n") //正则的“|”处于顶端而不处于()中时,具有类似||的熔断效果,故以此机制简化原来的代码 - val matcher = Pattern.compile("]*src *= *\"([^\"{]*\\{(?:[^{}]|\\{[^}]+\\})+\\})\"[^>]*>|]*data-[^=]*= *\"([^\"]*)\"[^>]*>|]*src *= *\"([^\"]*)\"[^>]*>", Pattern.CASE_INSENSITIVE).matcher(keepImgHtml) + val matcher = formatImagePattern.matcher(keepImgHtml) var appendPos = 0 val sb = StringBuffer() - while (matcher.find()){ + while (matcher.find()) { var param = "" - sb.append( keepImgHtml.substring(appendPos, matcher.start()),"") + sb.append( + keepImgHtml.substring(appendPos, matcher.start()), "" + ) appendPos = matcher.end() } - if (appendPos < keepImgHtml.length) sb.append(keepImgHtml.substring( appendPos, keepImgHtml.length )) + if (appendPos < keepImgHtml.length) sb.append( + keepImgHtml.substring( + appendPos, + keepImgHtml.length + ) + ) return sb.toString() } } From b71493ef5d975448b17c1588c510ce87112947e7 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 19 Jul 2021 08:12:54 +0800 Subject: [PATCH 05/40] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt index 6e9807aad..bc0c18113 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt @@ -39,7 +39,7 @@ class AnalyzeUrl( headerMapF: Map? = null ) : JsExtensions { companion object { - val paramPattern = Pattern.compile("\\s*,\\s*(?=\\{)") + val paramPattern: Pattern = Pattern.compile("\\s*,\\s*(?=\\{)") private val pagePattern = Pattern.compile("<(.*?)>") } From 89d77c5ecd9422ab0675baad940bc39273c964a6 Mon Sep 17 00:00:00 2001 From: celetor <86599970+celetor@users.noreply.github.com> Date: Mon, 19 Jul 2021 12:35:00 +0800 Subject: [PATCH 06/40] =?UTF-8?q?xpath=E7=9A=84bug=E6=9C=AA=E8=A7=A3?= =?UTF-8?q?=E5=86=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 具体见 https://github.com/zhegexiaohuozi/JsoupXpath/issues/53 --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 6d8529cd8..a16afacd3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -178,7 +178,7 @@ dependencies { //规则相关 implementation 'org.jsoup:jsoup:1.14.1' //noinspection GradleDependency - implementation 'cn.wanghaomiao:JsoupXpath:2.4.3' + implementation 'cn.wanghaomiao:JsoupXpath:2.3.2' implementation 'com.jayway.jsonpath:json-path:2.6.0' //JS rhino From 39d09c763635f1920cb33c8d510112f2f17669e3 Mon Sep 17 00:00:00 2001 From: celetor Date: Mon, 19 Jul 2021 16:15:57 +0800 Subject: [PATCH 07/40] =?UTF-8?q?web=E4=B9=A6=E6=9E=B6=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E3=80=90=E6=9C=80=E8=BF=91=E4=B8=80=E6=AC=A1=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E4=B9=A6=E7=B1=8D=E4=BF=A1=E6=81=AF=E7=9A=84=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E3=80=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...{about.dbe575e1.css => about.1cd9ab87.css} | 2 +- app/src/main/assets/web/bookshelf/favicon.ico | Bin 0 -> 4286 bytes .../web/bookshelf/img/icons/favicon-16x16.png | Bin 0 -> 799 bytes .../web/bookshelf/img/icons/favicon-32x32.png | Bin 0 -> 1271 bytes app/src/main/assets/web/bookshelf/index.html | 41 +----------------- .../assets/web/bookshelf/js/about.517e59ea.js | 1 - .../assets/web/bookshelf/js/about.a53813f7.js | 1 + .../web/bookshelf/js/about~detail.8270a871.js | 1 + .../web/bookshelf/js/about~detail.d5f6fddf.js | 1 - .../js/{app.b53a207e.js => app.fd005031.js} | 2 +- .../web/bookshelf/js/detail.133910b4.js | 1 - .../web/bookshelf/js/detail.2147373a.js | 1 + ...ifest.1be475b0fa464f5fe47e53b13d56bc1a.js} | 26 +++++------ .../assets/web/bookshelf/service-worker.js | 2 +- 14 files changed, 21 insertions(+), 58 deletions(-) rename app/src/main/assets/web/bookshelf/css/{about.dbe575e1.css => about.1cd9ab87.css} (53%) create mode 100644 app/src/main/assets/web/bookshelf/favicon.ico create mode 100644 app/src/main/assets/web/bookshelf/img/icons/favicon-16x16.png create mode 100644 app/src/main/assets/web/bookshelf/img/icons/favicon-32x32.png delete mode 100644 app/src/main/assets/web/bookshelf/js/about.517e59ea.js create mode 100644 app/src/main/assets/web/bookshelf/js/about.a53813f7.js create mode 100644 app/src/main/assets/web/bookshelf/js/about~detail.8270a871.js delete mode 100644 app/src/main/assets/web/bookshelf/js/about~detail.d5f6fddf.js rename app/src/main/assets/web/bookshelf/js/{app.b53a207e.js => app.fd005031.js} (97%) delete mode 100644 app/src/main/assets/web/bookshelf/js/detail.133910b4.js create mode 100644 app/src/main/assets/web/bookshelf/js/detail.2147373a.js rename app/src/main/assets/web/bookshelf/{precache-manifest.04173d48a716bddd6fb159c907ea23ec.js => precache-manifest.1be475b0fa464f5fe47e53b13d56bc1a.js} (70%) diff --git a/app/src/main/assets/web/bookshelf/css/about.dbe575e1.css b/app/src/main/assets/web/bookshelf/css/about.1cd9ab87.css similarity index 53% rename from app/src/main/assets/web/bookshelf/css/about.dbe575e1.css rename to app/src/main/assets/web/bookshelf/css/about.1cd9ab87.css index 1fcd705d9..d9acd3ed5 100644 --- a/app/src/main/assets/web/bookshelf/css/about.dbe575e1.css +++ b/app/src/main/assets/web/bookshelf/css/about.1cd9ab87.css @@ -1 +1 @@ -@charset "UTF-8";@font-face{font-family:FZZCYSK;src:local("☺"),url(../fonts/shelffont.6c094b6d.ttf);font-style:normal;font-weight:400}.index-wrapper[data-v-21a7f6c5]{height:100%;width:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.index-wrapper .navigation-wrapper[data-v-21a7f6c5]{width:260px;min-width:260px;padding:48px 36px;background-color:#f7f7f7}.index-wrapper .navigation-wrapper .navigation-title[data-v-21a7f6c5]{font-size:24px;font-weight:500;font-family:FZZCYSK}.index-wrapper .navigation-wrapper .navigation-sub-title[data-v-21a7f6c5]{font-size:16px;font-weight:300;font-family:FZZCYSK;margin-top:16px;color:#b1b1b1}.index-wrapper .navigation-wrapper .search-wrapper .search-input[data-v-21a7f6c5]{border-radius:50%;margin-top:24px}.index-wrapper .navigation-wrapper .search-wrapper .search-input[data-v-21a7f6c5] .el-input__inner{border-radius:50px;border-color:#e3e3e3}.index-wrapper .navigation-wrapper .recent-wrapper[data-v-21a7f6c5]{margin-top:36px}.index-wrapper .navigation-wrapper .recent-wrapper .recent-title[data-v-21a7f6c5]{font-size:14px;color:#b1b1b1;font-family:FZZCYSK}.index-wrapper .navigation-wrapper .recent-wrapper .reading-recent[data-v-21a7f6c5]{margin:18px 0}.index-wrapper .navigation-wrapper .recent-wrapper .reading-recent .recent-book[data-v-21a7f6c5]{font-size:10px;cursor:pointer}.index-wrapper .navigation-wrapper .setting-wrapper[data-v-21a7f6c5]{margin-top:36px}.index-wrapper .navigation-wrapper .setting-wrapper .setting-title[data-v-21a7f6c5]{font-size:14px;color:#b1b1b1;font-family:FZZCYSK}.index-wrapper .navigation-wrapper .setting-wrapper .no-point[data-v-21a7f6c5]{pointer-events:none}.index-wrapper .navigation-wrapper .setting-wrapper .setting-connect[data-v-21a7f6c5]{font-size:8px;margin-top:16px;cursor:pointer}.index-wrapper .navigation-wrapper .bottom-icons[data-v-21a7f6c5]{position:fixed;bottom:0;height:120px;width:260px;-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.index-wrapper .shelf-wrapper[data-v-21a7f6c5]{padding:48px 48px;width:100%}.index-wrapper .shelf-wrapper[data-v-21a7f6c5] .el-icon-loading{font-size:36px;color:#b5b5b5}.index-wrapper .shelf-wrapper[data-v-21a7f6c5] .el-loading-text{font-weight:500;color:#b5b5b5}.index-wrapper .shelf-wrapper .books-wrapper[data-v-21a7f6c5]{height:100%;overflow:scroll}.index-wrapper .shelf-wrapper .books-wrapper .wrapper[data-v-21a7f6c5]{display:grid;grid-template-columns:repeat(auto-fill,380px);-ms-flex-pack:distribute;justify-content:space-around;grid-gap:10px}.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book[data-v-21a7f6c5]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:-webkit-box;display:-ms-flexbox;display:flex;cursor:pointer;margin-bottom:18px;padding:24px 24px;width:360px;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-pack:distribute;justify-content:space-around}.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book .cover-img .cover[data-v-21a7f6c5],.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book .cover-img[data-v-21a7f6c5]{width:84px;height:112px}.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book .info[data-v-21a7f6c5]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:distribute;justify-content:space-around;-webkit-box-align:left;-ms-flex-align:left;align-items:left;height:112px;margin-left:20px;-webkit-box-flex:1;-ms-flex:1;flex:1}.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book .info .name[data-v-21a7f6c5]{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;font-size:16px;font-weight:700;color:#33373d}.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book .info .sub[data-v-21a7f6c5]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;font-size:12px;font-weight:600;color:#6b6b6b}.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book .info .sub .dot[data-v-21a7f6c5]{margin:0 7px}.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book .info .dur-chapter[data-v-21a7f6c5],.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book .info .intro[data-v-21a7f6c5],.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book .info .last-chapter[data-v-21a7f6c5]{color:#969ba3;font-size:13px;margin-top:3px;font-weight:500;word-wrap:break-word;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1;text-align:left}.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book[data-v-21a7f6c5]:hover{background:rgba(0,0,0,.1);-webkit-transition-duration:.5s;transition-duration:.5s}.index-wrapper .shelf-wrapper .books-wrapper .wrapper[data-v-21a7f6c5]:last-child{margin-right:auto}.index-wrapper .shelf-wrapper .books-wrapper[data-v-21a7f6c5]::-webkit-scrollbar{width:0!important} \ No newline at end of file +@charset "UTF-8";@font-face{font-family:FZZCYSK;src:local("☺"),url(../fonts/shelffont.6c094b6d.ttf);font-style:normal;font-weight:400}.index-wrapper[data-v-1b75763a]{height:100%;width:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.index-wrapper .navigation-wrapper[data-v-1b75763a]{width:260px;min-width:260px;padding:48px 36px;background-color:#f7f7f7}.index-wrapper .navigation-wrapper .navigation-title[data-v-1b75763a]{font-size:24px;font-weight:500;font-family:FZZCYSK}.index-wrapper .navigation-wrapper .navigation-sub-title[data-v-1b75763a]{font-size:16px;font-weight:300;font-family:FZZCYSK;margin-top:16px;color:#b1b1b1}.index-wrapper .navigation-wrapper .search-wrapper .search-input[data-v-1b75763a]{border-radius:50%;margin-top:24px}.index-wrapper .navigation-wrapper .search-wrapper .search-input[data-v-1b75763a] .el-input__inner{border-radius:50px;border-color:#e3e3e3}.index-wrapper .navigation-wrapper .recent-wrapper[data-v-1b75763a]{margin-top:36px}.index-wrapper .navigation-wrapper .recent-wrapper .recent-title[data-v-1b75763a]{font-size:14px;color:#b1b1b1;font-family:FZZCYSK}.index-wrapper .navigation-wrapper .recent-wrapper .reading-recent[data-v-1b75763a]{margin:18px 0}.index-wrapper .navigation-wrapper .recent-wrapper .reading-recent .recent-book[data-v-1b75763a]{font-size:10px;cursor:pointer}.index-wrapper .navigation-wrapper .setting-wrapper[data-v-1b75763a]{margin-top:36px}.index-wrapper .navigation-wrapper .setting-wrapper .setting-title[data-v-1b75763a]{font-size:14px;color:#b1b1b1;font-family:FZZCYSK}.index-wrapper .navigation-wrapper .setting-wrapper .no-point[data-v-1b75763a]{pointer-events:none}.index-wrapper .navigation-wrapper .setting-wrapper .setting-connect[data-v-1b75763a]{font-size:8px;margin-top:16px;cursor:pointer}.index-wrapper .navigation-wrapper .bottom-icons[data-v-1b75763a]{position:fixed;bottom:0;height:120px;width:260px;-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.index-wrapper .shelf-wrapper[data-v-1b75763a]{padding:48px 48px;width:100%}.index-wrapper .shelf-wrapper[data-v-1b75763a] .el-icon-loading{font-size:36px;color:#b5b5b5}.index-wrapper .shelf-wrapper[data-v-1b75763a] .el-loading-text{font-weight:500;color:#b5b5b5}.index-wrapper .shelf-wrapper .books-wrapper[data-v-1b75763a]{height:100%;overflow:scroll}.index-wrapper .shelf-wrapper .books-wrapper .wrapper[data-v-1b75763a]{display:grid;grid-template-columns:repeat(auto-fill,380px);-ms-flex-pack:distribute;justify-content:space-around;grid-gap:10px}.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book[data-v-1b75763a]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:-webkit-box;display:-ms-flexbox;display:flex;cursor:pointer;margin-bottom:18px;padding:24px 24px;width:360px;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-pack:distribute;justify-content:space-around}.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book .cover-img .cover[data-v-1b75763a],.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book .cover-img[data-v-1b75763a]{width:84px;height:112px}.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book .info[data-v-1b75763a]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:distribute;justify-content:space-around;-webkit-box-align:left;-ms-flex-align:left;align-items:left;height:112px;margin-left:20px;-webkit-box-flex:1;-ms-flex:1;flex:1}.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book .info .name[data-v-1b75763a]{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;font-size:16px;font-weight:700;color:#33373d}.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book .info .sub[data-v-1b75763a]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;font-size:12px;font-weight:600;color:#6b6b6b}.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book .info .sub .dot[data-v-1b75763a]{margin:0 7px}.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book .info .dur-chapter[data-v-1b75763a],.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book .info .intro[data-v-1b75763a],.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book .info .last-chapter[data-v-1b75763a]{color:#969ba3;font-size:13px;margin-top:3px;font-weight:500;word-wrap:break-word;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1;text-align:left}.index-wrapper .shelf-wrapper .books-wrapper .wrapper .book[data-v-1b75763a]:hover{background:rgba(0,0,0,.1);-webkit-transition-duration:.5s;transition-duration:.5s}.index-wrapper .shelf-wrapper .books-wrapper .wrapper[data-v-1b75763a]:last-child{margin-right:auto}.index-wrapper .shelf-wrapper .books-wrapper[data-v-1b75763a]::-webkit-scrollbar{width:0!important} \ No newline at end of file diff --git a/app/src/main/assets/web/bookshelf/favicon.ico b/app/src/main/assets/web/bookshelf/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..df36fcfb72584e00488330b560ebcf34a41c64c2 GIT binary patch literal 4286 zcmds*O-Phc6o&64GDVCEQHxsW(p4>LW*W<827=Unuo8sGpRux(DN@jWP-e29Wl%wj zY84_aq9}^Am9-cWTD5GGEo#+5Fi2wX_P*bo+xO!)p*7B;iKlbFd(U~_d(U?#hLj56 zPhFkj-|A6~Qk#@g^#D^U0XT1cu=c-vu1+SElX9NR;kzAUV(q0|dl0|%h|dI$%VICy zJnu2^L*Te9JrJMGh%-P79CL0}dq92RGU6gI{v2~|)p}sG5x0U*z<8U;Ij*hB9z?ei z@g6Xq-pDoPl=MANPiR7%172VA%r)kevtV-_5H*QJKFmd;8yA$98zCxBZYXTNZ#QFk2(TX0;Y2dt&WitL#$96|gJY=3xX zpCoi|YNzgO3R`f@IiEeSmKrPSf#h#Qd<$%Ej^RIeeYfsxhPMOG`S`Pz8q``=511zm zAm)MX5AV^5xIWPyEu7u>qYs?pn$I4nL9J!=K=SGlKLXpE<5x+2cDTXq?brj?n6sp= zphe9;_JHf40^9~}9i08r{XM$7HB!`{Ys~TK0kx<}ZQng`UPvH*11|q7&l9?@FQz;8 zx!=3<4seY*%=OlbCbcae?5^V_}*K>Uo6ZWV8mTyE^B=DKy7-sdLYkR5Z?paTgK-zyIkKjIcpyO z{+uIt&YSa_$QnN_@t~L014dyK(fOOo+W*MIxbA6Ndgr=Y!f#Tokqv}n<7-9qfHkc3 z=>a|HWqcX8fzQCT=dqVbogRq!-S>H%yA{1w#2Pn;=e>JiEj7Hl;zdt-2f+j2%DeVD zsW0Ab)ZK@0cIW%W7z}H{&~yGhn~D;aiP4=;m-HCo`BEI+Kd6 z={Xwx{TKxD#iCLfl2vQGDitKtN>z|-AdCN|$jTFDg0m3O`WLD4_s#$S literal 0 HcmV?d00001 diff --git a/app/src/main/assets/web/bookshelf/img/icons/favicon-16x16.png b/app/src/main/assets/web/bookshelf/img/icons/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..42af00963d81b8e39a30435c60ac482d1f8756e0 GIT binary patch literal 799 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>WRE8{w#)hawXn|-Xp4{E;v!=;4B^%-x&;Cm1 zP>^*#n_J!T^1SBMI!C4h-R53dN8`?ylD}d{L%(vZvUKT)~-CgWFQy3lt zIqW5#zOL*K8HL%o&D;R|TePl5?VWhq^wrj^qed%lKKkpp-FogeyEi+p zE?K8rW7E1fuEJ{5jaaAp0~aIt+keS?T)@vXM=*X}V#VGMCm1~v-+0wr{w3CJ-R8wG zS@XVpzqP-5Mf0H?y-zh=XVL>S6E;rKnDmrMQlmoKbK9p$evXN`oe{6g>lvi)-+#c) zb+Al&$zRqtWk1@VTt6MPmq9d7^!kmXZn8k{sFt`!l%ynwlArU1(iRB6fMfqu& zIjIUIl?AB^nFS@u3=9=>9)IHDC=AokIOTu(jOWuJ24-b$y<~1-Wnu5hBFw@HE)6D! wQ<#-EhbWxBaplC3Ge=~Ou%B-5Sm33{@Jd{;RG<|Mp00i_>zopr0DGh}-~a#s literal 0 HcmV?d00001 diff --git a/app/src/main/assets/web/bookshelf/img/icons/favicon-32x32.png b/app/src/main/assets/web/bookshelf/img/icons/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..46ca04dee251a4fa85a2891a145fbe20cc619d96 GIT binary patch literal 1271 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+0817m!EPlzi}fpbWjb7-1N zREF=ab|~82?p|H&9FPi<3Q0p2_nKbg9F`6d2a)0F5LviN5F-?-1uh6wgGU@;KHLFx zWcX}ub<4|h4hH*lce~e|TIa|N-yLo4RYl&*8eQTtJ=)5A);GJR=Xg%80{Y!&YpYvf zzSsOZP>Ahpcdsq>UfJl9kmb=;?z6GQH8a<1TD9-CHn-w}|NsA+Nb6JrgE+J#$S)X3 zGcfS;fdK35)2Be-Oetf?`zOY13)%G^e)6sPw@*;|%KXdcU#*P1v1-S;t21mOG>nAE-eH;@V%$t;WjcxYXwEUbR z3z>7z#DtTVO-oacoh9{_MQY8Ot-i}F{j^uD+E(t7w)x6MKX=vIp4w>b*IOPH6jixJ zZ#|uAv~yR1m9_1`d$&$jY?ogCnOnuicG8u{jt?HmM3~l)E(#;^5{P>Y|zRB0* zEz%!bA15~jCmrhl{dVr6;-~M#%Kx{>DI^zpsl1afdH67nWtqCYg=$*b#>z9DEt9H` z|+MWPFs%ZPNO+J zf0-lgZs?zWIq~q~#m;eY33n#>L}?XxEeV>+^y8e1Yo5XT(EXF-y$iEBhj zN@7W>RdP`(kYX@0Ff`XSFw`})3^6dbGBmU@HPtpSv@$SAK61eeMMG|WN@iLmZVf^+ zGrj>egja<`lmsP~D-;yvr)B1(DwI?fq$*?3oE!Zm>f=FR^A+ - - - - - - Legado Bookshelf - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - \ No newline at end of file + }
\ No newline at end of file diff --git a/app/src/main/assets/web/bookshelf/js/about.517e59ea.js b/app/src/main/assets/web/bookshelf/js/about.517e59ea.js deleted file mode 100644 index 1376eecb1..000000000 --- a/app/src/main/assets/web/bookshelf/js/about.517e59ea.js +++ /dev/null @@ -1 +0,0 @@ -(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["about"],{"04d1":function(t,e,n){var a=n("342f"),s=a.match(/firefox\/(\d+)/i);t.exports=!!s&&+s[1]},"0e8a":function(t,e,n){},"4e82":function(t,e,n){"use strict";var a=n("23e7"),s=n("1c0b"),r=n("7b0b"),i=n("50c4"),c=n("d039"),o=n("addb"),l=n("a640"),u=n("04d1"),d=n("d998"),h=n("2d00"),f=n("512c"),p=[],v=p.sort,g=c((function(){p.sort(void 0)})),m=c((function(){p.sort(null)})),C=l("sort"),w=!c((function(){if(h)return h<70;if(!(u&&u>3)){if(d)return!0;if(f)return f<603;var t,e,n,a,s="";for(t=65;t<76;t++){switch(e=String.fromCharCode(t),t){case 66:case 69:case 70:case 72:n=3;break;case 68:case 71:n=4;break;default:n=2}for(a=0;a<47;a++)p.push({k:e+a,v:n})}for(p.sort((function(t,e){return e.v-t.v})),a=0;aString(n)?1:-1}};a({target:"Array",proto:!0,forced:A},{sort:function(t){void 0!==t&&s(t);var e=r(this);if(w)return void 0===t?v.call(e):v.call(e,t);var n,a,c=[],l=i(e.length);for(a=0;a0)t[a]=t[--a];a!==r++&&(t[a]=n)}return t},r=function(t,e,n){var a=t.length,s=e.length,r=0,i=0,c=[];while(r]*>)/g,c=/\$([$&'`]|\d{1,2})/g;t.exports=function(t,e,n,o,l,u){var d=n+t.length,f=o.length,h=c;return void 0!==l&&(l=r(l),h=s),i.call(u,h,(function(r,i){var s;switch(i.charAt(0)){case"$":return"$";case"&":return t;case"`":return e.slice(0,n);case"'":return e.slice(d);case"<":s=l[i.slice(1,-1)];break;default:var c=+i;if(0===c)return r;if(c>f){var u=a(c/10);return 0===u?r:u<=f?void 0===o[u-1]?i.charAt(1):o[u-1]+i.charAt(1):r}s=o[c-1]}return void 0===s?"":s}))}},3182:function(t,e,n){},"4d63":function(t,e,n){var r=n("83ab"),a=n("da84"),i=n("94ca"),s=n("7156"),c=n("9112"),o=n("9bf2").f,l=n("241c").f,u=n("44e7"),d=n("ad6d"),f=n("9f7f"),h=n("6eeb"),g=n("d039"),p=n("5135"),v=n("69f3").enforce,m=n("2626"),b=n("b622"),w=n("fce3"),x=n("107c"),C=b("match"),A=a.RegExp,I=A.prototype,R=/^\?<[^\s\d!#%&*+<=>@^][^\s!#%&*+<=>@^]*>/,y=/a/g,k=/a/g,E=new A(y)!==y,M=f.UNSUPPORTED_Y,S=r&&(!E||M||w||x||g((function(){return k[C]=!1,A(y)!=y||A(k)==k||"/a/i"!=A(y,"i")}))),B=function(t){for(var e,n=t.length,r=0,a="",i=!1;r<=n;r++)e=t.charAt(r),"\\"!==e?i||"."!==e?("["===e?i=!0:"]"===e&&(i=!1),a+=e):a+="[\\s\\S]":a+=e+t.charAt(++r);return a},T=function(t){for(var e,n=t.length,r=0,a="",i=[],s={},c=!1,o=!1,l=0,u="";r<=n;r++){if(e=t.charAt(r),"\\"===e)e+=t.charAt(++r);else if("]"===e)c=!1;else if(!c)switch(!0){case"["===e:c=!0;break;case"("===e:R.test(t.slice(r+1))&&(r+=2,o=!0),a+=e,l++;continue;case">"===e&&o:if(""===u||p(s,u))throw new SyntaxError("Invalid capture group name");s[u]=!0,i.push([u,l]),o=!1,u="";continue}o?u+=e:a+=e}return[a,i]};if(i("RegExp",S)){for(var $=function(t,e){var n,r,a,i,o,l,f=this instanceof $,h=u(t),g=void 0===e,p=[],m=t;if(!f&&h&&g&&t.constructor===$)return t;if((h||t instanceof $)&&(t=t.source,g&&(e="flags"in m?m.flags:d.call(m))),t=void 0===t?"":String(t),e=void 0===e?"":String(e),m=t,w&&"dotAll"in y&&(r=!!e&&e.indexOf("s")>-1,r&&(e=e.replace(/s/g,""))),n=e,M&&"sticky"in y&&(a=!!e&&e.indexOf("y")>-1,a&&(e=e.replace(/y/g,""))),x&&(i=T(t),t=i[0],p=i[1]),o=s(A(t,e),f?this:I,$),(r||a||p.length)&&(l=v(o),r&&(l.dotAll=!0,l.raw=$(B(t),n)),a&&(l.sticky=!0),p.length&&(l.groups=p)),t!==m)try{c(o,"source",""===m?"(?:)":m)}catch(b){}return o},_=function(t){t in $||o($,t,{configurable:!0,get:function(){return A[t]},set:function(e){A[t]=e}})},D=l(A),z=0;D.length>z;)_(D[z++]);I.constructor=$,$.prototype=I,h(a,"RegExp",$)}m("RegExp")},"4e82":function(t,e,n){"use strict";var r=n("23e7"),a=n("1c0b"),i=n("7b0b"),s=n("50c4"),c=n("d039"),o=n("addb"),l=n("a640"),u=n("04d1"),d=n("d998"),f=n("2d00"),h=n("512c"),g=[],p=g.sort,v=c((function(){g.sort(void 0)})),m=c((function(){g.sort(null)})),b=l("sort"),w=!c((function(){if(f)return f<70;if(!(u&&u>3)){if(d)return!0;if(h)return h<603;var t,e,n,r,a="";for(t=65;t<76;t++){switch(e=String.fromCharCode(t),t){case 66:case 69:case 70:case 72:n=3;break;case 68:case 71:n=4;break;default:n=2}for(r=0;r<47;r++)g.push({k:e+r,v:n})}for(g.sort((function(t,e){return e.v-t.v})),r=0;rString(n)?1:-1}};r({target:"Array",proto:!0,forced:x},{sort:function(t){void 0!==t&&a(t);var e=i(this);if(w)return void 0===t?p.call(e):p.call(e,t);var n,r,c=[],l=s(e.length);for(r=0;r")}));r("replace",(function(t,e,n){var r=b?"$":"$0";return[function(t,n){var r=o(this),a=void 0==t?void 0:t[h];return void 0!==a?a.call(t,r,n):e.call(String(r),t,n)},function(t,a){if("string"===typeof a&&-1===a.indexOf(r)&&-1===a.indexOf("$<")){var o=n(e,this,t,a);if(o.done)return o.value}var f=i(this),h=String(t),m="function"===typeof a;m||(a=String(a));var b=f.global;if(b){var w=f.unicode;f.lastIndex=0}var x=[];while(1){var C=d(f,h);if(null===C)break;if(x.push(C),!b)break;var A=String(C[0]);""===A&&(f.lastIndex=l(h,s(f.lastIndex),w))}for(var I="",R=0,y=0;y=R&&(I+=h.slice(R,E)+$,R=E+k.length)}return I+h.slice(R)}]}),!w||!m||b)},7156:function(t,e,n){var r=n("861d"),a=n("d2bb");t.exports=function(t,e,n){var i,s;return a&&"function"==typeof(i=e.constructor)&&i!==n&&r(s=i.prototype)&&s!==n.prototype&&a(t,s),t}},"7b5b":function(t,e,n){},"7e43":function(t,e,n){t.exports=n.p+"img/noCover.b5c48bc1.jpeg"},"9e00":function(t,e,n){"use strict";n("3182")},a640:function(t,e,n){"use strict";var r=n("d039");t.exports=function(t,e){var n=[][t];return!!n&&r((function(){n.call(null,e||function(){throw 1},1)}))}},addb:function(t,e){var n=Math.floor,r=function(t,e){var s=t.length,c=n(s/2);return s<8?a(t,e):i(r(t.slice(0,c),e),r(t.slice(c),e),e)},a=function(t,e){var n,r,a=t.length,i=1;while(i0)t[r]=t[--r];r!==i++&&(t[r]=n)}return t},i=function(t,e,n){var r=t.length,a=e.length,i=0,s=0,c=[];while(ib)","string".charAt(5));return"b"!==e.exec("b").groups.a||"bc"!=="b".replace(e,"$c")}))},"14c3":function(e,t,n){var r=n("c6b6"),o=n("9263");e.exports=function(e,t){var n=e.exec;if("function"===typeof n){var i=n.call(e,t);if("object"!==typeof i)throw TypeError("RegExp exec method returned something other than an Object or null");return i}if("RegExp"!==r(e))throw TypeError("RegExp#exec called on incompatible receiver");return o.call(e,t)}},"1d2b":function(e,t,n){"use strict";e.exports=function(e,t){return function(){for(var n=new Array(arguments.length),r=0;r=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*"}}};r.forEach(["delete","get","head"],(function(e){c.headers[e]={}})),r.forEach(["post","put","patch"],(function(e){c.headers[e]=r.merge(i)})),e.exports=c}).call(this,n("4362"))},"2d83":function(e,t,n){"use strict";var r=n("387f");e.exports=function(e,t,n,o,i){var s=new Error(e);return r(s,t,n,o,i)}},"2e67":function(e,t,n){"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},"30b5":function(e,t,n){"use strict";var r=n("c532");function o(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}e.exports=function(e,t,n){if(!t)return e;var i;if(n)i=n(t);else if(r.isURLSearchParams(t))i=t.toString();else{var s=[];r.forEach(t,(function(e,t){null!==e&&"undefined"!==typeof e&&(r.isArray(e)?t+="[]":e=[e],r.forEach(e,(function(e){r.isDate(e)?e=e.toISOString():r.isObject(e)&&(e=JSON.stringify(e)),s.push(o(t)+"="+o(e))})))})),i=s.join("&")}if(i){var a=e.indexOf("#");-1!==a&&(e=e.slice(0,a)),e+=(-1===e.indexOf("?")?"?":"&")+i}return e}},"387f":function(e,t,n){"use strict";e.exports=function(e,t,n,r,o){return e.config=t,n&&(e.code=n),e.request=r,e.response=o,e.isAxiosError=!0,e.toJSON=function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code}},e}},3934:function(e,t,n){"use strict";var r=n("c532");e.exports=r.isStandardBrowserEnv()?function(){var e,t=/(msie|trident)/i.test(navigator.userAgent),n=document.createElement("a");function o(e){var r=e;return t&&(n.setAttribute("href",r),r=n.href),n.setAttribute("href",r),{href:n.href,protocol:n.protocol?n.protocol.replace(/:$/,""):"",host:n.host,search:n.search?n.search.replace(/^\?/,""):"",hash:n.hash?n.hash.replace(/^#/,""):"",hostname:n.hostname,port:n.port,pathname:"/"===n.pathname.charAt(0)?n.pathname:"/"+n.pathname}}return e=o(window.location.href),function(t){var n=r.isString(t)?o(t):t;return n.protocol===e.protocol&&n.host===e.host}}():function(){return function(){return!0}}()},4362:function(e,t,n){t.nextTick=function(e){var t=Array.prototype.slice.call(arguments);t.shift(),setTimeout((function(){e.apply(null,t)}),0)},t.platform=t.arch=t.execPath=t.title="browser",t.pid=1,t.browser=!0,t.env={},t.argv=[],t.binding=function(e){throw new Error("No such module. (Possibly not yet loaded)")},function(){var e,r="/";t.cwd=function(){return r},t.chdir=function(t){e||(e=n("df7c")),r=e.resolve(t,r)}}(),t.exit=t.kill=t.umask=t.dlopen=t.uptime=t.memoryUsage=t.uvCounters=function(){},t.features={}},"44e7":function(e,t,n){var r=n("861d"),o=n("c6b6"),i=n("b622"),s=i("match");e.exports=function(e){var t;return r(e)&&(void 0!==(t=e[s])?!!t:"RegExp"==o(e))}},"467f":function(e,t,n){"use strict";var r=n("2d83");e.exports=function(e,t,n){var o=n.config.validateStatus;n.status&&o&&!o(n.status)?t(r("Request failed with status code "+n.status,n.config,null,n.request,n)):e(n)}},"4a7b":function(e,t,n){"use strict";var r=n("c532");e.exports=function(e,t){t=t||{};var n={},o=["url","method","data"],i=["headers","auth","proxy","params"],s=["baseURL","transformRequest","transformResponse","paramsSerializer","timeout","timeoutMessage","withCredentials","adapter","responseType","xsrfCookieName","xsrfHeaderName","onUploadProgress","onDownloadProgress","decompress","maxContentLength","maxBodyLength","maxRedirects","transport","httpAgent","httpsAgent","cancelToken","socketPath","responseEncoding"],a=["validateStatus"];function c(e,t){return r.isPlainObject(e)&&r.isPlainObject(t)?r.merge(e,t):r.isPlainObject(t)?r.merge({},t):r.isArray(t)?t.slice():t}function u(o){r.isUndefined(t[o])?r.isUndefined(e[o])||(n[o]=c(void 0,e[o])):n[o]=c(e[o],t[o])}r.forEach(o,(function(e){r.isUndefined(t[e])||(n[e]=c(void 0,t[e]))})),r.forEach(i,u),r.forEach(s,(function(o){r.isUndefined(t[o])?r.isUndefined(e[o])||(n[o]=c(void 0,e[o])):n[o]=c(void 0,t[o])})),r.forEach(a,(function(r){r in t?n[r]=c(e[r],t[r]):r in e&&(n[r]=c(void 0,e[r]))}));var f=o.concat(i).concat(s).concat(a),l=Object.keys(e).concat(Object.keys(t)).filter((function(e){return-1===f.indexOf(e)}));return r.forEach(l,u),n}},5270:function(e,t,n){"use strict";var r=n("c532"),o=n("c401"),i=n("2e67"),s=n("2444");function a(e){e.cancelToken&&e.cancelToken.throwIfRequested()}e.exports=function(e){a(e),e.headers=e.headers||{},e.data=o(e.data,e.headers,e.transformRequest),e.headers=r.merge(e.headers.common||{},e.headers[e.method]||{},e.headers),r.forEach(["delete","get","head","post","put","patch","common"],(function(t){delete e.headers[t]}));var t=e.adapter||s.adapter;return t(e).then((function(t){return a(e),t.data=o(t.data,t.headers,e.transformResponse),t}),(function(t){return i(t)||(a(e),t&&t.response&&(t.response.data=o(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)}))}},"5f02":function(e,t,n){"use strict";e.exports=function(e){return"object"===typeof e&&!0===e.isAxiosError}},"7a77":function(e,t,n){"use strict";function r(e){this.message=e}r.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},r.prototype.__CANCEL__=!0,e.exports=r},"7aac":function(e,t,n){"use strict";var r=n("c532");e.exports=r.isStandardBrowserEnv()?function(){return{write:function(e,t,n,o,i,s){var a=[];a.push(e+"="+encodeURIComponent(t)),r.isNumber(n)&&a.push("expires="+new Date(n).toGMTString()),r.isString(o)&&a.push("path="+o),r.isString(i)&&a.push("domain="+i),!0===s&&a.push("secure"),document.cookie=a.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}()},"83b9":function(e,t,n){"use strict";var r=n("d925"),o=n("e683");e.exports=function(e,t){return e&&!r(t)?o(e,t):t}},"8aa5":function(e,t,n){"use strict";var r=n("6547").charAt;e.exports=function(e,t,n){return t+(n?r(e,t).length:1)}},"8df4":function(e,t,n){"use strict";var r=n("7a77");function o(e){if("function"!==typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise((function(e){t=e}));var n=this;e((function(e){n.reason||(n.reason=new r(e),t(n.reason))}))}o.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},o.source=function(){var e,t=new o((function(t){e=t}));return{token:t,cancel:e}},e.exports=o},9263:function(e,t,n){"use strict";var r=n("ad6d"),o=n("9f7f"),i=n("5692"),s=n("7c73"),a=n("69f3").get,c=n("fce3"),u=n("107c"),f=RegExp.prototype.exec,l=i("native-string-replace",String.prototype.replace),p=f,d=function(){var e=/a/,t=/b*/g;return f.call(e,"a"),f.call(t,"a"),0!==e.lastIndex||0!==t.lastIndex}(),h=o.UNSUPPORTED_Y||o.BROKEN_CARET,m=void 0!==/()??/.exec("")[1],g=d||m||h||c||u;g&&(p=function(e){var t,n,o,i,c,u,g,v=this,x=a(v),y=x.raw;if(y)return y.lastIndex=v.lastIndex,t=p.call(y,e),v.lastIndex=y.lastIndex,t;var b=x.groups,w=h&&v.sticky,E=r.call(v),R=v.source,A=0,C=e;if(w&&(E=E.replace("y",""),-1===E.indexOf("g")&&(E+="g"),C=String(e).slice(v.lastIndex),v.lastIndex>0&&(!v.multiline||v.multiline&&"\n"!==e[v.lastIndex-1])&&(R="(?: "+R+")",C=" "+C,A++),n=new RegExp("^(?:"+R+")",E)),m&&(n=new RegExp("^"+R+"$(?!\\s)",E)),d&&(o=v.lastIndex),i=f.call(w?n:v,C),w?i?(i.input=i.input.slice(A),i[0]=i[0].slice(A),i.index=v.lastIndex,v.lastIndex+=i[0].length):v.lastIndex=0:d&&i&&(v.lastIndex=v.global?i.index+i[0].length:o),m&&i&&i.length>1&&l.call(i[0],n,(function(){for(c=1;c=0)return;s[t]="set-cookie"===t?(s[t]?s[t]:[]).concat([n]):s[t]?s[t]+", "+n:n}})),s):s}},c401:function(e,t,n){"use strict";var r=n("c532");e.exports=function(e,t,n){return r.forEach(n,(function(n){e=n(e,t)})),e}},c532:function(e,t,n){"use strict";var r=n("1d2b"),o=Object.prototype.toString;function i(e){return"[object Array]"===o.call(e)}function s(e){return"undefined"===typeof e}function a(e){return null!==e&&!s(e)&&null!==e.constructor&&!s(e.constructor)&&"function"===typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}function c(e){return"[object ArrayBuffer]"===o.call(e)}function u(e){return"undefined"!==typeof FormData&&e instanceof FormData}function f(e){var t;return t="undefined"!==typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer,t}function l(e){return"string"===typeof e}function p(e){return"number"===typeof e}function d(e){return null!==e&&"object"===typeof e}function h(e){if("[object Object]"!==o.call(e))return!1;var t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}function m(e){return"[object Date]"===o.call(e)}function g(e){return"[object File]"===o.call(e)}function v(e){return"[object Blob]"===o.call(e)}function x(e){return"[object Function]"===o.call(e)}function y(e){return d(e)&&x(e.pipe)}function b(e){return"undefined"!==typeof URLSearchParams&&e instanceof URLSearchParams}function w(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function E(){return("undefined"===typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!==typeof window&&"undefined"!==typeof document)}function R(e,t){if(null!==e&&"undefined"!==typeof e)if("object"!==typeof e&&(e=[e]),i(e))for(var n=0,r=e.length;n=0;r--){var o=e[r];"."===o?e.splice(r,1):".."===o?(e.splice(r,1),n++):n&&(e.splice(r,1),n--)}if(t)for(;n--;n)e.unshift("..");return e}function r(e){"string"!==typeof e&&(e+="");var t,n=0,r=-1,o=!0;for(t=e.length-1;t>=0;--t)if(47===e.charCodeAt(t)){if(!o){n=t+1;break}}else-1===r&&(o=!1,r=t+1);return-1===r?"":e.slice(n,r)}function o(e,t){if(e.filter)return e.filter(t);for(var n=[],r=0;r=-1&&!r;i--){var s=i>=0?arguments[i]:e.cwd();if("string"!==typeof s)throw new TypeError("Arguments to path.resolve must be strings");s&&(t=s+"/"+t,r="/"===s.charAt(0))}return t=n(o(t.split("/"),(function(e){return!!e})),!r).join("/"),(r?"/":"")+t||"."},t.normalize=function(e){var r=t.isAbsolute(e),s="/"===i(e,-1);return e=n(o(e.split("/"),(function(e){return!!e})),!r).join("/"),e||r||(e="."),e&&s&&(e+="/"),(r?"/":"")+e},t.isAbsolute=function(e){return"/"===e.charAt(0)},t.join=function(){var e=Array.prototype.slice.call(arguments,0);return t.normalize(o(e,(function(e,t){if("string"!==typeof e)throw new TypeError("Arguments to path.join must be strings");return e})).join("/"))},t.relative=function(e,n){function r(e){for(var t=0;t=0;n--)if(""!==e[n])break;return t>n?[]:e.slice(t,n-t+1)}e=t.resolve(e).substr(1),n=t.resolve(n).substr(1);for(var o=r(e.split("/")),i=r(n.split("/")),s=Math.min(o.length,i.length),a=s,c=0;c=1;--i)if(t=e.charCodeAt(i),47===t){if(!o){r=i;break}}else o=!1;return-1===r?n?"/":".":n&&1===r?"/":e.slice(0,r)},t.basename=function(e,t){var n=r(e);return t&&n.substr(-1*t.length)===t&&(n=n.substr(0,n.length-t.length)),n},t.extname=function(e){"string"!==typeof e&&(e+="");for(var t=-1,n=0,r=-1,o=!0,i=0,s=e.length-1;s>=0;--s){var a=e.charCodeAt(s);if(47!==a)-1===r&&(o=!1,r=s+1),46===a?-1===t?t=s:1!==i&&(i=1):-1!==t&&(i=-1);else if(!o){n=s+1;break}}return-1===t||-1===r||0===i||1===i&&t===r-1&&t===n+1?"":e.slice(t,r)};var i="b"==="ab".substr(-1)?function(e,t,n){return e.substr(t,n)}:function(e,t,n){return t<0&&(t=e.length+t),e.substr(t,n)}}).call(this,n("4362"))},e683:function(e,t,n){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},f6b4:function(e,t,n){"use strict";var r=n("c532");function o(){this.handlers=[]}o.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},o.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},o.prototype.forEach=function(e){r.forEach(this.handlers,(function(t){null!==t&&e(t)}))},e.exports=o},fce3:function(e,t,n){var r=n("d039");e.exports=r((function(){var e=RegExp(".","string".charAt(0));return!(e.dotAll&&e.exec("\n")&&"s"===e.flags)}))}}]); \ No newline at end of file diff --git a/app/src/main/assets/web/bookshelf/js/about~detail.d5f6fddf.js b/app/src/main/assets/web/bookshelf/js/about~detail.d5f6fddf.js deleted file mode 100644 index 1de390135..000000000 --- a/app/src/main/assets/web/bookshelf/js/about~detail.d5f6fddf.js +++ /dev/null @@ -1 +0,0 @@ -(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["about~detail"],{"0a06":function(e,t,r){"use strict";var n=r("c532"),o=r("30b5"),i=r("f6b4"),s=r("5270"),a=r("4a7b");function u(e){this.defaults=e,this.interceptors={request:new i,response:new i}}u.prototype.request=function(e){"string"===typeof e?(e=arguments[1]||{},e.url=arguments[0]):e=e||{},e=a(this.defaults,e),e.method?e.method=e.method.toLowerCase():this.defaults.method?e.method=this.defaults.method.toLowerCase():e.method="get";var t=[s,void 0],r=Promise.resolve(e);this.interceptors.request.forEach((function(e){t.unshift(e.fulfilled,e.rejected)})),this.interceptors.response.forEach((function(e){t.push(e.fulfilled,e.rejected)}));while(t.length)r=r.then(t.shift(),t.shift());return r},u.prototype.getUri=function(e){return e=a(this.defaults,e),o(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},n.forEach(["delete","get","head","options"],(function(e){u.prototype[e]=function(t,r){return this.request(a(r||{},{method:e,url:t,data:(r||{}).data}))}})),n.forEach(["post","put","patch"],(function(e){u.prototype[e]=function(t,r,n){return this.request(a(n||{},{method:e,url:t,data:r}))}})),e.exports=u},"0df6":function(e,t,r){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}},"1d2b":function(e,t,r){"use strict";e.exports=function(e,t){return function(){for(var r=new Array(arguments.length),n=0;n=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*"}}};n.forEach(["delete","get","head"],(function(e){u.headers[e]={}})),n.forEach(["post","put","patch"],(function(e){u.headers[e]=n.merge(i)})),e.exports=u}).call(this,r("4362"))},"2d83":function(e,t,r){"use strict";var n=r("387f");e.exports=function(e,t,r,o,i){var s=new Error(e);return n(s,t,r,o,i)}},"2e67":function(e,t,r){"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},"30b5":function(e,t,r){"use strict";var n=r("c532");function o(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}e.exports=function(e,t,r){if(!t)return e;var i;if(r)i=r(t);else if(n.isURLSearchParams(t))i=t.toString();else{var s=[];n.forEach(t,(function(e,t){null!==e&&"undefined"!==typeof e&&(n.isArray(e)?t+="[]":e=[e],n.forEach(e,(function(e){n.isDate(e)?e=e.toISOString():n.isObject(e)&&(e=JSON.stringify(e)),s.push(o(t)+"="+o(e))})))})),i=s.join("&")}if(i){var a=e.indexOf("#");-1!==a&&(e=e.slice(0,a)),e+=(-1===e.indexOf("?")?"?":"&")+i}return e}},"387f":function(e,t,r){"use strict";e.exports=function(e,t,r,n,o){return e.config=t,r&&(e.code=r),e.request=n,e.response=o,e.isAxiosError=!0,e.toJSON=function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code}},e}},3934:function(e,t,r){"use strict";var n=r("c532");e.exports=n.isStandardBrowserEnv()?function(){var e,t=/(msie|trident)/i.test(navigator.userAgent),r=document.createElement("a");function o(e){var n=e;return t&&(r.setAttribute("href",n),n=r.href),r.setAttribute("href",n),{href:r.href,protocol:r.protocol?r.protocol.replace(/:$/,""):"",host:r.host,search:r.search?r.search.replace(/^\?/,""):"",hash:r.hash?r.hash.replace(/^#/,""):"",hostname:r.hostname,port:r.port,pathname:"/"===r.pathname.charAt(0)?r.pathname:"/"+r.pathname}}return e=o(window.location.href),function(t){var r=n.isString(t)?o(t):t;return r.protocol===e.protocol&&r.host===e.host}}():function(){return function(){return!0}}()},4362:function(e,t,r){t.nextTick=function(e){var t=Array.prototype.slice.call(arguments);t.shift(),setTimeout((function(){e.apply(null,t)}),0)},t.platform=t.arch=t.execPath=t.title="browser",t.pid=1,t.browser=!0,t.env={},t.argv=[],t.binding=function(e){throw new Error("No such module. (Possibly not yet loaded)")},function(){var e,n="/";t.cwd=function(){return n},t.chdir=function(t){e||(e=r("df7c")),n=e.resolve(t,n)}}(),t.exit=t.kill=t.umask=t.dlopen=t.uptime=t.memoryUsage=t.uvCounters=function(){},t.features={}},"467f":function(e,t,r){"use strict";var n=r("2d83");e.exports=function(e,t,r){var o=r.config.validateStatus;r.status&&o&&!o(r.status)?t(n("Request failed with status code "+r.status,r.config,null,r.request,r)):e(r)}},"4a7b":function(e,t,r){"use strict";var n=r("c532");e.exports=function(e,t){t=t||{};var r={},o=["url","method","data"],i=["headers","auth","proxy","params"],s=["baseURL","transformRequest","transformResponse","paramsSerializer","timeout","timeoutMessage","withCredentials","adapter","responseType","xsrfCookieName","xsrfHeaderName","onUploadProgress","onDownloadProgress","decompress","maxContentLength","maxBodyLength","maxRedirects","transport","httpAgent","httpsAgent","cancelToken","socketPath","responseEncoding"],a=["validateStatus"];function u(e,t){return n.isPlainObject(e)&&n.isPlainObject(t)?n.merge(e,t):n.isPlainObject(t)?n.merge({},t):n.isArray(t)?t.slice():t}function c(o){n.isUndefined(t[o])?n.isUndefined(e[o])||(r[o]=u(void 0,e[o])):r[o]=u(e[o],t[o])}n.forEach(o,(function(e){n.isUndefined(t[e])||(r[e]=u(void 0,t[e]))})),n.forEach(i,c),n.forEach(s,(function(o){n.isUndefined(t[o])?n.isUndefined(e[o])||(r[o]=u(void 0,e[o])):r[o]=u(void 0,t[o])})),n.forEach(a,(function(n){n in t?r[n]=u(e[n],t[n]):n in e&&(r[n]=u(void 0,e[n]))}));var f=o.concat(i).concat(s).concat(a),p=Object.keys(e).concat(Object.keys(t)).filter((function(e){return-1===f.indexOf(e)}));return n.forEach(p,c),r}},5270:function(e,t,r){"use strict";var n=r("c532"),o=r("c401"),i=r("2e67"),s=r("2444");function a(e){e.cancelToken&&e.cancelToken.throwIfRequested()}e.exports=function(e){a(e),e.headers=e.headers||{},e.data=o(e.data,e.headers,e.transformRequest),e.headers=n.merge(e.headers.common||{},e.headers[e.method]||{},e.headers),n.forEach(["delete","get","head","post","put","patch","common"],(function(t){delete e.headers[t]}));var t=e.adapter||s.adapter;return t(e).then((function(t){return a(e),t.data=o(t.data,t.headers,e.transformResponse),t}),(function(t){return i(t)||(a(e),t&&t.response&&(t.response.data=o(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)}))}},"5f02":function(e,t,r){"use strict";e.exports=function(e){return"object"===typeof e&&!0===e.isAxiosError}},"7a77":function(e,t,r){"use strict";function n(e){this.message=e}n.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},n.prototype.__CANCEL__=!0,e.exports=n},"7aac":function(e,t,r){"use strict";var n=r("c532");e.exports=n.isStandardBrowserEnv()?function(){return{write:function(e,t,r,o,i,s){var a=[];a.push(e+"="+encodeURIComponent(t)),n.isNumber(r)&&a.push("expires="+new Date(r).toGMTString()),n.isString(o)&&a.push("path="+o),n.isString(i)&&a.push("domain="+i),!0===s&&a.push("secure"),document.cookie=a.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}()},"83b9":function(e,t,r){"use strict";var n=r("d925"),o=r("e683");e.exports=function(e,t){return e&&!n(t)?o(e,t):t}},"8df4":function(e,t,r){"use strict";var n=r("7a77");function o(e){if("function"!==typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise((function(e){t=e}));var r=this;e((function(e){r.reason||(r.reason=new n(e),t(r.reason))}))}o.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},o.source=function(){var e,t=new o((function(t){e=t}));return{token:t,cancel:e}},e.exports=o},b50d:function(e,t,r){"use strict";var n=r("c532"),o=r("467f"),i=r("7aac"),s=r("30b5"),a=r("83b9"),u=r("c345"),c=r("3934"),f=r("2d83");e.exports=function(e){return new Promise((function(t,r){var p=e.data,l=e.headers;n.isFormData(p)&&delete l["Content-Type"];var d=new XMLHttpRequest;if(e.auth){var h=e.auth.username||"",m=e.auth.password?unescape(encodeURIComponent(e.auth.password)):"";l.Authorization="Basic "+btoa(h+":"+m)}var g=a(e.baseURL,e.url);if(d.open(e.method.toUpperCase(),s(g,e.params,e.paramsSerializer),!0),d.timeout=e.timeout,d.onreadystatechange=function(){if(d&&4===d.readyState&&(0!==d.status||d.responseURL&&0===d.responseURL.indexOf("file:"))){var n="getAllResponseHeaders"in d?u(d.getAllResponseHeaders()):null,i=e.responseType&&"text"!==e.responseType?d.response:d.responseText,s={data:i,status:d.status,statusText:d.statusText,headers:n,config:e,request:d};o(t,r,s),d=null}},d.onabort=function(){d&&(r(f("Request aborted",e,"ECONNABORTED",d)),d=null)},d.onerror=function(){r(f("Network Error",e,null,d)),d=null},d.ontimeout=function(){var t="timeout of "+e.timeout+"ms exceeded";e.timeoutErrorMessage&&(t=e.timeoutErrorMessage),r(f(t,e,"ECONNABORTED",d)),d=null},n.isStandardBrowserEnv()){var v=(e.withCredentials||c(g))&&e.xsrfCookieName?i.read(e.xsrfCookieName):void 0;v&&(l[e.xsrfHeaderName]=v)}if("setRequestHeader"in d&&n.forEach(l,(function(e,t){"undefined"===typeof p&&"content-type"===t.toLowerCase()?delete l[t]:d.setRequestHeader(t,e)})),n.isUndefined(e.withCredentials)||(d.withCredentials=!!e.withCredentials),e.responseType)try{d.responseType=e.responseType}catch(y){if("json"!==e.responseType)throw y}"function"===typeof e.onDownloadProgress&&d.addEventListener("progress",e.onDownloadProgress),"function"===typeof e.onUploadProgress&&d.upload&&d.upload.addEventListener("progress",e.onUploadProgress),e.cancelToken&&e.cancelToken.promise.then((function(e){d&&(d.abort(),r(e),d=null)})),p||(p=null),d.send(p)}))}},bc3a:function(e,t,r){e.exports=r("cee4")},c345:function(e,t,r){"use strict";var n=r("c532"),o=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];e.exports=function(e){var t,r,i,s={};return e?(n.forEach(e.split("\n"),(function(e){if(i=e.indexOf(":"),t=n.trim(e.substr(0,i)).toLowerCase(),r=n.trim(e.substr(i+1)),t){if(s[t]&&o.indexOf(t)>=0)return;s[t]="set-cookie"===t?(s[t]?s[t]:[]).concat([r]):s[t]?s[t]+", "+r:r}})),s):s}},c401:function(e,t,r){"use strict";var n=r("c532");e.exports=function(e,t,r){return n.forEach(r,(function(r){e=r(e,t)})),e}},c532:function(e,t,r){"use strict";var n=r("1d2b"),o=Object.prototype.toString;function i(e){return"[object Array]"===o.call(e)}function s(e){return"undefined"===typeof e}function a(e){return null!==e&&!s(e)&&null!==e.constructor&&!s(e.constructor)&&"function"===typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}function u(e){return"[object ArrayBuffer]"===o.call(e)}function c(e){return"undefined"!==typeof FormData&&e instanceof FormData}function f(e){var t;return t="undefined"!==typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer,t}function p(e){return"string"===typeof e}function l(e){return"number"===typeof e}function d(e){return null!==e&&"object"===typeof e}function h(e){if("[object Object]"!==o.call(e))return!1;var t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}function m(e){return"[object Date]"===o.call(e)}function g(e){return"[object File]"===o.call(e)}function v(e){return"[object Blob]"===o.call(e)}function y(e){return"[object Function]"===o.call(e)}function b(e){return d(e)&&y(e.pipe)}function w(e){return"undefined"!==typeof URLSearchParams&&e instanceof URLSearchParams}function x(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function E(){return("undefined"===typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!==typeof window&&"undefined"!==typeof document)}function A(e,t){if(null!==e&&"undefined"!==typeof e)if("object"!==typeof e&&(e=[e]),i(e))for(var r=0,n=e.length;r=0;n--){var o=e[n];"."===o?e.splice(n,1):".."===o?(e.splice(n,1),r++):r&&(e.splice(n,1),r--)}if(t)for(;r--;r)e.unshift("..");return e}function n(e){"string"!==typeof e&&(e+="");var t,r=0,n=-1,o=!0;for(t=e.length-1;t>=0;--t)if(47===e.charCodeAt(t)){if(!o){r=t+1;break}}else-1===n&&(o=!1,n=t+1);return-1===n?"":e.slice(r,n)}function o(e,t){if(e.filter)return e.filter(t);for(var r=[],n=0;n=-1&&!n;i--){var s=i>=0?arguments[i]:e.cwd();if("string"!==typeof s)throw new TypeError("Arguments to path.resolve must be strings");s&&(t=s+"/"+t,n="/"===s.charAt(0))}return t=r(o(t.split("/"),(function(e){return!!e})),!n).join("/"),(n?"/":"")+t||"."},t.normalize=function(e){var n=t.isAbsolute(e),s="/"===i(e,-1);return e=r(o(e.split("/"),(function(e){return!!e})),!n).join("/"),e||n||(e="."),e&&s&&(e+="/"),(n?"/":"")+e},t.isAbsolute=function(e){return"/"===e.charAt(0)},t.join=function(){var e=Array.prototype.slice.call(arguments,0);return t.normalize(o(e,(function(e,t){if("string"!==typeof e)throw new TypeError("Arguments to path.join must be strings");return e})).join("/"))},t.relative=function(e,r){function n(e){for(var t=0;t=0;r--)if(""!==e[r])break;return t>r?[]:e.slice(t,r-t+1)}e=t.resolve(e).substr(1),r=t.resolve(r).substr(1);for(var o=n(e.split("/")),i=n(r.split("/")),s=Math.min(o.length,i.length),a=s,u=0;u=1;--i)if(t=e.charCodeAt(i),47===t){if(!o){n=i;break}}else o=!1;return-1===n?r?"/":".":r&&1===n?"/":e.slice(0,n)},t.basename=function(e,t){var r=n(e);return t&&r.substr(-1*t.length)===t&&(r=r.substr(0,r.length-t.length)),r},t.extname=function(e){"string"!==typeof e&&(e+="");for(var t=-1,r=0,n=-1,o=!0,i=0,s=e.length-1;s>=0;--s){var a=e.charCodeAt(s);if(47!==a)-1===n&&(o=!1,n=s+1),46===a?-1===t?t=s:1!==i&&(i=1):-1!==t&&(i=-1);else if(!o){r=s+1;break}}return-1===t||-1===n||0===i||1===i&&t===n-1&&t===r+1?"":e.slice(t,n)};var i="b"==="ab".substr(-1)?function(e,t,r){return e.substr(t,r)}:function(e,t,r){return t<0&&(t=e.length+t),e.substr(t,r)}}).call(this,r("4362"))},e683:function(e,t,r){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},f6b4:function(e,t,r){"use strict";var n=r("c532");function o(){this.handlers=[]}o.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},o.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},o.prototype.forEach=function(e){n.forEach(this.handlers,(function(t){null!==t&&e(t)}))},e.exports=o}}]); \ No newline at end of file diff --git a/app/src/main/assets/web/bookshelf/js/app.b53a207e.js b/app/src/main/assets/web/bookshelf/js/app.fd005031.js similarity index 97% rename from app/src/main/assets/web/bookshelf/js/app.b53a207e.js rename to app/src/main/assets/web/bookshelf/js/app.fd005031.js index 1482c8893..88be8360a 100644 --- a/app/src/main/assets/web/bookshelf/js/app.b53a207e.js +++ b/app/src/main/assets/web/bookshelf/js/app.fd005031.js @@ -1 +1 @@ -(function(e){function t(t){for(var o,r,i=t[0],c=t[1],l=t[2],f=0,d=[];f>>32-t}function o(e,t){var n,o,r,a,u;return r=2147483648&e,a=2147483648&t,n=1073741824&e,o=1073741824&t,u=(1073741823&e)+(1073741823&t),n&o?2147483648^u^r^a:n|o?1073741824&u?3221225472^u^r^a:1073741824^u^r^a:u^r^a}function r(e,t,n){return e&t|~e&n}function a(e,t,n){return e&n|t&~n}function u(e,t,n){return e^t^n}function i(e,t,n){return t^(e|~n)}function c(e,t,a,u,i,c,l){return e=o(e,o(o(r(t,a,u),i),l)),o(n(e,c),t)}function l(e,t,r,u,i,c,l){return e=o(e,o(o(a(t,r,u),i),l)),o(n(e,c),t)}function f(e,t,r,a,i,c,l){return e=o(e,o(o(u(t,r,a),i),l)),o(n(e,c),t)}function d(e,t,r,a,u,c,l){return e=o(e,o(o(i(t,r,a),u),l)),o(n(e,c),t)}function s(e){var t,n=e.length,o=n+8,r=(o-o%64)/64,a=16*(r+1),u=Array(a-1),i=0,c=0;while(c>>29,u}function p(e){var t,n,o="",r="";for(n=0;n<=3;n++)t=e>>>8*n&255,r="0"+t.toString(16),o+=r.substr(r.length-2,2);return o}var b,h,g,m,v,y,w,C,S,P=Array(),O=7,j=12,k=17,T=22,_=5,x=9,E=14,A=20,L=4,$=11,B=16,N=23,V=6,M=10,D=15,q=21;for(P=s(t),y=1732584193,w=4023233417,C=2562383102,S=271733878,b=0;b>>32-t}function o(e,t){var n,o,r,a,u;return r=2147483648&e,a=2147483648&t,n=1073741824&e,o=1073741824&t,u=(1073741823&e)+(1073741823&t),n&o?2147483648^u^r^a:n|o?1073741824&u?3221225472^u^r^a:1073741824^u^r^a:u^r^a}function r(e,t,n){return e&t|~e&n}function a(e,t,n){return e&n|t&~n}function u(e,t,n){return e^t^n}function i(e,t,n){return t^(e|~n)}function c(e,t,a,u,i,c,l){return e=o(e,o(o(r(t,a,u),i),l)),o(n(e,c),t)}function l(e,t,r,u,i,c,l){return e=o(e,o(o(a(t,r,u),i),l)),o(n(e,c),t)}function f(e,t,r,a,i,c,l){return e=o(e,o(o(u(t,r,a),i),l)),o(n(e,c),t)}function d(e,t,r,a,u,c,l){return e=o(e,o(o(i(t,r,a),u),l)),o(n(e,c),t)}function s(e){var t,n=e.length,o=n+8,r=(o-o%64)/64,a=16*(r+1),u=Array(a-1),i=0,c=0;while(c>>29,u}function p(e){var t,n,o="",r="";for(n=0;n<=3;n++)t=e>>>8*n&255,r="0"+t.toString(16),o+=r.substr(r.length-2,2);return o}var b,h,g,m,v,y,w,C,S,P=Array(),O=7,j=12,k=17,T=22,_=5,x=9,E=14,A=20,L=4,$=11,B=16,N=23,V=6,M=10,D=15,q=21;for(P=s(t),y=1732584193,w=4023233417,C=2562383102,S=271733878,b=0;bb)","string".charAt(5));return"b"!==t.exec("b").groups.a||"bc"!=="b".replace(t,"$c")}))},1276:function(t,e,n){"use strict";var o=n("d784"),i=n("44e7"),a=n("825a"),s=n("1d80"),r=n("4840"),c=n("8aa5"),l=n("50c4"),u=n("14c3"),A=n("9263"),f=n("9f7f"),g=n("d039"),d=f.UNSUPPORTED_Y,h=[].push,p=Math.min,m=4294967295,v=!g((function(){var t=/(?:)/,e=t.exec;t.exec=function(){return e.apply(this,arguments)};var n="ab".split(t);return 2!==n.length||"a"!==n[0]||"b"!==n[1]}));o("split",(function(t,e,n){var o;return o="c"=="abbc".split(/(b)*/)[1]||4!="test".split(/(?:)/,-1).length||2!="ab".split(/(?:ab)*/).length||4!=".".split(/(.?)(.?)/).length||".".split(/()()/).length>1||"".split(/.?/).length?function(t,n){var o=String(s(this)),a=void 0===n?m:n>>>0;if(0===a)return[];if(void 0===t)return[o];if(!i(t))return e.call(o,t,a);var r,c,l,u=[],f=(t.ignoreCase?"i":"")+(t.multiline?"m":"")+(t.unicode?"u":"")+(t.sticky?"y":""),g=0,d=new RegExp(t.source,f+"g");while(r=A.call(d,o)){if(c=d.lastIndex,c>g&&(u.push(o.slice(g,r.index)),r.length>1&&r.index=a))break;d.lastIndex===r.index&&d.lastIndex++}return g===o.length?!l&&d.test("")||u.push(""):u.push(o.slice(g)),u.length>a?u.slice(0,a):u}:"0".split(void 0,0).length?function(t,n){return void 0===t&&0===n?[]:e.call(this,t,n)}:e,[function(e,n){var i=s(this),a=void 0==e?void 0:e[t];return void 0!==a?a.call(e,i,n):o.call(String(i),e,n)},function(t,i){var s=n(o,this,t,i,o!==e);if(s.done)return s.value;var A=a(this),f=String(t),g=r(A,RegExp),h=A.unicode,v=(A.ignoreCase?"i":"")+(A.multiline?"m":"")+(A.unicode?"u":"")+(d?"g":"y"),b=new g(d?"^(?:"+A.source+")":A,v),C=void 0===i?m:i>>>0;if(0===C)return[];if(0===f.length)return null===u(b,f)?[f]:[];var y=0,x=0,B=[];while(x=51||!o((function(){var e=[],n=e.constructor={};return n[s]=function(){return{foo:1}},1!==e[t](Boolean).foo}))}},"1e75":function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAADFBMVEXM2t7O3ODQ3uLR4OTDp25yAAACdUlEQVQozw3P70sTcQDH8c/3/M7NG+j35mnHwjwh4hRy/QFK3zvPNbeIG1koPZmxfj2IDAwihL53zj0JYisfmEHcZJZOiBUG60lZiI8T/ANusuftgQ+kCPIPeMP7hS5mUrV9c1g6MQCAEZ8tDLHwofImAGRlX+SZK3Vu9rRRPuO4PK6/9nA4GIATsxlODS+rdCMhkAZivpYV0LWoQHSLSA4NfUg+6mY+7BKL2++F9LvnrBDYm6JO9i/YO3i/HJTGQ4pdIV82TbEDFG6vGYCd4wZchgK5J2CrKTLE+Tx0v+YGlIbdWJFcQl4ptBN8fUJQN1MCJLcZLYwUVVo+famGGty8EXJF5ofOEDzcodT3/Fb0I5sHmc1ZG7CcSl8COgxlXx09jT05OafjCZLIHJhGIaU6wDZHsuMQ41wbdjmQXbhKnMq1zlXSYrjCnyZblqexA7fC8RxS74tq2P3OxSQwTuJSApH8OZLzBBp1pOe0i3rdyDUA47GySZ31YmC4EQYSXvFSvieORGBxXF9aeVtUWKGS9WMC4Z9Y2uXnJ2nCUXVMbPOYqNYNmGWWQ7Evr+BWC+a0JAMTImcq/S4Z5INdQMeuOqDIMa9beilxfA60iC6sP1INcPDpmHBW8drZHNmqwyddJtVje9q8WGUgWAOzmbU4FCQBFi8B2Wk6pickBnYhJMenmJGuRmtt2IoKq9NuFGbNFR99sHnvrnLsLysKANDIsxbp6RNMAsoDSKuRpMwZbAAzI68QatIjmZ0aImyM3O8/4e2MNlOHZomFsa/fLDsysliHS+nlYLQJMnynxrH8QO4PaAV2Li8B/+52UgeGIVNFYf8B1XG/kFSmLcUAAAAASUVORK5CYII="},"356c":function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAADFBMVEXPz8/R0dHT09PU1NToNyAhAAACdElEQVQozw3NP0xTQQDH8d9d7sFrG+QeKVgQ4aoFCwFkYERyLY//0UB8GNGg1WAC0RBGJrzW4mCXQmpgvCYOwEAYiulSpYtza2KiW7s5FgNJFSV2/CzfL7RwpoJ20iadmgA8owOyaxmusKE44scBeb4vIv00dqYgmf6jzWcr7W6INbDQeZbQL9ytXeYgtFfzmW1Fek5msxJlwhyt6qDDxOLQzpVPompYrMPnEnhvLm7M5BxY5nowAj3zkydAkpC0FIG6g7AK+Ub25ybyNWVYwtpseP2rfrQwiGRpfqrnMuPeuvr2dA0p2YsHF2XghkrXKtZ8tLBjR7S2qIaYbKmyLd/QP+EogLjqqwNw5Lq1pDlMLkM5+gNoSvdq+Pxmz9/61EFq6GYM6GqaGvlN95zy3gsmEWI8K3k8OP9OmRLEPO6DP3Wv3g42COinJTZ33dcIvs4ESp6opMTjDs6mcYTEbFeUifuxh989yZrIx4lkpuixxz0nHLCekKbE17suKhYkMGhoYhTZtVBvg4bfq/1L1Im0AGMVpBFwumM0zwyuKiCMi5dqR4Flx47AGyF2xTbxqUdTwCH94BT3DozpLV5WuAL/N8rGtHKjotBOOuOtCJ9E21uqsyBoLOzaXbHPrK5PQBP+fBfeidvJAeMIAmzVt5IkJJ9DBWaZDAepYUhlQqHt0h72SJ3j8TZHom64f516xx9T5evgMPgwG82jZdJaJIDyWp6LAjOCclVyzNA3iTKzIULlBQEPaTXlPHok5gISclmyaWZlqY2aTHdRHpJOwTdDEQ3ZfKtbpclcNhyVClagmY+fIfyKukntPqBgnx5QvZHk/D/MK8JMClrSigAAAABJRU5ErkJggg=="},4039:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyBAMAAADsEZWCAAAAD1BMVEX0/PTx+fH2/vbz+/P4//htSO9OAAAC5UlEQVQ4yyWT0QGjMAxDZTsDWKQDmJQBYrgBUsr+M517x0+LRWw9CyA+pC1YzndrMgHaNXVKQ+di13Of1qbur48nWhuRjj8i6ON8e7pNm7zyag/DBTfS9Z4Hup1fUuXMKY4HEE8QOHCByXkIkl7lDT239RtL9quO4JItmmhOAHXg45QuYKrQFLyGJcRvaTw6kQqZy6mkR6JAPFH/XqsQjEDRmUOA+MNLHGyMUT7AHApoAhjgjIJmCxy6XHdf648AWCdGe57IUDazCeTImQOY4/z+eVYVX2IjOw9RydeAeJwl79iGi4HpgQgHEchWraUZLtayu8scq0lHHHUKMY3Ml8hB7CS1jOckDLG9ccgNeX3124phOcjL9fPnWJhTXpLHeG9DRmHnTxHEaHakS2J51lwAJcUraNbuU7q4gMTDQj3Eripc/x+qFM5VEKAB1roQfAkX5/PxqnS2QpOrxfK1Zft0/omV5T+xCSBUAIbEIwUQgvAfxFE1O8dnk233+1UZiqJ1mAbsue6Yt8tF+yOrxC/YrUhzC4qPlE3EbR5hGKhhHdlrg7J9WunV7L7BcYQwAeE59u2tnN1c6gfVYrQiLSZ9OxZdWDXQq0+r0Pbarh3UqGCwauVvbiXuDsNxCtLDdW9rTF8oQYN4EoXXdfmwNguQP26n/tRjDeo+F2W7PjWtfSr6Bn/z+cXOLp4NnMV4RytvSW4B68m+XN9XfZTFGhO/S+cHTuTqZDC21ccA0N7QsePALaDQC3D1f94U9CWo+aq6BjB3v0rxIimBM12296M3aKPHjXLQE9KQKH4By8RHraJ3AgVto2r4xdFqlaPaiAHLl1ZF4P2pI6cYc+K8UZdcmxy7lqGc1IoPxLmIFuIeEZ6j2sQT88muEg1zwrEDTIX5U/ZmcsqfgVlBumiBLF4sAyhf9BFlXOPKLZ4H0iFb3VoHrGhtHTldKrOvP2/reu2zfV8CXMPqzRdlgd0a5eI7WwB/AYcgavcqxXWEAAAAAElFTkSuQmCC"},4400:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyBAMAAADsEZWCAAAAElBMVEUQERMODxESFBYWGBkaHB0eICLm6ozJAAACkUlEQVQ4yyWTUdLbMAiEASfvoOkBkBy/O5keIE0v8E/uf5h+68qZWALELgu2MG9PP9qyvCzTVhrrsPGOCjvTfXQZvtp/W3Gy6LCITqs4q/DZ+KYl76zKzHVYpY2wNY27nqN1sbLGcrLH3/ENH4oWlGctsDu8AO+HzTLlsYdh8MzP1m6YDMz0ACfcimvakBj+mwO/+5Uta5teOD379sxK1fUxmUhv8MU3jUT5gs26PMephFznkLcpQZ6/dPL9C/GWHcCxDN6oZhD5xBm5qoYBPA+PFE/H1tXDWcWl8TW7rS+4dUzAVy0BIrvC4/HcqW2TkG1HO8q9dC23INAg7NA4AFRFkDTM2lfELPyFzi1VddcpX2z0KjHBUDmdLNJ6dDps4ytrX+FPsZwE31wSL+6OWfHOAJ3+Y0Rk/MiKfmWNPg7oVP/U3Ck9FoCkC2gBpALOiqbMNTkOe8P4FWkTD2Y9Q3+5VmV0uLUJBl68U5uAK2Kl6QDXvLxbwweOL2sixW78uU8p0ysfc7cWrF1j6B1sPJ4WgclYSnJN1bzozrhEcFHmRzBkbJWqqdG+EYJXRFmT5jnLXPUNF6WBdoFbTxYsmDXVLU/WA7MExNc93sJS5hIXDeLxzMScHzdhKvEkibr6cQXYPrmtmTA7JcInISrTzRDvShTdka0uVGrsJAAR6tSn1sKziZtfKVjAxPrJsYgZO0bye+vKTZ/DgoAoLGNO6jYHimZYTL/3pLJHawquJukjBpfz8WOGVSVIWx9ywUfS5iENutidRM4NzkAmxgUSQ68xgNOU+ZLalr4TS2V+D2xqukZig+Z9DilR7Nouzwp1cp/3E5q6Rdlf08obKvAM4qZ6pMr+w3PmQALSSBfjyZn5DwrNRVbywBQiAAAAAElFTkSuQmCC"},"44e7":function(t,e,n){var o=n("861d"),i=n("c6b6"),a=n("b622"),s=a("match");t.exports=function(t){var e;return o(t)&&(void 0!==(e=t[s])?!!e:"RegExp"==i(t))}},"477e":function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAACVBMVEX28ef48+n69esoK7jYAAAB4UlEQVQozw2OsW4bQQxEhwLXkDrysGdEqRRgVShfQQq8wOr2jD0jSpXCLvwXbtKfADlFqgSwC/9ljqweZgYzQFnb/QGepYhA9jzmTc1WaSEtQpbFgjWATI00ZZtIckXx8q2Oe5yEByBy+RHOTcM+VVTadULsvxvRC/q8WTwgcWGD+Mnaqa0oy2gw2pKFzK+PzEsus5hP9AHojKslVynLlioVTBEN8cjDNnZoR1uMGTiZAAN47HxMtEkGUE9b8HWzkqNX5Lpk0yVziAJOs46rK1pG/xNuXLjz95fSDoJE5IqG23MAYPtWoeWPvfVtIV/Ng9oH3W0gGMPIOqd4MK4QZ55dV61gOb8Zxp7I9qayaGxp6Q91cmC0ZRdBwEQVHWzSAanlZwVWc9yljeTCeaHjBVvlPSLeyeBUT2rPdJegQI103jVS3uYkyIx1il6mslMDedZuOkwzolsagvPuQAfp7cYg7k9V1NOxfq64PNSvMdwONV4VYEmqlbpZy5OAakRKkjPnL4CBv5/OZRgoWHBmNbxB0LgB1I4vXFj93UoF2/0TPEsWwV9EhbIiTPqYoTHYoMn3enTDjmrFeDTIzaL1bUC/PBIMuF+vSSYSaxoVt90EO3Gu1zrMuMRGUk7Ffv3L+A931Gsb/yBoIgAAAABJRU5ErkJggg=="},"537b":function(t,e,n){"use strict";n.r(e);var o=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"chapter-wrapper",class:{night:t.isNight,day:!t.isNight},style:t.bodyTheme},[n("div",{staticClass:"tool-bar",style:t.leftBarTheme},[n("div",{staticClass:"tools"},[n("el-popover",{attrs:{placement:"right",width:t.cataWidth,trigger:"click","visible-arrow":!1,"popper-class":"pop-cata"},model:{value:t.popCataVisible,callback:function(e){t.popCataVisible=e},expression:"popCataVisible"}},[n("PopCata",{ref:"popCata",staticClass:"popup",on:{getContent:t.getContent}}),n("div",{staticClass:"tool-icon",class:{"no-point":t.noPoint},attrs:{slot:"reference"},slot:"reference"},[n("div",{staticClass:"iconfont"},[t._v("  ")]),n("div",{staticClass:"icon-text"},[t._v("目录")])])],1),n("el-popover",{attrs:{placement:"bottom-right",width:"470",trigger:"click","visible-arrow":!1,"popper-class":"pop-setting"},model:{value:t.readSettingsVisible,callback:function(e){t.readSettingsVisible=e},expression:"readSettingsVisible"}},[n("ReadSettings",{staticClass:"popup"}),n("div",{staticClass:"tool-icon",class:{"no-point":t.noPoint},attrs:{slot:"reference"},slot:"reference"},[n("div",{staticClass:"iconfont"},[t._v("  ")]),n("div",{staticClass:"icon-text"},[t._v("设置")])])],1),n("div",{staticClass:"tool-icon",on:{click:t.toShelf}},[n("div",{staticClass:"iconfont"},[t._v("  ")]),n("div",{staticClass:"icon-text"},[t._v("书架")])]),n("div",{staticClass:"tool-icon",class:{"no-point":t.noPoint},on:{click:t.toTop}},[n("div",{staticClass:"iconfont"},[t._v("  ")]),n("div",{staticClass:"icon-text"},[t._v("顶部")])]),n("div",{staticClass:"tool-icon",class:{"no-point":t.noPoint},on:{click:t.toBottom}},[n("div",{staticClass:"iconfont"},[t._v("  ")]),n("div",{staticClass:"icon-text"},[t._v("底部")])])],1)]),n("div",{staticClass:"read-bar",style:t.rightBarTheme},[n("div",{staticClass:"tools"},[n("div",{staticClass:"tool-icon",class:{"no-point":t.noPoint},on:{click:t.toLastChapter}},[n("div",{staticClass:"iconfont"},[t._v("  ")])]),n("div",{staticClass:"tool-icon",class:{"no-point":t.noPoint},on:{click:t.toNextChapter}},[n("div",{staticClass:"iconfont"},[t._v("  ")])])])]),n("div",{staticClass:"chapter-bar"}),n("div",{ref:"content",staticClass:"chapter",style:t.chapterTheme},[n("div",{staticClass:"content"},[n("div",{ref:"top",staticClass:"top-bar"}),t.show?n("div",{ref:"title",staticClass:"title"},[t._v(t._s(t.title))]):t._e(),n("Pcontent",{attrs:{carray:t.content}}),n("div",{ref:"bottom",staticClass:"bottom-bar"})],1)])])},i=[],a=(n("ac1f"),n("1276"),function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"cata-wrapper",style:t.popupTheme},[n("div",{staticClass:"title"},[t._v(" 目录 ")]),n("div",{ref:"cataData",staticClass:"data-wrapper",class:{night:t.isNight,day:!t.isNight}},[n("div",{staticClass:"cata"},t._l(t.catalog,(function(e,o){return n("div",{key:e.durChapterIndex,ref:"cata",refInFor:!0,staticClass:"log",class:{selected:t.isSelected(o)},on:{click:function(n){return t.gotoChapter(e)}}},[n("div",{staticClass:"log-text"},[t._v(" "+t._s(e.title)+" ")])])})),0)])])}),s=[];n("a4d3"),n("e01a"),n("d3b7"),n("d28b"),n("3ca3"),n("ddb0");function r(t){return r="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"===typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},r(t)}var c,l,u=function(t,e,n,o){return t/=o/2,t<1?n/2*t*t+e:(t--,-n/2*(t*(t-2)-1)+e)},A=function(){var t,e,n,o,i,a,s,c,l,A,f,g,d;function h(){var e=t.scrollTop||t.scrollY||t.pageYOffset;return e="undefined"===typeof e?0:e,e}function p(e){var o=e.getBoundingClientRect().top,i=t.getBoundingClientRect?t.getBoundingClientRect().top:0;return o-i+n}function m(e){t.scrollTo?t.scrollTo(0,e):t.scrollTop=e}function v(t){A||(A=t),f=t-A,g=a(f,n,c,l),m(g),f1&&void 0!==arguments[1]?arguments[1]:{};switch(l=f.duration||1e3,i=f.offset||0,d=f.callback,a=f.easing||u,s=f.a11y||!1,r(f.container)){case"object":t=f.container;break;case"string":t=document.querySelector(f.container);break;default:t=window}switch(n=h(),r(A)){case"number":e=void 0,s=!1,o=n+A;break;case"object":e=A,o=p(e);break;case"string":e=document.querySelector(A),o=p(e);break}switch(c=o-n+i,r(f.duration)){case"number":l=f.duration;break;case"function":l=f.duration(c);break}requestAnimationFrame(v)}return C},f=A(),g=f,d=n("7286"),h=n.n(d),p=n("477e"),m=n.n(p),v=n("e160"),b=n.n(v),C=n("df5e"),y=n.n(C),x=n("ec0f"),B=n.n(x),S=n("b671"),I=n.n(S),k=n("5629"),w=n.n(k),E=n("d0e3"),U=n.n(E),Q=n("4039"),D=n.n(Q),O=n("1e75"),V=n.n(O),R=n("1632"),P=n.n(R),F=n("7abd"),N=n.n(F),M=n("356c"),K=n.n(M),H=n("b165"),T=n.n(H),J=n("cf68"),z=n.n(J),L=n("4400"),W=n.n(L),q=n("802e"),G=n.n(q),Y=n("0827"),Z=n.n(Y),j={themes:[{body:"#ede7da url("+h.a+") repeat",content:"#ede7da url("+m.a+") repeat",popup:"#ede7da url("+b.a+") repeat"},{body:"#ede7da url("+y.a+") repeat",content:"#ede7da url("+B.a+") repeat",popup:"#ede7da url("+I.a+") repeat"},{body:"#ede7da url("+w.a+") repeat",content:"#ede7da url("+U.a+") repeat",popup:"#ede7da url("+D.a+") repeat"},{body:"#ede7da url("+V.a+") repeat",content:"#ede7da url("+P.a+") repeat",popup:"#ede7da url("+N.a+") repeat"},{body:"#ebcece repeat",content:"#f5e4e4 repeat",popup:"#faeceb repeat"},{body:"#ede7da url("+K.a+") repeat",content:"#ede7da url("+T.a+") repeat",popup:"#ede7da url("+z.a+") repeat"},{body:"#ede7da url("+W.a+") repeat",content:"#ede7da url("+G.a+") repeat",popup:"#ede7da url("+Z.a+") repeat"}],fonts:[{fontFamily:"Microsoft YaHei, PingFangSC-Regular, HelveticaNeue-Light, Helvetica Neue Light, sans-serif"},{fontFamily:"PingFangSC-Regular, -apple-system, Simsun"},{fontFamily:"Kaiti"}]},X=j,$=(n("05b3"),{name:"PopCata",data:function(){return{isNight:6==this.$store.state.config.theme,index:this.$store.state.readingBook.index}},computed:{catalog:function(){return this.$store.state.readingBook.catalog},popCataVisible:function(){return this.$store.state.popCataVisible},theme:function(){return this.$store.state.config.theme},popupTheme:function(){return{background:X.themes[this.theme].popup}}},mounted:function(){},watch:{theme:function(t){this.isNight=6==t},popCataVisible:function(){this.$nextTick((function(){var t=this.$store.state.readingBook.index,e=this.$refs.cataData;g(this.$refs.cata[t],{container:e,duration:0})}))}},methods:{isSelected:function(t){return t==this.$store.state.readingBook.index},gotoChapter:function(t){this.index=this.catalog.indexOf(t),this.$store.commit("setPopCataVisible",!1),this.$store.commit("setContentLoading",!0),this.$emit("getContent",this.index)}}}),_=$,tt=(n("9078"),n("2877")),et=Object(tt["a"])(_,a,s,!1,null,"22f8c37b",null),nt=et.exports,ot=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"settings-wrapper",class:{night:t.isNight,day:!t.isNight},style:t.popupTheme},[n("div",{staticClass:"settings-title"},[t._v("设置")]),n("div",{staticClass:"setting-list"},[n("ul",[n("li",{staticClass:"theme-list"},[n("i",[t._v("阅读主题")]),t._l(t.themeColors,(function(e,o){return n("span",{key:o,ref:"themes",refInFor:!0,staticClass:"theme-item",class:{selected:t.selectedTheme==o},style:e,on:{click:function(e){return t.setTheme(o)}}},[o<6?n("em",{staticClass:"iconfont"},[t._v("")]):n("em",{staticClass:"moon-icon"},[t._v(t._s(t.moonIcon))])])}))],2),n("li",{staticClass:"font-list"},[n("i",[t._v("正文字体")]),t._l(t.fonts,(function(e,o){return n("span",{key:o,staticClass:"font-item",class:{selected:t.selectedFont==o},on:{click:function(e){return t.setFont(o)}}},[t._v(t._s(e))])}))],2),n("li",{staticClass:"font-size"},[n("i",[t._v("字体大小")]),n("div",{staticClass:"resize"},[n("span",{staticClass:"less",on:{click:t.lessFontSize}},[n("em",{staticClass:"iconfont"},[t._v("")])]),n("b"),t._v(" "),n("span",{staticClass:"lang"},[t._v(t._s(t.fontSize))]),n("b"),n("span",{staticClass:"more",on:{click:t.moreFontSize}},[n("em",{staticClass:"iconfont"},[t._v("")])])])]),n("li",{staticClass:"read-width"},[n("i",[t._v("页面宽度")]),n("div",{staticClass:"resize"},[n("span",{staticClass:"less",on:{click:t.lessReadWidth}},[n("em",{staticClass:"iconfont"},[t._v("")])]),n("b"),t._v(" "),n("span",{staticClass:"lang"},[t._v(t._s(t.readWidth))]),n("b"),n("span",{staticClass:"more",on:{click:t.moreReadWidth}},[n("em",{staticClass:"iconfont"},[t._v("")])])])])])])])},it=[],at=(n("82da"),{name:"ReadSettings",data:function(){return{theme:0,isNight:6==this.$store.state.config.theme,moonIcon:"",themeColors:[{background:"rgba(250, 245, 235, 0.8)"},{background:"rgba(245, 234, 204, 0.8)"},{background:"rgba(230, 242, 230, 0.8)"},{background:"rgba(228, 241, 245, 0.8)"},{background:"rgba(245, 228, 228, 0.8)"},{background:"rgba(224, 224, 224, 0.8)"},{background:"rgba(0, 0, 0, 0.5)"}],moonIconStyle:{display:"inline",color:"rgba(255,255,255,0.2)"},fonts:["雅黑","宋体","楷书"]}},mounted:function(){var t=this.$store.state.config;this.theme=t.theme,6==this.theme?this.moonIcon="":this.moonIcon=""},computed:{config:function(){return this.$store.state.config},popupTheme:function(){return{background:X.themes[this.config.theme].popup}},selectedTheme:function(){return this.$store.state.config.theme},selectedFont:function(){return this.$store.state.config.font},fontSize:function(){return this.$store.state.config.fontSize},readWidth:function(){return this.$store.state.config.readWidth}},methods:{setTheme:function(t){6==t?(this.isNight=!0,this.moonIcon="",this.moonIconStyle.color="#ed4259"):(this.isNight=!1,this.moonIcon="",this.moonIconStyle.color="rgba(255,255,255,0.2)");var e=this.config;e.theme=t,this.$store.commit("setConfig",e)},setFont:function(t){var e=this.config;e.font=t,this.$store.commit("setConfig",e)},moreFontSize:function(){var t=this.config;t.fontSize<48&&(t.fontSize+=2),this.$store.commit("setConfig",t)},lessFontSize:function(){var t=this.config;t.fontSize>12&&(t.fontSize-=2),this.$store.commit("setConfig",t)},moreReadWidth:function(){var t=this.config;t.readWidth<960&&(t.readWidth+=160),this.$store.commit("setConfig",t)},lessReadWidth:function(){var t=this.config;t.readWidth>640&&(t.readWidth-=160),this.$store.commit("setConfig",t)}}}),st=at,rt=(n("75ab"),Object(tt["a"])(st,ot,it,!1,null,"36dafd56",null)),ct=rt.exports,lt=(n("d81d"),{name:"pcontent",data:function(){return{}},props:["carray"],render:function(){var t=arguments[0],e=this.fontFamily,n=this.fontSize,o=e;return o.fontSize=n,this.show?t("div",[this.carray.map((function(e){return t("p",{style:o,domProps:{innerHTML:e}})}))]):t("div")},computed:{show:function(){return this.$store.state.showContent},fontFamily:function(){return X.fonts[this.$store.state.config.font]},fontSize:function(){return this.$store.state.config.fontSize+"px"}},watch:{fontSize:function(){var t=this;t.$store.commit("setShowContent",!1),this.$nextTick((function(){t.$store.commit("setShowContent",!0)}))}}}),ut=lt,At=(n("ca36"),Object(tt["a"])(ut,c,l,!1,null,"7b03cca0",null)),ft=At.exports,gt=n("bc3a"),dt=n.n(gt),ht={components:{PopCata:nt,Pcontent:ft,ReadSettings:ct},created:function(){var t=JSON.parse(localStorage.getItem("config"));null!=t&&this.$store.commit("setConfig",t)},beforeCreate:function(){var t=JSON.parse(localStorage.getItem("config"));null!=t&&this.$store.commit("setConfig",t)},mounted:function(){var t=this;this.loading=this.$loading({target:this.$refs.content,lock:!0,text:"正在获取内容",spinner:"el-icon-loading",background:"rgba(0,0,0,0)"});var e=this,n=sessionStorage.getItem("bookUrl"),o=sessionStorage.getItem("bookName"),i=sessionStorage.getItem("chapterIndex")||0,a=JSON.parse(localStorage.getItem(n));(null==a||i>0)&&(a={bookName:o,bookUrl:n,index:i},localStorage.setItem(n,JSON.stringify(a))),this.func_keyup=function(t){switch(t.key){case"ArrowLeft":t.stopPropagation(),t.preventDefault(),e.toLastChapter();break;case"ArrowRight":t.stopPropagation(),t.preventDefault(),e.toNextChapter();break;case"ArrowUp":t.stopPropagation(),t.preventDefault(),0===document.documentElement.scrollTop?e.$message.warning("已到达页面顶部"):g(0-document.documentElement.clientHeight+100);break;case"ArrowDown":t.stopPropagation(),t.preventDefault(),document.documentElement.clientHeight+document.documentElement.scrollTop===document.documentElement.scrollHeight?e.$message.warning("已到达页面底部"):g(document.documentElement.clientHeight-100);break}},this.getCatalog(n).then((function(n){var o=n.data.data;a.catalog=o,e.$store.commit("setReadingBook",a);var i=e.$store.state.readingBook.index||0;t.getContent(i),window.addEventListener("keyup",t.func_keyup)}),(function(t){throw e.loading.close(),e.$message.error("获取书籍目录失败"),t}))},destroyed:function(){window.removeEventListener("keyup",this.func_keyup)},watch:{chapterName:function(t){this.title=t},content:function(){this.$store.commit("setContentLoading",!1)},theme:function(t){this.isNight=6==t},bodyColor:function(t){this.bodyTheme.background=t},chapterColor:function(t){this.chapterTheme.background=t},readWidth:function(t){this.chapterTheme.width=t;var e=-((parseInt(t)+130)/2+68)+"px",n=-((parseInt(t)+130)/2+52)+"px";this.leftBarTheme.marginLeft=e,this.rightBarTheme.marginRight=n},popupColor:function(t){this.leftBarTheme.background=t,this.rightBarTheme.background=t},readSettingsVisible:function(t){if(!t){var e=JSON.stringify(this.$store.state.config);localStorage.setItem("config",e)}}},data:function(){return{title:"",content:[],noPoint:!0,isNight:6==this.$store.state.config.theme,bodyTheme:{background:X.themes[this.$store.state.config.theme].body},chapterTheme:{background:X.themes[this.$store.state.config.theme].content,width:this.$store.state.config.readWidth-130+"px"},leftBarTheme:{background:X.themes[this.$store.state.config.theme].popup,marginLeft:-(this.$store.state.config.readWidth/2+68)+"px"},rightBarTheme:{background:X.themes[this.$store.state.config.theme].popup,marginRight:-(this.$store.state.config.readWidth/2+52)+"px"}}},computed:{catalog:function(){return this.$store.state.catalog},windowHeight:function(){return window.innerHeight},contentHeight:function(){return this.$refs.content.offsetHeight},popCataVisible:{get:function(){return this.$store.state.popCataVisible},set:function(t){this.$store.commit("setPopCataVisible",t)}},readSettingsVisible:{get:function(){return this.$store.state.readSettingsVisible},set:function(t){this.$store.commit("setReadSettingsVisible",t)}},config:function(){return this.$store.state.config},theme:function(){return this.config.theme},bodyColor:function(){return X.themes[this.config.theme].body},chapterColor:function(){return X.themes[this.config.theme].content},popupColor:function(){return X.themes[this.config.theme].popup},readWidth:function(){return this.$store.state.config.readWidth-130+"px"},cataWidth:function(){return this.$store.state.config.readWidth-33},show:function(){return this.$store.state.showContent}},methods:{getCatalog:function(t){return dt.a.get("/getChapterList?url="+encodeURIComponent(t))},getContent:function(t){var e=this;this.$store.commit("setShowContent",!1),this.loading.visible||(this.loading=this.$loading({target:this.$refs.content,lock:!0,text:"正在获取内容",spinner:"el-icon-loading",background:"rgba(0,0,0,0)"}));var n=sessionStorage.getItem("bookUrl"),o=JSON.parse(localStorage.getItem(n));o.index=t,localStorage.setItem(n,JSON.stringify(o)),this.$store.state.readingBook.index=t;var i=this.$store.state.readingBook.catalog[t].title,a=this.$store.state.readingBook.catalog[t].index;this.title=i,g(this.$refs.top,{duration:0});var s=this;dt.a.get("/getBookContent?url="+encodeURIComponent(n)+"&index="+a).then((function(t){var n=t.data.data;s.content=n.split(/\n+/),e.$store.commit("setContentLoading",!0),s.loading.close(),s.noPoint=!1,s.$store.commit("setShowContent",!0)}),(function(t){throw s.$message.error("获取章节内容失败"),s.content="  获取章节内容失败!",t}))},toTop:function(){g(this.$refs.top)},toBottom:function(){g(this.$refs.bottom)},toNextChapter:function(){this.$store.commit("setContentLoading",!0);var t=this.$store.state.readingBook.index;t++,"undefined"!==typeof this.$store.state.readingBook.catalog[t]?(this.$message.info("下一章"),this.getContent(t)):this.$message.error("本章是最后一章")},toLastChapter:function(){this.$store.commit("setContentLoading",!0);var t=this.$store.state.readingBook.index;t--,"undefined"!==typeof this.$store.state.readingBook.catalog[t]?(this.$message.info("上一章"),this.getContent(t)):this.$message.error("本章是第一章")},toShelf:function(){this.$router.push("/")}}},pt=ht,mt=(n("ace6"),Object(tt["a"])(pt,o,i,!1,null,"0405dcaf",null));e["default"]=mt.exports},5629:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAADFBMVEXN383Q4tDP4c/R5NEInCCXAAACVElEQVQozw3Hv2sTYRwH4M/79pJ7bZL2bXqtERJ97zjUpbZDhg6pfC8qibi8hLR0EaJ0EFxaCSWDxjfpj1zrYBcRBKE6SAfBJWsx9i8IQfdQxDlKtA6t2OnhQfN3lbG7ytYRywF8rVoPCNO0X2sQOKDpAnSDK2VwkHgmh5yLGT8qASt+2KofnNt2Xg1gf1UF8AoM6052cRMNaloLZb7RKQGrKKji2OefsZF+VqIvos5ZLVIZCX61JcwUdk56wASVkgQvzPfvmT2twTSwyYaC/Pl/UhAHorFhBgZtL6XdAZRp1tkPwC1NLa9CWs5prLhI85NBQsLdXvjDymG3/EbYfQhVNYqc3TtktQhWLY3ko0QsdMbSEp+64v0NfxyqLbIGdh6M2xHHlLBGqKTyQo4E/nebBgBfe1GpdeywYXc8CT7D3cKXuMXkBy4xN6o5OuKamYp3DVI6uccO9lxgd2CAlJgI2BGgaAgIJV/TYwKqu3WFccjbMuA+bVkWgS2bfnlRbD1Eb1sDyWMmjKYIBgGAWbqKRicfvzBkBIz3V5AKnguWdglQEysQsSuVzOg6ALy1pitA5ykGCsc857BRYcgCSZyFOdvoOigSGoPc5Ta73mgxshIcQE5sHMHd9D7yqITw7JO+GHVMxjhzYLcKPSEgmz3fU+BRy3iYNtiXLaBssCW8KguReqkQOTb3MStV0Ugt4U1eIs1RZWRII6Ww8xeNNItyGGQI4ZMlpg/3lQtkl2JFnBp1imRyFe0kK2Id3PCslMgiQNMS77gvFeDhG3cSkYvheeg/e7ClIh5oh+IAAAAASUVORK5CYII="},"58c8":function(t,e,n){},"5ca7":function(t,e,n){"use strict";n("a7bd")},"63f0":function(t,e,n){},"65f0":function(t,e,n){var o=n("861d"),i=n("e8b5"),a=n("b622"),s=a("species");t.exports=function(t,e){var n;return i(t)&&(n=t.constructor,"function"!=typeof n||n!==Array&&!i(n.prototype)?o(n)&&(n=n[s],null===n&&(n=void 0)):n=void 0),new(void 0===n?Array:n)(0===e?0:e)}},7286:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAADFBMVEXr5djn4dTp49bt59rT6LKxAAACnElEQVQozw3NUUwScRzA8d8R6MF8YMIx8uk47hDSJbj14IPzOGc7jPLvwTGg5uAYDbe2tt56cLtznvEnS6yDqCcEaWi91DvrbLJZz7b1aFtz1aO+2OZWvn+/+4CHeB6BMYaqBLfjPNRY6RFT2JJYby+uAk4WUTrtlmJ4hgPYb2q1XGDQjaK8pgJHvqNaAX+KyuIkDXpgQinb46nOulnn4b5laUHTxLfseeArAoNOeJlOIjdoal0n1FA7tKFv5roK+YaHOqP3P0XyKHPHY+MhTRe5uCZnKhtJKw2eSrSoBDPLtpZuNcFNJcFyiCMxOaaHIfXz1e8HQbWLySrBQ4x0x1qlhnHlnz2HQEC6TNb0gTHXa7IKhcaHqkE015hk9whA0YeWiLIXf7Fa2CZo3DjqjB4tTuF8jIcbfcEx5z/w4sXpQhXW+ju0cqh7icTFmRMaG+v6CIvTjcSpHcH8JEsF3EPh3fRthYdVLLgI2fWXm85/pGFE4l046s70L+yKCcirGFR+jbpy3kMmiCGHrSezVONsn1RBixncyk2PcVWk7DlgxHo8iZwDyq5uAUD854dZhdIFYzKoQig2haUKi1lVufz2RZUZPZ41n/hrOQB6h0Hhg8I367FNoEHgeM/KY7szSeQwD8q2WE3HM35ZLl0K1MJiOtHIkBclRQUwZnyOWcNsRQQgVLj1PSqkjF9DsoOSaSg3iinKzvfmgsNFFfpP/2T3GLGvL4fHEfwIX1sVvXcPqLztehWGcfn9nI2U9nTfCgJPe/jFPLZwgVEzimBgAm0VIyK2tt1cE/AzQdLK+SxLSQ4aDCZnnId94OG2S1XwvnTbNk/ZnhyRCQT+sZM6z9g6LXL1BOBe+zJySiFkHAINCtnQokbCJ/apCv0foqPiZVfhpywAAAAASUVORK5CYII="},"746f":function(t,e,n){var o=n("428f"),i=n("5135"),a=n("e538"),s=n("9bf2").f;t.exports=function(t){var e=o.Symbol||(o.Symbol={});i(e,t)||s(e,t,{value:a.f(t)})}},"75ab":function(t,e,n){"use strict";n("63f0")},"7abd":function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAADFBMVEXm9PXq+Pno9vfs+vttWKBGAAACPElEQVQozw3RQWrbQACF4TfCMjPqZgIj4RRaxsZKE0PuMBZ2cLKaCI9RDAXFmJJknUWWI1O1UlamOMHJSjGkuFn3AD2Cr9CepDrAg+/xIxK4QwIqHHQkUhQ/WuphInVIFBojl8QXc012Tgq4RTtVHWVLZVFh1tEoI91uiN4joCqde8Ukn/zGM1B2W4ari2PtTwyw55Ld+Wways54qhGPyS6FzbIT3lIY8WwWdCq56Yolx6KmSKzoqrsCB5heAp4TGNQWJ1Pc6XlE5jQD5OlIX9I47A9uiUQcPQxcury/ToyxWJG/za6ki88crxKPocKS59Sl3EtBG7C89fCGflpfqoSzCeC4crioJA7F0V5+8MaSIk4qSCdwzpogmbqzEirVpGiS2dOVJvUuuqFEmhHao06KEpq+8lvHI14NJk3Qrmi9vBuRLwAz0qZB4hsDXQFXgtnlpDX3C6ug9BquSw/CYtwAzuTz5vuQNdr/YibhR68378ehZH30FSpjh71LpQkrsj+Q062h5WwZ5wlRoD6uQJy1DqvSYuCUapMBqT5YA4ZFw4KlWapxoUGlKWrx0eDQvmigu4WMYt97ruru98fYL8/0lG6CTOFcFWBhFK5gKw19h2JN808nh7xhkU6sWKLXdtkqBL6h+lULK5k19wFB/FldnGYf3LDeuf6IC2/MzJOSOP0qPxLqzaGIqtBcFIItrstkazONOkrc1D1czjuwEGESB4JJnjgSMN7PXAu7fZQpl1C236C+9mM4Af8P98Ch4R2TRl8AAAAASUVORK5CYII="},"802e":function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAADFBMVEUWGBkYGhsdHyAfISI1t/v6AAAB5ElEQVQozxXQsYoTURSA4f/EeycZsDgDdySDjihk38Hy3GWi2J2BCaziQhaiaB+tt9AFu1kwvYUPsIXNPoB9BAUfwAfwEUzKv/v4odGrroyp9/rUaC6rZ5skv5F8qPsfYYP+yKUMymmAEEeW55oUR4o8jr05KNzJ07yvB7w0KKfLwcQUSjfmMU0PJfPHFoEVU+ohNrcKMEzMQ23FDnVSI2dqtYWI7KlLu6vE4UnyvKc3SJuL7lBbeEEl42ItpGLjzIT8PRJCmkRjVpVpsbJFVN0687okJNZiHAr5Z7MV0BnGIDc+THM1zlbieBc1Fq+tH5BH+OpnbWkj40hSqC8Lw2TvFuF0SUFJCk2IytXbjeqcRAt6NHpnrUkUU4KRzZs8RCK8N/Akn2W04LwxMU/V7XK0bDyN2RxfDyx7I4h5vjZby72V8UnOWumZL3qtYc+8DTE0siSBMXGhywx2dMYPnQHbxdFZ7deiNGxCCtD/QWnbwDoGhRYPDzUdUA3krjpnkvdAgDN4ddLkEQSov9qjd42HaDjI34gEqS9TUueAk+sc4qg5ws407KQYKs8G1jv4xBlqBVk6cb4dISZIwVi1Jzu4+HLk6lyfUxkXvwy+1Q+4WVdHIhwfybZ6CWVhxMEhShOgsP/HOW0MvZJeFwAAAABJRU5ErkJggg=="},"82da":function(t,e,n){},"8aa5":function(t,e,n){"use strict";var o=n("6547").charAt;t.exports=function(t,e,n){return e+(n?o(t,e).length:1)}},9078:function(t,e,n){"use strict";n("c0bc")},9263:function(t,e,n){"use strict";var o=n("ad6d"),i=n("9f7f"),a=n("5692"),s=n("7c73"),r=n("69f3").get,c=n("fce3"),l=n("107c"),u=RegExp.prototype.exec,A=a("native-string-replace",String.prototype.replace),f=u,g=function(){var t=/a/,e=/b*/g;return u.call(t,"a"),u.call(e,"a"),0!==t.lastIndex||0!==e.lastIndex}(),d=i.UNSUPPORTED_Y||i.BROKEN_CARET,h=void 0!==/()??/.exec("")[1],p=g||h||d||c||l;p&&(f=function(t){var e,n,i,a,c,l,p,m=this,v=r(m),b=v.raw;if(b)return b.lastIndex=m.lastIndex,e=f.call(b,t),m.lastIndex=b.lastIndex,e;var C=v.groups,y=d&&m.sticky,x=o.call(m),B=m.source,S=0,I=t;if(y&&(x=x.replace("y",""),-1===x.indexOf("g")&&(x+="g"),I=String(t).slice(m.lastIndex),m.lastIndex>0&&(!m.multiline||m.multiline&&"\n"!==t[m.lastIndex-1])&&(B="(?: "+B+")",I=" "+I,S++),n=new RegExp("^(?:"+B+")",x)),h&&(n=new RegExp("^"+B+"$(?!\\s)",x)),g&&(i=m.lastIndex),a=u.call(y?n:m,I),y?a?(a.input=a.input.slice(S),a[0]=a[0].slice(S),a.index=m.lastIndex,m.lastIndex+=a[0].length):m.lastIndex=0:g&&a&&(m.lastIndex=m.global?a.index+a[0].length:i),h&&a&&a.length>1&&A.call(a[0],n,(function(){for(c=1;ca)i.push(arguments[a++]);if(o=e,(g(e)||void 0!==t)&&!rt(t))return f(e)||(e=function(t,e){if("function"==typeof o&&(e=o.call(this,t,e)),!rt(e))return e}),i[1]=e,G.apply(null,i)}})}q[T][J]||w(q[T],J,q[T].valueOf),F(q,H),D[K]=!0},a7bd:function(t,e,n){},ac1f:function(t,e,n){"use strict";var o=n("23e7"),i=n("9263");o({target:"RegExp",proto:!0,forced:/./.exec!==i},{exec:i})},ace6:function(t,e,n){"use strict";n("fc0c")},b165:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAADFBMVEXe3t7a2trc3Nzg4OCXP9lCAAACoklEQVQozwXBzU/TYBwA4N+QEr4CNbSFwcFuowSqMRvEAwShHWAYNsu7dS0dLnGUSWT4kZB4lGzE4VtcwgIDJqcOWLJxcv4BOoQZuCPxSNSD4WSWLJGL8XmAIiyo2RgJ4A1pxQQlOxRAszLTdnPu2oQGb05RC5slJld7ZAIfo4O44Bn1ud59F0BcjnYOa17Jhwc6EdiKettncsXjT1f8KUBZUW41pK0Jc1Az4dEV3rkkPBtDSZ83Blyt0kSf2PRjzIykoBwINisPbPPtljdVE9iAXRfUPkXLVIgYrCccp5g687NdZbcJ+xa5VE/HhTtT23IKsN5jj/pcUd0dTZNAqCVw72n4gOwnTOC0vvHfaauT8d9zAoRRfPpISZRVyUiw8ELzOG1b2DZpFzkSrHLhq52twDEdyZHwvp2j4uv/bjvOf23/AcEtTuJbY5Cp4YcAer1IGkUzOo2rn8LQOKjFJw3NTw24nprQXY5aF4wxcqcSdbFQ00H4xFl8Drx4X4CikvAM1tuR8bKIBCBoLnKN10KJG4zKAsc7c9WEB9gnCi6BhVjqoco6t20ILAJuVctvaEZK732cRHDRmGfuihOam0o2CHByUZ/epCcVlRs2wmCnMqsd6aSim3ibBJtm1LGyXW3Bb7tJCPlFtUG+SvPdeEUAB60lNdo+VQbLcwRNVtT68FsLcr1+NotgNihlpExS1V2SFgNbeC8bEhgm8sM17wSi6Us2gxVWJU/5GKBpandvfyYbU1yHCLpCgWGbbPXn40rehEsUXKIJr9DMKgICfjc4bl1YfvUhE/YIECGRqjCxSM9hrybAIkND5OeWfFZsXkxB+qDzb7pUQ3EfQ3Ml6EChEt3D+iS01VqC7EQ/Z/DuPQcz4yChoFQJce2Qr+NNAv0HxofmpXGqgHkAAAAASUVORK5CYII="},b671:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyBAMAAADsEZWCAAAAD1BMVEX48dr48Nf58tv379X17NJtIBxUAAACFUlEQVQ4y1XRUZakMAgF0Af2AiDWApDZgHZqAV1nZv9rGh7Rj7Y8McUFEg1wvcMESMNVD/neU8Xcaz7nYYkYlYO6Ti82PBI4BvIEg1aj3wKwRvIMgZsUy5LdhCawPFh1sZs4SrlyN9fQKpv8s5dgZ2eLyqqJiu+WkCmUEybXkm3INS01WAiv0PapJ0CZc0SJQUzcWnZYbOOY20iFD8Bk+/j2A3wNxH7GdShFYS5ff237kXh9I9zSkQmIAhOsOSVfJ6DIXTMDaPnzkRJ92S1BQQmXl5LdirgRLLDdcYqcGPwe3QN4xCBiGNbrqq9wpW1XCecChwaQdVOsRDpPCpeoolPdxeXp3WNB9PHVzWBHlygy4NJCCrFHREv6bDt0VGwJZASkpONmm1UseGeFKAQexgaAkrfYWl3AGxWOLL2AIMBNbCXpktmS3k3vHeYjGCPBa43wJTurO3ZFVpQSJdAZGLoHTyk1upkjxMEaIxum3iIARcCa5kSkFAW5fi1mUlL9eyOsaanFmOMruwvEdE3ZYzsRSzo5ewRLXyVPPEvknt8ij4DvCg2O7xOgBCUprEzV4z1WekSpUgI8DT2mrnSOXKRfQavwuKA1F+tFnMKdJSUpMA7wQAifWRkMgjUKKZE4lBl6MCM4B1pq1P4uIjDE6Pq6rL0FnW1nIFmta5vrSvq/Ch4tpqG/ZNyyWa5jZPktq81eYv8Bt5s4iFITOp4AAAAASUVORK5CYII="},b727:function(t,e,n){var o=n("0366"),i=n("44ad"),a=n("7b0b"),s=n("50c4"),r=n("65f0"),c=[].push,l=function(t){var e=1==t,n=2==t,l=3==t,u=4==t,A=6==t,f=7==t,g=5==t||A;return function(d,h,p,m){for(var v,b,C=a(d),y=i(C),x=o(h,p,3),B=s(y.length),S=0,I=m||r,k=e?I(d,B):n||f?I(d,0):void 0;B>S;S++)if((g||S in y)&&(v=y[S],b=x(v,S,C),t))if(e)k[S]=b;else if(b)switch(t){case 3:return!0;case 5:return v;case 6:return S;case 2:c.call(k,v)}else switch(t){case 4:return!1;case 7:c.call(k,v)}return A?-1:l||u?u:k}};t.exports={forEach:l(0),map:l(1),filter:l(2),some:l(3),every:l(4),find:l(5),findIndex:l(6),filterOut:l(7)}},c0bc:function(t,e,n){},c84b:function(t,e,n){"use strict";n.r(e);var o=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"detail-wrapper"},[n("div",{staticClass:"detail"},[n("div",{staticClass:"bar"},[n("el-breadcrumb",{attrs:{separator:"/"}},[n("el-breadcrumb-item",{staticClass:"index",attrs:{to:{path:"/"}}},[t._v("书架")]),n("el-breadcrumb-item",{staticClass:"sub"},[t._v("目录")])],1)],1),n("el-divider"),n("div",{staticClass:"catalog"},t._l(this.$store.state.catalog,(function(e){return n("div",{key:e.index,staticClass:"note",on:{click:function(n){return t.toChapter(e.url,e.title,e.index)}}},[t._v(" "+t._s(e.title)+" ")])})),0)],1)])},i=[],a=n("bc3a"),s=n.n(a),r={data:function(){return{key:"value"}},mounted:function(){var t=this;s.a.get("/getChapterList?url="+encodeURIComponent(sessionStorage.getItem("bookUrl"))).then((function(e){t.$store.commit("setCatalog",e.data.data),sessionStorage.setItem("catalog",JSON.stringify(e.data.data))}),(function(t){throw t}))},methods:{toChapter:function(t,e,n){sessionStorage.setItem("chapterID",n),this.$router.push({path:"/chapter"})}}},c=r,l=(n("5ca7"),n("2877")),u=Object(l["a"])(c,o,i,!1,null,"57115a66",null);e["default"]=u.exports},ca36:function(t,e,n){"use strict";n("58c8")},cf68:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyBAMAAADsEZWCAAAAD1BMVEXm5ubo6Ojp6enr6+vt7e1FnZagAAACrklEQVQ4yx1SixUbMQgT3AKAFwDcAfzpBN1/qMrJS5w7bCQhC6IGSUGYQJd6Ox9ZPXi1AGJBavhUTT0JjYPGAab9WcDYIxsmlnxkayX8mhxCmKHA75az5cfRbWybEExiu08xDSgGym0mwuf3j4SvHeQxDJJzh2zp4iOlrD8iOb4SXyC1wiOLRTcnrje+nGamFeXVKWkmzbFIPChkmJ6Fg7mBpV8n+JGOVCd4jv1thThkjeQGNeafpeV3rsEWLfyWc8tC9jOv6FQ8rRzHOOVB+jCYEUAJpDvh8xHNFm/Tm5p5lw94Pp3NhtKEfQsGvnXhowdZE73hPwxKvjDd4i4PCdd0fe3W5fO8ktAsUAacLgstpUw60JCiPLg2XpkgiqPIYYXJd9ksGIT3q+LlevypzItvO+s0F1dBzVr2QDMUkYmuyGcrIS44mVJ7JVKwQXjYuBYp0Uetecbswzsikzu3gUR8bJC/C8Gd/NAzI/xdUGOYQQHDZ8X2d5XuzGRUiXAi9si5CRgoiToRZPtzLJkd0FUHRHZwJf0BHT1sE7gcnh0jmKKlSSF4/GBirGk5+K9NKlGDCfc9JtPhg78JdabH0YQRKNZnJ8tFnPfXHJb4xum1TTCeEmyEdbyEJLjznMLHuFD2Y9NEkSleIBs7SiCbblhgctVi9ch++kDYnn1C9DA5TvdPsToXM55wI6k+8eKT1blwPTqWb5CFJ+7dTBmab+KHy+xwNtItXhZNSpHD2fxnynrxG3ZBKRe8KBpXk11AnadlccEhr9w1nBBvBylNkv7A8eqpGBCDqhitmWQXBjjdS6idr/QjXWLDeMzMbVDoJuM8zN7WenMZWXgZ2vX3F01J3jHZbwk1LRP+DWEvDJtOUoh/AIaBUz5VpWyhuyx4QtgL/NmgC6kM/JvNe+R/C/5aL7BKIbYAAAAASUVORK5CYII="},d0e3:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAADFBMVEXh7eHl8eXj7+Pn8+eTbH1KAAACPElEQVQozxWPQWrbQABF/0xn3JFKQRTZOIuUsbCCbOgdRoYEOauxkYPcTRyTlPQWIxEltrsRwQ6hK9nEQek6F+gNTE/Q3qLLusv34cN7SH3mFicdYW4gNIhJWXPBRVXzjcFD0IqeU4o4PRbAIVjyico0vJpIifqPfL80QN9DAQY5ucRHE/hpHxBldXe9GilaHKcKMlj6pho2zXgkNdBl0oJ8kiF1DSiJF1ZHBJkQr0Dbux/5I42Zp4cFahJDFGeW6/QjBwmFY/Q7vZ2SnoOdW2parv/Cnm81+m0xrEfiVXQ3W4nOXIqVYi3l6AAQBwMFkViVBANMto4enXHPNTkHBB0oVj4r5vHzCWayrgBvxtygDlDB2CNDjd80ZInY69aKVYZcfJ8DW+fWuc+syEODALx+ojqoafHsthTI+ZW27PGpIeo/cR6YKcbqIuIFhHmBrzAovzIOOJk1ucvcDzrMRYGVBH2yvcAOf0KiKwfRovBI3tm/kW1eemtfNWwIIXE2mJNhvoszfmMBfRCv0OPwd2321uDW3nx2q/BDxFVeoN1g7a6Im8yRnoawa8kbdXnU0cHeTMxKfZGlJgvLb3sKsxgglQnDdAfvj9LUnqWRDo0GiUmPwyU7TAsD7wHeIW3Nfy1qVGKoE9NgJCdYCAexNRob9yCn4DAQmXtQuUtera6bEmTTXhZy6h856xi4mnEl6BI9mfISkLbtJyZIMJIAUd5ZOBEu88KRAk71yxfItj/hpIB0Errv4gO1os4/UICf+o3kkqwAAAAASUVORK5CYII="},d28b:function(t,e,n){var o=n("746f");o("iterator")},d784:function(t,e,n){"use strict";n("ac1f");var o=n("6eeb"),i=n("9263"),a=n("d039"),s=n("b622"),r=n("9112"),c=s("species"),l=RegExp.prototype;t.exports=function(t,e,n,u){var A=s(t),f=!a((function(){var e={};return e[A]=function(){return 7},7!=""[t](e)})),g=f&&!a((function(){var e=!1,n=/a/;return"split"===t&&(n={},n.constructor={},n.constructor[c]=function(){return n},n.flags="",n[A]=/./[A]),n.exec=function(){return e=!0,null},n[A](""),!e}));if(!f||!g||n){var d=/./[A],h=e(A,""[t],(function(t,e,n,o,a){var s=e.exec;return s===i||s===l.exec?f&&!a?{done:!0,value:d.call(e,n,o)}:{done:!0,value:t.call(n,e,o)}:{done:!1}}));o(String.prototype,t,h[0]),o(l,A,h[1])}u&&r(l[A],"sham",!0)}},d81d:function(t,e,n){"use strict";var o=n("23e7"),i=n("b727").map,a=n("1dde"),s=a("map");o({target:"Array",proto:!0,forced:!s},{map:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0)}})},df5e:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAATlBMVEXdzaHh0KPgz6LdzKDezqLczJ7ezZ/fz6Dcy5zi0aXdzZ3fz6Tfz57h0KDg0aLcyZrg0KXi0qPfzZ3j06bh0qbdyJbfzJrhz5/cxpLZwo0vDconAAAFn0lEQVRIxxyPW5LjMAwDAT5FybLl2JnM3P+i6+wXWVC1GoQGaD0h4XM3Q5o4T0HgABHBi6pZ4CDXXcUOFd6VhqC3Kch4EI8w9oMXwvU6m5LOOvcxKMOhuu8i5+5cMjcgb0t4F2uvOoeI3/MlT4IqsbtM9UG2AGSXUOsxzPevnXzK1CSHytZLvx7VdQmUcJsJCxJh2nmHW12Qod1qPjt8pih47uQ9aGpoNWF+yElCt60oH7vdIU/MnlRPSBLC/VwqxcKR8PFqnADN9ih5ufqnTlG9KwCofvs7kKYqOPHTNMQ93j9qNImFw9vjHPZ0F1m8hUUVB/Q/TrRYDMXr9++APMFARAt6sPh6wVAXzxUGhZsFUwCNfPZ8/72TAHebAhvuOuT3gO1Vn5d9Jd5sBRkg0p2seL9B7ulkjFJFIt9HPpLzdSzzMP3UcodAfMqC6pBuET2heHK1itZf1GZ1bi0BwOSxiCS8f/JBHMPMM4XCu3Mt1uz9lJbDJRqsKDZuikzkvskQEz6hanfDfO494azY5JpqPqOF1RhxD9XYEdaNxiqWqakKgmPfmrsta8KAiwF4HBxGVUJAgeSqQaiRRZJ7D2jedhw5t1CIAKxag0CBA60BpoBE6DcUi8O5AuM4pLfN0kHLmeu2B4e6HofqbgxsTWUw3PAODqa1oDtyzgXBlusi1KFdclMPE8O3jvLJ8RNi5/RxDQVzVmXA233XQ4KummunfxvLOZo+iH37964YjP06995CTdu9hsvErqJNzmf4wTrZ5DL7+qW9EoLnadrx67b8dUtrJnBXaT1N1uvPaYRKpWkq52xNsMN7vv4Sdryt/f4MhQoMCKnvVxikai1CQ6ZsnwJDc8+3Y/z8HcfvYQNq66pnAu1Hwa+3KNSwbNu8h3nDPqTl9fl7tx8fBhFfdS0o0F3JUKEZtZG9b/LZEM95lzaR30OnWPzroMxyZYdBIMoMnpN0J+m7/40+/P4soFSUjgzE7yY5zrMJuoZv0CmpVguYx1pprfb5HOviRVhHUVi/352shxCYrYBZxGtVaxiAz/MsaGSIsB7R1t4zJXH//n7RTTQQwxqcGEqEvklFHUgiO2GvJV+jAIPR+N29usWDoiSOVrN3XuqT1egQJAAU9EwslVJC8u0rGcy+WPqktJhjfMpatIG6CDAb0v5H34MGKqiVRue7GGLZ9Otxtt4JIrAhxBDwDuqI9JavcO0A7GlqFt219tH/bln9jBXzaKWAEqJV0CBxs5TwM8EvUPHaa8S86vN303MVWOsl3goDBHPWSoQ9c0kQmCKljfsKNH1+ofEOHW8a9a7glZGS8fPieL/SRSs0LAhI4FDTnXs1QYtubv2+IXPZpHB4bhivRexBkYKsSrYXNjvMUbVXpVJ+N6haV72c1k2zrnv5IYBMJBYTSZx0KTkoM3vY93rU/qs7zHplc/3d2ACadhFWByrn9LUk2IWb5JywvawTQc3F0iz+lgsBmInAIemBJtft2plKIlAFOgcroigrG2XlDsAzywQECNyaI8yr2ogoh7D4qJOYmZBzQgoZAM1PAcB8sDrr1uE5CDMR+nWSSVUGUCHAs8Vd21HOE0FzNj37pX0sLp9p3K8k++xxpkmzDxK64rmTSJnDUuIgTeslui6lg92jonZXI4jqNiUuzN4IagcKMjCniMGCODoo8T4tGDprn2hRww+NrnYiCwokd9iiWrkmbRfXYGLAoZrjO1lVQKExjUy5fIkgJURmz2uGFdASwwlWx5gDVTMK7hP6ISRVsFbYNmqtZL9MQtio285PaekyzDhZmtdexCYB0SZcTmBdhvdbmAEonk8hwcHQuZN1kVqrhyKoHHsnQhQAjF7SG533Da2S4LGjx1LoZqp7XeKQLDUBmYmydG0NQHpMeR5lRIRQc1PQ2ASMQflF4YBDMt0/GFlEHeRwCcEAAAAASUVORK5CYII="},e01a:function(t,e,n){"use strict";var o=n("23e7"),i=n("83ab"),a=n("da84"),s=n("5135"),r=n("861d"),c=n("9bf2").f,l=n("e893"),u=a.Symbol;if(i&&"function"==typeof u&&(!("description"in u.prototype)||void 0!==u().description)){var A={},f=function(){var t=arguments.length<1||void 0===arguments[0]?void 0:String(arguments[0]),e=this instanceof f?new u(t):void 0===t?u():u(t);return""===t&&(A[e]=!0),e};l(f,u);var g=f.prototype=u.prototype;g.constructor=f;var d=g.toString,h="Symbol(test)"==String(u("test")),p=/^Symbol\((.*)\)[^)]+$/;c(g,"description",{configurable:!0,get:function(){var t=r(this)?this.valueOf():this,e=d.call(t);if(s(A,t))return"";var n=h?e.slice(7,-1):e.replace(p,"$1");return""===n?void 0:n}}),o({global:!0,forced:!0},{Symbol:f})}},e160:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAADFBMVEX6+fP8+/X+/ff///kbczPAAAACeElEQVQozxXHQUgUUQAG4P8936yzs6VvZNZmN9QxVxiF9OLBoOjtOC6rQq6ygXjI2fCQBdXBg4egtzFGdqkoI+zgBFbqkm3hQSxhFYLotOcubeKhOnVYoqQy+m4f5g5TvpX0xHLbLY9j8SMhJp+Jk4LfAUS2kVRIjILmnwGBTX42PhCVlDJQkIiy2nWAvaJ1h+oFIpJ0hMSYVbyyrgDWshcMpMyL1brPDQKWmduO+KTJ6XeXAMK9Yc3FpD7atyNwg6kt5XgFpLPhjUTFSYVn2abDiugGShwD8JTVRJVo/2ecuKtRb/qc4BK+9TboFfokog4T2Fn6Oqdnsjk90NMS76Rji6E0NmwkPBAZ4Xbkw8KoDAkAbEhkc78e9omxxgxg6qa5HvMv+UZbCV0qmHnSHKl5TxeA2XTCGWekR581mwC5crBH81PznASqB9va3TbkYAjJPLfg5uBfXaJgIgIBv9eessRIhxe7PA7kj6uUMeMaQ/OEQOYRaaHlqH2Gxwsl6E/pwVY5FH7uCypBZPKvDQyVziYBrAkMURe2MOOOxG/eQpp5PF+bFzUV5HtPj9GeiVSNZDELleifYTp9NAjsoiXg4cW+4ZORkdSMB/B74aAdjhsVakhgkugsbmqcDSLEoWp8zRjrux3tli6Q5uM3E+maT99Wy0RiP7tboiuRZle2c6CYeL2kcUc1KvPtQKucogMadKVTQOJYCeyCYlhQQ/Q7Etfd/vBygy9iqy+LyHeF46saCYvW6ingsbA9RBWtdi8GgUXW+oQx9/wP6bAAX1TWeV+CbShZDlQ9xT6SoSxZmKRAkmXb60kzEzkRF+Ccb94BGspGJoN/UzmyR4wjXHAAAAAASUVORK5CYII="},e538:function(t,e,n){var o=n("b622");e.f=o},e8b5:function(t,e,n){var o=n("c6b6");t.exports=Array.isArray||function(t){return"Array"==o(t)}},ec0f:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyBAMAAADsEZWCAAAALVBMVEXx58b168ny6Mjz6sn06sf27Mvw5sTz6cbw5cLy58T37svv47/168v37s7t4Ltrv0//AAAEjUlEQVQ4yw2Ty2sTURxGf3dmOqmPxb0zmaStCnfmZpL6gpmbxIpUSMZGrSJkxsZiVZimNVaqMklrUnWTRq2KIDFWWx+IFrIRFxXEB4KIgqu6EBdu7M6FIPg32PW3+DhwDmBaYrK56KP4HGIsvg/uvOV0wK+qgBMlO9BujuH4DSJlOseqV5a/BEF97gt0ChyIPqBhXI9BtqtIB8vJB/LdCQ3OVjaLNX0g7+OmoI4e7nkemAqX6o8vg0yyQAyQS7IfgvFbI+6QyI3R4KELxw7kwM2ooQfyQigYnwY5MZbMlHI1DvnQVCoVcrt+R+bO7vPDif3ybNajwqAAe443dpfDsPt379VMWZzGRuqM79mQF+DUz9nt74bQ8J/O80MtVR51U02JKKmTCvTzLVf+vuxP/aHnPo9+2bW+zVsJ0Y630/CrfzX+b+UL+7O68Rczv+7lrMh5etfKXvhc2rk6KforxuoO2xB2tcxKfeXHt18rHOiHI/0RRjW/YGRDkHiwo3nzqL60o58C/bgRuaj7vk+QOwOhpnFNdjuWpKMCGP8Yapu9Ty5FTHKQLGSEFikjd9ADwP9ciaNNjc5qMH6w50AF/LKOsOYqsOG9GjKgc7ZXolqntm6fysJ6Ma6ll2CiqmOgE6O7x1wXExklbeqMYcwsmJmOoigt8SBg2WfilDSsAZJcBxDcrqtBXzFQJqZNHfscyIhoZlygAtyYAceah+elrFbI+46gEHDGiW878Kj7JpWyfhg6iyRMymV1MKBSeVpfgLHIohyTojI6sRyK1VpcqzVZeEBLOnA9unhGKUXPJDYtV9Dxuz4iA5xSkSWhCJdAiJR9PHlvfvbntbrR14FDqUNRAYDJmSnv3oKxuz5+7fiblgVJyYLTbgUM05P7LESkoXvyWNfb0aUU6FZizgQIa25VqKQZqFrk6v6BsqqIHlQmkQ9KrBhkC20/DrFsAFEEYLjM+lj2wYHXCwnNvZQR42XJ2iVK+UBXnI+OBE6oXpUUHiQ1yg0MhA03iwGbnOdQYc1CMiPIPQrCQJFH4L4BMFktAtKd9PN5gnU2Gra4KuK+V+mjtBRpAGIqDVe4wnSnajiFGO5d7smvhVQEMEYwqshrENIEaY7YeblJYtsb3QhAHWZCEKK67swwPMKw0If1Ta+6DgHmlgPzcUTSbi3rrv1Y64/BYEMPQ5SDHUOR022B4QRF6xLUPAaPX/V4IDI5N2BMwx4LqO1uO4j6uW7NvM7lATqGAxY/ZHVgoGZbu7SvkNR75x6qGSB23FdouENVwN7sCbewTdsXGrrnQ5ZZKOCOFtMTIzxlPu6eYmtL+nMFmoK7OeXajn86r9sqWbfmvHC4IagE5qfCPGZvLSq5F55hHIxJFa4/vRxHBlz0og4TojU1l/MOHJX17lybdF0mQhFO44JYUNt3UA473IXw/iPfDWtKG5oFSXIF5iU/VnyDSjxxeDk3jAXRyVyGTNB9FxH9qcFDNJpVbt2y9LytUXkK7Py6+z1RezHQqnoY8XcLimmd8dCnBhQCuaGpJCq3SoIlmYvLz8UkWhJw7T8k+Db/DYEKwgAAAABJRU5ErkJggg=="},fc0c:function(t,e,n){},fce3:function(t,e,n){var o=n("d039");t.exports=o((function(){var t=RegExp(".","string".charAt(0));return!(t.dotAll&&t.exec("\n")&&"s"===t.flags)}))}}]); \ No newline at end of file diff --git a/app/src/main/assets/web/bookshelf/js/detail.2147373a.js b/app/src/main/assets/web/bookshelf/js/detail.2147373a.js new file mode 100644 index 000000000..eaefb9b7f --- /dev/null +++ b/app/src/main/assets/web/bookshelf/js/detail.2147373a.js @@ -0,0 +1 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["detail"],{"057f":function(t,e,n){var o=n("fc6a"),i=n("241c").f,s={}.toString,a="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],r=function(t){try{return i(t)}catch(e){return a.slice()}};t.exports.f=function(t){return a&&"[object Window]"==s.call(t)?r(t):i(o(t))}},"05b3":function(t,e,n){},"0827":function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAADFBMVEUWGBkYGhsdHyAfISI1t/v6AAAB5ElEQVQozxXQsYoTURSA4f/EeycZsDgDdySDjihk38Hy3GWi2J2BCaziQhaiaB+tt9AFu1kwvYUPsIXNPoB9BAUfwAfwEUzKv/v4odGrroyp9/rUaC6rZ5skv5F8qPsfYYP+yKUMymmAEEeW55oUR4o8jr05KNzJ07yvB7w0KKfLwcQUSjfmMU0PJfPHFoEVU+ohNrcKMEzMQ23FDnVSI2dqtYWI7KlLu6vE4UnyvKc3SJuL7lBbeEEl42ItpGLjzIT8PRJCmkRjVpVpsbJFVN0687okJNZiHAr5Z7MV0BnGIDc+THM1zlbieBc1Fq+tH5BH+OpnbWkj40hSqC8Lw2TvFuF0SUFJCk2IytXbjeqcRAt6NHpnrUkUU4KRzZs8RCK8N/Akn2W04LwxMU/V7XK0bDyN2RxfDyx7I4h5vjZby72V8UnOWumZL3qtYc+8DTE0siSBMXGhywx2dMYPnQHbxdFZ7deiNGxCCtD/QWnbwDoGhRYPDzUdUA3krjpnkvdAgDN4ddLkEQSov9qjd42HaDjI34gEqS9TUueAk+sc4qg5ws407KQYKs8G1jv4xBlqBVk6cb4dISZIwVi1Jzu4+HLk6lyfUxkXvwy+1Q+4WVdHIhwfybZ6CWVhxMEhShOgsP/HOW0MvZJeFwAAAABJRU5ErkJggg=="},1276:function(t,e,n){"use strict";var o=n("d784"),i=n("44e7"),s=n("825a"),a=n("1d80"),r=n("4840"),c=n("8aa5"),A=n("50c4"),l=n("14c3"),u=n("9263"),f=n("9f7f"),g=n("d039"),d=f.UNSUPPORTED_Y,h=[].push,p=Math.min,m=4294967295,b=!g((function(){var t=/(?:)/,e=t.exec;t.exec=function(){return e.apply(this,arguments)};var n="ab".split(t);return 2!==n.length||"a"!==n[0]||"b"!==n[1]}));o("split",(function(t,e,n){var o;return o="c"=="abbc".split(/(b)*/)[1]||4!="test".split(/(?:)/,-1).length||2!="ab".split(/(?:ab)*/).length||4!=".".split(/(.?)(.?)/).length||".".split(/()()/).length>1||"".split(/.?/).length?function(t,n){var o=String(a(this)),s=void 0===n?m:n>>>0;if(0===s)return[];if(void 0===t)return[o];if(!i(t))return e.call(o,t,s);var r,c,A,l=[],f=(t.ignoreCase?"i":"")+(t.multiline?"m":"")+(t.unicode?"u":"")+(t.sticky?"y":""),g=0,d=new RegExp(t.source,f+"g");while(r=u.call(d,o)){if(c=d.lastIndex,c>g&&(l.push(o.slice(g,r.index)),r.length>1&&r.index=s))break;d.lastIndex===r.index&&d.lastIndex++}return g===o.length?!A&&d.test("")||l.push(""):l.push(o.slice(g)),l.length>s?l.slice(0,s):l}:"0".split(void 0,0).length?function(t,n){return void 0===t&&0===n?[]:e.call(this,t,n)}:e,[function(e,n){var i=a(this),s=void 0==e?void 0:e[t];return void 0!==s?s.call(e,i,n):o.call(String(i),e,n)},function(t,i){var a=n(o,this,t,i,o!==e);if(a.done)return a.value;var u=s(this),f=String(t),g=r(u,RegExp),h=u.unicode,b=(u.ignoreCase?"i":"")+(u.multiline?"m":"")+(u.unicode?"u":"")+(d?"g":"y"),v=new g(d?"^(?:"+u.source+")":u,b),C=void 0===i?m:i>>>0;if(0===C)return[];if(0===f.length)return null===l(v,f)?[f]:[];var y=0,B=0,S=[];while(B=51||!o((function(){var e=[],n=e.constructor={};return n[a]=function(){return{foo:1}},1!==e[t](Boolean).foo}))}},"1e75":function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAADFBMVEXM2t7O3ODQ3uLR4OTDp25yAAACdUlEQVQozw3P70sTcQDH8c/3/M7NG+j35mnHwjwh4hRy/QFK3zvPNbeIG1koPZmxfj2IDAwihL53zj0JYisfmEHcZJZOiBUG60lZiI8T/ANusuftgQ+kCPIPeMP7hS5mUrV9c1g6MQCAEZ8tDLHwofImAGRlX+SZK3Vu9rRRPuO4PK6/9nA4GIATsxlODS+rdCMhkAZivpYV0LWoQHSLSA4NfUg+6mY+7BKL2++F9LvnrBDYm6JO9i/YO3i/HJTGQ4pdIV82TbEDFG6vGYCd4wZchgK5J2CrKTLE+Tx0v+YGlIbdWJFcQl4ptBN8fUJQN1MCJLcZLYwUVVo+famGGty8EXJF5ofOEDzcodT3/Fb0I5sHmc1ZG7CcSl8COgxlXx09jT05OafjCZLIHJhGIaU6wDZHsuMQ41wbdjmQXbhKnMq1zlXSYrjCnyZblqexA7fC8RxS74tq2P3OxSQwTuJSApH8OZLzBBp1pOe0i3rdyDUA47GySZ31YmC4EQYSXvFSvieORGBxXF9aeVtUWKGS9WMC4Z9Y2uXnJ2nCUXVMbPOYqNYNmGWWQ7Evr+BWC+a0JAMTImcq/S4Z5INdQMeuOqDIMa9beilxfA60iC6sP1INcPDpmHBW8drZHNmqwyddJtVje9q8WGUgWAOzmbU4FCQBFi8B2Wk6pickBnYhJMenmJGuRmtt2IoKq9NuFGbNFR99sHnvrnLsLysKANDIsxbp6RNMAsoDSKuRpMwZbAAzI68QatIjmZ0aImyM3O8/4e2MNlOHZomFsa/fLDsysliHS+nlYLQJMnynxrH8QO4PaAV2Li8B/+52UgeGIVNFYf8B1XG/kFSmLcUAAAAASUVORK5CYII="},"356c":function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAADFBMVEXPz8/R0dHT09PU1NToNyAhAAACdElEQVQozw3NP0xTQQDH8d9d7sFrG+QeKVgQ4aoFCwFkYERyLY//0UB8GNGg1WAC0RBGJrzW4mCXQmpgvCYOwEAYiulSpYtza2KiW7s5FgNJFSV2/CzfL7RwpoJ20iadmgA8owOyaxmusKE44scBeb4vIv00dqYgmf6jzWcr7W6INbDQeZbQL9ytXeYgtFfzmW1Fek5msxJlwhyt6qDDxOLQzpVPompYrMPnEnhvLm7M5BxY5nowAj3zkydAkpC0FIG6g7AK+Ub25ybyNWVYwtpseP2rfrQwiGRpfqrnMuPeuvr2dA0p2YsHF2XghkrXKtZ8tLBjR7S2qIaYbKmyLd/QP+EogLjqqwNw5Lq1pDlMLkM5+gNoSvdq+Pxmz9/61EFq6GYM6GqaGvlN95zy3gsmEWI8K3k8OP9OmRLEPO6DP3Wv3g42COinJTZ33dcIvs4ESp6opMTjDs6mcYTEbFeUifuxh989yZrIx4lkpuixxz0nHLCekKbE17suKhYkMGhoYhTZtVBvg4bfq/1L1Im0AGMVpBFwumM0zwyuKiCMi5dqR4Flx47AGyF2xTbxqUdTwCH94BT3DozpLV5WuAL/N8rGtHKjotBOOuOtCJ9E21uqsyBoLOzaXbHPrK5PQBP+fBfeidvJAeMIAmzVt5IkJJ9DBWaZDAepYUhlQqHt0h72SJ3j8TZHom64f516xx9T5evgMPgwG82jZdJaJIDyWp6LAjOCclVyzNA3iTKzIULlBQEPaTXlPHok5gISclmyaWZlqY2aTHdRHpJOwTdDEQ3ZfKtbpclcNhyVClagmY+fIfyKukntPqBgnx5QvZHk/D/MK8JMClrSigAAAABJRU5ErkJggg=="},4039:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyBAMAAADsEZWCAAAAD1BMVEX0/PTx+fH2/vbz+/P4//htSO9OAAAC5UlEQVQ4yyWT0QGjMAxDZTsDWKQDmJQBYrgBUsr+M517x0+LRWw9CyA+pC1YzndrMgHaNXVKQ+di13Of1qbur48nWhuRjj8i6ON8e7pNm7zyag/DBTfS9Z4Hup1fUuXMKY4HEE8QOHCByXkIkl7lDT239RtL9quO4JItmmhOAHXg45QuYKrQFLyGJcRvaTw6kQqZy6mkR6JAPFH/XqsQjEDRmUOA+MNLHGyMUT7AHApoAhjgjIJmCxy6XHdf648AWCdGe57IUDazCeTImQOY4/z+eVYVX2IjOw9RydeAeJwl79iGi4HpgQgHEchWraUZLtayu8scq0lHHHUKMY3Ml8hB7CS1jOckDLG9ccgNeX3124phOcjL9fPnWJhTXpLHeG9DRmHnTxHEaHakS2J51lwAJcUraNbuU7q4gMTDQj3Eripc/x+qFM5VEKAB1roQfAkX5/PxqnS2QpOrxfK1Zft0/omV5T+xCSBUAIbEIwUQgvAfxFE1O8dnk233+1UZiqJ1mAbsue6Yt8tF+yOrxC/YrUhzC4qPlE3EbR5hGKhhHdlrg7J9WunV7L7BcYQwAeE59u2tnN1c6gfVYrQiLSZ9OxZdWDXQq0+r0Pbarh3UqGCwauVvbiXuDsNxCtLDdW9rTF8oQYN4EoXXdfmwNguQP26n/tRjDeo+F2W7PjWtfSr6Bn/z+cXOLp4NnMV4RytvSW4B68m+XN9XfZTFGhO/S+cHTuTqZDC21ccA0N7QsePALaDQC3D1f94U9CWo+aq6BjB3v0rxIimBM12296M3aKPHjXLQE9KQKH4By8RHraJ3AgVto2r4xdFqlaPaiAHLl1ZF4P2pI6cYc+K8UZdcmxy7lqGc1IoPxLmIFuIeEZ6j2sQT88muEg1zwrEDTIX5U/ZmcsqfgVlBumiBLF4sAyhf9BFlXOPKLZ4H0iFb3VoHrGhtHTldKrOvP2/reu2zfV8CXMPqzRdlgd0a5eI7WwB/AYcgavcqxXWEAAAAAElFTkSuQmCC"},4400:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyBAMAAADsEZWCAAAAElBMVEUQERMODxESFBYWGBkaHB0eICLm6ozJAAACkUlEQVQ4yyWTUdLbMAiEASfvoOkBkBy/O5keIE0v8E/uf5h+68qZWALELgu2MG9PP9qyvCzTVhrrsPGOCjvTfXQZvtp/W3Gy6LCITqs4q/DZ+KYl76zKzHVYpY2wNY27nqN1sbLGcrLH3/ENH4oWlGctsDu8AO+HzTLlsYdh8MzP1m6YDMz0ACfcimvakBj+mwO/+5Uta5teOD379sxK1fUxmUhv8MU3jUT5gs26PMephFznkLcpQZ6/dPL9C/GWHcCxDN6oZhD5xBm5qoYBPA+PFE/H1tXDWcWl8TW7rS+4dUzAVy0BIrvC4/HcqW2TkG1HO8q9dC23INAg7NA4AFRFkDTM2lfELPyFzi1VddcpX2z0KjHBUDmdLNJ6dDps4ytrX+FPsZwE31wSL+6OWfHOAJ3+Y0Rk/MiKfmWNPg7oVP/U3Ck9FoCkC2gBpALOiqbMNTkOe8P4FWkTD2Y9Q3+5VmV0uLUJBl68U5uAK2Kl6QDXvLxbwweOL2sixW78uU8p0ysfc7cWrF1j6B1sPJ4WgclYSnJN1bzozrhEcFHmRzBkbJWqqdG+EYJXRFmT5jnLXPUNF6WBdoFbTxYsmDXVLU/WA7MExNc93sJS5hIXDeLxzMScHzdhKvEkibr6cQXYPrmtmTA7JcInISrTzRDvShTdka0uVGrsJAAR6tSn1sKziZtfKVjAxPrJsYgZO0bye+vKTZ/DgoAoLGNO6jYHimZYTL/3pLJHawquJukjBpfz8WOGVSVIWx9ywUfS5iENutidRM4NzkAmxgUSQ68xgNOU+ZLalr4TS2V+D2xqukZig+Z9DilR7Nouzwp1cp/3E5q6Rdlf08obKvAM4qZ6pMr+w3PmQALSSBfjyZn5DwrNRVbywBQiAAAAAElFTkSuQmCC"},"477e":function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAACVBMVEX28ef48+n69esoK7jYAAAB4UlEQVQozw2OsW4bQQxEhwLXkDrysGdEqRRgVShfQQq8wOr2jD0jSpXCLvwXbtKfADlFqgSwC/9ljqweZgYzQFnb/QGepYhA9jzmTc1WaSEtQpbFgjWATI00ZZtIckXx8q2Oe5yEByBy+RHOTcM+VVTadULsvxvRC/q8WTwgcWGD+Mnaqa0oy2gw2pKFzK+PzEsus5hP9AHojKslVynLlioVTBEN8cjDNnZoR1uMGTiZAAN47HxMtEkGUE9b8HWzkqNX5Lpk0yVziAJOs46rK1pG/xNuXLjz95fSDoJE5IqG23MAYPtWoeWPvfVtIV/Ng9oH3W0gGMPIOqd4MK4QZ55dV61gOb8Zxp7I9qayaGxp6Q91cmC0ZRdBwEQVHWzSAanlZwVWc9yljeTCeaHjBVvlPSLeyeBUT2rPdJegQI103jVS3uYkyIx1il6mslMDedZuOkwzolsagvPuQAfp7cYg7k9V1NOxfq64PNSvMdwONV4VYEmqlbpZy5OAakRKkjPnL4CBv5/OZRgoWHBmNbxB0LgB1I4vXFj93UoF2/0TPEsWwV9EhbIiTPqYoTHYoMn3enTDjmrFeDTIzaL1bUC/PBIMuF+vSSYSaxoVt90EO3Gu1zrMuMRGUk7Ffv3L+A931Gsb/yBoIgAAAABJRU5ErkJggg=="},"537b":function(t,e,n){"use strict";n.r(e);var o=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"chapter-wrapper",class:{night:t.isNight,day:!t.isNight},style:t.bodyTheme},[n("div",{staticClass:"tool-bar",style:t.leftBarTheme},[n("div",{staticClass:"tools"},[n("el-popover",{attrs:{placement:"right",width:t.cataWidth,trigger:"click","visible-arrow":!1,"popper-class":"pop-cata"},model:{value:t.popCataVisible,callback:function(e){t.popCataVisible=e},expression:"popCataVisible"}},[n("PopCata",{ref:"popCata",staticClass:"popup",on:{getContent:t.getContent}}),n("div",{staticClass:"tool-icon",class:{"no-point":t.noPoint},attrs:{slot:"reference"},slot:"reference"},[n("div",{staticClass:"iconfont"},[t._v("  ")]),n("div",{staticClass:"icon-text"},[t._v("目录")])])],1),n("el-popover",{attrs:{placement:"bottom-right",width:"470",trigger:"click","visible-arrow":!1,"popper-class":"pop-setting"},model:{value:t.readSettingsVisible,callback:function(e){t.readSettingsVisible=e},expression:"readSettingsVisible"}},[n("ReadSettings",{staticClass:"popup"}),n("div",{staticClass:"tool-icon",class:{"no-point":t.noPoint},attrs:{slot:"reference"},slot:"reference"},[n("div",{staticClass:"iconfont"},[t._v("  ")]),n("div",{staticClass:"icon-text"},[t._v("设置")])])],1),n("div",{staticClass:"tool-icon",on:{click:t.toShelf}},[n("div",{staticClass:"iconfont"},[t._v("  ")]),n("div",{staticClass:"icon-text"},[t._v("书架")])]),n("div",{staticClass:"tool-icon",class:{"no-point":t.noPoint},on:{click:t.toTop}},[n("div",{staticClass:"iconfont"},[t._v("  ")]),n("div",{staticClass:"icon-text"},[t._v("顶部")])]),n("div",{staticClass:"tool-icon",class:{"no-point":t.noPoint},on:{click:t.toBottom}},[n("div",{staticClass:"iconfont"},[t._v("  ")]),n("div",{staticClass:"icon-text"},[t._v("底部")])])],1)]),n("div",{staticClass:"read-bar",style:t.rightBarTheme},[n("div",{staticClass:"tools"},[n("div",{staticClass:"tool-icon",class:{"no-point":t.noPoint},on:{click:t.toLastChapter}},[n("div",{staticClass:"iconfont"},[t._v("  ")])]),n("div",{staticClass:"tool-icon",class:{"no-point":t.noPoint},on:{click:t.toNextChapter}},[n("div",{staticClass:"iconfont"},[t._v("  ")])])])]),n("div",{staticClass:"chapter-bar"}),n("div",{ref:"content",staticClass:"chapter",style:t.chapterTheme},[n("div",{staticClass:"content"},[n("div",{ref:"top",staticClass:"top-bar"}),t.show?n("div",{ref:"title",staticClass:"title"},[t._v(t._s(t.title))]):t._e(),n("Pcontent",{attrs:{carray:t.content}}),n("div",{ref:"bottom",staticClass:"bottom-bar"})],1)])])},i=[],s=(n("ac1f"),n("1276"),function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"cata-wrapper",style:t.popupTheme},[n("div",{staticClass:"title"},[t._v(" 目录 ")]),n("div",{ref:"cataData",staticClass:"data-wrapper",class:{night:t.isNight,day:!t.isNight}},[n("div",{staticClass:"cata"},t._l(t.catalog,(function(e,o){return n("div",{key:e.durChapterIndex,ref:"cata",refInFor:!0,staticClass:"log",class:{selected:t.isSelected(o)},on:{click:function(n){return t.gotoChapter(e)}}},[n("div",{staticClass:"log-text"},[t._v(" "+t._s(e.title)+" ")])])})),0)])])}),a=[];n("a4d3"),n("e01a"),n("d3b7"),n("d28b"),n("3ca3"),n("ddb0");function r(t){return r="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"===typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},r(t)}var c,A,l=function(t,e,n,o){return t/=o/2,t<1?n/2*t*t+e:(t--,-n/2*(t*(t-2)-1)+e)},u=function(){var t,e,n,o,i,s,a,c,A,u,f,g,d;function h(){var e=t.scrollTop||t.scrollY||t.pageYOffset;return e="undefined"===typeof e?0:e,e}function p(e){var o=e.getBoundingClientRect().top,i=t.getBoundingClientRect?t.getBoundingClientRect().top:0;return o-i+n}function m(e){t.scrollTo?t.scrollTo(0,e):t.scrollTop=e}function b(t){u||(u=t),f=t-u,g=s(f,n,c,A),m(g),f1&&void 0!==arguments[1]?arguments[1]:{};switch(A=f.duration||1e3,i=f.offset||0,d=f.callback,s=f.easing||l,a=f.a11y||!1,r(f.container)){case"object":t=f.container;break;case"string":t=document.querySelector(f.container);break;default:t=window}switch(n=h(),r(u)){case"number":e=void 0,a=!1,o=n+u;break;case"object":e=u,o=p(e);break;case"string":e=document.querySelector(u),o=p(e);break}switch(c=o-n+i,r(f.duration)){case"number":A=f.duration;break;case"function":A=f.duration(c);break}requestAnimationFrame(b)}return C},f=u(),g=f,d=n("7286"),h=n.n(d),p=n("477e"),m=n.n(p),b=n("e160"),v=n.n(b),C=n("df5e"),y=n.n(C),B=n("ec0f"),S=n.n(B),I=n("b671"),k=n.n(I),w=n("5629"),x=n.n(w),E=n("d0e3"),Q=n.n(E),U=n("4039"),D=n.n(U),V=n("1e75"),O=n.n(V),F=n("1632"),P=n.n(F),R=n("7abd"),M=n.n(R),N=n("356c"),K=n.n(N),H=n("b165"),J=n.n(H),z=n("cf68"),L=n.n(z),W=n("4400"),T=n.n(W),q=n("802e"),G=n.n(q),Z=n("0827"),Y=n.n(Z),j={themes:[{body:"#ede7da url("+h.a+") repeat",content:"#ede7da url("+m.a+") repeat",popup:"#ede7da url("+v.a+") repeat"},{body:"#ede7da url("+y.a+") repeat",content:"#ede7da url("+S.a+") repeat",popup:"#ede7da url("+k.a+") repeat"},{body:"#ede7da url("+x.a+") repeat",content:"#ede7da url("+Q.a+") repeat",popup:"#ede7da url("+D.a+") repeat"},{body:"#ede7da url("+O.a+") repeat",content:"#ede7da url("+P.a+") repeat",popup:"#ede7da url("+M.a+") repeat"},{body:"#ebcece repeat",content:"#f5e4e4 repeat",popup:"#faeceb repeat"},{body:"#ede7da url("+K.a+") repeat",content:"#ede7da url("+J.a+") repeat",popup:"#ede7da url("+L.a+") repeat"},{body:"#ede7da url("+T.a+") repeat",content:"#ede7da url("+G.a+") repeat",popup:"#ede7da url("+Y.a+") repeat"}],fonts:[{fontFamily:"Microsoft YaHei, PingFangSC-Regular, HelveticaNeue-Light, Helvetica Neue Light, sans-serif"},{fontFamily:"PingFangSC-Regular, -apple-system, Simsun"},{fontFamily:"Kaiti"}]},X=j,$=(n("05b3"),{name:"PopCata",data:function(){return{isNight:6==this.$store.state.config.theme,index:this.$store.state.readingBook.index}},computed:{catalog:function(){return this.$store.state.readingBook.catalog},popCataVisible:function(){return this.$store.state.popCataVisible},theme:function(){return this.$store.state.config.theme},popupTheme:function(){return{background:X.themes[this.theme].popup}}},mounted:function(){},watch:{theme:function(t){this.isNight=6==t},popCataVisible:function(){this.$nextTick((function(){var t=this.$store.state.readingBook.index,e=this.$refs.cataData;g(this.$refs.cata[t],{container:e,duration:0})}))}},methods:{isSelected:function(t){return t==this.$store.state.readingBook.index},gotoChapter:function(t){this.index=this.catalog.indexOf(t),this.$store.commit("setPopCataVisible",!1),this.$store.commit("setContentLoading",!0),this.$emit("getContent",this.index)}}}),_=$,tt=(n("9078"),n("2877")),et=Object(tt["a"])(_,s,a,!1,null,"22f8c37b",null),nt=et.exports,ot=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"settings-wrapper",class:{night:t.isNight,day:!t.isNight},style:t.popupTheme},[n("div",{staticClass:"settings-title"},[t._v("设置")]),n("div",{staticClass:"setting-list"},[n("ul",[n("li",{staticClass:"theme-list"},[n("i",[t._v("阅读主题")]),t._l(t.themeColors,(function(e,o){return n("span",{key:o,ref:"themes",refInFor:!0,staticClass:"theme-item",class:{selected:t.selectedTheme==o},style:e,on:{click:function(e){return t.setTheme(o)}}},[o<6?n("em",{staticClass:"iconfont"},[t._v("")]):n("em",{staticClass:"moon-icon"},[t._v(t._s(t.moonIcon))])])}))],2),n("li",{staticClass:"font-list"},[n("i",[t._v("正文字体")]),t._l(t.fonts,(function(e,o){return n("span",{key:o,staticClass:"font-item",class:{selected:t.selectedFont==o},on:{click:function(e){return t.setFont(o)}}},[t._v(t._s(e))])}))],2),n("li",{staticClass:"font-size"},[n("i",[t._v("字体大小")]),n("div",{staticClass:"resize"},[n("span",{staticClass:"less",on:{click:t.lessFontSize}},[n("em",{staticClass:"iconfont"},[t._v("")])]),n("b"),t._v(" "),n("span",{staticClass:"lang"},[t._v(t._s(t.fontSize))]),n("b"),n("span",{staticClass:"more",on:{click:t.moreFontSize}},[n("em",{staticClass:"iconfont"},[t._v("")])])])]),n("li",{staticClass:"read-width"},[n("i",[t._v("页面宽度")]),n("div",{staticClass:"resize"},[n("span",{staticClass:"less",on:{click:t.lessReadWidth}},[n("em",{staticClass:"iconfont"},[t._v("")])]),n("b"),t._v(" "),n("span",{staticClass:"lang"},[t._v(t._s(t.readWidth))]),n("b"),n("span",{staticClass:"more",on:{click:t.moreReadWidth}},[n("em",{staticClass:"iconfont"},[t._v("")])])])])])])])},it=[],st=(n("82da"),{name:"ReadSettings",data:function(){return{theme:0,isNight:6==this.$store.state.config.theme,moonIcon:"",themeColors:[{background:"rgba(250, 245, 235, 0.8)"},{background:"rgba(245, 234, 204, 0.8)"},{background:"rgba(230, 242, 230, 0.8)"},{background:"rgba(228, 241, 245, 0.8)"},{background:"rgba(245, 228, 228, 0.8)"},{background:"rgba(224, 224, 224, 0.8)"},{background:"rgba(0, 0, 0, 0.5)"}],moonIconStyle:{display:"inline",color:"rgba(255,255,255,0.2)"},fonts:["雅黑","宋体","楷书"]}},mounted:function(){var t=this.$store.state.config;this.theme=t.theme,6==this.theme?this.moonIcon="":this.moonIcon=""},computed:{config:function(){return this.$store.state.config},popupTheme:function(){return{background:X.themes[this.config.theme].popup}},selectedTheme:function(){return this.$store.state.config.theme},selectedFont:function(){return this.$store.state.config.font},fontSize:function(){return this.$store.state.config.fontSize},readWidth:function(){return this.$store.state.config.readWidth}},methods:{setTheme:function(t){6==t?(this.isNight=!0,this.moonIcon="",this.moonIconStyle.color="#ed4259"):(this.isNight=!1,this.moonIcon="",this.moonIconStyle.color="rgba(255,255,255,0.2)");var e=this.config;e.theme=t,this.$store.commit("setConfig",e)},setFont:function(t){var e=this.config;e.font=t,this.$store.commit("setConfig",e)},moreFontSize:function(){var t=this.config;t.fontSize<48&&(t.fontSize+=2),this.$store.commit("setConfig",t)},lessFontSize:function(){var t=this.config;t.fontSize>12&&(t.fontSize-=2),this.$store.commit("setConfig",t)},moreReadWidth:function(){var t=this.config;t.readWidth<960&&(t.readWidth+=160),this.$store.commit("setConfig",t)},lessReadWidth:function(){var t=this.config;t.readWidth>640&&(t.readWidth-=160),this.$store.commit("setConfig",t)}}}),at=st,rt=(n("75ab"),Object(tt["a"])(at,ot,it,!1,null,"36dafd56",null)),ct=rt.exports,At=(n("d81d"),{name:"pcontent",data:function(){return{}},props:["carray"],render:function(){var t=arguments[0],e=this.fontFamily,n=this.fontSize,o=e;return o.fontSize=n,this.show?t("div",[this.carray.map((function(e){return t("p",{style:o,domProps:{innerHTML:e}})}))]):t("div")},computed:{show:function(){return this.$store.state.showContent},fontFamily:function(){return X.fonts[this.$store.state.config.font]},fontSize:function(){return this.$store.state.config.fontSize+"px"}},watch:{fontSize:function(){var t=this;t.$store.commit("setShowContent",!1),this.$nextTick((function(){t.$store.commit("setShowContent",!0)}))}}}),lt=At,ut=(n("ca36"),Object(tt["a"])(lt,c,A,!1,null,"7b03cca0",null)),ft=ut.exports,gt=n("bc3a"),dt=n.n(gt),ht={components:{PopCata:nt,Pcontent:ft,ReadSettings:ct},created:function(){var t=JSON.parse(localStorage.getItem("config"));null!=t&&this.$store.commit("setConfig",t)},beforeCreate:function(){var t=JSON.parse(localStorage.getItem("config"));null!=t&&this.$store.commit("setConfig",t)},mounted:function(){var t=this;this.loading=this.$loading({target:this.$refs.content,lock:!0,text:"正在获取内容",spinner:"el-icon-loading",background:"rgba(0,0,0,0)"});var e=this,n=sessionStorage.getItem("bookUrl"),o=sessionStorage.getItem("bookName"),i=sessionStorage.getItem("chapterIndex")||0,s=JSON.parse(localStorage.getItem(n));(null==s||i>0)&&(s={bookName:o,bookUrl:n,index:i},localStorage.setItem(n,JSON.stringify(s))),this.func_keyup=function(t){switch(t.key){case"ArrowLeft":t.stopPropagation(),t.preventDefault(),e.toLastChapter();break;case"ArrowRight":t.stopPropagation(),t.preventDefault(),e.toNextChapter();break;case"ArrowUp":t.stopPropagation(),t.preventDefault(),0===document.documentElement.scrollTop?e.$message.warning("已到达页面顶部"):g(0-document.documentElement.clientHeight+100);break;case"ArrowDown":t.stopPropagation(),t.preventDefault(),document.documentElement.clientHeight+document.documentElement.scrollTop===document.documentElement.scrollHeight?e.$message.warning("已到达页面底部"):g(document.documentElement.clientHeight-100);break}},this.getCatalog(n).then((function(n){var o=n.data.data;s.catalog=o,e.$store.commit("setReadingBook",s);var i=e.$store.state.readingBook.index||0;t.getContent(i),window.addEventListener("keyup",t.func_keyup)}),(function(t){throw e.loading.close(),e.$message.error("获取书籍目录失败"),t}))},destroyed:function(){window.removeEventListener("keyup",this.func_keyup)},watch:{chapterName:function(t){this.title=t},content:function(){this.$store.commit("setContentLoading",!1)},theme:function(t){this.isNight=6==t},bodyColor:function(t){this.bodyTheme.background=t},chapterColor:function(t){this.chapterTheme.background=t},readWidth:function(t){this.chapterTheme.width=t;var e=-((parseInt(t)+130)/2+68)+"px",n=-((parseInt(t)+130)/2+52)+"px";this.leftBarTheme.marginLeft=e,this.rightBarTheme.marginRight=n},popupColor:function(t){this.leftBarTheme.background=t,this.rightBarTheme.background=t},readSettingsVisible:function(t){if(!t){var e=JSON.stringify(this.$store.state.config);localStorage.setItem("config",e)}}},data:function(){return{title:"",content:[],noPoint:!0,isNight:6==this.$store.state.config.theme,bodyTheme:{background:X.themes[this.$store.state.config.theme].body},chapterTheme:{background:X.themes[this.$store.state.config.theme].content,width:this.$store.state.config.readWidth-130+"px"},leftBarTheme:{background:X.themes[this.$store.state.config.theme].popup,marginLeft:-(this.$store.state.config.readWidth/2+68)+"px"},rightBarTheme:{background:X.themes[this.$store.state.config.theme].popup,marginRight:-(this.$store.state.config.readWidth/2+52)+"px"}}},computed:{catalog:function(){return this.$store.state.catalog},windowHeight:function(){return window.innerHeight},contentHeight:function(){return this.$refs.content.offsetHeight},popCataVisible:{get:function(){return this.$store.state.popCataVisible},set:function(t){this.$store.commit("setPopCataVisible",t)}},readSettingsVisible:{get:function(){return this.$store.state.readSettingsVisible},set:function(t){this.$store.commit("setReadSettingsVisible",t)}},config:function(){return this.$store.state.config},theme:function(){return this.config.theme},bodyColor:function(){return X.themes[this.config.theme].body},chapterColor:function(){return X.themes[this.config.theme].content},popupColor:function(){return X.themes[this.config.theme].popup},readWidth:function(){return this.$store.state.config.readWidth-130+"px"},cataWidth:function(){return this.$store.state.config.readWidth-33},show:function(){return this.$store.state.showContent}},methods:{getCatalog:function(t){return dt.a.get("/getChapterList?url="+encodeURIComponent(t))},getContent:function(t){var e=this;this.$store.commit("setShowContent",!1),this.loading.visible||(this.loading=this.$loading({target:this.$refs.content,lock:!0,text:"正在获取内容",spinner:"el-icon-loading",background:"rgba(0,0,0,0)"}));var n=sessionStorage.getItem("bookUrl"),o=JSON.parse(localStorage.getItem(n));o.index=t,localStorage.setItem(n,JSON.stringify(o)),this.$store.state.readingBook.index=t;var i=this.$store.state.readingBook.catalog[t].title,s=this.$store.state.readingBook.catalog[t].index;this.title=i,g(this.$refs.top,{duration:0});var a=this;dt.a.get("/getBookContent?url="+encodeURIComponent(n)+"&index="+s).then((function(t){var n=t.data.data;a.content=n.split(/\n+/),e.$store.commit("setContentLoading",!0),a.loading.close(),a.noPoint=!1,a.$store.commit("setShowContent",!0)}),(function(t){throw a.$message.error("获取章节内容失败"),a.content="  获取章节内容失败!",t}))},toTop:function(){g(this.$refs.top)},toBottom:function(){g(this.$refs.bottom)},toNextChapter:function(){this.$store.commit("setContentLoading",!0);var t=this.$store.state.readingBook.index;t++,"undefined"!==typeof this.$store.state.readingBook.catalog[t]?(this.$message.info("下一章"),this.getContent(t)):this.$message.error("本章是最后一章")},toLastChapter:function(){this.$store.commit("setContentLoading",!0);var t=this.$store.state.readingBook.index;t--,"undefined"!==typeof this.$store.state.readingBook.catalog[t]?(this.$message.info("上一章"),this.getContent(t)):this.$message.error("本章是第一章")},toShelf:function(){this.$router.push("/")}}},pt=ht,mt=(n("ace6"),Object(tt["a"])(pt,o,i,!1,null,"0405dcaf",null));e["default"]=mt.exports},5629:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAADFBMVEXN383Q4tDP4c/R5NEInCCXAAACVElEQVQozw3Hv2sTYRwH4M/79pJ7bZL2bXqtERJ97zjUpbZDhg6pfC8qibi8hLR0EaJ0EFxaCSWDxjfpj1zrYBcRBKE6SAfBJWsx9i8IQfdQxDlKtA6t2OnhQfN3lbG7ytYRywF8rVoPCNO0X2sQOKDpAnSDK2VwkHgmh5yLGT8qASt+2KofnNt2Xg1gf1UF8AoM6052cRMNaloLZb7RKQGrKKji2OefsZF+VqIvos5ZLVIZCX61JcwUdk56wASVkgQvzPfvmT2twTSwyYaC/Pl/UhAHorFhBgZtL6XdAZRp1tkPwC1NLa9CWs5prLhI85NBQsLdXvjDymG3/EbYfQhVNYqc3TtktQhWLY3ko0QsdMbSEp+64v0NfxyqLbIGdh6M2xHHlLBGqKTyQo4E/nebBgBfe1GpdeywYXc8CT7D3cKXuMXkBy4xN6o5OuKamYp3DVI6uccO9lxgd2CAlJgI2BGgaAgIJV/TYwKqu3WFccjbMuA+bVkWgS2bfnlRbD1Eb1sDyWMmjKYIBgGAWbqKRicfvzBkBIz3V5AKnguWdglQEysQsSuVzOg6ALy1pitA5ykGCsc857BRYcgCSZyFOdvoOigSGoPc5Ta73mgxshIcQE5sHMHd9D7yqITw7JO+GHVMxjhzYLcKPSEgmz3fU+BRy3iYNtiXLaBssCW8KguReqkQOTb3MStV0Ugt4U1eIs1RZWRII6Ww8xeNNItyGGQI4ZMlpg/3lQtkl2JFnBp1imRyFe0kK2Id3PCslMgiQNMS77gvFeDhG3cSkYvheeg/e7ClIh5oh+IAAAAASUVORK5CYII="},"58c8":function(t,e,n){},"5ca7":function(t,e,n){"use strict";n("a7bd")},"63f0":function(t,e,n){},"65f0":function(t,e,n){var o=n("861d"),i=n("e8b5"),s=n("b622"),a=s("species");t.exports=function(t,e){var n;return i(t)&&(n=t.constructor,"function"!=typeof n||n!==Array&&!i(n.prototype)?o(n)&&(n=n[a],null===n&&(n=void 0)):n=void 0),new(void 0===n?Array:n)(0===e?0:e)}},7286:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAADFBMVEXr5djn4dTp49bt59rT6LKxAAACnElEQVQozw3NUUwScRzA8d8R6MF8YMIx8uk47hDSJbj14IPzOGc7jPLvwTGg5uAYDbe2tt56cLtznvEnS6yDqCcEaWi91DvrbLJZz7b1aFtz1aO+2OZWvn+/+4CHeB6BMYaqBLfjPNRY6RFT2JJYby+uAk4WUTrtlmJ4hgPYb2q1XGDQjaK8pgJHvqNaAX+KyuIkDXpgQinb46nOulnn4b5laUHTxLfseeArAoNOeJlOIjdoal0n1FA7tKFv5roK+YaHOqP3P0XyKHPHY+MhTRe5uCZnKhtJKw2eSrSoBDPLtpZuNcFNJcFyiCMxOaaHIfXz1e8HQbWLySrBQ4x0x1qlhnHlnz2HQEC6TNb0gTHXa7IKhcaHqkE015hk9whA0YeWiLIXf7Fa2CZo3DjqjB4tTuF8jIcbfcEx5z/w4sXpQhXW+ju0cqh7icTFmRMaG+v6CIvTjcSpHcH8JEsF3EPh3fRthYdVLLgI2fWXm85/pGFE4l046s70L+yKCcirGFR+jbpy3kMmiCGHrSezVONsn1RBixncyk2PcVWk7DlgxHo8iZwDyq5uAUD854dZhdIFYzKoQig2haUKi1lVufz2RZUZPZ41n/hrOQB6h0Hhg8I367FNoEHgeM/KY7szSeQwD8q2WE3HM35ZLl0K1MJiOtHIkBclRQUwZnyOWcNsRQQgVLj1PSqkjF9DsoOSaSg3iinKzvfmgsNFFfpP/2T3GLGvL4fHEfwIX1sVvXcPqLztehWGcfn9nI2U9nTfCgJPe/jFPLZwgVEzimBgAm0VIyK2tt1cE/AzQdLK+SxLSQ4aDCZnnId94OG2S1XwvnTbNk/ZnhyRCQT+sZM6z9g6LXL1BOBe+zJySiFkHAINCtnQokbCJ/apCv0foqPiZVfhpywAAAAASUVORK5CYII="},"746f":function(t,e,n){var o=n("428f"),i=n("5135"),s=n("e538"),a=n("9bf2").f;t.exports=function(t){var e=o.Symbol||(o.Symbol={});i(e,t)||a(e,t,{value:s.f(t)})}},"75ab":function(t,e,n){"use strict";n("63f0")},"7abd":function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAADFBMVEXm9PXq+Pno9vfs+vttWKBGAAACPElEQVQozw3RQWrbQACF4TfCMjPqZgIj4RRaxsZKE0PuMBZ2cLKaCI9RDAXFmJJknUWWI1O1UlamOMHJSjGkuFn3AD2Cr9CepDrAg+/xIxK4QwIqHHQkUhQ/WuphInVIFBojl8QXc012Tgq4RTtVHWVLZVFh1tEoI91uiN4joCqde8Ukn/zGM1B2W4ari2PtTwyw55Ld+Wways54qhGPyS6FzbIT3lIY8WwWdCq56Yolx6KmSKzoqrsCB5heAp4TGNQWJ1Pc6XlE5jQD5OlIX9I47A9uiUQcPQxcury/ToyxWJG/za6ki88crxKPocKS59Sl3EtBG7C89fCGflpfqoSzCeC4crioJA7F0V5+8MaSIk4qSCdwzpogmbqzEirVpGiS2dOVJvUuuqFEmhHao06KEpq+8lvHI14NJk3Qrmi9vBuRLwAz0qZB4hsDXQFXgtnlpDX3C6ug9BquSw/CYtwAzuTz5vuQNdr/YibhR68378ehZH30FSpjh71LpQkrsj+Q062h5WwZ5wlRoD6uQJy1DqvSYuCUapMBqT5YA4ZFw4KlWapxoUGlKWrx0eDQvmigu4WMYt97ruru98fYL8/0lG6CTOFcFWBhFK5gKw19h2JN808nh7xhkU6sWKLXdtkqBL6h+lULK5k19wFB/FldnGYf3LDeuf6IC2/MzJOSOP0qPxLqzaGIqtBcFIItrstkazONOkrc1D1czjuwEGESB4JJnjgSMN7PXAu7fZQpl1C236C+9mM4Af8P98Ch4R2TRl8AAAAASUVORK5CYII="},"802e":function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAADFBMVEUWGBkYGhsdHyAfISI1t/v6AAAB5ElEQVQozxXQsYoTURSA4f/EeycZsDgDdySDjihk38Hy3GWi2J2BCaziQhaiaB+tt9AFu1kwvYUPsIXNPoB9BAUfwAfwEUzKv/v4odGrroyp9/rUaC6rZ5skv5F8qPsfYYP+yKUMymmAEEeW55oUR4o8jr05KNzJ07yvB7w0KKfLwcQUSjfmMU0PJfPHFoEVU+ohNrcKMEzMQ23FDnVSI2dqtYWI7KlLu6vE4UnyvKc3SJuL7lBbeEEl42ItpGLjzIT8PRJCmkRjVpVpsbJFVN0687okJNZiHAr5Z7MV0BnGIDc+THM1zlbieBc1Fq+tH5BH+OpnbWkj40hSqC8Lw2TvFuF0SUFJCk2IytXbjeqcRAt6NHpnrUkUU4KRzZs8RCK8N/Akn2W04LwxMU/V7XK0bDyN2RxfDyx7I4h5vjZby72V8UnOWumZL3qtYc+8DTE0siSBMXGhywx2dMYPnQHbxdFZ7deiNGxCCtD/QWnbwDoGhRYPDzUdUA3krjpnkvdAgDN4ddLkEQSov9qjd42HaDjI34gEqS9TUueAk+sc4qg5ws407KQYKs8G1jv4xBlqBVk6cb4dISZIwVi1Jzu4+HLk6lyfUxkXvwy+1Q+4WVdHIhwfybZ6CWVhxMEhShOgsP/HOW0MvZJeFwAAAABJRU5ErkJggg=="},"82da":function(t,e,n){},9078:function(t,e,n){"use strict";n("c0bc")},a4d3:function(t,e,n){"use strict";var o=n("23e7"),i=n("da84"),s=n("d066"),a=n("c430"),r=n("83ab"),c=n("4930"),A=n("fdbf"),l=n("d039"),u=n("5135"),f=n("e8b5"),g=n("861d"),d=n("825a"),h=n("7b0b"),p=n("fc6a"),m=n("c04e"),b=n("5c6c"),v=n("7c73"),C=n("df75"),y=n("241c"),B=n("057f"),S=n("7418"),I=n("06cf"),k=n("9bf2"),w=n("d1e7"),x=n("9112"),E=n("6eeb"),Q=n("5692"),U=n("f772"),D=n("d012"),V=n("90e3"),O=n("b622"),F=n("e538"),P=n("746f"),R=n("d44e"),M=n("69f3"),N=n("b727").forEach,K=U("hidden"),H="Symbol",J="prototype",z=O("toPrimitive"),L=M.set,W=M.getterFor(H),T=Object[J],q=i.Symbol,G=s("JSON","stringify"),Z=I.f,Y=k.f,j=B.f,X=w.f,$=Q("symbols"),_=Q("op-symbols"),tt=Q("string-to-symbol-registry"),et=Q("symbol-to-string-registry"),nt=Q("wks"),ot=i.QObject,it=!ot||!ot[J]||!ot[J].findChild,st=r&&l((function(){return 7!=v(Y({},"a",{get:function(){return Y(this,"a",{value:7}).a}})).a}))?function(t,e,n){var o=Z(T,e);o&&delete T[e],Y(t,e,n),o&&t!==T&&Y(T,e,o)}:Y,at=function(t,e){var n=$[t]=v(q[J]);return L(n,{type:H,tag:t,description:e}),r||(n.description=e),n},rt=A?function(t){return"symbol"==typeof t}:function(t){return Object(t)instanceof q},ct=function(t,e,n){t===T&&ct(_,e,n),d(t);var o=m(e,!0);return d(n),u($,o)?(n.enumerable?(u(t,K)&&t[K][o]&&(t[K][o]=!1),n=v(n,{enumerable:b(0,!1)})):(u(t,K)||Y(t,K,b(1,{})),t[K][o]=!0),st(t,o,n)):Y(t,o,n)},At=function(t,e){d(t);var n=p(e),o=C(n).concat(dt(n));return N(o,(function(e){r&&!ut.call(n,e)||ct(t,e,n[e])})),t},lt=function(t,e){return void 0===e?v(t):At(v(t),e)},ut=function(t){var e=m(t,!0),n=X.call(this,e);return!(this===T&&u($,e)&&!u(_,e))&&(!(n||!u(this,e)||!u($,e)||u(this,K)&&this[K][e])||n)},ft=function(t,e){var n=p(t),o=m(e,!0);if(n!==T||!u($,o)||u(_,o)){var i=Z(n,o);return!i||!u($,o)||u(n,K)&&n[K][o]||(i.enumerable=!0),i}},gt=function(t){var e=j(p(t)),n=[];return N(e,(function(t){u($,t)||u(D,t)||n.push(t)})),n},dt=function(t){var e=t===T,n=j(e?_:p(t)),o=[];return N(n,(function(t){!u($,t)||e&&!u(T,t)||o.push($[t])})),o};if(c||(q=function(){if(this instanceof q)throw TypeError("Symbol is not a constructor");var t=arguments.length&&void 0!==arguments[0]?String(arguments[0]):void 0,e=V(t),n=function(t){this===T&&n.call(_,t),u(this,K)&&u(this[K],e)&&(this[K][e]=!1),st(this,e,b(1,t))};return r&&it&&st(T,e,{configurable:!0,set:n}),at(e,t)},E(q[J],"toString",(function(){return W(this).tag})),E(q,"withoutSetter",(function(t){return at(V(t),t)})),w.f=ut,k.f=ct,I.f=ft,y.f=B.f=gt,S.f=dt,F.f=function(t){return at(O(t),t)},r&&(Y(q[J],"description",{configurable:!0,get:function(){return W(this).description}}),a||E(T,"propertyIsEnumerable",ut,{unsafe:!0}))),o({global:!0,wrap:!0,forced:!c,sham:!c},{Symbol:q}),N(C(nt),(function(t){P(t)})),o({target:H,stat:!0,forced:!c},{for:function(t){var e=String(t);if(u(tt,e))return tt[e];var n=q(e);return tt[e]=n,et[n]=e,n},keyFor:function(t){if(!rt(t))throw TypeError(t+" is not a symbol");if(u(et,t))return et[t]},useSetter:function(){it=!0},useSimple:function(){it=!1}}),o({target:"Object",stat:!0,forced:!c,sham:!r},{create:lt,defineProperty:ct,defineProperties:At,getOwnPropertyDescriptor:ft}),o({target:"Object",stat:!0,forced:!c},{getOwnPropertyNames:gt,getOwnPropertySymbols:dt}),o({target:"Object",stat:!0,forced:l((function(){S.f(1)}))},{getOwnPropertySymbols:function(t){return S.f(h(t))}}),G){var ht=!c||l((function(){var t=q();return"[null]"!=G([t])||"{}"!=G({a:t})||"{}"!=G(Object(t))}));o({target:"JSON",stat:!0,forced:ht},{stringify:function(t,e,n){var o,i=[t],s=1;while(arguments.length>s)i.push(arguments[s++]);if(o=e,(g(e)||void 0!==t)&&!rt(t))return f(e)||(e=function(t,e){if("function"==typeof o&&(e=o.call(this,t,e)),!rt(e))return e}),i[1]=e,G.apply(null,i)}})}q[J][z]||x(q[J],z,q[J].valueOf),R(q,H),D[K]=!0},a7bd:function(t,e,n){},ace6:function(t,e,n){"use strict";n("fc0c")},b165:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAADFBMVEXe3t7a2trc3Nzg4OCXP9lCAAACoklEQVQozwXBzU/TYBwA4N+QEr4CNbSFwcFuowSqMRvEAwShHWAYNsu7dS0dLnGUSWT4kZB4lGzE4VtcwgIDJqcOWLJxcv4BOoQZuCPxSNSD4WSWLJGL8XmAIiyo2RgJ4A1pxQQlOxRAszLTdnPu2oQGb05RC5slJld7ZAIfo4O44Bn1ud59F0BcjnYOa17Jhwc6EdiKettncsXjT1f8KUBZUW41pK0Jc1Az4dEV3rkkPBtDSZ83Blyt0kSf2PRjzIykoBwINisPbPPtljdVE9iAXRfUPkXLVIgYrCccp5g687NdZbcJ+xa5VE/HhTtT23IKsN5jj/pcUd0dTZNAqCVw72n4gOwnTOC0vvHfaauT8d9zAoRRfPpISZRVyUiw8ELzOG1b2DZpFzkSrHLhq52twDEdyZHwvp2j4uv/bjvOf23/AcEtTuJbY5Cp4YcAer1IGkUzOo2rn8LQOKjFJw3NTw24nprQXY5aF4wxcqcSdbFQ00H4xFl8Drx4X4CikvAM1tuR8bKIBCBoLnKN10KJG4zKAsc7c9WEB9gnCi6BhVjqoco6t20ILAJuVctvaEZK732cRHDRmGfuihOam0o2CHByUZ/epCcVlRs2wmCnMqsd6aSim3ibBJtm1LGyXW3Bb7tJCPlFtUG+SvPdeEUAB60lNdo+VQbLcwRNVtT68FsLcr1+NotgNihlpExS1V2SFgNbeC8bEhgm8sM17wSi6Us2gxVWJU/5GKBpandvfyYbU1yHCLpCgWGbbPXn40rehEsUXKIJr9DMKgICfjc4bl1YfvUhE/YIECGRqjCxSM9hrybAIkND5OeWfFZsXkxB+qDzb7pUQ3EfQ3Ml6EChEt3D+iS01VqC7EQ/Z/DuPQcz4yChoFQJce2Qr+NNAv0HxofmpXGqgHkAAAAASUVORK5CYII="},b671:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyBAMAAADsEZWCAAAAD1BMVEX48dr48Nf58tv379X17NJtIBxUAAACFUlEQVQ4y1XRUZakMAgF0Af2AiDWApDZgHZqAV1nZv9rGh7Rj7Y8McUFEg1wvcMESMNVD/neU8Xcaz7nYYkYlYO6Ti82PBI4BvIEg1aj3wKwRvIMgZsUy5LdhCawPFh1sZs4SrlyN9fQKpv8s5dgZ2eLyqqJiu+WkCmUEybXkm3INS01WAiv0PapJ0CZc0SJQUzcWnZYbOOY20iFD8Bk+/j2A3wNxH7GdShFYS5ff237kXh9I9zSkQmIAhOsOSVfJ6DIXTMDaPnzkRJ92S1BQQmXl5LdirgRLLDdcYqcGPwe3QN4xCBiGNbrqq9wpW1XCecChwaQdVOsRDpPCpeoolPdxeXp3WNB9PHVzWBHlygy4NJCCrFHREv6bDt0VGwJZASkpONmm1UseGeFKAQexgaAkrfYWl3AGxWOLL2AIMBNbCXpktmS3k3vHeYjGCPBa43wJTurO3ZFVpQSJdAZGLoHTyk1upkjxMEaIxum3iIARcCa5kSkFAW5fi1mUlL9eyOsaanFmOMruwvEdE3ZYzsRSzo5ewRLXyVPPEvknt8ij4DvCg2O7xOgBCUprEzV4z1WekSpUgI8DT2mrnSOXKRfQavwuKA1F+tFnMKdJSUpMA7wQAifWRkMgjUKKZE4lBl6MCM4B1pq1P4uIjDE6Pq6rL0FnW1nIFmta5vrSvq/Ch4tpqG/ZNyyWa5jZPktq81eYv8Bt5s4iFITOp4AAAAASUVORK5CYII="},b727:function(t,e,n){var o=n("0366"),i=n("44ad"),s=n("7b0b"),a=n("50c4"),r=n("65f0"),c=[].push,A=function(t){var e=1==t,n=2==t,A=3==t,l=4==t,u=6==t,f=7==t,g=5==t||u;return function(d,h,p,m){for(var b,v,C=s(d),y=i(C),B=o(h,p,3),S=a(y.length),I=0,k=m||r,w=e?k(d,S):n||f?k(d,0):void 0;S>I;I++)if((g||I in y)&&(b=y[I],v=B(b,I,C),t))if(e)w[I]=v;else if(v)switch(t){case 3:return!0;case 5:return b;case 6:return I;case 2:c.call(w,b)}else switch(t){case 4:return!1;case 7:c.call(w,b)}return u?-1:A||l?l:w}};t.exports={forEach:A(0),map:A(1),filter:A(2),some:A(3),every:A(4),find:A(5),findIndex:A(6),filterOut:A(7)}},c0bc:function(t,e,n){},c84b:function(t,e,n){"use strict";n.r(e);var o=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"detail-wrapper"},[n("div",{staticClass:"detail"},[n("div",{staticClass:"bar"},[n("el-breadcrumb",{attrs:{separator:"/"}},[n("el-breadcrumb-item",{staticClass:"index",attrs:{to:{path:"/"}}},[t._v("书架")]),n("el-breadcrumb-item",{staticClass:"sub"},[t._v("目录")])],1)],1),n("el-divider"),n("div",{staticClass:"catalog"},t._l(this.$store.state.catalog,(function(e){return n("div",{key:e.index,staticClass:"note",on:{click:function(n){return t.toChapter(e.url,e.title,e.index)}}},[t._v(" "+t._s(e.title)+" ")])})),0)],1)])},i=[],s=n("bc3a"),a=n.n(s),r={data:function(){return{key:"value"}},mounted:function(){var t=this;a.a.get("/getChapterList?url="+encodeURIComponent(sessionStorage.getItem("bookUrl"))).then((function(e){t.$store.commit("setCatalog",e.data.data),sessionStorage.setItem("catalog",JSON.stringify(e.data.data))}),(function(t){throw t}))},methods:{toChapter:function(t,e,n){sessionStorage.setItem("chapterID",n),this.$router.push({path:"/chapter"})}}},c=r,A=(n("5ca7"),n("2877")),l=Object(A["a"])(c,o,i,!1,null,"57115a66",null);e["default"]=l.exports},ca36:function(t,e,n){"use strict";n("58c8")},cf68:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyBAMAAADsEZWCAAAAD1BMVEXm5ubo6Ojp6enr6+vt7e1FnZagAAACrklEQVQ4yx1SixUbMQgT3AKAFwDcAfzpBN1/qMrJS5w7bCQhC6IGSUGYQJd6Ox9ZPXi1AGJBavhUTT0JjYPGAab9WcDYIxsmlnxkayX8mhxCmKHA75az5cfRbWybEExiu08xDSgGym0mwuf3j4SvHeQxDJJzh2zp4iOlrD8iOb4SXyC1wiOLRTcnrje+nGamFeXVKWkmzbFIPChkmJ6Fg7mBpV8n+JGOVCd4jv1thThkjeQGNeafpeV3rsEWLfyWc8tC9jOv6FQ8rRzHOOVB+jCYEUAJpDvh8xHNFm/Tm5p5lw94Pp3NhtKEfQsGvnXhowdZE73hPwxKvjDd4i4PCdd0fe3W5fO8ktAsUAacLgstpUw60JCiPLg2XpkgiqPIYYXJd9ksGIT3q+LlevypzItvO+s0F1dBzVr2QDMUkYmuyGcrIS44mVJ7JVKwQXjYuBYp0Uetecbswzsikzu3gUR8bJC/C8Gd/NAzI/xdUGOYQQHDZ8X2d5XuzGRUiXAi9si5CRgoiToRZPtzLJkd0FUHRHZwJf0BHT1sE7gcnh0jmKKlSSF4/GBirGk5+K9NKlGDCfc9JtPhg78JdabH0YQRKNZnJ8tFnPfXHJb4xum1TTCeEmyEdbyEJLjznMLHuFD2Y9NEkSleIBs7SiCbblhgctVi9ch++kDYnn1C9DA5TvdPsToXM55wI6k+8eKT1blwPTqWb5CFJ+7dTBmab+KHy+xwNtItXhZNSpHD2fxnynrxG3ZBKRe8KBpXk11AnadlccEhr9w1nBBvBylNkv7A8eqpGBCDqhitmWQXBjjdS6idr/QjXWLDeMzMbVDoJuM8zN7WenMZWXgZ2vX3F01J3jHZbwk1LRP+DWEvDJtOUoh/AIaBUz5VpWyhuyx4QtgL/NmgC6kM/JvNe+R/C/5aL7BKIbYAAAAASUVORK5CYII="},d0e3:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAADFBMVEXh7eHl8eXj7+Pn8+eTbH1KAAACPElEQVQozxWPQWrbQABF/0xn3JFKQRTZOIuUsbCCbOgdRoYEOauxkYPcTRyTlPQWIxEltrsRwQ6hK9nEQek6F+gNTE/Q3qLLusv34cN7SH3mFicdYW4gNIhJWXPBRVXzjcFD0IqeU4o4PRbAIVjyico0vJpIifqPfL80QN9DAQY5ucRHE/hpHxBldXe9GilaHKcKMlj6pho2zXgkNdBl0oJ8kiF1DSiJF1ZHBJkQr0Dbux/5I42Zp4cFahJDFGeW6/QjBwmFY/Q7vZ2SnoOdW2parv/Cnm81+m0xrEfiVXQ3W4nOXIqVYi3l6AAQBwMFkViVBANMto4enXHPNTkHBB0oVj4r5vHzCWayrgBvxtygDlDB2CNDjd80ZInY69aKVYZcfJ8DW+fWuc+syEODALx+ojqoafHsthTI+ZW27PGpIeo/cR6YKcbqIuIFhHmBrzAovzIOOJk1ucvcDzrMRYGVBH2yvcAOf0KiKwfRovBI3tm/kW1eemtfNWwIIXE2mJNhvoszfmMBfRCv0OPwd2321uDW3nx2q/BDxFVeoN1g7a6Im8yRnoawa8kbdXnU0cHeTMxKfZGlJgvLb3sKsxgglQnDdAfvj9LUnqWRDo0GiUmPwyU7TAsD7wHeIW3Nfy1qVGKoE9NgJCdYCAexNRob9yCn4DAQmXtQuUtera6bEmTTXhZy6h856xi4mnEl6BI9mfISkLbtJyZIMJIAUd5ZOBEu88KRAk71yxfItj/hpIB0Errv4gO1os4/UICf+o3kkqwAAAAASUVORK5CYII="},d28b:function(t,e,n){var o=n("746f");o("iterator")},d81d:function(t,e,n){"use strict";var o=n("23e7"),i=n("b727").map,s=n("1dde"),a=s("map");o({target:"Array",proto:!0,forced:!a},{map:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0)}})},df5e:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAATlBMVEXdzaHh0KPgz6LdzKDezqLczJ7ezZ/fz6Dcy5zi0aXdzZ3fz6Tfz57h0KDg0aLcyZrg0KXi0qPfzZ3j06bh0qbdyJbfzJrhz5/cxpLZwo0vDconAAAFn0lEQVRIxxyPW5LjMAwDAT5FybLl2JnM3P+i6+wXWVC1GoQGaD0h4XM3Q5o4T0HgABHBi6pZ4CDXXcUOFd6VhqC3Kch4EI8w9oMXwvU6m5LOOvcxKMOhuu8i5+5cMjcgb0t4F2uvOoeI3/MlT4IqsbtM9UG2AGSXUOsxzPevnXzK1CSHytZLvx7VdQmUcJsJCxJh2nmHW12Qod1qPjt8pih47uQ9aGpoNWF+yElCt60oH7vdIU/MnlRPSBLC/VwqxcKR8PFqnADN9ih5ufqnTlG9KwCofvs7kKYqOPHTNMQ93j9qNImFw9vjHPZ0F1m8hUUVB/Q/TrRYDMXr9++APMFARAt6sPh6wVAXzxUGhZsFUwCNfPZ8/72TAHebAhvuOuT3gO1Vn5d9Jd5sBRkg0p2seL9B7ulkjFJFIt9HPpLzdSzzMP3UcodAfMqC6pBuET2heHK1itZf1GZ1bi0BwOSxiCS8f/JBHMPMM4XCu3Mt1uz9lJbDJRqsKDZuikzkvskQEz6hanfDfO494azY5JpqPqOF1RhxD9XYEdaNxiqWqakKgmPfmrsta8KAiwF4HBxGVUJAgeSqQaiRRZJ7D2jedhw5t1CIAKxag0CBA60BpoBE6DcUi8O5AuM4pLfN0kHLmeu2B4e6HofqbgxsTWUw3PAODqa1oDtyzgXBlusi1KFdclMPE8O3jvLJ8RNi5/RxDQVzVmXA233XQ4KummunfxvLOZo+iH37964YjP06995CTdu9hsvErqJNzmf4wTrZ5DL7+qW9EoLnadrx67b8dUtrJnBXaT1N1uvPaYRKpWkq52xNsMN7vv4Sdryt/f4MhQoMCKnvVxikai1CQ6ZsnwJDc8+3Y/z8HcfvYQNq66pnAu1Hwa+3KNSwbNu8h3nDPqTl9fl7tx8fBhFfdS0o0F3JUKEZtZG9b/LZEM95lzaR30OnWPzroMxyZYdBIMoMnpN0J+m7/40+/P4soFSUjgzE7yY5zrMJuoZv0CmpVguYx1pprfb5HOviRVhHUVi/352shxCYrYBZxGtVaxiAz/MsaGSIsB7R1t4zJXH//n7RTTQQwxqcGEqEvklFHUgiO2GvJV+jAIPR+N29usWDoiSOVrN3XuqT1egQJAAU9EwslVJC8u0rGcy+WPqktJhjfMpatIG6CDAb0v5H34MGKqiVRue7GGLZ9Otxtt4JIrAhxBDwDuqI9JavcO0A7GlqFt219tH/bln9jBXzaKWAEqJV0CBxs5TwM8EvUPHaa8S86vN303MVWOsl3goDBHPWSoQ9c0kQmCKljfsKNH1+ofEOHW8a9a7glZGS8fPieL/SRSs0LAhI4FDTnXs1QYtubv2+IXPZpHB4bhivRexBkYKsSrYXNjvMUbVXpVJ+N6haV72c1k2zrnv5IYBMJBYTSZx0KTkoM3vY93rU/qs7zHplc/3d2ACadhFWByrn9LUk2IWb5JywvawTQc3F0iz+lgsBmInAIemBJtft2plKIlAFOgcroigrG2XlDsAzywQECNyaI8yr2ogoh7D4qJOYmZBzQgoZAM1PAcB8sDrr1uE5CDMR+nWSSVUGUCHAs8Vd21HOE0FzNj37pX0sLp9p3K8k++xxpkmzDxK64rmTSJnDUuIgTeslui6lg92jonZXI4jqNiUuzN4IagcKMjCniMGCODoo8T4tGDprn2hRww+NrnYiCwokd9iiWrkmbRfXYGLAoZrjO1lVQKExjUy5fIkgJURmz2uGFdASwwlWx5gDVTMK7hP6ISRVsFbYNmqtZL9MQtio285PaekyzDhZmtdexCYB0SZcTmBdhvdbmAEonk8hwcHQuZN1kVqrhyKoHHsnQhQAjF7SG533Da2S4LGjx1LoZqp7XeKQLDUBmYmydG0NQHpMeR5lRIRQc1PQ2ASMQflF4YBDMt0/GFlEHeRwCcEAAAAASUVORK5CYII="},e01a:function(t,e,n){"use strict";var o=n("23e7"),i=n("83ab"),s=n("da84"),a=n("5135"),r=n("861d"),c=n("9bf2").f,A=n("e893"),l=s.Symbol;if(i&&"function"==typeof l&&(!("description"in l.prototype)||void 0!==l().description)){var u={},f=function(){var t=arguments.length<1||void 0===arguments[0]?void 0:String(arguments[0]),e=this instanceof f?new l(t):void 0===t?l():l(t);return""===t&&(u[e]=!0),e};A(f,l);var g=f.prototype=l.prototype;g.constructor=f;var d=g.toString,h="Symbol(test)"==String(l("test")),p=/^Symbol\((.*)\)[^)]+$/;c(g,"description",{configurable:!0,get:function(){var t=r(this)?this.valueOf():this,e=d.call(t);if(a(u,t))return"";var n=h?e.slice(7,-1):e.replace(p,"$1");return""===n?void 0:n}}),o({global:!0,forced:!0},{Symbol:f})}},e160:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyAgMAAABjUWAiAAAADFBMVEX6+fP8+/X+/ff///kbczPAAAACeElEQVQozxXHQUgUUQAG4P8936yzs6VvZNZmN9QxVxiF9OLBoOjtOC6rQq6ygXjI2fCQBdXBg4egtzFGdqkoI+zgBFbqkm3hQSxhFYLotOcubeKhOnVYoqQy+m4f5g5TvpX0xHLbLY9j8SMhJp+Jk4LfAUS2kVRIjILmnwGBTX42PhCVlDJQkIiy2nWAvaJ1h+oFIpJ0hMSYVbyyrgDWshcMpMyL1brPDQKWmduO+KTJ6XeXAMK9Yc3FpD7atyNwg6kt5XgFpLPhjUTFSYVn2abDiugGShwD8JTVRJVo/2ecuKtRb/qc4BK+9TboFfokog4T2Fn6Oqdnsjk90NMS76Rji6E0NmwkPBAZ4Xbkw8KoDAkAbEhkc78e9omxxgxg6qa5HvMv+UZbCV0qmHnSHKl5TxeA2XTCGWekR581mwC5crBH81PznASqB9va3TbkYAjJPLfg5uBfXaJgIgIBv9eessRIhxe7PA7kj6uUMeMaQ/OEQOYRaaHlqH2Gxwsl6E/pwVY5FH7uCypBZPKvDQyVziYBrAkMURe2MOOOxG/eQpp5PF+bFzUV5HtPj9GeiVSNZDELleifYTp9NAjsoiXg4cW+4ZORkdSMB/B74aAdjhsVakhgkugsbmqcDSLEoWp8zRjrux3tli6Q5uM3E+maT99Wy0RiP7tboiuRZle2c6CYeL2kcUc1KvPtQKucogMadKVTQOJYCeyCYlhQQ/Q7Etfd/vBygy9iqy+LyHeF46saCYvW6ingsbA9RBWtdi8GgUXW+oQx9/wP6bAAX1TWeV+CbShZDlQ9xT6SoSxZmKRAkmXb60kzEzkRF+Ccb94BGspGJoN/UzmyR4wjXHAAAAAASUVORK5CYII="},e538:function(t,e,n){var o=n("b622");e.f=o},e8b5:function(t,e,n){var o=n("c6b6");t.exports=Array.isArray||function(t){return"Array"==o(t)}},ec0f:function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyBAMAAADsEZWCAAAALVBMVEXx58b168ny6Mjz6sn06sf27Mvw5sTz6cbw5cLy58T37svv47/168v37s7t4Ltrv0//AAAEjUlEQVQ4yw2Ty2sTURxGf3dmOqmPxb0zmaStCnfmZpL6gpmbxIpUSMZGrSJkxsZiVZimNVaqMklrUnWTRq2KIDFWWx+IFrIRFxXEB4KIgqu6EBdu7M6FIPg32PW3+DhwDmBaYrK56KP4HGIsvg/uvOV0wK+qgBMlO9BujuH4DSJlOseqV5a/BEF97gt0ChyIPqBhXI9BtqtIB8vJB/LdCQ3OVjaLNX0g7+OmoI4e7nkemAqX6o8vg0yyQAyQS7IfgvFbI+6QyI3R4KELxw7kwM2ooQfyQigYnwY5MZbMlHI1DvnQVCoVcrt+R+bO7vPDif3ybNajwqAAe443dpfDsPt379VMWZzGRuqM79mQF+DUz9nt74bQ8J/O80MtVR51U02JKKmTCvTzLVf+vuxP/aHnPo9+2bW+zVsJ0Y630/CrfzX+b+UL+7O68Rczv+7lrMh5etfKXvhc2rk6KforxuoO2xB2tcxKfeXHt18rHOiHI/0RRjW/YGRDkHiwo3nzqL60o58C/bgRuaj7vk+QOwOhpnFNdjuWpKMCGP8Yapu9Ty5FTHKQLGSEFikjd9ADwP9ciaNNjc5qMH6w50AF/LKOsOYqsOG9GjKgc7ZXolqntm6fysJ6Ma6ll2CiqmOgE6O7x1wXExklbeqMYcwsmJmOoigt8SBg2WfilDSsAZJcBxDcrqtBXzFQJqZNHfscyIhoZlygAtyYAceah+elrFbI+46gEHDGiW878Kj7JpWyfhg6iyRMymV1MKBSeVpfgLHIohyTojI6sRyK1VpcqzVZeEBLOnA9unhGKUXPJDYtV9Dxuz4iA5xSkSWhCJdAiJR9PHlvfvbntbrR14FDqUNRAYDJmSnv3oKxuz5+7fiblgVJyYLTbgUM05P7LESkoXvyWNfb0aUU6FZizgQIa25VqKQZqFrk6v6BsqqIHlQmkQ9KrBhkC20/DrFsAFEEYLjM+lj2wYHXCwnNvZQR42XJ2iVK+UBXnI+OBE6oXpUUHiQ1yg0MhA03iwGbnOdQYc1CMiPIPQrCQJFH4L4BMFktAtKd9PN5gnU2Gra4KuK+V+mjtBRpAGIqDVe4wnSnajiFGO5d7smvhVQEMEYwqshrENIEaY7YeblJYtsb3QhAHWZCEKK67swwPMKw0If1Ta+6DgHmlgPzcUTSbi3rrv1Y64/BYEMPQ5SDHUOR022B4QRF6xLUPAaPX/V4IDI5N2BMwx4LqO1uO4j6uW7NvM7lATqGAxY/ZHVgoGZbu7SvkNR75x6qGSB23FdouENVwN7sCbewTdsXGrrnQ5ZZKOCOFtMTIzxlPu6eYmtL+nMFmoK7OeXajn86r9sqWbfmvHC4IagE5qfCPGZvLSq5F55hHIxJFa4/vRxHBlz0og4TojU1l/MOHJX17lybdF0mQhFO44JYUNt3UA473IXw/iPfDWtKG5oFSXIF5iU/VnyDSjxxeDk3jAXRyVyGTNB9FxH9qcFDNJpVbt2y9LytUXkK7Py6+z1RezHQqnoY8XcLimmd8dCnBhQCuaGpJCq3SoIlmYvLz8UkWhJw7T8k+Db/DYEKwgAAAABJRU5ErkJggg=="},fc0c:function(t,e,n){}}]); \ No newline at end of file diff --git a/app/src/main/assets/web/bookshelf/precache-manifest.04173d48a716bddd6fb159c907ea23ec.js b/app/src/main/assets/web/bookshelf/precache-manifest.1be475b0fa464f5fe47e53b13d56bc1a.js similarity index 70% rename from app/src/main/assets/web/bookshelf/precache-manifest.04173d48a716bddd6fb159c907ea23ec.js rename to app/src/main/assets/web/bookshelf/precache-manifest.1be475b0fa464f5fe47e53b13d56bc1a.js index 8fe7654e7..c5431a081 100644 --- a/app/src/main/assets/web/bookshelf/precache-manifest.04173d48a716bddd6fb159c907ea23ec.js +++ b/app/src/main/assets/web/bookshelf/precache-manifest.1be475b0fa464f5fe47e53b13d56bc1a.js @@ -1,10 +1,10 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([ { - "revision": "8d9fdb8415d07e6ccb43", - "url": "css/about.dbe575e1.css" + "revision": "cba8f50059529470d85c", + "url": "css/about.1cd9ab87.css" }, { - "revision": "21fa37c7c5f599610e4a", + "revision": "541d582d52204c074e10", "url": "css/app.e4c919b7.css" }, { @@ -12,7 +12,7 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([ "url": "css/chunk-vendors.8a465a1d.css" }, { - "revision": "f125263b13dcf9460fac", + "revision": "d60be8c4b24801df7253", "url": "css/detail.e03dc50b.css" }, { @@ -40,28 +40,28 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([ "url": "img/noCover.b5c48bc1.jpeg" }, { - "revision": "a62c375778d5252b73301bc267956812", + "revision": "45fde37aeed7eeacbb2c404196962348", "url": "index.html" }, { - "revision": "8d9fdb8415d07e6ccb43", - "url": "js/about.517e59ea.js" + "revision": "cba8f50059529470d85c", + "url": "js/about.a53813f7.js" }, { - "revision": "6fb055833dc45d74e12c", - "url": "js/about~detail.d5f6fddf.js" + "revision": "5998ccb313ed338c15e1", + "url": "js/about~detail.8270a871.js" }, { - "revision": "21fa37c7c5f599610e4a", - "url": "js/app.b53a207e.js" + "revision": "541d582d52204c074e10", + "url": "js/app.fd005031.js" }, { "revision": "9a65f05f9810a3ea7f46", "url": "js/chunk-vendors.3ef7796f.js" }, { - "revision": "f125263b13dcf9460fac", - "url": "js/detail.133910b4.js" + "revision": "d60be8c4b24801df7253", + "url": "js/detail.2147373a.js" }, { "revision": "b46d04eb43bc31ca0f9f95121646440d", diff --git a/app/src/main/assets/web/bookshelf/service-worker.js b/app/src/main/assets/web/bookshelf/service-worker.js index bfa399f2d..3f40362b9 100644 --- a/app/src/main/assets/web/bookshelf/service-worker.js +++ b/app/src/main/assets/web/bookshelf/service-worker.js @@ -14,7 +14,7 @@ importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js"); importScripts( - "precache-manifest.04173d48a716bddd6fb159c907ea23ec.js" + "precache-manifest.1be475b0fa464f5fe47e53b13d56bc1a.js" ); workbox.core.setCacheNameDetails({prefix: "yd-web-tool"}); From 95d7b3c44139d9ca2fa27447c7c9eaba5f2997fd Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 19 Jul 2021 17:45:27 +0800 Subject: [PATCH 08/40] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 7 ++++--- .../java/io/legado/app/model/analyzeRule/AnalyzeRule.kt | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index a16afacd3..767a79371 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -35,9 +35,7 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" project.ext.set("archivesBaseName", name + "_" + version) multiDexEnabled true - ndk { - abiFilters 'arm64-v8a' - } + javaCompileOptions { annotationProcessorOptions { arguments += [ @@ -91,6 +89,9 @@ android { dimension "mode" applicationId "io.legado.cronet" manifestPlaceholders = [APP_CHANNEL_VALUE: "cronet"] + ndk { + abiFilters 'arm64-v8a' + } } } compileOptions { diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt index b959987b3..47f60b720 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt @@ -431,7 +431,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { mode = Mode.Json ruleStr.substring(6) } - (ruleStr[1] == '.' || ruleStr[1] == '[') && ruleStr.startsWith("$.") || isJSON -> { + ruleStr.startsWith("$.") || isJSON -> { mode = Mode.Json ruleStr } From b7e89f522f96fd26c536763f6381502adf9fdab1 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 19 Jul 2021 18:21:24 +0800 Subject: [PATCH 09/40] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/model/analyzeRule/AnalyzeRule.kt | 219 ++++++++++-------- .../app/model/analyzeRule/AnalyzeUrl.kt | 8 +- 2 files changed, 126 insertions(+), 101 deletions(-) diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt index 47f60b720..9765605c5 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt @@ -25,9 +25,7 @@ import kotlin.collections.HashMap @Keep @Suppress("unused", "RegExpRedundantEscape") class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { - - var book = if (ruleData is BaseBook) ruleData else null - + var book: BaseBook? = null var chapter: BookChapter? = null var nextChapterUrl: String? = null var content: Any? = null @@ -44,12 +42,18 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { private var objectChangedJS = false private var objectChangedJP = false + init { + if (ruleData is BaseBook) { + book = ruleData + } + } + @JvmOverloads fun setContent(content: Any?, baseUrl: String? = null): AnalyzeRule { - if (content == null) throw AssertionError("内容不可空(Content cannot be null)") + if (content == null) throw AssertionError("Content cannot be null") this.content = content - isJSON = content.toString().isJson() setBaseUrl(baseUrl) + isJSON = content.toString().isJson() objectChangedXP = true objectChangedJS = true objectChangedJP = true @@ -59,14 +63,16 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { fun setBaseUrl(baseUrl: String?): AnalyzeRule { baseUrl?.let { this.baseUrl = baseUrl + val urlMatcher = AnalyzeUrl.paramPattern.matcher(baseUrl) + if (urlMatcher.find()) this.baseUrl = baseUrl.substring(0, urlMatcher.start()) } return this } fun setRedirectUrl(url: String): URL? { kotlin.runCatching { - val urlMatcher = AnalyzeUrl.paramPattern.matcher(url) - redirectUrl = URL( if(urlMatcher.find())url.substring(0,urlMatcher.start()) else url) + val urlMatcher = AnalyzeUrl.paramPattern.matcher(baseUrl) + redirectUrl = URL(if (urlMatcher.find()) url.substring(0, urlMatcher.start()) else url) } return redirectUrl } @@ -122,7 +128,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { @JvmOverloads fun getStringList(rule: String?, isUrl: Boolean = false): List? { if (rule.isNullOrEmpty()) return null - val ruleList = splitSourceRule(rule, true) + val ruleList = splitSourceRule(rule) return getStringList(ruleList, isUrl) } @@ -282,7 +288,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { @Suppress("UNCHECKED_CAST") fun getElements(ruleStr: String): List { var result: Any? = null - val ruleList = splitSourceRule(ruleStr, true) + val ruleList = splitSourceRule(ruleStr) content?.let { o -> if (ruleList.isNotEmpty()) result = o for (sourceRule in ruleList) { @@ -357,44 +363,58 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { /** * 分解规则生成规则列表 */ - fun splitSourceRule(ruleStr: String?, isList: Boolean = false): List { - if (ruleStr.isNullOrEmpty()) return ArrayList() + fun splitSourceRule(ruleStr: String?, mode: Mode = Mode.Default): List { + var vRuleStr = ruleStr val ruleList = ArrayList() - var mMode: Mode = Mode.Default - var start = 0 - //仅首字符为:时为AllInOne,其实:与伪类选择器冲突,建议改成?更合理 - if (isList && ruleStr.startsWith(":")) { - mMode = Mode.Regex - isRegex = true - start = 1 + if (vRuleStr.isNullOrEmpty()) return ruleList + //检测Mode + var mMode: Mode = mode + when { + vRuleStr.startsWith("@@") -> { + vRuleStr = vRuleStr.substring(2) + } + vRuleStr.startsWith("@XPath:", true) -> { + mMode = Mode.XPath + vRuleStr = vRuleStr.substring(7) + } + vRuleStr.startsWith("@Json:", true) -> { + mMode = Mode.Json + vRuleStr = vRuleStr.substring(6) + } + vRuleStr.startsWith(":") -> { + mMode = Mode.Regex + isRegex = true + vRuleStr = vRuleStr.substring(1) + } + isRegex -> mMode = Mode.Regex + isJSON -> mMode = Mode.Json } + //拆分为规则列表 + var start = 0 var tmp: String - val jsMatcher = JS_PATTERN.matcher(ruleStr) + val jsMatcher = JS_PATTERN.matcher(vRuleStr) while (jsMatcher.find()) { if (jsMatcher.start() > start) { - tmp = ruleStr.substring(start, jsMatcher.start()).trim { it <= ' ' } - if (tmp.isNotEmpty()) { + tmp = vRuleStr.substring(start, jsMatcher.start()).trim { it <= ' ' } + if (!TextUtils.isEmpty(tmp)) { ruleList.add(SourceRule(tmp, mMode)) } } - ruleList.add(SourceRule(jsMatcher.group(2) ?: jsMatcher.group(1), Mode.Js)) + ruleList.add(SourceRule(jsMatcher.group(), Mode.Js)) start = jsMatcher.end() } - - if (ruleStr.length > start) { - tmp = ruleStr.substring(start).trim { it <= ' ' } - if (tmp.isNotEmpty()) { + if (vRuleStr.length > start) { + tmp = vRuleStr.substring(start).trim { it <= ' ' } + if (!TextUtils.isEmpty(tmp)) { ruleList.add(SourceRule(tmp, mMode)) } } - return ruleList } /** * 规则类 */ - inner class SourceRule internal constructor(ruleStr: String, mainMode: Mode = Mode.Default) { internal var mode: Mode internal var rule: String @@ -410,33 +430,39 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { init { this.mode = mainMode - rule = when { - ruleStr.startsWith("@CSS:", true) -> { - mode = Mode.Default - ruleStr - } - ruleStr.startsWith("@@") -> { - mode = Mode.Default - ruleStr.substring(2) - } - ruleStr.startsWith("@XPath:", true) -> { - mode = Mode.XPath - ruleStr.substring(7) - } - ruleStr.startsWith("/") -> {//XPath特征很明显,无需配置单独的识别标头 - mode = Mode.XPath - ruleStr - } - ruleStr.startsWith("@Json:", true) -> { - mode = Mode.Json - ruleStr.substring(6) - } - ruleStr.startsWith("$.") || isJSON -> { - mode = Mode.Json - ruleStr + if (mode == Mode.Js) { + rule = if (ruleStr.startsWith("")) { + ruleStr.substring(4, ruleStr.lastIndexOf("<")) + } else { + ruleStr.substring(4) } - else -> { - ruleStr + } else { + when { + ruleStr.startsWith("@CSS:", true) -> { + mode = Mode.Default + rule = ruleStr + } + ruleStr.startsWith("@@") -> { + mode = Mode.Default + rule = ruleStr.substring(2) + } + ruleStr.startsWith("@XPath:", true) -> { + mode = Mode.XPath + rule = ruleStr.substring(7) + } + ruleStr.startsWith("//") -> {//XPath特征很明显,无需配置单独的识别标头 + mode = Mode.XPath + rule = ruleStr + } + ruleStr.startsWith("@Json:", true) -> { + mode = Mode.Json + rule = ruleStr.substring(6) + } + ruleStr.startsWith("$.") -> { + mode = Mode.Json + rule = ruleStr + } + else -> rule = ruleStr } } //分离put @@ -445,39 +471,35 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { var start = 0 var tmp: String val evalMatcher = evalPattern.matcher(rule) - - if (evalMatcher.find()) { - if (mode != Mode.Js && mode != Mode.Regex + while (evalMatcher.find()) { + if (evalMatcher.start() > start) { + tmp = rule.substring(start, evalMatcher.start()) + if (mode != Mode.Js && mode != Mode.Regex + && start == 0 && !tmp.contains("##") + ) { + mode = Mode.Regex + } + splitRegex(tmp) + } else if (mode != Mode.Js && mode != Mode.Regex && evalMatcher.start() == 0 ) { mode = Mode.Regex } - do { - if (evalMatcher.start() > start) { - tmp = rule.substring(start, evalMatcher.start()) - if (mode != Mode.Js && mode != Mode.Regex - && start == 0 && !tmp.contains("##") - ) { - mode = Mode.Regex - } - splitRegex(tmp) + tmp = evalMatcher.group() + when { + tmp.startsWith("@get:", true) -> { + ruleType.add(getRuleType) + ruleParam.add(tmp.substring(6, tmp.lastIndex)) } - tmp = evalMatcher.group() - when { - tmp.startsWith("@get:", true) -> { - ruleType.add(getRuleType) - ruleParam.add(tmp.substring(6, tmp.lastIndex)) - } - tmp.startsWith("{{") -> { - ruleType.add(jsRuleType) - ruleParam.add(tmp.substring(2, tmp.length - 2)) - } - else -> { - splitRegex(tmp) - } + tmp.startsWith("{{") -> { + ruleType.add(jsRuleType) + ruleParam.add(tmp.substring(2, tmp.length - 2)) } - start = evalMatcher.end() - } while (evalMatcher.find()) + else -> { + splitRegex(tmp) + } + } + start = evalMatcher.end() } if (rule.length > start) { tmp = rule.substring(start) @@ -493,22 +515,19 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { var tmp: String val ruleStrArray = ruleStr.split("##") val regexMatcher = regexPattern.matcher(ruleStrArray[0]) - - if (regexMatcher.find()) { + while (regexMatcher.find()) { if (mode != Mode.Js && mode != Mode.Regex) { mode = Mode.Regex } - do { - if (regexMatcher.start() > start) { - tmp = ruleStr.substring(start, regexMatcher.start()) - ruleType.add(defaultRuleType) - ruleParam.add(tmp) - } - tmp = regexMatcher.group() - ruleType.add(tmp.substring(1).toInt()) + if (regexMatcher.start() > start) { + tmp = ruleStr.substring(start, regexMatcher.start()) + ruleType.add(defaultRuleType) ruleParam.add(tmp) - start = regexMatcher.end() - } while (regexMatcher.find()) + } + tmp = regexMatcher.group() + ruleType.add(tmp.substring(1).toInt()) + ruleParam.add(tmp) + start = regexMatcher.end() } if (ruleStr.length > start) { tmp = ruleStr.substring(start) @@ -581,10 +600,15 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { } private fun isRule(ruleStr: String): Boolean { - return ruleStr.startsWith('@') //js首个字符不可能是@,除非是装饰器,所以@开头规定为规则 - || ruleStr.startsWith("$.") - || ruleStr.startsWith("$[") - || ruleStr.startsWith("//") + return when { + ruleStr.startsWith("$.") -> true + ruleStr.startsWith("@Json:", true) -> true + ruleStr.startsWith("//") -> true + ruleStr.startsWith("@XPath:", true) -> true + ruleStr.startsWith("@CSS:", true) -> true + ruleStr.startsWith("@@") -> true + else -> false + } } } @@ -662,6 +686,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { companion object { private val putPattern = Pattern.compile("@put:(\\{[^}]+?\\})", Pattern.CASE_INSENSITIVE) + private val getPattern = Pattern.compile("@get:\\{([^}]+?)\\}", Pattern.CASE_INSENSITIVE) private val evalPattern = Pattern.compile("@get:\\{[^}]+?\\}|\\{\\{[\\w\\W]*?\\}\\}", Pattern.CASE_INSENSITIVE) private val regexPattern = Pattern.compile("\\$\\d{1,2}") diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt index bc0c18113..a89d78ec8 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt @@ -57,7 +57,7 @@ class AnalyzeUrl( init { val urlMatcher = paramPattern.matcher(baseUrl) - if(urlMatcher.find())baseUrl = baseUrl.substring(0,urlMatcher.start()) + if (urlMatcher.find()) baseUrl = baseUrl.substring(0, urlMatcher.start()) headerMapF?.let { headerMap.putAll(it) if (it.containsKey("proxy")) { @@ -144,12 +144,12 @@ class AnalyzeUrl( */ private fun initUrl() { //replaceKeyPageJs已经替换掉额外内容,此处url是基础形式,可以直接切首个‘,’之前字符串。 val urlMatcher = paramPattern.matcher(ruleUrl) - urlHasQuery = if(urlMatcher.find())ruleUrl.substring(0,urlMatcher.start()) else ruleUrl - url = NetworkUtils.getAbsoluteURL(baseUrl,urlHasQuery ) + urlHasQuery = if (urlMatcher.find()) ruleUrl.substring(0, urlMatcher.start()) else ruleUrl + url = NetworkUtils.getAbsoluteURL(baseUrl, urlHasQuery) NetworkUtils.getBaseUrl(url)?.let { baseUrl = it } - if(urlHasQuery.length != ruleUrl.length ) { + if (urlHasQuery.length != ruleUrl.length) { GSON.fromJsonObject(ruleUrl.substring(urlMatcher.end()))?.let { option -> option.method?.let { if (it.equals("POST", true)) method = RequestMethod.POST From 6cdb889c6e6476d0ae7fe9f4533c96123783c2ba Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 19 Jul 2021 18:26:34 +0800 Subject: [PATCH 10/40] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt index 9765605c5..d0ff5ba2e 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt @@ -71,7 +71,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { fun setRedirectUrl(url: String): URL? { kotlin.runCatching { - val urlMatcher = AnalyzeUrl.paramPattern.matcher(baseUrl) + val urlMatcher = AnalyzeUrl.paramPattern.matcher(url) redirectUrl = URL(if (urlMatcher.find()) url.substring(0, urlMatcher.start()) else url) } return redirectUrl From 846ce02d93118570dce1da46182c33f69cbc1563 Mon Sep 17 00:00:00 2001 From: ag2s20150909 Date: Mon, 19 Jul 2021 19:04:55 +0800 Subject: [PATCH 11/40] =?UTF-8?q?Cronet:=E4=BF=AE=E5=A4=8Dcronet=E8=AE=BF?= =?UTF-8?q?=E9=97=AE=E6=94=AF=E6=8C=81http3=E7=94=B1=E4=BA=8E=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E5=A4=B4=E7=9A=84keep-alive=E8=80=8C=E5=AF=BC?= =?UTF-8?q?=E8=87=B4400?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/help/AppConfig.kt | 4 ++-- .../java/io/legado/app/help/http/cronet/CronetHelper.kt | 7 +++++-- app/src/main/res/xml/network_security_config.xml | 7 +++++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/AppConfig.kt b/app/src/main/java/io/legado/app/help/AppConfig.kt index d1141276b..97a43f359 100644 --- a/app/src/main/java/io/legado/app/help/AppConfig.kt +++ b/app/src/main/java/io/legado/app/help/AppConfig.kt @@ -231,8 +231,8 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener { private fun getPrefUserAgent(): String { val ua = appCtx.getPrefString(PreferKey.userAgent) if (ua.isNullOrBlank()) { - return "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36" - } + return "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" + } return ua } } diff --git a/app/src/main/java/io/legado/app/help/http/cronet/CronetHelper.kt b/app/src/main/java/io/legado/app/help/http/cronet/CronetHelper.kt index 8229f46cd..b6d50f116 100644 --- a/app/src/main/java/io/legado/app/help/http/cronet/CronetHelper.kt +++ b/app/src/main/java/io/legado/app/help/http/cronet/CronetHelper.kt @@ -41,7 +41,11 @@ fun buildRequest(request: Request, callback: UrlRequest.Callback): UrlRequest { } val headers: Headers = request.headers headers.forEachIndexed { index, _ -> - requestBuilder.addHeader(headers.name(index), headers.value(index)) + val name = headers.name(index) + if (!name.equals("Keep-Alive", true)) { + requestBuilder.addHeader(name, headers.value(index)) + } + } val requestBody = request.body @@ -60,6 +64,5 @@ fun buildRequest(request: Request, callback: UrlRequest.Callback): UrlRequest { } return requestBuilder.build() - } diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml index 2c950a5b6..99b392d5a 100644 --- a/app/src/main/res/xml/network_security_config.xml +++ b/app/src/main/res/xml/network_security_config.xml @@ -3,4 +3,11 @@ + + + + + + + \ No newline at end of file From 538d40822e9efe64f8d335142bec448c059330dd Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 19 Jul 2021 19:33:45 +0800 Subject: [PATCH 12/40] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/model/analyzeRule/AnalyzeRule.kt | 221 ++++++++---------- 1 file changed, 98 insertions(+), 123 deletions(-) diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt index d0ff5ba2e..e19d90513 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt @@ -25,7 +25,9 @@ import kotlin.collections.HashMap @Keep @Suppress("unused", "RegExpRedundantEscape") class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { - var book: BaseBook? = null + + var book = if (ruleData is BaseBook) ruleData else null + var chapter: BookChapter? = null var nextChapterUrl: String? = null var content: Any? = null @@ -42,18 +44,12 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { private var objectChangedJS = false private var objectChangedJP = false - init { - if (ruleData is BaseBook) { - book = ruleData - } - } - @JvmOverloads fun setContent(content: Any?, baseUrl: String? = null): AnalyzeRule { - if (content == null) throw AssertionError("Content cannot be null") + if (content == null) throw AssertionError("内容不可空(Content cannot be null)") this.content = content - setBaseUrl(baseUrl) isJSON = content.toString().isJson() + setBaseUrl(baseUrl) objectChangedXP = true objectChangedJS = true objectChangedJP = true @@ -63,8 +59,6 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { fun setBaseUrl(baseUrl: String?): AnalyzeRule { baseUrl?.let { this.baseUrl = baseUrl - val urlMatcher = AnalyzeUrl.paramPattern.matcher(baseUrl) - if (urlMatcher.find()) this.baseUrl = baseUrl.substring(0, urlMatcher.start()) } return this } @@ -128,7 +122,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { @JvmOverloads fun getStringList(rule: String?, isUrl: Boolean = false): List? { if (rule.isNullOrEmpty()) return null - val ruleList = splitSourceRule(rule) + val ruleList = splitSourceRule(rule, true) return getStringList(ruleList, isUrl) } @@ -288,7 +282,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { @Suppress("UNCHECKED_CAST") fun getElements(ruleStr: String): List { var result: Any? = null - val ruleList = splitSourceRule(ruleStr) + val ruleList = splitSourceRule(ruleStr, true) content?.let { o -> if (ruleList.isNotEmpty()) result = o for (sourceRule in ruleList) { @@ -363,60 +357,48 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { /** * 分解规则生成规则列表 */ - fun splitSourceRule(ruleStr: String?, mode: Mode = Mode.Default): List { - var vRuleStr = ruleStr + fun splitSourceRule(ruleStr: String?, isList: Boolean = false): List { + if (ruleStr.isNullOrEmpty()) return ArrayList() val ruleList = ArrayList() - if (vRuleStr.isNullOrEmpty()) return ruleList - //检测Mode - var mMode: Mode = mode - when { - vRuleStr.startsWith("@@") -> { - vRuleStr = vRuleStr.substring(2) - } - vRuleStr.startsWith("@XPath:", true) -> { - mMode = Mode.XPath - vRuleStr = vRuleStr.substring(7) - } - vRuleStr.startsWith("@Json:", true) -> { - mMode = Mode.Json - vRuleStr = vRuleStr.substring(6) - } - vRuleStr.startsWith(":") -> { - mMode = Mode.Regex - isRegex = true - vRuleStr = vRuleStr.substring(1) - } - isRegex -> mMode = Mode.Regex - isJSON -> mMode = Mode.Json - } - //拆分为规则列表 + var mMode: Mode = Mode.Default var start = 0 + //仅首字符为:时为AllInOne,其实:与伪类选择器冲突,建议改成?更合理 + if (isList && ruleStr.startsWith(":")) { + mMode = Mode.Regex + isRegex = true + start = 1 + } var tmp: String - val jsMatcher = JS_PATTERN.matcher(vRuleStr) + val jsMatcher = JS_PATTERN.matcher(ruleStr) while (jsMatcher.find()) { if (jsMatcher.start() > start) { - tmp = vRuleStr.substring(start, jsMatcher.start()).trim { it <= ' ' } - if (!TextUtils.isEmpty(tmp)) { + tmp = ruleStr.substring(start, jsMatcher.start()).trim { it <= ' ' } + if (tmp.isNotEmpty()) { ruleList.add(SourceRule(tmp, mMode)) } } - ruleList.add(SourceRule(jsMatcher.group(), Mode.Js)) + ruleList.add(SourceRule(jsMatcher.group(2) ?: jsMatcher.group(1), Mode.Js)) start = jsMatcher.end() } - if (vRuleStr.length > start) { - tmp = vRuleStr.substring(start).trim { it <= ' ' } - if (!TextUtils.isEmpty(tmp)) { + + if (ruleStr.length > start) { + tmp = ruleStr.substring(start).trim { it <= ' ' } + if (tmp.isNotEmpty()) { ruleList.add(SourceRule(tmp, mMode)) } } + return ruleList } /** * 规则类 */ - inner class SourceRule internal constructor(ruleStr: String, mainMode: Mode = Mode.Default) { - internal var mode: Mode + + inner class SourceRule internal constructor( + ruleStr: String, + internal var mode: Mode = Mode.Default + ) { internal var rule: String internal var replaceRegex = "" internal var replacement = "" @@ -429,41 +411,33 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { private val defaultRuleType = 0 init { - this.mode = mainMode - if (mode == Mode.Js) { - rule = if (ruleStr.startsWith("")) { - ruleStr.substring(4, ruleStr.lastIndexOf("<")) - } else { - ruleStr.substring(4) + rule = when { + mode == Mode.Js -> ruleStr + ruleStr.startsWith("@CSS:", true) -> { + mode = Mode.Default + ruleStr } - } else { - when { - ruleStr.startsWith("@CSS:", true) -> { - mode = Mode.Default - rule = ruleStr - } - ruleStr.startsWith("@@") -> { - mode = Mode.Default - rule = ruleStr.substring(2) - } - ruleStr.startsWith("@XPath:", true) -> { - mode = Mode.XPath - rule = ruleStr.substring(7) - } - ruleStr.startsWith("//") -> {//XPath特征很明显,无需配置单独的识别标头 - mode = Mode.XPath - rule = ruleStr - } - ruleStr.startsWith("@Json:", true) -> { - mode = Mode.Json - rule = ruleStr.substring(6) - } - ruleStr.startsWith("$.") -> { - mode = Mode.Json - rule = ruleStr - } - else -> rule = ruleStr + ruleStr.startsWith("@@") -> { + mode = Mode.Default + ruleStr.substring(2) + } + ruleStr.startsWith("@XPath:", true) -> { + mode = Mode.XPath + ruleStr.substring(7) + } + ruleStr.startsWith("/") -> {//XPath特征很明显,无需配置单独的识别标头 + mode = Mode.XPath + ruleStr + } + ruleStr.startsWith("@Json:", true) -> { + mode = Mode.Json + ruleStr.substring(6) } + isJSON || ruleStr.startsWith("$.") || ruleStr.startsWith("$[") -> { + mode = Mode.Json + ruleStr + } + else -> ruleStr } //分离put rule = splitPutRule(rule, putMap) @@ -471,35 +445,39 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { var start = 0 var tmp: String val evalMatcher = evalPattern.matcher(rule) - while (evalMatcher.find()) { - if (evalMatcher.start() > start) { - tmp = rule.substring(start, evalMatcher.start()) - if (mode != Mode.Js && mode != Mode.Regex - && start == 0 && !tmp.contains("##") - ) { - mode = Mode.Regex - } - splitRegex(tmp) - } else if (mode != Mode.Js && mode != Mode.Regex + + if (evalMatcher.find()) { + if (mode != Mode.Js && mode != Mode.Regex && evalMatcher.start() == 0 ) { mode = Mode.Regex } - tmp = evalMatcher.group() - when { - tmp.startsWith("@get:", true) -> { - ruleType.add(getRuleType) - ruleParam.add(tmp.substring(6, tmp.lastIndex)) - } - tmp.startsWith("{{") -> { - ruleType.add(jsRuleType) - ruleParam.add(tmp.substring(2, tmp.length - 2)) - } - else -> { + do { + if (evalMatcher.start() > start) { + tmp = rule.substring(start, evalMatcher.start()) + if (mode != Mode.Js && mode != Mode.Regex + && start == 0 && !tmp.contains("##") + ) { + mode = Mode.Regex + } splitRegex(tmp) } - } - start = evalMatcher.end() + tmp = evalMatcher.group() + when { + tmp.startsWith("@get:", true) -> { + ruleType.add(getRuleType) + ruleParam.add(tmp.substring(6, tmp.lastIndex)) + } + tmp.startsWith("{{") -> { + ruleType.add(jsRuleType) + ruleParam.add(tmp.substring(2, tmp.length - 2)) + } + else -> { + splitRegex(tmp) + } + } + start = evalMatcher.end() + } while (evalMatcher.find()) } if (rule.length > start) { tmp = rule.substring(start) @@ -515,19 +493,22 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { var tmp: String val ruleStrArray = ruleStr.split("##") val regexMatcher = regexPattern.matcher(ruleStrArray[0]) - while (regexMatcher.find()) { + + if (regexMatcher.find()) { if (mode != Mode.Js && mode != Mode.Regex) { mode = Mode.Regex } - if (regexMatcher.start() > start) { - tmp = ruleStr.substring(start, regexMatcher.start()) - ruleType.add(defaultRuleType) + do { + if (regexMatcher.start() > start) { + tmp = ruleStr.substring(start, regexMatcher.start()) + ruleType.add(defaultRuleType) + ruleParam.add(tmp) + } + tmp = regexMatcher.group() + ruleType.add(tmp.substring(1).toInt()) ruleParam.add(tmp) - } - tmp = regexMatcher.group() - ruleType.add(tmp.substring(1).toInt()) - ruleParam.add(tmp) - start = regexMatcher.end() + start = regexMatcher.end() + } while (regexMatcher.find()) } if (ruleStr.length > start) { tmp = ruleStr.substring(start) @@ -600,15 +581,10 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { } private fun isRule(ruleStr: String): Boolean { - return when { - ruleStr.startsWith("$.") -> true - ruleStr.startsWith("@Json:", true) -> true - ruleStr.startsWith("//") -> true - ruleStr.startsWith("@XPath:", true) -> true - ruleStr.startsWith("@CSS:", true) -> true - ruleStr.startsWith("@@") -> true - else -> false - } + return ruleStr.startsWith('@') //js首个字符不可能是@,除非是装饰器,所以@开头规定为规则 + || ruleStr.startsWith("$.") + || ruleStr.startsWith("$[") + || ruleStr.startsWith("//") } } @@ -686,7 +662,6 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { companion object { private val putPattern = Pattern.compile("@put:(\\{[^}]+?\\})", Pattern.CASE_INSENSITIVE) - private val getPattern = Pattern.compile("@get:\\{([^}]+?)\\}", Pattern.CASE_INSENSITIVE) private val evalPattern = Pattern.compile("@get:\\{[^}]+?\\}|\\{\\{[\\w\\W]*?\\}\\}", Pattern.CASE_INSENSITIVE) private val regexPattern = Pattern.compile("\\$\\d{1,2}") From 5d3655ed1dd28c92da663d2f6536a9a15d456b30 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 19 Jul 2021 19:43:39 +0800 Subject: [PATCH 13/40] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/help/JsExtensions.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/io/legado/app/help/JsExtensions.kt b/app/src/main/java/io/legado/app/help/JsExtensions.kt index 56326e382..e01fe20e4 100644 --- a/app/src/main/java/io/legado/app/help/JsExtensions.kt +++ b/app/src/main/java/io/legado/app/help/JsExtensions.kt @@ -85,6 +85,8 @@ interface JsExtensions { /** * 实现16进制字符串转文件 + * @param content 需要转成文件的16进制字符串 + * @param url 通过url里的参数来判断文件类型 * @return 相对路径 */ fun downloadFile(content: String, url: String): String { From 6ba9cf84e30134c3d3219836d93db708d901b6d5 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 19 Jul 2021 19:51:32 +0800 Subject: [PATCH 14/40] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt index e19d90513..bff9d21f3 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt @@ -412,7 +412,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { init { rule = when { - mode == Mode.Js -> ruleStr + mode == Mode.Js || mode == Mode.Regex -> ruleStr ruleStr.startsWith("@CSS:", true) -> { mode = Mode.Default ruleStr From 4ad8e7a1bd37fa38d756944d38ba189f0053d14e Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 19 Jul 2021 19:53:03 +0800 Subject: [PATCH 15/40] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/utils/HtmlFormatter.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt index a88a59995..cae9b2251 100644 --- a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt +++ b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt @@ -38,7 +38,7 @@ object HtmlFormatter { var param = "" sb.append( keepImgHtml.substring(appendPos, matcher.start()), "" ) appendPos = matcher.end() From 755dc574858fd1f960a1140d417793a7e99c0e27 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 19 Jul 2021 19:57:54 +0800 Subject: [PATCH 16/40] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/model/analyzeRule/AnalyzeRule.kt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt index bff9d21f3..a727ce40c 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt @@ -413,6 +413,14 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { init { rule = when { mode == Mode.Js || mode == Mode.Regex -> ruleStr + isRegex -> { + mode = Mode.Regex + ruleStr + } + isJSON || ruleStr.startsWith("$.") || ruleStr.startsWith("$[") -> { + mode = Mode.Json + ruleStr + } ruleStr.startsWith("@CSS:", true) -> { mode = Mode.Default ruleStr @@ -433,10 +441,6 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { mode = Mode.Json ruleStr.substring(6) } - isJSON || ruleStr.startsWith("$.") || ruleStr.startsWith("$[") -> { - mode = Mode.Json - ruleStr - } else -> ruleStr } //分离put From 3de7ec5e3da281a4f6c1fb98b6856b9c89557569 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 19 Jul 2021 19:59:00 +0800 Subject: [PATCH 17/40] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/model/analyzeRule/AnalyzeRule.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt index a727ce40c..473f3a145 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt @@ -413,14 +413,6 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { init { rule = when { mode == Mode.Js || mode == Mode.Regex -> ruleStr - isRegex -> { - mode = Mode.Regex - ruleStr - } - isJSON || ruleStr.startsWith("$.") || ruleStr.startsWith("$[") -> { - mode = Mode.Json - ruleStr - } ruleStr.startsWith("@CSS:", true) -> { mode = Mode.Default ruleStr @@ -441,6 +433,14 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { mode = Mode.Json ruleStr.substring(6) } + isJSON || ruleStr.startsWith("$.") || ruleStr.startsWith("$[") -> { + mode = Mode.Json + ruleStr + } + isRegex -> { + mode = Mode.Regex + ruleStr + } else -> ruleStr } //分离put From 8629f75e5d40be4542992520fa7f18afe1ba8b70 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 19 Jul 2021 20:14:23 +0800 Subject: [PATCH 18/40] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/model/analyzeRule/AnalyzeRule.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt index 473f3a145..e205e4e40 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt @@ -367,6 +367,8 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { mMode = Mode.Regex isRegex = true start = 1 + } else if (isRegex) { + mMode = Mode.Regex } var tmp: String val jsMatcher = JS_PATTERN.matcher(ruleStr) @@ -437,10 +439,6 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { mode = Mode.Json ruleStr } - isRegex -> { - mode = Mode.Regex - ruleStr - } else -> ruleStr } //分离put From 12b0bc5dcacba613aaa0baa5576dea6ca0fc3ef5 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 19 Jul 2021 20:46:20 +0800 Subject: [PATCH 19/40] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/utils/HtmlFormatter.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt index cae9b2251..3fe6f8d6f 100644 --- a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt +++ b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt @@ -26,10 +26,8 @@ object HtmlFormatter { fun formatKeepImg(html: String?, redirectUrl: URL?): String { html ?: return "" - val keepImgHtml = html.replace(wrapHtmlRegex, "\n") - .replace(notImgHtmlRegex, "") - .replace("\\n\\s*$|^\\s*\\n".toRegex(), "") - .replace("\\n\\s*\\n".toRegex(), "\n") + val keepImgHtml = formatKeepImg(html) + //正则的“|”处于顶端而不处于()中时,具有类似||的熔断效果,故以此机制简化原来的代码 val matcher = formatImagePattern.matcher(keepImgHtml) var appendPos = 0 @@ -38,7 +36,8 @@ object HtmlFormatter { var param = "" sb.append( keepImgHtml.substring(appendPos, matcher.start()), " Date: Mon, 19 Jul 2021 20:51:57 +0800 Subject: [PATCH 20/40] =?UTF-8?q?Revert=20"=E4=BC=98=E5=8C=96"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 12b0bc5d --- app/src/main/java/io/legado/app/utils/HtmlFormatter.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt index 3fe6f8d6f..cae9b2251 100644 --- a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt +++ b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt @@ -26,8 +26,10 @@ object HtmlFormatter { fun formatKeepImg(html: String?, redirectUrl: URL?): String { html ?: return "" - val keepImgHtml = formatKeepImg(html) - + val keepImgHtml = html.replace(wrapHtmlRegex, "\n") + .replace(notImgHtmlRegex, "") + .replace("\\n\\s*$|^\\s*\\n".toRegex(), "") + .replace("\\n\\s*\\n".toRegex(), "\n") //正则的“|”处于顶端而不处于()中时,具有类似||的熔断效果,故以此机制简化原来的代码 val matcher = formatImagePattern.matcher(keepImgHtml) var appendPos = 0 @@ -36,8 +38,7 @@ object HtmlFormatter { var param = "" sb.append( keepImgHtml.substring(appendPos, matcher.start()), " Date: Mon, 19 Jul 2021 21:20:21 +0800 Subject: [PATCH 21/40] =?UTF-8?q?api=E8=8E=B7=E5=8F=96=E6=AD=A3=E6=96=87?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/api/controller/BookController.kt | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/io/legado/app/api/controller/BookController.kt b/app/src/main/java/io/legado/app/api/controller/BookController.kt index ed64b96d4..c7b8e0ae5 100644 --- a/app/src/main/java/io/legado/app/api/controller/BookController.kt +++ b/app/src/main/java/io/legado/app/api/controller/BookController.kt @@ -6,15 +6,13 @@ import io.legado.app.constant.PreferKey import io.legado.app.data.appDb import io.legado.app.data.entities.Book import io.legado.app.help.BookHelp +import io.legado.app.help.ContentProcessor import io.legado.app.help.ImageLoader import io.legado.app.model.localBook.LocalBook import io.legado.app.model.webBook.WebBook import io.legado.app.service.help.ReadBook import io.legado.app.ui.widget.image.CoverImageView -import io.legado.app.utils.GSON -import io.legado.app.utils.cnCompare -import io.legado.app.utils.fromJsonObject -import io.legado.app.utils.getPrefInt +import io.legado.app.utils.* import kotlinx.coroutines.runBlocking import splitties.init.appCtx @@ -130,22 +128,31 @@ object BookController { val book = appDb.bookDao.getBook(bookUrl) val chapter = appDb.bookChapterDao.getChapter(bookUrl, index) if (book == null || chapter == null) { - returnData.setErrorMsg("未找到") - } else { - val content: String? = BookHelp.getContent(book, chapter) - if (content != null) { - saveBookReadIndex(book, index) - returnData.setData(content) - } else { - appDb.bookSourceDao.getBookSource(book.origin)?.let { source -> - runBlocking { - WebBook(source).getContentAwait(this, book, chapter) - }.let { - saveBookReadIndex(book, index) - returnData.setData(it) - } - } ?: returnData.setErrorMsg("未找到书源") + return returnData.setErrorMsg("未找到") + } + var content: String? = BookHelp.getContent(book, chapter) + if (content != null) { + val contentProcessor = ContentProcessor.get(book.name, book.origin) + saveBookReadIndex(book, index) + return returnData.setData( + contentProcessor.getContent(book, chapter.title, content) + .joinToString("\n") + ) + } + val bookSource = appDb.bookSourceDao.getBookSource(book.origin) + ?: return returnData.setErrorMsg("未找到书源") + try { + content = runBlocking { + WebBook(bookSource).getContentAwait(this, book, chapter) } + val contentProcessor = ContentProcessor.get(book.name, book.origin) + saveBookReadIndex(book, index) + returnData.setData( + contentProcessor.getContent(book, chapter.title, content) + .joinToString("\n") + ) + } catch (e: Exception) { + returnData.setErrorMsg(e.msg) } return returnData } From db1fc2ee1973072ce8d5e3da1a5df9eba75be8b1 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 19 Jul 2021 22:37:05 +0800 Subject: [PATCH 22/40] =?UTF-8?q?=E9=9D=9E=E5=85=B3=E9=94=AE=E8=A7=84?= =?UTF-8?q?=E5=88=99=E6=B7=BB=E5=8A=A0try=E9=98=B2=E6=AD=A2=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E4=B8=AD=E6=96=AD=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/model/webBook/BookInfo.kt | 56 +++++++++++++------ .../io/legado/app/model/webBook/BookList.kt | 43 ++++++++++---- 2 files changed, 70 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/io/legado/app/model/webBook/BookInfo.kt b/app/src/main/java/io/legado/app/model/webBook/BookInfo.kt index bb3e99a39..df12185fc 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookInfo.kt +++ b/app/src/main/java/io/legado/app/model/webBook/BookInfo.kt @@ -73,36 +73,56 @@ object BookInfo { } scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取分类") - analyzeRule.getStringList(infoRule.kind) - ?.joinToString(",") - ?.let { - if (it.isNotEmpty()) book.kind = it - } - Debug.log(bookSource.bookSourceUrl, "└${book.kind}") + try { + analyzeRule.getStringList(infoRule.kind) + ?.joinToString(",") + ?.let { + if (it.isNotEmpty()) book.kind = it + } + Debug.log(bookSource.bookSourceUrl, "└${book.kind}") + } catch (e: Exception) { + Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}") + } scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取字数") - wordCountFormat(analyzeRule.getString(infoRule.wordCount)).let { - if (it.isNotEmpty()) book.wordCount = it + try { + wordCountFormat(analyzeRule.getString(infoRule.wordCount)).let { + if (it.isNotEmpty()) book.wordCount = it + } + Debug.log(bookSource.bookSourceUrl, "└${book.wordCount}") + } catch (e: Exception) { + Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}") } - Debug.log(bookSource.bookSourceUrl, "└${book.wordCount}") scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取最新章节") - analyzeRule.getString(infoRule.lastChapter).let { - if (it.isNotEmpty()) book.latestChapterTitle = it + try { + analyzeRule.getString(infoRule.lastChapter).let { + if (it.isNotEmpty()) book.latestChapterTitle = it + } + Debug.log(bookSource.bookSourceUrl, "└${book.latestChapterTitle}") + } catch (e: Exception) { + Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}") } - Debug.log(bookSource.bookSourceUrl, "└${book.latestChapterTitle}") scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取简介") - analyzeRule.getString(infoRule.intro).let { - if (it.isNotEmpty()) book.intro = HtmlFormatter.format(it) + try { + analyzeRule.getString(infoRule.intro).let { + if (it.isNotEmpty()) book.intro = HtmlFormatter.format(it) + } + Debug.log(bookSource.bookSourceUrl, "└${book.intro}") + } catch (e: Exception) { + Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}") } - Debug.log(bookSource.bookSourceUrl, "└${book.intro}") scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取封面链接") - analyzeRule.getString(infoRule.coverUrl).let { - if (it.isNotEmpty()) book.coverUrl = NetworkUtils.getAbsoluteURL(redirectUrl, it) + try { + analyzeRule.getString(infoRule.coverUrl).let { + if (it.isNotEmpty()) book.coverUrl = NetworkUtils.getAbsoluteURL(redirectUrl, it) + } + Debug.log(bookSource.bookSourceUrl, "└${book.coverUrl}") + } catch (e: Exception) { + Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}") } - Debug.log(bookSource.bookSourceUrl, "└${book.coverUrl}") scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取目录链接") book.tocUrl = analyzeRule.getString(infoRule.tocUrl, true) diff --git a/app/src/main/java/io/legado/app/model/webBook/BookList.kt b/app/src/main/java/io/legado/app/model/webBook/BookList.kt index 64c0618d9..2c1faab41 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookList.kt +++ b/app/src/main/java/io/legado/app/model/webBook/BookList.kt @@ -186,26 +186,47 @@ object BookList { Debug.log(bookSource.bookSourceUrl, "└${searchBook.author}", log) scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取分类", log) - searchBook.kind = analyzeRule.getStringList(ruleKind)?.joinToString(",") - Debug.log(bookSource.bookSourceUrl, "└${searchBook.kind}", log) + try { + searchBook.kind = analyzeRule.getStringList(ruleKind)?.joinToString(",") + Debug.log(bookSource.bookSourceUrl, "└${searchBook.kind}", log) + } catch (e: Exception) { + Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}", log) + } scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取字数", log) - searchBook.wordCount = wordCountFormat(analyzeRule.getString(ruleWordCount)) - Debug.log(bookSource.bookSourceUrl, "└${searchBook.wordCount}", log) + try { + searchBook.wordCount = wordCountFormat(analyzeRule.getString(ruleWordCount)) + Debug.log(bookSource.bookSourceUrl, "└${searchBook.wordCount}", log) + } catch (e: java.lang.Exception) { + Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}", log) + } scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取最新章节", log) - searchBook.latestChapterTitle = analyzeRule.getString(ruleLastChapter) - Debug.log(bookSource.bookSourceUrl, "└${searchBook.latestChapterTitle}", log) + try { + searchBook.latestChapterTitle = analyzeRule.getString(ruleLastChapter) + Debug.log(bookSource.bookSourceUrl, "└${searchBook.latestChapterTitle}", log) + } catch (e: java.lang.Exception) { + Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}", log) + } scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取简介", log) - searchBook.intro = HtmlFormatter.format(analyzeRule.getString(ruleIntro)) - Debug.log(bookSource.bookSourceUrl, "└${searchBook.intro}", log) + try { + searchBook.intro = HtmlFormatter.format(analyzeRule.getString(ruleIntro)) + Debug.log(bookSource.bookSourceUrl, "└${searchBook.intro}", log) + } catch (e: java.lang.Exception) { + Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}", log) + } scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取封面链接", log) - analyzeRule.getString(ruleCoverUrl).let { - if (it.isNotEmpty()) searchBook.coverUrl = NetworkUtils.getAbsoluteURL(baseUrl, it) + try { + analyzeRule.getString(ruleCoverUrl).let { + if (it.isNotEmpty()) searchBook.coverUrl = + NetworkUtils.getAbsoluteURL(baseUrl, it) + } + Debug.log(bookSource.bookSourceUrl, "└${searchBook.coverUrl}", log) + } catch (e: java.lang.Exception) { + Debug.log(bookSource.bookSourceUrl, "└${e.localizedMessage}", log) } - Debug.log(bookSource.bookSourceUrl, "└${searchBook.coverUrl}", log) scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取详情页链接", log) searchBook.bookUrl = analyzeRule.getString(ruleBookUrl, true) From bfb6ee869e03242ec92b4c9120069c8d78123026 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 19 Jul 2021 22:42:22 +0800 Subject: [PATCH 23/40] =?UTF-8?q?htmlFormat=E6=81=A2=E5=A4=8D=E5=8E=9F?= =?UTF-8?q?=E6=9D=A5=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/legado/app/utils/HtmlFormatter.kt | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt index cae9b2251..52763a41f 100644 --- a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt +++ b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt @@ -22,14 +22,10 @@ object HtmlFormatter { .replace("[\\n\\s]+$".toRegex(), "") } - fun formatKeepImg(html: String?) = format(html, notImgHtmlRegex) - - fun formatKeepImg(html: String?, redirectUrl: URL?): String { + fun formatKeepImg(html: String?, redirectUrl: URL? = null): String { html ?: return "" - val keepImgHtml = html.replace(wrapHtmlRegex, "\n") - .replace(notImgHtmlRegex, "") - .replace("\\n\\s*$|^\\s*\\n".toRegex(), "") - .replace("\\n\\s*\\n".toRegex(), "\n") + val keepImgHtml = format(html, notImgHtmlRegex) + //正则的“|”处于顶端而不处于()中时,具有类似||的熔断效果,故以此机制简化原来的代码 val matcher = formatImagePattern.matcher(keepImgHtml) var appendPos = 0 @@ -38,7 +34,8 @@ object HtmlFormatter { var param = "" sb.append( keepImgHtml.substring(appendPos, matcher.start()), " Date: Mon, 19 Jul 2021 22:49:13 +0800 Subject: [PATCH 24/40] edit updateLog.md --- app/src/main/assets/updateLog.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index ac1946a60..0fcb26205 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -8,6 +8,13 @@ * 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则出现问题。先关闭替换净化并刷新,再观察是否正常。如果正常说明净化规则存在误杀,如果关闭后仍然出现相关问题,请点击源链接查看原文与正文是否相同,如果不同,再进行反馈。 * 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源! +**2021/07/19** +* 非关键规则添加try防止报错中断解析 +* 添加获取封面的api +* 获取正文api使用替换规则 +* 添加一个ronet版本,网络访问使用Chromium内核 +* web书架增加【最近一次更新书籍信息的时间】 + **2021/07/16** 1. js扩展函数添加删除本地文件方法 2. js扩展函数对于文件的读写删操作都是相对路径,只能操作阅读缓存内的文件,/android/data/{package}/cache/... From 8ef05fd3bcf29f95bfda3f3e33f868a1ca369598 Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 20 Jul 2021 09:09:20 +0800 Subject: [PATCH 25/40] =?UTF-8?q?=E5=B0=86xpath=E9=9B=86=E6=88=90=E5=88=B0?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- JsoupXpath/.gitignore | 1 + JsoupXpath/build.gradle | 38 + JsoupXpath/consumer-rules.pro | 0 JsoupXpath/proguard-rules.pro | 21 + JsoupXpath/src/main/AndroidManifest.xml | 4 + .../org/seimicrawler/xpath/JXDocument.java | 152 + .../java/org/seimicrawler/xpath/JXNode.java | 130 + .../org/seimicrawler/xpath/antlr/Xpath.interp | 120 + .../org/seimicrawler/xpath/antlr/Xpath.tokens | 76 + .../xpath/antlr/XpathBaseListener.java | 535 ++ .../xpath/antlr/XpathBaseVisitor.java | 311 + .../xpath/antlr/XpathLexer.interp | 143 + .../seimicrawler/xpath/antlr/XpathLexer.java | 325 + .../xpath/antlr/XpathLexer.tokens | 76 + .../xpath/antlr/XpathListener.java | 388 ++ .../seimicrawler/xpath/antlr/XpathParser.java | 2490 ++++++++ .../xpath/antlr/XpathVisitor.java | 229 + .../seimicrawler/xpath/core/AxisSelector.java | 24 + .../seimicrawler/xpath/core/Constants.java | 10 + .../org/seimicrawler/xpath/core/Function.java | 13 + .../org/seimicrawler/xpath/core/NodeTest.java | 21 + .../org/seimicrawler/xpath/core/Scope.java | 76 + .../org/seimicrawler/xpath/core/XValue.java | 226 + .../xpath/core/XpathProcessor.java | 613 ++ .../core/axis/AncestorOrSelfSelector.java | 34 + .../xpath/core/axis/AncestorSelector.java | 33 + .../xpath/core/axis/AttributeSelector.java | 32 + .../xpath/core/axis/ChildSelector.java | 28 + .../core/axis/DescendantOrSelfSelector.java | 34 + .../xpath/core/axis/DescendantSelector.java | 37 + .../xpath/core/axis/FollowingSelector.java | 51 + .../axis/FollowingSiblingOneSelector.java | 44 + .../core/axis/FollowingSiblingSelector.java | 48 + .../xpath/core/axis/ParentSelector.java | 34 + .../xpath/core/axis/PrecedingSelector.java | 56 + .../axis/PrecedingSiblingOneSelector.java | 44 + .../core/axis/PrecedingSiblingSelector.java | 48 + .../xpath/core/axis/SelfSelector.java | 23 + .../xpath/core/function/Concat.java | 30 + .../xpath/core/function/Contains.java | 29 + .../xpath/core/function/Count.java | 29 + .../xpath/core/function/First.java | 25 + .../xpath/core/function/FormatDate.java | 47 + .../xpath/core/function/Last.java | 28 + .../seimicrawler/xpath/core/function/Not.java | 30 + .../xpath/core/function/Position.java | 28 + .../xpath/core/function/StartsWith.java | 29 + .../xpath/core/function/StringLength.java | 30 + .../xpath/core/function/SubString.java | 47 + .../xpath/core/function/SubStringAfter.java | 32 + .../core/function/SubStringAfterLast.java | 32 + .../xpath/core/function/SubStringBefore.java | 33 + .../core/function/SubStringBeforeLast.java | 33 + .../xpath/core/function/SubStringEx.java | 44 + .../seimicrawler/xpath/core/node/AllText.java | 44 + .../seimicrawler/xpath/core/node/Html.java | 31 + .../seimicrawler/xpath/core/node/Node.java | 45 + .../org/seimicrawler/xpath/core/node/Num.java | 51 + .../xpath/core/node/OuterHtml.java | 31 + .../seimicrawler/xpath/core/node/Text.java | 108 + .../xpath/exception/DoFailOnErrorHandler.java | 35 + .../xpath/exception/NoSuchAxisException.java | 28 + .../exception/NoSuchFunctionException.java | 26 + .../exception/XpathMergeValueException.java | 13 + .../xpath/exception/XpathParserException.java | 23 + .../exception/XpathSyntaxErrorException.java | 30 + .../seimicrawler/xpath/util/CommonUtil.java | 121 + .../org/seimicrawler/xpath/util/Scanner.java | 141 + JsoupXpath/src/main/resources/Xpath.g4 | 296 + .../java/org/seimicrawler/xpath/BaseTest.java | 12 + .../seimicrawler/xpath/JXDocumentTest.java | 289 + .../xpath/core/function/DateFormatTest.java | 49 + .../xpath/core/function/SubStringTest.java | 53 + .../seimicrawler/xpath/core/node/NumTest.java | 59 + .../org/seimicrawler/xpath/expr/ExprTest.java | 63 + JsoupXpath/src/test/resources/d_test.html | 5416 +++++++++++++++++ app/build.gradle | 4 +- settings.gradle | 2 +- 78 files changed, 14060 insertions(+), 4 deletions(-) create mode 100644 JsoupXpath/.gitignore create mode 100644 JsoupXpath/build.gradle create mode 100644 JsoupXpath/consumer-rules.pro create mode 100644 JsoupXpath/proguard-rules.pro create mode 100644 JsoupXpath/src/main/AndroidManifest.xml create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/JXDocument.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/JXNode.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/Xpath.interp create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/Xpath.tokens create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathBaseListener.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathBaseVisitor.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathLexer.interp create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathLexer.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathLexer.tokens create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathListener.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathParser.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathVisitor.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/AxisSelector.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/Constants.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/Function.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/NodeTest.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/Scope.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/XValue.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/XpathProcessor.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/AncestorOrSelfSelector.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/AncestorSelector.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/AttributeSelector.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/ChildSelector.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/DescendantOrSelfSelector.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/DescendantSelector.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/FollowingSelector.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/FollowingSiblingOneSelector.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/FollowingSiblingSelector.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/ParentSelector.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/PrecedingSelector.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/PrecedingSiblingOneSelector.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/PrecedingSiblingSelector.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/SelfSelector.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Concat.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Contains.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Count.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/First.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/FormatDate.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Last.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Not.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Position.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/StartsWith.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/StringLength.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubString.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringAfter.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringAfterLast.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringBefore.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringBeforeLast.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringEx.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/AllText.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Html.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Node.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Num.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/OuterHtml.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Text.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/DoFailOnErrorHandler.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/NoSuchAxisException.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/NoSuchFunctionException.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/XpathMergeValueException.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/XpathParserException.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/XpathSyntaxErrorException.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/util/CommonUtil.java create mode 100644 JsoupXpath/src/main/java/org/seimicrawler/xpath/util/Scanner.java create mode 100644 JsoupXpath/src/main/resources/Xpath.g4 create mode 100644 JsoupXpath/src/test/java/org/seimicrawler/xpath/BaseTest.java create mode 100644 JsoupXpath/src/test/java/org/seimicrawler/xpath/JXDocumentTest.java create mode 100644 JsoupXpath/src/test/java/org/seimicrawler/xpath/core/function/DateFormatTest.java create mode 100644 JsoupXpath/src/test/java/org/seimicrawler/xpath/core/function/SubStringTest.java create mode 100644 JsoupXpath/src/test/java/org/seimicrawler/xpath/core/node/NumTest.java create mode 100644 JsoupXpath/src/test/java/org/seimicrawler/xpath/expr/ExprTest.java create mode 100644 JsoupXpath/src/test/resources/d_test.html diff --git a/JsoupXpath/.gitignore b/JsoupXpath/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/JsoupXpath/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/JsoupXpath/build.gradle b/JsoupXpath/build.gradle new file mode 100644 index 000000000..3d8e01066 --- /dev/null +++ b/JsoupXpath/build.gradle @@ -0,0 +1,38 @@ +plugins { + id 'com.android.library' +} + +android { + compileSdkVersion 30 + buildToolsVersion "30.0.3" + + defaultConfig { + minSdkVersion 21 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + android { + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + api 'org.jsoup:jsoup:1.14.1' + implementation 'org.antlr:antlr4-runtime:4.7.2' + implementation 'org.apache.commons:commons-lang3:3.11' + implementation 'org.slf4j:slf4j-api:1.7.30' +} \ No newline at end of file diff --git a/JsoupXpath/consumer-rules.pro b/JsoupXpath/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/JsoupXpath/proguard-rules.pro b/JsoupXpath/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/JsoupXpath/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/JsoupXpath/src/main/AndroidManifest.xml b/JsoupXpath/src/main/AndroidManifest.xml new file mode 100644 index 000000000..2710c9ef1 --- /dev/null +++ b/JsoupXpath/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/JXDocument.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/JXDocument.java new file mode 100644 index 000000000..a67d5d085 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/JXDocument.java @@ -0,0 +1,152 @@ +package org.seimicrawler.xpath; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.tree.ParseTree; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.antlr.XpathLexer; +import org.seimicrawler.xpath.antlr.XpathParser; +import org.seimicrawler.xpath.core.XValue; +import org.seimicrawler.xpath.core.XpathProcessor; +import org.seimicrawler.xpath.exception.DoFailOnErrorHandler; +import org.seimicrawler.xpath.exception.XpathParserException; +import org.seimicrawler.xpath.exception.XpathSyntaxErrorException; + +import java.util.LinkedList; +import java.util.List; + +/* + Copyright 2014 Wang Haomiao + + 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. + */ + +/** + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + */ +public class JXDocument { + private final Elements elements; + + public JXDocument(Elements els) { + elements = els; + } + + public static JXDocument create(Document doc) { + Elements els = doc.children(); + return new JXDocument(els); + } + + public static JXDocument create(Elements els) { + return new JXDocument(els); + } + + public static JXDocument create(String html) { + Elements els = Jsoup.parse(html).children(); + return new JXDocument(els); + } + + public static JXDocument createByUrl(String url) { + Elements els; + try { + els = Jsoup.connect(url).get().children(); + } catch (Exception e) { + throw new XpathParserException("url资源获取失败", e); + } + return new JXDocument(els); + } + + public List sel(String xpath) { + List res = new LinkedList<>(); + for (JXNode node : selN(xpath)) { + if (node.isElement()) { + res.add(node.asElement()); + } else { + res.add(node.toString()); + } + } + return res; + } + + public List selN(String xpath) { + List finalRes = new LinkedList<>(); + try { + CharStream input = CharStreams.fromString(xpath); + XpathLexer lexer = new XpathLexer(input); + CommonTokenStream tokens = new CommonTokenStream(lexer); + XpathParser parser = new XpathParser(tokens); + parser.setErrorHandler(new DoFailOnErrorHandler()); + ParseTree tree = parser.main(); + XpathProcessor processor = new XpathProcessor(elements); + XValue calRes = processor.visit(tree); + + if (calRes.isElements()) { + for (Element el : calRes.asElements()) { + finalRes.add(JXNode.create(el)); + } + return finalRes; + } + if (calRes.isList()) { + for (String str : calRes.asList()) { + finalRes.add(JXNode.create(str)); + } + return finalRes; + } + if (calRes.isString()) { + finalRes.add(JXNode.create(calRes.asString())); + return finalRes; + } + if (calRes.isNumber()) { + finalRes.add(JXNode.create(calRes.asDouble())); + return finalRes; + } + if (calRes.isBoolean()) { + finalRes.add(JXNode.create(calRes.asBoolean())); + return finalRes; + } + if (calRes.isDate()) { + finalRes.add(JXNode.create(calRes.asDate())); + return finalRes; + } + finalRes.add(JXNode.create(calRes.asString())); + } catch (Exception e) { + String msg = "Please check the syntax of your xpath expr or commit a "; + throw new XpathSyntaxErrorException(msg + ExceptionUtils.getRootCauseMessage(e), e); + } + return finalRes; + } + + public Object selOne(String xpath) { + JXNode jxNode = selNOne(xpath); + if (jxNode != null) { + if (jxNode.isElement()) { + return jxNode.asElement(); + } else { + return jxNode.toString(); + } + } + return null; + } + + public JXNode selNOne(String xpath) { + List jxNodeList = selN(xpath); + if (jxNodeList != null && jxNodeList.size() > 0) { + return jxNodeList.get(0); + } + return null; + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/JXNode.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/JXNode.java new file mode 100644 index 000000000..255a8dde1 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/JXNode.java @@ -0,0 +1,130 @@ +package org.seimicrawler.xpath; +/* + Copyright 2014 Wang Haomiao + + 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. + */ + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.core.Constants; + +import java.util.Date; +import java.util.List; +import java.util.Objects; + +/** + * XPath提取后的 + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2016/5/12. + */ +public class JXNode { + private final Object value; + + public JXNode(Object val) { + this.value = val; + } + + public boolean isElement() { + return value instanceof Element; + } + + public Element asElement() { + return (Element) value; + } + + public boolean isString() { + return value instanceof String; + } + + public String asString() { + if (isString()) { + return (String) value; + } else if (isElement()) { + Element e = (Element) value; + if (Objects.equals(e.tagName(), Constants.DEF_TEXT_TAG_NAME)) { + return e.ownText(); + } else { + return e.toString(); + } + } else { + return String.valueOf(value); + } + } + + public boolean isNumber() { + return value instanceof Number; + } + + public Double asDouble() { + return (Double) value; + } + + public boolean isBoolean() { + return value instanceof Boolean; + } + + public Boolean asBoolean() { + return (Boolean) value; + } + + public boolean isDate() { + return value instanceof Date; + } + + public Date asDate() { + return (Date) value; + } + + public List sel(String xpath) { + if (!isElement()) { + return null; + } + JXDocument doc = new JXDocument(new Elements(asElement())); + return doc.selN(xpath); + } + + public JXNode selOne(String xpath) { + List jxNodeList = sel(xpath); + if (jxNodeList != null && jxNodeList.size() > 0) { + return jxNodeList.get(0); + } + return null; + } + + public static JXNode create(Object val) { + return new JXNode(val); + } + + @Override + public String toString() { + return asString(); + } + + public Object value() { + if (isElement()) { + return asElement(); + } + if (isBoolean()) { + return asBoolean(); + } + if (isNumber()) { + return asDouble(); + } + if (isDate()) { + return asDate(); + } + return asString(); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/Xpath.interp b/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/Xpath.interp new file mode 100644 index 000000000..6b795019e --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/Xpath.interp @@ -0,0 +1,120 @@ +token literal names: +null +'processing-instruction' +'or' +'and' +'$' +null +null +null +'/' +'//' +'(' +')' +'[' +']' +'-' +'+' +'.' +'*' +'`div`' +'`mod`' +'..' +'@' +',' +'|' +'<' +'>' +'<=' +'>=' +'=' +'!=' +'^=' +'$=' +'*=' +'~=' +'!~' +':' +'::' +'\'' +'"' +null +null +null + +token symbolic names: +null +null +null +null +null +NodeType +Number +AxisName +PATHSEP +ABRPATH +LPAR +RPAR +LBRAC +RBRAC +MINUS +PLUS +DOT +MUL +DIVISION +MODULO +DOTDOT +AT +COMMA +PIPE +LESS +MORE_ +LE +GE +EQUALITY +INEQUALITY +START_WITH +END_WITH +CONTAIN_WITH +REGEXP_WITH +REGEXP_NOT_WITH +COLON +CC +APOS +QUOT +Literal +Whitespace +NCName + +rule names: +main +locationPath +absoluteLocationPathNoroot +relativeLocationPath +step +axisSpecifier +nodeTest +predicate +abbreviatedStep +expr +primaryExpr +functionCall +unionExprNoRoot +pathExprNoRoot +filterExpr +orExpr +andExpr +equalityExpr +relationalExpr +additiveExpr +multiplicativeExpr +unaryExprNoRoot +qName +functionName +variableReference +nameTest +nCName + + +atn: +[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 43, 229, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 3, 2, 3, 2, 3, 3, 3, 3, 5, 3, 61, 10, 3, 3, 4, 3, 4, 3, 4, 3, 5, 3, 5, 3, 5, 7, 5, 69, 10, 5, 12, 5, 14, 5, 72, 11, 5, 3, 6, 3, 6, 3, 6, 7, 6, 77, 10, 6, 12, 6, 14, 6, 80, 11, 6, 3, 6, 5, 6, 83, 10, 6, 3, 7, 3, 7, 3, 7, 5, 7, 88, 10, 7, 5, 7, 90, 10, 7, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 5, 8, 100, 10, 8, 3, 9, 3, 9, 3, 9, 3, 9, 3, 10, 3, 10, 3, 11, 3, 11, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 5, 12, 118, 10, 12, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 7, 13, 125, 10, 13, 12, 13, 14, 13, 128, 11, 13, 5, 13, 130, 10, 13, 3, 13, 3, 13, 3, 14, 3, 14, 3, 14, 5, 14, 137, 10, 14, 3, 14, 3, 14, 3, 14, 5, 14, 142, 10, 14, 3, 15, 3, 15, 3, 15, 3, 15, 5, 15, 148, 10, 15, 5, 15, 150, 10, 15, 3, 16, 3, 16, 7, 16, 154, 10, 16, 12, 16, 14, 16, 157, 11, 16, 3, 17, 3, 17, 3, 17, 7, 17, 162, 10, 17, 12, 17, 14, 17, 165, 11, 17, 3, 18, 3, 18, 3, 18, 7, 18, 170, 10, 18, 12, 18, 14, 18, 173, 11, 18, 3, 19, 3, 19, 3, 19, 7, 19, 178, 10, 19, 12, 19, 14, 19, 181, 11, 19, 3, 20, 3, 20, 3, 20, 7, 20, 186, 10, 20, 12, 20, 14, 20, 189, 11, 20, 3, 21, 3, 21, 3, 21, 7, 21, 194, 10, 21, 12, 21, 14, 21, 197, 11, 21, 3, 22, 3, 22, 3, 22, 5, 22, 202, 10, 22, 3, 23, 5, 23, 205, 10, 23, 3, 23, 3, 23, 3, 24, 3, 24, 3, 24, 5, 24, 212, 10, 24, 3, 25, 3, 25, 3, 26, 3, 26, 3, 26, 3, 27, 3, 27, 3, 27, 3, 27, 3, 27, 3, 27, 5, 27, 225, 10, 27, 3, 28, 3, 28, 3, 28, 2, 2, 29, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 2, 9, 3, 2, 10, 11, 4, 2, 18, 18, 22, 22, 3, 2, 30, 31, 5, 2, 26, 27, 29, 29, 32, 36, 3, 2, 16, 17, 3, 2, 19, 21, 4, 2, 9, 9, 43, 43, 2, 230, 2, 56, 3, 2, 2, 2, 4, 60, 3, 2, 2, 2, 6, 62, 3, 2, 2, 2, 8, 65, 3, 2, 2, 2, 10, 82, 3, 2, 2, 2, 12, 89, 3, 2, 2, 2, 14, 99, 3, 2, 2, 2, 16, 101, 3, 2, 2, 2, 18, 105, 3, 2, 2, 2, 20, 107, 3, 2, 2, 2, 22, 117, 3, 2, 2, 2, 24, 119, 3, 2, 2, 2, 26, 141, 3, 2, 2, 2, 28, 149, 3, 2, 2, 2, 30, 151, 3, 2, 2, 2, 32, 158, 3, 2, 2, 2, 34, 166, 3, 2, 2, 2, 36, 174, 3, 2, 2, 2, 38, 182, 3, 2, 2, 2, 40, 190, 3, 2, 2, 2, 42, 198, 3, 2, 2, 2, 44, 204, 3, 2, 2, 2, 46, 208, 3, 2, 2, 2, 48, 213, 3, 2, 2, 2, 50, 215, 3, 2, 2, 2, 52, 224, 3, 2, 2, 2, 54, 226, 3, 2, 2, 2, 56, 57, 5, 20, 11, 2, 57, 3, 3, 2, 2, 2, 58, 61, 5, 8, 5, 2, 59, 61, 5, 6, 4, 2, 60, 58, 3, 2, 2, 2, 60, 59, 3, 2, 2, 2, 61, 5, 3, 2, 2, 2, 62, 63, 9, 2, 2, 2, 63, 64, 5, 8, 5, 2, 64, 7, 3, 2, 2, 2, 65, 70, 5, 10, 6, 2, 66, 67, 9, 2, 2, 2, 67, 69, 5, 10, 6, 2, 68, 66, 3, 2, 2, 2, 69, 72, 3, 2, 2, 2, 70, 68, 3, 2, 2, 2, 70, 71, 3, 2, 2, 2, 71, 9, 3, 2, 2, 2, 72, 70, 3, 2, 2, 2, 73, 74, 5, 12, 7, 2, 74, 78, 5, 14, 8, 2, 75, 77, 5, 16, 9, 2, 76, 75, 3, 2, 2, 2, 77, 80, 3, 2, 2, 2, 78, 76, 3, 2, 2, 2, 78, 79, 3, 2, 2, 2, 79, 83, 3, 2, 2, 2, 80, 78, 3, 2, 2, 2, 81, 83, 5, 18, 10, 2, 82, 73, 3, 2, 2, 2, 82, 81, 3, 2, 2, 2, 83, 11, 3, 2, 2, 2, 84, 85, 7, 9, 2, 2, 85, 90, 7, 38, 2, 2, 86, 88, 7, 23, 2, 2, 87, 86, 3, 2, 2, 2, 87, 88, 3, 2, 2, 2, 88, 90, 3, 2, 2, 2, 89, 84, 3, 2, 2, 2, 89, 87, 3, 2, 2, 2, 90, 13, 3, 2, 2, 2, 91, 100, 5, 52, 27, 2, 92, 93, 7, 7, 2, 2, 93, 94, 7, 12, 2, 2, 94, 100, 7, 13, 2, 2, 95, 96, 7, 3, 2, 2, 96, 97, 7, 12, 2, 2, 97, 98, 7, 41, 2, 2, 98, 100, 7, 13, 2, 2, 99, 91, 3, 2, 2, 2, 99, 92, 3, 2, 2, 2, 99, 95, 3, 2, 2, 2, 100, 15, 3, 2, 2, 2, 101, 102, 7, 14, 2, 2, 102, 103, 5, 20, 11, 2, 103, 104, 7, 15, 2, 2, 104, 17, 3, 2, 2, 2, 105, 106, 9, 3, 2, 2, 106, 19, 3, 2, 2, 2, 107, 108, 5, 32, 17, 2, 108, 21, 3, 2, 2, 2, 109, 118, 5, 50, 26, 2, 110, 111, 7, 12, 2, 2, 111, 112, 5, 20, 11, 2, 112, 113, 7, 13, 2, 2, 113, 118, 3, 2, 2, 2, 114, 118, 7, 41, 2, 2, 115, 118, 7, 8, 2, 2, 116, 118, 5, 24, 13, 2, 117, 109, 3, 2, 2, 2, 117, 110, 3, 2, 2, 2, 117, 114, 3, 2, 2, 2, 117, 115, 3, 2, 2, 2, 117, 116, 3, 2, 2, 2, 118, 23, 3, 2, 2, 2, 119, 120, 5, 48, 25, 2, 120, 129, 7, 12, 2, 2, 121, 126, 5, 20, 11, 2, 122, 123, 7, 24, 2, 2, 123, 125, 5, 20, 11, 2, 124, 122, 3, 2, 2, 2, 125, 128, 3, 2, 2, 2, 126, 124, 3, 2, 2, 2, 126, 127, 3, 2, 2, 2, 127, 130, 3, 2, 2, 2, 128, 126, 3, 2, 2, 2, 129, 121, 3, 2, 2, 2, 129, 130, 3, 2, 2, 2, 130, 131, 3, 2, 2, 2, 131, 132, 7, 13, 2, 2, 132, 25, 3, 2, 2, 2, 133, 136, 5, 28, 15, 2, 134, 135, 7, 25, 2, 2, 135, 137, 5, 26, 14, 2, 136, 134, 3, 2, 2, 2, 136, 137, 3, 2, 2, 2, 137, 142, 3, 2, 2, 2, 138, 139, 7, 10, 2, 2, 139, 140, 7, 25, 2, 2, 140, 142, 5, 26, 14, 2, 141, 133, 3, 2, 2, 2, 141, 138, 3, 2, 2, 2, 142, 27, 3, 2, 2, 2, 143, 150, 5, 4, 3, 2, 144, 147, 5, 30, 16, 2, 145, 146, 9, 2, 2, 2, 146, 148, 5, 8, 5, 2, 147, 145, 3, 2, 2, 2, 147, 148, 3, 2, 2, 2, 148, 150, 3, 2, 2, 2, 149, 143, 3, 2, 2, 2, 149, 144, 3, 2, 2, 2, 150, 29, 3, 2, 2, 2, 151, 155, 5, 22, 12, 2, 152, 154, 5, 16, 9, 2, 153, 152, 3, 2, 2, 2, 154, 157, 3, 2, 2, 2, 155, 153, 3, 2, 2, 2, 155, 156, 3, 2, 2, 2, 156, 31, 3, 2, 2, 2, 157, 155, 3, 2, 2, 2, 158, 163, 5, 34, 18, 2, 159, 160, 7, 4, 2, 2, 160, 162, 5, 34, 18, 2, 161, 159, 3, 2, 2, 2, 162, 165, 3, 2, 2, 2, 163, 161, 3, 2, 2, 2, 163, 164, 3, 2, 2, 2, 164, 33, 3, 2, 2, 2, 165, 163, 3, 2, 2, 2, 166, 171, 5, 36, 19, 2, 167, 168, 7, 5, 2, 2, 168, 170, 5, 36, 19, 2, 169, 167, 3, 2, 2, 2, 170, 173, 3, 2, 2, 2, 171, 169, 3, 2, 2, 2, 171, 172, 3, 2, 2, 2, 172, 35, 3, 2, 2, 2, 173, 171, 3, 2, 2, 2, 174, 179, 5, 38, 20, 2, 175, 176, 9, 4, 2, 2, 176, 178, 5, 38, 20, 2, 177, 175, 3, 2, 2, 2, 178, 181, 3, 2, 2, 2, 179, 177, 3, 2, 2, 2, 179, 180, 3, 2, 2, 2, 180, 37, 3, 2, 2, 2, 181, 179, 3, 2, 2, 2, 182, 187, 5, 40, 21, 2, 183, 184, 9, 5, 2, 2, 184, 186, 5, 40, 21, 2, 185, 183, 3, 2, 2, 2, 186, 189, 3, 2, 2, 2, 187, 185, 3, 2, 2, 2, 187, 188, 3, 2, 2, 2, 188, 39, 3, 2, 2, 2, 189, 187, 3, 2, 2, 2, 190, 195, 5, 42, 22, 2, 191, 192, 9, 6, 2, 2, 192, 194, 5, 42, 22, 2, 193, 191, 3, 2, 2, 2, 194, 197, 3, 2, 2, 2, 195, 193, 3, 2, 2, 2, 195, 196, 3, 2, 2, 2, 196, 41, 3, 2, 2, 2, 197, 195, 3, 2, 2, 2, 198, 201, 5, 44, 23, 2, 199, 200, 9, 7, 2, 2, 200, 202, 5, 42, 22, 2, 201, 199, 3, 2, 2, 2, 201, 202, 3, 2, 2, 2, 202, 43, 3, 2, 2, 2, 203, 205, 7, 16, 2, 2, 204, 203, 3, 2, 2, 2, 204, 205, 3, 2, 2, 2, 205, 206, 3, 2, 2, 2, 206, 207, 5, 26, 14, 2, 207, 45, 3, 2, 2, 2, 208, 211, 5, 54, 28, 2, 209, 210, 7, 37, 2, 2, 210, 212, 5, 54, 28, 2, 211, 209, 3, 2, 2, 2, 211, 212, 3, 2, 2, 2, 212, 47, 3, 2, 2, 2, 213, 214, 5, 46, 24, 2, 214, 49, 3, 2, 2, 2, 215, 216, 7, 6, 2, 2, 216, 217, 5, 46, 24, 2, 217, 51, 3, 2, 2, 2, 218, 225, 7, 19, 2, 2, 219, 220, 5, 54, 28, 2, 220, 221, 7, 37, 2, 2, 221, 222, 7, 19, 2, 2, 222, 225, 3, 2, 2, 2, 223, 225, 5, 46, 24, 2, 224, 218, 3, 2, 2, 2, 224, 219, 3, 2, 2, 2, 224, 223, 3, 2, 2, 2, 225, 53, 3, 2, 2, 2, 226, 227, 9, 8, 2, 2, 227, 55, 3, 2, 2, 2, 26, 60, 70, 78, 82, 87, 89, 99, 117, 126, 129, 136, 141, 147, 149, 155, 163, 171, 179, 187, 195, 201, 204, 211, 224] \ No newline at end of file diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/Xpath.tokens b/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/Xpath.tokens new file mode 100644 index 000000000..79142cd34 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/Xpath.tokens @@ -0,0 +1,76 @@ +T__0=1 +T__1=2 +T__2=3 +T__3=4 +NodeType=5 +Number=6 +AxisName=7 +PATHSEP=8 +ABRPATH=9 +LPAR=10 +RPAR=11 +LBRAC=12 +RBRAC=13 +MINUS=14 +PLUS=15 +DOT=16 +MUL=17 +DIVISION=18 +MODULO=19 +DOTDOT=20 +AT=21 +COMMA=22 +PIPE=23 +LESS=24 +MORE_=25 +LE=26 +GE=27 +EQUALITY=28 +INEQUALITY=29 +START_WITH=30 +END_WITH=31 +CONTAIN_WITH=32 +REGEXP_WITH=33 +REGEXP_NOT_WITH=34 +COLON=35 +CC=36 +APOS=37 +QUOT=38 +Literal=39 +Whitespace=40 +NCName=41 +'processing-instruction'=1 +'or'=2 +'and'=3 +'$'=4 +'/'=8 +'//'=9 +'('=10 +')'=11 +'['=12 +']'=13 +'-'=14 +'+'=15 +'.'=16 +'*'=17 +'`div`'=18 +'`mod`'=19 +'..'=20 +'@'=21 +','=22 +'|'=23 +'<'=24 +'>'=25 +'<='=26 +'>='=27 +'='=28 +'!='=29 +'^='=30 +'$='=31 +'*='=32 +'~='=33 +'!~'=34 +':'=35 +'::'=36 +'\''=37 +'"'=38 diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathBaseListener.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathBaseListener.java new file mode 100644 index 000000000..cf05c7e57 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathBaseListener.java @@ -0,0 +1,535 @@ +// Generated from resources/Xpath.g4 by ANTLR 4.7.2 +package org.seimicrawler.xpath.antlr; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.ErrorNode; +import org.antlr.v4.runtime.tree.TerminalNode; + +/** + * This class provides an empty implementation of {@link XpathListener}, + * which can be extended to create a listener which only needs to handle a subset + * of the available methods. + */ +public class XpathBaseListener implements XpathListener { + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterMain(XpathParser.MainContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitMain(XpathParser.MainContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterLocationPath(XpathParser.LocationPathContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitLocationPath(XpathParser.LocationPathContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterAbsoluteLocationPathNoroot(XpathParser.AbsoluteLocationPathNorootContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitAbsoluteLocationPathNoroot(XpathParser.AbsoluteLocationPathNorootContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterRelativeLocationPath(XpathParser.RelativeLocationPathContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitRelativeLocationPath(XpathParser.RelativeLocationPathContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterStep(XpathParser.StepContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitStep(XpathParser.StepContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterAxisSpecifier(XpathParser.AxisSpecifierContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitAxisSpecifier(XpathParser.AxisSpecifierContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterNodeTest(XpathParser.NodeTestContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitNodeTest(XpathParser.NodeTestContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterPredicate(XpathParser.PredicateContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitPredicate(XpathParser.PredicateContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterAbbreviatedStep(XpathParser.AbbreviatedStepContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitAbbreviatedStep(XpathParser.AbbreviatedStepContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterExpr(XpathParser.ExprContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitExpr(XpathParser.ExprContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterPrimaryExpr(XpathParser.PrimaryExprContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitPrimaryExpr(XpathParser.PrimaryExprContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterFunctionCall(XpathParser.FunctionCallContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitFunctionCall(XpathParser.FunctionCallContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterUnionExprNoRoot(XpathParser.UnionExprNoRootContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitUnionExprNoRoot(XpathParser.UnionExprNoRootContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterPathExprNoRoot(XpathParser.PathExprNoRootContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitPathExprNoRoot(XpathParser.PathExprNoRootContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterFilterExpr(XpathParser.FilterExprContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitFilterExpr(XpathParser.FilterExprContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterOrExpr(XpathParser.OrExprContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitOrExpr(XpathParser.OrExprContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterAndExpr(XpathParser.AndExprContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitAndExpr(XpathParser.AndExprContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterEqualityExpr(XpathParser.EqualityExprContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitEqualityExpr(XpathParser.EqualityExprContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterRelationalExpr(XpathParser.RelationalExprContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitRelationalExpr(XpathParser.RelationalExprContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterAdditiveExpr(XpathParser.AdditiveExprContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitAdditiveExpr(XpathParser.AdditiveExprContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterMultiplicativeExpr(XpathParser.MultiplicativeExprContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitMultiplicativeExpr(XpathParser.MultiplicativeExprContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterUnaryExprNoRoot(XpathParser.UnaryExprNoRootContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitUnaryExprNoRoot(XpathParser.UnaryExprNoRootContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterQName(XpathParser.QNameContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitQName(XpathParser.QNameContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterFunctionName(XpathParser.FunctionNameContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitFunctionName(XpathParser.FunctionNameContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterVariableReference(XpathParser.VariableReferenceContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitVariableReference(XpathParser.VariableReferenceContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterNameTest(XpathParser.NameTestContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitNameTest(XpathParser.NameTestContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterNCName(XpathParser.NCNameContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitNCName(XpathParser.NCNameContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void enterEveryRule(ParserRuleContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void exitEveryRule(ParserRuleContext ctx) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void visitTerminal(TerminalNode node) { + } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override + public void visitErrorNode(ErrorNode node) { + } +} \ No newline at end of file diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathBaseVisitor.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathBaseVisitor.java new file mode 100644 index 000000000..dca29eb56 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathBaseVisitor.java @@ -0,0 +1,311 @@ +// Generated from resources/Xpath.g4 by ANTLR 4.7.2 +package org.seimicrawler.xpath.antlr; + +import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor; + +/** + * This class provides an empty implementation of {@link XpathVisitor}, + * which can be extended to create a visitor which only needs to handle a subset + * of the available methods. + * + * @param The return type of the visit operation. Use {@link Void} for + * operations with no return type. + */ +public class XpathBaseVisitor extends AbstractParseTreeVisitor implements XpathVisitor { + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitMain(XpathParser.MainContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitLocationPath(XpathParser.LocationPathContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitAbsoluteLocationPathNoroot(XpathParser.AbsoluteLocationPathNorootContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitRelativeLocationPath(XpathParser.RelativeLocationPathContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitStep(XpathParser.StepContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitAxisSpecifier(XpathParser.AxisSpecifierContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitNodeTest(XpathParser.NodeTestContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitPredicate(XpathParser.PredicateContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitAbbreviatedStep(XpathParser.AbbreviatedStepContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitExpr(XpathParser.ExprContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitPrimaryExpr(XpathParser.PrimaryExprContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitFunctionCall(XpathParser.FunctionCallContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitUnionExprNoRoot(XpathParser.UnionExprNoRootContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitPathExprNoRoot(XpathParser.PathExprNoRootContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitFilterExpr(XpathParser.FilterExprContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitOrExpr(XpathParser.OrExprContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitAndExpr(XpathParser.AndExprContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitEqualityExpr(XpathParser.EqualityExprContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitRelationalExpr(XpathParser.RelationalExprContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitAdditiveExpr(XpathParser.AdditiveExprContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitMultiplicativeExpr(XpathParser.MultiplicativeExprContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitUnaryExprNoRoot(XpathParser.UnaryExprNoRootContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitQName(XpathParser.QNameContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitFunctionName(XpathParser.FunctionNameContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitVariableReference(XpathParser.VariableReferenceContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitNameTest(XpathParser.NameTestContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override + public T visitNCName(XpathParser.NCNameContext ctx) { + return visitChildren(ctx); + } +} \ No newline at end of file diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathLexer.interp b/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathLexer.interp new file mode 100644 index 000000000..464993d51 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathLexer.interp @@ -0,0 +1,143 @@ +token literal names: +null +'processing-instruction' +'or' +'and' +'$' +null +null +null +'/' +'//' +'(' +')' +'[' +']' +'-' +'+' +'.' +'*' +'`div`' +'`mod`' +'..' +'@' +',' +'|' +'<' +'>' +'<=' +'>=' +'=' +'!=' +'^=' +'$=' +'*=' +'~=' +'!~' +':' +'::' +'\'' +'"' +null +null +null + +token symbolic names: +null +null +null +null +null +NodeType +Number +AxisName +PATHSEP +ABRPATH +LPAR +RPAR +LBRAC +RBRAC +MINUS +PLUS +DOT +MUL +DIVISION +MODULO +DOTDOT +AT +COMMA +PIPE +LESS +MORE_ +LE +GE +EQUALITY +INEQUALITY +START_WITH +END_WITH +CONTAIN_WITH +REGEXP_WITH +REGEXP_NOT_WITH +COLON +CC +APOS +QUOT +Literal +Whitespace +NCName + +rule names: +T__0 +T__1 +T__2 +T__3 +NodeType +Number +Digits +AxisName +PATHSEP +ABRPATH +LPAR +RPAR +LBRAC +RBRAC +MINUS +PLUS +DOT +MUL +DIVISION +MODULO +DOTDOT +AT +COMMA +PIPE +LESS +MORE_ +LE +GE +EQUALITY +INEQUALITY +START_WITH +END_WITH +CONTAIN_WITH +REGEXP_WITH +REGEXP_NOT_WITH +COLON +CC +APOS +QUOT +Literal +Whitespace +NCName +NCNameStartChar +NCNameChar + +channel names: +DEFAULT_TOKEN_CHANNEL +HIDDEN + +mode names: +DEFAULT_MODE + +atn: +[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 43, 499, 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, 4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4, 39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44, 9, 44, 4, 45, 9, 45, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 5, 6, 184, 10, 6, 3, 7, 3, 7, 3, 7, 5, 7, 189, 10, 7, 5, 7, 191, 10, 7, 3, 7, 3, 7, 5, 7, 195, 10, 7, 3, 8, 6, 8, 198, 10, 8, 13, 8, 14, 8, 199, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 5, 9, 379, 10, 9, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3, 12, 3, 12, 3, 13, 3, 13, 3, 14, 3, 14, 3, 15, 3, 15, 3, 16, 3, 16, 3, 17, 3, 17, 3, 18, 3, 18, 3, 19, 3, 19, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 21, 3, 21, 3, 21, 3, 21, 3, 21, 3, 21, 3, 22, 3, 22, 3, 22, 3, 23, 3, 23, 3, 24, 3, 24, 3, 25, 3, 25, 3, 26, 3, 26, 3, 27, 3, 27, 3, 28, 3, 28, 3, 28, 3, 29, 3, 29, 3, 29, 3, 30, 3, 30, 3, 31, 3, 31, 3, 31, 3, 32, 3, 32, 3, 32, 3, 33, 3, 33, 3, 33, 3, 34, 3, 34, 3, 34, 3, 35, 3, 35, 3, 35, 3, 36, 3, 36, 3, 36, 3, 37, 3, 37, 3, 38, 3, 38, 3, 38, 3, 39, 3, 39, 3, 40, 3, 40, 3, 41, 3, 41, 7, 41, 464, 10, 41, 12, 41, 14, 41, 467, 11, 41, 3, 41, 3, 41, 3, 41, 7, 41, 472, 10, 41, 12, 41, 14, 41, 475, 11, 41, 3, 41, 5, 41, 478, 10, 41, 3, 42, 6, 42, 481, 10, 42, 13, 42, 14, 42, 482, 3, 42, 3, 42, 3, 43, 3, 43, 7, 43, 489, 10, 43, 12, 43, 14, 43, 492, 11, 43, 3, 44, 3, 44, 3, 45, 3, 45, 5, 45, 498, 10, 45, 2, 2, 46, 3, 3, 5, 4, 7, 5, 9, 6, 11, 7, 13, 8, 15, 2, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 2, 89, 2, 3, 2, 7, 3, 2, 36, 36, 3, 2, 41, 41, 5, 2, 11, 12, 15, 15, 34, 34, 16, 2, 67, 92, 97, 97, 99, 124, 194, 216, 218, 248, 250, 769, 882, 895, 897, 8193, 8206, 8207, 8306, 8593, 11266, 12273, 12291, 55297, 63746, 64977, 65010, 65535, 7, 2, 47, 48, 50, 59, 185, 185, 770, 881, 8257, 8258, 2, 526, 2, 3, 3, 2, 2, 2, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3, 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 25, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 29, 3, 2, 2, 2, 2, 31, 3, 2, 2, 2, 2, 33, 3, 2, 2, 2, 2, 35, 3, 2, 2, 2, 2, 37, 3, 2, 2, 2, 2, 39, 3, 2, 2, 2, 2, 41, 3, 2, 2, 2, 2, 43, 3, 2, 2, 2, 2, 45, 3, 2, 2, 2, 2, 47, 3, 2, 2, 2, 2, 49, 3, 2, 2, 2, 2, 51, 3, 2, 2, 2, 2, 53, 3, 2, 2, 2, 2, 55, 3, 2, 2, 2, 2, 57, 3, 2, 2, 2, 2, 59, 3, 2, 2, 2, 2, 61, 3, 2, 2, 2, 2, 63, 3, 2, 2, 2, 2, 65, 3, 2, 2, 2, 2, 67, 3, 2, 2, 2, 2, 69, 3, 2, 2, 2, 2, 71, 3, 2, 2, 2, 2, 73, 3, 2, 2, 2, 2, 75, 3, 2, 2, 2, 2, 77, 3, 2, 2, 2, 2, 79, 3, 2, 2, 2, 2, 81, 3, 2, 2, 2, 2, 83, 3, 2, 2, 2, 2, 85, 3, 2, 2, 2, 3, 91, 3, 2, 2, 2, 5, 114, 3, 2, 2, 2, 7, 117, 3, 2, 2, 2, 9, 121, 3, 2, 2, 2, 11, 183, 3, 2, 2, 2, 13, 194, 3, 2, 2, 2, 15, 197, 3, 2, 2, 2, 17, 378, 3, 2, 2, 2, 19, 380, 3, 2, 2, 2, 21, 382, 3, 2, 2, 2, 23, 385, 3, 2, 2, 2, 25, 387, 3, 2, 2, 2, 27, 389, 3, 2, 2, 2, 29, 391, 3, 2, 2, 2, 31, 393, 3, 2, 2, 2, 33, 395, 3, 2, 2, 2, 35, 397, 3, 2, 2, 2, 37, 399, 3, 2, 2, 2, 39, 401, 3, 2, 2, 2, 41, 407, 3, 2, 2, 2, 43, 413, 3, 2, 2, 2, 45, 416, 3, 2, 2, 2, 47, 418, 3, 2, 2, 2, 49, 420, 3, 2, 2, 2, 51, 422, 3, 2, 2, 2, 53, 424, 3, 2, 2, 2, 55, 426, 3, 2, 2, 2, 57, 429, 3, 2, 2, 2, 59, 432, 3, 2, 2, 2, 61, 434, 3, 2, 2, 2, 63, 437, 3, 2, 2, 2, 65, 440, 3, 2, 2, 2, 67, 443, 3, 2, 2, 2, 69, 446, 3, 2, 2, 2, 71, 449, 3, 2, 2, 2, 73, 452, 3, 2, 2, 2, 75, 454, 3, 2, 2, 2, 77, 457, 3, 2, 2, 2, 79, 459, 3, 2, 2, 2, 81, 477, 3, 2, 2, 2, 83, 480, 3, 2, 2, 2, 85, 486, 3, 2, 2, 2, 87, 493, 3, 2, 2, 2, 89, 497, 3, 2, 2, 2, 91, 92, 7, 114, 2, 2, 92, 93, 7, 116, 2, 2, 93, 94, 7, 113, 2, 2, 94, 95, 7, 101, 2, 2, 95, 96, 7, 103, 2, 2, 96, 97, 7, 117, 2, 2, 97, 98, 7, 117, 2, 2, 98, 99, 7, 107, 2, 2, 99, 100, 7, 112, 2, 2, 100, 101, 7, 105, 2, 2, 101, 102, 7, 47, 2, 2, 102, 103, 7, 107, 2, 2, 103, 104, 7, 112, 2, 2, 104, 105, 7, 117, 2, 2, 105, 106, 7, 118, 2, 2, 106, 107, 7, 116, 2, 2, 107, 108, 7, 119, 2, 2, 108, 109, 7, 101, 2, 2, 109, 110, 7, 118, 2, 2, 110, 111, 7, 107, 2, 2, 111, 112, 7, 113, 2, 2, 112, 113, 7, 112, 2, 2, 113, 4, 3, 2, 2, 2, 114, 115, 7, 113, 2, 2, 115, 116, 7, 116, 2, 2, 116, 6, 3, 2, 2, 2, 117, 118, 7, 99, 2, 2, 118, 119, 7, 112, 2, 2, 119, 120, 7, 102, 2, 2, 120, 8, 3, 2, 2, 2, 121, 122, 7, 38, 2, 2, 122, 10, 3, 2, 2, 2, 123, 124, 7, 101, 2, 2, 124, 125, 7, 113, 2, 2, 125, 126, 7, 111, 2, 2, 126, 127, 7, 111, 2, 2, 127, 128, 7, 103, 2, 2, 128, 129, 7, 112, 2, 2, 129, 184, 7, 118, 2, 2, 130, 131, 7, 118, 2, 2, 131, 132, 7, 103, 2, 2, 132, 133, 7, 122, 2, 2, 133, 184, 7, 118, 2, 2, 134, 135, 7, 114, 2, 2, 135, 136, 7, 116, 2, 2, 136, 137, 7, 113, 2, 2, 137, 138, 7, 101, 2, 2, 138, 139, 7, 103, 2, 2, 139, 140, 7, 117, 2, 2, 140, 141, 7, 117, 2, 2, 141, 142, 7, 107, 2, 2, 142, 143, 7, 112, 2, 2, 143, 144, 7, 105, 2, 2, 144, 145, 7, 47, 2, 2, 145, 146, 7, 107, 2, 2, 146, 147, 7, 112, 2, 2, 147, 148, 7, 117, 2, 2, 148, 149, 7, 118, 2, 2, 149, 150, 7, 116, 2, 2, 150, 151, 7, 119, 2, 2, 151, 152, 7, 101, 2, 2, 152, 153, 7, 118, 2, 2, 153, 154, 7, 107, 2, 2, 154, 155, 7, 113, 2, 2, 155, 184, 7, 112, 2, 2, 156, 157, 7, 112, 2, 2, 157, 158, 7, 113, 2, 2, 158, 159, 7, 102, 2, 2, 159, 184, 7, 103, 2, 2, 160, 161, 7, 112, 2, 2, 161, 162, 7, 119, 2, 2, 162, 184, 7, 111, 2, 2, 163, 164, 7, 99, 2, 2, 164, 165, 7, 110, 2, 2, 165, 166, 7, 110, 2, 2, 166, 167, 7, 86, 2, 2, 167, 168, 7, 103, 2, 2, 168, 169, 7, 122, 2, 2, 169, 184, 7, 118, 2, 2, 170, 171, 7, 113, 2, 2, 171, 172, 7, 119, 2, 2, 172, 173, 7, 118, 2, 2, 173, 174, 7, 103, 2, 2, 174, 175, 7, 116, 2, 2, 175, 176, 7, 74, 2, 2, 176, 177, 7, 118, 2, 2, 177, 178, 7, 111, 2, 2, 178, 184, 7, 110, 2, 2, 179, 180, 7, 106, 2, 2, 180, 181, 7, 118, 2, 2, 181, 182, 7, 111, 2, 2, 182, 184, 7, 110, 2, 2, 183, 123, 3, 2, 2, 2, 183, 130, 3, 2, 2, 2, 183, 134, 3, 2, 2, 2, 183, 156, 3, 2, 2, 2, 183, 160, 3, 2, 2, 2, 183, 163, 3, 2, 2, 2, 183, 170, 3, 2, 2, 2, 183, 179, 3, 2, 2, 2, 184, 12, 3, 2, 2, 2, 185, 190, 5, 15, 8, 2, 186, 188, 7, 48, 2, 2, 187, 189, 5, 15, 8, 2, 188, 187, 3, 2, 2, 2, 188, 189, 3, 2, 2, 2, 189, 191, 3, 2, 2, 2, 190, 186, 3, 2, 2, 2, 190, 191, 3, 2, 2, 2, 191, 195, 3, 2, 2, 2, 192, 193, 7, 48, 2, 2, 193, 195, 5, 15, 8, 2, 194, 185, 3, 2, 2, 2, 194, 192, 3, 2, 2, 2, 195, 14, 3, 2, 2, 2, 196, 198, 4, 50, 59, 2, 197, 196, 3, 2, 2, 2, 198, 199, 3, 2, 2, 2, 199, 197, 3, 2, 2, 2, 199, 200, 3, 2, 2, 2, 200, 16, 3, 2, 2, 2, 201, 202, 7, 99, 2, 2, 202, 203, 7, 112, 2, 2, 203, 204, 7, 101, 2, 2, 204, 205, 7, 103, 2, 2, 205, 206, 7, 117, 2, 2, 206, 207, 7, 118, 2, 2, 207, 208, 7, 113, 2, 2, 208, 379, 7, 116, 2, 2, 209, 210, 7, 99, 2, 2, 210, 211, 7, 112, 2, 2, 211, 212, 7, 101, 2, 2, 212, 213, 7, 103, 2, 2, 213, 214, 7, 117, 2, 2, 214, 215, 7, 118, 2, 2, 215, 216, 7, 113, 2, 2, 216, 217, 7, 116, 2, 2, 217, 218, 7, 47, 2, 2, 218, 219, 7, 113, 2, 2, 219, 220, 7, 116, 2, 2, 220, 221, 7, 47, 2, 2, 221, 222, 7, 117, 2, 2, 222, 223, 7, 103, 2, 2, 223, 224, 7, 110, 2, 2, 224, 379, 7, 104, 2, 2, 225, 226, 7, 99, 2, 2, 226, 227, 7, 118, 2, 2, 227, 228, 7, 118, 2, 2, 228, 229, 7, 116, 2, 2, 229, 230, 7, 107, 2, 2, 230, 231, 7, 100, 2, 2, 231, 232, 7, 119, 2, 2, 232, 233, 7, 118, 2, 2, 233, 379, 7, 103, 2, 2, 234, 235, 7, 101, 2, 2, 235, 236, 7, 106, 2, 2, 236, 237, 7, 107, 2, 2, 237, 238, 7, 110, 2, 2, 238, 379, 7, 102, 2, 2, 239, 240, 7, 102, 2, 2, 240, 241, 7, 103, 2, 2, 241, 242, 7, 117, 2, 2, 242, 243, 7, 101, 2, 2, 243, 244, 7, 103, 2, 2, 244, 245, 7, 112, 2, 2, 245, 246, 7, 102, 2, 2, 246, 247, 7, 99, 2, 2, 247, 248, 7, 112, 2, 2, 248, 379, 7, 118, 2, 2, 249, 250, 7, 102, 2, 2, 250, 251, 7, 103, 2, 2, 251, 252, 7, 117, 2, 2, 252, 253, 7, 101, 2, 2, 253, 254, 7, 103, 2, 2, 254, 255, 7, 112, 2, 2, 255, 256, 7, 102, 2, 2, 256, 257, 7, 99, 2, 2, 257, 258, 7, 112, 2, 2, 258, 259, 7, 118, 2, 2, 259, 260, 7, 47, 2, 2, 260, 261, 7, 113, 2, 2, 261, 262, 7, 116, 2, 2, 262, 263, 7, 47, 2, 2, 263, 264, 7, 117, 2, 2, 264, 265, 7, 103, 2, 2, 265, 266, 7, 110, 2, 2, 266, 379, 7, 104, 2, 2, 267, 268, 7, 104, 2, 2, 268, 269, 7, 113, 2, 2, 269, 270, 7, 110, 2, 2, 270, 271, 7, 110, 2, 2, 271, 272, 7, 113, 2, 2, 272, 273, 7, 121, 2, 2, 273, 274, 7, 107, 2, 2, 274, 275, 7, 112, 2, 2, 275, 379, 7, 105, 2, 2, 276, 277, 7, 104, 2, 2, 277, 278, 7, 113, 2, 2, 278, 279, 7, 110, 2, 2, 279, 280, 7, 110, 2, 2, 280, 281, 7, 113, 2, 2, 281, 282, 7, 121, 2, 2, 282, 283, 7, 107, 2, 2, 283, 284, 7, 112, 2, 2, 284, 285, 7, 105, 2, 2, 285, 286, 7, 47, 2, 2, 286, 287, 7, 117, 2, 2, 287, 288, 7, 107, 2, 2, 288, 289, 7, 100, 2, 2, 289, 290, 7, 110, 2, 2, 290, 291, 7, 107, 2, 2, 291, 292, 7, 112, 2, 2, 292, 379, 7, 105, 2, 2, 293, 294, 7, 114, 2, 2, 294, 295, 7, 99, 2, 2, 295, 296, 7, 116, 2, 2, 296, 297, 7, 103, 2, 2, 297, 298, 7, 112, 2, 2, 298, 379, 7, 118, 2, 2, 299, 300, 7, 114, 2, 2, 300, 301, 7, 116, 2, 2, 301, 302, 7, 103, 2, 2, 302, 303, 7, 101, 2, 2, 303, 304, 7, 103, 2, 2, 304, 305, 7, 102, 2, 2, 305, 306, 7, 107, 2, 2, 306, 307, 7, 112, 2, 2, 307, 379, 7, 105, 2, 2, 308, 309, 7, 114, 2, 2, 309, 310, 7, 116, 2, 2, 310, 311, 7, 103, 2, 2, 311, 312, 7, 101, 2, 2, 312, 313, 7, 103, 2, 2, 313, 314, 7, 102, 2, 2, 314, 315, 7, 107, 2, 2, 315, 316, 7, 112, 2, 2, 316, 317, 7, 105, 2, 2, 317, 318, 7, 47, 2, 2, 318, 319, 7, 117, 2, 2, 319, 320, 7, 107, 2, 2, 320, 321, 7, 100, 2, 2, 321, 322, 7, 110, 2, 2, 322, 323, 7, 107, 2, 2, 323, 324, 7, 112, 2, 2, 324, 379, 7, 105, 2, 2, 325, 326, 7, 117, 2, 2, 326, 327, 7, 103, 2, 2, 327, 328, 7, 110, 2, 2, 328, 379, 7, 104, 2, 2, 329, 330, 7, 104, 2, 2, 330, 331, 7, 113, 2, 2, 331, 332, 7, 110, 2, 2, 332, 333, 7, 110, 2, 2, 333, 334, 7, 113, 2, 2, 334, 335, 7, 121, 2, 2, 335, 336, 7, 107, 2, 2, 336, 337, 7, 112, 2, 2, 337, 338, 7, 105, 2, 2, 338, 339, 7, 47, 2, 2, 339, 340, 7, 117, 2, 2, 340, 341, 7, 107, 2, 2, 341, 342, 7, 100, 2, 2, 342, 343, 7, 110, 2, 2, 343, 344, 7, 107, 2, 2, 344, 345, 7, 112, 2, 2, 345, 346, 7, 105, 2, 2, 346, 347, 7, 47, 2, 2, 347, 348, 7, 113, 2, 2, 348, 349, 7, 112, 2, 2, 349, 379, 7, 103, 2, 2, 350, 351, 7, 114, 2, 2, 351, 352, 7, 116, 2, 2, 352, 353, 7, 103, 2, 2, 353, 354, 7, 101, 2, 2, 354, 355, 7, 103, 2, 2, 355, 356, 7, 102, 2, 2, 356, 357, 7, 107, 2, 2, 357, 358, 7, 112, 2, 2, 358, 359, 7, 105, 2, 2, 359, 360, 7, 47, 2, 2, 360, 361, 7, 117, 2, 2, 361, 362, 7, 107, 2, 2, 362, 363, 7, 100, 2, 2, 363, 364, 7, 110, 2, 2, 364, 365, 7, 107, 2, 2, 365, 366, 7, 112, 2, 2, 366, 367, 7, 105, 2, 2, 367, 368, 7, 47, 2, 2, 368, 369, 7, 113, 2, 2, 369, 370, 7, 112, 2, 2, 370, 379, 7, 103, 2, 2, 371, 372, 7, 117, 2, 2, 372, 373, 7, 107, 2, 2, 373, 374, 7, 100, 2, 2, 374, 375, 7, 110, 2, 2, 375, 376, 7, 107, 2, 2, 376, 377, 7, 112, 2, 2, 377, 379, 7, 105, 2, 2, 378, 201, 3, 2, 2, 2, 378, 209, 3, 2, 2, 2, 378, 225, 3, 2, 2, 2, 378, 234, 3, 2, 2, 2, 378, 239, 3, 2, 2, 2, 378, 249, 3, 2, 2, 2, 378, 267, 3, 2, 2, 2, 378, 276, 3, 2, 2, 2, 378, 293, 3, 2, 2, 2, 378, 299, 3, 2, 2, 2, 378, 308, 3, 2, 2, 2, 378, 325, 3, 2, 2, 2, 378, 329, 3, 2, 2, 2, 378, 350, 3, 2, 2, 2, 378, 371, 3, 2, 2, 2, 379, 18, 3, 2, 2, 2, 380, 381, 7, 49, 2, 2, 381, 20, 3, 2, 2, 2, 382, 383, 7, 49, 2, 2, 383, 384, 7, 49, 2, 2, 384, 22, 3, 2, 2, 2, 385, 386, 7, 42, 2, 2, 386, 24, 3, 2, 2, 2, 387, 388, 7, 43, 2, 2, 388, 26, 3, 2, 2, 2, 389, 390, 7, 93, 2, 2, 390, 28, 3, 2, 2, 2, 391, 392, 7, 95, 2, 2, 392, 30, 3, 2, 2, 2, 393, 394, 7, 47, 2, 2, 394, 32, 3, 2, 2, 2, 395, 396, 7, 45, 2, 2, 396, 34, 3, 2, 2, 2, 397, 398, 7, 48, 2, 2, 398, 36, 3, 2, 2, 2, 399, 400, 7, 44, 2, 2, 400, 38, 3, 2, 2, 2, 401, 402, 7, 98, 2, 2, 402, 403, 7, 102, 2, 2, 403, 404, 7, 107, 2, 2, 404, 405, 7, 120, 2, 2, 405, 406, 7, 98, 2, 2, 406, 40, 3, 2, 2, 2, 407, 408, 7, 98, 2, 2, 408, 409, 7, 111, 2, 2, 409, 410, 7, 113, 2, 2, 410, 411, 7, 102, 2, 2, 411, 412, 7, 98, 2, 2, 412, 42, 3, 2, 2, 2, 413, 414, 7, 48, 2, 2, 414, 415, 7, 48, 2, 2, 415, 44, 3, 2, 2, 2, 416, 417, 7, 66, 2, 2, 417, 46, 3, 2, 2, 2, 418, 419, 7, 46, 2, 2, 419, 48, 3, 2, 2, 2, 420, 421, 7, 126, 2, 2, 421, 50, 3, 2, 2, 2, 422, 423, 7, 62, 2, 2, 423, 52, 3, 2, 2, 2, 424, 425, 7, 64, 2, 2, 425, 54, 3, 2, 2, 2, 426, 427, 7, 62, 2, 2, 427, 428, 7, 63, 2, 2, 428, 56, 3, 2, 2, 2, 429, 430, 7, 64, 2, 2, 430, 431, 7, 63, 2, 2, 431, 58, 3, 2, 2, 2, 432, 433, 7, 63, 2, 2, 433, 60, 3, 2, 2, 2, 434, 435, 7, 35, 2, 2, 435, 436, 7, 63, 2, 2, 436, 62, 3, 2, 2, 2, 437, 438, 7, 96, 2, 2, 438, 439, 7, 63, 2, 2, 439, 64, 3, 2, 2, 2, 440, 441, 7, 38, 2, 2, 441, 442, 7, 63, 2, 2, 442, 66, 3, 2, 2, 2, 443, 444, 7, 44, 2, 2, 444, 445, 7, 63, 2, 2, 445, 68, 3, 2, 2, 2, 446, 447, 7, 128, 2, 2, 447, 448, 7, 63, 2, 2, 448, 70, 3, 2, 2, 2, 449, 450, 7, 35, 2, 2, 450, 451, 7, 128, 2, 2, 451, 72, 3, 2, 2, 2, 452, 453, 7, 60, 2, 2, 453, 74, 3, 2, 2, 2, 454, 455, 7, 60, 2, 2, 455, 456, 7, 60, 2, 2, 456, 76, 3, 2, 2, 2, 457, 458, 7, 41, 2, 2, 458, 78, 3, 2, 2, 2, 459, 460, 7, 36, 2, 2, 460, 80, 3, 2, 2, 2, 461, 465, 7, 36, 2, 2, 462, 464, 10, 2, 2, 2, 463, 462, 3, 2, 2, 2, 464, 467, 3, 2, 2, 2, 465, 463, 3, 2, 2, 2, 465, 466, 3, 2, 2, 2, 466, 468, 3, 2, 2, 2, 467, 465, 3, 2, 2, 2, 468, 478, 7, 36, 2, 2, 469, 473, 7, 41, 2, 2, 470, 472, 10, 3, 2, 2, 471, 470, 3, 2, 2, 2, 472, 475, 3, 2, 2, 2, 473, 471, 3, 2, 2, 2, 473, 474, 3, 2, 2, 2, 474, 476, 3, 2, 2, 2, 475, 473, 3, 2, 2, 2, 476, 478, 7, 41, 2, 2, 477, 461, 3, 2, 2, 2, 477, 469, 3, 2, 2, 2, 478, 82, 3, 2, 2, 2, 479, 481, 9, 4, 2, 2, 480, 479, 3, 2, 2, 2, 481, 482, 3, 2, 2, 2, 482, 480, 3, 2, 2, 2, 482, 483, 3, 2, 2, 2, 483, 484, 3, 2, 2, 2, 484, 485, 8, 42, 2, 2, 485, 84, 3, 2, 2, 2, 486, 490, 5, 87, 44, 2, 487, 489, 5, 89, 45, 2, 488, 487, 3, 2, 2, 2, 489, 492, 3, 2, 2, 2, 490, 488, 3, 2, 2, 2, 490, 491, 3, 2, 2, 2, 491, 86, 3, 2, 2, 2, 492, 490, 3, 2, 2, 2, 493, 494, 9, 5, 2, 2, 494, 88, 3, 2, 2, 2, 495, 498, 5, 87, 44, 2, 496, 498, 9, 6, 2, 2, 497, 495, 3, 2, 2, 2, 497, 496, 3, 2, 2, 2, 498, 90, 3, 2, 2, 2, 15, 2, 183, 188, 190, 194, 199, 378, 465, 473, 477, 482, 490, 497, 3, 8, 2, 2] \ No newline at end of file diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathLexer.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathLexer.java new file mode 100644 index 000000000..78c2667a5 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathLexer.java @@ -0,0 +1,325 @@ +// Generated from resources/Xpath.g4 by ANTLR 4.7.2 +package org.seimicrawler.xpath.antlr; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.RuntimeMetaData; +import org.antlr.v4.runtime.Vocabulary; +import org.antlr.v4.runtime.VocabularyImpl; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.LexerATNSimulator; +import org.antlr.v4.runtime.atn.PredictionContextCache; +import org.antlr.v4.runtime.dfa.DFA; + +@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"}) +public class XpathLexer extends Lexer { + static { + RuntimeMetaData.checkVersion("4.7.2", RuntimeMetaData.VERSION); + } + + protected static final DFA[] _decisionToDFA; + protected static final PredictionContextCache _sharedContextCache = + new PredictionContextCache(); + public static final int + T__0 = 1, T__1 = 2, T__2 = 3, T__3 = 4, NodeType = 5, Number = 6, AxisName = 7, PATHSEP = 8, + ABRPATH = 9, LPAR = 10, RPAR = 11, LBRAC = 12, RBRAC = 13, MINUS = 14, PLUS = 15, DOT = 16, + MUL = 17, DIVISION = 18, MODULO = 19, DOTDOT = 20, AT = 21, COMMA = 22, PIPE = 23, LESS = 24, + MORE_ = 25, LE = 26, GE = 27, EQUALITY = 28, INEQUALITY = 29, START_WITH = 30, END_WITH = 31, + CONTAIN_WITH = 32, REGEXP_WITH = 33, REGEXP_NOT_WITH = 34, COLON = 35, CC = 36, + APOS = 37, QUOT = 38, Literal = 39, Whitespace = 40, NCName = 41; + public static String[] channelNames = { + "DEFAULT_TOKEN_CHANNEL", "HIDDEN" + }; + + public static String[] modeNames = { + "DEFAULT_MODE" + }; + + private static String[] makeRuleNames() { + return new String[]{ + "T__0", "T__1", "T__2", "T__3", "NodeType", "Number", "Digits", "AxisName", + "PATHSEP", "ABRPATH", "LPAR", "RPAR", "LBRAC", "RBRAC", "MINUS", "PLUS", + "DOT", "MUL", "DIVISION", "MODULO", "DOTDOT", "AT", "COMMA", "PIPE", + "LESS", "MORE_", "LE", "GE", "EQUALITY", "INEQUALITY", "START_WITH", + "END_WITH", "CONTAIN_WITH", "REGEXP_WITH", "REGEXP_NOT_WITH", "COLON", + "CC", "APOS", "QUOT", "Literal", "Whitespace", "NCName", "NCNameStartChar", + "NCNameChar" + }; + } + + public static final String[] ruleNames = makeRuleNames(); + + private static String[] makeLiteralNames() { + return new String[]{ + null, "'processing-instruction'", "'or'", "'and'", "'$'", null, null, + null, "'/'", "'//'", "'('", "')'", "'['", "']'", "'-'", "'+'", "'.'", + "'*'", "'`div`'", "'`mod`'", "'..'", "'@'", "','", "'|'", "'<'", "'>'", + "'<='", "'>='", "'='", "'!='", "'^='", "'$='", "'*='", "'~='", "'!~'", + "':'", "'::'", "'''", "'\"'" + }; + } + + private static final String[] _LITERAL_NAMES = makeLiteralNames(); + + private static String[] makeSymbolicNames() { + return new String[]{ + null, null, null, null, null, "NodeType", "Number", "AxisName", "PATHSEP", + "ABRPATH", "LPAR", "RPAR", "LBRAC", "RBRAC", "MINUS", "PLUS", "DOT", + "MUL", "DIVISION", "MODULO", "DOTDOT", "AT", "COMMA", "PIPE", "LESS", + "MORE_", "LE", "GE", "EQUALITY", "INEQUALITY", "START_WITH", "END_WITH", + "CONTAIN_WITH", "REGEXP_WITH", "REGEXP_NOT_WITH", "COLON", "CC", "APOS", + "QUOT", "Literal", "Whitespace", "NCName" + }; + } + + private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); + public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); + + /** + * @deprecated Use {@link #VOCABULARY} instead. + */ + @Deprecated + public static final String[] tokenNames; + + static { + tokenNames = new String[_SYMBOLIC_NAMES.length]; + for (int i = 0; i < tokenNames.length; i++) { + tokenNames[i] = VOCABULARY.getLiteralName(i); + if (tokenNames[i] == null) { + tokenNames[i] = VOCABULARY.getSymbolicName(i); + } + + if (tokenNames[i] == null) { + tokenNames[i] = ""; + } + } + } + + @Override + @Deprecated + public String[] getTokenNames() { + return tokenNames; + } + + @Override + + public Vocabulary getVocabulary() { + return VOCABULARY; + } + + + public XpathLexer(CharStream input) { + super(input); + _interp = new LexerATNSimulator(this, _ATN, _decisionToDFA, _sharedContextCache); + } + + @Override + public String getGrammarFileName() { + return "Xpath.g4"; + } + + @Override + public String[] getRuleNames() { + return ruleNames; + } + + @Override + public String getSerializedATN() { + return _serializedATN; + } + + @Override + public String[] getChannelNames() { + return channelNames; + } + + @Override + public String[] getModeNames() { + return modeNames; + } + + @Override + public ATN getATN() { + return _ATN; + } + + public static final String _serializedATN = + "\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2+\u01f3\b\1\4\2\t" + + "\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13" + + "\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22" + + "\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31" + + "\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36\t\36\4\37\t\37\4 \t \4!" + + "\t!\4\"\t\"\4#\t#\4$\t$\4%\t%\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t+\4" + + ",\t,\4-\t-\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2" + + "\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\3\3\3\3\3\3\4\3\4\3\4\3\4\3\5\3\5\3" + + "\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6" + + "\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3" + + "\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6" + + "\3\6\3\6\3\6\3\6\3\6\3\6\3\6\5\6\u00b8\n\6\3\7\3\7\3\7\5\7\u00bd\n\7\5" + + "\7\u00bf\n\7\3\7\3\7\5\7\u00c3\n\7\3\b\6\b\u00c6\n\b\r\b\16\b\u00c7\3" + + "\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t" + + "\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3" + + "\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t" + + "\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3" + + "\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t" + + "\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3" + + "\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t" + + "\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3" + + "\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t" + + "\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3" + + "\t\3\t\5\t\u017b\n\t\3\n\3\n\3\13\3\13\3\13\3\f\3\f\3\r\3\r\3\16\3\16" + + "\3\17\3\17\3\20\3\20\3\21\3\21\3\22\3\22\3\23\3\23\3\24\3\24\3\24\3\24" + + "\3\24\3\24\3\25\3\25\3\25\3\25\3\25\3\25\3\26\3\26\3\26\3\27\3\27\3\30" + + "\3\30\3\31\3\31\3\32\3\32\3\33\3\33\3\34\3\34\3\34\3\35\3\35\3\35\3\36" + + "\3\36\3\37\3\37\3\37\3 \3 \3 \3!\3!\3!\3\"\3\"\3\"\3#\3#\3#\3$\3$\3$\3" + + "%\3%\3&\3&\3&\3\'\3\'\3(\3(\3)\3)\7)\u01d0\n)\f)\16)\u01d3\13)\3)\3)\3" + + ")\7)\u01d8\n)\f)\16)\u01db\13)\3)\5)\u01de\n)\3*\6*\u01e1\n*\r*\16*\u01e2" + + "\3*\3*\3+\3+\7+\u01e9\n+\f+\16+\u01ec\13+\3,\3,\3-\3-\5-\u01f2\n-\2\2" + + ".\3\3\5\4\7\5\t\6\13\7\r\b\17\2\21\t\23\n\25\13\27\f\31\r\33\16\35\17" + + "\37\20!\21#\22%\23\'\24)\25+\26-\27/\30\61\31\63\32\65\33\67\349\35;\36" + + "=\37? A!C\"E#G$I%K&M\'O(Q)S*U+W\2Y\2\3\2\7\3\2$$\3\2))\5\2\13\f\17\17" + + "\"\"\20\2C\\aac|\u00c2\u00d8\u00da\u00f8\u00fa\u0301\u0372\u037f\u0381" + + "\u2001\u200e\u200f\u2072\u2191\u2c02\u2ff1\u3003\ud801\uf902\ufdd1\ufdf2" + + "\uffff\7\2/\60\62;\u00b9\u00b9\u0302\u0371\u2041\u2042\2\u020e\2\3\3\2" + + "\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\21" + + "\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2\2\31\3\2\2\2\2\33\3\2" + + "\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2\2\2\2%\3\2\2\2\2\'\3" + + "\2\2\2\2)\3\2\2\2\2+\3\2\2\2\2-\3\2\2\2\2/\3\2\2\2\2\61\3\2\2\2\2\63\3" + + "\2\2\2\2\65\3\2\2\2\2\67\3\2\2\2\29\3\2\2\2\2;\3\2\2\2\2=\3\2\2\2\2?\3" + + "\2\2\2\2A\3\2\2\2\2C\3\2\2\2\2E\3\2\2\2\2G\3\2\2\2\2I\3\2\2\2\2K\3\2\2" + + "\2\2M\3\2\2\2\2O\3\2\2\2\2Q\3\2\2\2\2S\3\2\2\2\2U\3\2\2\2\3[\3\2\2\2\5" + + "r\3\2\2\2\7u\3\2\2\2\ty\3\2\2\2\13\u00b7\3\2\2\2\r\u00c2\3\2\2\2\17\u00c5" + + "\3\2\2\2\21\u017a\3\2\2\2\23\u017c\3\2\2\2\25\u017e\3\2\2\2\27\u0181\3" + + "\2\2\2\31\u0183\3\2\2\2\33\u0185\3\2\2\2\35\u0187\3\2\2\2\37\u0189\3\2" + + "\2\2!\u018b\3\2\2\2#\u018d\3\2\2\2%\u018f\3\2\2\2\'\u0191\3\2\2\2)\u0197" + + "\3\2\2\2+\u019d\3\2\2\2-\u01a0\3\2\2\2/\u01a2\3\2\2\2\61\u01a4\3\2\2\2" + + "\63\u01a6\3\2\2\2\65\u01a8\3\2\2\2\67\u01aa\3\2\2\29\u01ad\3\2\2\2;\u01b0" + + "\3\2\2\2=\u01b2\3\2\2\2?\u01b5\3\2\2\2A\u01b8\3\2\2\2C\u01bb\3\2\2\2E" + + "\u01be\3\2\2\2G\u01c1\3\2\2\2I\u01c4\3\2\2\2K\u01c6\3\2\2\2M\u01c9\3\2" + + "\2\2O\u01cb\3\2\2\2Q\u01dd\3\2\2\2S\u01e0\3\2\2\2U\u01e6\3\2\2\2W\u01ed" + + "\3\2\2\2Y\u01f1\3\2\2\2[\\\7r\2\2\\]\7t\2\2]^\7q\2\2^_\7e\2\2_`\7g\2\2" + + "`a\7u\2\2ab\7u\2\2bc\7k\2\2cd\7p\2\2de\7i\2\2ef\7/\2\2fg\7k\2\2gh\7p\2" + + "\2hi\7u\2\2ij\7v\2\2jk\7t\2\2kl\7w\2\2lm\7e\2\2mn\7v\2\2no\7k\2\2op\7" + + "q\2\2pq\7p\2\2q\4\3\2\2\2rs\7q\2\2st\7t\2\2t\6\3\2\2\2uv\7c\2\2vw\7p\2" + + "\2wx\7f\2\2x\b\3\2\2\2yz\7&\2\2z\n\3\2\2\2{|\7e\2\2|}\7q\2\2}~\7o\2\2" + + "~\177\7o\2\2\177\u0080\7g\2\2\u0080\u0081\7p\2\2\u0081\u00b8\7v\2\2\u0082" + + "\u0083\7v\2\2\u0083\u0084\7g\2\2\u0084\u0085\7z\2\2\u0085\u00b8\7v\2\2" + + "\u0086\u0087\7r\2\2\u0087\u0088\7t\2\2\u0088\u0089\7q\2\2\u0089\u008a" + + "\7e\2\2\u008a\u008b\7g\2\2\u008b\u008c\7u\2\2\u008c\u008d\7u\2\2\u008d" + + "\u008e\7k\2\2\u008e\u008f\7p\2\2\u008f\u0090\7i\2\2\u0090\u0091\7/\2\2" + + "\u0091\u0092\7k\2\2\u0092\u0093\7p\2\2\u0093\u0094\7u\2\2\u0094\u0095" + + "\7v\2\2\u0095\u0096\7t\2\2\u0096\u0097\7w\2\2\u0097\u0098\7e\2\2\u0098" + + "\u0099\7v\2\2\u0099\u009a\7k\2\2\u009a\u009b\7q\2\2\u009b\u00b8\7p\2\2" + + "\u009c\u009d\7p\2\2\u009d\u009e\7q\2\2\u009e\u009f\7f\2\2\u009f\u00b8" + + "\7g\2\2\u00a0\u00a1\7p\2\2\u00a1\u00a2\7w\2\2\u00a2\u00b8\7o\2\2\u00a3" + + "\u00a4\7c\2\2\u00a4\u00a5\7n\2\2\u00a5\u00a6\7n\2\2\u00a6\u00a7\7V\2\2" + + "\u00a7\u00a8\7g\2\2\u00a8\u00a9\7z\2\2\u00a9\u00b8\7v\2\2\u00aa\u00ab" + + "\7q\2\2\u00ab\u00ac\7w\2\2\u00ac\u00ad\7v\2\2\u00ad\u00ae\7g\2\2\u00ae" + + "\u00af\7t\2\2\u00af\u00b0\7J\2\2\u00b0\u00b1\7v\2\2\u00b1\u00b2\7o\2\2" + + "\u00b2\u00b8\7n\2\2\u00b3\u00b4\7j\2\2\u00b4\u00b5\7v\2\2\u00b5\u00b6" + + "\7o\2\2\u00b6\u00b8\7n\2\2\u00b7{\3\2\2\2\u00b7\u0082\3\2\2\2\u00b7\u0086" + + "\3\2\2\2\u00b7\u009c\3\2\2\2\u00b7\u00a0\3\2\2\2\u00b7\u00a3\3\2\2\2\u00b7" + + "\u00aa\3\2\2\2\u00b7\u00b3\3\2\2\2\u00b8\f\3\2\2\2\u00b9\u00be\5\17\b" + + "\2\u00ba\u00bc\7\60\2\2\u00bb\u00bd\5\17\b\2\u00bc\u00bb\3\2\2\2\u00bc" + + "\u00bd\3\2\2\2\u00bd\u00bf\3\2\2\2\u00be\u00ba\3\2\2\2\u00be\u00bf\3\2" + + "\2\2\u00bf\u00c3\3\2\2\2\u00c0\u00c1\7\60\2\2\u00c1\u00c3\5\17\b\2\u00c2" + + "\u00b9\3\2\2\2\u00c2\u00c0\3\2\2\2\u00c3\16\3\2\2\2\u00c4\u00c6\4\62;" + + "\2\u00c5\u00c4\3\2\2\2\u00c6\u00c7\3\2\2\2\u00c7\u00c5\3\2\2\2\u00c7\u00c8" + + "\3\2\2\2\u00c8\20\3\2\2\2\u00c9\u00ca\7c\2\2\u00ca\u00cb\7p\2\2\u00cb" + + "\u00cc\7e\2\2\u00cc\u00cd\7g\2\2\u00cd\u00ce\7u\2\2\u00ce\u00cf\7v\2\2" + + "\u00cf\u00d0\7q\2\2\u00d0\u017b\7t\2\2\u00d1\u00d2\7c\2\2\u00d2\u00d3" + + "\7p\2\2\u00d3\u00d4\7e\2\2\u00d4\u00d5\7g\2\2\u00d5\u00d6\7u\2\2\u00d6" + + "\u00d7\7v\2\2\u00d7\u00d8\7q\2\2\u00d8\u00d9\7t\2\2\u00d9\u00da\7/\2\2" + + "\u00da\u00db\7q\2\2\u00db\u00dc\7t\2\2\u00dc\u00dd\7/\2\2\u00dd\u00de" + + "\7u\2\2\u00de\u00df\7g\2\2\u00df\u00e0\7n\2\2\u00e0\u017b\7h\2\2\u00e1" + + "\u00e2\7c\2\2\u00e2\u00e3\7v\2\2\u00e3\u00e4\7v\2\2\u00e4\u00e5\7t\2\2" + + "\u00e5\u00e6\7k\2\2\u00e6\u00e7\7d\2\2\u00e7\u00e8\7w\2\2\u00e8\u00e9" + + "\7v\2\2\u00e9\u017b\7g\2\2\u00ea\u00eb\7e\2\2\u00eb\u00ec\7j\2\2\u00ec" + + "\u00ed\7k\2\2\u00ed\u00ee\7n\2\2\u00ee\u017b\7f\2\2\u00ef\u00f0\7f\2\2" + + "\u00f0\u00f1\7g\2\2\u00f1\u00f2\7u\2\2\u00f2\u00f3\7e\2\2\u00f3\u00f4" + + "\7g\2\2\u00f4\u00f5\7p\2\2\u00f5\u00f6\7f\2\2\u00f6\u00f7\7c\2\2\u00f7" + + "\u00f8\7p\2\2\u00f8\u017b\7v\2\2\u00f9\u00fa\7f\2\2\u00fa\u00fb\7g\2\2" + + "\u00fb\u00fc\7u\2\2\u00fc\u00fd\7e\2\2\u00fd\u00fe\7g\2\2\u00fe\u00ff" + + "\7p\2\2\u00ff\u0100\7f\2\2\u0100\u0101\7c\2\2\u0101\u0102\7p\2\2\u0102" + + "\u0103\7v\2\2\u0103\u0104\7/\2\2\u0104\u0105\7q\2\2\u0105\u0106\7t\2\2" + + "\u0106\u0107\7/\2\2\u0107\u0108\7u\2\2\u0108\u0109\7g\2\2\u0109\u010a" + + "\7n\2\2\u010a\u017b\7h\2\2\u010b\u010c\7h\2\2\u010c\u010d\7q\2\2\u010d" + + "\u010e\7n\2\2\u010e\u010f\7n\2\2\u010f\u0110\7q\2\2\u0110\u0111\7y\2\2" + + "\u0111\u0112\7k\2\2\u0112\u0113\7p\2\2\u0113\u017b\7i\2\2\u0114\u0115" + + "\7h\2\2\u0115\u0116\7q\2\2\u0116\u0117\7n\2\2\u0117\u0118\7n\2\2\u0118" + + "\u0119\7q\2\2\u0119\u011a\7y\2\2\u011a\u011b\7k\2\2\u011b\u011c\7p\2\2" + + "\u011c\u011d\7i\2\2\u011d\u011e\7/\2\2\u011e\u011f\7u\2\2\u011f\u0120" + + "\7k\2\2\u0120\u0121\7d\2\2\u0121\u0122\7n\2\2\u0122\u0123\7k\2\2\u0123" + + "\u0124\7p\2\2\u0124\u017b\7i\2\2\u0125\u0126\7r\2\2\u0126\u0127\7c\2\2" + + "\u0127\u0128\7t\2\2\u0128\u0129\7g\2\2\u0129\u012a\7p\2\2\u012a\u017b" + + "\7v\2\2\u012b\u012c\7r\2\2\u012c\u012d\7t\2\2\u012d\u012e\7g\2\2\u012e" + + "\u012f\7e\2\2\u012f\u0130\7g\2\2\u0130\u0131\7f\2\2\u0131\u0132\7k\2\2" + + "\u0132\u0133\7p\2\2\u0133\u017b\7i\2\2\u0134\u0135\7r\2\2\u0135\u0136" + + "\7t\2\2\u0136\u0137\7g\2\2\u0137\u0138\7e\2\2\u0138\u0139\7g\2\2\u0139" + + "\u013a\7f\2\2\u013a\u013b\7k\2\2\u013b\u013c\7p\2\2\u013c\u013d\7i\2\2" + + "\u013d\u013e\7/\2\2\u013e\u013f\7u\2\2\u013f\u0140\7k\2\2\u0140\u0141" + + "\7d\2\2\u0141\u0142\7n\2\2\u0142\u0143\7k\2\2\u0143\u0144\7p\2\2\u0144" + + "\u017b\7i\2\2\u0145\u0146\7u\2\2\u0146\u0147\7g\2\2\u0147\u0148\7n\2\2" + + "\u0148\u017b\7h\2\2\u0149\u014a\7h\2\2\u014a\u014b\7q\2\2\u014b\u014c" + + "\7n\2\2\u014c\u014d\7n\2\2\u014d\u014e\7q\2\2\u014e\u014f\7y\2\2\u014f" + + "\u0150\7k\2\2\u0150\u0151\7p\2\2\u0151\u0152\7i\2\2\u0152\u0153\7/\2\2" + + "\u0153\u0154\7u\2\2\u0154\u0155\7k\2\2\u0155\u0156\7d\2\2\u0156\u0157" + + "\7n\2\2\u0157\u0158\7k\2\2\u0158\u0159\7p\2\2\u0159\u015a\7i\2\2\u015a" + + "\u015b\7/\2\2\u015b\u015c\7q\2\2\u015c\u015d\7p\2\2\u015d\u017b\7g\2\2" + + "\u015e\u015f\7r\2\2\u015f\u0160\7t\2\2\u0160\u0161\7g\2\2\u0161\u0162" + + "\7e\2\2\u0162\u0163\7g\2\2\u0163\u0164\7f\2\2\u0164\u0165\7k\2\2\u0165" + + "\u0166\7p\2\2\u0166\u0167\7i\2\2\u0167\u0168\7/\2\2\u0168\u0169\7u\2\2" + + "\u0169\u016a\7k\2\2\u016a\u016b\7d\2\2\u016b\u016c\7n\2\2\u016c\u016d" + + "\7k\2\2\u016d\u016e\7p\2\2\u016e\u016f\7i\2\2\u016f\u0170\7/\2\2\u0170" + + "\u0171\7q\2\2\u0171\u0172\7p\2\2\u0172\u017b\7g\2\2\u0173\u0174\7u\2\2" + + "\u0174\u0175\7k\2\2\u0175\u0176\7d\2\2\u0176\u0177\7n\2\2\u0177\u0178" + + "\7k\2\2\u0178\u0179\7p\2\2\u0179\u017b\7i\2\2\u017a\u00c9\3\2\2\2\u017a" + + "\u00d1\3\2\2\2\u017a\u00e1\3\2\2\2\u017a\u00ea\3\2\2\2\u017a\u00ef\3\2" + + "\2\2\u017a\u00f9\3\2\2\2\u017a\u010b\3\2\2\2\u017a\u0114\3\2\2\2\u017a" + + "\u0125\3\2\2\2\u017a\u012b\3\2\2\2\u017a\u0134\3\2\2\2\u017a\u0145\3\2" + + "\2\2\u017a\u0149\3\2\2\2\u017a\u015e\3\2\2\2\u017a\u0173\3\2\2\2\u017b" + + "\22\3\2\2\2\u017c\u017d\7\61\2\2\u017d\24\3\2\2\2\u017e\u017f\7\61\2\2" + + "\u017f\u0180\7\61\2\2\u0180\26\3\2\2\2\u0181\u0182\7*\2\2\u0182\30\3\2" + + "\2\2\u0183\u0184\7+\2\2\u0184\32\3\2\2\2\u0185\u0186\7]\2\2\u0186\34\3" + + "\2\2\2\u0187\u0188\7_\2\2\u0188\36\3\2\2\2\u0189\u018a\7/\2\2\u018a \3" + + "\2\2\2\u018b\u018c\7-\2\2\u018c\"\3\2\2\2\u018d\u018e\7\60\2\2\u018e$" + + "\3\2\2\2\u018f\u0190\7,\2\2\u0190&\3\2\2\2\u0191\u0192\7b\2\2\u0192\u0193" + + "\7f\2\2\u0193\u0194\7k\2\2\u0194\u0195\7x\2\2\u0195\u0196\7b\2\2\u0196" + + "(\3\2\2\2\u0197\u0198\7b\2\2\u0198\u0199\7o\2\2\u0199\u019a\7q\2\2\u019a" + + "\u019b\7f\2\2\u019b\u019c\7b\2\2\u019c*\3\2\2\2\u019d\u019e\7\60\2\2\u019e" + + "\u019f\7\60\2\2\u019f,\3\2\2\2\u01a0\u01a1\7B\2\2\u01a1.\3\2\2\2\u01a2" + + "\u01a3\7.\2\2\u01a3\60\3\2\2\2\u01a4\u01a5\7~\2\2\u01a5\62\3\2\2\2\u01a6" + + "\u01a7\7>\2\2\u01a7\64\3\2\2\2\u01a8\u01a9\7@\2\2\u01a9\66\3\2\2\2\u01aa" + + "\u01ab\7>\2\2\u01ab\u01ac\7?\2\2\u01ac8\3\2\2\2\u01ad\u01ae\7@\2\2\u01ae" + + "\u01af\7?\2\2\u01af:\3\2\2\2\u01b0\u01b1\7?\2\2\u01b1<\3\2\2\2\u01b2\u01b3" + + "\7#\2\2\u01b3\u01b4\7?\2\2\u01b4>\3\2\2\2\u01b5\u01b6\7`\2\2\u01b6\u01b7" + + "\7?\2\2\u01b7@\3\2\2\2\u01b8\u01b9\7&\2\2\u01b9\u01ba\7?\2\2\u01baB\3" + + "\2\2\2\u01bb\u01bc\7,\2\2\u01bc\u01bd\7?\2\2\u01bdD\3\2\2\2\u01be\u01bf" + + "\7\u0080\2\2\u01bf\u01c0\7?\2\2\u01c0F\3\2\2\2\u01c1\u01c2\7#\2\2\u01c2" + + "\u01c3\7\u0080\2\2\u01c3H\3\2\2\2\u01c4\u01c5\7<\2\2\u01c5J\3\2\2\2\u01c6" + + "\u01c7\7<\2\2\u01c7\u01c8\7<\2\2\u01c8L\3\2\2\2\u01c9\u01ca\7)\2\2\u01ca" + + "N\3\2\2\2\u01cb\u01cc\7$\2\2\u01ccP\3\2\2\2\u01cd\u01d1\7$\2\2\u01ce\u01d0" + + "\n\2\2\2\u01cf\u01ce\3\2\2\2\u01d0\u01d3\3\2\2\2\u01d1\u01cf\3\2\2\2\u01d1" + + "\u01d2\3\2\2\2\u01d2\u01d4\3\2\2\2\u01d3\u01d1\3\2\2\2\u01d4\u01de\7$" + + "\2\2\u01d5\u01d9\7)\2\2\u01d6\u01d8\n\3\2\2\u01d7\u01d6\3\2\2\2\u01d8" + + "\u01db\3\2\2\2\u01d9\u01d7\3\2\2\2\u01d9\u01da\3\2\2\2\u01da\u01dc\3\2" + + "\2\2\u01db\u01d9\3\2\2\2\u01dc\u01de\7)\2\2\u01dd\u01cd\3\2\2\2\u01dd" + + "\u01d5\3\2\2\2\u01deR\3\2\2\2\u01df\u01e1\t\4\2\2\u01e0\u01df\3\2\2\2" + + "\u01e1\u01e2\3\2\2\2\u01e2\u01e0\3\2\2\2\u01e2\u01e3\3\2\2\2\u01e3\u01e4" + + "\3\2\2\2\u01e4\u01e5\b*\2\2\u01e5T\3\2\2\2\u01e6\u01ea\5W,\2\u01e7\u01e9" + + "\5Y-\2\u01e8\u01e7\3\2\2\2\u01e9\u01ec\3\2\2\2\u01ea\u01e8\3\2\2\2\u01ea" + + "\u01eb\3\2\2\2\u01ebV\3\2\2\2\u01ec\u01ea\3\2\2\2\u01ed\u01ee\t\5\2\2" + + "\u01eeX\3\2\2\2\u01ef\u01f2\5W,\2\u01f0\u01f2\t\6\2\2\u01f1\u01ef\3\2" + + "\2\2\u01f1\u01f0\3\2\2\2\u01f2Z\3\2\2\2\17\2\u00b7\u00bc\u00be\u00c2\u00c7" + + "\u017a\u01d1\u01d9\u01dd\u01e2\u01ea\u01f1\3\b\2\2"; + public static final ATN _ATN = + new ATNDeserializer().deserialize(_serializedATN.toCharArray()); + + static { + _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; + for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { + _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); + } + } +} \ No newline at end of file diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathLexer.tokens b/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathLexer.tokens new file mode 100644 index 000000000..79142cd34 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathLexer.tokens @@ -0,0 +1,76 @@ +T__0=1 +T__1=2 +T__2=3 +T__3=4 +NodeType=5 +Number=6 +AxisName=7 +PATHSEP=8 +ABRPATH=9 +LPAR=10 +RPAR=11 +LBRAC=12 +RBRAC=13 +MINUS=14 +PLUS=15 +DOT=16 +MUL=17 +DIVISION=18 +MODULO=19 +DOTDOT=20 +AT=21 +COMMA=22 +PIPE=23 +LESS=24 +MORE_=25 +LE=26 +GE=27 +EQUALITY=28 +INEQUALITY=29 +START_WITH=30 +END_WITH=31 +CONTAIN_WITH=32 +REGEXP_WITH=33 +REGEXP_NOT_WITH=34 +COLON=35 +CC=36 +APOS=37 +QUOT=38 +Literal=39 +Whitespace=40 +NCName=41 +'processing-instruction'=1 +'or'=2 +'and'=3 +'$'=4 +'/'=8 +'//'=9 +'('=10 +')'=11 +'['=12 +']'=13 +'-'=14 +'+'=15 +'.'=16 +'*'=17 +'`div`'=18 +'`mod`'=19 +'..'=20 +'@'=21 +','=22 +'|'=23 +'<'=24 +'>'=25 +'<='=26 +'>='=27 +'='=28 +'!='=29 +'^='=30 +'$='=31 +'*='=32 +'~='=33 +'!~'=34 +':'=35 +'::'=36 +'\''=37 +'"'=38 diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathListener.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathListener.java new file mode 100644 index 000000000..58287510b --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathListener.java @@ -0,0 +1,388 @@ +// Generated from resources/Xpath.g4 by ANTLR 4.7.2 +package org.seimicrawler.xpath.antlr; + +import org.antlr.v4.runtime.tree.ParseTreeListener; + +/** + * This interface defines a complete listener for a parse tree produced by + * {@link XpathParser}. + */ +public interface XpathListener extends ParseTreeListener { + /** + * Enter a parse tree produced by {@link XpathParser#main}. + * + * @param ctx the parse tree + */ + void enterMain(XpathParser.MainContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#main}. + * + * @param ctx the parse tree + */ + void exitMain(XpathParser.MainContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#locationPath}. + * + * @param ctx the parse tree + */ + void enterLocationPath(XpathParser.LocationPathContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#locationPath}. + * + * @param ctx the parse tree + */ + void exitLocationPath(XpathParser.LocationPathContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#absoluteLocationPathNoroot}. + * + * @param ctx the parse tree + */ + void enterAbsoluteLocationPathNoroot(XpathParser.AbsoluteLocationPathNorootContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#absoluteLocationPathNoroot}. + * + * @param ctx the parse tree + */ + void exitAbsoluteLocationPathNoroot(XpathParser.AbsoluteLocationPathNorootContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#relativeLocationPath}. + * + * @param ctx the parse tree + */ + void enterRelativeLocationPath(XpathParser.RelativeLocationPathContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#relativeLocationPath}. + * + * @param ctx the parse tree + */ + void exitRelativeLocationPath(XpathParser.RelativeLocationPathContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#step}. + * + * @param ctx the parse tree + */ + void enterStep(XpathParser.StepContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#step}. + * + * @param ctx the parse tree + */ + void exitStep(XpathParser.StepContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#axisSpecifier}. + * + * @param ctx the parse tree + */ + void enterAxisSpecifier(XpathParser.AxisSpecifierContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#axisSpecifier}. + * + * @param ctx the parse tree + */ + void exitAxisSpecifier(XpathParser.AxisSpecifierContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#nodeTest}. + * + * @param ctx the parse tree + */ + void enterNodeTest(XpathParser.NodeTestContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#nodeTest}. + * + * @param ctx the parse tree + */ + void exitNodeTest(XpathParser.NodeTestContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#predicate}. + * + * @param ctx the parse tree + */ + void enterPredicate(XpathParser.PredicateContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#predicate}. + * + * @param ctx the parse tree + */ + void exitPredicate(XpathParser.PredicateContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#abbreviatedStep}. + * + * @param ctx the parse tree + */ + void enterAbbreviatedStep(XpathParser.AbbreviatedStepContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#abbreviatedStep}. + * + * @param ctx the parse tree + */ + void exitAbbreviatedStep(XpathParser.AbbreviatedStepContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#expr}. + * + * @param ctx the parse tree + */ + void enterExpr(XpathParser.ExprContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#expr}. + * + * @param ctx the parse tree + */ + void exitExpr(XpathParser.ExprContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#primaryExpr}. + * + * @param ctx the parse tree + */ + void enterPrimaryExpr(XpathParser.PrimaryExprContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#primaryExpr}. + * + * @param ctx the parse tree + */ + void exitPrimaryExpr(XpathParser.PrimaryExprContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#functionCall}. + * + * @param ctx the parse tree + */ + void enterFunctionCall(XpathParser.FunctionCallContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#functionCall}. + * + * @param ctx the parse tree + */ + void exitFunctionCall(XpathParser.FunctionCallContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#unionExprNoRoot}. + * + * @param ctx the parse tree + */ + void enterUnionExprNoRoot(XpathParser.UnionExprNoRootContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#unionExprNoRoot}. + * + * @param ctx the parse tree + */ + void exitUnionExprNoRoot(XpathParser.UnionExprNoRootContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#pathExprNoRoot}. + * + * @param ctx the parse tree + */ + void enterPathExprNoRoot(XpathParser.PathExprNoRootContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#pathExprNoRoot}. + * + * @param ctx the parse tree + */ + void exitPathExprNoRoot(XpathParser.PathExprNoRootContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#filterExpr}. + * + * @param ctx the parse tree + */ + void enterFilterExpr(XpathParser.FilterExprContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#filterExpr}. + * + * @param ctx the parse tree + */ + void exitFilterExpr(XpathParser.FilterExprContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#orExpr}. + * + * @param ctx the parse tree + */ + void enterOrExpr(XpathParser.OrExprContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#orExpr}. + * + * @param ctx the parse tree + */ + void exitOrExpr(XpathParser.OrExprContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#andExpr}. + * + * @param ctx the parse tree + */ + void enterAndExpr(XpathParser.AndExprContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#andExpr}. + * + * @param ctx the parse tree + */ + void exitAndExpr(XpathParser.AndExprContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#equalityExpr}. + * + * @param ctx the parse tree + */ + void enterEqualityExpr(XpathParser.EqualityExprContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#equalityExpr}. + * + * @param ctx the parse tree + */ + void exitEqualityExpr(XpathParser.EqualityExprContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#relationalExpr}. + * + * @param ctx the parse tree + */ + void enterRelationalExpr(XpathParser.RelationalExprContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#relationalExpr}. + * + * @param ctx the parse tree + */ + void exitRelationalExpr(XpathParser.RelationalExprContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#additiveExpr}. + * + * @param ctx the parse tree + */ + void enterAdditiveExpr(XpathParser.AdditiveExprContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#additiveExpr}. + * + * @param ctx the parse tree + */ + void exitAdditiveExpr(XpathParser.AdditiveExprContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#multiplicativeExpr}. + * + * @param ctx the parse tree + */ + void enterMultiplicativeExpr(XpathParser.MultiplicativeExprContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#multiplicativeExpr}. + * + * @param ctx the parse tree + */ + void exitMultiplicativeExpr(XpathParser.MultiplicativeExprContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#unaryExprNoRoot}. + * + * @param ctx the parse tree + */ + void enterUnaryExprNoRoot(XpathParser.UnaryExprNoRootContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#unaryExprNoRoot}. + * + * @param ctx the parse tree + */ + void exitUnaryExprNoRoot(XpathParser.UnaryExprNoRootContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#qName}. + * + * @param ctx the parse tree + */ + void enterQName(XpathParser.QNameContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#qName}. + * + * @param ctx the parse tree + */ + void exitQName(XpathParser.QNameContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#functionName}. + * + * @param ctx the parse tree + */ + void enterFunctionName(XpathParser.FunctionNameContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#functionName}. + * + * @param ctx the parse tree + */ + void exitFunctionName(XpathParser.FunctionNameContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#variableReference}. + * + * @param ctx the parse tree + */ + void enterVariableReference(XpathParser.VariableReferenceContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#variableReference}. + * + * @param ctx the parse tree + */ + void exitVariableReference(XpathParser.VariableReferenceContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#nameTest}. + * + * @param ctx the parse tree + */ + void enterNameTest(XpathParser.NameTestContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#nameTest}. + * + * @param ctx the parse tree + */ + void exitNameTest(XpathParser.NameTestContext ctx); + + /** + * Enter a parse tree produced by {@link XpathParser#nCName}. + * + * @param ctx the parse tree + */ + void enterNCName(XpathParser.NCNameContext ctx); + + /** + * Exit a parse tree produced by {@link XpathParser#nCName}. + * + * @param ctx the parse tree + */ + void exitNCName(XpathParser.NCNameContext ctx); +} \ No newline at end of file diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathParser.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathParser.java new file mode 100644 index 000000000..06248f719 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/antlr/XpathParser.java @@ -0,0 +1,2490 @@ +// Generated from resources/Xpath.g4 by ANTLR 4.7.2 +package org.seimicrawler.xpath.antlr; + +import org.antlr.v4.runtime.NoViableAltException; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.RuntimeMetaData; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.Vocabulary; +import org.antlr.v4.runtime.VocabularyImpl; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.ParserATNSimulator; +import org.antlr.v4.runtime.atn.PredictionContextCache; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.tree.ParseTreeListener; +import org.antlr.v4.runtime.tree.ParseTreeVisitor; +import org.antlr.v4.runtime.tree.TerminalNode; + +import java.util.List; + +@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"}) +public class XpathParser extends Parser { + static { + RuntimeMetaData.checkVersion("4.7.2", RuntimeMetaData.VERSION); + } + + protected static final DFA[] _decisionToDFA; + protected static final PredictionContextCache _sharedContextCache = + new PredictionContextCache(); + public static final int + T__0 = 1, T__1 = 2, T__2 = 3, T__3 = 4, NodeType = 5, Number = 6, AxisName = 7, PATHSEP = 8, + ABRPATH = 9, LPAR = 10, RPAR = 11, LBRAC = 12, RBRAC = 13, MINUS = 14, PLUS = 15, DOT = 16, + MUL = 17, DIVISION = 18, MODULO = 19, DOTDOT = 20, AT = 21, COMMA = 22, PIPE = 23, LESS = 24, + MORE_ = 25, LE = 26, GE = 27, EQUALITY = 28, INEQUALITY = 29, START_WITH = 30, END_WITH = 31, + CONTAIN_WITH = 32, REGEXP_WITH = 33, REGEXP_NOT_WITH = 34, COLON = 35, CC = 36, + APOS = 37, QUOT = 38, Literal = 39, Whitespace = 40, NCName = 41; + public static final int + RULE_main = 0, RULE_locationPath = 1, RULE_absoluteLocationPathNoroot = 2, + RULE_relativeLocationPath = 3, RULE_step = 4, RULE_axisSpecifier = 5, + RULE_nodeTest = 6, RULE_predicate = 7, RULE_abbreviatedStep = 8, RULE_expr = 9, + RULE_primaryExpr = 10, RULE_functionCall = 11, RULE_unionExprNoRoot = 12, + RULE_pathExprNoRoot = 13, RULE_filterExpr = 14, RULE_orExpr = 15, RULE_andExpr = 16, + RULE_equalityExpr = 17, RULE_relationalExpr = 18, RULE_additiveExpr = 19, + RULE_multiplicativeExpr = 20, RULE_unaryExprNoRoot = 21, RULE_qName = 22, + RULE_functionName = 23, RULE_variableReference = 24, RULE_nameTest = 25, + RULE_nCName = 26; + + private static String[] makeRuleNames() { + return new String[]{ + "main", "locationPath", "absoluteLocationPathNoroot", "relativeLocationPath", + "step", "axisSpecifier", "nodeTest", "predicate", "abbreviatedStep", + "expr", "primaryExpr", "functionCall", "unionExprNoRoot", "pathExprNoRoot", + "filterExpr", "orExpr", "andExpr", "equalityExpr", "relationalExpr", + "additiveExpr", "multiplicativeExpr", "unaryExprNoRoot", "qName", "functionName", + "variableReference", "nameTest", "nCName" + }; + } + + public static final String[] ruleNames = makeRuleNames(); + + private static String[] makeLiteralNames() { + return new String[]{ + null, "'processing-instruction'", "'or'", "'and'", "'$'", null, null, + null, "'/'", "'//'", "'('", "')'", "'['", "']'", "'-'", "'+'", "'.'", + "'*'", "'`div`'", "'`mod`'", "'..'", "'@'", "','", "'|'", "'<'", "'>'", + "'<='", "'>='", "'='", "'!='", "'^='", "'$='", "'*='", "'~='", "'!~'", + "':'", "'::'", "'''", "'\"'" + }; + } + + private static final String[] _LITERAL_NAMES = makeLiteralNames(); + + private static String[] makeSymbolicNames() { + return new String[]{ + null, null, null, null, null, "NodeType", "Number", "AxisName", "PATHSEP", + "ABRPATH", "LPAR", "RPAR", "LBRAC", "RBRAC", "MINUS", "PLUS", "DOT", + "MUL", "DIVISION", "MODULO", "DOTDOT", "AT", "COMMA", "PIPE", "LESS", + "MORE_", "LE", "GE", "EQUALITY", "INEQUALITY", "START_WITH", "END_WITH", + "CONTAIN_WITH", "REGEXP_WITH", "REGEXP_NOT_WITH", "COLON", "CC", "APOS", + "QUOT", "Literal", "Whitespace", "NCName" + }; + } + + private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); + public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); + + /** + * @deprecated Use {@link #VOCABULARY} instead. + */ + @Deprecated + public static final String[] tokenNames; + + static { + tokenNames = new String[_SYMBOLIC_NAMES.length]; + for (int i = 0; i < tokenNames.length; i++) { + tokenNames[i] = VOCABULARY.getLiteralName(i); + if (tokenNames[i] == null) { + tokenNames[i] = VOCABULARY.getSymbolicName(i); + } + + if (tokenNames[i] == null) { + tokenNames[i] = ""; + } + } + } + + @Override + @Deprecated + public String[] getTokenNames() { + return tokenNames; + } + + @Override + + public Vocabulary getVocabulary() { + return VOCABULARY; + } + + @Override + public String getGrammarFileName() { + return "Xpath.g4"; + } + + @Override + public String[] getRuleNames() { + return ruleNames; + } + + @Override + public String getSerializedATN() { + return _serializedATN; + } + + @Override + public ATN getATN() { + return _ATN; + } + + public XpathParser(TokenStream input) { + super(input); + _interp = new ParserATNSimulator(this, _ATN, _decisionToDFA, _sharedContextCache); + } + + public static class MainContext extends ParserRuleContext { + public ExprContext expr() { + return getRuleContext(ExprContext.class, 0); + } + + public MainContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_main; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).enterMain(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).exitMain(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitMain(this); + else return visitor.visitChildren(this); + } + } + + public final MainContext main() throws RecognitionException { + MainContext _localctx = new MainContext(_ctx, getState()); + enterRule(_localctx, 0, RULE_main); + try { + enterOuterAlt(_localctx, 1); + { + setState(54); + expr(); + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class LocationPathContext extends ParserRuleContext { + public RelativeLocationPathContext relativeLocationPath() { + return getRuleContext(RelativeLocationPathContext.class, 0); + } + + public AbsoluteLocationPathNorootContext absoluteLocationPathNoroot() { + return getRuleContext(AbsoluteLocationPathNorootContext.class, 0); + } + + public LocationPathContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_locationPath; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).enterLocationPath(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).exitLocationPath(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitLocationPath(this); + else return visitor.visitChildren(this); + } + } + + public final LocationPathContext locationPath() throws RecognitionException { + LocationPathContext _localctx = new LocationPathContext(_ctx, getState()); + enterRule(_localctx, 2, RULE_locationPath); + try { + setState(58); + _errHandler.sync(this); + switch (_input.LA(1)) { + case T__0: + case NodeType: + case AxisName: + case DOT: + case MUL: + case DOTDOT: + case AT: + case NCName: + enterOuterAlt(_localctx, 1); + { + setState(56); + relativeLocationPath(); + } + break; + case PATHSEP: + case ABRPATH: + enterOuterAlt(_localctx, 2); + { + setState(57); + absoluteLocationPathNoroot(); + } + break; + default: + throw new NoViableAltException(this); + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class AbsoluteLocationPathNorootContext extends ParserRuleContext { + public Token op; + + public RelativeLocationPathContext relativeLocationPath() { + return getRuleContext(RelativeLocationPathContext.class, 0); + } + + public TerminalNode PATHSEP() { + return getToken(XpathParser.PATHSEP, 0); + } + + public TerminalNode ABRPATH() { + return getToken(XpathParser.ABRPATH, 0); + } + + public AbsoluteLocationPathNorootContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_absoluteLocationPathNoroot; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).enterAbsoluteLocationPathNoroot(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).exitAbsoluteLocationPathNoroot(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitAbsoluteLocationPathNoroot(this); + else return visitor.visitChildren(this); + } + } + + public final AbsoluteLocationPathNorootContext absoluteLocationPathNoroot() throws RecognitionException { + AbsoluteLocationPathNorootContext _localctx = new AbsoluteLocationPathNorootContext(_ctx, getState()); + enterRule(_localctx, 4, RULE_absoluteLocationPathNoroot); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(60); + ((AbsoluteLocationPathNorootContext) _localctx).op = _input.LT(1); + _la = _input.LA(1); + if (!(_la == PATHSEP || _la == ABRPATH)) { + ((AbsoluteLocationPathNorootContext) _localctx).op = (Token) _errHandler.recoverInline(this); + } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + setState(61); + relativeLocationPath(); + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class RelativeLocationPathContext extends ParserRuleContext { + public Token op; + + public List step() { + return getRuleContexts(StepContext.class); + } + + public StepContext step(int i) { + return getRuleContext(StepContext.class, i); + } + + public List PATHSEP() { + return getTokens(XpathParser.PATHSEP); + } + + public TerminalNode PATHSEP(int i) { + return getToken(XpathParser.PATHSEP, i); + } + + public List ABRPATH() { + return getTokens(XpathParser.ABRPATH); + } + + public TerminalNode ABRPATH(int i) { + return getToken(XpathParser.ABRPATH, i); + } + + public RelativeLocationPathContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_relativeLocationPath; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).enterRelativeLocationPath(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).exitRelativeLocationPath(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitRelativeLocationPath(this); + else return visitor.visitChildren(this); + } + } + + public final RelativeLocationPathContext relativeLocationPath() throws RecognitionException { + RelativeLocationPathContext _localctx = new RelativeLocationPathContext(_ctx, getState()); + enterRule(_localctx, 6, RULE_relativeLocationPath); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(63); + step(); + setState(68); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la == PATHSEP || _la == ABRPATH) { + { + { + setState(64); + ((RelativeLocationPathContext) _localctx).op = _input.LT(1); + _la = _input.LA(1); + if (!(_la == PATHSEP || _la == ABRPATH)) { + ((RelativeLocationPathContext) _localctx).op = (Token) _errHandler.recoverInline(this); + } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + setState(65); + step(); + } + } + setState(70); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class StepContext extends ParserRuleContext { + public AxisSpecifierContext axisSpecifier() { + return getRuleContext(AxisSpecifierContext.class, 0); + } + + public NodeTestContext nodeTest() { + return getRuleContext(NodeTestContext.class, 0); + } + + public List predicate() { + return getRuleContexts(PredicateContext.class); + } + + public PredicateContext predicate(int i) { + return getRuleContext(PredicateContext.class, i); + } + + public AbbreviatedStepContext abbreviatedStep() { + return getRuleContext(AbbreviatedStepContext.class, 0); + } + + public StepContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_step; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).enterStep(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).exitStep(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitStep(this); + else return visitor.visitChildren(this); + } + } + + public final StepContext step() throws RecognitionException { + StepContext _localctx = new StepContext(_ctx, getState()); + enterRule(_localctx, 8, RULE_step); + int _la; + try { + setState(80); + _errHandler.sync(this); + switch (_input.LA(1)) { + case T__0: + case NodeType: + case AxisName: + case MUL: + case AT: + case NCName: + enterOuterAlt(_localctx, 1); + { + setState(71); + axisSpecifier(); + setState(72); + nodeTest(); + setState(76); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la == LBRAC) { + { + { + setState(73); + predicate(); + } + } + setState(78); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + break; + case DOT: + case DOTDOT: + enterOuterAlt(_localctx, 2); + { + setState(79); + abbreviatedStep(); + } + break; + default: + throw new NoViableAltException(this); + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class AxisSpecifierContext extends ParserRuleContext { + public TerminalNode AxisName() { + return getToken(XpathParser.AxisName, 0); + } + + public TerminalNode CC() { + return getToken(XpathParser.CC, 0); + } + + public TerminalNode AT() { + return getToken(XpathParser.AT, 0); + } + + public AxisSpecifierContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_axisSpecifier; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).enterAxisSpecifier(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).exitAxisSpecifier(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitAxisSpecifier(this); + else return visitor.visitChildren(this); + } + } + + public final AxisSpecifierContext axisSpecifier() throws RecognitionException { + AxisSpecifierContext _localctx = new AxisSpecifierContext(_ctx, getState()); + enterRule(_localctx, 10, RULE_axisSpecifier); + int _la; + try { + setState(87); + _errHandler.sync(this); + switch (getInterpreter().adaptivePredict(_input, 5, _ctx)) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(82); + match(AxisName); + setState(83); + match(CC); + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(85); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la == AT) { + { + setState(84); + match(AT); + } + } + + } + break; + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class NodeTestContext extends ParserRuleContext { + public NameTestContext nameTest() { + return getRuleContext(NameTestContext.class, 0); + } + + public TerminalNode NodeType() { + return getToken(XpathParser.NodeType, 0); + } + + public TerminalNode LPAR() { + return getToken(XpathParser.LPAR, 0); + } + + public TerminalNode RPAR() { + return getToken(XpathParser.RPAR, 0); + } + + public TerminalNode Literal() { + return getToken(XpathParser.Literal, 0); + } + + public NodeTestContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_nodeTest; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).enterNodeTest(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).exitNodeTest(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitNodeTest(this); + else return visitor.visitChildren(this); + } + } + + public final NodeTestContext nodeTest() throws RecognitionException { + NodeTestContext _localctx = new NodeTestContext(_ctx, getState()); + enterRule(_localctx, 12, RULE_nodeTest); + try { + setState(97); + _errHandler.sync(this); + switch (_input.LA(1)) { + case AxisName: + case MUL: + case NCName: + enterOuterAlt(_localctx, 1); + { + setState(89); + nameTest(); + } + break; + case NodeType: + enterOuterAlt(_localctx, 2); + { + setState(90); + match(NodeType); + setState(91); + match(LPAR); + setState(92); + match(RPAR); + } + break; + case T__0: + enterOuterAlt(_localctx, 3); + { + setState(93); + match(T__0); + setState(94); + match(LPAR); + setState(95); + match(Literal); + setState(96); + match(RPAR); + } + break; + default: + throw new NoViableAltException(this); + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class PredicateContext extends ParserRuleContext { + public TerminalNode LBRAC() { + return getToken(XpathParser.LBRAC, 0); + } + + public ExprContext expr() { + return getRuleContext(ExprContext.class, 0); + } + + public TerminalNode RBRAC() { + return getToken(XpathParser.RBRAC, 0); + } + + public PredicateContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_predicate; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).enterPredicate(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).exitPredicate(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitPredicate(this); + else return visitor.visitChildren(this); + } + } + + public final PredicateContext predicate() throws RecognitionException { + PredicateContext _localctx = new PredicateContext(_ctx, getState()); + enterRule(_localctx, 14, RULE_predicate); + try { + enterOuterAlt(_localctx, 1); + { + setState(99); + match(LBRAC); + setState(100); + expr(); + setState(101); + match(RBRAC); + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class AbbreviatedStepContext extends ParserRuleContext { + public TerminalNode DOT() { + return getToken(XpathParser.DOT, 0); + } + + public TerminalNode DOTDOT() { + return getToken(XpathParser.DOTDOT, 0); + } + + public AbbreviatedStepContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_abbreviatedStep; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).enterAbbreviatedStep(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).exitAbbreviatedStep(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitAbbreviatedStep(this); + else return visitor.visitChildren(this); + } + } + + public final AbbreviatedStepContext abbreviatedStep() throws RecognitionException { + AbbreviatedStepContext _localctx = new AbbreviatedStepContext(_ctx, getState()); + enterRule(_localctx, 16, RULE_abbreviatedStep); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(103); + _la = _input.LA(1); + if (!(_la == DOT || _la == DOTDOT)) { + _errHandler.recoverInline(this); + } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class ExprContext extends ParserRuleContext { + public OrExprContext orExpr() { + return getRuleContext(OrExprContext.class, 0); + } + + public ExprContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_expr; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).enterExpr(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).exitExpr(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitExpr(this); + else return visitor.visitChildren(this); + } + } + + public final ExprContext expr() throws RecognitionException { + ExprContext _localctx = new ExprContext(_ctx, getState()); + enterRule(_localctx, 18, RULE_expr); + try { + enterOuterAlt(_localctx, 1); + { + setState(105); + orExpr(); + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class PrimaryExprContext extends ParserRuleContext { + public VariableReferenceContext variableReference() { + return getRuleContext(VariableReferenceContext.class, 0); + } + + public TerminalNode LPAR() { + return getToken(XpathParser.LPAR, 0); + } + + public ExprContext expr() { + return getRuleContext(ExprContext.class, 0); + } + + public TerminalNode RPAR() { + return getToken(XpathParser.RPAR, 0); + } + + public TerminalNode Literal() { + return getToken(XpathParser.Literal, 0); + } + + public TerminalNode Number() { + return getToken(XpathParser.Number, 0); + } + + public FunctionCallContext functionCall() { + return getRuleContext(FunctionCallContext.class, 0); + } + + public PrimaryExprContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_primaryExpr; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).enterPrimaryExpr(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).exitPrimaryExpr(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitPrimaryExpr(this); + else return visitor.visitChildren(this); + } + } + + public final PrimaryExprContext primaryExpr() throws RecognitionException { + PrimaryExprContext _localctx = new PrimaryExprContext(_ctx, getState()); + enterRule(_localctx, 20, RULE_primaryExpr); + try { + setState(115); + _errHandler.sync(this); + switch (_input.LA(1)) { + case T__3: + enterOuterAlt(_localctx, 1); + { + setState(107); + variableReference(); + } + break; + case LPAR: + enterOuterAlt(_localctx, 2); + { + setState(108); + match(LPAR); + setState(109); + expr(); + setState(110); + match(RPAR); + } + break; + case Literal: + enterOuterAlt(_localctx, 3); + { + setState(112); + match(Literal); + } + break; + case Number: + enterOuterAlt(_localctx, 4); + { + setState(113); + match(Number); + } + break; + case AxisName: + case NCName: + enterOuterAlt(_localctx, 5); + { + setState(114); + functionCall(); + } + break; + default: + throw new NoViableAltException(this); + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class FunctionCallContext extends ParserRuleContext { + public FunctionNameContext functionName() { + return getRuleContext(FunctionNameContext.class, 0); + } + + public TerminalNode LPAR() { + return getToken(XpathParser.LPAR, 0); + } + + public TerminalNode RPAR() { + return getToken(XpathParser.RPAR, 0); + } + + public List expr() { + return getRuleContexts(ExprContext.class); + } + + public ExprContext expr(int i) { + return getRuleContext(ExprContext.class, i); + } + + public List COMMA() { + return getTokens(XpathParser.COMMA); + } + + public TerminalNode COMMA(int i) { + return getToken(XpathParser.COMMA, i); + } + + public FunctionCallContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_functionCall; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).enterFunctionCall(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).exitFunctionCall(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitFunctionCall(this); + else return visitor.visitChildren(this); + } + } + + public final FunctionCallContext functionCall() throws RecognitionException { + FunctionCallContext _localctx = new FunctionCallContext(_ctx, getState()); + enterRule(_localctx, 22, RULE_functionCall); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(117); + functionName(); + setState(118); + match(LPAR); + setState(127); + _errHandler.sync(this); + _la = _input.LA(1); + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__0) | (1L << T__3) | (1L << NodeType) | (1L << Number) | (1L << AxisName) | (1L << PATHSEP) | (1L << ABRPATH) | (1L << LPAR) | (1L << MINUS) | (1L << DOT) | (1L << MUL) | (1L << DOTDOT) | (1L << AT) | (1L << Literal) | (1L << NCName))) != 0)) { + { + setState(119); + expr(); + setState(124); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la == COMMA) { + { + { + setState(120); + match(COMMA); + setState(121); + expr(); + } + } + setState(126); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + + setState(129); + match(RPAR); + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class UnionExprNoRootContext extends ParserRuleContext { + public Token op; + + public PathExprNoRootContext pathExprNoRoot() { + return getRuleContext(PathExprNoRootContext.class, 0); + } + + public UnionExprNoRootContext unionExprNoRoot() { + return getRuleContext(UnionExprNoRootContext.class, 0); + } + + public TerminalNode PIPE() { + return getToken(XpathParser.PIPE, 0); + } + + public TerminalNode PATHSEP() { + return getToken(XpathParser.PATHSEP, 0); + } + + public UnionExprNoRootContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_unionExprNoRoot; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).enterUnionExprNoRoot(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).exitUnionExprNoRoot(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitUnionExprNoRoot(this); + else return visitor.visitChildren(this); + } + } + + public final UnionExprNoRootContext unionExprNoRoot() throws RecognitionException { + UnionExprNoRootContext _localctx = new UnionExprNoRootContext(_ctx, getState()); + enterRule(_localctx, 24, RULE_unionExprNoRoot); + int _la; + try { + setState(139); + _errHandler.sync(this); + switch (getInterpreter().adaptivePredict(_input, 11, _ctx)) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(131); + pathExprNoRoot(); + setState(134); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la == PIPE) { + { + setState(132); + ((UnionExprNoRootContext) _localctx).op = match(PIPE); + setState(133); + unionExprNoRoot(); + } + } + + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(136); + match(PATHSEP); + setState(137); + match(PIPE); + setState(138); + unionExprNoRoot(); + } + break; + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class PathExprNoRootContext extends ParserRuleContext { + public Token op; + + public LocationPathContext locationPath() { + return getRuleContext(LocationPathContext.class, 0); + } + + public FilterExprContext filterExpr() { + return getRuleContext(FilterExprContext.class, 0); + } + + public RelativeLocationPathContext relativeLocationPath() { + return getRuleContext(RelativeLocationPathContext.class, 0); + } + + public TerminalNode PATHSEP() { + return getToken(XpathParser.PATHSEP, 0); + } + + public TerminalNode ABRPATH() { + return getToken(XpathParser.ABRPATH, 0); + } + + public PathExprNoRootContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_pathExprNoRoot; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).enterPathExprNoRoot(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).exitPathExprNoRoot(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitPathExprNoRoot(this); + else return visitor.visitChildren(this); + } + } + + public final PathExprNoRootContext pathExprNoRoot() throws RecognitionException { + PathExprNoRootContext _localctx = new PathExprNoRootContext(_ctx, getState()); + enterRule(_localctx, 26, RULE_pathExprNoRoot); + int _la; + try { + setState(147); + _errHandler.sync(this); + switch (getInterpreter().adaptivePredict(_input, 13, _ctx)) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(141); + locationPath(); + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(142); + filterExpr(); + setState(145); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la == PATHSEP || _la == ABRPATH) { + { + setState(143); + ((PathExprNoRootContext) _localctx).op = _input.LT(1); + _la = _input.LA(1); + if (!(_la == PATHSEP || _la == ABRPATH)) { + ((PathExprNoRootContext) _localctx).op = (Token) _errHandler.recoverInline(this); + } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + setState(144); + relativeLocationPath(); + } + } + + } + break; + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class FilterExprContext extends ParserRuleContext { + public PrimaryExprContext primaryExpr() { + return getRuleContext(PrimaryExprContext.class, 0); + } + + public List predicate() { + return getRuleContexts(PredicateContext.class); + } + + public PredicateContext predicate(int i) { + return getRuleContext(PredicateContext.class, i); + } + + public FilterExprContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_filterExpr; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).enterFilterExpr(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).exitFilterExpr(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitFilterExpr(this); + else return visitor.visitChildren(this); + } + } + + public final FilterExprContext filterExpr() throws RecognitionException { + FilterExprContext _localctx = new FilterExprContext(_ctx, getState()); + enterRule(_localctx, 28, RULE_filterExpr); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(149); + primaryExpr(); + setState(153); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la == LBRAC) { + { + { + setState(150); + predicate(); + } + } + setState(155); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class OrExprContext extends ParserRuleContext { + public List andExpr() { + return getRuleContexts(AndExprContext.class); + } + + public AndExprContext andExpr(int i) { + return getRuleContext(AndExprContext.class, i); + } + + public OrExprContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_orExpr; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).enterOrExpr(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).exitOrExpr(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitOrExpr(this); + else return visitor.visitChildren(this); + } + } + + public final OrExprContext orExpr() throws RecognitionException { + OrExprContext _localctx = new OrExprContext(_ctx, getState()); + enterRule(_localctx, 30, RULE_orExpr); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(156); + andExpr(); + setState(161); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la == T__1) { + { + { + setState(157); + match(T__1); + setState(158); + andExpr(); + } + } + setState(163); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class AndExprContext extends ParserRuleContext { + public List equalityExpr() { + return getRuleContexts(EqualityExprContext.class); + } + + public EqualityExprContext equalityExpr(int i) { + return getRuleContext(EqualityExprContext.class, i); + } + + public AndExprContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_andExpr; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).enterAndExpr(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).exitAndExpr(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitAndExpr(this); + else return visitor.visitChildren(this); + } + } + + public final AndExprContext andExpr() throws RecognitionException { + AndExprContext _localctx = new AndExprContext(_ctx, getState()); + enterRule(_localctx, 32, RULE_andExpr); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(164); + equalityExpr(); + setState(169); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la == T__2) { + { + { + setState(165); + match(T__2); + setState(166); + equalityExpr(); + } + } + setState(171); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class EqualityExprContext extends ParserRuleContext { + public Token op; + + public List relationalExpr() { + return getRuleContexts(RelationalExprContext.class); + } + + public RelationalExprContext relationalExpr(int i) { + return getRuleContext(RelationalExprContext.class, i); + } + + public List EQUALITY() { + return getTokens(XpathParser.EQUALITY); + } + + public TerminalNode EQUALITY(int i) { + return getToken(XpathParser.EQUALITY, i); + } + + public List INEQUALITY() { + return getTokens(XpathParser.INEQUALITY); + } + + public TerminalNode INEQUALITY(int i) { + return getToken(XpathParser.INEQUALITY, i); + } + + public EqualityExprContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_equalityExpr; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).enterEqualityExpr(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).exitEqualityExpr(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitEqualityExpr(this); + else return visitor.visitChildren(this); + } + } + + public final EqualityExprContext equalityExpr() throws RecognitionException { + EqualityExprContext _localctx = new EqualityExprContext(_ctx, getState()); + enterRule(_localctx, 34, RULE_equalityExpr); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(172); + relationalExpr(); + setState(177); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la == EQUALITY || _la == INEQUALITY) { + { + { + setState(173); + ((EqualityExprContext) _localctx).op = _input.LT(1); + _la = _input.LA(1); + if (!(_la == EQUALITY || _la == INEQUALITY)) { + ((EqualityExprContext) _localctx).op = (Token) _errHandler.recoverInline(this); + } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + setState(174); + relationalExpr(); + } + } + setState(179); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class RelationalExprContext extends ParserRuleContext { + public Token op; + + public List additiveExpr() { + return getRuleContexts(AdditiveExprContext.class); + } + + public AdditiveExprContext additiveExpr(int i) { + return getRuleContext(AdditiveExprContext.class, i); + } + + public List LESS() { + return getTokens(XpathParser.LESS); + } + + public TerminalNode LESS(int i) { + return getToken(XpathParser.LESS, i); + } + + public List MORE_() { + return getTokens(XpathParser.MORE_); + } + + public TerminalNode MORE_(int i) { + return getToken(XpathParser.MORE_, i); + } + + public List GE() { + return getTokens(XpathParser.GE); + } + + public TerminalNode GE(int i) { + return getToken(XpathParser.GE, i); + } + + public List START_WITH() { + return getTokens(XpathParser.START_WITH); + } + + public TerminalNode START_WITH(int i) { + return getToken(XpathParser.START_WITH, i); + } + + public List END_WITH() { + return getTokens(XpathParser.END_WITH); + } + + public TerminalNode END_WITH(int i) { + return getToken(XpathParser.END_WITH, i); + } + + public List CONTAIN_WITH() { + return getTokens(XpathParser.CONTAIN_WITH); + } + + public TerminalNode CONTAIN_WITH(int i) { + return getToken(XpathParser.CONTAIN_WITH, i); + } + + public List REGEXP_WITH() { + return getTokens(XpathParser.REGEXP_WITH); + } + + public TerminalNode REGEXP_WITH(int i) { + return getToken(XpathParser.REGEXP_WITH, i); + } + + public List REGEXP_NOT_WITH() { + return getTokens(XpathParser.REGEXP_NOT_WITH); + } + + public TerminalNode REGEXP_NOT_WITH(int i) { + return getToken(XpathParser.REGEXP_NOT_WITH, i); + } + + public RelationalExprContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_relationalExpr; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).enterRelationalExpr(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).exitRelationalExpr(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitRelationalExpr(this); + else return visitor.visitChildren(this); + } + } + + public final RelationalExprContext relationalExpr() throws RecognitionException { + RelationalExprContext _localctx = new RelationalExprContext(_ctx, getState()); + enterRule(_localctx, 36, RULE_relationalExpr); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(180); + additiveExpr(); + setState(185); + _errHandler.sync(this); + _la = _input.LA(1); + while ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << LESS) | (1L << MORE_) | (1L << GE) | (1L << START_WITH) | (1L << END_WITH) | (1L << CONTAIN_WITH) | (1L << REGEXP_WITH) | (1L << REGEXP_NOT_WITH))) != 0)) { + { + { + setState(181); + ((RelationalExprContext) _localctx).op = _input.LT(1); + _la = _input.LA(1); + if (!((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << LESS) | (1L << MORE_) | (1L << GE) | (1L << START_WITH) | (1L << END_WITH) | (1L << CONTAIN_WITH) | (1L << REGEXP_WITH) | (1L << REGEXP_NOT_WITH))) != 0))) { + ((RelationalExprContext) _localctx).op = (Token) _errHandler.recoverInline(this); + } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + setState(182); + additiveExpr(); + } + } + setState(187); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class AdditiveExprContext extends ParserRuleContext { + public Token op; + + public List multiplicativeExpr() { + return getRuleContexts(MultiplicativeExprContext.class); + } + + public MultiplicativeExprContext multiplicativeExpr(int i) { + return getRuleContext(MultiplicativeExprContext.class, i); + } + + public List PLUS() { + return getTokens(XpathParser.PLUS); + } + + public TerminalNode PLUS(int i) { + return getToken(XpathParser.PLUS, i); + } + + public List MINUS() { + return getTokens(XpathParser.MINUS); + } + + public TerminalNode MINUS(int i) { + return getToken(XpathParser.MINUS, i); + } + + public AdditiveExprContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_additiveExpr; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).enterAdditiveExpr(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).exitAdditiveExpr(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitAdditiveExpr(this); + else return visitor.visitChildren(this); + } + } + + public final AdditiveExprContext additiveExpr() throws RecognitionException { + AdditiveExprContext _localctx = new AdditiveExprContext(_ctx, getState()); + enterRule(_localctx, 38, RULE_additiveExpr); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(188); + multiplicativeExpr(); + setState(193); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la == MINUS || _la == PLUS) { + { + { + setState(189); + ((AdditiveExprContext) _localctx).op = _input.LT(1); + _la = _input.LA(1); + if (!(_la == MINUS || _la == PLUS)) { + ((AdditiveExprContext) _localctx).op = (Token) _errHandler.recoverInline(this); + } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + setState(190); + multiplicativeExpr(); + } + } + setState(195); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class MultiplicativeExprContext extends ParserRuleContext { + public Token op; + + public UnaryExprNoRootContext unaryExprNoRoot() { + return getRuleContext(UnaryExprNoRootContext.class, 0); + } + + public MultiplicativeExprContext multiplicativeExpr() { + return getRuleContext(MultiplicativeExprContext.class, 0); + } + + public TerminalNode MUL() { + return getToken(XpathParser.MUL, 0); + } + + public TerminalNode DIVISION() { + return getToken(XpathParser.DIVISION, 0); + } + + public TerminalNode MODULO() { + return getToken(XpathParser.MODULO, 0); + } + + public MultiplicativeExprContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_multiplicativeExpr; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).enterMultiplicativeExpr(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).exitMultiplicativeExpr(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitMultiplicativeExpr(this); + else return visitor.visitChildren(this); + } + } + + public final MultiplicativeExprContext multiplicativeExpr() throws RecognitionException { + MultiplicativeExprContext _localctx = new MultiplicativeExprContext(_ctx, getState()); + enterRule(_localctx, 40, RULE_multiplicativeExpr); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(196); + unaryExprNoRoot(); + setState(199); + _errHandler.sync(this); + _la = _input.LA(1); + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << MUL) | (1L << DIVISION) | (1L << MODULO))) != 0)) { + { + setState(197); + ((MultiplicativeExprContext) _localctx).op = _input.LT(1); + _la = _input.LA(1); + if (!((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << MUL) | (1L << DIVISION) | (1L << MODULO))) != 0))) { + ((MultiplicativeExprContext) _localctx).op = (Token) _errHandler.recoverInline(this); + } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + setState(198); + multiplicativeExpr(); + } + } + + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class UnaryExprNoRootContext extends ParserRuleContext { + public Token sign; + + public UnionExprNoRootContext unionExprNoRoot() { + return getRuleContext(UnionExprNoRootContext.class, 0); + } + + public TerminalNode MINUS() { + return getToken(XpathParser.MINUS, 0); + } + + public UnaryExprNoRootContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_unaryExprNoRoot; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).enterUnaryExprNoRoot(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).exitUnaryExprNoRoot(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitUnaryExprNoRoot(this); + else return visitor.visitChildren(this); + } + } + + public final UnaryExprNoRootContext unaryExprNoRoot() throws RecognitionException { + UnaryExprNoRootContext _localctx = new UnaryExprNoRootContext(_ctx, getState()); + enterRule(_localctx, 42, RULE_unaryExprNoRoot); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(202); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la == MINUS) { + { + setState(201); + ((UnaryExprNoRootContext) _localctx).sign = match(MINUS); + } + } + + setState(204); + unionExprNoRoot(); + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class QNameContext extends ParserRuleContext { + public List nCName() { + return getRuleContexts(NCNameContext.class); + } + + public NCNameContext nCName(int i) { + return getRuleContext(NCNameContext.class, i); + } + + public TerminalNode COLON() { + return getToken(XpathParser.COLON, 0); + } + + public QNameContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_qName; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).enterQName(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).exitQName(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitQName(this); + else return visitor.visitChildren(this); + } + } + + public final QNameContext qName() throws RecognitionException { + QNameContext _localctx = new QNameContext(_ctx, getState()); + enterRule(_localctx, 44, RULE_qName); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(206); + nCName(); + setState(209); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la == COLON) { + { + setState(207); + match(COLON); + setState(208); + nCName(); + } + } + + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class FunctionNameContext extends ParserRuleContext { + public QNameContext qName() { + return getRuleContext(QNameContext.class, 0); + } + + public FunctionNameContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_functionName; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).enterFunctionName(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).exitFunctionName(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitFunctionName(this); + else return visitor.visitChildren(this); + } + } + + public final FunctionNameContext functionName() throws RecognitionException { + FunctionNameContext _localctx = new FunctionNameContext(_ctx, getState()); + enterRule(_localctx, 46, RULE_functionName); + try { + enterOuterAlt(_localctx, 1); + { + setState(211); + qName(); + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class VariableReferenceContext extends ParserRuleContext { + public QNameContext qName() { + return getRuleContext(QNameContext.class, 0); + } + + public VariableReferenceContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_variableReference; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).enterVariableReference(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) + ((XpathListener) listener).exitVariableReference(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitVariableReference(this); + else return visitor.visitChildren(this); + } + } + + public final VariableReferenceContext variableReference() throws RecognitionException { + VariableReferenceContext _localctx = new VariableReferenceContext(_ctx, getState()); + enterRule(_localctx, 48, RULE_variableReference); + try { + enterOuterAlt(_localctx, 1); + { + setState(213); + match(T__3); + setState(214); + qName(); + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class NameTestContext extends ParserRuleContext { + public TerminalNode MUL() { + return getToken(XpathParser.MUL, 0); + } + + public NCNameContext nCName() { + return getRuleContext(NCNameContext.class, 0); + } + + public TerminalNode COLON() { + return getToken(XpathParser.COLON, 0); + } + + public QNameContext qName() { + return getRuleContext(QNameContext.class, 0); + } + + public NameTestContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_nameTest; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).enterNameTest(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).exitNameTest(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitNameTest(this); + else return visitor.visitChildren(this); + } + } + + public final NameTestContext nameTest() throws RecognitionException { + NameTestContext _localctx = new NameTestContext(_ctx, getState()); + enterRule(_localctx, 50, RULE_nameTest); + try { + setState(222); + _errHandler.sync(this); + switch (getInterpreter().adaptivePredict(_input, 23, _ctx)) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(216); + match(MUL); + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(217); + nCName(); + setState(218); + match(COLON); + setState(219); + match(MUL); + } + break; + case 3: + enterOuterAlt(_localctx, 3); + { + setState(221); + qName(); + } + break; + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static class NCNameContext extends ParserRuleContext { + public TerminalNode NCName() { + return getToken(XpathParser.NCName, 0); + } + + public TerminalNode AxisName() { + return getToken(XpathParser.AxisName, 0); + } + + public NCNameContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + + @Override + public int getRuleIndex() { + return RULE_nCName; + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).enterNCName(this); + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof XpathListener) ((XpathListener) listener).exitNCName(this); + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof XpathVisitor) + return ((XpathVisitor) visitor).visitNCName(this); + else return visitor.visitChildren(this); + } + } + + public final NCNameContext nCName() throws RecognitionException { + NCNameContext _localctx = new NCNameContext(_ctx, getState()); + enterRule(_localctx, 52, RULE_nCName); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(224); + _la = _input.LA(1); + if (!(_la == AxisName || _la == NCName)) { + _errHandler.recoverInline(this); + } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } finally { + exitRule(); + } + return _localctx; + } + + public static final String _serializedATN = + "\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3+\u00e5\4\2\t\2\4" + + "\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t" + + "\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22" + + "\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31" + + "\4\32\t\32\4\33\t\33\4\34\t\34\3\2\3\2\3\3\3\3\5\3=\n\3\3\4\3\4\3\4\3" + + "\5\3\5\3\5\7\5E\n\5\f\5\16\5H\13\5\3\6\3\6\3\6\7\6M\n\6\f\6\16\6P\13\6" + + "\3\6\5\6S\n\6\3\7\3\7\3\7\5\7X\n\7\5\7Z\n\7\3\b\3\b\3\b\3\b\3\b\3\b\3" + + "\b\3\b\5\bd\n\b\3\t\3\t\3\t\3\t\3\n\3\n\3\13\3\13\3\f\3\f\3\f\3\f\3\f" + + "\3\f\3\f\3\f\5\fv\n\f\3\r\3\r\3\r\3\r\3\r\7\r}\n\r\f\r\16\r\u0080\13\r" + + "\5\r\u0082\n\r\3\r\3\r\3\16\3\16\3\16\5\16\u0089\n\16\3\16\3\16\3\16\5" + + "\16\u008e\n\16\3\17\3\17\3\17\3\17\5\17\u0094\n\17\5\17\u0096\n\17\3\20" + + "\3\20\7\20\u009a\n\20\f\20\16\20\u009d\13\20\3\21\3\21\3\21\7\21\u00a2" + + "\n\21\f\21\16\21\u00a5\13\21\3\22\3\22\3\22\7\22\u00aa\n\22\f\22\16\22" + + "\u00ad\13\22\3\23\3\23\3\23\7\23\u00b2\n\23\f\23\16\23\u00b5\13\23\3\24" + + "\3\24\3\24\7\24\u00ba\n\24\f\24\16\24\u00bd\13\24\3\25\3\25\3\25\7\25" + + "\u00c2\n\25\f\25\16\25\u00c5\13\25\3\26\3\26\3\26\5\26\u00ca\n\26\3\27" + + "\5\27\u00cd\n\27\3\27\3\27\3\30\3\30\3\30\5\30\u00d4\n\30\3\31\3\31\3" + + "\32\3\32\3\32\3\33\3\33\3\33\3\33\3\33\3\33\5\33\u00e1\n\33\3\34\3\34" + + "\3\34\2\2\35\2\4\6\b\n\f\16\20\22\24\26\30\32\34\36 \"$&(*,.\60\62\64" + + "\66\2\t\3\2\n\13\4\2\22\22\26\26\3\2\36\37\5\2\32\33\35\35 $\3\2\20\21" + + "\3\2\23\25\4\2\t\t++\2\u00e6\28\3\2\2\2\4<\3\2\2\2\6>\3\2\2\2\bA\3\2\2" + + "\2\nR\3\2\2\2\fY\3\2\2\2\16c\3\2\2\2\20e\3\2\2\2\22i\3\2\2\2\24k\3\2\2" + + "\2\26u\3\2\2\2\30w\3\2\2\2\32\u008d\3\2\2\2\34\u0095\3\2\2\2\36\u0097" + + "\3\2\2\2 \u009e\3\2\2\2\"\u00a6\3\2\2\2$\u00ae\3\2\2\2&\u00b6\3\2\2\2" + + "(\u00be\3\2\2\2*\u00c6\3\2\2\2,\u00cc\3\2\2\2.\u00d0\3\2\2\2\60\u00d5" + + "\3\2\2\2\62\u00d7\3\2\2\2\64\u00e0\3\2\2\2\66\u00e2\3\2\2\289\5\24\13" + + "\29\3\3\2\2\2:=\5\b\5\2;=\5\6\4\2<:\3\2\2\2<;\3\2\2\2=\5\3\2\2\2>?\t\2" + + "\2\2?@\5\b\5\2@\7\3\2\2\2AF\5\n\6\2BC\t\2\2\2CE\5\n\6\2DB\3\2\2\2EH\3" + + "\2\2\2FD\3\2\2\2FG\3\2\2\2G\t\3\2\2\2HF\3\2\2\2IJ\5\f\7\2JN\5\16\b\2K" + + "M\5\20\t\2LK\3\2\2\2MP\3\2\2\2NL\3\2\2\2NO\3\2\2\2OS\3\2\2\2PN\3\2\2\2" + + "QS\5\22\n\2RI\3\2\2\2RQ\3\2\2\2S\13\3\2\2\2TU\7\t\2\2UZ\7&\2\2VX\7\27" + + "\2\2WV\3\2\2\2WX\3\2\2\2XZ\3\2\2\2YT\3\2\2\2YW\3\2\2\2Z\r\3\2\2\2[d\5" + + "\64\33\2\\]\7\7\2\2]^\7\f\2\2^d\7\r\2\2_`\7\3\2\2`a\7\f\2\2ab\7)\2\2b" + + "d\7\r\2\2c[\3\2\2\2c\\\3\2\2\2c_\3\2\2\2d\17\3\2\2\2ef\7\16\2\2fg\5\24" + + "\13\2gh\7\17\2\2h\21\3\2\2\2ij\t\3\2\2j\23\3\2\2\2kl\5 \21\2l\25\3\2\2" + + "\2mv\5\62\32\2no\7\f\2\2op\5\24\13\2pq\7\r\2\2qv\3\2\2\2rv\7)\2\2sv\7" + + "\b\2\2tv\5\30\r\2um\3\2\2\2un\3\2\2\2ur\3\2\2\2us\3\2\2\2ut\3\2\2\2v\27" + + "\3\2\2\2wx\5\60\31\2x\u0081\7\f\2\2y~\5\24\13\2z{\7\30\2\2{}\5\24\13\2" + + "|z\3\2\2\2}\u0080\3\2\2\2~|\3\2\2\2~\177\3\2\2\2\177\u0082\3\2\2\2\u0080" + + "~\3\2\2\2\u0081y\3\2\2\2\u0081\u0082\3\2\2\2\u0082\u0083\3\2\2\2\u0083" + + "\u0084\7\r\2\2\u0084\31\3\2\2\2\u0085\u0088\5\34\17\2\u0086\u0087\7\31" + + "\2\2\u0087\u0089\5\32\16\2\u0088\u0086\3\2\2\2\u0088\u0089\3\2\2\2\u0089" + + "\u008e\3\2\2\2\u008a\u008b\7\n\2\2\u008b\u008c\7\31\2\2\u008c\u008e\5" + + "\32\16\2\u008d\u0085\3\2\2\2\u008d\u008a\3\2\2\2\u008e\33\3\2\2\2\u008f" + + "\u0096\5\4\3\2\u0090\u0093\5\36\20\2\u0091\u0092\t\2\2\2\u0092\u0094\5" + + "\b\5\2\u0093\u0091\3\2\2\2\u0093\u0094\3\2\2\2\u0094\u0096\3\2\2\2\u0095" + + "\u008f\3\2\2\2\u0095\u0090\3\2\2\2\u0096\35\3\2\2\2\u0097\u009b\5\26\f" + + "\2\u0098\u009a\5\20\t\2\u0099\u0098\3\2\2\2\u009a\u009d\3\2\2\2\u009b" + + "\u0099\3\2\2\2\u009b\u009c\3\2\2\2\u009c\37\3\2\2\2\u009d\u009b\3\2\2" + + "\2\u009e\u00a3\5\"\22\2\u009f\u00a0\7\4\2\2\u00a0\u00a2\5\"\22\2\u00a1" + + "\u009f\3\2\2\2\u00a2\u00a5\3\2\2\2\u00a3\u00a1\3\2\2\2\u00a3\u00a4\3\2" + + "\2\2\u00a4!\3\2\2\2\u00a5\u00a3\3\2\2\2\u00a6\u00ab\5$\23\2\u00a7\u00a8" + + "\7\5\2\2\u00a8\u00aa\5$\23\2\u00a9\u00a7\3\2\2\2\u00aa\u00ad\3\2\2\2\u00ab" + + "\u00a9\3\2\2\2\u00ab\u00ac\3\2\2\2\u00ac#\3\2\2\2\u00ad\u00ab\3\2\2\2" + + "\u00ae\u00b3\5&\24\2\u00af\u00b0\t\4\2\2\u00b0\u00b2\5&\24\2\u00b1\u00af" + + "\3\2\2\2\u00b2\u00b5\3\2\2\2\u00b3\u00b1\3\2\2\2\u00b3\u00b4\3\2\2\2\u00b4" + + "%\3\2\2\2\u00b5\u00b3\3\2\2\2\u00b6\u00bb\5(\25\2\u00b7\u00b8\t\5\2\2" + + "\u00b8\u00ba\5(\25\2\u00b9\u00b7\3\2\2\2\u00ba\u00bd\3\2\2\2\u00bb\u00b9" + + "\3\2\2\2\u00bb\u00bc\3\2\2\2\u00bc\'\3\2\2\2\u00bd\u00bb\3\2\2\2\u00be" + + "\u00c3\5*\26\2\u00bf\u00c0\t\6\2\2\u00c0\u00c2\5*\26\2\u00c1\u00bf\3\2" + + "\2\2\u00c2\u00c5\3\2\2\2\u00c3\u00c1\3\2\2\2\u00c3\u00c4\3\2\2\2\u00c4" + + ")\3\2\2\2\u00c5\u00c3\3\2\2\2\u00c6\u00c9\5,\27\2\u00c7\u00c8\t\7\2\2" + + "\u00c8\u00ca\5*\26\2\u00c9\u00c7\3\2\2\2\u00c9\u00ca\3\2\2\2\u00ca+\3" + + "\2\2\2\u00cb\u00cd\7\20\2\2\u00cc\u00cb\3\2\2\2\u00cc\u00cd\3\2\2\2\u00cd" + + "\u00ce\3\2\2\2\u00ce\u00cf\5\32\16\2\u00cf-\3\2\2\2\u00d0\u00d3\5\66\34" + + "\2\u00d1\u00d2\7%\2\2\u00d2\u00d4\5\66\34\2\u00d3\u00d1\3\2\2\2\u00d3" + + "\u00d4\3\2\2\2\u00d4/\3\2\2\2\u00d5\u00d6\5.\30\2\u00d6\61\3\2\2\2\u00d7" + + "\u00d8\7\6\2\2\u00d8\u00d9\5.\30\2\u00d9\63\3\2\2\2\u00da\u00e1\7\23\2" + + "\2\u00db\u00dc\5\66\34\2\u00dc\u00dd\7%\2\2\u00dd\u00de\7\23\2\2\u00de" + + "\u00e1\3\2\2\2\u00df\u00e1\5.\30\2\u00e0\u00da\3\2\2\2\u00e0\u00db\3\2" + + "\2\2\u00e0\u00df\3\2\2\2\u00e1\65\3\2\2\2\u00e2\u00e3\t\b\2\2\u00e3\67" + + "\3\2\2\2\32 The return type of the visit operation. Use {@link Void} for + * operations with no return type. + */ +public interface XpathVisitor extends ParseTreeVisitor { + /** + * Visit a parse tree produced by {@link XpathParser#main}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitMain(XpathParser.MainContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#locationPath}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitLocationPath(XpathParser.LocationPathContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#absoluteLocationPathNoroot}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitAbsoluteLocationPathNoroot(XpathParser.AbsoluteLocationPathNorootContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#relativeLocationPath}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitRelativeLocationPath(XpathParser.RelativeLocationPathContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#step}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitStep(XpathParser.StepContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#axisSpecifier}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitAxisSpecifier(XpathParser.AxisSpecifierContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#nodeTest}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitNodeTest(XpathParser.NodeTestContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#predicate}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitPredicate(XpathParser.PredicateContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#abbreviatedStep}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitAbbreviatedStep(XpathParser.AbbreviatedStepContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#expr}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitExpr(XpathParser.ExprContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#primaryExpr}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitPrimaryExpr(XpathParser.PrimaryExprContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#functionCall}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitFunctionCall(XpathParser.FunctionCallContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#unionExprNoRoot}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitUnionExprNoRoot(XpathParser.UnionExprNoRootContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#pathExprNoRoot}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitPathExprNoRoot(XpathParser.PathExprNoRootContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#filterExpr}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitFilterExpr(XpathParser.FilterExprContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#orExpr}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitOrExpr(XpathParser.OrExprContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#andExpr}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitAndExpr(XpathParser.AndExprContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#equalityExpr}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitEqualityExpr(XpathParser.EqualityExprContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#relationalExpr}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitRelationalExpr(XpathParser.RelationalExprContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#additiveExpr}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitAdditiveExpr(XpathParser.AdditiveExprContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#multiplicativeExpr}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitMultiplicativeExpr(XpathParser.MultiplicativeExprContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#unaryExprNoRoot}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitUnaryExprNoRoot(XpathParser.UnaryExprNoRootContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#qName}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitQName(XpathParser.QNameContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#functionName}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitFunctionName(XpathParser.FunctionNameContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#variableReference}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitVariableReference(XpathParser.VariableReferenceContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#nameTest}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitNameTest(XpathParser.NameTestContext ctx); + + /** + * Visit a parse tree produced by {@link XpathParser#nCName}. + * + * @param ctx the parse tree + * @return the visitor result + */ + T visitNCName(XpathParser.NCNameContext ctx); +} \ No newline at end of file diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/AxisSelector.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/AxisSelector.java new file mode 100644 index 000000000..fa726fcb8 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/AxisSelector.java @@ -0,0 +1,24 @@ +package org.seimicrawler.xpath.core; + +import org.jsoup.select.Elements; + +/** + * https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-AxisSpecifier + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/2/28. + */ +public interface AxisSelector { + /** + * assign name + * + * @return name + */ + String name(); + + /** + * @param context + * @return res + */ + XValue apply(Elements context); +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/Constants.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/Constants.java new file mode 100644 index 000000000..617de970f --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/Constants.java @@ -0,0 +1,10 @@ +package org.seimicrawler.xpath.core; + +/** + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2020/9/24. + */ +public class Constants { + public final static String DEF_TEXT_TAG_NAME = "JX_TEXT"; + public final static String EL_SAME_TAG_INDEX_KEY = "EL_SAME_TAG_INDEX_KEY"; +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/Function.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/Function.java new file mode 100644 index 000000000..e1cd6ff60 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/Function.java @@ -0,0 +1,13 @@ +package org.seimicrawler.xpath.core; + +import java.util.List; + +/** + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/2/28. + */ +public interface Function { + String name(); + + XValue call(Scope scope, List params); +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/NodeTest.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/NodeTest.java new file mode 100644 index 000000000..99ca5c0b7 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/NodeTest.java @@ -0,0 +1,21 @@ +package org.seimicrawler.xpath.core; + + +/** + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/2/28. + */ +public interface NodeTest { + /** + * 支持的函数名 + */ + String name(); + + /** + * 函数具体逻辑 + * + * @param scope 上下文 + * @return 计算好的节点 + */ + XValue call(Scope scope); +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/Scope.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/Scope.java new file mode 100644 index 000000000..fda915c8d --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/Scope.java @@ -0,0 +1,76 @@ +package org.seimicrawler.xpath.core; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.exception.XpathParserException; + +/** + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/13. + */ +public class Scope { + private Elements context; + private boolean isRecursion = false; + private Scope parent; + + private Scope(Elements context) { + super(); + this.context = new Elements(); + this.context.addAll(context); + } + + private Scope(Element context) { + super(); + this.context = new Elements(); + this.context.add(context); + } + + public static Scope create(Elements elements) { + return new Scope(elements); + } + + public static Scope create(Element el) { + return new Scope(el); + } + + public static Scope create(Scope scope) { + return new Scope(scope.context()).setParent(scope); + } + + public Scope setParent(Scope scope) { + this.parent = scope; + return this; + } + + public Scope getParent() { + return parent; + } + + public void setContext(Elements context) { + this.context = context; + } + + public boolean isRecursion() { + return isRecursion; + } + + void recursion() { + this.isRecursion = true; + } + + public void notRecursion() { + this.isRecursion = false; + } + + public Elements context() { + return this.context; + } + + public Element singleEl() { + if (context.size() == 1) { + return context.first(); + } + throw new XpathParserException("current context is more than one el,total = " + context.size()); + } +} + diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/XValue.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/XValue.java new file mode 100644 index 000000000..2b30bc23b --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/XValue.java @@ -0,0 +1,226 @@ +package org.seimicrawler.xpath.core; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.exception.XpathParserException; + +import java.math.BigDecimal; +import java.text.ParseException; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +/** + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2017/12/5. + */ +public class XValue implements Comparable { + public XValue(Object val) { + this.value = val; + } + + public static XValue create(Object val) { + return new XValue(val); + } + + private Object value; + + private boolean isAttr = false; + private boolean isExprStr = false; + + /** + * 同类型节点的顺序 + */ + private int siblingIndex; + + /** + * 满足特殊场景计算筛选需求 + */ + private List xValues; + + public boolean isBoolean() { + return value instanceof Boolean; + } + + public boolean isNumber() { + return value instanceof Number; + } + + public boolean isElements() { + return value instanceof Elements; + } + + public boolean isString() { + return value instanceof String; + } + + public boolean isList() { + return value instanceof List; + } + + public boolean isDate() { + return value instanceof Date; + } + + public Boolean asBoolean() { + if (value instanceof Boolean) { + return (Boolean) value; + } + return value != null && !StringUtils.isBlank(asString()); + } + + public Date asDate() { + if (value instanceof String) { + try { + return DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.parse((String) value); + } catch (ParseException e) { + throw new XpathParserException("cast to date fail. vale = " + value); + } + } + if (value instanceof Date) { + return (Date) value; + } + throw new XpathParserException("cast to date fail. vale = " + value); + } + + public Double asDouble() { + if (value instanceof String) { + return new BigDecimal((String) value).doubleValue(); + } else if (value instanceof Number) { + return ((Number) value).doubleValue(); + } else { + throw new XpathParserException("cast to number fail. vale = " + value); + } + } + + public Long asLong() { + if (value instanceof String) { + //对于带小数点的,四舍五入 + return new BigDecimal((String) value).setScale(0, BigDecimal.ROUND_HALF_UP).longValue(); + } else if (value instanceof Number) { + return ((Number) value).longValue(); + } else { + throw new XpathParserException("cast to number fail. vale = " + value); + } + } + + public Elements asElements() { + return (Elements) value; + } + + public String asString() { + if (isElements()) { + StringBuilder accum = new StringBuilder(); + for (Element e : asElements()) { + accum.append(e.ownText()); + } + return accum.toString(); + } else if (value instanceof Element && Objects.equals(((Element) value).tagName(), Constants.DEF_TEXT_TAG_NAME)) { + return ((Element) value).ownText(); + } else if (value instanceof List) { + return StringUtils.join((List) value, ","); + } else { + return String.valueOf(value).trim(); + } + } + + public List asList() { + return (List) value; + } + + public XValue attr() { + this.isAttr = true; + return this; + } + + public boolean isAttr() { + return isAttr; + } + + public XValue exprStr() { + this.isExprStr = true; + String str = StringUtils.removeStart(String.valueOf(this.value), "'"); + str = StringUtils.removeStart(str, "\""); + str = StringUtils.removeEnd(str, "'"); + this.value = StringUtils.removeEnd(str, "\""); + return this; + } + + public boolean isExprStr() { + return isExprStr; + } + + public int getSiblingIndex() { + return siblingIndex; + } + + public void setSiblingIndex(int siblingIndex) { + this.siblingIndex = siblingIndex; + } + + public List getxValues() { + return xValues; + } + + public void setxValues(List xValues) { + this.xValues = xValues; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("value", value) + .append("isAttr", isAttr) + .append("isExprStr", isExprStr) + .toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + XValue value1 = (XValue) o; + return Objects.equals(value, value1.value); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + + @Override + public int compareTo(XValue o) { + if (this.equals(o)) { + return 0; + } + if (o == null || o.value == null) { + return 1; + } + if (this.value == null) { + return -1; + } + + if (isString()) { + return asString().compareTo(o.asString()); + } else if (isNumber()) { + return asDouble().compareTo(o.asDouble()); + } else { + throw new XpathParserException("Unsupported comparable XValue = " + toString()); + } + } + + public Class valType() { + if (value == null) { + return Object.class; + } + return value.getClass(); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/XpathProcessor.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/XpathProcessor.java new file mode 100644 index 000000000..c3480546f --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/XpathProcessor.java @@ -0,0 +1,613 @@ +package org.seimicrawler.xpath.core; + +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.TerminalNode; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.antlr.XpathBaseVisitor; +import org.seimicrawler.xpath.antlr.XpathParser; +import org.seimicrawler.xpath.exception.XpathMergeValueException; +import org.seimicrawler.xpath.exception.XpathParserException; +import org.seimicrawler.xpath.util.CommonUtil; +import org.seimicrawler.xpath.util.Scanner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.Stack; + +import static org.seimicrawler.xpath.antlr.XpathParser.CONTAIN_WITH; +import static org.seimicrawler.xpath.antlr.XpathParser.DIVISION; +import static org.seimicrawler.xpath.antlr.XpathParser.END_WITH; +import static org.seimicrawler.xpath.antlr.XpathParser.GE; +import static org.seimicrawler.xpath.antlr.XpathParser.LE; +import static org.seimicrawler.xpath.antlr.XpathParser.LESS; +import static org.seimicrawler.xpath.antlr.XpathParser.MODULO; +import static org.seimicrawler.xpath.antlr.XpathParser.MORE_; +import static org.seimicrawler.xpath.antlr.XpathParser.MUL; +import static org.seimicrawler.xpath.antlr.XpathParser.REGEXP_NOT_WITH; +import static org.seimicrawler.xpath.antlr.XpathParser.REGEXP_WITH; +import static org.seimicrawler.xpath.antlr.XpathParser.START_WITH; + +/** + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2017/8/30. + */ +public class XpathProcessor extends XpathBaseVisitor { + private final Logger logger = LoggerFactory.getLogger(XpathProcessor.class); + private final Stack scopeStack = new Stack<>(); + private final Scope rootScope; + + public XpathProcessor(Elements root) { + rootScope = Scope.create(root); + scopeStack.push(Scope.create(root).setParent(rootScope)); + } + + @Override + public XValue visitMain(XpathParser.MainContext ctx) { + return visit(ctx.expr()); + } + + @Override + public XValue visitLocationPath(XpathParser.LocationPathContext ctx) { + if (ctx.relativeLocationPath() != null && !ctx.relativeLocationPath().isEmpty()) { + return visit(ctx.relativeLocationPath()); + } + return visit(ctx.absoluteLocationPathNoroot()); + } + + @Override + public XValue visitAbsoluteLocationPathNoroot(XpathParser.AbsoluteLocationPathNorootContext ctx) { + // '//' + if (Objects.equals(ctx.op.getText(), "//")) { + currentScope().recursion(); + } + return visit(ctx.relativeLocationPath()); + } + + @Override + public XValue visitRelativeLocationPath(XpathParser.RelativeLocationPathContext ctx) { + XValue finalVal = null; + for (int i = 0; i < ctx.getChildCount(); i++) { + ParseTree step = ctx.getChild(i); + if (step instanceof XpathParser.StepContext) { + finalVal = visit(step); + if (finalVal.isElements()) { + updateCurrentContext(finalVal.asElements()); + } + } else { + if ("//".equals(step.getText())) { + currentScope().recursion(); + } else { + currentScope().notRecursion(); + } + } + } + return finalVal; + } + + @Override + public XValue visitStep(XpathParser.StepContext ctx) { + if (ctx.abbreviatedStep() != null && !ctx.abbreviatedStep().isEmpty()) { + return visit(ctx.abbreviatedStep()); + } + boolean filterByAttr = false; + boolean isAxisOk = false; + if (ctx.axisSpecifier() != null && !ctx.axisSpecifier().isEmpty()) { + XValue axis = visit(ctx.axisSpecifier()); + if (axis != null) { + isAxisOk = true; + if (axis.isElements()) { + updateCurrentContext(axis.asElements()); + } else if (axis.isAttr()) { + filterByAttr = true; + } + } + } + if (ctx.nodeTest() != null && !ctx.nodeTest().isEmpty()) { + XValue nodeTest = visit(ctx.nodeTest()); + if (filterByAttr) { + Elements context = currentScope().context(); + String attrName = nodeTest.asString(); + if (currentScope().isRecursion()) { + if (context.size() == 1) { + Element el = currentScope().singleEl(); + Elements findRes = el.select("[" + attrName + "]"); + List attrs = new LinkedList<>(); + for (Element e : findRes) { + attrs.add(e.attr(attrName)); + } + return XValue.create(attrs); + } else { + Elements findRes = new Elements(); + for (Element el : context) { + findRes.addAll(el.select("[" + attrName + "]")); + } + List attrs = new LinkedList<>(); + for (Element e : findRes) { + attrs.add(e.attr(attrName)); + } + return XValue.create(attrs); + } + } else { + if (context.size() == 1) { + Element el = currentScope().singleEl(); + return XValue.create(el.attr(attrName)); + } else { + List attrs = new LinkedList<>(); + for (Element el : context) { + attrs.add(el.attr(attrName)); + } + return XValue.create(attrs); + } + } + + } else { + if (nodeTest.isExprStr()) { + String tagName = nodeTest.asString(); + Elements current = currentScope().context(); + if (currentScope().isRecursion()) { + updateCurrentContext(current.select(tagName)); + } else { + Elements newContext = new Elements(); + for (Element el : currentScope().context()) { + if (isAxisOk) { + if (el.nodeName().equals(tagName) || "*".equals(tagName)) { + newContext.add(el); + } + } else { + for (Element e : el.children()) { + if (e.nodeName().equals(tagName) || "*".equals(tagName)) { + newContext.add(e); + } + } + } + + } + updateCurrentContext(newContext); + } + } else { + // nodeType 计算结果,直接返给上层 + if (nodeTest.isElements()) { + updateCurrentContext(nodeTest.asElements()); + } else { + return nodeTest; + } + } + } + } + if (ctx.predicate() != null && ctx.predicate().size() > 0) { + for (XpathParser.PredicateContext predicate : ctx.predicate()) { + XValue predicateVal = visit(predicate); + updateCurrentContext(predicateVal.asElements()); + } + } + return XValue.create(currentScope().context()); + } + + @Override + public XValue visitAbbreviatedStep(XpathParser.AbbreviatedStepContext ctx) { + if ("..".equals(ctx.getText())) { + Set total = new HashSet<>(); + Elements newContext = new Elements(); + for (Element e : currentScope().context()) { + total.add(e.parent()); + } + newContext.addAll(total); + return XValue.create(newContext); + } else { + return XValue.create(currentScope().context()); + } + } + + @Override + public XValue visitAxisSpecifier(XpathParser.AxisSpecifierContext ctx) { + TerminalNode axisNode = ctx.AxisName(); + if (axisNode != null) { + String axis = ctx.AxisName().getText(); + AxisSelector axisSelector = Scanner.findSelectorByName(axis); + return axisSelector.apply(currentScope().context()); + } else { + String token = ctx.getText(); + if ("@".equals(token)) { + return XValue.create(null).attr(); + } + } + return null; + } + + @Override + public XValue visitNodeTest(XpathParser.NodeTestContext ctx) { + if (ctx.nameTest() != null) { + return visit(ctx.nameTest()); + } + if (ctx.NodeType() != null) { + NodeTest nodeTest = Scanner.findNodeTestByName(ctx.NodeType().getText()); + return nodeTest.call(currentScope()); + } + // todo: | 'processing-instruction' '(' Literal ')' + return null; + } + + @Override + public XValue visitPredicate(XpathParser.PredicateContext ctx) { + Elements newContext = new Elements(); + for (Element e : currentScope().context()) { + scopeStack.push(Scope.create(e).setParent(currentScope())); + XValue exprVal = visit(ctx.expr()); + scopeStack.pop(); + if (exprVal.isNumber()) { + long index = exprVal.asLong(); + if (index < 0) { + if (Objects.equals(e.tagName(), Constants.DEF_TEXT_TAG_NAME)) { + index = CommonUtil.getJxSameTagIndexInSiblings(e) + index + 1; + } else { + index = CommonUtil.sameTagElNums(e, currentScope()) + index + 1; + } + if (index < 0) { + index = 1; + } + } + if (Objects.equals(e.tagName(), Constants.DEF_TEXT_TAG_NAME)) { + if (index == CommonUtil.getJxSameTagIndexInSiblings(e)) { + newContext.add(e); + } + } else { + if (index == CommonUtil.getElIndexInSameTags(e, currentScope())) { + newContext.add(e); + } + } + + } else if (exprVal.isBoolean()) { + if (exprVal.asBoolean()) { + newContext.add(e); + } + } else if (exprVal.isString()) { + //根据表达式执行结果是否为空作为条件,如 //*[@foo] + if (StringUtils.isNotBlank(exprVal.asString())) { + newContext.add(e); + } + } else if (exprVal.isElements()) { + //根据表达式执行结果是否为空进行过滤,如 //div[./a] + Elements els = exprVal.asElements(); + if (els.size() > 0) { + newContext.add(e); + } + } else if (exprVal.isList()) { + //根据表达式执行结果是否为空进行过滤,如 //div[./a/text()] + List stringList = exprVal.asList(); + if (stringList.size() > 0) { + newContext.add(e); + } + } else { + throw new XpathParserException("unknown expr val:" + exprVal); + } + } + return XValue.create(newContext); + } + + @Override + public XValue visitNameTest(XpathParser.NameTestContext ctx) { + if ("*".equals(ctx.getText())) { + return XValue.create("*").exprStr(); + } else if (ctx.qName() != null && !ctx.qName().isEmpty()) { + return visit(ctx.qName()); + } else if (ctx.nCName() != null && !ctx.nCName().isEmpty()) { + return visit(ctx.nCName()); + } + return null; + } + + @Override + public XValue visitQName(XpathParser.QNameContext ctx) { + List ncNameContexts = ctx.nCName(); + if (ncNameContexts != null) { + if (ncNameContexts.size() > 1) { + List ncNames = new LinkedList<>(); + for (XpathParser.NCNameContext ncNameContext : ncNameContexts) { + XValue value = visit(ncNameContext); + if (value != null) { + ncNames.add(value.asString()); + } + } + // TODO: 2018/3/14 考虑体现出这个结构来 + return XValue.create(StringUtils.join(ncNames, ":")); + } else { + return visit(ncNameContexts.get(0)); + } + } + return null; + } + + @Override + public XValue visitNCName(XpathParser.NCNameContext ctx) { + if (ctx.AxisName() != null) { + return XValue.create(ctx.AxisName().getText()).exprStr(); + } else { + return XValue.create(ctx.NCName().getText()).exprStr(); + } + } + + @Override + public XValue visitExpr(XpathParser.ExprContext ctx) { + return visit(ctx.orExpr()); + } + + @Override + public XValue visitOrExpr(XpathParser.OrExprContext ctx) { + List andExprContexts = ctx.andExpr(); + if (andExprContexts.size() > 1) { + Boolean res = visit(andExprContexts.get(0)).asBoolean(); + for (int i = 1; i < andExprContexts.size(); i++) { + res = (res | visit(andExprContexts.get(i)).asBoolean()); + } + return XValue.create(res); + } else { + return visit(andExprContexts.get(0)); + } + } + + @Override + public XValue visitAndExpr(XpathParser.AndExprContext ctx) { + List equalityExprContexts = ctx.equalityExpr(); + if (equalityExprContexts.size() > 1) { + Boolean res = visit(equalityExprContexts.get(0)).asBoolean(); + for (int i = 1; i < equalityExprContexts.size(); i++) { + res = (res & visit(equalityExprContexts.get(i)).asBoolean()); + } + return XValue.create(res); + } else { + return visit(equalityExprContexts.get(0)); + } + } + + @Override + public XValue visitEqualityExpr(XpathParser.EqualityExprContext ctx) { + List relationalExprContexts = ctx.relationalExpr(); + if (relationalExprContexts.size() == 1) { + return visit(relationalExprContexts.get(0)); + } else if (relationalExprContexts.size() == 2) { + XValue left = visit(relationalExprContexts.get(0)); + XValue right = visit(relationalExprContexts.get(1)); + if ("=".equals(ctx.op.getText())) { + if (left.valType().equals(right.valType())) { + return XValue.create(Objects.equals(left, right)); + } else { + return XValue.create(Objects.equals(left.asString(), right.asString())); + } + } else { + if (left.valType().equals(right.valType())) { + return XValue.create(!Objects.equals(left, right)); + } else { + return XValue.create(!Objects.equals(left.asString(), right.asString())); + } + } + } else { + throw new XpathParserException("error equalityExpr near:" + ctx.getText()); + } + } + + @Override + public XValue visitRelationalExpr(XpathParser.RelationalExprContext ctx) { + List additiveExprContexts = ctx.additiveExpr(); + if (additiveExprContexts.size() == 1) { + return visit(additiveExprContexts.get(0)); + } else if (additiveExprContexts.size() == 2) { + XValue left = visit(additiveExprContexts.get(0)); + XValue right = visit(additiveExprContexts.get(1)); + switch (ctx.op.getType()) { + case MORE_: + return XValue.create(left.compareTo(right) > 0); + case GE: + return XValue.create(left.compareTo(right) >= 0); + case LESS: + return XValue.create(left.compareTo(right) < 0); + case LE: + return XValue.create(left.compareTo(right) <= 0); + case START_WITH: + return XValue.create(left.asString().startsWith(right.asString())); + case END_WITH: + return XValue.create(left.asString().endsWith(right.asString())); + case CONTAIN_WITH: + return XValue.create(left.asString().contains(right.asString())); + case REGEXP_WITH: + return XValue.create(left.asString().matches(right.asString())); + case REGEXP_NOT_WITH: + return XValue.create(!left.asString().matches(right.asString())); + default: + throw new XpathParserException("unknown operator" + ctx.op.getText()); + } + } else { + throw new XpathParserException("error equalityExpr near:" + ctx.getText()); + } + } + + @Override + public XValue visitAdditiveExpr(XpathParser.AdditiveExprContext ctx) { + List multiplicativeExprContexts = ctx.multiplicativeExpr(); + if (multiplicativeExprContexts.size() == 1) { + return visit(multiplicativeExprContexts.get(0)); + } else { + Double res = visit(multiplicativeExprContexts.get(0)).asDouble(); + String op = null; + for (int i = 1; i < ctx.getChildCount(); i++) { + ParseTree chiCtx = ctx.getChild(i); + if (chiCtx instanceof XpathParser.MultiplicativeExprContext) { + XValue next = visit(chiCtx); + if ("+".equals(op)) { + res += next.asDouble(); + } else if ("-".equals(op)) { + res -= next.asDouble(); + } else { + throw new XpathParserException("syntax error, " + ctx.getText()); + } + } else { + op = chiCtx.getText(); + } + } + return XValue.create(res); + } + } + + @Override + public XValue visitMultiplicativeExpr(XpathParser.MultiplicativeExprContext ctx) { + if (ctx.multiplicativeExpr() == null || ctx.multiplicativeExpr().isEmpty()) { + return visit(ctx.unaryExprNoRoot()); + } else { + XValue left = visit(ctx.unaryExprNoRoot()); + XValue right = visit(ctx.multiplicativeExpr()); + switch (ctx.op.getType()) { + case MUL: + return XValue.create(left.asDouble() * right.asDouble()); + case DIVISION: + return XValue.create(left.asDouble() / right.asDouble()); + case MODULO: + return XValue.create(left.asDouble() % right.asDouble()); + default: + throw new XpathParserException("syntax error, " + ctx.getText()); + } + } + } + + @Override + public XValue visitUnaryExprNoRoot(XpathParser.UnaryExprNoRootContext ctx) { + XValue value = visit(ctx.unionExprNoRoot()); + if (ctx.sign == null) { + return value; + } + return XValue.create(-value.asDouble()); + } + + @Override + public XValue visitUnionExprNoRoot(XpathParser.UnionExprNoRootContext ctx) { + if (ctx.pathExprNoRoot() == null && !ctx.pathExprNoRoot().isEmpty()) { + return visit(ctx.unionExprNoRoot()); + } + XValue pathExprNoRoot = visit(ctx.pathExprNoRoot()); + if (ctx.op == null) { + return pathExprNoRoot; + } + scopeStack.push(Scope.create(currentScope().getParent())); + XValue unionExprNoRoot = visit(ctx.unionExprNoRoot()); + scopeStack.pop(); + if (pathExprNoRoot.isElements()) { + if (unionExprNoRoot.isElements()) { + pathExprNoRoot.asElements().addAll(unionExprNoRoot.asElements()); + } else { + Element element = new Element("V"); + element.appendText(unionExprNoRoot.asString()); + pathExprNoRoot.asElements().add(element); + } + return pathExprNoRoot; + } else if (pathExprNoRoot.isString()) { + if (unionExprNoRoot.isElements()) { + Element element = new Element("V"); + element.appendText(pathExprNoRoot.asString()); + unionExprNoRoot.asElements().add(element); + return unionExprNoRoot; + } else { + return XValue.create(pathExprNoRoot.asString() + unionExprNoRoot.asString()); + } + } else if (pathExprNoRoot.isBoolean()) { + if (unionExprNoRoot.isBoolean()) { + return XValue.create(pathExprNoRoot.asBoolean() | unionExprNoRoot.asBoolean()); + } else if (unionExprNoRoot.isElements()) { + Element element = new Element("V"); + element.appendText(pathExprNoRoot.asString()); + unionExprNoRoot.asElements().add(element); + return unionExprNoRoot; + } else if (unionExprNoRoot.isString()) { + return XValue.create(pathExprNoRoot.asBoolean() + unionExprNoRoot.asString()); + } else { + throw new XpathMergeValueException("can not merge val1=" + pathExprNoRoot.asBoolean() + ",val2=" + unionExprNoRoot.asString()); + } + } else if (pathExprNoRoot.isNumber()) { + if (unionExprNoRoot.isString()) { + return XValue.create(pathExprNoRoot.asDouble() + unionExprNoRoot.asString()); + } else if (unionExprNoRoot.isElements()) { + Element element = new Element("V"); + element.appendText(pathExprNoRoot.asString()); + unionExprNoRoot.asElements().add(element); + return unionExprNoRoot; + } else { + throw new XpathMergeValueException("can not merge val1=" + pathExprNoRoot.asDouble() + ",val2=" + unionExprNoRoot.asString()); + } + } else { + List tmpVal = new LinkedList<>(); + if (StringUtils.isNotBlank(pathExprNoRoot.asString())) { + tmpVal.add(pathExprNoRoot.asString()); + } + if (StringUtils.isNotBlank(unionExprNoRoot.asString())) { + tmpVal.add(unionExprNoRoot.asString()); + } + return XValue.create(StringUtils.join(tmpVal, ",")); + } + } + + @Override + public XValue visitPathExprNoRoot(XpathParser.PathExprNoRootContext ctx) { + if (ctx.locationPath() != null && !ctx.locationPath().isEmpty()) { + return visit(ctx.locationPath()); + } + if (ctx.op == null) { + return visit(ctx.filterExpr()); + } + if ("//".equals(ctx.op.getText())) { + currentScope().recursion(); + } + return visit(ctx.relativeLocationPath()); + } + + @Override + public XValue visitFilterExpr(XpathParser.FilterExprContext ctx) { + //暂时先不考虑支持 : primaryExpr predicate* + return visit(ctx.primaryExpr()); + } + + @Override + public XValue visitPrimaryExpr(XpathParser.PrimaryExprContext ctx) { + if (ctx.expr() != null && !ctx.expr().isEmpty()) { + return visit(ctx.expr()); + } else if (ctx.functionCall() != null && !ctx.functionCall().isEmpty()) { + return visit(ctx.functionCall()); + } else if (ctx.Literal() != null) { + return XValue.create(ctx.Literal().getText()).exprStr(); + } else if (ctx.Number() != null) { + return XValue.create(NumberUtils.createDouble(ctx.Number().getText())); + } + throw new XpathParserException("not support variableReference:" + ctx.getText()); + } + + @Override + public XValue visitFunctionCall(XpathParser.FunctionCallContext ctx) { + List params = new LinkedList<>(); + XValue funcName = visit(ctx.functionName()); + for (XpathParser.ExprContext exprContext : ctx.expr()) { + scopeStack.push(Scope.create(currentScope())); + params.add(visit(exprContext)); + scopeStack.pop(); + } + Function function = Scanner.findFunctionByName(funcName.asString()); + return function.call(currentScope(), params); + } + + @Override + public XValue visitFunctionName(XpathParser.FunctionNameContext ctx) { + return visit(ctx.qName()); + } + + private Scope currentScope() { + return scopeStack.peek(); + } + + private void updateCurrentContext(Elements newContext) { + scopeStack.peek().setContext(newContext); + } + +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/AncestorOrSelfSelector.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/AncestorOrSelfSelector.java new file mode 100644 index 000000000..564803d5b --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/AncestorOrSelfSelector.java @@ -0,0 +1,34 @@ +package org.seimicrawler.xpath.core.axis; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.core.AxisSelector; +import org.seimicrawler.xpath.core.XValue; + +import java.util.LinkedList; +import java.util.List; + +/** + * the ancestor-or-self axis contains the context node and the ancestors of the context node; + * thus, the ancestor axis will always include the root node + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/26. + */ +public class AncestorOrSelfSelector implements AxisSelector { + @Override + public String name() { + return "ancestor-or-self"; + } + + @Override + public XValue apply(Elements context) { + List total = new LinkedList<>(); + for (Element el : context) { + total.addAll(el.parents()); + //include self + total.add(el); + } + return XValue.create(new Elements(total)); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/AncestorSelector.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/AncestorSelector.java new file mode 100644 index 000000000..bd3620088 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/AncestorSelector.java @@ -0,0 +1,33 @@ +package org.seimicrawler.xpath.core.axis; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.core.AxisSelector; +import org.seimicrawler.xpath.core.XValue; + +import java.util.LinkedList; +import java.util.List; + +/** + * the ancestor axis contains the ancestors of the context node; the ancestors of the context node consist of + * the parent of context node and the parent's parent and so on; thus, the ancestor axis will always include + * the root node, unless the context node is the root node + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/26. + */ +public class AncestorSelector implements AxisSelector { + @Override + public String name() { + return "ancestor"; + } + + @Override + public XValue apply(Elements context) { + List total = new LinkedList<>(); + for (Element el : context) { + total.addAll(el.parents()); + } + return XValue.create(new Elements(total)); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/AttributeSelector.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/AttributeSelector.java new file mode 100644 index 000000000..f47e5d2e5 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/AttributeSelector.java @@ -0,0 +1,32 @@ +package org.seimicrawler.xpath.core.axis; + +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.core.AxisSelector; +import org.seimicrawler.xpath.core.XValue; + +/** + * the attribute axis contains the attributes of the context node; the axis will be empty unless the context node is an element + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/26. + */ +public class AttributeSelector implements AxisSelector { + /** + * assign name + * + * @return name + */ + @Override + public String name() { + return "attribute"; + } + + /** + * @param context + * @return res + */ + @Override + public XValue apply(Elements context) { + return XValue.create(null).attr(); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/ChildSelector.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/ChildSelector.java new file mode 100644 index 000000000..7d1c1edb4 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/ChildSelector.java @@ -0,0 +1,28 @@ +package org.seimicrawler.xpath.core.axis; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.core.AxisSelector; +import org.seimicrawler.xpath.core.XValue; + +/** + * the child axis contains the children of the context node + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/26. + */ +public class ChildSelector implements AxisSelector { + @Override + public String name() { + return "child"; + } + + @Override + public XValue apply(Elements context) { + Elements childs = new Elements(); + for (Element el : context) { + childs.addAll(el.children()); + } + return XValue.create(childs); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/DescendantOrSelfSelector.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/DescendantOrSelfSelector.java new file mode 100644 index 000000000..438899b89 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/DescendantOrSelfSelector.java @@ -0,0 +1,34 @@ +package org.seimicrawler.xpath.core.axis; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.core.AxisSelector; +import org.seimicrawler.xpath.core.XValue; + +import java.util.HashSet; +import java.util.Set; + +/** + * the descendant-or-self axis contains the context node and the descendants of the context node + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/26. + */ +public class DescendantOrSelfSelector implements AxisSelector { + @Override + public String name() { + return "descendant-or-self"; + } + + @Override + public XValue apply(Elements context) { + Set total = new HashSet<>(); + Elements descendant = new Elements(); + for (Element el : context) { + Elements tmp = el.getAllElements(); + total.addAll(tmp); + } + descendant.addAll(total); + return XValue.create(descendant); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/DescendantSelector.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/DescendantSelector.java new file mode 100644 index 000000000..4a676a24d --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/DescendantSelector.java @@ -0,0 +1,37 @@ +package org.seimicrawler.xpath.core.axis; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.core.AxisSelector; +import org.seimicrawler.xpath.core.XValue; + +import java.util.HashSet; +import java.util.Set; + +/** + * the descendant axis contains the descendants of the context node; a descendant is a child or a child + * of a child and so on; thus the descendant axis never contains attribute or namespace nodes + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/26. + */ +public class DescendantSelector implements AxisSelector { + @Override + public String name() { + return "descendant"; + } + + @Override + public XValue apply(Elements context) { + Set total = new HashSet<>(); + Elements descendant = new Elements(); + for (Element el : context) { + Elements tmp = el.getAllElements(); + //exclude self + tmp.remove(el); + total.addAll(tmp); + } + descendant.addAll(total); + return XValue.create(descendant); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/FollowingSelector.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/FollowingSelector.java new file mode 100644 index 000000000..b37e3fdab --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/FollowingSelector.java @@ -0,0 +1,51 @@ +package org.seimicrawler.xpath.core.axis; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.core.AxisSelector; +import org.seimicrawler.xpath.core.XValue; +import org.seimicrawler.xpath.util.CommonUtil; + +import java.util.LinkedList; +import java.util.List; + + +/** + * the following axis contains all nodes in the same document as the context node that are after the context node in + * document order, excluding any descendants and excluding attribute nodes and namespace nodes + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/26. + */ +public class FollowingSelector implements AxisSelector { + @Override + public String name() { + return "following"; + } + + @Override + public XValue apply(Elements context) { + List total = new LinkedList<>(); + for (Element el : context) { + Elements p = el.parents(); + for (Element pe : p) { + Elements fs = CommonUtil.followingSibling(pe); + if (fs == null) { + continue; + } + for (Element pse : fs) { + //include pse + total.addAll(pse.getAllElements()); + } + } + Elements fs = CommonUtil.followingSibling(el); + if (fs == null) { + continue; + } + for (Element se : fs) { + total.addAll(se.getAllElements()); + } + } + return XValue.create(new Elements(total)); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/FollowingSiblingOneSelector.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/FollowingSiblingOneSelector.java new file mode 100644 index 000000000..c91c085b7 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/FollowingSiblingOneSelector.java @@ -0,0 +1,44 @@ +package org.seimicrawler.xpath.core.axis; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.core.AxisSelector; +import org.seimicrawler.xpath.core.XValue; + +import java.util.LinkedList; +import java.util.List; + +/** + * the following-sibling-one JsoupXpath自定义扩展,比较常用 + * + * @author https://github.com/hermitmmll + * @since 2018/3/27. + */ +public class FollowingSiblingOneSelector implements AxisSelector { + /** + * assign name + * + * @return name + */ + @Override + public String name() { + return "following-sibling-one"; + } + + /** + * @param context + * @return res + */ + @Override + public XValue apply(Elements context) { + List total = new LinkedList<>(); + for (Element el : context) { + if (el.nextElementSibling() != null) { + total.add(el.nextElementSibling()); + } + } + Elements newContext = new Elements(); + newContext.addAll(total); + return XValue.create(newContext); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/FollowingSiblingSelector.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/FollowingSiblingSelector.java new file mode 100644 index 000000000..e4b1e329c --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/FollowingSiblingSelector.java @@ -0,0 +1,48 @@ +package org.seimicrawler.xpath.core.axis; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.core.AxisSelector; +import org.seimicrawler.xpath.core.XValue; +import org.seimicrawler.xpath.util.CommonUtil; + +import java.util.LinkedList; +import java.util.List; + +/** + * the following-sibling axis contains all the following siblings of the context node; if the context node is an + * attribute node or namespace node, the following-sibling axis is empty + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/27. + */ +public class FollowingSiblingSelector implements AxisSelector { + /** + * assign name + * + * @return name + */ + @Override + public String name() { + return "following-sibling"; + } + + /** + * @param context + * @return res + */ + @Override + public XValue apply(Elements context) { + List total = new LinkedList<>(); + for (Element el : context) { + Elements fs = CommonUtil.followingSibling(el); + if (fs == null) { + continue; + } + total.addAll(fs); + } + Elements newContext = new Elements(); + newContext.addAll(total); + return XValue.create(newContext); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/ParentSelector.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/ParentSelector.java new file mode 100644 index 000000000..2dc7dfb0d --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/ParentSelector.java @@ -0,0 +1,34 @@ +package org.seimicrawler.xpath.core.axis; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.core.AxisSelector; +import org.seimicrawler.xpath.core.XValue; + +import java.util.LinkedList; +import java.util.List; + +/** + * the parent axis contains the parent of the context node, if there is one + * https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-AxisSpecifier + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/2/28. + */ +public class ParentSelector implements AxisSelector { + @Override + public String name() { + return "parent"; + } + + @Override + public XValue apply(Elements context) { + List total = new LinkedList<>(); + Elements parents = new Elements(); + for (Element el : context) { + total.add(el.parent()); + } + parents.addAll(total); + return XValue.create(parents); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/PrecedingSelector.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/PrecedingSelector.java new file mode 100644 index 000000000..082f51286 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/PrecedingSelector.java @@ -0,0 +1,56 @@ +package org.seimicrawler.xpath.core.axis; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.core.AxisSelector; +import org.seimicrawler.xpath.core.XValue; +import org.seimicrawler.xpath.util.CommonUtil; + +import java.util.LinkedList; +import java.util.List; + +/** + * the preceding axis contains all nodes in the same document as the context node that are before the context + * node in document order, excluding any ancestors and excluding attribute nodes and namespace nodes + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/27. + */ +public class PrecedingSelector implements AxisSelector { + /** + * assign name + * + * @return name + */ + @Override + public String name() { + return "preceding"; + } + + /** + * @param context + * @return res + */ + @Override + public XValue apply(Elements context) { + Elements preceding = new Elements(); + List total = new LinkedList<>(); + for (Element el : context) { + Elements p = el.parents(); + for (Element pe : p) { + Elements ps = CommonUtil.precedingSibling(pe); + if (ps == null) { + continue; + } + total.addAll(ps); + } + Elements ps = CommonUtil.precedingSibling(el); + if (ps == null) { + continue; + } + total.addAll(ps); + } + preceding.addAll(total); + return XValue.create(preceding); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/PrecedingSiblingOneSelector.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/PrecedingSiblingOneSelector.java new file mode 100644 index 000000000..de92957c5 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/PrecedingSiblingOneSelector.java @@ -0,0 +1,44 @@ +package org.seimicrawler.xpath.core.axis; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.core.AxisSelector; +import org.seimicrawler.xpath.core.XValue; + +import java.util.LinkedList; +import java.util.List; + +/** + * the preceding-sibling-one axis JsoupXpath自定义扩展,用于选取节点的前一个兄弟节点,如果存在的话。 + * + * @author github.com/hermitmmll + * @since 2018/3/27. + */ +public class PrecedingSiblingOneSelector implements AxisSelector { + /** + * assign name + * + * @return name + */ + @Override + public String name() { + return "preceding-sibling-one"; + } + + /** + * @param context + * @return res + */ + @Override + public XValue apply(Elements context) { + List total = new LinkedList<>(); + for (Element el : context) { + if (el.previousElementSibling() != null) { + total.add(el); + } + } + Elements newContext = new Elements(); + newContext.addAll(total); + return XValue.create(newContext); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/PrecedingSiblingSelector.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/PrecedingSiblingSelector.java new file mode 100644 index 000000000..d170e0032 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/PrecedingSiblingSelector.java @@ -0,0 +1,48 @@ +package org.seimicrawler.xpath.core.axis; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.core.AxisSelector; +import org.seimicrawler.xpath.core.XValue; +import org.seimicrawler.xpath.util.CommonUtil; + +import java.util.LinkedList; +import java.util.List; + +/** + * the preceding-sibling axis contains all the preceding siblings of the context node; if the context node is + * an attribute node or namespace node, the preceding-sibling axis is empty + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/27. + */ +public class PrecedingSiblingSelector implements AxisSelector { + /** + * assign name + * + * @return name + */ + @Override + public String name() { + return "preceding-sibling"; + } + + /** + * @param context + * @return res + */ + @Override + public XValue apply(Elements context) { + List total = new LinkedList<>(); + for (Element el : context) { + Elements ps = CommonUtil.precedingSibling(el); + if (ps == null) { + continue; + } + total.addAll(ps); + } + Elements newContext = new Elements(); + newContext.addAll(total); + return XValue.create(newContext); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/SelfSelector.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/SelfSelector.java new file mode 100644 index 000000000..3d4555200 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/axis/SelfSelector.java @@ -0,0 +1,23 @@ +package org.seimicrawler.xpath.core.axis; + +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.core.AxisSelector; +import org.seimicrawler.xpath.core.XValue; + +/** + * the self axis contains just the context node itself + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/2/28. + */ +public class SelfSelector implements AxisSelector { + @Override + public String name() { + return "self"; + } + + @Override + public XValue apply(Elements es) { + return XValue.create(es); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Concat.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Concat.java new file mode 100644 index 000000000..3c149b372 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Concat.java @@ -0,0 +1,30 @@ +package org.seimicrawler.xpath.core.function; + +import org.seimicrawler.xpath.core.Function; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; + +import java.util.List; + +/** + * Function: string concat(string, string, string*) + * The concat function returns the concatenation of its arguments. + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/26. + */ +public class Concat implements Function { + @Override + public String name() { + return "concat"; + } + + @Override + public XValue call(Scope scope, List params) { + StringBuilder accum = new StringBuilder(); + for (XValue v : params) { + accum.append(v.asString()); + } + return XValue.create(accum.toString()); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Contains.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Contains.java new file mode 100644 index 000000000..796ef5019 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Contains.java @@ -0,0 +1,29 @@ +package org.seimicrawler.xpath.core.function; + +import org.seimicrawler.xpath.core.Function; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; + +import java.util.List; + +/** + * Function: boolean contains(string, string) + *

+ * The contains function returns true if the first argument string contains the second argument string, and otherwise returns false. + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/26. + */ +public class Contains implements Function { + @Override + public String name() { + return "contains"; + } + + @Override + public XValue call(Scope scope, List params) { + String first = params.get(0).asString(); + String second = params.get(1).asString(); + return XValue.create(first.contains(second)); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Count.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Count.java new file mode 100644 index 000000000..4c53cd52e --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Count.java @@ -0,0 +1,29 @@ +package org.seimicrawler.xpath.core.function; + +import org.seimicrawler.xpath.core.Function; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; + +import java.util.List; + +/** + * Function: number count(node-set) + * The count function returns the number of nodes in the argument node-set. + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/27. + */ +public class Count implements Function { + @Override + public String name() { + return "count"; + } + + @Override + public XValue call(Scope scope, List params) { + if (params == null || params.size() == 0) { + return XValue.create(0); + } + return XValue.create(params.get(0).asElements().size()); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/First.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/First.java new file mode 100644 index 000000000..cd2670520 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/First.java @@ -0,0 +1,25 @@ +package org.seimicrawler.xpath.core.function; + +import org.seimicrawler.xpath.core.Function; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; + +import java.util.List; + +/** + * first in xpath is 1 + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/29. + */ +public class First implements Function { + @Override + public String name() { + return "first"; + } + + @Override + public XValue call(Scope scope, List params) { + return XValue.create(1); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/FormatDate.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/FormatDate.java new file mode 100644 index 000000000..1372b7a03 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/FormatDate.java @@ -0,0 +1,47 @@ +package org.seimicrawler.xpath.core.function; + +import org.apache.commons.lang3.time.FastDateFormat; +import org.seimicrawler.xpath.core.Function; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; +import org.seimicrawler.xpath.exception.XpathParserException; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Locale; + +/** + * Function: string format-date(string, string, string) + * The format-date function returns Date object + * The first parameter is the date and time + * The second parameter is the time format of the first parameter. + * The third parameter is not required, and is required if the date format of the first parameter requires that the time zone must be specified + * For example, format-date("1999/04/01","yyyy/MM/dd") returns Date Object, and format-date("1999/04/01 07:55:23 pm","yyyy/MM/dd hh:mm:ss a",'en') returns Date Object. + * + * @author github.com/zzldn@163.com + * @since 2019/1/22. + */ +public class FormatDate implements Function { + @Override + public String name() { + return "format-date"; + } + + @Override + public XValue call(Scope scope, List params) { + String value = params.get(0).asString(); + String patten = params.get(1).asString(); + try { + if (params.size() > 2 && null != params.get(2)) { + final Locale locale = Locale.forLanguageTag(params.get(2).asString()); + final SimpleDateFormat format = new SimpleDateFormat(patten, locale); + return XValue.create(format.parse(value)); + } + return XValue.create(FastDateFormat.getInstance(patten).parse(value)); + } catch (ParseException e) { + throw new XpathParserException("date format exception!", e); + } + + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Last.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Last.java new file mode 100644 index 000000000..6dae66f6b --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Last.java @@ -0,0 +1,28 @@ +package org.seimicrawler.xpath.core.function; + +import org.seimicrawler.xpath.core.Function; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; + +import java.util.List; + +/** + * Function: number last() + * The last function returns a number equal to the context size from the expression evaluation context. + * e.g. + * para[last()] selects the last para child of the context node + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/27. + */ +public class Last implements Function { + @Override + public String name() { + return "last"; + } + + @Override + public XValue call(Scope scope, List params) { + return XValue.create(-1); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Not.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Not.java new file mode 100644 index 000000000..d5af0b500 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Not.java @@ -0,0 +1,30 @@ +package org.seimicrawler.xpath.core.function; + +import org.seimicrawler.xpath.core.Function; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; +import org.seimicrawler.xpath.exception.XpathParserException; + +import java.util.List; + +/** + * bool not(bool) + * + * @author github.com/hermitmmll + * @since 2018/4/3. + */ +public class Not implements Function { + @Override + public String name() { + return "not"; + } + + @Override + public XValue call(Scope scope, List params) { + if (params.size() == 1) { + return XValue.create(!params.get(0).asBoolean()); + } else { + throw new XpathParserException("error param in not(bool) function.Please check."); + } + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Position.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Position.java new file mode 100644 index 000000000..24cb49259 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/Position.java @@ -0,0 +1,28 @@ +package org.seimicrawler.xpath.core.function; + +import org.seimicrawler.xpath.core.Function; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; +import org.seimicrawler.xpath.util.CommonUtil; + +import java.util.List; + +/** + * The position function returns a number equal to the context position from the expression evaluation context. + * e.g. + * /child::doc/child::chapter[position()=5]/child::section[position()=2] selects the second section of the fifth chapter of the doc document element + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/2/28. + */ +public class Position implements Function { + @Override + public String name() { + return "position"; + } + + @Override + public XValue call(Scope scope, List params) { + return XValue.create(CommonUtil.getElIndexInSameTags(scope.singleEl(), scope.getParent())); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/StartsWith.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/StartsWith.java new file mode 100644 index 000000000..f70db13f9 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/StartsWith.java @@ -0,0 +1,29 @@ +package org.seimicrawler.xpath.core.function; + +import org.seimicrawler.xpath.core.Function; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; + +import java.util.List; + +/** + * Function: boolean starts-with(string, string) + *

+ * The starts-with function returns true if the first argument string starts with the second argument string, and otherwise returns false. + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/26. + */ +public class StartsWith implements Function { + @Override + public String name() { + return "starts-with"; + } + + @Override + public XValue call(Scope scope, List params) { + String first = params.get(0).asString(); + String second = params.get(1).asString(); + return XValue.create(first.startsWith(second)); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/StringLength.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/StringLength.java new file mode 100644 index 000000000..169c4f8d2 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/StringLength.java @@ -0,0 +1,30 @@ +package org.seimicrawler.xpath.core.function; + +import org.seimicrawler.xpath.core.Function; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; + +import java.util.List; + +/** + * number string-length(string?) + * The string-length returns the number of characters in the string (see [3.6 Strings]). If the argument is + * omitted, it defaults to the context node converted to a string, in other words the string-value of the context node. + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/27. + */ +public class StringLength implements Function { + @Override + public String name() { + return "string-length"; + } + + @Override + public XValue call(Scope scope, List params) { + if (params == null || params.size() == 0) { + return XValue.create(0); + } + return XValue.create(params.get(0).asString().length()); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubString.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubString.java new file mode 100644 index 000000000..d264bc9cf --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubString.java @@ -0,0 +1,47 @@ +package org.seimicrawler.xpath.core.function; + +import org.apache.commons.lang3.StringUtils; +import org.seimicrawler.xpath.core.Function; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; + +import java.util.List; + +/** + * Function: string substring(string, number, number?) + * https://www.w3.org/TR/1999/REC-xpath-19991116/#function-substring + * The substring function returns the substring of the first argument starting at the position specified in + * the second argument with length specified in the third argument. For example, substring("12345",2,3) returns "234". + * If the third argument is not specified, it returns the substring starting at the position specified in the + * second argument and continuing to the end of the string. For example, substring("12345",2) returns "2345". + *

+ * substring("12345", 1.5, 2.6) returns "234" + * substring("12345", 0 `div` 0, 3) returns "" + * substring("12345", 1, 0 `div` 0) returns "" + * substring("12345", -42, 1 `div` 0) returns "12345" + * substring("12345", -1 `div` 0, 1 `div` 0) returns "" + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/26. + */ + +public class SubString implements Function { + @Override + public String name() { + return "substring"; + } + + @Override + public XValue call(Scope scope, List params) { + String target = params.get(0).asString(); + int start = params.get(1).asLong().intValue(); + start = Math.max(start - 1, 0); + if (params.get(2) != null) { + int end = params.get(2).asLong().intValue(); + end = Math.min(start + end, target.length()); + end = Math.max(end, 0); + return XValue.create(StringUtils.substring(target, start, end)); + } + return XValue.create(StringUtils.substring(target, start)); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringAfter.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringAfter.java new file mode 100644 index 000000000..d52de57aa --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringAfter.java @@ -0,0 +1,32 @@ +package org.seimicrawler.xpath.core.function; + +import org.apache.commons.lang3.StringUtils; +import org.seimicrawler.xpath.core.Function; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; + +import java.util.List; + +/** + * Function: string substring-after(string, string) + * The substring-after function returns the substring of the first argument string that follows + * the first occurrence of the second argument string in the first argument string, or the empty string if + * the first argument string does not contain the second argument string. + * For example, substring-after("1999/04/01","/") returns 04/01, and substring-after("1999/04/01","19") returns 99/04/01. + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/26. + */ +public class SubStringAfter implements Function { + @Override + public String name() { + return "substring-after"; + } + + @Override + public XValue call(Scope scope, List params) { + String target = params.get(0).asString(); + String sep = params.get(1).asString(); + return XValue.create(StringUtils.substringAfter(target, sep)); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringAfterLast.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringAfterLast.java new file mode 100644 index 000000000..ffb28edd1 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringAfterLast.java @@ -0,0 +1,32 @@ +package org.seimicrawler.xpath.core.function; + +import org.apache.commons.lang3.StringUtils; +import org.seimicrawler.xpath.core.Function; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; + +import java.util.List; + +/** + * Function: string substring-after-last(string, string) + * The substring-after function returns the substring of the first argument string that follows + * the first occurrence of the second argument string in the first argument string, or the empty string if + * the first argument string does not contain the second argument string. + * For example, substring-after-last("1999/04/01","/") returns 01. + * + * @author github.com/zzldnl + * @since 2018/3/26. + */ +public class SubStringAfterLast implements Function { + @Override + public String name() { + return "substring-after-last"; + } + + @Override + public XValue call(Scope scope, List params) { + String target = params.get(0).asString(); + String sep = params.get(1).asString(); + return XValue.create(StringUtils.substringAfterLast(target, sep)); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringBefore.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringBefore.java new file mode 100644 index 000000000..6db7980a3 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringBefore.java @@ -0,0 +1,33 @@ +package org.seimicrawler.xpath.core.function; + +import org.apache.commons.lang3.StringUtils; +import org.seimicrawler.xpath.core.Function; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; + +import java.util.List; + +/** + * Function: string substring-before(string, string) + *

+ * The substring-before function returns the substring of the first argument string that precedes the + * first occurrence of the second argument string in the first argument string, or the empty string + * if the first argument string does not contain the second argument string. + * For example, substring-before("1999/04/01","/") returns 1999. + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/26. + */ +public class SubStringBefore implements Function { + @Override + public String name() { + return "substring-before"; + } + + @Override + public XValue call(Scope scope, List params) { + String target = params.get(0).asString(); + String sep = params.get(1).asString(); + return XValue.create(StringUtils.substringBefore(target, sep)); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringBeforeLast.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringBeforeLast.java new file mode 100644 index 000000000..6bc022104 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringBeforeLast.java @@ -0,0 +1,33 @@ +package org.seimicrawler.xpath.core.function; + +import org.apache.commons.lang3.StringUtils; +import org.seimicrawler.xpath.core.Function; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; + +import java.util.List; + +/** + * Function: string substring-before-last(string, string) + *

+ * The substring-before function returns the substring of the first argument string that precedes the + * first occurrence of the second argument string in the first argument string, or the empty string + * if the first argument string does not contain the second argument string. + * For example, substring-before-last("1999/04/01","/") returns 1999/04. + * + * @author github.com/zzldnl + * @since 2018/3/26. + */ +public class SubStringBeforeLast implements Function { + @Override + public String name() { + return "substring-before-last"; + } + + @Override + public XValue call(Scope scope, List params) { + String target = params.get(0).asString(); + String sep = params.get(1).asString(); + return XValue.create(StringUtils.substringBeforeLast(target, sep)); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringEx.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringEx.java new file mode 100644 index 000000000..ae1db61f2 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/function/SubStringEx.java @@ -0,0 +1,44 @@ +package org.seimicrawler.xpath.core.function; + +import org.apache.commons.lang3.StringUtils; +import org.seimicrawler.xpath.core.Function; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; + +import java.util.List; + +/** + * JsoupXpath扩展函数(统一使用 ‘jx’ 域),保持使用习惯java开发者习惯相同,第一个数字为起始索引(且索引从0开始),第二个数字为结束索引 + *

+ * StringUtils.substring(null, *, *) = null + * StringUtils.substring("", * , *) = ""; + * StringUtils.substring("abc", 0, 2) = "ab" + * StringUtils.substring("abc", 2, 0) = "" + * StringUtils.substring("abc", 2, 4) = "c" + * StringUtils.substring("abc", 2.13, 3.7) = "c" + * StringUtils.substring("abc", 4, 6) = "" + * StringUtils.substring("abc", 2, 2) = "" + * StringUtils.substring("abc", -2, -1) = "b" + * StringUtils.substring("abc", -4, 2) = "ab" + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/26. + */ + +public class SubStringEx implements Function { + @Override + public String name() { + return "substring-ex"; + } + + @Override + public XValue call(Scope scope, List params) { + String target = params.get(0).asString(); + int start = params.get(1).asLong().intValue(); + if (params.get(2) != null) { + int end = params.get(2).asLong().intValue(); + return XValue.create(StringUtils.substring(target, start, end)); + } + return XValue.create(StringUtils.substring(target, start)); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/AllText.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/AllText.java new file mode 100644 index 000000000..36582504b --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/AllText.java @@ -0,0 +1,44 @@ +package org.seimicrawler.xpath.core.node; + +import org.jsoup.nodes.Element; +import org.seimicrawler.xpath.core.NodeTest; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; + +import java.util.LinkedList; +import java.util.List; + +/** + * 获取当前节点下以及所有子孙节点中纯文本 + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/26. + */ +public class AllText implements NodeTest { + /** + * 支持的函数名 + */ + @Override + public String name() { + return "allText"; + } + + /** + * 函数具体逻辑 + * + * @param scope 上下文 + * @return 计算好的节点 + */ + @Override + public XValue call(Scope scope) { + List res = new LinkedList<>(); + for (Element e : scope.context()) { + if ("script".equals(e.nodeName())) { + res.add(e.data()); + } else { + res.add(e.text()); + } + } + return XValue.create(res); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Html.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Html.java new file mode 100644 index 000000000..0bca9fb15 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Html.java @@ -0,0 +1,31 @@ +package org.seimicrawler.xpath.core.node; + +import org.jsoup.nodes.Element; +import org.seimicrawler.xpath.core.NodeTest; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; + +import java.util.LinkedList; +import java.util.List; + +/** + * 获取全部节点的内部的html + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/4/9. + */ +public class Html implements NodeTest { + @Override + public String name() { + return "html"; + } + + @Override + public XValue call(Scope scope) { + List res = new LinkedList<>(); + for (Element e : scope.context()) { + res.add(e.html()); + } + return XValue.create(res); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Node.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Node.java new file mode 100644 index 000000000..db6aef02d --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Node.java @@ -0,0 +1,45 @@ +package org.seimicrawler.xpath.core.node; + +import org.apache.commons.lang3.StringUtils; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.core.NodeTest; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; + +/** + * 获取当前节点下所有子节点以及独立文本 + * + * @author: github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/4/4. + */ +public class Node implements NodeTest { + /** + * 支持的函数名 + */ + @Override + public String name() { + return "node"; + } + + /** + * 函数具体逻辑 + * + * @param scope 上下文 + * @return 计算好的节点 + */ + @Override + public XValue call(Scope scope) { + Elements context = new Elements(); + for (Element el : scope.context()) { + context.addAll(el.children()); + String txt = el.ownText(); + if (StringUtils.isNotBlank(txt)) { + Element et = new Element(""); + et.appendText(txt); + context.add(et); + } + } + return XValue.create(context); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Num.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Num.java new file mode 100644 index 000000000..9d66fc45f --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Num.java @@ -0,0 +1,51 @@ +package org.seimicrawler.xpath.core.node; + +import org.apache.commons.lang3.StringUtils; +import org.seimicrawler.xpath.core.NodeTest; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; +import org.seimicrawler.xpath.util.Scanner; + +import java.math.BigDecimal; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 提取自由文本中的数字,如果知道节点的自有文本(即非子代节点所包含的文本)中只存在一个数字,如阅读数,评论数,价格等那么直接可以直接提取此数字出来。 + * 如果有多个数字将提取第一个匹配的连续数字,支持小数,返回double + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/26. + */ +public class Num implements NodeTest { + private static final Pattern numExt = Pattern.compile("\\d*\\.?\\d+"); + + /** + * 支持的函数名 + */ + @Override + public String name() { + return "num"; + } + + /** + * 函数具体逻辑 + * + * @param scope 上下文 + * @return 计算好的节点 + */ + @Override + public XValue call(Scope scope) { + NodeTest textFun = Scanner.findNodeTestByName("allText"); + XValue textVal = textFun.call(scope); + String whole = StringUtils.join(textVal.asList(), ""); + Matcher matcher = numExt.matcher(whole); + if (matcher.find()) { + String numStr = matcher.group(); + BigDecimal num = new BigDecimal(numStr); + return XValue.create(num.doubleValue()); + } else { + return XValue.create(null); + } + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/OuterHtml.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/OuterHtml.java new file mode 100644 index 000000000..f158ef88c --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/OuterHtml.java @@ -0,0 +1,31 @@ +package org.seimicrawler.xpath.core.node; + +import org.jsoup.nodes.Element; +import org.seimicrawler.xpath.core.NodeTest; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; + +import java.util.LinkedList; +import java.util.List; + +/** + * 获取全部节点的 包含节点本身在内的全部html + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/4/9. + */ +public class OuterHtml implements NodeTest { + @Override + public String name() { + return "outerHtml"; + } + + @Override + public XValue call(Scope scope) { + List res = new LinkedList<>(); + for (Element e : scope.context()) { + res.add(e.outerHtml()); + } + return XValue.create(res); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Text.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Text.java new file mode 100644 index 000000000..d52553d15 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/core/node/Text.java @@ -0,0 +1,108 @@ +package org.seimicrawler.xpath.core.node; + +import org.jsoup.nodes.Element; +import org.jsoup.nodes.Node; +import org.jsoup.nodes.TextNode; +import org.jsoup.select.Elements; +import org.jsoup.select.NodeTraversor; +import org.jsoup.select.NodeVisitor; +import org.seimicrawler.xpath.core.Constants; +import org.seimicrawler.xpath.core.NodeTest; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; +import org.seimicrawler.xpath.util.CommonUtil; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/2/28. + * `text()`不再简单的返回节点下的所有文本,而是按照标准语义识别出多个文本块,返回文本块列表,如 + * ``` + *

one two three

+ * ``` + * - `//text()` 返回 `["one", "two", "three" ]` + * - `//text()[2]` 返回 `["three"]` + */ +public class Text implements NodeTest { + /** + * 支持的函数名 + */ + @Override + public String name() { + return "text"; + } + + /** + * 函数具体逻辑 + * + * @param scope 上下文 + * @return 计算好的节点 + */ + @Override + public XValue call(Scope scope) { + Elements context = scope.context(); + final Elements res = new Elements(); + if (context != null && context.size() > 0) { + if (scope.isRecursion()) { + for (final Element e : context) { + final Map indexMap = new HashMap<>(); + NodeTraversor.traverse(new NodeVisitor() { + @Override + public void head(Node node, int depth) { + if (node instanceof TextNode) { + TextNode textNode = (TextNode) node; + String key = depth + "_" + textNode.parent().hashCode(); + Integer index = indexMap.get(key); + if (index == null) { + index = 1; + } else { + index += 1; + } + indexMap.put(key, index); + Element data = new Element(Constants.DEF_TEXT_TAG_NAME); + data.text(textNode.getWholeText()); + try { + Method parent = Node.class.getDeclaredMethod("setParentNode", Node.class); + parent.setAccessible(true); + parent.invoke(data, textNode.parent()); + } catch (Exception e) { + //ignore + } + CommonUtil.setSameTagIndexInSiblings(data, index); + res.add(data); + } + } + + @Override + public void tail(Node node, int depth) { + + } + }, e); + } + } else { + for (Element e : context) { + if ("script".equals(e.nodeName())) { + Element data = new Element(Constants.DEF_TEXT_TAG_NAME); + data.text(e.data()); + CommonUtil.setSameTagIndexInSiblings(data, 1); + res.add(data); + } else { + List textNodes = e.textNodes(); + for (int i = 0; i < textNodes.size(); i++) { + TextNode textNode = textNodes.get(i); + Element data = new Element(Constants.DEF_TEXT_TAG_NAME); + data.text(textNode.getWholeText()); + CommonUtil.setSameTagIndexInSiblings(data, i + 1); + res.add(data); + } + } + } + } + } + return XValue.create(res); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/DoFailOnErrorHandler.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/DoFailOnErrorHandler.java new file mode 100644 index 000000000..1dfcd670a --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/DoFailOnErrorHandler.java @@ -0,0 +1,35 @@ +package org.seimicrawler.xpath.exception; + +import org.antlr.v4.runtime.DefaultErrorStrategy; +import org.antlr.v4.runtime.InputMismatchException; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.misc.ParseCancellationException; + +/** + * 如果语法解析异常,直接抛出并终止解析 + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/23. + */ +public class DoFailOnErrorHandler extends DefaultErrorStrategy { + + @Override + public void recover(Parser recognizer, RecognitionException e) { + for (ParserRuleContext context = recognizer.getContext(); context != null; context = context.getParent()) { + context.exception = e; + } + throw new ParseCancellationException(e); + } + + @Override + public Token recoverInline(Parser recognizer) throws RecognitionException { + InputMismatchException e = new InputMismatchException(recognizer); + for (ParserRuleContext context = recognizer.getContext(); context != null; context = context.getParent()) { + context.exception = e; + } + throw new ParseCancellationException(e); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/NoSuchAxisException.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/NoSuchAxisException.java new file mode 100644 index 000000000..deb40ad7d --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/NoSuchAxisException.java @@ -0,0 +1,28 @@ +package org.seimicrawler.xpath.exception; +/* + Copyright 2014 Wang Haomiao + + 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. + */ + +/** + * 使用不存在的轴语法则抛出此异常 + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * Date: 14-3-15 + */ +public class NoSuchAxisException extends RuntimeException { + public NoSuchAxisException(String msg) { + super(msg); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/NoSuchFunctionException.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/NoSuchFunctionException.java new file mode 100644 index 000000000..5266905d9 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/NoSuchFunctionException.java @@ -0,0 +1,26 @@ +package org.seimicrawler.xpath.exception; +/* + Copyright 2014 Wang Haomiao + + 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. + */ + +/** + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * Date: 14-3-16 + */ +public class NoSuchFunctionException extends RuntimeException { + public NoSuchFunctionException(String msg) { + super(msg); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/XpathMergeValueException.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/XpathMergeValueException.java new file mode 100644 index 000000000..e7036050b --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/XpathMergeValueException.java @@ -0,0 +1,13 @@ +package org.seimicrawler.xpath.exception; + +/** + * 无法合并多个表达式的解析结果 + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2017/12/5. + */ +public class XpathMergeValueException extends RuntimeException { + public XpathMergeValueException(String message) { + super(message); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/XpathParserException.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/XpathParserException.java new file mode 100644 index 000000000..62f5065a8 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/XpathParserException.java @@ -0,0 +1,23 @@ +package org.seimicrawler.xpath.exception; + +/** + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2017/12/5. + */ +public class XpathParserException extends RuntimeException { + /** + * Constructs a new runtime exception with the specified detail message. + * The cause is not initialized, and may subsequently be initialized by a + * call to {@link #initCause}. + * + * @param message the detail message. The detail message is saved for + * later retrieval by the {@link #getMessage()} method. + */ + public XpathParserException(String message) { + super(message); + } + + public XpathParserException(String message, Throwable e) { + super(message, e); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/XpathSyntaxErrorException.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/XpathSyntaxErrorException.java new file mode 100644 index 000000000..f4a83909d --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/exception/XpathSyntaxErrorException.java @@ -0,0 +1,30 @@ +package org.seimicrawler.xpath.exception; +/* + Copyright 2014 Wang Haomiao + + 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. + */ + +/** + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 14-3-19 + */ +public class XpathSyntaxErrorException extends RuntimeException { + public XpathSyntaxErrorException(String msg) { + super(msg); + } + + public XpathSyntaxErrorException(String msg, Throwable e) { + super(msg, e); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/util/CommonUtil.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/util/CommonUtil.java new file mode 100644 index 000000000..9cebd438c --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/util/CommonUtil.java @@ -0,0 +1,121 @@ +package org.seimicrawler.xpath.util; +/* + Copyright 2014 Wang Haomiao + + 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. + */ + +import org.apache.commons.lang3.StringUtils; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.core.Constants; +import org.seimicrawler.xpath.core.Scope; + +import java.util.Objects; + +/** + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * Date: 14-3-15 + */ +public class CommonUtil { + + /** + * 获取同名元素在同胞中的index + * + * @param e + * @return + */ + public static int getElIndexInSameTags(Element e, Scope scope) { + Elements chs = e.parent().children(); + int index = 1; + for (Element cur : chs) { + if (e.tagName().equals(cur.tagName()) && scope.context().contains(cur)) { + if (e.equals(cur)) { + break; + } else { + index += 1; + } + } + } + return index; + } + + + /** + * 获取同胞中同名元素的数量 + * + * @param e + * @return + */ + public static int sameTagElNums(Element e, Scope scope) { + Elements context = new Elements(); + Elements els = e.parent().getElementsByTag(e.tagName()); + for (Element el : els) { + if (scope.context().contains(el)) { + context.add(el); + } + } + return context.size(); + } + + public static int getIndexInContext(Scope scope, Element el) { + for (int i = 0; i < scope.context().size(); i++) { + Element tmp = scope.context().get(i); + if (Objects.equals(tmp, el)) { + return i + 1; + } + } + return Integer.MIN_VALUE; + } + + public static Elements followingSibling(Element el) { + Elements rs = new Elements(); + Element tmp = el.nextElementSibling(); + while (tmp != null) { + rs.add(tmp); + tmp = tmp.nextElementSibling(); + } + if (rs.size() > 0) { + return rs; + } + return null; + } + + public static Elements precedingSibling(Element el) { + Elements rs = new Elements(); + Element tmp = el.previousElementSibling(); + while (tmp != null) { + rs.add(tmp); + tmp = tmp.previousElementSibling(); + } + if (rs.size() > 0) { + return rs; + } + return null; + } + + public static void setSameTagIndexInSiblings(Element ori, int index) { + if (ori == null) { + return; + } + ori.attr(Constants.EL_SAME_TAG_INDEX_KEY, String.valueOf(index)); + } + + public static int getJxSameTagIndexInSiblings(Element ori) { + String val = ori.attr(Constants.EL_SAME_TAG_INDEX_KEY); + if (StringUtils.isBlank(val)) { + return -1; + } + return Integer.parseInt(val); + } +} diff --git a/JsoupXpath/src/main/java/org/seimicrawler/xpath/util/Scanner.java b/JsoupXpath/src/main/java/org/seimicrawler/xpath/util/Scanner.java new file mode 100644 index 000000000..01a999303 --- /dev/null +++ b/JsoupXpath/src/main/java/org/seimicrawler/xpath/util/Scanner.java @@ -0,0 +1,141 @@ +package org.seimicrawler.xpath.util; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.seimicrawler.xpath.core.AxisSelector; +import org.seimicrawler.xpath.core.Function; +import org.seimicrawler.xpath.core.NodeTest; +import org.seimicrawler.xpath.core.axis.AncestorOrSelfSelector; +import org.seimicrawler.xpath.core.axis.AncestorSelector; +import org.seimicrawler.xpath.core.axis.AttributeSelector; +import org.seimicrawler.xpath.core.axis.ChildSelector; +import org.seimicrawler.xpath.core.axis.DescendantOrSelfSelector; +import org.seimicrawler.xpath.core.axis.DescendantSelector; +import org.seimicrawler.xpath.core.axis.FollowingSelector; +import org.seimicrawler.xpath.core.axis.FollowingSiblingOneSelector; +import org.seimicrawler.xpath.core.axis.FollowingSiblingSelector; +import org.seimicrawler.xpath.core.axis.ParentSelector; +import org.seimicrawler.xpath.core.axis.PrecedingSelector; +import org.seimicrawler.xpath.core.axis.PrecedingSiblingOneSelector; +import org.seimicrawler.xpath.core.axis.PrecedingSiblingSelector; +import org.seimicrawler.xpath.core.axis.SelfSelector; +import org.seimicrawler.xpath.core.function.Concat; +import org.seimicrawler.xpath.core.function.Contains; +import org.seimicrawler.xpath.core.function.Count; +import org.seimicrawler.xpath.core.function.First; +import org.seimicrawler.xpath.core.function.FormatDate; +import org.seimicrawler.xpath.core.function.Last; +import org.seimicrawler.xpath.core.function.Not; +import org.seimicrawler.xpath.core.function.Position; +import org.seimicrawler.xpath.core.function.StartsWith; +import org.seimicrawler.xpath.core.function.StringLength; +import org.seimicrawler.xpath.core.function.SubString; +import org.seimicrawler.xpath.core.function.SubStringAfter; +import org.seimicrawler.xpath.core.function.SubStringAfterLast; +import org.seimicrawler.xpath.core.function.SubStringBefore; +import org.seimicrawler.xpath.core.function.SubStringBeforeLast; +import org.seimicrawler.xpath.core.function.SubStringEx; +import org.seimicrawler.xpath.core.node.AllText; +import org.seimicrawler.xpath.core.node.Html; +import org.seimicrawler.xpath.core.node.Node; +import org.seimicrawler.xpath.core.node.Num; +import org.seimicrawler.xpath.core.node.OuterHtml; +import org.seimicrawler.xpath.core.node.Text; +import org.seimicrawler.xpath.exception.NoSuchAxisException; +import org.seimicrawler.xpath.exception.NoSuchFunctionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; + +/** + * 考虑更广泛的兼容性,替换掉 FastClasspathScanner,采用手工注册 + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/2/28. + */ +public class Scanner { + private static final Map axisSelectorMap = new HashMap<>(); + private static final Map nodeTestMap = new HashMap<>(); + private static final Map functionMap = new HashMap<>(); + private static final Logger logger = LoggerFactory.getLogger(Scanner.class); + + static { + initAxis(AncestorOrSelfSelector.class, AncestorSelector.class, AttributeSelector.class, ChildSelector.class, DescendantOrSelfSelector.class, DescendantSelector.class, FollowingSelector.class, FollowingSiblingOneSelector.class, FollowingSiblingSelector.class, ParentSelector.class, PrecedingSelector.class, PrecedingSiblingOneSelector.class, PrecedingSiblingSelector.class, SelfSelector.class); + initFunction(Concat.class, Contains.class, Count.class, First.class, Last.class, Not.class, Position.class, StartsWith.class, StringLength.class, SubString.class, SubStringAfter.class, SubStringBefore.class, SubStringEx.class, FormatDate.class, SubStringAfterLast.class, SubStringBeforeLast.class); + initNode(AllText.class, Html.class, Node.class, Num.class, OuterHtml.class, Text.class); + } + + public static AxisSelector findSelectorByName(String selectorName) { + AxisSelector selector = axisSelectorMap.get(selectorName); + if (selector == null) { + throw new NoSuchAxisException("not support axis: " + selectorName); + } + return selector; + } + + public static NodeTest findNodeTestByName(String nodeTestName) { + NodeTest nodeTest = nodeTestMap.get(nodeTestName); + if (nodeTest == null) { + throw new NoSuchFunctionException("not support nodeTest: " + nodeTestName); + } + return nodeTest; + } + + public static Function findFunctionByName(String funcName) { + Function function = functionMap.get(funcName); + if (function == null) { + throw new NoSuchFunctionException("not support function: " + funcName); + } + return function; + } + + public static void registerFunction(Class func) { + Function function; + try { + function = func.newInstance(); + functionMap.put(function.name(), function); + } catch (Exception e) { + logger.info(ExceptionUtils.getRootCauseMessage(e), e); + } + } + + public static void registerNodeTest(Class nodeTestClass) { + NodeTest nodeTest; + try { + nodeTest = nodeTestClass.newInstance(); + nodeTestMap.put(nodeTest.name(), nodeTest); + } catch (Exception e) { + logger.info(ExceptionUtils.getRootCauseMessage(e), e); + } + } + + public static void registerAxisSelector(Class axisSelectorClass) { + AxisSelector axisSelector; + try { + axisSelector = axisSelectorClass.newInstance(); + axisSelectorMap.put(axisSelector.name(), axisSelector); + } catch (Exception e) { + logger.info(ExceptionUtils.getRootCauseMessage(e), e); + } + } + + public static void initAxis(Class... cls) { + for (Class axis : cls) { + registerAxisSelector(axis); + } + } + + public static void initFunction(Class... cls) { + for (Class func : cls) { + registerFunction(func); + } + } + + public static void initNode(Class... cls) { + for (Class node : cls) { + registerNodeTest(node); + } + } + +} diff --git a/JsoupXpath/src/main/resources/Xpath.g4 b/JsoupXpath/src/main/resources/Xpath.g4 new file mode 100644 index 000000000..9d439dcab --- /dev/null +++ b/JsoupXpath/src/main/resources/Xpath.g4 @@ -0,0 +1,296 @@ +grammar Xpath; + +/* +XPath 1.0 grammar. Should conform to the official spec at +http://www.w3.org/TR/1999/REC-xpath-19991116. The grammar +rules have been kept as close as possible to those in the +spec, but some adjustmewnts were unavoidable. These were +mainly removing left recursion (spec seems to be based on +LR), and to deal with the double nature of the '*' token +(node wildcard and multiplication operator). See also +section 3.7 in the spec. These rule changes should make +no difference to the strings accepted by the grammar. + +Written by Jan-Willem van den Broek +Version 1.0 + +Do with this code as you will. +*/ +/* + Ported to Antlr4 by Tom Everett +*/ +/** +操作符扩展: + a^=b 字符串a以字符串b开头 a startwith b + a*=b a包含b, a contains b + a$=b a以b结尾 a endwith b + a~=b a的内容符合 正则表达式b + a!~b a的内容不符合 正则表达式b + +轴扩展: + following-sibling-one + preceding-sibling-one + sibling + +NodeTest扩展: + num 抽取数字 + allText 提取节点下全部文本 + outerHtml 获取全部节点的 包含节点本身在内的全部html + html 获取全部节点的内部的html + +*/ + +main : expr + ; + +locationPath + : relativeLocationPath + | absoluteLocationPathNoroot + ; + +absoluteLocationPathNoroot + : op=(PATHSEP|ABRPATH) relativeLocationPath + ; + +relativeLocationPath + : step (op=(PATHSEP|ABRPATH) step)* + ; + +step + : axisSpecifier nodeTest predicate* + | abbreviatedStep + ; + +axisSpecifier + : AxisName '::' + | '@'? + ; + +nodeTest: nameTest + | NodeType '(' ')' + | 'processing-instruction' '(' Literal ')' + ; + +predicate + : '[' expr ']' + ; + +abbreviatedStep + : '.' + | '..' + ; + +expr : orExpr + ; + +primaryExpr + : variableReference + | '(' expr ')' + | Literal + | Number + | functionCall + ; + +functionCall + : functionName '(' ( expr ( ',' expr )* )? ')' + ; + +unionExprNoRoot + : pathExprNoRoot (op=PIPE unionExprNoRoot)? + | PATHSEP PIPE unionExprNoRoot + ; + +pathExprNoRoot + : locationPath + | filterExpr (op=(PATHSEP|ABRPATH) relativeLocationPath)? + ; + +filterExpr + : primaryExpr predicate* + ; + +orExpr : andExpr ('or' andExpr)* + ; + +andExpr : equalityExpr ('and' equalityExpr)* + ; + +equalityExpr + : relationalExpr (op=(EQUALITY|INEQUALITY) relationalExpr)* + ; + +relationalExpr + : additiveExpr (op=(LESS|MORE_|LESS|GE|START_WITH|END_WITH|CONTAIN_WITH|REGEXP_WITH|REGEXP_NOT_WITH) additiveExpr)* + ; + +additiveExpr + : multiplicativeExpr (op=(PLUS|MINUS) multiplicativeExpr)* + ; + +multiplicativeExpr + : unaryExprNoRoot (op=(MUL|DIVISION|MODULO) multiplicativeExpr)? +// | '/' (op=('`div`'|'`mod`') multiplicativeExpr)? + ; + +unaryExprNoRoot + : (sign=MINUS)? unionExprNoRoot + ; + +qName : nCName (':' nCName)? + ; + +functionName + : qName // Does not match nodeType, as per spec. + ; + +variableReference + : '$' qName + ; + +nameTest: '*' + | nCName ':' '*' + | qName + ; + +nCName : NCName + | AxisName + ; + +NodeType: 'comment' + | 'text' + | 'processing-instruction' + | 'node' + | 'num' //抽取数字 + | 'allText' //提取节点下全部文本 + | 'outerHtml' //获取全部节点的 包含节点本身在内的全部html + | 'html' //获取全部节点的内部的html + ; + +Number : Digits ('.' Digits?)? + | '.' Digits + ; + +fragment +Digits : ('0'..'9')+ + ; + +AxisName: 'ancestor' + | 'ancestor-or-self' + | 'attribute' + | 'child' + | 'descendant' + | 'descendant-or-self' + | 'following' + | 'following-sibling' +// | 'namespace' + | 'parent' + | 'preceding' + | 'preceding-sibling' + | 'self' + | 'following-sibling-one' + | 'preceding-sibling-one' + | 'sibling' + ; + + + PATHSEP + :'/'; + ABRPATH + : '//'; + LPAR + : '('; + RPAR + : ')'; + LBRAC + : '['; + RBRAC + : ']'; + MINUS + : '-'; + PLUS + : '+'; + DOT + : '.'; + MUL + : '*'; + DIVISION + : '`div`'; + MODULO + : '`mod`'; + DOTDOT + : '..'; + AT + : '@'; + COMMA + : ','; + PIPE + : '|'; + LESS + : '<'; + MORE_ + : '>'; + LE + : '<='; + GE + : '>='; + EQUALITY + : '='; + INEQUALITY + : '!='; + START_WITH + : '^='; + END_WITH + : '$='; + CONTAIN_WITH + : '*='; + REGEXP_WITH + : '~='; + REGEXP_NOT_WITH + : '!~'; + COLON + : ':'; + CC + : '::'; + APOS + : '\''; + QUOT + : '"'; + +Literal : '"' ~'"'* '"' + | '\'' ~'\''* '\'' + ; + +Whitespace + : (' '|'\t'|'\n'|'\r')+ ->skip + ; + +NCName : NCNameStartChar NCNameChar* + ; + +fragment +NCNameStartChar + : 'A'..'Z' + | '_' + | 'a'..'z' + | '\u00C0'..'\u00D6' + | '\u00D8'..'\u00F6' + | '\u00F8'..'\u02FF' + | '\u0370'..'\u037D' + | '\u037F'..'\u1FFF' + | '\u200C'..'\u200D' + | '\u2070'..'\u218F' + | '\u2C00'..'\u2FEF' + | '\u3001'..'\uD7FF' + | '\uF900'..'\uFDCF' + | '\uFDF0'..'\uFFFD' +// Unfortunately, java escapes can't handle this conveniently, +// as they're limited to 4 hex digits. TODO. +// | '\U010000'..'\U0EFFFF' + ; + +fragment +NCNameChar + : NCNameStartChar | '-' | '.' | '0'..'9' + | '\u00B7' | '\u0300'..'\u036F' + | '\u203F'..'\u2040' + ; \ No newline at end of file diff --git a/JsoupXpath/src/test/java/org/seimicrawler/xpath/BaseTest.java b/JsoupXpath/src/test/java/org/seimicrawler/xpath/BaseTest.java new file mode 100644 index 000000000..fc97764c2 --- /dev/null +++ b/JsoupXpath/src/test/java/org/seimicrawler/xpath/BaseTest.java @@ -0,0 +1,12 @@ +package org.seimicrawler.xpath; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author: github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2018/3/26. + */ +public class BaseTest { + protected Logger logger = LoggerFactory.getLogger(BaseTest.class); +} diff --git a/JsoupXpath/src/test/java/org/seimicrawler/xpath/JXDocumentTest.java b/JsoupXpath/src/test/java/org/seimicrawler/xpath/JXDocumentTest.java new file mode 100644 index 000000000..5a578b548 --- /dev/null +++ b/JsoupXpath/src/test/java/org/seimicrawler/xpath/JXDocumentTest.java @@ -0,0 +1,289 @@ +package org.seimicrawler.xpath; + +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.seimicrawler.xpath.exception.XpathSyntaxErrorException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * JXDocument Tester. + * + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @version 1.0 + */ +@RunWith(DataProviderRunner.class) +public class JXDocumentTest { + + private JXDocument underTest; + + private JXDocument doubanTest; + + private JXDocument custom; + private final ClassLoader loader = getClass().getClassLoader(); + private final Logger logger = LoggerFactory.getLogger(JXDocumentTest.class); + + @Before + public void before() throws Exception { + String html = "
some body
Two
"; + underTest = JXDocument.create(html); + if (doubanTest == null) { + URL t = loader.getResource("d_test.html"); + assert t != null; + File dBook = new File(t.toURI()); + String context = FileUtils.readFileToString(dBook, StandardCharsets.UTF_8); + doubanTest = JXDocument.create(context); + } + custom = JXDocument.create("
  • 性别:
  • "); + } + + /** + * Method: sel(String xpath) + */ + @Test + public void testSel() throws Exception { + String xpath = "//script[1]/text()"; + JXNode res = underTest.selNOne(xpath); + Assert.assertNotNull(res); + Assert.assertEquals("console.log('aaaaa')", res.asString()); + } + + @Test + public void testNotMatchFilter() throws Exception { + String xpath = "//div[contains(@class,'xiao')]/text()"; + JXNode node = underTest.selNOne(xpath); + Assert.assertEquals("Two", node.asString()); + } + + @Test + @DataProvider(value = { + "//a/@href", + "//div[@class='paginator']/span[@class='next']/a/@href", + }) + public void testXpath(String xpath) throws XpathSyntaxErrorException { + logger.info("current xpath: {}", xpath); + List rs = doubanTest.selN(xpath); + for (JXNode n : rs) { + if (!n.isString()) { + int index = n.asElement().siblingIndex(); + logger.info("index = {}", index); + } + logger.info(n.toString()); + } + } + + /** + * d_test.html 来源于 https://book.douban.com/tag/%E4%BA%92%E8%81%94%E7%BD%91 + *

    + * 为了测试各种可能情况,ul[@class='subject-list']节点以及其下内容被复制了一份出来,并修改部分书名前缀为'T2-'以便区分 + */ + @DataProvider + public static Object[][] dataOfXpathAndexpect() { + return new Object[][]{ + {"//ul[@class='subject-list']/li[position()<3][last()]/div/h2/allText()", "黑客与画家 : 硅谷创业之父Paul Graham文集T2-黑客与画家 : 硅谷创业之父Paul Graham文集"}, + {"//ul[@class='subject-list']/li[first()]/div/h2/allText()", "失控 : 全人类的最终命运和结局T2-失控 : 全人类的最终命运和结局"}, + {"//ul[@class='subject-list']/li[./div/div/span[@class='pl']/num()>(1000+90*(2*50))][last()][1]/div/h2/allText()", "长尾理论长尾理论"}, + {"//ul[@class='subject-list']/li[self::li/div/div/span[@class='pl']/num()>10000][-1]/div/h2/allText()", "长尾理论长尾理论"}, + {"//ul[@class='subject-list']/li[contains(self::li/div/div/span[@class='pl']//text(),'14582')]/div/h2//text()", "黑客与画家: 硅谷创业之父Paul Graham文集T2-黑客与画家: 硅谷创业之父Paul Graham文集"}, + {"//ul[@class='subject-list']/li[contains(./div/div/span[@class='pl']//text(),'14582')]/div/h2//text()", "黑客与画家: 硅谷创业之父Paul Graham文集T2-黑客与画家: 硅谷创业之父Paul Graham文集"}, + {"//*[@id=\"subject_list\"]/ul/li[2]/div[2]/h2/a//text()", "黑客与画家: 硅谷创业之父Paul Graham文集T2-黑客与画家: 硅谷创业之父Paul Graham文集"}, + {"//ul[@class]", 3L}, + {"//a[@id]/@href", "https://www.douban.com/doumail/"}, + {"//*[@id=\"subject_list\"]/ul[1]/li[8]/div[2]/div[2]/span[3]/num()", "3734.0"}, + {"//a[@id]/@href | //*[@id=\"subject_list\"]/ul[1]/li[8]/div[2]/div[2]/span[3]/num()", "https://www.douban.com/doumail/3734.0"}, + }; + } + + @UseDataProvider("dataOfXpathAndexpect") + @Test + public void testXpathAndAssert(String xpath, Object expect) throws XpathSyntaxErrorException { + logger.info("current xpath: {}", xpath); + List rs = doubanTest.selN(xpath); + if (expect instanceof String) { + String res = StringUtils.join(rs, ""); + logger.info(res); + Assert.assertEquals(expect, res); + } else if (expect instanceof Number) { + long size = (long) expect; + Assert.assertEquals(size, rs.size()); + } + } + + @Test + @DataProvider(value = { + "//ul[@class='subject-list']/li[position()<3]" + }) + public void testJXNode(String xpath) throws XpathSyntaxErrorException { + logger.info("current xpath: {}", xpath); + List jxNodeList = doubanTest.selN(xpath); + Set expect = new HashSet<>(); + //第一个 ul 中的 + expect.add("失控: 全人类的最终命运和结局"); + expect.add("黑客与画家: 硅谷创业之父Paul Graham文集"); + //第二个 ul 中的 + expect.add("T2-失控: 全人类的最终命运和结局"); + expect.add("T2-黑客与画家: 硅谷创业之父Paul Graham文集"); + + Set res = new HashSet<>(); + for (JXNode node : jxNodeList) { + if (!node.isString()) { + String currentRes = StringUtils.join(node.sel("/div/h2/a//text()"), ""); + logger.info(currentRes); + res.add(currentRes); + } + } + Assert.assertEquals(expect, res); + } + + @Test + @DataProvider(value = { + "//ul[@class='subject-list']" + }) + public void testRecursionNode(String xpath) throws XpathSyntaxErrorException { + logger.info("current xpath: {}", xpath); + List jxNodeList = doubanTest.selN(xpath); + logger.info("size = {}", jxNodeList.size()); + // 有两个ul,下面的是为了测试特意复制添加的 + Assert.assertEquals(2, jxNodeList.size()); + } + + @Test + @DataProvider(value = { + "//body/div/div/h1/text()", + "/body/div/div/h1/text()" + }) + public void absolutePathTest(String xpath) throws XpathSyntaxErrorException { + logger.info("current xpath: {}", xpath); + List jxNodeList = doubanTest.selN(xpath); + logger.info("size = {},res ={}", jxNodeList.size(), jxNodeList); + } + + @Test + public void testAs() throws XpathSyntaxErrorException { + List jxNodeList = custom.selN("//b[contains(text(),'性别')]/parent::*/text()"); + Assert.assertEquals("男", StringUtils.join(jxNodeList, "")); + for (JXNode jxNode : jxNodeList) { + logger.info(jxNode.toString()); + } + } + + /** + * fix https://github.com/zhegexiaohuozi/JsoupXpath/issues/33 + */ +// @Test + public void testNotObj() { + JXDocument doc = JXDocument.createByUrl("https://www.gxwztv.com/61/61514/"); +// List nodes = doc.selN("//*[@id=\"chapters-list\"]/li[@style]"); + List nodes = doc.selN("//*[@id=\"chapters-list\"]/li[not(@style)]"); + for (JXNode node : nodes) { + logger.info("r = {}", node); + } + } + + /** + * fix https://github.com/zhegexiaohuozi/JsoupXpath/issues/34 + */ + @Test + public void testAttrAtRoot() { + String content = "\n" + + " \n" + + " \n" + + " 第2章 神奇交流群\n" + + " \n" + + ""; + JXDocument doc = JXDocument.create(content); + List nodes = doc.selN("//@href"); + for (JXNode node : nodes) { + logger.info("r = {}", node); + } + } + + @Test + public void testA() { + String content = "网页设计师"; + JXDocument doc = JXDocument.create(content); + List nodes = doc.selN("//*[text()='网页设计师']"); + for (JXNode node : nodes) { + logger.info("r = {}", node); + } + } + + /** + * fix https://github.com/zhegexiaohuozi/JsoupXpath/issues/52 + */ + @Test + public void fixTextBehaviorTest() { + String html = "

    分类:动漫地区:日本年份:2010

    "; + JXDocument jxDocument = JXDocument.create(html); + List jxNodes = jxDocument.selN("//text()[3]"); + String actual = StringUtils.join(jxNodes, ""); + logger.info("actual = {}", actual); + Assert.assertEquals("2010", actual); + List nodes = jxDocument.selN("//text()"); + String allText = StringUtils.join(nodes, ""); + Assert.assertEquals("分类:动漫地区:日本年份:2010", allText); + logger.info("all = {}", allText); + } + + /** + * fix https://github.com/zhegexiaohuozi/JsoupXpath/issues/44 + */ + @Test + public void fixTextElNoParentTest() { + String test = "
    a
    need
    not need
    c
    "; + JXDocument j = JXDocument.create(test); + List l = j.selN("//div[@class='a']//text()[not(ancestor::div[@class='e'])]"); + Set finalRes = new HashSet<>(); + for (JXNode i : l) { + logger.info("{}", i.toString()); + finalRes.add(i.asString()); + } + Assert.assertFalse(finalRes.contains("not need")); + Assert.assertTrue(finalRes.contains("need")); + Assert.assertEquals(4, finalRes.size()); + } + + /** + * fix https://github.com/zhegexiaohuozi/JsoupXpath/issues/53 + */ + @Test + public void fixIssue53() { + String content = "
  • \n" + + "
    \n" + + "
    \n" + + "
    \n" + + "

    我们平凡我们忠诚

    \n" + + "

    巡璃 |短篇|连载

    \n" + + "

    这是一位普通老兵的故事,这位老兵没有走上战场,也没有人歌颂他,但他的工作却是面对生与死,他是一名普通的军转干部,没有得到任何荣誉,却仍旧坚守着信仰,永远忠诚。除了他的家人,他的战友,他的故事不被任何人所知,但他的故事正是一代军人、一代军转干部的写照。所以,我来歌颂他,歌颂那一代人。

    \n" + + "

    最新更新 第一次见识到生死·2020-02-19

    \n" + + "
    \n" + + "
    \n" + + "
    \n" + + "

    4497总字数

    \n" + + "

    0总推荐

    \n" + + "
    \n" + + "

    书籍详情 加入书架

    \n" + + "
  • "; + JXDocument j = JXDocument.create(content); + List l = j.selN("//*[text()='总字数']//text()"); + Assert.assertEquals(2, l.size()); + Assert.assertEquals("4497", l.get(0).asString()); + Assert.assertEquals("总字数", l.get(1).asString()); + } + +} diff --git a/JsoupXpath/src/test/java/org/seimicrawler/xpath/core/function/DateFormatTest.java b/JsoupXpath/src/test/java/org/seimicrawler/xpath/core/function/DateFormatTest.java new file mode 100644 index 000000000..8b9571d89 --- /dev/null +++ b/JsoupXpath/src/test/java/org/seimicrawler/xpath/core/function/DateFormatTest.java @@ -0,0 +1,49 @@ +package org.seimicrawler.xpath.core.function; + +import org.junit.Test; +import org.seimicrawler.xpath.core.XValue; + +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; + +/** + * @description: TODO
    + * @create: 2019-01-21 21:07 + * @author: zzldn@163.com + * @since JDK1.8 + **/ + +public class DateFormatTest { + + @Test + public void defaultTest() { + List params = new LinkedList<>(); + params.add(XValue.create("2019-01-21 19:05:42")); + params.add(XValue.create("yyyy-MM-dd HH:mm:ss")); + FormatDate formatDate = new FormatDate(); + XValue value = formatDate.call(null, params); + System.out.println(value.asDate()); + } + + @Test + public void defaultTimeTest() { + List params = new LinkedList<>(); + params.add(XValue.create("19:05:42")); + params.add(XValue.create("HH:mm:ss")); + FormatDate formatDate = new FormatDate(); + XValue value = formatDate.call(null, params); + System.out.println(value.asDate()); + } + + @Test + public void localTest() { + List params = new LinkedList<>(); + params.add(XValue.create("1/21/2019 07:05:42 AM")); + params.add(XValue.create("MM/dd/yyyy hh:mm:ss aa")); + params.add(XValue.create(Locale.ENGLISH.toString())); + FormatDate formatDate = new FormatDate(); + XValue value = formatDate.call(null, params); + System.out.println(value.asDate()); + } +} diff --git a/JsoupXpath/src/test/java/org/seimicrawler/xpath/core/function/SubStringTest.java b/JsoupXpath/src/test/java/org/seimicrawler/xpath/core/function/SubStringTest.java new file mode 100644 index 000000000..1ca106f86 --- /dev/null +++ b/JsoupXpath/src/test/java/org/seimicrawler/xpath/core/function/SubStringTest.java @@ -0,0 +1,53 @@ +package org.seimicrawler.xpath.core.function; + +import org.junit.Assert; +import org.junit.Test; +import org.seimicrawler.xpath.core.XValue; + +import java.util.LinkedList; +import java.util.List; + +/** + * SubString Tester. + * + * @author seimimaster@gmail.com + * @version 1.0 + */ +public class SubStringTest { + + /** + * substring("12345", 1.5, 2.6) returns "234" + * Method: call(Element context, List params) + */ + @Test + public void testCall() throws Exception { + List params = new LinkedList<>(); + params.add(XValue.create("12345")); + params.add(XValue.create("1.5")); + params.add(XValue.create("2.6")); + SubString subStringFunc = new SubString(); + Assert.assertEquals(subStringFunc.call(null, params).asString(), "234"); + } + + @Test + public void testZeroLength() throws Exception { + List params = new LinkedList<>(); + params.add(XValue.create("12345")); + params.add(XValue.create("2")); + params.add(XValue.create("-6")); + SubString subStringFunc = new SubString(); + Assert.assertEquals(subStringFunc.call(null, params).asString(), ""); + } + + @Test + public void testOneLength() throws Exception { + List params = new LinkedList<>(); + params.add(XValue.create("12345")); + params.add(XValue.create("0")); + params.add(XValue.create("1")); + SubString subStringFunc = new SubString(); + Assert.assertEquals(subStringFunc.call(null, params).asString(), "1"); + } + + +} diff --git a/JsoupXpath/src/test/java/org/seimicrawler/xpath/core/node/NumTest.java b/JsoupXpath/src/test/java/org/seimicrawler/xpath/core/node/NumTest.java new file mode 100644 index 000000000..d685fcbac --- /dev/null +++ b/JsoupXpath/src/test/java/org/seimicrawler/xpath/core/node/NumTest.java @@ -0,0 +1,59 @@ +package org.seimicrawler.xpath.core.node; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.junit.Assert; +import org.junit.Test; +import org.seimicrawler.xpath.BaseTest; +import org.seimicrawler.xpath.core.Scope; +import org.seimicrawler.xpath.core.XValue; + +/** + * Num Tester. + * + * @author seimimaster@gmail.com + * @version 1.0 + */ +public class NumTest extends BaseTest { + + /** + * Method: call(Elements context) + */ + @Test + public void testCall() throws Exception { + Elements context = new Elements(); + Element el = new Element("V"); + el.appendText("test 33.69"); + context.add(el); + Num n = new Num(); + XValue v = n.call(Scope.create(context)); + logger.info("v = {}", v); + Assert.assertEquals(33.69, v.asDouble(), 0.00000000000001); + } + + @Test + public void testShort() throws Exception { + Elements context = new Elements(); + Element el = new Element("V"); + el.appendText("test .69"); + context.add(el); + Num n = new Num(); + XValue v = n.call(Scope.create(context)); + logger.info("v = {}", v); + Assert.assertEquals(0.69, v.asDouble(), 0.00000000000001); + } + + @Test + public void testOnZero() throws Exception { + Elements context = new Elements(); + Element el = new Element("V"); + el.appendText("test 69."); + context.add(el); + Num n = new Num(); + XValue v = n.call(Scope.create(context)); + logger.info("v = {}", v); + Assert.assertEquals(69, v.asDouble(), 0.00000000000001); + } + + +} diff --git a/JsoupXpath/src/test/java/org/seimicrawler/xpath/expr/ExprTest.java b/JsoupXpath/src/test/java/org/seimicrawler/xpath/expr/ExprTest.java new file mode 100644 index 000000000..df7bbc832 --- /dev/null +++ b/JsoupXpath/src/test/java/org/seimicrawler/xpath/expr/ExprTest.java @@ -0,0 +1,63 @@ +package org.seimicrawler.xpath.expr; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.tree.ParseTree; +import org.apache.commons.io.FileUtils; +import org.jsoup.Jsoup; +import org.jsoup.select.Elements; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.seimicrawler.xpath.BaseTest; +import org.seimicrawler.xpath.antlr.XpathLexer; +import org.seimicrawler.xpath.antlr.XpathParser; +import org.seimicrawler.xpath.core.XValue; +import org.seimicrawler.xpath.core.XpathProcessor; +import org.seimicrawler.xpath.exception.DoFailOnErrorHandler; + +import java.io.File; +import java.math.BigDecimal; +import java.net.URL; +import java.nio.charset.StandardCharsets; + +/** + * @author github.com/zhegexiaohuozi seimimaster@gmail.com + * @since 2017/12/6. + */ +public class ExprTest extends BaseTest { + + private Elements root; + private final ClassLoader loader = getClass().getClassLoader(); + + @Before + public void init() throws Exception { + // https://book.douban.com/tag/%E4%BA%92%E8%81%94%E7%BD%91 + URL t = loader.getResource("d_test.html"); + assert t != null; + File dBook = new File(t.toURI()); + String context = FileUtils.readFileToString(dBook, StandardCharsets.UTF_8); + root = Jsoup.parse(context).children(); + } + + @Test + public void exp() { + String xpath = "//a[@id]/@href"; + CharStream input = CharStreams.fromString(xpath); + XpathLexer lexer = new XpathLexer(input); + CommonTokenStream tokens = new CommonTokenStream(lexer); + XpathParser parser = new XpathParser(tokens); + parser.setErrorHandler(new DoFailOnErrorHandler()); + ParseTree tree = parser.main(); + XpathProcessor processor = new XpathProcessor(root); + XValue value = processor.visit(tree); + logger.info("visit res = {}", value); + } + + @Test + public void roundHalfUp() { + int x = new BigDecimal("5.53").setScale(0, BigDecimal.ROUND_HALF_UP).intValue(); + Assert.assertEquals(6, x); + } +} \ No newline at end of file diff --git a/JsoupXpath/src/test/resources/d_test.html b/JsoupXpath/src/test/resources/d_test.html new file mode 100644 index 000000000..b7becb155 --- /dev/null +++ b/JsoupXpath/src/test/resources/d_test.html @@ -0,0 +1,5416 @@ + + + + + + 豆瓣图书标签: 互联网 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    + +

    豆瓣图书标签: 互联网

    + +
    + +
    +
    + + +
    + + + 综合排序 +  /  + + 按出版日期排序 +  /  + + 按评价排序 + +
    + + + + + +
    + + <前页 + + + + 1 + + 2 + + + 3 + + + 4 + + + 5 + + + 6 + + + 7 + + + 8 + + + 9 + + ... + + 96 + + 97 + + + + 后页> + + +
    + + +
    +
    +
    + +
    +
    + + +

    + 相关的标签 +  · · · · · · + +

    + + + + + +
    + + +
    +
    +
    + + +

    + > 浏览全部图书标签 +

    + + +

    + "互联网"相关豆列 +  · · · · · · + +

    + + + + + +
    +
    + +
    +

    最近受关注的书-互联网

    + +
    + + + + + +
    +
    + +
    +
    +
    + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/build.gradle b/app/build.gradle index 767a79371..182d3bd01 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -123,6 +123,7 @@ kapt { dependencies { implementation project(path: ':epublib') + implementation project(path: ':JsoupXpath') coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) testImplementation 'junit:junit:4.13.2' @@ -177,9 +178,6 @@ dependencies { implementation 'io.github.jeremyliao:live-event-bus-x:1.8.0' //规则相关 - implementation 'org.jsoup:jsoup:1.14.1' - //noinspection GradleDependency - implementation 'cn.wanghaomiao:JsoupXpath:2.3.2' implementation 'com.jayway.jsonpath:json-path:2.6.0' //JS rhino diff --git a/settings.gradle b/settings.gradle index 6e5b9d8ee..a489efdc2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app',':epublib' +include ':app',':JsoupXpath',':epublib' From d3983a33f5c9333349e59a908650ed723982e354 Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 20 Jul 2021 09:27:10 +0800 Subject: [PATCH 26/40] =?UTF-8?q?=E5=B0=86xpath=E9=9B=86=E6=88=90=E5=88=B0?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- JsoupXpath/build.gradle | 7 ++++++- app/build.gradle | 5 +++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/JsoupXpath/build.gradle b/JsoupXpath/build.gradle index 3d8e01066..f798aa3bf 100644 --- a/JsoupXpath/build.gradle +++ b/JsoupXpath/build.gradle @@ -31,8 +31,13 @@ android { } dependencies { - api 'org.jsoup:jsoup:1.14.1' + implementation 'org.jsoup:jsoup:1.14.1' implementation 'org.antlr:antlr4-runtime:4.7.2' implementation 'org.apache.commons:commons-lang3:3.11' implementation 'org.slf4j:slf4j-api:1.7.30' + testImplementation('ch.qos.logback:logback-core:1.2.3') + testImplementation('org.powermock:powermock-module-junit4:1.6.3') + testImplementation('org.powermock:powermock-api-mockito:1.6.3') + testImplementation('com.tngtech.java:junit-dataprovider:1.10.2') + testImplementation('commons-io:commons-io:2.6') } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 182d3bd01..fb4a759e0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -122,8 +122,6 @@ kapt { } dependencies { - implementation project(path: ':epublib') - implementation project(path: ':JsoupXpath') coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) testImplementation 'junit:junit:4.13.2' @@ -178,7 +176,10 @@ dependencies { implementation 'io.github.jeremyliao:live-event-bus-x:1.8.0' //规则相关 + implementation 'org.jsoup:jsoup:1.14.1' implementation 'com.jayway.jsonpath:json-path:2.6.0' + implementation project(path: ':JsoupXpath') + implementation project(path: ':epublib') //JS rhino implementation 'com.github.gedoor:rhino-android:1.6' From d485f3afe0b24c55358955bf086db9db40f18a81 Mon Sep 17 00:00:00 2001 From: celetor <86599970+celetor@users.noreply.github.com> Date: Tue, 20 Jul 2021 11:14:12 +0800 Subject: [PATCH 27/40] =?UTF-8?q?=E8=8E=B7=E5=8F=96=E7=BD=91=E7=BB=9Czip?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E9=87=8C=E9=9D=A2=E7=9A=84=E6=95=B0=E6=8D=AE?= =?UTF-8?q?-=E5=A2=9E=E5=8A=A0=E4=BB=8E=E5=8D=81=E5=85=AD=E8=BF=9B?= =?UTF-8?q?=E5=88=B6=E5=AD=97=E7=AC=A6=E4=B8=B2=E4=B8=AD=E8=AF=BB=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/help/JsExtensions.kt | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/JsExtensions.kt b/app/src/main/java/io/legado/app/help/JsExtensions.kt index 56326e382..db9f3cbfa 100644 --- a/app/src/main/java/io/legado/app/help/JsExtensions.kt +++ b/app/src/main/java/io/legado/app/help/JsExtensions.kt @@ -312,13 +312,17 @@ interface JsExtensions { /** * 获取网络zip文件里面的数据 - * @param url zip文件的链接 + * @param url zip文件的链接或十六进制字符串 * @param path 所需获取文件在zip内的路径 * @return zip指定文件的数据 */ fun getZipStringContent(url: String, path: String): String { - val bytes = runBlocking { - return@runBlocking okHttpClient.newCall { url(url) }.bytes() + val bytes = if (url.startsWith("http://") || url.startsWith("https://")) { + runBlocking { + return@runBlocking okHttpClient.newCall { url(url) }.bytes() + } + } else { + StringUtils.hexStringToByte(url) } val bos = ByteArrayOutputStream() val zis = ZipInputStream(ByteArrayInputStream(bytes)) @@ -339,13 +343,17 @@ interface JsExtensions { /** * 获取网络zip文件里面的数据 - * @param url zip文件的链接 + * @param url zip文件的链接或十六进制字符串 * @param path 所需获取文件在zip内的路径 * @return zip指定文件的数据 */ fun getZipByteArrayContent(url: String, path: String): ByteArray? { - val bytes = runBlocking { - return@runBlocking okHttpClient.newCall { url(url) }.bytes() + val bytes = if (url.startsWith("http://") || url.startsWith("https://")) { + runBlocking { + return@runBlocking okHttpClient.newCall { url(url) }.bytes() + } + } else { + StringUtils.hexStringToByte(url) } val bos = ByteArrayOutputStream() val zis = ZipInputStream(ByteArrayInputStream(bytes)) From 688ee2e986b1c08c0c2a4830d36e12dcd96f8ab1 Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 20 Jul 2021 15:00:57 +0800 Subject: [PATCH 28/40] =?UTF-8?q?=E5=B0=86xpath=E9=9B=86=E6=88=90=E5=88=B0?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- JsoupXpath/build.gradle | 9 ++-- app/build.gradle | 104 ++++++++++++++++++++-------------------- 2 files changed, 57 insertions(+), 56 deletions(-) diff --git a/JsoupXpath/build.gradle b/JsoupXpath/build.gradle index f798aa3bf..72f0785d2 100644 --- a/JsoupXpath/build.gradle +++ b/JsoupXpath/build.gradle @@ -31,10 +31,11 @@ android { } dependencies { - implementation 'org.jsoup:jsoup:1.14.1' - implementation 'org.antlr:antlr4-runtime:4.7.2' - implementation 'org.apache.commons:commons-lang3:3.11' - implementation 'org.slf4j:slf4j-api:1.7.30' + implementation('org.jsoup:jsoup:1.14.1') + implementation('org.antlr:antlr4-runtime:4.7.2') + implementation('org.apache.commons:commons-lang3:3.11') + implementation('org.slf4j:slf4j-api:1.7.30') + implementation('org.slf4j:slf4j-simple:1.7.30') testImplementation('ch.qos.logback:logback-core:1.2.3') testImplementation('org.powermock:powermock-module-junit4:1.6.3') testImplementation('org.powermock:powermock-api-mockito:1.6.3') diff --git a/app/build.gradle b/app/build.gradle index fb4a759e0..08c49ef61 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -106,9 +106,9 @@ android { jvmTarget = "1.8" } buildToolsVersion '30.0.3' -// tasks.withType(JavaCompile) { -// options.compilerArgs << "-Xlint:unchecked" -// } + tasks.withType(JavaCompile) { + //options.compilerArgs << "-Xlint:unchecked" + } } resourcePlaceholders { @@ -122,37 +122,37 @@ kapt { } dependencies { - coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test:runner:1.4.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - implementation 'androidx.multidex:multidex:2.0.1' + coreLibraryDesugaring('com.android.tools:desugar_jdk_libs:1.1.5') + implementation(fileTree(dir: 'libs', include: ['*.jar', '*.aar'])) + testImplementation('junit:junit:4.13.2') + androidTestImplementation('androidx.test:runner:1.4.0') + androidTestImplementation('androidx.test.espresso:espresso-core:3.4.0') + implementation('androidx.multidex:multidex:2.0.1') //kotlin - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version") //协程 def coroutines_version = '1.5.1' - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version") //androidX - implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'androidx.core:core-ktx:1.6.0' - implementation "androidx.activity:activity-ktx:1.2.3" - implementation "androidx.fragment:fragment-ktx:1.3.5" - implementation 'androidx.preference:preference-ktx:1.1.1' - implementation 'androidx.constraintlayout:constraintlayout:2.0.4' - implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' - implementation 'androidx.viewpager2:viewpager2:1.0.0' - implementation 'com.google.android.material:material:1.4.0' - implementation 'com.google.android.flexbox:flexbox:3.0.0' - implementation 'com.google.code.gson:gson:2.8.7' - implementation 'androidx.webkit:webkit:1.4.0' + implementation('androidx.appcompat:appcompat:1.3.0') + implementation('androidx.core:core-ktx:1.6.0') + implementation("androidx.activity:activity-ktx:1.2.3") + implementation("androidx.fragment:fragment-ktx:1.3.5") + implementation('androidx.preference:preference-ktx:1.1.1') + implementation('androidx.constraintlayout:constraintlayout:2.0.4') + implementation('androidx.swiperefreshlayout:swiperefreshlayout:1.1.0') + implementation('androidx.viewpager2:viewpager2:1.0.0') + implementation('com.google.android.material:material:1.4.0') + implementation('com.google.android.flexbox:flexbox:3.0.0') + implementation('com.google.code.gson:gson:2.8.7') + implementation('androidx.webkit:webkit:1.4.0') //media def media2_version = "1.1.3" - implementation "androidx.media2:media2-session:$media2_version" - implementation "androidx.media:media:1.3.1" + implementation("androidx.media2:media2-session:$media2_version") + implementation("androidx.media:media:1.3.1") //implementation "androidx.media2:media2-player:$media2_version" //implementation 'com.google.android.exoplayer:exoplayer:2.13.0' @@ -164,58 +164,58 @@ dependencies { //lifecycle def lifecycle_version = '2.3.1' - implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" + implementation("androidx.lifecycle:lifecycle-common-java8:$lifecycle_version") //room def room_version = '2.3.0' - implementation "androidx.room:room-runtime:$room_version" - kapt "androidx.room:room-compiler:$room_version" - testImplementation "androidx.room:room-testing:$room_version" + implementation("androidx.room:room-runtime:$room_version") + kapt("androidx.room:room-compiler:$room_version") + testImplementation("androidx.room:room-testing:$room_version") //liveEventBus - implementation 'io.github.jeremyliao:live-event-bus-x:1.8.0' + implementation('io.github.jeremyliao:live-event-bus-x:1.8.0') //规则相关 - implementation 'org.jsoup:jsoup:1.14.1' - implementation 'com.jayway.jsonpath:json-path:2.6.0' - implementation project(path: ':JsoupXpath') - implementation project(path: ':epublib') + implementation('org.jsoup:jsoup:1.14.1') + implementation('com.jayway.jsonpath:json-path:2.6.0') + implementation(project(path: ':JsoupXpath')) + implementation(project(path: ':epublib')) //JS rhino - implementation 'com.github.gedoor:rhino-android:1.6' + implementation('com.github.gedoor:rhino-android:1.6') //网络 - implementation 'com.squareup.okhttp3:okhttp:4.9.1' + implementation('com.squareup.okhttp3:okhttp:4.9.1') def cronet_version = '92.0.4509.1' - compileOnly "org.microg:cronet-api:$cronet_version" - cronetImplementation "org.microg:cronet-native:$cronet_version" - cronetImplementation "org.microg:cronet-api:$cronet_version" - cronetImplementation "org.microg:cronet-common:$cronet_version" + compileOnly("org.microg:cronet-api:$cronet_version") + cronetImplementation("org.microg:cronet-native:$cronet_version") + cronetImplementation("org.microg:cronet-api:$cronet_version") + cronetImplementation("org.microg:cronet-common:$cronet_version") //Glide - implementation 'com.github.bumptech.glide:glide:4.12.0' + implementation('com.github.bumptech.glide:glide:4.12.0') //webServer - implementation 'org.nanohttpd:nanohttpd:2.3.1' - implementation 'org.nanohttpd:nanohttpd-websocket:2.3.1' + implementation('org.nanohttpd:nanohttpd:2.3.1') + implementation('org.nanohttpd:nanohttpd-websocket:2.3.1') //二维码 - implementation 'com.github.jenly1314:zxing-lite:2.1.0' + implementation('com.github.jenly1314:zxing-lite:2.1.0') //颜色选择 - implementation 'com.jaredrummler:colorpicker:1.1.0' + implementation('com.jaredrummler:colorpicker:1.1.0') //apache - implementation 'org.apache.commons:commons-text:1.9' + implementation('org.apache.commons:commons-text:1.9') //MarkDown def markwonVersion = "4.6.2" - implementation "io.noties.markwon:core:$markwonVersion" - implementation "io.noties.markwon:image-glide:$markwonVersion" - implementation "io.noties.markwon:ext-tables:$markwonVersion" - implementation "io.noties.markwon:html:$markwonVersion" + implementation("io.noties.markwon:core:$markwonVersion") + implementation("io.noties.markwon:image-glide:$markwonVersion") + implementation("io.noties.markwon:ext-tables:$markwonVersion") + implementation("io.noties.markwon:html:$markwonVersion") //转换繁体 - implementation 'com.github.liuyueyi.quick-chinese-transfer:quick-transfer-core:0.2.1' + implementation('com.github.liuyueyi.quick-chinese-transfer:quick-transfer-core:0.2.1') } From ed891c3e2b73fc47d9403c4bdf2ffc9de67dd6f5 Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 20 Jul 2021 21:03:42 +0800 Subject: [PATCH 29/40] =?UTF-8?q?=E9=87=87=E7=94=A8Flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 1 + .../java/io/legado/app/data/dao/BookDao.kt | 16 ++-- .../io/legado/app/data/dao/BookSourceDao.kt | 24 +++--- .../ui/book/arrange/ArrangeBookActivity.kt | 28 ++++--- .../legado/app/ui/book/cache/CacheActivity.kt | 47 ++++++----- .../book/changesource/ChangeSourceDialog.kt | 16 ++-- .../app/ui/book/local/ImportBookActivity.kt | 13 ++- .../app/ui/book/search/SearchActivity.kt | 55 ++++++------ .../book/source/manage/BookSourceActivity.kt | 83 ++++++++++--------- .../book/source/manage/GroupManageDialog.kt | 23 ++--- .../bookshelf/style1/books/BooksFragment.kt | 24 +++--- .../bookshelf/style2/BookshelfFragment2.kt | 23 ++--- .../app/ui/main/explore/ExploreFragment.kt | 70 ++++++++-------- 13 files changed, 228 insertions(+), 195 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 08c49ef61..2fd7672db 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -169,6 +169,7 @@ dependencies { //room def room_version = '2.3.0' implementation("androidx.room:room-runtime:$room_version") + implementation("androidx.room:room-ktx:$room_version") kapt("androidx.room:room-compiler:$room_version") testImplementation("androidx.room:room-testing:$room_version") diff --git a/app/src/main/java/io/legado/app/data/dao/BookDao.kt b/app/src/main/java/io/legado/app/data/dao/BookDao.kt index 9a2a5a0db..e541bf37e 100644 --- a/app/src/main/java/io/legado/app/data/dao/BookDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/BookDao.kt @@ -1,33 +1,33 @@ package io.legado.app.data.dao -import androidx.lifecycle.LiveData import androidx.room.* import io.legado.app.constant.BookType import io.legado.app.data.entities.Book +import kotlinx.coroutines.flow.Flow @Dao interface BookDao { @Query("SELECT * FROM books order by durChapterTime desc") - fun observeAll(): LiveData> + fun observeAll(): Flow> @Query("SELECT * FROM books WHERE type = ${BookType.audio}") - fun observeAudio(): LiveData> + fun observeAudio(): Flow> @Query("SELECT * FROM books WHERE origin = '${BookType.local}'") - fun observeLocal(): LiveData> + fun observeLocal(): Flow> @Query("select * from books where type != ${BookType.audio} and origin != '${BookType.local}' and ((SELECT sum(groupId) FROM book_groups where groupId > 0) & `group`) = 0") - fun observeNoGroup(): LiveData> + fun observeNoGroup(): Flow> @Query("SELECT bookUrl FROM books WHERE origin = '${BookType.local}'") - fun observeLocalUri(): LiveData> + fun observeLocalUri(): Flow> @Query("SELECT * FROM books WHERE (`group` & :group) > 0") - fun observeByGroup(group: Long): LiveData> + fun observeByGroup(group: Long): Flow> @Query("SELECT * FROM books WHERE name like '%'||:key||'%' or author like '%'||:key||'%'") - fun liveDataSearch(key: String): LiveData> + fun liveDataSearch(key: String): Flow> @Query("SELECT * FROM books WHERE (`group` & :group) > 0") fun getBooksByGroup(group: Long): List diff --git a/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt b/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt index a9d113098..7bc28742d 100644 --- a/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt @@ -1,14 +1,14 @@ package io.legado.app.data.dao -import androidx.lifecycle.LiveData import androidx.room.* import io.legado.app.data.entities.BookSource +import kotlinx.coroutines.flow.Flow @Dao interface BookSourceDao { @Query("select * from book_sources order by customOrder asc") - fun liveDataAll(): LiveData> + fun liveDataAll(): Flow> @Query( """ @@ -20,19 +20,19 @@ interface BookSourceDao { order by customOrder asc """ ) - fun liveDataSearch(searchKey: String): LiveData> + fun liveDataSearch(searchKey: String): Flow> @Query("select * from book_sources where bookSourceGroup like :searchKey order by customOrder asc") - fun liveDataGroupSearch(searchKey: String): LiveData> + fun liveDataGroupSearch(searchKey: String): Flow> @Query("select * from book_sources where enabled = 1 order by customOrder asc") - fun liveDataEnabled(): LiveData> + fun liveDataEnabled(): Flow> @Query("select * from book_sources where enabled = 0 order by customOrder asc") - fun liveDataDisabled(): LiveData> + fun liveDataDisabled(): Flow> @Query("select * from book_sources where enabledExplore = 1 and trim(exploreUrl) <> '' order by customOrder asc") - fun liveExplore(): LiveData> + fun liveExplore(): Flow> @Query( """ @@ -43,7 +43,7 @@ interface BookSourceDao { order by customOrder asc """ ) - fun liveExplore(key: String): LiveData> + fun liveExplore(key: String): Flow> @Query( """ @@ -54,13 +54,13 @@ interface BookSourceDao { order by customOrder asc """ ) - fun liveGroupExplore(key: String): LiveData> + fun liveGroupExplore(key: String): Flow> @Query("select distinct bookSourceGroup from book_sources where trim(bookSourceGroup) <> ''") - fun liveGroup(): LiveData> + fun liveGroup(): Flow> @Query("select distinct bookSourceGroup from book_sources where enabled = 1 and trim(bookSourceGroup) <> ''") - fun liveGroupEnabled(): LiveData> + fun liveGroupEnabled(): Flow> @Query( """ @@ -70,7 +70,7 @@ interface BookSourceDao { and trim(bookSourceGroup) <> '' """ ) - fun liveExploreGroup(): LiveData> + fun liveExploreGroup(): Flow> @Query("select * from book_sources where bookSourceGroup like '%' || :group || '%'") fun getByGroup(group: String): List diff --git a/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt index 63268b2d9..2495f36f0 100644 --- a/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt @@ -28,6 +28,8 @@ import io.legado.app.utils.cnCompare import io.legado.app.utils.getPrefInt import io.legado.app.utils.viewbindingdelegate.viewBinding import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -45,7 +47,7 @@ class ArrangeBookActivity : VMBaseActivity>? = null - private var booksLiveData: LiveData>? = null + private var booksFlowJob: Job? = null private var menu: Menu? = null private var groupId: Long = -1 @@ -118,26 +120,26 @@ class ArrangeBookActivity : VMBaseActivity appDb.bookDao.observeAll() AppConst.bookGroupLocalId -> appDb.bookDao.observeLocal() AppConst.bookGroupAudioId -> appDb.bookDao.observeAudio() AppConst.bookGroupNoneId -> appDb.bookDao.observeNoGroup() else -> appDb.bookDao.observeByGroup(groupId) - } - booksLiveData?.observe(this, { list -> - val books = when (getPrefInt(PreferKey.bookshelfSort)) { - 1 -> list.sortedByDescending { it.latestChapterTime } - 2 -> list.sortedWith { o1, o2 -> - o1.name.cnCompare(o2.name) + }.collect { list -> + val books = when (getPrefInt(PreferKey.bookshelfSort)) { + 1 -> list.sortedByDescending { it.latestChapterTime } + 2 -> list.sortedWith { o1, o2 -> + o1.name.cnCompare(o2.name) + } + 3 -> list.sortedBy { it.order } + else -> list.sortedByDescending { it.durChapterTime } } - 3 -> list.sortedBy { it.order } - else -> list.sortedByDescending { it.durChapterTime } + adapter.setItems(books) } - adapter.setItems(books) - }) + } } override fun onCompatOptionsItemSelected(item: MenuItem): Boolean { diff --git a/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt b/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt index 5a3822c1d..8d24c807b 100644 --- a/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt @@ -31,6 +31,8 @@ import io.legado.app.utils.* import io.legado.app.utils.viewbindingdelegate.viewBinding import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.util.concurrent.ConcurrentHashMap @@ -45,7 +47,7 @@ class CacheActivity : VMBaseActivity() private val exportBookPathKey = "exportBookPath" lateinit var adapter: CacheAdapter private var groupLiveData: LiveData>? = null - private var booksLiveData: LiveData>? = null + private var booksFlowJob: Job? = null private var menu: Menu? = null private var exportPosition = -1 private val groupList: ArrayList = arrayListOf() @@ -147,29 +149,30 @@ class CacheActivity : VMBaseActivity() } private fun initBookData() { - booksLiveData?.removeObservers(this) - booksLiveData = when (groupId) { - AppConst.bookGroupAllId -> appDb.bookDao.observeAll() - AppConst.bookGroupLocalId -> appDb.bookDao.observeLocal() - AppConst.bookGroupAudioId -> appDb.bookDao.observeAudio() - AppConst.bookGroupNoneId -> appDb.bookDao.observeNoGroup() - else -> appDb.bookDao.observeByGroup(groupId) - } - booksLiveData?.observe(this, { list -> - val booksDownload = list.filter { - it.isOnLineTxt() - } - val books = when (getPrefInt(PreferKey.bookshelfSort)) { - 1 -> booksDownload.sortedByDescending { it.latestChapterTime } - 2 -> booksDownload.sortedWith { o1, o2 -> - o1.name.cnCompare(o2.name) + booksFlowJob?.cancel() + booksFlowJob = launch { + when (groupId) { + AppConst.bookGroupAllId -> appDb.bookDao.observeAll() + AppConst.bookGroupLocalId -> appDb.bookDao.observeLocal() + AppConst.bookGroupAudioId -> appDb.bookDao.observeAudio() + AppConst.bookGroupNoneId -> appDb.bookDao.observeNoGroup() + else -> appDb.bookDao.observeByGroup(groupId) + }.collect { list -> + val booksDownload = list.filter { + it.isOnLineTxt() + } + val books = when (getPrefInt(PreferKey.bookshelfSort)) { + 1 -> booksDownload.sortedByDescending { it.latestChapterTime } + 2 -> booksDownload.sortedWith { o1, o2 -> + o1.name.cnCompare(o2.name) + } + 3 -> booksDownload.sortedBy { it.order } + else -> booksDownload.sortedByDescending { it.durChapterTime } } - 3 -> booksDownload.sortedBy { it.order } - else -> booksDownload.sortedByDescending { it.durChapterTime } + adapter.setItems(books) + initCacheSize(books) } - adapter.setItems(books) - initCacheSize(books) - }) + } } private fun initGroupData() { diff --git a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt index e83118039..969217fd8 100644 --- a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt @@ -22,6 +22,8 @@ import io.legado.app.ui.book.source.manage.BookSourceActivity import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.utils.* import io.legado.app.utils.viewbindingdelegate.viewBinding +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch class ChangeSourceDialog : BaseDialogFragment(), @@ -148,13 +150,15 @@ class ChangeSourceDialog : BaseDialogFragment(), viewModel.searchBooksLiveData.observe(viewLifecycleOwner, { adapter.setItems(it) }) - appDb.bookSourceDao.liveGroupEnabled().observe(this, { - groups.clear() - it.map { group -> - groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + launch { + appDb.bookSourceDao.liveGroupEnabled().collect { + groups.clear() + it.map { group -> + groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + } + upGroupMenu() } - upGroupMenu() - }) + } } private val stopMenuItem: MenuItem? diff --git a/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt index 5d45d4795..02388239e 100644 --- a/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt @@ -9,7 +9,6 @@ import android.view.MenuItem import androidx.activity.viewModels import androidx.appcompat.widget.PopupMenu import androidx.documentfile.provider.DocumentFile -import androidx.lifecycle.LiveData import androidx.recyclerview.widget.LinearLayoutManager import io.legado.app.R import io.legado.app.base.VMBaseActivity @@ -27,6 +26,7 @@ import io.legado.app.utils.* import io.legado.app.utils.viewbindingdelegate.viewBinding import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File @@ -46,7 +46,6 @@ class ImportBookActivity : VMBaseActivity() private lateinit var adapter: ImportBookAdapter - private var localUriLiveData: LiveData>? = null private var sdPath = FileUtils.getSdCardPath() private var path = sdPath private val selectFolder = registerForActivityResult(FilePicker()) { uri -> @@ -125,11 +124,11 @@ class ImportBookActivity : VMBaseActivity>? = null - private var bookData: LiveData>? = null + private var booksFlowJob: Job? = null private var menu: Menu? = null private var precisionSearchMenuItem: MenuItem? = null private var groups = linkedSetOf() @@ -58,7 +60,7 @@ class SearchActivity : VMBaseActivity - groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + private fun initData() { + launch { + appDb.bookSourceDao.liveGroupEnabled().collect { + groups.clear() + it.map { group -> + groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + } + upGroupMenu() } - upGroupMenu() - }) + } viewModel.searchBookLiveData.observe(this, { upSearchItems(it) }) @@ -268,23 +272,26 @@ class SearchActivity : VMBaseActivity(), @@ -52,7 +54,7 @@ class BookSourceActivity : VMBaseActivity>? = null + private var sourceFlowJob: Job? = null private val groups = linkedSetOf() private var groupMenu: SubMenu? = null private var sort = Sort.Default @@ -94,7 +96,7 @@ class BookSourceActivity : VMBaseActivity { item.isChecked = true sortCheck(Sort.Default) - initLiveDataBookSource(searchView.query?.toString()) + upBookSource(searchView.query?.toString()) } R.id.menu_sort_auto -> { item.isChecked = true sortCheck(Sort.Weight) - initLiveDataBookSource(searchView.query?.toString()) + upBookSource(searchView.query?.toString()) } R.id.menu_sort_name -> { item.isChecked = true sortCheck(Sort.Name) - initLiveDataBookSource(searchView.query?.toString()) + upBookSource(searchView.query?.toString()) } R.id.menu_sort_url -> { item.isChecked = true sortCheck(Sort.Url) - initLiveDataBookSource(searchView.query?.toString()) + upBookSource(searchView.query?.toString()) } R.id.menu_sort_time -> { item.isChecked = true sortCheck(Sort.Update) - initLiveDataBookSource(searchView.query?.toString()) + upBookSource(searchView.query?.toString()) } R.id.menu_sort_enable -> { item.isChecked = true sortCheck(Sort.Enable) - initLiveDataBookSource(searchView.query?.toString()) + upBookSource(searchView.query?.toString()) } R.id.menu_enabled_group -> { searchView.setQuery(getString(R.string.enabled), true) @@ -199,27 +201,27 @@ class BookSourceActivity : VMBaseActivity { - appDb.bookSourceDao.liveDataAll() - } - searchKey == getString(R.string.enabled) -> { - appDb.bookSourceDao.liveDataEnabled() - } - searchKey == getString(R.string.disabled) -> { - appDb.bookSourceDao.liveDataDisabled() - } - searchKey.startsWith("group:") -> { - val key = searchKey.substringAfter("group:") - appDb.bookSourceDao.liveDataGroupSearch("%$key%") - } - else -> { - appDb.bookSourceDao.liveDataSearch("%$searchKey%") - } - }.apply { - observe(this@BookSourceActivity, { data -> + private fun upBookSource(searchKey: String? = null) { + sourceFlowJob?.cancel() + sourceFlowJob = launch { + when { + searchKey.isNullOrEmpty() -> { + appDb.bookSourceDao.liveDataAll() + } + searchKey == getString(R.string.enabled) -> { + appDb.bookSourceDao.liveDataEnabled() + } + searchKey == getString(R.string.disabled) -> { + appDb.bookSourceDao.liveDataDisabled() + } + searchKey.startsWith("group:") -> { + val key = searchKey.substringAfter("group:") + appDb.bookSourceDao.liveDataGroupSearch("%$key%") + } + else -> { + appDb.bookSourceDao.liveDataSearch("%$searchKey%") + } + }.collect { data -> val sourceList = if (sortAscending) when (sort) { Sort.Weight -> data.sortedBy { it.weight } @@ -254,7 +256,7 @@ class BookSourceActivity : VMBaseActivity data.reversed() } adapter.setItems(sourceList, adapter.diffItemCallback) - }) + } } } @@ -273,13 +275,16 @@ class BookSourceActivity : VMBaseActivity - groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) - } - upGroupMenu() - }) + launch { + appDb.bookSourceDao.liveGroup() + .collect { + groups.clear() + it.forEach { group -> + groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + } + upGroupMenu() + } + } } override fun selectAll(selectAll: Boolean) { @@ -453,7 +458,7 @@ class BookSourceActivity : VMBaseActivity() - it.map { group -> - groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + launch { + appDb.bookSourceDao.liveGroup().collect { + val groups = linkedSetOf() + it.map { group -> + groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + } + adapter.setItems(groups.toList()) } - adapter.setItems(groups.toList()) - }) + } } override fun onMenuItemClick(item: MenuItem?): Boolean { diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt index ea918d46a..70ae87acc 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt @@ -4,7 +4,6 @@ import android.os.Bundle import android.view.View import androidx.core.view.isGone import androidx.fragment.app.activityViewModels -import androidx.lifecycle.LiveData import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -29,6 +28,9 @@ import io.legado.app.utils.getPrefInt import io.legado.app.utils.observeEvent import io.legado.app.utils.startActivity import io.legado.app.utils.viewbindingdelegate.viewBinding +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch import kotlin.math.max /** @@ -52,7 +54,7 @@ class BooksFragment : BaseFragment(R.layout.fragment_books), private val activityViewModel: MainViewModel by activityViewModels() private lateinit var booksAdapter: BaseBooksAdapter<*> - private var bookshelfLiveData: LiveData>? = null + private var booksFlowJob: Job? = null private var position = 0 private var groupId = -1L @@ -101,15 +103,15 @@ class BooksFragment : BaseFragment(R.layout.fragment_books), } private fun upRecyclerData() { - bookshelfLiveData?.removeObservers(this) - bookshelfLiveData = when (groupId) { - AppConst.bookGroupAllId -> appDb.bookDao.observeAll() - AppConst.bookGroupLocalId -> appDb.bookDao.observeLocal() - AppConst.bookGroupAudioId -> appDb.bookDao.observeAudio() - AppConst.bookGroupNoneId -> appDb.bookDao.observeNoGroup() - else -> appDb.bookDao.observeByGroup(groupId) - }.apply { - observe(viewLifecycleOwner) { list -> + booksFlowJob?.cancel() + booksFlowJob = launch { + when (groupId) { + AppConst.bookGroupAllId -> appDb.bookDao.observeAll() + AppConst.bookGroupLocalId -> appDb.bookDao.observeLocal() + AppConst.bookGroupAudioId -> appDb.bookDao.observeAudio() + AppConst.bookGroupNoneId -> appDb.bookDao.observeNoGroup() + else -> appDb.bookDao.observeByGroup(groupId) + }.collect { list -> binding.tvEmptyMsg.isGone = list.isNotEmpty() val books = when (getPrefInt(PreferKey.bookshelfSort)) { 1 -> list.sortedByDescending { it.latestChapterTime } diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt index 36bf0a16b..d3627eb23 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt @@ -32,6 +32,9 @@ import io.legado.app.utils.getPrefInt import io.legado.app.utils.observeEvent import io.legado.app.utils.startActivity import io.legado.app.utils.viewbindingdelegate.viewBinding +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch import kotlin.math.max /** @@ -46,7 +49,7 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1), private lateinit var booksAdapter: BaseBooksAdapter<*> override var groupId = AppConst.bookGroupNoneId private var bookGroupLiveData: LiveData>? = null - private var bookshelfLiveData: LiveData>? = null + private var booksFlowJob: Job? = null private var bookGroups: List = emptyList() override var books: List = emptyList() @@ -130,15 +133,15 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1), } private fun initBooksData() { - bookshelfLiveData?.removeObservers(this) - bookshelfLiveData = when (groupId) { - AppConst.bookGroupAllId -> appDb.bookDao.observeAll() - AppConst.bookGroupLocalId -> appDb.bookDao.observeLocal() - AppConst.bookGroupAudioId -> appDb.bookDao.observeAudio() - AppConst.bookGroupNoneId -> appDb.bookDao.observeNoGroup() - else -> appDb.bookDao.observeByGroup(groupId) - }.apply { - observe(viewLifecycleOwner) { list -> + booksFlowJob?.cancel() + booksFlowJob = launch { + when (groupId) { + AppConst.bookGroupAllId -> appDb.bookDao.observeAll() + AppConst.bookGroupLocalId -> appDb.bookDao.observeLocal() + AppConst.bookGroupAudioId -> appDb.bookDao.observeAudio() + AppConst.bookGroupNoneId -> appDb.bookDao.observeNoGroup() + else -> appDb.bookDao.observeByGroup(groupId) + }.collect { list -> binding.tvEmptyMsg.isGone = list.isNotEmpty() books = when (getPrefInt(PreferKey.bookshelfSort)) { 1 -> list.sortedByDescending { diff --git a/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt b/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt index d8988e842..606616dab 100644 --- a/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt @@ -8,7 +8,6 @@ import android.view.View import androidx.appcompat.widget.SearchView import androidx.core.view.isGone import androidx.fragment.app.viewModels -import androidx.lifecycle.LiveData import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -27,6 +26,9 @@ import io.legado.app.utils.cnCompare import io.legado.app.utils.splitNotBlank import io.legado.app.utils.startActivity import io.legado.app.utils.viewbindingdelegate.viewBinding +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch /** * 发现界面 @@ -39,8 +41,7 @@ class ExploreFragment : VMBaseFragment(R.layout.fragment_explo private lateinit var linearLayoutManager: LinearLayoutManager private lateinit var searchView: SearchView private val groups = linkedSetOf() - private var liveGroup: LiveData>? = null - private var liveExplore: LiveData>? = null + private var exploreFlowJob: Job? = null private var groupsMenu: SubMenu? = null override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { @@ -49,7 +50,7 @@ class ExploreFragment : VMBaseFragment(R.layout.fragment_explo initSearchView() initRecyclerView() initGroupData() - initExploreData() + upExploreData() } override fun onCompatCreateOptionsMenu(menu: Menu) { @@ -76,7 +77,7 @@ class ExploreFragment : VMBaseFragment(R.layout.fragment_explo } override fun onQueryTextChange(newText: String?): Boolean { - initExploreData(newText) + upExploreData(newText) return false } }) @@ -100,38 +101,41 @@ class ExploreFragment : VMBaseFragment(R.layout.fragment_explo } private fun initGroupData() { - liveGroup?.removeObservers(viewLifecycleOwner) - liveGroup = appDb.bookSourceDao.liveExploreGroup() - liveGroup?.observe(viewLifecycleOwner, { - groups.clear() - it.map { group -> - groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) - } - upGroupsMenu() - }) + launch { + appDb.bookSourceDao.liveExploreGroup() + .collect { + groups.clear() + it.map { group -> + groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + } + upGroupsMenu() + } + } } - private fun initExploreData(searchKey: String? = null) { - liveExplore?.removeObservers(viewLifecycleOwner) - liveExplore = when { - searchKey.isNullOrBlank() -> { - appDb.bookSourceDao.liveExplore() - } - searchKey.startsWith("group:") -> { - val key = searchKey.substringAfter("group:") - appDb.bookSourceDao.liveGroupExplore("%$key%") + private fun upExploreData(searchKey: String? = null) { + exploreFlowJob?.cancel() + exploreFlowJob = launch { + val exploreFlow = when { + searchKey.isNullOrBlank() -> { + appDb.bookSourceDao.liveExplore() + } + searchKey.startsWith("group:") -> { + val key = searchKey.substringAfter("group:") + appDb.bookSourceDao.liveGroupExplore("%$key%") + } + else -> { + appDb.bookSourceDao.liveExplore("%$searchKey%") + } } - else -> { - appDb.bookSourceDao.liveExplore("%$searchKey%") + exploreFlow.collect { + binding.tvEmptyMsg.isGone = it.isNotEmpty() || searchView.query.isNotEmpty() + val diffResult = DiffUtil + .calculateDiff(ExploreDiffCallBack(ArrayList(adapter.getItems()), it)) + adapter.setItems(it) + diffResult.dispatchUpdatesTo(adapter) } } - liveExplore?.observe(viewLifecycleOwner, { - binding.tvEmptyMsg.isGone = it.isNotEmpty() || searchView.query.isNotEmpty() - val diffResult = DiffUtil - .calculateDiff(ExploreDiffCallBack(ArrayList(adapter.getItems()), it)) - adapter.setItems(it) - diffResult.dispatchUpdatesTo(adapter) - }) } private fun upGroupsMenu() = groupsMenu?.let { subMenu -> @@ -151,7 +155,7 @@ class ExploreFragment : VMBaseFragment(R.layout.fragment_explo } override fun refreshData() { - initExploreData(searchView.query?.toString()) + upExploreData(searchView.query?.toString()) } override fun scrollTo(pos: Int) { From d583501b5e231177229af5a3958576db40ececc3 Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 20 Jul 2021 22:29:07 +0800 Subject: [PATCH 30/40] =?UTF-8?q?=E9=87=87=E7=94=A8Flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/data/dao/RssSourceDao.kt | 27 +++++---- .../io/legado/app/ui/main/rss/RssFragment.kt | 53 ++++++++-------- .../ui/rss/source/manage/GroupManageDialog.kt | 20 +++++-- .../ui/rss/source/manage/RssSourceActivity.kt | 60 ++++++++++--------- 4 files changed, 90 insertions(+), 70 deletions(-) diff --git a/app/src/main/java/io/legado/app/data/dao/RssSourceDao.kt b/app/src/main/java/io/legado/app/data/dao/RssSourceDao.kt index deaa45726..6abd2f847 100644 --- a/app/src/main/java/io/legado/app/data/dao/RssSourceDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/RssSourceDao.kt @@ -1,8 +1,8 @@ package io.legado.app.data.dao -import androidx.lifecycle.LiveData import androidx.room.* import io.legado.app.data.entities.RssSource +import kotlinx.coroutines.flow.Flow @Dao interface RssSourceDao { @@ -20,25 +20,32 @@ interface RssSourceDao { val size: Int @Query("SELECT * FROM rssSources order by customOrder") - fun liveAll(): LiveData> + fun liveAll(): Flow> @Query("SELECT * FROM rssSources where sourceName like :key or sourceUrl like :key or sourceGroup like :key order by customOrder") - fun liveSearch(key: String): LiveData> + fun liveSearch(key: String): Flow> @Query("SELECT * FROM rssSources where sourceGroup like :key order by customOrder") - fun liveGroupSearch(key: String): LiveData> + fun liveGroupSearch(key: String): Flow> @Query("SELECT * FROM rssSources where enabled = 1 order by customOrder") - fun liveEnabled(): LiveData> - - @Query("SELECT * FROM rssSources where enabled = 1 and (sourceName like :searchKey or sourceGroup like :searchKey or sourceUrl like :searchKey) order by customOrder") - fun liveEnabled(searchKey: String): LiveData> + fun liveEnabled(): Flow> + + @Query( + """ + SELECT * FROM rssSources + where enabled = 1 + and (sourceName like :searchKey or sourceGroup like :searchKey or sourceUrl like :searchKey) + order by customOrder + """ + ) + fun liveEnabled(searchKey: String): Flow> @Query("SELECT * FROM rssSources where enabled = 1 and sourceGroup like :searchKey order by customOrder") - fun liveEnabledByGroup(searchKey: String): LiveData> + fun liveEnabledByGroup(searchKey: String): Flow> @Query("select distinct sourceGroup from rssSources where trim(sourceGroup) <> ''") - fun liveGroup(): LiveData> + fun liveGroup(): Flow> @get:Query("select distinct sourceGroup from rssSources where trim(sourceGroup) <> ''") val allGroup: List diff --git a/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt b/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt index 39c9143d5..0d20d7252 100644 --- a/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt @@ -7,7 +7,6 @@ import android.view.SubMenu import android.view.View import androidx.appcompat.widget.SearchView import androidx.fragment.app.viewModels -import androidx.lifecycle.LiveData import io.legado.app.R import io.legado.app.base.VMBaseFragment import io.legado.app.constant.AppPattern @@ -29,6 +28,9 @@ import io.legado.app.utils.openUrl import io.legado.app.utils.splitNotBlank import io.legado.app.utils.startActivity import io.legado.app.utils.viewbindingdelegate.viewBinding +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch /** @@ -40,9 +42,8 @@ class RssFragment : VMBaseFragment(R.layout.fragment_rss), override val viewModel by viewModels() private lateinit var adapter: RssAdapter private lateinit var searchView: SearchView - private var liveRssData: LiveData>? = null + private var rssFlowJob: Job? = null private val groups = linkedSetOf() - private var liveGroup: LiveData>? = null private var groupsMenu: SubMenu? = null override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { @@ -51,7 +52,7 @@ class RssFragment : VMBaseFragment(R.layout.fragment_rss), initSearchView() initRecyclerView() initGroupData() - initData() + upRssFlowJob() } override fun onCompatCreateOptionsMenu(menu: Menu) { @@ -97,7 +98,7 @@ class RssFragment : VMBaseFragment(R.layout.fragment_rss), } override fun onQueryTextChange(newText: String?): Boolean { - initData(newText) + upRssFlowJob(newText) return false } }) @@ -118,32 +119,32 @@ class RssFragment : VMBaseFragment(R.layout.fragment_rss), } } - private fun initData(searchKey: String? = null) { - liveRssData?.removeObservers(this) - liveRssData = when { - searchKey.isNullOrEmpty() -> appDb.rssSourceDao.liveEnabled() - searchKey.startsWith("group:") -> { - val key = searchKey.substringAfter("group:") - appDb.rssSourceDao.liveEnabledByGroup("%$key%") + private fun initGroupData() { + launch { + appDb.rssSourceDao.liveGroup().collect { + groups.clear() + it.map { group -> + groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + } + upGroupsMenu() } - else -> appDb.rssSourceDao.liveEnabled("%$searchKey%") - }.apply { - observe(viewLifecycleOwner, { - adapter.setItems(it) - }) } } - private fun initGroupData() { - liveGroup?.removeObservers(viewLifecycleOwner) - liveGroup = appDb.rssSourceDao.liveGroup() - liveGroup?.observe(viewLifecycleOwner, { - groups.clear() - it.map { group -> - groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + private fun upRssFlowJob(searchKey: String? = null) { + rssFlowJob?.cancel() + rssFlowJob = launch { + when { + searchKey.isNullOrEmpty() -> appDb.rssSourceDao.liveEnabled() + searchKey.startsWith("group:") -> { + val key = searchKey.substringAfter("group:") + appDb.rssSourceDao.liveEnabledByGroup("%$key%") + } + else -> appDb.rssSourceDao.liveEnabled("%$searchKey%") + }.collect { + adapter.setItems(it) } - upGroupsMenu() - }) + } } override fun openRss(rssSource: RssSource) { diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt index 0c4286e15..806e8122d 100644 --- a/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt +++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt @@ -26,6 +26,8 @@ import io.legado.app.lib.theme.primaryColor import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.utils.* import io.legado.app.utils.viewbindingdelegate.viewBinding +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener { @@ -62,13 +64,19 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener tvOk.setOnClickListener { dismissAllowingStateLoss() } - appDb.rssSourceDao.liveGroup().observe(viewLifecycleOwner, { - val groups = linkedSetOf() - it.map { group -> - groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + initData() + } + + private fun initData() { + launch { + appDb.rssSourceDao.liveGroup().collect { + val groups = linkedSetOf() + it.map { group -> + groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + } + adapter.setItems(groups.toList()) } - adapter.setItems(groups.toList()) - }) + } } override fun onMenuItemClick(item: MenuItem?): Boolean { diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt index e90a1f287..23ecb9f17 100644 --- a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt @@ -10,7 +10,6 @@ import androidx.activity.viewModels import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.SearchView import androidx.documentfile.provider.DocumentFile -import androidx.lifecycle.LiveData import androidx.recyclerview.widget.ItemTouchHelper import io.legado.app.R import io.legado.app.base.VMBaseActivity @@ -35,6 +34,9 @@ import io.legado.app.ui.widget.recycler.ItemTouchCallback import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.utils.* import io.legado.app.utils.viewbindingdelegate.viewBinding +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch import java.io.File /** @@ -49,7 +51,7 @@ class RssSourceActivity : VMBaseActivity() private val importRecordKey = "rssSourceRecordKey" private lateinit var adapter: RssSourceAdapter - private var sourceLiveData: LiveData>? = null + private var sourceFlowJob: Job? = null private var groups = hashSetOf() private var groupMenu: SubMenu? = null private val qrCodeResult = registerForActivityResult(QrCodeResult()) { @@ -86,8 +88,8 @@ class RssSourceActivity : VMBaseActivity - groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + private fun initGroupFlow() { + launch { + appDb.rssSourceDao.liveGroup().collect { + groups.clear() + it.map { group -> + groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + } + upGroupMenu() } - upGroupMenu() - }) + } } override fun selectAll(selectAll: Boolean) { @@ -224,23 +228,23 @@ class RssSourceActivity : VMBaseActivity { - appDb.rssSourceDao.liveAll() - } - searchKey.startsWith("group:") -> { - val key = searchKey.substringAfter("group:") - appDb.rssSourceDao.liveGroupSearch("%$key%") - } - else -> { - appDb.rssSourceDao.liveSearch("%$searchKey%") - } - }.apply { - observe(this@RssSourceActivity, { + private fun upSourceFlow(searchKey: String? = null) { + sourceFlowJob?.cancel() + sourceFlowJob = launch { + when { + searchKey.isNullOrBlank() -> { + appDb.rssSourceDao.liveAll() + } + searchKey.startsWith("group:") -> { + val key = searchKey.substringAfter("group:") + appDb.rssSourceDao.liveGroupSearch("%$key%") + } + else -> { + appDb.rssSourceDao.liveSearch("%$searchKey%") + } + }.collect { adapter.setItems(it, adapter.diffItemCallback) - }) + } } } From 91bdedf0c0e8d57e7c8a9311f2b6810d15755d31 Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 20 Jul 2021 23:11:56 +0800 Subject: [PATCH 31/40] =?UTF-8?q?=E9=87=87=E7=94=A8Flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/data/dao/BookChapterDao.kt | 6 +-- .../io/legado/app/data/dao/BookmarkDao.kt | 6 +-- .../app/ui/book/toc/BookmarkFragment.kt | 32 +++++++-------- .../app/ui/book/toc/ChapterListFragment.kt | 39 ++++++++----------- .../io/legado/app/ui/book/toc/TocViewModel.kt | 8 ++-- 5 files changed, 40 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/io/legado/app/data/dao/BookChapterDao.kt b/app/src/main/java/io/legado/app/data/dao/BookChapterDao.kt index 0041591e3..24e632b0d 100644 --- a/app/src/main/java/io/legado/app/data/dao/BookChapterDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/BookChapterDao.kt @@ -1,20 +1,20 @@ package io.legado.app.data.dao -import androidx.lifecycle.LiveData import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import io.legado.app.data.entities.BookChapter +import kotlinx.coroutines.flow.Flow @Dao interface BookChapterDao { @Query("select * from chapters where bookUrl = :bookUrl order by `index`") - fun observeByBook(bookUrl: String): LiveData> + fun observeByBook(bookUrl: String): Flow> @Query("SELECT * FROM chapters where bookUrl = :bookUrl and title like '%'||:key||'%' order by `index`") - fun liveDataSearch(bookUrl: String, key: String): LiveData> + fun liveDataSearch(bookUrl: String, key: String): Flow> @Query("select * from chapters where bookUrl = :bookUrl order by `index`") fun getChapterList(bookUrl: String): List diff --git a/app/src/main/java/io/legado/app/data/dao/BookmarkDao.kt b/app/src/main/java/io/legado/app/data/dao/BookmarkDao.kt index 4ce8b05bb..052884126 100644 --- a/app/src/main/java/io/legado/app/data/dao/BookmarkDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/BookmarkDao.kt @@ -1,8 +1,8 @@ package io.legado.app.data.dao -import androidx.lifecycle.LiveData import androidx.room.* import io.legado.app.data.entities.Bookmark +import kotlinx.coroutines.flow.Flow @Dao @@ -15,7 +15,7 @@ interface BookmarkDao { fun observeByBook( bookName: String, bookAuthor: String - ): LiveData> + ): Flow> @Query( """ @@ -27,7 +27,7 @@ interface BookmarkDao { bookName: String, bookAuthor: String, key: String - ): LiveData> + ): Flow> @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(vararg bookmark: Bookmark) diff --git a/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt b/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt index bac6fc8d2..a0933af30 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt @@ -5,12 +5,10 @@ import android.content.Intent import android.os.Bundle import android.view.View import androidx.fragment.app.activityViewModels -import androidx.lifecycle.LiveData import androidx.recyclerview.widget.LinearLayoutManager import io.legado.app.R import io.legado.app.base.VMBaseFragment import io.legado.app.data.appDb -import io.legado.app.data.entities.Book import io.legado.app.data.entities.Bookmark import io.legado.app.databinding.DialogBookmarkBinding import io.legado.app.databinding.FragmentBookmarkBinding @@ -20,6 +18,9 @@ import io.legado.app.lib.theme.ATH import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.utils.requestInputMethod import io.legado.app.utils.viewbindingdelegate.viewBinding +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch class BookmarkFragment : VMBaseFragment(R.layout.fragment_bookmark), @@ -28,13 +29,13 @@ class BookmarkFragment : VMBaseFragment(R.layout.fragment_bookmark override val viewModel by activityViewModels() private val binding by viewBinding(FragmentBookmarkBinding::bind) private lateinit var adapter: BookmarkAdapter - private var bookmarkLiveData: LiveData>? = null + private var bookmarkFlowJob: Job? = null override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { viewModel.bookMarkCallBack = this initRecyclerView() viewModel.bookData.observe(this) { - initData(it) + upBookmark(null) } } @@ -46,20 +47,15 @@ class BookmarkFragment : VMBaseFragment(R.layout.fragment_bookmark binding.recyclerView.adapter = adapter } - private fun initData(book: Book) { - bookmarkLiveData?.removeObservers(viewLifecycleOwner) - bookmarkLiveData = appDb.bookmarkDao.observeByBook(book.name, book.author) - bookmarkLiveData?.observe(viewLifecycleOwner, { adapter.setItems(it) }) - } - - override fun startBookmarkSearch(newText: String?) { - viewModel.bookData.value?.let { book -> - if (newText.isNullOrBlank()) { - initData(book) - } else { - bookmarkLiveData?.removeObservers(viewLifecycleOwner) - bookmarkLiveData = appDb.bookmarkDao.liveDataSearch(book.name, book.author, newText) - bookmarkLiveData?.observe(viewLifecycleOwner, { adapter.setItems(it) }) + override fun upBookmark(searchKey: String?) { + val book = viewModel.bookData.value ?: return + bookmarkFlowJob?.cancel() + bookmarkFlowJob = launch { + when { + searchKey.isNullOrBlank() -> appDb.bookmarkDao.observeByBook(book.name, book.author) + else -> appDb.bookmarkDao.liveDataSearch(book.name, book.author, searchKey) + }.collect { + adapter.setItems(it) } } } diff --git a/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt b/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt index 417b740ca..feef7aa82 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt @@ -6,7 +6,6 @@ import android.content.Intent import android.os.Bundle import android.view.View import androidx.fragment.app.activityViewModels -import androidx.lifecycle.LiveData import io.legado.app.R import io.legado.app.base.VMBaseFragment import io.legado.app.constant.EventBus @@ -24,6 +23,8 @@ import io.legado.app.utils.observeEvent import io.legado.app.utils.viewbindingdelegate.viewBinding import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlin.math.min @@ -36,7 +37,7 @@ class ChapterListFragment : VMBaseFragment(R.layout.fragment_chapt lateinit var adapter: ChapterListAdapter private var durChapterIndex = 0 private lateinit var mLayoutManager: UpLinearLayoutManager - private var tocLiveData: LiveData>? = null + private var tocFlowJob: Job? = null private var scrollToDurChapter = false override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) = binding.run { @@ -77,7 +78,7 @@ class ChapterListFragment : VMBaseFragment(R.layout.fragment_chapt @SuppressLint("SetTextI18n") private fun initBook(book: Book) { launch { - initToc() + upChapterList(null) durChapterIndex = book.durChapterIndex binding.tvCurrentChapterInfo.text = "${book.durChapterTitle}(${book.durChapterIndex + 1}/${book.totalChapterNum})" @@ -85,18 +86,6 @@ class ChapterListFragment : VMBaseFragment(R.layout.fragment_chapt } } - private fun initToc() { - tocLiveData?.removeObservers(this@ChapterListFragment) - tocLiveData = appDb.bookChapterDao.observeByBook(viewModel.bookUrl) - tocLiveData?.observe(viewLifecycleOwner, { - adapter.setItems(it) - if (!scrollToDurChapter) { - mLayoutManager.scrollToPositionWithOffset(durChapterIndex, 0) - scrollToDurChapter = true - } - }) - } - private fun initCacheFileNames(book: Book) { launch(IO) { adapter.cacheFileNames.addAll(BookHelp.getChapterFiles(book)) @@ -117,15 +106,19 @@ class ChapterListFragment : VMBaseFragment(R.layout.fragment_chapt } } - override fun startChapterListSearch(newText: String?) { - if (newText.isNullOrBlank()) { - initToc() - } else { - tocLiveData?.removeObservers(this) - tocLiveData = appDb.bookChapterDao.liveDataSearch(viewModel.bookUrl, newText) - tocLiveData?.observe(viewLifecycleOwner, { + override fun upChapterList(searchKey: String?) { + tocFlowJob?.cancel() + tocFlowJob = launch { + when { + searchKey.isNullOrBlank() -> appDb.bookChapterDao.observeByBook(viewModel.bookUrl) + else -> appDb.bookChapterDao.liveDataSearch(viewModel.bookUrl, searchKey) + }.collect { adapter.setItems(it) - }) + if (searchKey.isNullOrBlank() && !scrollToDurChapter) { + mLayoutManager.scrollToPositionWithOffset(durChapterIndex, 0) + scrollToDurChapter = true + } + } } } diff --git a/app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt b/app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt index 992101490..1fdb66fd6 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt @@ -39,18 +39,18 @@ class TocViewModel(application: Application) : BaseViewModel(application) { } fun startChapterListSearch(newText: String?) { - chapterCallBack?.startChapterListSearch(newText) + chapterCallBack?.upChapterList(newText) } fun startBookmarkSearch(newText: String?) { - bookMarkCallBack?.startBookmarkSearch(newText) + bookMarkCallBack?.upBookmark(newText) } interface ChapterListCallBack { - fun startChapterListSearch(newText: String?) + fun upChapterList(searchKey: String?) } interface BookmarkCallBack { - fun startBookmarkSearch(newText: String?) + fun upBookmark(searchKey: String?) } } \ No newline at end of file From a2f2b8289f94aa19cdc3195ed9f71b194d8e8003 Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 20 Jul 2021 23:20:07 +0800 Subject: [PATCH 32/40] =?UTF-8?q?=E9=87=87=E7=94=A8Flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/data/dao/RssArticleDao.kt | 7 ++++--- .../java/io/legado/app/data/dao/RssStarDao.kt | 4 ++-- .../app/ui/rss/article/RssArticlesFragment.kt | 18 ++++++++++-------- .../ui/rss/favorites/RssFavoritesActivity.kt | 14 +++++++------- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/io/legado/app/data/dao/RssArticleDao.kt b/app/src/main/java/io/legado/app/data/dao/RssArticleDao.kt index c05dcb880..e75a8eba8 100644 --- a/app/src/main/java/io/legado/app/data/dao/RssArticleDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/RssArticleDao.kt @@ -1,9 +1,9 @@ package io.legado.app.data.dao -import androidx.lifecycle.LiveData import androidx.room.* import io.legado.app.data.entities.RssArticle import io.legado.app.data.entities.RssReadRecord +import kotlinx.coroutines.flow.Flow @Dao interface RssArticleDao { @@ -12,12 +12,13 @@ interface RssArticleDao { fun get(origin: String, link: String): RssArticle? @Query( - """select t1.link, t1.sort, t1.origin, t1.`order`, t1.title, t1.content, t1.description, t1.image, t1.pubDate, t1.variable, ifNull(t2.read, 0) as read + """select t1.link, t1.sort, t1.origin, t1.`order`, t1.title, t1.content, + t1.description, t1.image, t1.pubDate, t1.variable, ifNull(t2.read, 0) as read from rssArticles as t1 left join rssReadRecords as t2 on t1.link = t2.record where origin = :origin and sort = :sort order by `order` desc""" ) - fun liveByOriginSort(origin: String, sort: String): LiveData> + fun liveByOriginSort(origin: String, sort: String): Flow> @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(vararg rssArticle: RssArticle) diff --git a/app/src/main/java/io/legado/app/data/dao/RssStarDao.kt b/app/src/main/java/io/legado/app/data/dao/RssStarDao.kt index faaf85b9f..2c40349c0 100644 --- a/app/src/main/java/io/legado/app/data/dao/RssStarDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/RssStarDao.kt @@ -1,8 +1,8 @@ package io.legado.app.data.dao -import androidx.lifecycle.LiveData import androidx.room.* import io.legado.app.data.entities.RssStar +import kotlinx.coroutines.flow.Flow @Dao interface RssStarDao { @@ -14,7 +14,7 @@ interface RssStarDao { fun get(origin: String, link: String): RssStar? @Query("select * from rssStars order by starTime desc") - fun liveAll(): LiveData> + fun liveAll(): Flow> @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(vararg rssStar: RssStar) diff --git a/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesFragment.kt b/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesFragment.kt index da784aa0c..228ac9df9 100644 --- a/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesFragment.kt +++ b/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesFragment.kt @@ -5,7 +5,6 @@ import android.os.Bundle import android.view.View import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels -import androidx.lifecycle.LiveData import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -22,6 +21,9 @@ import io.legado.app.ui.widget.recycler.LoadMoreView import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.utils.startActivity import io.legado.app.utils.viewbindingdelegate.viewBinding +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch class RssArticlesFragment : VMBaseFragment(R.layout.fragment_rss_articles), BaseRssArticlesAdapter.CallBack { @@ -42,7 +44,7 @@ class RssArticlesFragment : VMBaseFragment(R.layout.fragme override val viewModel by viewModels() lateinit var adapter: BaseRssArticlesAdapter<*> private lateinit var loadMoreView: LoadMoreView - private var rssArticlesData: LiveData>? = null + private var articlesFlowJob: Job? = null override val isGridLayout: Boolean get() = activityViewModel.isGridLayout @@ -92,12 +94,12 @@ class RssArticlesFragment : VMBaseFragment(R.layout.fragme } private fun initData() { - activityViewModel.url?.let { - rssArticlesData?.removeObservers(this) - rssArticlesData = appDb.rssArticleDao.liveByOriginSort(it, viewModel.sortName) - rssArticlesData?.observe(viewLifecycleOwner, { list -> - adapter.setItems(list) - }) + val rssUrl = activityViewModel.url ?: return + articlesFlowJob?.cancel() + articlesFlowJob = launch { + appDb.rssArticleDao.liveByOriginSort(rssUrl, viewModel.sortName).collect { + adapter.setItems(it) + } } } diff --git a/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesActivity.kt b/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesActivity.kt index 3f90252b0..74710a7f2 100644 --- a/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesActivity.kt @@ -1,7 +1,6 @@ package io.legado.app.ui.rss.favorites import android.os.Bundle -import androidx.lifecycle.LiveData import androidx.recyclerview.widget.LinearLayoutManager import io.legado.app.base.BaseActivity import io.legado.app.data.appDb @@ -11,13 +10,14 @@ import io.legado.app.ui.rss.read.ReadRssActivity import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.utils.startActivity import io.legado.app.utils.viewbindingdelegate.viewBinding +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch class RssFavoritesActivity : BaseActivity(), RssFavoritesAdapter.CallBack { override val binding by viewBinding(ActivityRssFavoritesBinding::inflate) - private var liveData: LiveData>? = null private lateinit var adapter: RssFavoritesAdapter override fun onActivityCreated(savedInstanceState: Bundle?) { @@ -35,11 +35,11 @@ class RssFavoritesActivity : BaseActivity(), } private fun initData() { - liveData?.removeObservers(this) - liveData = appDb.rssStarDao.liveAll() - liveData?.observe(this, { - adapter.setItems(it) - }) + launch { + appDb.rssStarDao.liveAll().collect { + adapter.setItems(it) + } + } } override fun readRss(rssStar: RssStar) { From df077da03f03b6c09c568c95034cd14809790616 Mon Sep 17 00:00:00 2001 From: gedoor Date: Wed, 21 Jul 2021 09:09:01 +0800 Subject: [PATCH 33/40] =?UTF-8?q?=E9=87=87=E7=94=A8Flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/base/BaseActivity.kt | 11 +------ .../io/legado/app/base/BaseDialogFragment.kt | 20 ++---------- .../java/io/legado/app/base/BaseFragment.kt | 31 ++++--------------- .../java/io/legado/app/base/BaseViewModel.kt | 13 ++++---- .../io/legado/app/data/dao/BookGroupDao.kt | 11 +++---- .../legado/app/ui/about/ReadRecordActivity.kt | 5 +-- .../ui/book/arrange/ArrangeBookActivity.kt | 23 +++++++------- .../legado/app/ui/book/cache/CacheActivity.kt | 25 +++++++-------- .../book/changecover/ChangeCoverViewModel.kt | 3 +- .../book/changesource/ChangeSourceDialog.kt | 3 +- .../changesource/ChangeSourceViewModel.kt | 7 +++-- .../ui/book/explore/ExploreShowViewModel.kt | 3 +- .../app/ui/book/group/GroupManageDialog.kt | 11 +++++-- .../app/ui/book/group/GroupSelectDialog.kt | 11 +++++-- .../app/ui/book/info/BookInfoActivity.kt | 3 +- .../app/ui/book/local/ImportBookActivity.kt | 11 ++++--- .../app/ui/book/read/ReadBookActivity.kt | 15 ++++----- .../app/ui/book/read/ReadBookViewModel.kt | 5 +-- .../app/ui/book/read/config/TocRegexDialog.kt | 7 +++-- .../app/ui/book/search/SearchActivity.kt | 7 +++-- .../app/ui/book/search/SearchViewModel.kt | 5 +-- .../searchContent/SearchContentActivity.kt | 5 +-- .../source/debug/BookSourceDebugActivity.kt | 3 +- .../book/source/manage/BookSourceActivity.kt | 5 +-- .../book/source/manage/GroupManageDialog.kt | 3 +- .../app/ui/book/toc/BookmarkFragment.kt | 3 +- .../app/ui/book/toc/ChapterListFragment.kt | 7 +++-- .../bookshelf/style1/BookshelfFragment1.kt | 10 +++--- .../bookshelf/style1/books/BooksFragment.kt | 3 +- .../bookshelf/style2/BookshelfFragment2.kt | 8 ++--- .../app/ui/main/explore/ExploreFragment.kt | 7 +++-- .../io/legado/app/ui/main/rss/RssFragment.kt | 5 +-- .../app/ui/rss/article/RssArticlesFragment.kt | 3 +- .../ui/rss/article/RssArticlesViewModel.kt | 5 +-- .../ui/rss/favorites/RssFavoritesActivity.kt | 3 +- .../legado/app/ui/rss/read/ReadRssActivity.kt | 3 +- .../app/ui/rss/read/ReadRssViewModel.kt | 8 ++--- .../source/debug/RssSourceDebugActivity.kt | 3 +- .../rss/source/debug/RssSourceDebugModel.kt | 3 +- .../ui/rss/source/manage/GroupManageDialog.kt | 3 +- .../ui/rss/source/manage/RssSourceActivity.kt | 5 +-- .../ui/rss/subscription/RuleSubActivity.kt | 9 +++--- .../legado/app/ui/widget/dialog/TextDialog.kt | 3 +- .../app/ui/widget/font/FontSelectDialog.kt | 3 +- 44 files changed, 168 insertions(+), 172 deletions(-) diff --git a/app/src/main/java/io/legado/app/base/BaseActivity.kt b/app/src/main/java/io/legado/app/base/BaseActivity.kt index 6406c18bf..bdfa48514 100644 --- a/app/src/main/java/io/legado/app/base/BaseActivity.kt +++ b/app/src/main/java/io/legado/app/base/BaseActivity.kt @@ -24,9 +24,6 @@ import io.legado.app.lib.theme.backgroundColor import io.legado.app.lib.theme.primaryColor import io.legado.app.ui.widget.TitleBar import io.legado.app.utils.* -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.MainScope -import kotlinx.coroutines.cancel abstract class BaseActivity( @@ -35,8 +32,7 @@ abstract class BaseActivity( private val toolBarTheme: Theme = Theme.Auto, private val transparent: Boolean = false, private val imageBg: Boolean = true -) : AppCompatActivity(), - CoroutineScope by MainScope() { +) : AppCompatActivity() { protected abstract val binding: VB @@ -120,11 +116,6 @@ abstract class BaseActivity( setupSystemBar() } - override fun onDestroy() { - super.onDestroy() - cancel() - } - abstract fun onActivityCreated(savedInstanceState: Bundle?) final override fun onCreateOptionsMenu(menu: Menu?): Boolean { diff --git a/app/src/main/java/io/legado/app/base/BaseDialogFragment.kt b/app/src/main/java/io/legado/app/base/BaseDialogFragment.kt index 468dd4c95..ce7f3514f 100644 --- a/app/src/main/java/io/legado/app/base/BaseDialogFragment.kt +++ b/app/src/main/java/io/legado/app/base/BaseDialogFragment.kt @@ -4,23 +4,14 @@ import android.os.Bundle import android.view.View import androidx.fragment.app.DialogFragment import androidx.fragment.app.FragmentManager +import androidx.lifecycle.lifecycleScope import io.legado.app.help.coroutine.Coroutine import io.legado.app.lib.theme.ThemeStore import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job import kotlin.coroutines.CoroutineContext - -abstract class BaseDialogFragment : DialogFragment(), CoroutineScope { - override val coroutineContext: CoroutineContext - get() = job + Dispatchers.Main - private lateinit var job: Job - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - job = Job() - } +abstract class BaseDialogFragment : DialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -39,13 +30,8 @@ abstract class BaseDialogFragment : DialogFragment(), CoroutineScope { } } - override fun onDestroy() { - super.onDestroy() - job.cancel() - } - fun execute( - scope: CoroutineScope = this, + scope: CoroutineScope = lifecycleScope, context: CoroutineContext = Dispatchers.IO, block: suspend CoroutineScope.() -> T ) = Coroutine.async(scope, context) { block() } diff --git a/app/src/main/java/io/legado/app/base/BaseFragment.kt b/app/src/main/java/io/legado/app/base/BaseFragment.kt index 3b3f3dd28..fba8ef6b2 100644 --- a/app/src/main/java/io/legado/app/base/BaseFragment.kt +++ b/app/src/main/java/io/legado/app/base/BaseFragment.kt @@ -3,22 +3,20 @@ package io.legado.app.base import android.annotation.SuppressLint import android.content.res.Configuration import android.os.Bundle -import android.view.* +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View import androidx.appcompat.view.SupportMenuInflater import androidx.appcompat.widget.Toolbar import androidx.fragment.app.Fragment import io.legado.app.R import io.legado.app.ui.widget.TitleBar import io.legado.app.utils.applyTint -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlin.coroutines.CoroutineContext @Suppress("MemberVisibilityCanBePrivate") -abstract class BaseFragment(layoutID: Int) : Fragment(layoutID), - CoroutineScope { - lateinit var job: Job +abstract class BaseFragment(layoutID: Int) : Fragment(layoutID) { + var supportToolbar: Toolbar? = null private set @@ -26,18 +24,6 @@ abstract class BaseFragment(layoutID: Int) : Fragment(layoutID), @SuppressLint("RestrictedApi") get() = SupportMenuInflater(requireContext()) - override val coroutineContext: CoroutineContext - get() = job + Dispatchers.Main - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - job = Job() - return super.onCreateView(inflater, container, savedInstanceState) - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) onMultiWindowModeChanged() @@ -64,11 +50,6 @@ abstract class BaseFragment(layoutID: Int) : Fragment(layoutID), } } - override fun onDestroy() { - super.onDestroy() - job.cancel() - } - fun setSupportToolbar(toolbar: Toolbar) { supportToolbar = toolbar supportToolbar?.let { diff --git a/app/src/main/java/io/legado/app/base/BaseViewModel.kt b/app/src/main/java/io/legado/app/base/BaseViewModel.kt index 88ddc4b5e..6c7a8c782 100644 --- a/app/src/main/java/io/legado/app/base/BaseViewModel.kt +++ b/app/src/main/java/io/legado/app/base/BaseViewModel.kt @@ -4,20 +4,22 @@ import android.app.Application import android.content.Context import androidx.annotation.CallSuper import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.viewModelScope import io.legado.app.App import io.legado.app.help.coroutine.Coroutine import io.legado.app.utils.toastOnUi -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers import kotlin.coroutines.CoroutineContext @Suppress("unused") -open class BaseViewModel(application: Application) : AndroidViewModel(application), - CoroutineScope by MainScope() { +open class BaseViewModel(application: Application) : AndroidViewModel(application) { val context: Context by lazy { this.getApplication() } fun execute( - scope: CoroutineScope = this, + scope: CoroutineScope = viewModelScope, context: CoroutineContext = Dispatchers.IO, block: suspend CoroutineScope.() -> T ): Coroutine { @@ -25,7 +27,7 @@ open class BaseViewModel(application: Application) : AndroidViewModel(applicatio } fun submit( - scope: CoroutineScope = this, + scope: CoroutineScope = viewModelScope, context: CoroutineContext = Dispatchers.IO, block: suspend CoroutineScope.() -> Deferred ): Coroutine { @@ -35,7 +37,6 @@ open class BaseViewModel(application: Application) : AndroidViewModel(applicatio @CallSuper override fun onCleared() { super.onCleared() - cancel() } open fun toastOnUi(message: Int) { diff --git a/app/src/main/java/io/legado/app/data/dao/BookGroupDao.kt b/app/src/main/java/io/legado/app/data/dao/BookGroupDao.kt index 34e6a3e88..51d73542e 100644 --- a/app/src/main/java/io/legado/app/data/dao/BookGroupDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/BookGroupDao.kt @@ -1,9 +1,9 @@ package io.legado.app.data.dao -import androidx.lifecycle.LiveData import androidx.room.* import io.legado.app.constant.BookType import io.legado.app.data.entities.BookGroup +import kotlinx.coroutines.flow.Flow @Dao interface BookGroupDao { @@ -15,7 +15,7 @@ interface BookGroupDao { fun getByName(groupName: String): BookGroup? @Query("SELECT * FROM book_groups ORDER BY `order`") - fun liveDataAll(): LiveData> + fun liveDataAll(): Flow> @Query( """ @@ -24,13 +24,12 @@ interface BookGroupDao { or (groupId = -3 and show > 0 and (select count(bookUrl) from books where type = ${BookType.audio}) > 0) or (groupId = -2 and show > 0 and (select count(bookUrl) from books where origin = '${BookType.local}') > 0) or (groupId = -1 and show > 0) - ORDER BY `order` - """ + ORDER BY `order`""" ) - fun liveDataShow(): LiveData> + fun liveDataShow(): Flow> @Query("SELECT * FROM book_groups where groupId >= 0 ORDER BY `order`") - fun liveDataSelect(): LiveData> + fun liveDataSelect(): Flow> @get:Query("SELECT sum(groupId) FROM book_groups where groupId >= 0") val idsSum: Long diff --git a/app/src/main/java/io/legado/app/ui/about/ReadRecordActivity.kt b/app/src/main/java/io/legado/app/ui/about/ReadRecordActivity.kt index 8f49e6cd7..4a68f7f43 100644 --- a/app/src/main/java/io/legado/app/ui/about/ReadRecordActivity.kt +++ b/app/src/main/java/io/legado/app/ui/about/ReadRecordActivity.kt @@ -5,6 +5,7 @@ import android.os.Bundle import android.view.Menu import android.view.MenuItem import android.view.ViewGroup +import androidx.lifecycle.lifecycleScope import io.legado.app.R import io.legado.app.base.BaseActivity import io.legado.app.base.adapter.ItemViewHolder @@ -72,7 +73,7 @@ class ReadRecordActivity : BaseActivity() { } private fun initData() { - launch(IO) { + lifecycleScope.launch(IO) { val allTime = appDb.readRecordDao.allTime withContext(Main) { binding.readRecord.tvReadTime.text = formatDuring(allTime) @@ -115,7 +116,7 @@ class ReadRecordActivity : BaseActivity() { binding.apply { root.setOnClickListener { val item = getItem(holder.layoutPosition) ?: return@setOnClickListener - launch { + lifecycleScope.launch { val book = withContext(IO) { appDb.bookDao.findByName(item.bookName).firstOrNull() } diff --git a/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt index 2495f36f0..68c5a7467 100644 --- a/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt @@ -5,7 +5,7 @@ import android.view.Menu import android.view.MenuItem import androidx.activity.viewModels import androidx.appcompat.widget.PopupMenu -import androidx.lifecycle.LiveData +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import io.legado.app.R @@ -46,14 +46,13 @@ class ArrangeBookActivity : VMBaseActivity>? = null private var booksFlowJob: Job? = null private var menu: Menu? = null private var groupId: Long = -1 override fun onActivityCreated(savedInstanceState: Bundle?) { groupId = intent.getLongExtra("groupId", -1) - launch { + lifecycleScope.launch { binding.titleBar.subtitle = withContext(IO) { appDb.bookGroupDao.getByID(groupId)?.groupName ?: getString(R.string.no_group) @@ -109,19 +108,19 @@ class ArrangeBookActivity : VMBaseActivity appDb.bookDao.observeAll() AppConst.bookGroupLocalId -> appDb.bookDao.observeLocal() diff --git a/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt b/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt index 8d24c807b..313ba092b 100644 --- a/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt @@ -4,7 +4,7 @@ import android.os.Bundle import android.view.Menu import android.view.MenuItem import androidx.activity.viewModels -import androidx.lifecycle.LiveData +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.snackbar.Snackbar import io.legado.app.R @@ -46,7 +46,6 @@ class CacheActivity : VMBaseActivity() private val exportBookPathKey = "exportBookPath" lateinit var adapter: CacheAdapter - private var groupLiveData: LiveData>? = null private var booksFlowJob: Job? = null private var menu: Menu? = null private var exportPosition = -1 @@ -68,7 +67,7 @@ class CacheActivity : VMBaseActivity() override fun onActivityCreated(savedInstanceState: Bundle?) { groupId = intent.getLongExtra("groupId", -1) - launch { + lifecycleScope.launch { binding.titleBar.subtitle = withContext(IO) { appDb.bookGroupDao.getByID(groupId)?.groupName ?: getString(R.string.no_group) @@ -150,7 +149,7 @@ class CacheActivity : VMBaseActivity() private fun initBookData() { booksFlowJob?.cancel() - booksFlowJob = launch { + booksFlowJob = lifecycleScope.launch { when (groupId) { AppConst.bookGroupAllId -> appDb.bookDao.observeAll() AppConst.bookGroupLocalId -> appDb.bookDao.observeLocal() @@ -176,18 +175,18 @@ class CacheActivity : VMBaseActivity() } private fun initGroupData() { - groupLiveData?.removeObservers(this) - groupLiveData = appDb.bookGroupDao.liveDataAll() - groupLiveData?.observe(this, { - groupList.clear() - groupList.addAll(it) - adapter.notifyDataSetChanged() - upMenu() - }) + lifecycleScope.launch { + appDb.bookGroupDao.liveDataAll().collect { + groupList.clear() + groupList.addAll(it) + adapter.notifyDataSetChanged() + upMenu() + } + } } private fun initCacheSize(books: List) { - launch(IO) { + lifecycleScope.launch(IO) { books.forEach { book -> val chapterCaches = hashSetOf() val cacheNames = BookHelp.getChapterFiles(book) diff --git a/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt b/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt index 13488567b..cdd67fb02 100644 --- a/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt @@ -5,6 +5,7 @@ import android.os.Bundle import android.os.Handler import android.os.Looper import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope import io.legado.app.base.BaseViewModel import io.legado.app.constant.AppPattern import io.legado.app.data.appDb @@ -102,7 +103,7 @@ class ChangeCoverViewModel(application: Application) : BaseViewModel(application return } val task = WebBook(source) - .searchBook(this, name, context = searchPool!!) + .searchBook(viewModelScope, name, context = searchPool!!) .timeout(60000L) .onSuccess(Dispatchers.IO) { if (it.isNotEmpty()) { diff --git a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt index 969217fd8..53cf693d9 100644 --- a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt @@ -6,6 +6,7 @@ import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.Toolbar import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import io.legado.app.R @@ -150,7 +151,7 @@ class ChangeSourceDialog : BaseDialogFragment(), viewModel.searchBooksLiveData.observe(viewLifecycleOwner, { adapter.setItems(it) }) - launch { + lifecycleScope.launch { appDb.bookSourceDao.liveGroupEnabled().collect { groups.clear() it.map { group -> diff --git a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt index 6197725bd..7fa050b36 100644 --- a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt @@ -5,6 +5,7 @@ import android.os.Bundle import android.os.Handler import android.os.Looper import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope import io.legado.app.base.BaseViewModel import io.legado.app.constant.AppPattern import io.legado.app.constant.PreferKey @@ -129,7 +130,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio val source = bookSourceList[searchIndex] val webBook = WebBook(source) val task = webBook - .searchBook(this, name, context = searchPool!!) + .searchBook(viewModelScope, name, context = searchPool!!) .timeout(60000L) .onSuccess(IO) { it.forEach { searchBook -> @@ -168,7 +169,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio } private fun loadBookInfo(webBook: WebBook, book: Book) { - webBook.getBookInfo(this, book) + webBook.getBookInfo(viewModelScope, book) .onSuccess { if (context.getPrefBoolean(PreferKey.changeSourceLoadToc)) { loadBookToc(webBook, book) @@ -184,7 +185,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio } private fun loadBookToc(webBook: WebBook, book: Book) { - webBook.getChapterList(this, book) + webBook.getChapterList(viewModelScope, book) .onSuccess(IO) { chapters -> if (chapters.isNotEmpty()) { book.latestChapterTitle = chapters.last().title diff --git a/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt index 9fedbd613..9bdd8cffa 100644 --- a/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt @@ -3,6 +3,7 @@ package io.legado.app.ui.book.explore import android.app.Application import android.content.Intent import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope import io.legado.app.base.BaseViewModel import io.legado.app.data.appDb import io.legado.app.data.entities.BookSource @@ -34,7 +35,7 @@ class ExploreShowViewModel(application: Application) : BaseViewModel(application val source = bookSource val url = exploreUrl if (source != null && url != null) { - WebBook(source).exploreBook(this, url, page) + WebBook(source).exploreBook(viewModelScope, url, page) .timeout(30000L) .onSuccess(IO) { searchBooks -> booksData.postValue(searchBooks) diff --git a/app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt b/app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt index 33a3d55ca..e7a6a6989 100644 --- a/app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt @@ -9,6 +9,7 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.widget.Toolbar import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -32,6 +33,8 @@ import io.legado.app.utils.getSize import io.legado.app.utils.requestInputMethod import io.legado.app.utils.viewbindingdelegate.viewBinding import io.legado.app.utils.visible +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener { @@ -77,9 +80,11 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener } private fun initData() { - appDb.bookGroupDao.liveDataAll().observe(viewLifecycleOwner, { - adapter.setItems(it) - }) + lifecycleScope.launch { + appDb.bookGroupDao.liveDataAll().collect { + adapter.setItems(it) + } + } } private fun initMenu() { diff --git a/app/src/main/java/io/legado/app/ui/book/group/GroupSelectDialog.kt b/app/src/main/java/io/legado/app/ui/book/group/GroupSelectDialog.kt index fdb1cfd11..a4b0e33b1 100644 --- a/app/src/main/java/io/legado/app/ui/book/group/GroupSelectDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/group/GroupSelectDialog.kt @@ -10,6 +10,7 @@ import android.view.ViewGroup import androidx.appcompat.widget.Toolbar import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -32,6 +33,8 @@ import io.legado.app.utils.applyTint import io.legado.app.utils.getSize import io.legado.app.utils.requestInputMethod import io.legado.app.utils.viewbindingdelegate.viewBinding +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch class GroupSelectDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener { @@ -105,9 +108,11 @@ class GroupSelectDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener } private fun initData() { - appDb.bookGroupDao.liveDataSelect().observe(viewLifecycleOwner, { - adapter.setItems(it) - }) + lifecycleScope.launch { + appDb.bookGroupDao.liveDataSelect().collect { + adapter.setItems(it) + } + } } override fun onMenuItemClick(item: MenuItem?): Boolean { diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt index cfdf84d02..5497fe12f 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt @@ -10,6 +10,7 @@ import android.widget.CheckBox import android.widget.LinearLayout import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels +import androidx.lifecycle.lifecycleScope import com.bumptech.glide.RequestBuilder import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions import com.bumptech.glide.request.RequestOptions.bitmapTransform @@ -53,7 +54,7 @@ class BookInfoActivity : private val tocActivityResult = registerForActivityResult(TocActivityResult()) { it?.let { viewModel.bookData.value?.let { book -> - launch { + lifecycleScope.launch { withContext(IO) { viewModel.durChapterIndex = it.first book.durChapterIndex = it.first diff --git a/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt index 02388239e..4b18d3b1e 100644 --- a/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt @@ -9,6 +9,7 @@ import android.view.MenuItem import androidx.activity.viewModels import androidx.appcompat.widget.PopupMenu import androidx.documentfile.provider.DocumentFile +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import io.legado.app.R import io.legado.app.base.VMBaseActivity @@ -124,7 +125,7 @@ class ImportBookActivity : VMBaseActivity Unit = { - launch { + lifecycleScope.launch { adapter.addItem(it) } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index ce1f7e710..354d665e9 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -13,6 +13,7 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.core.view.get import androidx.core.view.isVisible import androidx.core.view.size +import androidx.lifecycle.lifecycleScope import com.jaredrummler.android.colorpicker.ColorPickerDialogListener import io.legado.app.BuildConfig import io.legado.app.R @@ -108,7 +109,7 @@ class ReadBookActivity : ReadBookBaseActivity(), TextActionMenu(this, this) } - override val scope: CoroutineScope get() = this + override val scope: CoroutineScope get() = lifecycleScope override val isInitFinish: Boolean get() = viewModel.isInitFinish override val isScroll: Boolean get() = binding.readView.isScroll private val mHandler = Handler(Looper.getMainLooper()) @@ -591,7 +592,7 @@ class ReadBookActivity : ReadBookBaseActivity(), resetPageOffset: Boolean, success: (() -> Unit)? ) { - launch { + lifecycleScope.launch { autoPageProgress = 0 binding.readView.upContent(relativePosition, resetPageOffset) binding.readMenu.setSeekPage(ReadBook.durPageIndex()) @@ -604,13 +605,13 @@ class ReadBookActivity : ReadBookBaseActivity(), * 更新视图 */ override fun upView() { - launch { + lifecycleScope.launch { binding.readMenu.upBookView() } } override fun upPageAnim() { - launch { + lifecycleScope.launch { binding.readView.upPageAnim() } } @@ -620,7 +621,7 @@ class ReadBookActivity : ReadBookBaseActivity(), */ override fun pageChanged() { autoPageProgress = 0 - launch { + lifecycleScope.launch { binding.readMenu.setSeekPage(ReadBook.durPageIndex()) } mHandler.postDelayed(backupRunnable, 600000) @@ -846,7 +847,7 @@ class ReadBookActivity : ReadBookBaseActivity(), viewModel.searchContentQuery ) ReadBook.skipToPage(positions[0]) { - launch { + lifecycleScope.launch { binding.readView.curPage.selectStartMoveIndex(0, positions[1], positions[2]) delay(20L) when (positions[3]) { @@ -936,7 +937,7 @@ class ReadBookActivity : ReadBookBaseActivity(), } } observeEventSticky(EventBus.TTS_PROGRESS) { chapterStart -> - launch(IO) { + lifecycleScope.launch(IO) { if (BaseReadAloudService.isPlay()) { ReadBook.curTextChapter?.let { textChapter -> val pageStart = chapterStart - ReadBook.durChapterPos diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt index 6fe2feaec..92aeb6ca2 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt @@ -2,6 +2,7 @@ package io.legado.app.ui.book.read import android.app.Application import android.content.Intent +import androidx.lifecycle.viewModelScope import io.legado.app.R import io.legado.app.base.BaseViewModel import io.legado.app.data.appDb @@ -104,7 +105,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) { if (book.isLocalBook()) { loadChapterList(book, changeDruChapterIndex) } else { - ReadBook.webBook?.getBookInfo(this, book, canReName = false) + ReadBook.webBook?.getBookInfo(viewModelScope, book, canReName = false) ?.onSuccess { loadChapterList(book, changeDruChapterIndex) } @@ -133,7 +134,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) { ReadBook.upMsg("LoadTocError:${it.localizedMessage}") } } else { - ReadBook.webBook?.getChapterList(this, book) + ReadBook.webBook?.getChapterList(viewModelScope, book) ?.onSuccess(IO) { cList -> if (cList.isNotEmpty()) { if (changeDruChapterIndex == null) { diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/TocRegexDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/TocRegexDialog.kt index 136ae587a..54755d1a5 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/TocRegexDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/TocRegexDialog.kt @@ -11,6 +11,7 @@ import androidx.appcompat.widget.Toolbar import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import androidx.lifecycle.LiveData +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView import com.google.android.material.snackbar.Snackbar @@ -222,7 +223,7 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener { if (buttonView.isPressed) { getItem(holder.layoutPosition)?.let { it.enable = isChecked - launch(IO) { + lifecycleScope.launch(IO) { appDb.txtTocRuleDao.update(it) } } @@ -233,7 +234,7 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener { } ivDelete.setOnClickListener { getItem(holder.layoutPosition)?.let { item -> - launch(IO) { + lifecycleScope.launch(IO) { appDb.txtTocRuleDao.delete(item) } } @@ -255,7 +256,7 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener { for ((index, item) in getItems().withIndex()) { item.serialNumber = index + 1 } - launch(IO) { + lifecycleScope.launch(IO) { appDb.txtTocRuleDao.update(*getItems().toTypedArray()) } } diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt index 99ad24340..afed9c5ab 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt @@ -11,6 +11,7 @@ import android.widget.TextView import androidx.activity.viewModels import androidx.appcompat.widget.SearchView import androidx.lifecycle.LiveData +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.google.android.flexbox.FlexboxLayoutManager @@ -192,7 +193,7 @@ class SearchActivity : VMBaseActivity @@ -273,7 +274,7 @@ class SearchActivity : VMBaseActivity { searchView.setQuery(key, true) diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt index 6a88d649d..9b2f9999e 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt @@ -4,6 +4,7 @@ import android.app.Application import android.os.Handler import android.os.Looper import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope import io.legado.app.base.BaseViewModel import io.legado.app.constant.PreferKey import io.legado.app.data.appDb @@ -17,7 +18,7 @@ import kotlinx.coroutines.isActive class SearchViewModel(application: Application) : BaseViewModel(application), SearchBookModel.CallBack { val handler = Handler(Looper.getMainLooper()) - private val searchBookModel = SearchBookModel(this, this) + private val searchBookModel = SearchBookModel(viewModelScope, this) var isSearchLiveData = MutableLiveData() var searchBookLiveData = MutableLiveData>() var searchKey: String = "" @@ -64,7 +65,7 @@ class SearchViewModel(application: Application) : BaseViewModel(application), override fun onSearchSuccess(searchBooks: ArrayList) { val precision = context.getPrefBoolean(PreferKey.precisionSearch) appDb.searchBookDao.insert(*searchBooks.toTypedArray()) - mergeItems(this, searchBooks, precision) + mergeItems(viewModelScope, searchBooks, precision) } override fun onSearchFinish() { diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentActivity.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentActivity.kt index 77382d7c5..1624fd618 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentActivity.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.os.Bundle import androidx.activity.viewModels import androidx.appcompat.widget.SearchView +import androidx.lifecycle.lifecycleScope import com.github.liuyueyi.quick.transfer.ChineseUtils import io.legado.app.R import io.legado.app.base.VMBaseActivity @@ -115,7 +116,7 @@ class SearchContentActivity : } private fun initCacheFileNames(book: Book) { - launch(Dispatchers.IO) { + lifecycleScope.launch(Dispatchers.IO) { adapter.cacheFileNames.addAll(BookHelp.getChapterFiles(book)) withContext(Dispatchers.Main) { adapter.notifyItemRangeChanged(0, adapter.itemCount, true) @@ -144,7 +145,7 @@ class SearchContentActivity : searchResultCounts = 0 viewModel.lastQuery = newText var searchResults = listOf() - launch(Dispatchers.Main) { + lifecycleScope.launch(Dispatchers.Main) { appDb.bookChapterDao.getChapterList(viewModel.bookUrl).map { chapter -> withContext(Dispatchers.IO) { if (isLocalBook diff --git a/app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugActivity.kt index 43c2ba1ae..a300935cf 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugActivity.kt @@ -5,6 +5,7 @@ import android.view.Menu import android.view.MenuItem import androidx.activity.viewModels import androidx.appcompat.widget.SearchView +import androidx.lifecycle.lifecycleScope import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.databinding.ActivitySourceDebugBinding @@ -36,7 +37,7 @@ class BookSourceDebugActivity : VMBaseActivity - launch { + lifecycleScope.launch { adapter.addItem(msg) if (state == -1 || state == 1000) { binding.rotateLoading.hide() diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt index 60e91cb94..8f8f6fc41 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt @@ -10,6 +10,7 @@ import androidx.activity.viewModels import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.SearchView import androidx.documentfile.provider.DocumentFile +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.ItemTouchHelper import com.google.android.material.snackbar.Snackbar import io.legado.app.R @@ -203,7 +204,7 @@ class BookSourceActivity : VMBaseActivity { appDb.bookSourceDao.liveDataAll() @@ -275,7 +276,7 @@ class BookSourceActivity : VMBaseActivity() it.map { group -> diff --git a/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt b/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt index a0933af30..2f81b0a04 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.os.Bundle import android.view.View import androidx.fragment.app.activityViewModels +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import io.legado.app.R import io.legado.app.base.VMBaseFragment @@ -50,7 +51,7 @@ class BookmarkFragment : VMBaseFragment(R.layout.fragment_bookmark override fun upBookmark(searchKey: String?) { val book = viewModel.bookData.value ?: return bookmarkFlowJob?.cancel() - bookmarkFlowJob = launch { + bookmarkFlowJob = lifecycleScope.launch { when { searchKey.isNullOrBlank() -> appDb.bookmarkDao.observeByBook(book.name, book.author) else -> appDb.bookmarkDao.liveDataSearch(book.name, book.author, searchKey) diff --git a/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt b/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt index feef7aa82..6143cf456 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt @@ -6,6 +6,7 @@ import android.content.Intent import android.os.Bundle import android.view.View import androidx.fragment.app.activityViewModels +import androidx.lifecycle.lifecycleScope import io.legado.app.R import io.legado.app.base.VMBaseFragment import io.legado.app.constant.EventBus @@ -77,7 +78,7 @@ class ChapterListFragment : VMBaseFragment(R.layout.fragment_chapt @SuppressLint("SetTextI18n") private fun initBook(book: Book) { - launch { + lifecycleScope.launch { upChapterList(null) durChapterIndex = book.durChapterIndex binding.tvCurrentChapterInfo.text = @@ -87,7 +88,7 @@ class ChapterListFragment : VMBaseFragment(R.layout.fragment_chapt } private fun initCacheFileNames(book: Book) { - launch(IO) { + lifecycleScope.launch(IO) { adapter.cacheFileNames.addAll(BookHelp.getChapterFiles(book)) withContext(Main) { adapter.notifyItemRangeChanged(0, adapter.itemCount, true) @@ -108,7 +109,7 @@ class ChapterListFragment : VMBaseFragment(R.layout.fragment_chapt override fun upChapterList(searchKey: String?) { tocFlowJob?.cancel() - tocFlowJob = launch { + tocFlowJob = lifecycleScope.launch { when { searchKey.isNullOrBlank() -> appDb.bookChapterDao.observeByBook(viewModel.bookUrl) else -> appDb.bookChapterDao.liveDataSearch(viewModel.bookUrl, searchKey) diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/BookshelfFragment1.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/BookshelfFragment1.kt index 4f6ce4978..ea76c3efc 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/BookshelfFragment1.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/BookshelfFragment1.kt @@ -10,7 +10,7 @@ import androidx.appcompat.widget.SearchView import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentStatePagerAdapter -import androidx.lifecycle.LiveData +import androidx.lifecycle.lifecycleScope import com.google.android.material.tabs.TabLayout import io.legado.app.R import io.legado.app.constant.AppConst @@ -28,6 +28,8 @@ import io.legado.app.utils.getPrefInt import io.legado.app.utils.putPrefInt import io.legado.app.utils.toastOnUi import io.legado.app.utils.viewbindingdelegate.viewBinding +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch /** * 书架界面 @@ -39,7 +41,6 @@ class BookshelfFragment1 : BaseBookshelfFragment(R.layout.fragment_bookshelf), private val binding by viewBinding(FragmentBookshelfBinding::bind) private lateinit var adapter: FragmentStatePagerAdapter private lateinit var tabLayout: TabLayout - private var bookGroupLiveData: LiveData>? = null private val bookGroups = mutableListOf() private val fragmentMap = hashMapOf() @@ -77,9 +78,8 @@ class BookshelfFragment1 : BaseBookshelfFragment(R.layout.fragment_bookshelf), } private fun initBookGroupData() { - bookGroupLiveData?.removeObservers(viewLifecycleOwner) - bookGroupLiveData = appDb.bookGroupDao.liveDataShow().apply { - observe(viewLifecycleOwner) { + lifecycleScope.launch { + appDb.bookGroupDao.liveDataShow().collect { viewModel.checkGroup(it) upGroup(it) } diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt index 70ae87acc..8fd8b4199 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.View import androidx.core.view.isGone import androidx.fragment.app.activityViewModels +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -104,7 +105,7 @@ class BooksFragment : BaseFragment(R.layout.fragment_books), private fun upRecyclerData() { booksFlowJob?.cancel() - booksFlowJob = launch { + booksFlowJob = lifecycleScope.launch { when (groupId) { AppConst.bookGroupAllId -> appDb.bookDao.observeAll() AppConst.bookGroupLocalId -> appDb.bookDao.observeLocal() diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt index d3627eb23..5b8953317 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt @@ -6,6 +6,7 @@ import android.view.View import androidx.appcompat.widget.SearchView import androidx.core.view.isGone import androidx.lifecycle.LiveData +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -121,9 +122,8 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1), } private fun initGroupData() { - bookGroupLiveData?.removeObservers(this) - bookGroupLiveData = appDb.bookGroupDao.liveDataShow().apply { - observe(viewLifecycleOwner) { + lifecycleScope.launch { + appDb.bookGroupDao.liveDataShow().collect { if (it != bookGroups) { bookGroups = it booksAdapter.notifyDataSetChanged() @@ -134,7 +134,7 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1), private fun initBooksData() { booksFlowJob?.cancel() - booksFlowJob = launch { + booksFlowJob = lifecycleScope.launch { when (groupId) { AppConst.bookGroupAllId -> appDb.bookDao.observeAll() AppConst.bookGroupLocalId -> appDb.bookDao.observeLocal() diff --git a/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt b/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt index 606616dab..630627b39 100644 --- a/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt @@ -8,6 +8,7 @@ import android.view.View import androidx.appcompat.widget.SearchView import androidx.core.view.isGone import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -87,7 +88,7 @@ class ExploreFragment : VMBaseFragment(R.layout.fragment_explo ATH.applyEdgeEffectColor(binding.rvFind) linearLayoutManager = LinearLayoutManager(context) binding.rvFind.layoutManager = linearLayoutManager - adapter = ExploreAdapter(requireContext(), this, this) + adapter = ExploreAdapter(requireContext(), lifecycleScope, this) binding.rvFind.adapter = adapter adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { @@ -101,7 +102,7 @@ class ExploreFragment : VMBaseFragment(R.layout.fragment_explo } private fun initGroupData() { - launch { + lifecycleScope.launch { appDb.bookSourceDao.liveExploreGroup() .collect { groups.clear() @@ -115,7 +116,7 @@ class ExploreFragment : VMBaseFragment(R.layout.fragment_explo private fun upExploreData(searchKey: String? = null) { exploreFlowJob?.cancel() - exploreFlowJob = launch { + exploreFlowJob = lifecycleScope.launch { val exploreFlow = when { searchKey.isNullOrBlank() -> { appDb.bookSourceDao.liveExplore() diff --git a/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt b/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt index 0d20d7252..5cfad3b31 100644 --- a/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt @@ -7,6 +7,7 @@ import android.view.SubMenu import android.view.View import androidx.appcompat.widget.SearchView import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import io.legado.app.R import io.legado.app.base.VMBaseFragment import io.legado.app.constant.AppPattern @@ -120,7 +121,7 @@ class RssFragment : VMBaseFragment(R.layout.fragment_rss), } private fun initGroupData() { - launch { + lifecycleScope.launch { appDb.rssSourceDao.liveGroup().collect { groups.clear() it.map { group -> @@ -133,7 +134,7 @@ class RssFragment : VMBaseFragment(R.layout.fragment_rss), private fun upRssFlowJob(searchKey: String? = null) { rssFlowJob?.cancel() - rssFlowJob = launch { + rssFlowJob = lifecycleScope.launch { when { searchKey.isNullOrEmpty() -> appDb.rssSourceDao.liveEnabled() searchKey.startsWith("group:") -> { diff --git a/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesFragment.kt b/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesFragment.kt index 228ac9df9..737a93dba 100644 --- a/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesFragment.kt +++ b/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesFragment.kt @@ -5,6 +5,7 @@ import android.os.Bundle import android.view.View import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -96,7 +97,7 @@ class RssArticlesFragment : VMBaseFragment(R.layout.fragme private fun initData() { val rssUrl = activityViewModel.url ?: return articlesFlowJob?.cancel() - articlesFlowJob = launch { + articlesFlowJob = lifecycleScope.launch { appDb.rssArticleDao.liveByOriginSort(rssUrl, viewModel.sortName).collect { adapter.setItems(it) } diff --git a/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesViewModel.kt index 1f81c4ba2..0c672841f 100644 --- a/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesViewModel.kt @@ -3,6 +3,7 @@ package io.legado.app.ui.rss.article import android.app.Application import android.os.Bundle import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope import io.legado.app.base.BaseViewModel import io.legado.app.data.appDb import io.legado.app.data.entities.RssArticle @@ -30,7 +31,7 @@ class RssArticlesViewModel(application: Application) : BaseViewModel(application fun loadContent(rssSource: RssSource) { isLoading = true page = 1 - Rss.getArticles(this, sortName, sortUrl, rssSource, page) + Rss.getArticles(viewModelScope, sortName, sortUrl, rssSource, page) .onSuccess(Dispatchers.IO) { nextPageUrl = it.nextPageUrl it.articles.let { list -> @@ -60,7 +61,7 @@ class RssArticlesViewModel(application: Application) : BaseViewModel(application page++ val pageUrl = nextPageUrl if (!pageUrl.isNullOrEmpty()) { - Rss.getArticles(this, sortName, pageUrl, rssSource, page) + Rss.getArticles(viewModelScope, sortName, pageUrl, rssSource, page) .onSuccess(Dispatchers.IO) { nextPageUrl = it.nextPageUrl loadMoreSuccess(it.articles) diff --git a/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesActivity.kt b/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesActivity.kt index 74710a7f2..4020d4e0a 100644 --- a/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesActivity.kt @@ -1,6 +1,7 @@ package io.legado.app.ui.rss.favorites import android.os.Bundle +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import io.legado.app.base.BaseActivity import io.legado.app.data.appDb @@ -35,7 +36,7 @@ class RssFavoritesActivity : BaseActivity(), } private fun initData() { - launch { + lifecycleScope.launch { appDb.rssStarDao.liveAll().collect { adapter.setItems(it) } diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt index 2a6767be9..73bfd6e24 100644 --- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt @@ -12,6 +12,7 @@ import android.view.* import android.webkit.* import androidx.activity.viewModels import androidx.core.view.size +import androidx.lifecycle.lifecycleScope import androidx.webkit.WebSettingsCompat import androidx.webkit.WebViewFeature import io.legado.app.R @@ -231,7 +232,7 @@ class ReadRssActivity : VMBaseActivity } override fun upTtsMenu(isPlaying: Boolean) { - launch { + lifecycleScope.launch { if (isPlaying) { ttsMenuItem?.setIcon(R.drawable.ic_stop_black_24dp) ttsMenuItem?.setTitle(R.string.aloud_stop) diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt index 594a4a543..d36643242 100644 --- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt @@ -9,6 +9,7 @@ import android.util.Base64 import android.webkit.URLUtil import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope import io.legado.app.R import io.legado.app.base.BaseViewModel import io.legado.app.constant.AppConst @@ -25,7 +26,6 @@ import io.legado.app.utils.FileUtils import io.legado.app.utils.isContentScheme import io.legado.app.utils.writeBytes import kotlinx.coroutines.Dispatchers.IO -import kotlinx.coroutines.launch import java.io.File import java.util.* @@ -95,7 +95,7 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application), private fun loadContent(rssArticle: RssArticle, ruleContent: String) { rssSource?.let { source -> - Rss.getContent(this, rssArticle, ruleContent, source) + Rss.getContent(viewModelScope, rssArticle, ruleContent, source) .onSuccess(IO) { body -> rssArticle.description = body appDb.rssArticleDao.insert(rssArticle) @@ -189,9 +189,7 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application), ttsInitFinish = true play() } else { - launch { - toastOnUi(R.string.tts_init_failed) - } + toastOnUi(R.string.tts_init_failed) } } diff --git a/app/src/main/java/io/legado/app/ui/rss/source/debug/RssSourceDebugActivity.kt b/app/src/main/java/io/legado/app/ui/rss/source/debug/RssSourceDebugActivity.kt index 2d0e5f302..1a955f495 100644 --- a/app/src/main/java/io/legado/app/ui/rss/source/debug/RssSourceDebugActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/source/debug/RssSourceDebugActivity.kt @@ -5,6 +5,7 @@ import android.view.Menu import android.view.MenuItem import android.widget.SearchView import androidx.activity.viewModels +import androidx.lifecycle.lifecycleScope import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.databinding.ActivitySourceDebugBinding @@ -29,7 +30,7 @@ class RssSourceDebugActivity : VMBaseActivity - launch { + lifecycleScope.launch { adapter.addItem(msg) if (state == -1 || state == 1000) { binding.rotateLoading.hide() diff --git a/app/src/main/java/io/legado/app/ui/rss/source/debug/RssSourceDebugModel.kt b/app/src/main/java/io/legado/app/ui/rss/source/debug/RssSourceDebugModel.kt index 9d1a5922a..23892dc6f 100644 --- a/app/src/main/java/io/legado/app/ui/rss/source/debug/RssSourceDebugModel.kt +++ b/app/src/main/java/io/legado/app/ui/rss/source/debug/RssSourceDebugModel.kt @@ -1,6 +1,7 @@ package io.legado.app.ui.rss.source.debug import android.app.Application +import androidx.lifecycle.viewModelScope import io.legado.app.base.BaseViewModel import io.legado.app.data.appDb import io.legado.app.data.entities.RssSource @@ -31,7 +32,7 @@ class RssSourceDebugModel(application: Application) : BaseViewModel(application) rssSource?.let { start?.invoke() Debug.callback = this - Debug.startDebug(this, it) + Debug.startDebug(viewModelScope, it) } ?: error?.invoke() } diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt index 806e8122d..6da833a55 100644 --- a/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt +++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt @@ -9,6 +9,7 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.widget.Toolbar import androidx.fragment.app.activityViewModels +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import io.legado.app.R import io.legado.app.base.BaseDialogFragment @@ -68,7 +69,7 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener } private fun initData() { - launch { + lifecycleScope.launch { appDb.rssSourceDao.liveGroup().collect { val groups = linkedSetOf() it.map { group -> diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt index 23ecb9f17..96a1e9293 100644 --- a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt @@ -10,6 +10,7 @@ import androidx.activity.viewModels import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.SearchView import androidx.documentfile.provider.DocumentFile +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.ItemTouchHelper import io.legado.app.R import io.legado.app.base.VMBaseActivity @@ -178,7 +179,7 @@ class RssSourceActivity : VMBaseActivity @@ -230,7 +231,7 @@ class RssSourceActivity : VMBaseActivity { appDb.rssSourceDao.liveAll() diff --git a/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubActivity.kt b/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubActivity.kt index 652a8c94c..1209591d7 100644 --- a/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubActivity.kt @@ -5,6 +5,7 @@ import android.view.Menu import android.view.MenuItem import androidx.core.view.isGone import androidx.lifecycle.LiveData +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.ItemTouchHelper import io.legado.app.R import io.legado.app.base.BaseActivity @@ -100,7 +101,7 @@ class RuleSubActivity : BaseActivity(), } customView { alertBinding.root } okButton { - launch { + lifecycleScope.launch { ruleSub.type = alertBinding.spType.selectedItemPosition ruleSub.name = alertBinding.etName.text?.toString() ?: "" ruleSub.url = alertBinding.etUrl.text?.toString() ?: "" @@ -121,19 +122,19 @@ class RuleSubActivity : BaseActivity(), } override fun delSubscription(ruleSub: RuleSub) { - launch(IO) { + lifecycleScope.launch(IO) { appDb.ruleSubDao.delete(ruleSub) } } override fun updateSourceSub(vararg ruleSub: RuleSub) { - launch(IO) { + lifecycleScope.launch(IO) { appDb.ruleSubDao.update(*ruleSub) } } override fun upOrder() { - launch(IO) { + lifecycleScope.launch(IO) { val sourceSubs = appDb.ruleSubDao.all for ((index: Int, ruleSub: RuleSub) in sourceSubs.withIndex()) { ruleSub.customOrder = index + 1 diff --git a/app/src/main/java/io/legado/app/ui/widget/dialog/TextDialog.kt b/app/src/main/java/io/legado/app/ui/widget/dialog/TextDialog.kt index 8c1df6110..5773988eb 100644 --- a/app/src/main/java/io/legado/app/ui/widget/dialog/TextDialog.kt +++ b/app/src/main/java/io/legado/app/ui/widget/dialog/TextDialog.kt @@ -5,6 +5,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.FragmentManager +import androidx.lifecycle.lifecycleScope import io.legado.app.R import io.legado.app.base.BaseDialogFragment import io.legado.app.databinding.DialogTextViewBinding @@ -79,7 +80,7 @@ class TextDialog : BaseDialogFragment() { } if (time > 0) { binding.badgeView.setBadgeCount((time / 1000).toInt()) - launch { + lifecycleScope.launch { while (time > 0) { delay(1000) time -= 1000 diff --git a/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt b/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt index 22f205101..ad653b60d 100644 --- a/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt +++ b/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt @@ -9,6 +9,7 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.widget.Toolbar import androidx.documentfile.provider.DocumentFile +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import io.legado.app.R import io.legado.app.base.BaseDialogFragment @@ -126,7 +127,7 @@ class FontSelectDialog : BaseDialogFragment(), } private fun openFolder() { - launch(Main) { + lifecycleScope.launch(Main) { val defaultPath = "SD${File.separator}Fonts" selectFontDir.launch( FilePickerParam( From 299e27862ec4aeae96af7428407294c1be30cb31 Mon Sep 17 00:00:00 2001 From: gedoor Date: Wed, 21 Jul 2021 09:37:35 +0800 Subject: [PATCH 34/40] =?UTF-8?q?=E9=87=87=E7=94=A8Flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/data/dao/HttpTTSDao.kt | 4 +- .../io/legado/app/data/dao/ReplaceRuleDao.kt | 14 ++--- .../java/io/legado/app/data/dao/RuleSubDao.kt | 4 +- .../legado/app/data/dao/SearchKeywordDao.kt | 8 +-- .../ui/book/read/config/SpeakEngineDialog.kt | 15 +++--- .../app/ui/book/search/SearchActivity.kt | 31 +++++------ .../app/ui/replace/GroupManageDialog.kt | 23 ++++++--- .../app/ui/replace/ReplaceRuleActivity.kt | 51 ++++++++++--------- .../ui/rss/subscription/RuleSubActivity.kt | 13 +++-- 9 files changed, 87 insertions(+), 76 deletions(-) diff --git a/app/src/main/java/io/legado/app/data/dao/HttpTTSDao.kt b/app/src/main/java/io/legado/app/data/dao/HttpTTSDao.kt index f97c17af1..934fcbfbb 100644 --- a/app/src/main/java/io/legado/app/data/dao/HttpTTSDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/HttpTTSDao.kt @@ -1,8 +1,8 @@ package io.legado.app.data.dao -import androidx.lifecycle.LiveData import androidx.room.* import io.legado.app.data.entities.HttpTTS +import kotlinx.coroutines.flow.Flow @Dao interface HttpTTSDao { @@ -11,7 +11,7 @@ interface HttpTTSDao { val all: List @Query("select * from httpTTS order by name") - fun observeAll(): LiveData> + fun observeAll(): Flow> @get:Query("select count(*) from httpTTS") val count: Int diff --git a/app/src/main/java/io/legado/app/data/dao/ReplaceRuleDao.kt b/app/src/main/java/io/legado/app/data/dao/ReplaceRuleDao.kt index 2a0e1149c..f18a362a0 100644 --- a/app/src/main/java/io/legado/app/data/dao/ReplaceRuleDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/ReplaceRuleDao.kt @@ -1,21 +1,24 @@ package io.legado.app.data.dao -import androidx.lifecycle.LiveData import androidx.room.* import io.legado.app.data.entities.ReplaceRule +import kotlinx.coroutines.flow.Flow @Dao interface ReplaceRuleDao { @Query("SELECT * FROM replace_rules ORDER BY sortOrder ASC") - fun liveDataAll(): LiveData> + fun liveDataAll(): Flow> @Query("SELECT * FROM replace_rules where `group` like :key or name like :key ORDER BY sortOrder ASC") - fun liveDataSearch(key: String): LiveData> + fun liveDataSearch(key: String): Flow> @Query("SELECT * FROM replace_rules where `group` like :key ORDER BY sortOrder ASC") - fun liveDataGroupSearch(key: String): LiveData> + fun liveDataGroupSearch(key: String): Flow> + + @Query("select `group` from replace_rules where `group` is not null and `group` <> ''") + fun liveGroup(): Flow> @get:Query("SELECT MIN(sortOrder) FROM replace_rules") val minOrder: Int @@ -44,9 +47,6 @@ interface ReplaceRuleDao { ) fun findEnabledByScope(name: String, origin: String): List - @Query("select `group` from replace_rules where `group` is not null and `group` <> ''") - fun liveGroup(): LiveData> - @Query("select * from replace_rules where `group` like '%' || :group || '%'") fun getByGroup(group: String): List diff --git a/app/src/main/java/io/legado/app/data/dao/RuleSubDao.kt b/app/src/main/java/io/legado/app/data/dao/RuleSubDao.kt index d5aea29ad..e6ab06827 100644 --- a/app/src/main/java/io/legado/app/data/dao/RuleSubDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/RuleSubDao.kt @@ -1,8 +1,8 @@ package io.legado.app.data.dao -import androidx.lifecycle.LiveData import androidx.room.* import io.legado.app.data.entities.RuleSub +import kotlinx.coroutines.flow.Flow @Dao interface RuleSubDao { @@ -11,7 +11,7 @@ interface RuleSubDao { val all: List @Query("select * from ruleSubs order by customOrder") - fun observeAll(): LiveData> + fun observeAll(): Flow> @get:Query("select customOrder from ruleSubs order by customOrder limit 0,1") val maxOrder: Int diff --git a/app/src/main/java/io/legado/app/data/dao/SearchKeywordDao.kt b/app/src/main/java/io/legado/app/data/dao/SearchKeywordDao.kt index d3063759a..699432939 100644 --- a/app/src/main/java/io/legado/app/data/dao/SearchKeywordDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/SearchKeywordDao.kt @@ -1,8 +1,8 @@ package io.legado.app.data.dao -import androidx.lifecycle.LiveData import androidx.room.* import io.legado.app.data.entities.SearchKeyword +import kotlinx.coroutines.flow.Flow @Dao @@ -12,13 +12,13 @@ interface SearchKeywordDao { val all: List @Query("SELECT * FROM search_keywords ORDER BY usage DESC") - fun liveDataByUsage(): LiveData> + fun liveDataByUsage(): Flow> @Query("SELECT * FROM search_keywords ORDER BY lastUseTime DESC") - fun liveDataByTime(): LiveData> + fun liveDataByTime(): Flow> @Query("SELECT * FROM search_keywords where word like '%'||:key||'%' ORDER BY usage DESC") - fun liveDataSearch(key: String): LiveData> + fun liveDataSearch(key: String): Flow> @Query("select * from search_keywords where word = :key") fun get(key: String): SearchKeyword? diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/SpeakEngineDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/SpeakEngineDialog.kt index 233eef978..a6d7e06d9 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/SpeakEngineDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/SpeakEngineDialog.kt @@ -9,7 +9,7 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.widget.Toolbar import androidx.fragment.app.viewModels -import androidx.lifecycle.LiveData +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import io.legado.app.R import io.legado.app.base.BaseDialogFragment @@ -31,6 +31,8 @@ import io.legado.app.ui.document.FilePickerParam import io.legado.app.ui.widget.dialog.TextDialog import io.legado.app.utils.* import io.legado.app.utils.viewbindingdelegate.viewBinding +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch import splitties.init.appCtx @@ -39,7 +41,6 @@ class SpeakEngineDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener private val ttsUrlKey = "ttsUrlKey" lateinit var adapter: Adapter private val viewModel: SpeakEngineViewModel by viewModels() - private var httpTTSData: LiveData>? = null private var engineId = appCtx.getPrefLong(PreferKey.speakEngine) private val importDocResult = registerForActivityResult(FilePicker()) { it?.let { @@ -103,11 +104,11 @@ class SpeakEngineDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener } private fun initData() { - httpTTSData?.removeObservers(this) - httpTTSData = appDb.httpTTSDao.observeAll() - httpTTSData?.observe(this, { - adapter.setItems(it) - }) + lifecycleScope.launch { + appDb.httpTTSDao.observeAll().collect { + adapter.setItems(it) + } + } } override fun onMenuItemClick(item: MenuItem?): Boolean { diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt index afed9c5ab..90f202441 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt @@ -10,7 +10,6 @@ import android.view.View.VISIBLE import android.widget.TextView import androidx.activity.viewModels import androidx.appcompat.widget.SearchView -import androidx.lifecycle.LiveData import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -49,7 +48,7 @@ class SearchActivity : VMBaseActivity>? = null + private var historyFlowJob: Job? = null private var booksFlowJob: Job? = null private var menu: Menu? = null private var precisionSearchMenuItem: MenuItem? = null @@ -292,22 +291,20 @@ class SearchActivity : VMBaseActivity appDb.searchKeywordDao.liveDataByUsage() + else -> appDb.searchKeywordDao.liveDataSearch(key) + }.collect { + historyKeyAdapter.setItems(it) + if (it.isEmpty()) { + binding.tvClearHistory.invisible() + } else { + binding.tvClearHistory.visible() + } } - }) + } } /** diff --git a/app/src/main/java/io/legado/app/ui/replace/GroupManageDialog.kt b/app/src/main/java/io/legado/app/ui/replace/GroupManageDialog.kt index 24bac89bb..64c04a6d7 100644 --- a/app/src/main/java/io/legado/app/ui/replace/GroupManageDialog.kt +++ b/app/src/main/java/io/legado/app/ui/replace/GroupManageDialog.kt @@ -10,6 +10,7 @@ import android.view.ViewGroup import androidx.appcompat.widget.Toolbar import androidx.fragment.app.DialogFragment import androidx.fragment.app.activityViewModels +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import io.legado.app.R import io.legado.app.base.adapter.ItemViewHolder @@ -25,6 +26,8 @@ import io.legado.app.lib.theme.primaryColor import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.utils.* import io.legado.app.utils.viewbindingdelegate.viewBinding +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener { @@ -50,10 +53,11 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener { super.onViewCreated(view, savedInstanceState) view.setBackgroundColor(backgroundColor) binding.toolBar.setBackgroundColor(primaryColor) + initView() initData() } - private fun initData() = binding.run { + private fun initView() = binding.run { toolBar.title = getString(R.string.group_manage) toolBar.inflateMenu(R.menu.group_manage) toolBar.menu.applyTint(requireContext()) @@ -62,13 +66,18 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener { recyclerView.layoutManager = LinearLayoutManager(requireContext()) recyclerView.addItemDecoration(VerticalDivider(requireContext())) recyclerView.adapter = adapter - appDb.replaceRuleDao.liveGroup().observe(viewLifecycleOwner, { - val groups = linkedSetOf() - it.map { group -> - groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + } + + private fun initData() { + lifecycleScope.launch { + appDb.replaceRuleDao.liveGroup().collect { + val groups = linkedSetOf() + it.map { group -> + groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + } + adapter.setItems(groups.toList()) } - adapter.setItems(groups.toList()) - }) + } } override fun onMenuItemClick(item: MenuItem?): Boolean { diff --git a/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt b/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt index 56ff1257e..a2204038f 100644 --- a/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt +++ b/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt @@ -11,7 +11,7 @@ import androidx.activity.viewModels import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.SearchView import androidx.documentfile.provider.DocumentFile -import androidx.lifecycle.LiveData +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import io.legado.app.R @@ -39,6 +39,9 @@ import io.legado.app.ui.widget.recycler.ItemTouchCallback import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.utils.* import io.legado.app.utils.viewbindingdelegate.viewBinding +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch import java.io.File /** @@ -56,7 +59,7 @@ class ReplaceRuleActivity : VMBaseActivity() private var groupMenu: SubMenu? = null - private var replaceRuleLiveData: LiveData>? = null + private var replaceRuleFlowJob: Job? = null private var dataInit = false private val qrCodeResult = registerForActivityResult(QrCodeResult()) { it ?: return@registerForActivityResult @@ -173,37 +176,39 @@ class ReplaceRuleActivity : VMBaseActivity { - appDb.replaceRuleDao.liveDataAll() - } - searchKey.startsWith("group:") -> { - val key = searchKey.substringAfter("group:") - appDb.replaceRuleDao.liveDataGroupSearch("%$key%") - } - else -> { - appDb.replaceRuleDao.liveDataSearch("%$searchKey%") - } - }.apply { - observe(this@ReplaceRuleActivity, { + replaceRuleFlowJob?.cancel() + replaceRuleFlowJob = lifecycleScope.launch { + when { + searchKey.isNullOrEmpty() -> { + appDb.replaceRuleDao.liveDataAll() + } + searchKey.startsWith("group:") -> { + val key = searchKey.substringAfter("group:") + appDb.replaceRuleDao.liveDataGroupSearch("%$key%") + } + else -> { + appDb.replaceRuleDao.liveDataSearch("%$searchKey%") + } + }.collect { if (dataInit) { setResult(Activity.RESULT_OK) } adapter.setItems(it, adapter.diffItemCallBack) dataInit = true - }) + } } } private fun observeGroupData() { - appDb.replaceRuleDao.liveGroup().observe(this, { - groups.clear() - it.map { group -> - groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + lifecycleScope.launch { + appDb.replaceRuleDao.liveGroup().collect { + groups.clear() + it.map { group -> + groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + } + upGroupMenu() } - upGroupMenu() - }) + } } override fun onCompatOptionsItemSelected(item: MenuItem): Boolean { diff --git a/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubActivity.kt b/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubActivity.kt index 1209591d7..c81b5a387 100644 --- a/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubActivity.kt @@ -4,7 +4,6 @@ import android.os.Bundle import android.view.Menu import android.view.MenuItem import androidx.core.view.isGone -import androidx.lifecycle.LiveData import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.ItemTouchHelper import io.legado.app.R @@ -22,6 +21,7 @@ import io.legado.app.utils.startActivity import io.legado.app.utils.toastOnUi import io.legado.app.utils.viewbindingdelegate.viewBinding import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -33,7 +33,6 @@ class RuleSubActivity : BaseActivity(), override val binding by viewBinding(ActivityRuleSubBinding::inflate) private lateinit var adapter: RuleSubAdapter - private var liveData: LiveData>? = null override fun onActivityCreated(savedInstanceState: Bundle?) { initView() @@ -64,11 +63,11 @@ class RuleSubActivity : BaseActivity(), } private fun initData() { - liveData?.removeObservers(this) - liveData = appDb.ruleSubDao.observeAll() - liveData?.observe(this) { - binding.tvEmptyMsg.isGone = it.isNotEmpty() - adapter.setItems(it) + lifecycleScope.launch { + appDb.ruleSubDao.observeAll().collect { + binding.tvEmptyMsg.isGone = it.isNotEmpty() + adapter.setItems(it) + } } } From 3646bc4bbc4cd56f6f8b784760e7495b04eef2f2 Mon Sep 17 00:00:00 2001 From: gedoor Date: Wed, 21 Jul 2021 10:00:07 +0800 Subject: [PATCH 35/40] =?UTF-8?q?=E9=87=87=E7=94=A8Flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 0fcb26205..67d131392 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -14,6 +14,7 @@ * 获取正文api使用替换规则 * 添加一个ronet版本,网络访问使用Chromium内核 * web书架增加【最近一次更新书籍信息的时间】 +* 用Flow替换LiveData,优化资源 **2021/07/16** 1. js扩展函数添加删除本地文件方法 From 4862a942d33b8f256dc092993f6376df3e1e9bd2 Mon Sep 17 00:00:00 2001 From: gedoor Date: Wed, 21 Jul 2021 10:04:27 +0800 Subject: [PATCH 36/40] =?UTF-8?q?=E9=87=87=E7=94=A8Flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 67d131392..46aa445f1 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -8,13 +8,13 @@ * 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则出现问题。先关闭替换净化并刷新,再观察是否正常。如果正常说明净化规则存在误杀,如果关闭后仍然出现相关问题,请点击源链接查看原文与正文是否相同,如果不同,再进行反馈。 * 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源! -**2021/07/19** +**2021/07/21** * 非关键规则添加try防止报错中断解析 * 添加获取封面的api * 获取正文api使用替换规则 * 添加一个ronet版本,网络访问使用Chromium内核 * web书架增加【最近一次更新书籍信息的时间】 -* 用Flow替换LiveData,优化资源 +* 采用Flow替换LiveData,优化资源使用 **2021/07/16** 1. js扩展函数添加删除本地文件方法 From 09913c03e42f940f3f17a858567aa57056db3305 Mon Sep 17 00:00:00 2001 From: gedoor Date: Wed, 21 Jul 2021 10:52:56 +0800 Subject: [PATCH 37/40] =?UTF-8?q?=E9=87=87=E7=94=A8Flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/data/dao/BookChapterDao.kt | 4 +- .../java/io/legado/app/data/dao/BookDao.kt | 14 +++--- .../io/legado/app/data/dao/BookGroupDao.kt | 6 +-- .../io/legado/app/data/dao/BookSourceDao.kt | 45 ++++++++----------- .../io/legado/app/data/dao/BookmarkDao.kt | 23 +++++----- .../java/io/legado/app/data/dao/HttpTTSDao.kt | 2 +- .../io/legado/app/data/dao/ReplaceRuleDao.kt | 14 +++--- .../io/legado/app/data/dao/RssArticleDao.kt | 2 +- .../io/legado/app/data/dao/RssSourceDao.kt | 20 ++++----- .../java/io/legado/app/data/dao/RuleSubDao.kt | 2 +- .../io/legado/app/data/dao/SearchBookDao.kt | 23 +++++----- .../legado/app/data/dao/SearchKeywordDao.kt | 6 +-- .../io/legado/app/data/dao/TxtTocRuleDao.kt | 4 +- .../ui/book/arrange/ArrangeBookActivity.kt | 12 ++--- .../legado/app/ui/book/cache/CacheActivity.kt | 12 ++--- .../book/changesource/ChangeSourceDialog.kt | 2 +- .../app/ui/book/group/GroupManageDialog.kt | 2 +- .../app/ui/book/group/GroupSelectDialog.kt | 2 +- .../app/ui/book/local/ImportBookActivity.kt | 2 +- .../ui/book/read/config/SpeakEngineDialog.kt | 2 +- .../app/ui/book/read/config/TocRegexDialog.kt | 15 +++---- .../app/ui/book/search/SearchActivity.kt | 8 ++-- .../book/source/manage/BookSourceActivity.kt | 12 ++--- .../book/source/manage/GroupManageDialog.kt | 2 +- .../app/ui/book/toc/BookmarkFragment.kt | 4 +- .../app/ui/book/toc/ChapterListFragment.kt | 4 +- .../bookshelf/style1/BookshelfFragment1.kt | 2 +- .../bookshelf/style1/books/BooksFragment.kt | 10 ++--- .../bookshelf/style2/BookshelfFragment2.kt | 12 ++--- .../app/ui/main/explore/ExploreFragment.kt | 8 ++-- .../io/legado/app/ui/main/rss/RssFragment.kt | 8 ++-- .../app/ui/replace/GroupManageDialog.kt | 2 +- .../app/ui/replace/ReplaceRuleActivity.kt | 8 ++-- .../app/ui/rss/article/RssArticlesFragment.kt | 2 +- .../ui/rss/source/manage/GroupManageDialog.kt | 2 +- .../ui/rss/source/manage/RssSourceActivity.kt | 8 ++-- .../ui/rss/subscription/RuleSubActivity.kt | 2 +- 37 files changed, 148 insertions(+), 160 deletions(-) diff --git a/app/src/main/java/io/legado/app/data/dao/BookChapterDao.kt b/app/src/main/java/io/legado/app/data/dao/BookChapterDao.kt index 24e632b0d..eaeaabd5c 100644 --- a/app/src/main/java/io/legado/app/data/dao/BookChapterDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/BookChapterDao.kt @@ -11,10 +11,10 @@ import kotlinx.coroutines.flow.Flow interface BookChapterDao { @Query("select * from chapters where bookUrl = :bookUrl order by `index`") - fun observeByBook(bookUrl: String): Flow> + fun flowByBook(bookUrl: String): Flow> @Query("SELECT * FROM chapters where bookUrl = :bookUrl and title like '%'||:key||'%' order by `index`") - fun liveDataSearch(bookUrl: String, key: String): Flow> + fun flowSearch(bookUrl: String, key: String): Flow> @Query("select * from chapters where bookUrl = :bookUrl order by `index`") fun getChapterList(bookUrl: String): List diff --git a/app/src/main/java/io/legado/app/data/dao/BookDao.kt b/app/src/main/java/io/legado/app/data/dao/BookDao.kt index e541bf37e..bfc933f64 100644 --- a/app/src/main/java/io/legado/app/data/dao/BookDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/BookDao.kt @@ -9,25 +9,25 @@ import kotlinx.coroutines.flow.Flow interface BookDao { @Query("SELECT * FROM books order by durChapterTime desc") - fun observeAll(): Flow> + fun flowAll(): Flow> @Query("SELECT * FROM books WHERE type = ${BookType.audio}") - fun observeAudio(): Flow> + fun flowAudio(): Flow> @Query("SELECT * FROM books WHERE origin = '${BookType.local}'") - fun observeLocal(): Flow> + fun flowLocal(): Flow> @Query("select * from books where type != ${BookType.audio} and origin != '${BookType.local}' and ((SELECT sum(groupId) FROM book_groups where groupId > 0) & `group`) = 0") - fun observeNoGroup(): Flow> + fun flowNoGroup(): Flow> @Query("SELECT bookUrl FROM books WHERE origin = '${BookType.local}'") - fun observeLocalUri(): Flow> + fun flowLocalUri(): Flow> @Query("SELECT * FROM books WHERE (`group` & :group) > 0") - fun observeByGroup(group: Long): Flow> + fun flowByGroup(group: Long): Flow> @Query("SELECT * FROM books WHERE name like '%'||:key||'%' or author like '%'||:key||'%'") - fun liveDataSearch(key: String): Flow> + fun flowSearch(key: String): Flow> @Query("SELECT * FROM books WHERE (`group` & :group) > 0") fun getBooksByGroup(group: Long): List diff --git a/app/src/main/java/io/legado/app/data/dao/BookGroupDao.kt b/app/src/main/java/io/legado/app/data/dao/BookGroupDao.kt index 51d73542e..346992faa 100644 --- a/app/src/main/java/io/legado/app/data/dao/BookGroupDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/BookGroupDao.kt @@ -15,7 +15,7 @@ interface BookGroupDao { fun getByName(groupName: String): BookGroup? @Query("SELECT * FROM book_groups ORDER BY `order`") - fun liveDataAll(): Flow> + fun flowAll(): Flow> @Query( """ @@ -26,10 +26,10 @@ interface BookGroupDao { or (groupId = -1 and show > 0) ORDER BY `order`""" ) - fun liveDataShow(): Flow> + fun flowShow(): Flow> @Query("SELECT * FROM book_groups where groupId >= 0 ORDER BY `order`") - fun liveDataSelect(): Flow> + fun flowSelect(): Flow> @get:Query("SELECT sum(groupId) FROM book_groups where groupId >= 0") val idsSum: Long diff --git a/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt b/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt index 7bc28742d..99677054c 100644 --- a/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt @@ -8,69 +8,62 @@ import kotlinx.coroutines.flow.Flow interface BookSourceDao { @Query("select * from book_sources order by customOrder asc") - fun liveDataAll(): Flow> + fun flowAll(): Flow> @Query( - """ - select * from book_sources + """select * from book_sources where bookSourceName like :searchKey or bookSourceGroup like :searchKey or bookSourceUrl like :searchKey or bookSourceComment like :searchKey - order by customOrder asc - """ + order by customOrder asc""" ) - fun liveDataSearch(searchKey: String): Flow> + fun flowSearch(searchKey: String): Flow> @Query("select * from book_sources where bookSourceGroup like :searchKey order by customOrder asc") - fun liveDataGroupSearch(searchKey: String): Flow> + fun flowGroupSearch(searchKey: String): Flow> @Query("select * from book_sources where enabled = 1 order by customOrder asc") - fun liveDataEnabled(): Flow> + fun flowEnabled(): Flow> @Query("select * from book_sources where enabled = 0 order by customOrder asc") - fun liveDataDisabled(): Flow> + fun flowDisabled(): Flow> @Query("select * from book_sources where enabledExplore = 1 and trim(exploreUrl) <> '' order by customOrder asc") - fun liveExplore(): Flow> + fun flowExplore(): Flow> @Query( - """ - select * from book_sources + """select * from book_sources where enabledExplore = 1 and trim(exploreUrl) <> '' and (bookSourceGroup like :key or bookSourceName like :key) - order by customOrder asc - """ + order by customOrder asc""" ) - fun liveExplore(key: String): Flow> + fun flowExplore(key: String): Flow> @Query( - """ - select * from book_sources + """select * from book_sources where enabledExplore = 1 and trim(exploreUrl) <> '' and (bookSourceGroup like :key) - order by customOrder asc - """ + order by customOrder asc""" ) - fun liveGroupExplore(key: String): Flow> + fun flowGroupExplore(key: String): Flow> @Query("select distinct bookSourceGroup from book_sources where trim(bookSourceGroup) <> ''") - fun liveGroup(): Flow> + fun flowGroup(): Flow> @Query("select distinct bookSourceGroup from book_sources where enabled = 1 and trim(bookSourceGroup) <> ''") - fun liveGroupEnabled(): Flow> + fun flowGroupEnabled(): Flow> @Query( - """ - select distinct bookSourceGroup from book_sources + """select distinct bookSourceGroup from book_sources where enabledExplore = 1 and trim(exploreUrl) <> '' and trim(bookSourceGroup) <> '' - """ + order by customOrder""" ) - fun liveExploreGroup(): Flow> + fun flowExploreGroup(): Flow> @Query("select * from book_sources where bookSourceGroup like '%' || :group || '%'") fun getByGroup(group: String): List diff --git a/app/src/main/java/io/legado/app/data/dao/BookmarkDao.kt b/app/src/main/java/io/legado/app/data/dao/BookmarkDao.kt index 052884126..830e83d6c 100644 --- a/app/src/main/java/io/legado/app/data/dao/BookmarkDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/BookmarkDao.kt @@ -11,23 +11,20 @@ interface BookmarkDao { @get:Query("select * from bookmarks") val all: List - @Query("select * from bookmarks where bookName = :bookName and bookAuthor = :bookAuthor") - fun observeByBook( - bookName: String, - bookAuthor: String - ): Flow> + @Query( + """select * from bookmarks + where bookName = :bookName and bookAuthor = :bookAuthor + order by chapterIndex""" + ) + fun flowByBook(bookName: String, bookAuthor: String): Flow> @Query( - """ - SELECT * FROM bookmarks where bookName = :bookName and bookAuthor = :bookAuthor + """SELECT * FROM bookmarks + where bookName = :bookName and bookAuthor = :bookAuthor and chapterName like '%'||:key||'%' or content like '%'||:key||'%' - """ + order by chapterIndex""" ) - fun liveDataSearch( - bookName: String, - bookAuthor: String, - key: String - ): Flow> + fun flowSearch(bookName: String, bookAuthor: String, key: String): Flow> @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(vararg bookmark: Bookmark) diff --git a/app/src/main/java/io/legado/app/data/dao/HttpTTSDao.kt b/app/src/main/java/io/legado/app/data/dao/HttpTTSDao.kt index 934fcbfbb..70ff64717 100644 --- a/app/src/main/java/io/legado/app/data/dao/HttpTTSDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/HttpTTSDao.kt @@ -11,7 +11,7 @@ interface HttpTTSDao { val all: List @Query("select * from httpTTS order by name") - fun observeAll(): Flow> + fun flowAll(): Flow> @get:Query("select count(*) from httpTTS") val count: Int diff --git a/app/src/main/java/io/legado/app/data/dao/ReplaceRuleDao.kt b/app/src/main/java/io/legado/app/data/dao/ReplaceRuleDao.kt index f18a362a0..b82014839 100644 --- a/app/src/main/java/io/legado/app/data/dao/ReplaceRuleDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/ReplaceRuleDao.kt @@ -9,16 +9,16 @@ import kotlinx.coroutines.flow.Flow interface ReplaceRuleDao { @Query("SELECT * FROM replace_rules ORDER BY sortOrder ASC") - fun liveDataAll(): Flow> + fun flowAll(): Flow> @Query("SELECT * FROM replace_rules where `group` like :key or name like :key ORDER BY sortOrder ASC") - fun liveDataSearch(key: String): Flow> + fun flowSearch(key: String): Flow> @Query("SELECT * FROM replace_rules where `group` like :key ORDER BY sortOrder ASC") - fun liveDataGroupSearch(key: String): Flow> + fun flowGroupSearch(key: String): Flow> @Query("select `group` from replace_rules where `group` is not null and `group` <> ''") - fun liveGroup(): Flow> + fun flowGroup(): Flow> @get:Query("SELECT MIN(sortOrder) FROM replace_rules") val minOrder: Int @@ -39,11 +39,9 @@ interface ReplaceRuleDao { fun findByIds(vararg ids: Long): List @Query( - """ - SELECT * FROM replace_rules WHERE isEnabled = 1 + """SELECT * FROM replace_rules WHERE isEnabled = 1 AND (scope LIKE '%' || :name || '%' or scope LIKE '%' || :origin || '%' or scope is null or scope = '') - order by sortOrder - """ + order by sortOrder""" ) fun findEnabledByScope(name: String, origin: String): List diff --git a/app/src/main/java/io/legado/app/data/dao/RssArticleDao.kt b/app/src/main/java/io/legado/app/data/dao/RssArticleDao.kt index e75a8eba8..30caf63da 100644 --- a/app/src/main/java/io/legado/app/data/dao/RssArticleDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/RssArticleDao.kt @@ -18,7 +18,7 @@ interface RssArticleDao { on t1.link = t2.record where origin = :origin and sort = :sort order by `order` desc""" ) - fun liveByOriginSort(origin: String, sort: String): Flow> + fun flowByOriginSort(origin: String, sort: String): Flow> @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(vararg rssArticle: RssArticle) diff --git a/app/src/main/java/io/legado/app/data/dao/RssSourceDao.kt b/app/src/main/java/io/legado/app/data/dao/RssSourceDao.kt index 6abd2f847..85605b3cf 100644 --- a/app/src/main/java/io/legado/app/data/dao/RssSourceDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/RssSourceDao.kt @@ -20,32 +20,30 @@ interface RssSourceDao { val size: Int @Query("SELECT * FROM rssSources order by customOrder") - fun liveAll(): Flow> + fun flowAll(): Flow> @Query("SELECT * FROM rssSources where sourceName like :key or sourceUrl like :key or sourceGroup like :key order by customOrder") - fun liveSearch(key: String): Flow> + fun flowSearch(key: String): Flow> @Query("SELECT * FROM rssSources where sourceGroup like :key order by customOrder") - fun liveGroupSearch(key: String): Flow> + fun flowGroupSearch(key: String): Flow> @Query("SELECT * FROM rssSources where enabled = 1 order by customOrder") - fun liveEnabled(): Flow> + fun flowEnabled(): Flow> @Query( - """ - SELECT * FROM rssSources + """SELECT * FROM rssSources where enabled = 1 and (sourceName like :searchKey or sourceGroup like :searchKey or sourceUrl like :searchKey) - order by customOrder - """ + order by customOrder""" ) - fun liveEnabled(searchKey: String): Flow> + fun flowEnabled(searchKey: String): Flow> @Query("SELECT * FROM rssSources where enabled = 1 and sourceGroup like :searchKey order by customOrder") - fun liveEnabledByGroup(searchKey: String): Flow> + fun flowEnabledByGroup(searchKey: String): Flow> @Query("select distinct sourceGroup from rssSources where trim(sourceGroup) <> ''") - fun liveGroup(): Flow> + fun flowGroup(): Flow> @get:Query("select distinct sourceGroup from rssSources where trim(sourceGroup) <> ''") val allGroup: List diff --git a/app/src/main/java/io/legado/app/data/dao/RuleSubDao.kt b/app/src/main/java/io/legado/app/data/dao/RuleSubDao.kt index e6ab06827..56d87b262 100644 --- a/app/src/main/java/io/legado/app/data/dao/RuleSubDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/RuleSubDao.kt @@ -11,7 +11,7 @@ interface RuleSubDao { val all: List @Query("select * from ruleSubs order by customOrder") - fun observeAll(): Flow> + fun flowAll(): Flow> @get:Query("select customOrder from ruleSubs order by customOrder limit 0,1") val maxOrder: Int diff --git a/app/src/main/java/io/legado/app/data/dao/SearchBookDao.kt b/app/src/main/java/io/legado/app/data/dao/SearchBookDao.kt index 8f53d0170..5454fd700 100644 --- a/app/src/main/java/io/legado/app/data/dao/SearchBookDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/SearchBookDao.kt @@ -16,24 +16,27 @@ interface SearchBookDao { fun getFirstByNameAuthor(name: String, author: String): SearchBook? @Query( - """ - select t1.name, t1.author, t1.origin, t1.originName, t1.coverUrl, t1.bookUrl, t1.type, t1.time, t1.intro, t1.kind, t1.latestChapterTitle, t1.tocUrl, t1.variable, t1.wordCount, t2.customOrder as originOrder + """select t1.name, t1.author, t1.origin, t1.originName, t1.coverUrl, t1.bookUrl, + t1.type, t1.time, t1.intro, t1.kind, t1.latestChapterTitle, t1.tocUrl, t1.variable, + t1.wordCount, t2.customOrder as originOrder from searchBooks as t1 inner join book_sources as t2 on t1.origin = t2.bookSourceUrl - where t1.name = :name and t1.author like '%'||:author||'%' and t2.enabled = 1 and t2.bookSourceGroup like '%'||:sourceGroup||'%' - order by t2.customOrder - """ + where t1.name = :name and t1.author like '%'||:author||'%' + and t2.enabled = 1 and t2.bookSourceGroup like '%'||:sourceGroup||'%' + order by t2.customOrder""" ) fun getChangeSourceSearch(name: String, author: String, sourceGroup: String): List @Query( - """ - select t1.name, t1.author, t1.origin, t1.originName, t1.coverUrl, t1.bookUrl, t1.type, t1.time, t1.intro, t1.kind, t1.latestChapterTitle, t1.tocUrl, t1.variable, t1.wordCount, t2.customOrder as originOrder + """select t1.name, t1.author, t1.origin, t1.originName, t1.coverUrl, t1.bookUrl, + t1.type, t1.time, t1.intro, t1.kind, t1.latestChapterTitle, t1.tocUrl, t1.variable, + t1.wordCount, t2.customOrder as originOrder from searchBooks as t1 inner join book_sources as t2 on t1.origin = t2.bookSourceUrl - where t1.name = :name and t1.author = :author and originName like '%'||:key||'%' and t2.enabled = 1 and t2.bookSourceGroup like '%'||:sourceGroup||'%' - order by t2.customOrder - """ + where t1.name = :name and t1.author = :author + and originName like '%'||:key||'%' and t2.enabled = 1 + and t2.bookSourceGroup like '%'||:sourceGroup||'%' + order by t2.customOrder""" ) fun getChangeSourceSearch( name: String, diff --git a/app/src/main/java/io/legado/app/data/dao/SearchKeywordDao.kt b/app/src/main/java/io/legado/app/data/dao/SearchKeywordDao.kt index 699432939..2b433e488 100644 --- a/app/src/main/java/io/legado/app/data/dao/SearchKeywordDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/SearchKeywordDao.kt @@ -12,13 +12,13 @@ interface SearchKeywordDao { val all: List @Query("SELECT * FROM search_keywords ORDER BY usage DESC") - fun liveDataByUsage(): Flow> + fun flowByUsage(): Flow> @Query("SELECT * FROM search_keywords ORDER BY lastUseTime DESC") - fun liveDataByTime(): Flow> + fun flowByTime(): Flow> @Query("SELECT * FROM search_keywords where word like '%'||:key||'%' ORDER BY usage DESC") - fun liveDataSearch(key: String): Flow> + fun flowSearch(key: String): Flow> @Query("select * from search_keywords where word = :key") fun get(key: String): SearchKeyword? diff --git a/app/src/main/java/io/legado/app/data/dao/TxtTocRuleDao.kt b/app/src/main/java/io/legado/app/data/dao/TxtTocRuleDao.kt index d1b023896..98925bc78 100644 --- a/app/src/main/java/io/legado/app/data/dao/TxtTocRuleDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/TxtTocRuleDao.kt @@ -1,14 +1,14 @@ package io.legado.app.data.dao -import androidx.lifecycle.LiveData import androidx.room.* import io.legado.app.data.entities.TxtTocRule +import kotlinx.coroutines.flow.Flow @Dao interface TxtTocRuleDao { @Query("select * from txtTocRules order by serialNumber") - fun observeAll(): LiveData> + fun observeAll(): Flow> @get:Query("select * from txtTocRules order by serialNumber") val all: List diff --git a/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt index 68c5a7467..a211560cc 100644 --- a/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt @@ -109,7 +109,7 @@ class ArrangeBookActivity : VMBaseActivity appDb.bookDao.observeAll() - AppConst.bookGroupLocalId -> appDb.bookDao.observeLocal() - AppConst.bookGroupAudioId -> appDb.bookDao.observeAudio() - AppConst.bookGroupNoneId -> appDb.bookDao.observeNoGroup() - else -> appDb.bookDao.observeByGroup(groupId) + AppConst.bookGroupAllId -> appDb.bookDao.flowAll() + AppConst.bookGroupLocalId -> appDb.bookDao.flowLocal() + AppConst.bookGroupAudioId -> appDb.bookDao.flowAudio() + AppConst.bookGroupNoneId -> appDb.bookDao.flowNoGroup() + else -> appDb.bookDao.flowByGroup(groupId) }.collect { list -> val books = when (getPrefInt(PreferKey.bookshelfSort)) { 1 -> list.sortedByDescending { it.latestChapterTime } diff --git a/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt b/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt index 313ba092b..148c7c66a 100644 --- a/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt @@ -151,11 +151,11 @@ class CacheActivity : VMBaseActivity() booksFlowJob?.cancel() booksFlowJob = lifecycleScope.launch { when (groupId) { - AppConst.bookGroupAllId -> appDb.bookDao.observeAll() - AppConst.bookGroupLocalId -> appDb.bookDao.observeLocal() - AppConst.bookGroupAudioId -> appDb.bookDao.observeAudio() - AppConst.bookGroupNoneId -> appDb.bookDao.observeNoGroup() - else -> appDb.bookDao.observeByGroup(groupId) + AppConst.bookGroupAllId -> appDb.bookDao.flowAll() + AppConst.bookGroupLocalId -> appDb.bookDao.flowLocal() + AppConst.bookGroupAudioId -> appDb.bookDao.flowAudio() + AppConst.bookGroupNoneId -> appDb.bookDao.flowNoGroup() + else -> appDb.bookDao.flowByGroup(groupId) }.collect { list -> val booksDownload = list.filter { it.isOnLineTxt() @@ -176,7 +176,7 @@ class CacheActivity : VMBaseActivity() private fun initGroupData() { lifecycleScope.launch { - appDb.bookGroupDao.liveDataAll().collect { + appDb.bookGroupDao.flowAll().collect { groupList.clear() groupList.addAll(it) adapter.notifyDataSetChanged() diff --git a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt index 53cf693d9..0a51722b5 100644 --- a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt @@ -152,7 +152,7 @@ class ChangeSourceDialog : BaseDialogFragment(), adapter.setItems(it) }) lifecycleScope.launch { - appDb.bookSourceDao.liveGroupEnabled().collect { + appDb.bookSourceDao.flowGroupEnabled().collect { groups.clear() it.map { group -> groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) diff --git a/app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt b/app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt index e7a6a6989..274febe1d 100644 --- a/app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt @@ -81,7 +81,7 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener private fun initData() { lifecycleScope.launch { - appDb.bookGroupDao.liveDataAll().collect { + appDb.bookGroupDao.flowAll().collect { adapter.setItems(it) } } diff --git a/app/src/main/java/io/legado/app/ui/book/group/GroupSelectDialog.kt b/app/src/main/java/io/legado/app/ui/book/group/GroupSelectDialog.kt index a4b0e33b1..b2e5ee454 100644 --- a/app/src/main/java/io/legado/app/ui/book/group/GroupSelectDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/group/GroupSelectDialog.kt @@ -109,7 +109,7 @@ class GroupSelectDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener private fun initData() { lifecycleScope.launch { - appDb.bookGroupDao.liveDataSelect().collect { + appDb.bookGroupDao.flowSelect().collect { adapter.setItems(it) } } diff --git a/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt index 4b18d3b1e..59d25743b 100644 --- a/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt @@ -126,7 +126,7 @@ class ImportBookActivity : VMBaseActivity>? = null var selectedName: String? = null private var durRegex: String? = null private val viewModel: TocRegexViewModel by viewModels() @@ -93,12 +92,12 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener { } private fun initData() { - tocRegexLiveData?.removeObservers(viewLifecycleOwner) - tocRegexLiveData = appDb.txtTocRuleDao.observeAll() - tocRegexLiveData?.observe(viewLifecycleOwner, { tocRules -> - initSelectedName(tocRules) - adapter.setItems(tocRules) - }) + lifecycleScope.launch { + appDb.txtTocRuleDao.observeAll().collect { tocRules -> + initSelectedName(tocRules) + adapter.setItems(tocRules) + } + } } private fun initSelectedName(tocRules: List) { diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt index 90f202441..aec3213cb 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt @@ -193,7 +193,7 @@ class SearchActivity : VMBaseActivity groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) @@ -278,7 +278,7 @@ class SearchActivity : VMBaseActivity appDb.searchKeywordDao.liveDataByUsage() - else -> appDb.searchKeywordDao.liveDataSearch(key) + key.isNullOrBlank() -> appDb.searchKeywordDao.flowByUsage() + else -> appDb.searchKeywordDao.flowSearch(key) }.collect { historyKeyAdapter.setItems(it) if (it.isEmpty()) { diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt index 8f8f6fc41..ac748d6c4 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt @@ -207,20 +207,20 @@ class BookSourceActivity : VMBaseActivity { - appDb.bookSourceDao.liveDataAll() + appDb.bookSourceDao.flowAll() } searchKey == getString(R.string.enabled) -> { - appDb.bookSourceDao.liveDataEnabled() + appDb.bookSourceDao.flowEnabled() } searchKey == getString(R.string.disabled) -> { - appDb.bookSourceDao.liveDataDisabled() + appDb.bookSourceDao.flowDisabled() } searchKey.startsWith("group:") -> { val key = searchKey.substringAfter("group:") - appDb.bookSourceDao.liveDataGroupSearch("%$key%") + appDb.bookSourceDao.flowGroupSearch("%$key%") } else -> { - appDb.bookSourceDao.liveDataSearch("%$searchKey%") + appDb.bookSourceDao.flowSearch("%$searchKey%") } }.collect { data -> val sourceList = @@ -277,7 +277,7 @@ class BookSourceActivity : VMBaseActivity diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/GroupManageDialog.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/GroupManageDialog.kt index 143c1b9ab..8d0bad63c 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/manage/GroupManageDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/manage/GroupManageDialog.kt @@ -68,7 +68,7 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener private fun initData() { lifecycleScope.launch { - appDb.bookSourceDao.liveGroup().collect { + appDb.bookSourceDao.flowGroup().collect { val groups = linkedSetOf() it.map { group -> groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) diff --git a/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt b/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt index 2f81b0a04..27fee596f 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt @@ -53,8 +53,8 @@ class BookmarkFragment : VMBaseFragment(R.layout.fragment_bookmark bookmarkFlowJob?.cancel() bookmarkFlowJob = lifecycleScope.launch { when { - searchKey.isNullOrBlank() -> appDb.bookmarkDao.observeByBook(book.name, book.author) - else -> appDb.bookmarkDao.liveDataSearch(book.name, book.author, searchKey) + searchKey.isNullOrBlank() -> appDb.bookmarkDao.flowByBook(book.name, book.author) + else -> appDb.bookmarkDao.flowSearch(book.name, book.author, searchKey) }.collect { adapter.setItems(it) } diff --git a/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt b/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt index 6143cf456..ca5c6b06b 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt @@ -111,8 +111,8 @@ class ChapterListFragment : VMBaseFragment(R.layout.fragment_chapt tocFlowJob?.cancel() tocFlowJob = lifecycleScope.launch { when { - searchKey.isNullOrBlank() -> appDb.bookChapterDao.observeByBook(viewModel.bookUrl) - else -> appDb.bookChapterDao.liveDataSearch(viewModel.bookUrl, searchKey) + searchKey.isNullOrBlank() -> appDb.bookChapterDao.flowByBook(viewModel.bookUrl) + else -> appDb.bookChapterDao.flowSearch(viewModel.bookUrl, searchKey) }.collect { adapter.setItems(it) if (searchKey.isNullOrBlank() && !scrollToDurChapter) { diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/BookshelfFragment1.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/BookshelfFragment1.kt index ea76c3efc..4de3d4276 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/BookshelfFragment1.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/BookshelfFragment1.kt @@ -79,7 +79,7 @@ class BookshelfFragment1 : BaseBookshelfFragment(R.layout.fragment_bookshelf), private fun initBookGroupData() { lifecycleScope.launch { - appDb.bookGroupDao.liveDataShow().collect { + appDb.bookGroupDao.flowShow().collect { viewModel.checkGroup(it) upGroup(it) } diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt index 8fd8b4199..aad90e0fa 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt @@ -107,11 +107,11 @@ class BooksFragment : BaseFragment(R.layout.fragment_books), booksFlowJob?.cancel() booksFlowJob = lifecycleScope.launch { when (groupId) { - AppConst.bookGroupAllId -> appDb.bookDao.observeAll() - AppConst.bookGroupLocalId -> appDb.bookDao.observeLocal() - AppConst.bookGroupAudioId -> appDb.bookDao.observeAudio() - AppConst.bookGroupNoneId -> appDb.bookDao.observeNoGroup() - else -> appDb.bookDao.observeByGroup(groupId) + AppConst.bookGroupAllId -> appDb.bookDao.flowAll() + AppConst.bookGroupLocalId -> appDb.bookDao.flowLocal() + AppConst.bookGroupAudioId -> appDb.bookDao.flowAudio() + AppConst.bookGroupNoneId -> appDb.bookDao.flowNoGroup() + else -> appDb.bookDao.flowByGroup(groupId) }.collect { list -> binding.tvEmptyMsg.isGone = list.isNotEmpty() val books = when (getPrefInt(PreferKey.bookshelfSort)) { diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt index 5b8953317..d6b0cdee6 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt @@ -123,7 +123,7 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1), private fun initGroupData() { lifecycleScope.launch { - appDb.bookGroupDao.liveDataShow().collect { + appDb.bookGroupDao.flowShow().collect { if (it != bookGroups) { bookGroups = it booksAdapter.notifyDataSetChanged() @@ -136,11 +136,11 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1), booksFlowJob?.cancel() booksFlowJob = lifecycleScope.launch { when (groupId) { - AppConst.bookGroupAllId -> appDb.bookDao.observeAll() - AppConst.bookGroupLocalId -> appDb.bookDao.observeLocal() - AppConst.bookGroupAudioId -> appDb.bookDao.observeAudio() - AppConst.bookGroupNoneId -> appDb.bookDao.observeNoGroup() - else -> appDb.bookDao.observeByGroup(groupId) + AppConst.bookGroupAllId -> appDb.bookDao.flowAll() + AppConst.bookGroupLocalId -> appDb.bookDao.flowLocal() + AppConst.bookGroupAudioId -> appDb.bookDao.flowAudio() + AppConst.bookGroupNoneId -> appDb.bookDao.flowNoGroup() + else -> appDb.bookDao.flowByGroup(groupId) }.collect { list -> binding.tvEmptyMsg.isGone = list.isNotEmpty() books = when (getPrefInt(PreferKey.bookshelfSort)) { diff --git a/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt b/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt index 630627b39..9b88a77b8 100644 --- a/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt @@ -103,7 +103,7 @@ class ExploreFragment : VMBaseFragment(R.layout.fragment_explo private fun initGroupData() { lifecycleScope.launch { - appDb.bookSourceDao.liveExploreGroup() + appDb.bookSourceDao.flowExploreGroup() .collect { groups.clear() it.map { group -> @@ -119,14 +119,14 @@ class ExploreFragment : VMBaseFragment(R.layout.fragment_explo exploreFlowJob = lifecycleScope.launch { val exploreFlow = when { searchKey.isNullOrBlank() -> { - appDb.bookSourceDao.liveExplore() + appDb.bookSourceDao.flowExplore() } searchKey.startsWith("group:") -> { val key = searchKey.substringAfter("group:") - appDb.bookSourceDao.liveGroupExplore("%$key%") + appDb.bookSourceDao.flowGroupExplore("%$key%") } else -> { - appDb.bookSourceDao.liveExplore("%$searchKey%") + appDb.bookSourceDao.flowExplore("%$searchKey%") } } exploreFlow.collect { diff --git a/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt b/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt index 5cfad3b31..916583edc 100644 --- a/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt @@ -122,7 +122,7 @@ class RssFragment : VMBaseFragment(R.layout.fragment_rss), private fun initGroupData() { lifecycleScope.launch { - appDb.rssSourceDao.liveGroup().collect { + appDb.rssSourceDao.flowGroup().collect { groups.clear() it.map { group -> groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) @@ -136,12 +136,12 @@ class RssFragment : VMBaseFragment(R.layout.fragment_rss), rssFlowJob?.cancel() rssFlowJob = lifecycleScope.launch { when { - searchKey.isNullOrEmpty() -> appDb.rssSourceDao.liveEnabled() + searchKey.isNullOrEmpty() -> appDb.rssSourceDao.flowEnabled() searchKey.startsWith("group:") -> { val key = searchKey.substringAfter("group:") - appDb.rssSourceDao.liveEnabledByGroup("%$key%") + appDb.rssSourceDao.flowEnabledByGroup("%$key%") } - else -> appDb.rssSourceDao.liveEnabled("%$searchKey%") + else -> appDb.rssSourceDao.flowEnabled("%$searchKey%") }.collect { adapter.setItems(it) } diff --git a/app/src/main/java/io/legado/app/ui/replace/GroupManageDialog.kt b/app/src/main/java/io/legado/app/ui/replace/GroupManageDialog.kt index 64c04a6d7..cd3dd5b7c 100644 --- a/app/src/main/java/io/legado/app/ui/replace/GroupManageDialog.kt +++ b/app/src/main/java/io/legado/app/ui/replace/GroupManageDialog.kt @@ -70,7 +70,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener { private fun initData() { lifecycleScope.launch { - appDb.replaceRuleDao.liveGroup().collect { + appDb.replaceRuleDao.flowGroup().collect { val groups = linkedSetOf() it.map { group -> groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) diff --git a/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt b/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt index a2204038f..5b17433da 100644 --- a/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt +++ b/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt @@ -180,14 +180,14 @@ class ReplaceRuleActivity : VMBaseActivity { - appDb.replaceRuleDao.liveDataAll() + appDb.replaceRuleDao.flowAll() } searchKey.startsWith("group:") -> { val key = searchKey.substringAfter("group:") - appDb.replaceRuleDao.liveDataGroupSearch("%$key%") + appDb.replaceRuleDao.flowGroupSearch("%$key%") } else -> { - appDb.replaceRuleDao.liveDataSearch("%$searchKey%") + appDb.replaceRuleDao.flowSearch("%$searchKey%") } }.collect { if (dataInit) { @@ -201,7 +201,7 @@ class ReplaceRuleActivity : VMBaseActivity groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) diff --git a/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesFragment.kt b/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesFragment.kt index 737a93dba..e80515cb9 100644 --- a/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesFragment.kt +++ b/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesFragment.kt @@ -98,7 +98,7 @@ class RssArticlesFragment : VMBaseFragment(R.layout.fragme val rssUrl = activityViewModel.url ?: return articlesFlowJob?.cancel() articlesFlowJob = lifecycleScope.launch { - appDb.rssArticleDao.liveByOriginSort(rssUrl, viewModel.sortName).collect { + appDb.rssArticleDao.flowByOriginSort(rssUrl, viewModel.sortName).collect { adapter.setItems(it) } } diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt index 6da833a55..52186b6ac 100644 --- a/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt +++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt @@ -70,7 +70,7 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener private fun initData() { lifecycleScope.launch { - appDb.rssSourceDao.liveGroup().collect { + appDb.rssSourceDao.flowGroup().collect { val groups = linkedSetOf() it.map { group -> groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt index 96a1e9293..c16cf2667 100644 --- a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt @@ -180,7 +180,7 @@ class RssSourceActivity : VMBaseActivity groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) @@ -234,14 +234,14 @@ class RssSourceActivity : VMBaseActivity { - appDb.rssSourceDao.liveAll() + appDb.rssSourceDao.flowAll() } searchKey.startsWith("group:") -> { val key = searchKey.substringAfter("group:") - appDb.rssSourceDao.liveGroupSearch("%$key%") + appDb.rssSourceDao.flowGroupSearch("%$key%") } else -> { - appDb.rssSourceDao.liveSearch("%$searchKey%") + appDb.rssSourceDao.flowSearch("%$searchKey%") } }.collect { adapter.setItems(it, adapter.diffItemCallback) diff --git a/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubActivity.kt b/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubActivity.kt index c81b5a387..a54fef5ed 100644 --- a/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubActivity.kt @@ -64,7 +64,7 @@ class RuleSubActivity : BaseActivity(), private fun initData() { lifecycleScope.launch { - appDb.ruleSubDao.observeAll().collect { + appDb.ruleSubDao.flowAll().collect { binding.tvEmptyMsg.isGone = it.isNotEmpty() adapter.setItems(it) } From b9967c52bac96b53af865bbb48943ab92fd851d5 Mon Sep 17 00:00:00 2001 From: gedoor Date: Wed, 21 Jul 2021 14:44:37 +0800 Subject: [PATCH 38/40] =?UTF-8?q?=E8=8E=B7=E5=8F=96zip=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=88=A4=E6=96=AD=E7=BC=96=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/help/JsExtensions.kt | 29 +++++-------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/JsExtensions.kt b/app/src/main/java/io/legado/app/help/JsExtensions.kt index eb0f4cda8..60580566f 100644 --- a/app/src/main/java/io/legado/app/help/JsExtensions.kt +++ b/app/src/main/java/io/legado/app/help/JsExtensions.kt @@ -19,6 +19,7 @@ import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.File import java.net.URLEncoder +import java.nio.charset.Charset import java.util.* import java.util.zip.ZipEntry import java.util.zip.ZipInputStream @@ -319,28 +320,14 @@ interface JsExtensions { * @return zip指定文件的数据 */ fun getZipStringContent(url: String, path: String): String { - val bytes = if (url.startsWith("http://") || url.startsWith("https://")) { - runBlocking { - return@runBlocking okHttpClient.newCall { url(url) }.bytes() - } - } else { - StringUtils.hexStringToByte(url) - } - val bos = ByteArrayOutputStream() - val zis = ZipInputStream(ByteArrayInputStream(bytes)) - - var entry: ZipEntry? = zis.nextEntry - - while (entry != null) { - if (entry.name.equals(path)) { - zis.use { it.copyTo(bos) } - return bos.toString() - } - entry = zis.nextEntry - } - Debug.log("getZipContent 未发现内容") + val byteArray = getZipByteArrayContent(url, path) ?: return "" + val charsetName = EncodingDetect.getEncode(byteArray) + return String(byteArray, Charset.forName(charsetName)) + } - return "" + fun getZipStringContent(url: String, path: String, charsetName: String): String { + val byteArray = getZipByteArrayContent(url, path) ?: return "" + return String(byteArray, Charset.forName(charsetName)) } /** From 1f761a5713391ef4a997bdb2ba84933a0f49166f Mon Sep 17 00:00:00 2001 From: gedoor Date: Wed, 21 Jul 2021 17:31:13 +0800 Subject: [PATCH 39/40] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E5=AF=BC=E5=85=A5url?= =?UTF-8?q?=E4=B8=BA=20legado://import/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 26 ++++++ .../app/receiver/SharedReceiverActivity.kt | 4 +- .../association/ImportBookSourceActivity.kt | 2 +- .../association/ImportReplaceRuleActivity.kt | 2 +- .../ui/association/ImportRssSourceActivity.kt | 2 +- .../ui/association/OnLineImportActivity.kt | 82 +++++++++++++++++++ 6 files changed, 113 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/io/legado/app/ui/association/OnLineImportActivity.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4cae9d17d..fea75c6a4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -319,6 +319,32 @@ + + + + + + + + + + + + + + + + + + + { + intent.action == Intent.ACTION_SEND && intent.type == receivingType -> { intent.getStringExtra(Intent.EXTRA_TEXT)?.let { dispose(it) } } Build.VERSION.SDK_INT >= Build.VERSION_CODES.M - && Intent.ACTION_PROCESS_TEXT == intent.action + && intent.action == Intent.ACTION_PROCESS_TEXT && intent.type == receivingType -> { intent.getStringExtra(Intent.EXTRA_PROCESS_TEXT)?.let { dispose(it) diff --git a/app/src/main/java/io/legado/app/ui/association/ImportBookSourceActivity.kt b/app/src/main/java/io/legado/app/ui/association/ImportBookSourceActivity.kt index 9884a768d..18b385627 100644 --- a/app/src/main/java/io/legado/app/ui/association/ImportBookSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/association/ImportBookSourceActivity.kt @@ -60,7 +60,7 @@ class ImportBookSourceActivity : finish() } } - } + } ?: finish() } private fun errorDialog(msg: String) { diff --git a/app/src/main/java/io/legado/app/ui/association/ImportReplaceRuleActivity.kt b/app/src/main/java/io/legado/app/ui/association/ImportReplaceRuleActivity.kt index d77256698..49439f76b 100644 --- a/app/src/main/java/io/legado/app/ui/association/ImportReplaceRuleActivity.kt +++ b/app/src/main/java/io/legado/app/ui/association/ImportReplaceRuleActivity.kt @@ -59,7 +59,7 @@ class ImportReplaceRuleActivity : finish() } } - } + } ?: finish() } private fun errorDialog(msg: String) { diff --git a/app/src/main/java/io/legado/app/ui/association/ImportRssSourceActivity.kt b/app/src/main/java/io/legado/app/ui/association/ImportRssSourceActivity.kt index 651aa82c2..0ee8a4126 100644 --- a/app/src/main/java/io/legado/app/ui/association/ImportRssSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/association/ImportRssSourceActivity.kt @@ -59,7 +59,7 @@ class ImportRssSourceActivity : finish() } } - } + } ?: finish() } private fun errorDialog(msg: String) { diff --git a/app/src/main/java/io/legado/app/ui/association/OnLineImportActivity.kt b/app/src/main/java/io/legado/app/ui/association/OnLineImportActivity.kt new file mode 100644 index 000000000..f0062270a --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/association/OnLineImportActivity.kt @@ -0,0 +1,82 @@ +package io.legado.app.ui.association + +import android.os.Bundle +import androidx.activity.viewModels +import io.legado.app.R +import io.legado.app.base.BaseActivity +import io.legado.app.constant.Theme +import io.legado.app.databinding.ActivityTranslucenceBinding +import io.legado.app.lib.dialogs.alert +import io.legado.app.utils.startActivity +import io.legado.app.utils.viewbindingdelegate.viewBinding + +class OnLineImportActivity : BaseActivity(theme = Theme.Transparent) { + + override val binding by viewBinding(ActivityTranslucenceBinding::inflate) + + override fun onActivityCreated(savedInstanceState: Bundle?) { + intent.data?.let { + when (it.path) { + "bookSource" -> it.getQueryParameter("src")?.let { url -> + importBookSource(url) + } + "rssSource" -> it.getQueryParameter("src")?.let { url -> + importRssSource(url) + } + "replaceRule" -> it.getQueryParameter("src")?.let { url -> + startActivity { + putExtra("source", url) + } + } + else -> { + } + } + } + } + + private fun importBookSource(url: String) { + val viewModel by viewModels() + binding.rotateLoading.show() + viewModel.errorLiveData.observe(this, { + binding.rotateLoading.hide() + errorDialog(it) + }) + viewModel.successLiveData.observe(this, { + binding.rotateLoading.hide() + if (it > 0) { + ImportBookSourceDialog().show(supportFragmentManager, "SourceDialog") + } else { + errorDialog(getString(R.string.wrong_format)) + } + }) + viewModel.importSource(url) + } + + private fun importRssSource(url: String) { + val viewModel by viewModels() + binding.rotateLoading.show() + viewModel.errorLiveData.observe(this, { + binding.rotateLoading.hide() + errorDialog(it) + }) + viewModel.successLiveData.observe(this, { + binding.rotateLoading.hide() + if (it > 0) { + ImportRssSourceDialog().show(supportFragmentManager, "SourceDialog") + } else { + errorDialog(getString(R.string.wrong_format)) + } + }) + viewModel.importSource(url) + } + + private fun errorDialog(msg: String) { + alert(getString(R.string.error), msg) { + okButton { } + onDismiss { + finish() + } + }.show() + } + +} \ No newline at end of file From 55efc2e67afc7fd0aa9eedb3a95f2740d0852367 Mon Sep 17 00:00:00 2001 From: gedoor Date: Wed, 21 Jul 2021 18:16:06 +0800 Subject: [PATCH 40/40] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/association/ImportReplaceRuleDialog.kt | 71 +++++++++++++++++++ .../association/ImportReplaceRuleViewModel.kt | 13 +++- .../ui/association/ImportRssSourceDialog.kt | 2 +- app/src/main/res/values-es-rES/strings.xml | 2 +- app/src/main/res/values-ja-rJP/strings.xml | 2 +- app/src/main/res/values-pt-rBR/strings.xml | 2 +- app/src/main/res/values-zh-rHK/strings.xml | 2 +- app/src/main/res/values-zh-rTW/strings.xml | 2 +- app/src/main/res/values-zh/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 10 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/io/legado/app/ui/association/ImportReplaceRuleDialog.kt diff --git a/app/src/main/java/io/legado/app/ui/association/ImportReplaceRuleDialog.kt b/app/src/main/java/io/legado/app/ui/association/ImportReplaceRuleDialog.kt new file mode 100644 index 000000000..03cda5066 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/association/ImportReplaceRuleDialog.kt @@ -0,0 +1,71 @@ +package io.legado.app.ui.association + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.viewModels +import androidx.recyclerview.widget.LinearLayoutManager +import io.legado.app.R +import io.legado.app.base.BaseDialogFragment +import io.legado.app.base.adapter.ItemViewHolder +import io.legado.app.base.adapter.RecyclerAdapter +import io.legado.app.data.entities.ReplaceRule +import io.legado.app.databinding.DialogRecyclerViewBinding +import io.legado.app.databinding.ItemSourceImportBinding +import io.legado.app.utils.viewbindingdelegate.viewBinding + +class ImportReplaceRuleDialog : BaseDialogFragment() { + + private val binding by viewBinding(DialogRecyclerViewBinding::bind) + private val viewModel by viewModels() + lateinit var adapter: SourcesAdapter + + override fun onStart() { + super.onStart() + dialog?.window?.setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.dialog_recycler_view, container) + } + + override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { + binding.toolBar.setTitle(R.string.import_replace_rule) + adapter = SourcesAdapter(requireContext()) + binding.recyclerView.layoutManager = LinearLayoutManager(requireContext()) + binding.recyclerView.adapter = adapter + adapter.setItems(viewModel.allRules) + } + + inner class SourcesAdapter(context: Context) : + RecyclerAdapter(context) { + + override fun getViewBinding(parent: ViewGroup): ItemSourceImportBinding { + return ItemSourceImportBinding.inflate(inflater, parent, false) + } + + override fun convert( + holder: ItemViewHolder, + binding: ItemSourceImportBinding, + item: ReplaceRule, + payloads: MutableList + ) { + TODO("Not yet implemented") + } + + override fun registerListener(holder: ItemViewHolder, binding: ItemSourceImportBinding) { + TODO("Not yet implemented") + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/association/ImportReplaceRuleViewModel.kt b/app/src/main/java/io/legado/app/ui/association/ImportReplaceRuleViewModel.kt index 8d6da537e..4dba9c35d 100644 --- a/app/src/main/java/io/legado/app/ui/association/ImportReplaceRuleViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/association/ImportReplaceRuleViewModel.kt @@ -14,7 +14,9 @@ class ImportReplaceRuleViewModel(app: Application) : BaseViewModel(app) { val errorLiveData = MutableLiveData() val successLiveData = MutableLiveData>() - private val allRules = arrayListOf() + val allRules = arrayListOf() + val checkRules = arrayListOf() + val selectStatus = arrayListOf() fun import(text: String) { execute { @@ -32,7 +34,16 @@ class ImportReplaceRuleViewModel(app: Application) : BaseViewModel(app) { }.onError { errorLiveData.postValue(it.localizedMessage ?: "ERROR") }.onSuccess { + comparisonSource() successLiveData.postValue(allRules) } } + + private fun comparisonSource() { + execute { + allRules.forEach { + + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/association/ImportRssSourceDialog.kt b/app/src/main/java/io/legado/app/ui/association/ImportRssSourceDialog.kt index 6a7c9b844..67cf3b348 100644 --- a/app/src/main/java/io/legado/app/ui/association/ImportRssSourceDialog.kt +++ b/app/src/main/java/io/legado/app/ui/association/ImportRssSourceDialog.kt @@ -37,7 +37,7 @@ import io.legado.app.utils.visible class ImportRssSourceDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener { private val binding by viewBinding(DialogRecyclerViewBinding::bind) - val viewModel: ImportRssSourceViewModel by activityViewModels() + private val viewModel by activityViewModels() lateinit var adapter: SourcesAdapter override fun onStart() { diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml index 18283fdb8..359674384 100644 --- a/app/src/main/res/values-es-rES/strings.xml +++ b/app/src/main/res/values-es-rES/strings.xml @@ -234,7 +234,7 @@ Descripción: sin introducción Abrir libro externo Origen: %s - Importar reglas locales + Importar reglas replace Importar reglas en línea Intervalo de actualizaciones Lista de recientes diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index 961d104c5..7ae49bac5 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -234,7 +234,7 @@ Description: no introduction Open external book Origin: %s - Import local rules + Import replace rules Import online rules Check interval for updates By recent list diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index efde516ba..6bb5db968 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -234,7 +234,7 @@ Descrição: sem introdução Abrir o livro externo Origem: %s - Importar regras locais + Import replace rule Importar regras on-line Intervalo de atualizações Lista de recentes diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 05c592dc2..0aaccd421 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -231,7 +231,7 @@ 簡介: 暫無簡介 打開外部書籍 來源: %s - 本地導入 + 導入替換規則 導入在線規則 檢查更新間隔 按閲讀時間 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index b781f4ee1..e042521bc 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -233,7 +233,7 @@ 簡介: 暫無簡介 打開外部書籍 來源: %s - 本機匯入 + 匯入替換規則 匯入線上規則 檢查更新間隔 按閱讀時間 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 509525cbc..321411476 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -233,7 +233,7 @@ 简介: 暂无简介 打开外部书籍 来源: %s - 本地导入 + 导入替换规则 导入在线规则 检查更新间隔 按阅读时间 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 722ac6550..b3c6e628f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -234,7 +234,7 @@ Description: no introduction Open external book Origin: %s - Import local rules + Import replace rules Import online rules Check interval for updates By recent list