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..72f0785d2 --- /dev/null +++ b/JsoupXpath/build.gradle @@ -0,0 +1,44 @@ +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 { + 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') + 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/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 6d8529cd8..2fd7672db 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 { @@ -105,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 { @@ -121,38 +122,37 @@ kapt { } dependencies { - implementation project(path: ':epublib') - 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,59 @@ 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") + implementation("androidx.room:room-ktx:$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' - //noinspection GradleDependency - implementation 'cn.wanghaomiao:JsoupXpath:2.4.3' - implementation 'com.jayway.jsonpath:json-path:2.6.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' + 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') } 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 @@ + + + + + + + + + + + + + + + + + + + - - - - - - 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"}); 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 } 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/BookChapterDao.kt b/app/src/main/java/io/legado/app/data/dao/BookChapterDao.kt index 0041591e3..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 @@ -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 flowByBook(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 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 9a2a5a0db..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 @@ -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 flowAll(): Flow> @Query("SELECT * FROM books WHERE type = ${BookType.audio}") - fun observeAudio(): LiveData> + fun flowAudio(): Flow> @Query("SELECT * FROM books WHERE origin = '${BookType.local}'") - fun observeLocal(): LiveData> + 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(): LiveData> + fun flowNoGroup(): Flow> @Query("SELECT bookUrl FROM books WHERE origin = '${BookType.local}'") - fun observeLocalUri(): LiveData> + fun flowLocalUri(): Flow> @Query("SELECT * FROM books WHERE (`group` & :group) > 0") - fun observeByGroup(group: Long): LiveData> + fun flowByGroup(group: Long): Flow> @Query("SELECT * FROM books WHERE name like '%'||:key||'%' or author like '%'||:key||'%'") - fun liveDataSearch(key: String): LiveData> + 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 34e6a3e88..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 @@ -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 flowAll(): 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 flowShow(): Flow> @Query("SELECT * FROM book_groups where groupId >= 0 ORDER BY `order`") - fun liveDataSelect(): LiveData> + 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 a9d113098..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 @@ -1,76 +1,69 @@ 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 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): LiveData> + fun flowSearch(searchKey: String): Flow> @Query("select * from book_sources where bookSourceGroup like :searchKey order by customOrder asc") - fun liveDataGroupSearch(searchKey: String): LiveData> + fun flowGroupSearch(searchKey: String): Flow> @Query("select * from book_sources where enabled = 1 order by customOrder asc") - fun liveDataEnabled(): LiveData> + fun flowEnabled(): Flow> @Query("select * from book_sources where enabled = 0 order by customOrder asc") - fun liveDataDisabled(): LiveData> + fun flowDisabled(): Flow> @Query("select * from book_sources where enabledExplore = 1 and trim(exploreUrl) <> '' order by customOrder asc") - fun liveExplore(): LiveData> + 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): LiveData> + 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): LiveData> + fun flowGroupExplore(key: String): Flow> @Query("select distinct bookSourceGroup from book_sources where trim(bookSourceGroup) <> ''") - fun liveGroup(): LiveData> + fun flowGroup(): Flow> @Query("select distinct bookSourceGroup from book_sources where enabled = 1 and trim(bookSourceGroup) <> ''") - fun liveGroupEnabled(): LiveData> + 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(): LiveData> + 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 4ce8b05bb..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 @@ -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 @@ -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 - ): LiveData> + @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 - ): LiveData> + 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 f97c17af1..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 @@ -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 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 2a0e1149c..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 @@ -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 flowAll(): Flow> @Query("SELECT * FROM replace_rules where `group` like :key or name like :key ORDER BY sortOrder ASC") - fun liveDataSearch(key: String): LiveData> + fun flowSearch(key: String): Flow> @Query("SELECT * FROM replace_rules where `group` like :key ORDER BY sortOrder ASC") - fun liveDataGroupSearch(key: String): LiveData> + fun flowGroupSearch(key: String): Flow> + + @Query("select `group` from replace_rules where `group` is not null and `group` <> ''") + fun flowGroup(): Flow> @get:Query("SELECT MIN(sortOrder) FROM replace_rules") val minOrder: Int @@ -36,17 +39,12 @@ 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 - @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/RssArticleDao.kt b/app/src/main/java/io/legado/app/data/dao/RssArticleDao.kt index c05dcb880..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 @@ -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 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 deaa45726..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 @@ -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,30 @@ interface RssSourceDao { val size: Int @Query("SELECT * FROM rssSources order by customOrder") - fun liveAll(): LiveData> + 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): LiveData> + fun flowSearch(key: String): Flow> @Query("SELECT * FROM rssSources where sourceGroup like :key order by customOrder") - fun liveGroupSearch(key: String): LiveData> + fun flowGroupSearch(key: String): Flow> @Query("SELECT * FROM rssSources where enabled = 1 order by customOrder") - fun liveEnabled(): LiveData> + fun flowEnabled(): 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): LiveData> + @Query( + """SELECT * FROM rssSources + where enabled = 1 + and (sourceName like :searchKey or sourceGroup like :searchKey or sourceUrl like :searchKey) + order by customOrder""" + ) + fun flowEnabled(searchKey: String): Flow> @Query("SELECT * FROM rssSources where enabled = 1 and sourceGroup like :searchKey order by customOrder") - fun liveEnabledByGroup(searchKey: String): LiveData> + fun flowEnabledByGroup(searchKey: String): Flow> @Query("select distinct sourceGroup from rssSources where trim(sourceGroup) <> ''") - fun liveGroup(): LiveData> + 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/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/data/dao/RuleSubDao.kt b/app/src/main/java/io/legado/app/data/dao/RuleSubDao.kt index d5aea29ad..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 @@ -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 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 d3063759a..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 @@ -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 flowByUsage(): Flow> @Query("SELECT * FROM search_keywords ORDER BY lastUseTime DESC") - fun liveDataByTime(): LiveData> + fun flowByTime(): Flow> @Query("SELECT * FROM search_keywords where word like '%'||:key||'%' ORDER BY usage DESC") - fun liveDataSearch(key: String): LiveData> + 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/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/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/help/JsExtensions.kt b/app/src/main/java/io/legado/app/help/JsExtensions.kt index 56326e382..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 @@ -85,6 +86,8 @@ interface JsExtensions { /** * 实现16进制字符串转文件 + * @param content 需要转成文件的16进制字符串 + * @param url 通过url里的参数来判断文件类型 * @return 相对路径 */ fun downloadFile(content: String, url: String): String { @@ -312,40 +315,34 @@ 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 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)) } /** * 获取网络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)) 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/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt index f9e0024c0..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 @@ -66,7 +66,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { 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) + redirectUrl = URL(if (urlMatcher.find()) url.substring(0, urlMatcher.start()) else url) } return redirectUrl } @@ -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) @@ -395,8 +397,10 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { * 规则类 */ - inner class SourceRule internal constructor(ruleStr: String, mainMode: Mode = Mode.Default) { - internal var mode = mainMode + inner class SourceRule internal constructor( + ruleStr: String, + internal var mode: Mode = Mode.Default + ) { internal var rule: String internal var replaceRegex = "" internal var replacement = "" @@ -410,9 +414,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { init { rule = when { - mode == Mode.Js || mode == Mode.Regex -> { //splitSourceRule预先确定了Mode.Js和Mode.Regex两种mode,避免被后面的值覆盖先进行判断 - ruleStr - } + mode == Mode.Js || mode == Mode.Regex -> ruleStr ruleStr.startsWith("@CSS:", true) -> { mode = Mode.Default ruleStr @@ -433,13 +435,11 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { mode = Mode.Json ruleStr.substring(6) } - ruleStr.startsWith("$.") || ruleStr.startsWith("$[") || isJSON -> { + isJSON || ruleStr.startsWith("$.") || ruleStr.startsWith("$[") -> { mode = Mode.Json ruleStr } - else -> { - ruleStr - } + else -> ruleStr } //分离put rule = splitPutRule(rule, putMap) 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..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 @@ -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("<(.*?)>") } @@ -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 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) diff --git a/app/src/main/java/io/legado/app/receiver/SharedReceiverActivity.kt b/app/src/main/java/io/legado/app/receiver/SharedReceiverActivity.kt index c445ed0f0..c5867c899 100644 --- a/app/src/main/java/io/legado/app/receiver/SharedReceiverActivity.kt +++ b/app/src/main/java/io/legado/app/receiver/SharedReceiverActivity.kt @@ -21,13 +21,13 @@ class SharedReceiverActivity : AppCompatActivity() { private fun initIntent() { when { - Intent.ACTION_SEND == intent.action && intent.type == receivingType -> { + 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/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/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/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/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/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/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/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/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/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 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..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 @@ -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 @@ -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 @@ -44,14 +46,13 @@ class ArrangeBookActivity : VMBaseActivity>? = null - private var booksLiveData: LiveData>? = 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) @@ -107,37 +108,37 @@ 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) + 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 } + 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..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 @@ -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 @@ -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 @@ -44,8 +46,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() @@ -66,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) @@ -147,44 +148,45 @@ 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 = lifecycleScope.launch { + when (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() + } + 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() { - groupLiveData?.removeObservers(this) - groupLiveData = appDb.bookGroupDao.liveDataAll() - groupLiveData?.observe(this, { - groupList.clear() - groupList.addAll(it) - adapter.notifyDataSetChanged() - upMenu() - }) + lifecycleScope.launch { + appDb.bookGroupDao.flowAll().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/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/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 e83118039..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 @@ -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 @@ -22,6 +23,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 +151,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)) + lifecycleScope.launch { + appDb.bookSourceDao.flowGroupEnabled().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/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..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 @@ -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.flowAll().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..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 @@ -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.flowSelect().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 5d45d4795..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 @@ -9,7 +9,7 @@ import android.view.MenuItem import androidx.activity.viewModels import androidx.appcompat.widget.PopupMenu import androidx.documentfile.provider.DocumentFile -import androidx.lifecycle.LiveData +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import io.legado.app.R import io.legado.app.base.VMBaseActivity @@ -27,6 +27,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 +47,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 +125,11 @@ 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/SpeakEngineDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/SpeakEngineDialog.kt index 233eef978..815ab2377 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.flowAll().collect { + adapter.setItems(it) + } + } } override fun onMenuItemClick(item: MenuItem?): Boolean { 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..2179e7aed 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 @@ -10,7 +10,7 @@ import android.view.ViewGroup 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 @@ -32,13 +32,13 @@ import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.utils.* import io.legado.app.utils.viewbindingdelegate.viewBinding import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import java.util.* class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener { private val importTocRuleKey = "tocRuleUrl" private lateinit var adapter: TocRegexAdapter - private var tocRegexLiveData: LiveData>? = null var selectedName: String? = null private var durRegex: String? = null private val viewModel: TocRegexViewModel by viewModels() @@ -92,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) { @@ -222,7 +222,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 +233,7 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener { } ivDelete.setOnClickListener { getItem(holder.layoutPosition)?.let { item -> - launch(IO) { + lifecycleScope.launch(IO) { appDb.txtTocRuleDao.delete(item) } } @@ -255,7 +255,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 e5ad0991c..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 @@ -10,7 +10,7 @@ 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 import com.google.android.flexbox.FlexboxLayoutManager @@ -30,6 +30,8 @@ import io.legado.app.ui.widget.recycler.LoadMoreView import io.legado.app.utils.* 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 @@ -46,8 +48,8 @@ class SearchActivity : VMBaseActivity>? = null - private var bookData: LiveData>? = null + private var historyFlowJob: Job? = 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() { + lifecycleScope.launch { + appDb.bookSourceDao.flowGroupEnabled().collect { + groups.clear() + it.map { group -> + groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + } + upGroupMenu() } - upGroupMenu() - }) + } viewModel.searchBookLiveData.observe(this, { upSearchItems(it) }) @@ -268,38 +272,39 @@ class SearchActivity : VMBaseActivity appDb.searchKeywordDao.flowByUsage() + else -> appDb.searchKeywordDao.flowSearch(key) + }.collect { + historyKeyAdapter.setItems(it) + if (it.isEmpty()) { + binding.tvClearHistory.invisible() + } else { + binding.tvClearHistory.visible() + } } - }) + } } /** @@ -354,7 +359,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/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/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 75cfb5124..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 @@ -10,7 +10,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 com.google.android.material.snackbar.Snackbar import io.legado.app.R @@ -40,6 +40,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 class BookSourceActivity : VMBaseActivity(), @@ -52,7 +55,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 +97,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 +202,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 = lifecycleScope.launch { + when { + searchKey.isNullOrEmpty() -> { + appDb.bookSourceDao.flowAll() + } + searchKey == getString(R.string.enabled) -> { + appDb.bookSourceDao.flowEnabled() + } + searchKey == getString(R.string.disabled) -> { + appDb.bookSourceDao.flowDisabled() + } + searchKey.startsWith("group:") -> { + val key = searchKey.substringAfter("group:") + appDb.bookSourceDao.flowGroupSearch("%$key%") + } + else -> { + appDb.bookSourceDao.flowSearch("%$searchKey%") + } + }.collect { data -> val sourceList = if (sortAscending) when (sort) { Sort.Weight -> data.sortedBy { it.weight } @@ -254,7 +257,7 @@ class BookSourceActivity : VMBaseActivity data.reversed() } adapter.setItems(sourceList, adapter.diffItemCallback) - }) + } } } @@ -273,13 +276,16 @@ class BookSourceActivity : VMBaseActivity - groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) - } - upGroupMenu() - }) + lifecycleScope.launch { + appDb.bookSourceDao.flowGroup() + .collect { + groups.clear() + it.forEach { group -> + groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + } + upGroupMenu() + } + } } override fun selectAll(selectAll: Boolean) { @@ -453,7 +459,7 @@ class BookSourceActivity : VMBaseActivity() - it.map { group -> - groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + lifecycleScope.launch { + appDb.bookSourceDao.flowGroup().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/book/toc/BookmarkFragment.kt b/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt index bac6fc8d2..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 @@ -5,12 +5,11 @@ import android.content.Intent import android.os.Bundle import android.view.View import androidx.fragment.app.activityViewModels -import androidx.lifecycle.LiveData +import androidx.lifecycle.lifecycleScope 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 +19,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 +30,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 +48,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 = lifecycleScope.launch { + when { + 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 417b740ca..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 @@ -6,7 +6,7 @@ import android.content.Intent import android.os.Bundle import android.view.View import androidx.fragment.app.activityViewModels -import androidx.lifecycle.LiveData +import androidx.lifecycle.lifecycleScope import io.legado.app.R import io.legado.app.base.VMBaseFragment import io.legado.app.constant.EventBus @@ -24,6 +24,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 +38,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 { @@ -76,8 +78,8 @@ class ChapterListFragment : VMBaseFragment(R.layout.fragment_chapt @SuppressLint("SetTextI18n") private fun initBook(book: Book) { - launch { - initToc() + lifecycleScope.launch { + upChapterList(null) durChapterIndex = book.durChapterIndex binding.tvCurrentChapterInfo.text = "${book.durChapterTitle}(${book.durChapterIndex + 1}/${book.totalChapterNum})" @@ -85,20 +87,8 @@ 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) { + lifecycleScope.launch(IO) { adapter.cacheFileNames.addAll(BookHelp.getChapterFiles(book)) withContext(Main) { adapter.notifyItemRangeChanged(0, adapter.itemCount, true) @@ -117,15 +107,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 = lifecycleScope.launch { + when { + searchKey.isNullOrBlank() -> appDb.bookChapterDao.flowByBook(viewModel.bookUrl) + else -> appDb.bookChapterDao.flowSearch(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 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..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 @@ -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.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 ea918d46a..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 @@ -4,7 +4,7 @@ import android.os.Bundle import android.view.View import androidx.core.view.isGone import androidx.fragment.app.activityViewModels -import androidx.lifecycle.LiveData +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -29,6 +29,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 +55,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 +104,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 = lifecycleScope.launch { + when (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)) { 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..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 @@ -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 @@ -32,6 +33,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 +50,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() @@ -118,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.flowShow().collect { if (it != bookGroups) { bookGroups = it booksAdapter.notifyDataSetChanged() @@ -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 = lifecycleScope.launch { + when (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)) { 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..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 @@ -8,7 +8,7 @@ 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.lifecycle.lifecycleScope import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -27,6 +27,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 +42,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 +51,7 @@ class ExploreFragment : VMBaseFragment(R.layout.fragment_explo initSearchView() initRecyclerView() initGroupData() - initExploreData() + upExploreData() } override fun onCompatCreateOptionsMenu(menu: Menu) { @@ -76,7 +78,7 @@ class ExploreFragment : VMBaseFragment(R.layout.fragment_explo } override fun onQueryTextChange(newText: String?): Boolean { - initExploreData(newText) + upExploreData(newText) return false } }) @@ -86,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() { @@ -100,38 +102,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() - }) + lifecycleScope.launch { + appDb.bookSourceDao.flowExploreGroup() + .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 = lifecycleScope.launch { + val exploreFlow = when { + searchKey.isNullOrBlank() -> { + appDb.bookSourceDao.flowExplore() + } + searchKey.startsWith("group:") -> { + val key = searchKey.substringAfter("group:") + appDb.bookSourceDao.flowGroupExplore("%$key%") + } + else -> { + appDb.bookSourceDao.flowExplore("%$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 +156,7 @@ class ExploreFragment : VMBaseFragment(R.layout.fragment_explo } override fun refreshData() { - initExploreData(searchView.query?.toString()) + upExploreData(searchView.query?.toString()) } override fun scrollTo(pos: Int) { 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..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 @@ -7,7 +7,7 @@ import android.view.SubMenu import android.view.View import androidx.appcompat.widget.SearchView import androidx.fragment.app.viewModels -import androidx.lifecycle.LiveData +import androidx.lifecycle.lifecycleScope import io.legado.app.R import io.legado.app.base.VMBaseFragment import io.legado.app.constant.AppPattern @@ -29,6 +29,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 +43,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 +53,7 @@ class RssFragment : VMBaseFragment(R.layout.fragment_rss), initSearchView() initRecyclerView() initGroupData() - initData() + upRssFlowJob() } override fun onCompatCreateOptionsMenu(menu: Menu) { @@ -97,7 +99,7 @@ class RssFragment : VMBaseFragment(R.layout.fragment_rss), } override fun onQueryTextChange(newText: String?): Boolean { - initData(newText) + upRssFlowJob(newText) return false } }) @@ -118,32 +120,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() { + lifecycleScope.launch { + appDb.rssSourceDao.flowGroup().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 = lifecycleScope.launch { + when { + searchKey.isNullOrEmpty() -> appDb.rssSourceDao.flowEnabled() + searchKey.startsWith("group:") -> { + val key = searchKey.substringAfter("group:") + appDb.rssSourceDao.flowEnabledByGroup("%$key%") + } + else -> appDb.rssSourceDao.flowEnabled("%$searchKey%") + }.collect { + adapter.setItems(it) } - upGroupsMenu() - }) + } } override fun openRss(rssSource: RssSource) { 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..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 @@ -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.flowGroup().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 2fc737bd9..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 @@ -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 @@ -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 @@ -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.flowAll() + } + searchKey.startsWith("group:") -> { + val key = searchKey.substringAfter("group:") + appDb.replaceRuleDao.flowGroupSearch("%$key%") + } + else -> { + appDb.replaceRuleDao.flowSearch("%$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.flowGroup().collect { + groups.clear() + it.map { group -> + groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + } + upGroupMenu() } - upGroupMenu() - }) + } } override fun onCompatOptionsItemSelected(item: MenuItem): Boolean { @@ -294,7 +299,7 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.fragment_rss_articles), BaseRssArticlesAdapter.CallBack { @@ -42,7 +45,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 +95,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 = lifecycleScope.launch { + appDb.rssArticleDao.flowByOriginSort(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 3f90252b0..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,7 +1,7 @@ package io.legado.app.ui.rss.favorites import android.os.Bundle -import androidx.lifecycle.LiveData +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import io.legado.app.base.BaseActivity import io.legado.app.data.appDb @@ -11,13 +11,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 +36,11 @@ class RssFavoritesActivity : BaseActivity(), } private fun initData() { - liveData?.removeObservers(this) - liveData = appDb.rssStarDao.liveAll() - liveData?.observe(this, { - adapter.setItems(it) - }) + lifecycleScope.launch { + appDb.rssStarDao.liveAll().collect { + adapter.setItems(it) + } + } } override fun readRss(rssStar: RssStar) { 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 0c4286e15..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 @@ -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 @@ -26,6 +27,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 +65,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() { + lifecycleScope.launch { + appDb.rssSourceDao.flowGroup().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..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 @@ -10,7 +10,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 io.legado.app.R import io.legado.app.base.VMBaseActivity @@ -35,6 +35,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 +52,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 +89,8 @@ class RssSourceActivity : VMBaseActivity - groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + private fun initGroupFlow() { + lifecycleScope.launch { + appDb.rssSourceDao.flowGroup().collect { + groups.clear() + it.map { group -> + groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + } + upGroupMenu() } - upGroupMenu() - }) + } } override fun selectAll(selectAll: Boolean) { @@ -224,23 +229,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 = lifecycleScope.launch { + when { + searchKey.isNullOrBlank() -> { + appDb.rssSourceDao.flowAll() + } + searchKey.startsWith("group:") -> { + val key = searchKey.substringAfter("group:") + appDb.rssSourceDao.flowGroupSearch("%$key%") + } + else -> { + 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 652a8c94c..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 @@ -4,7 +4,7 @@ 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 import io.legado.app.base.BaseActivity @@ -21,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 @@ -32,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() @@ -63,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.flowAll().collect { + binding.tvEmptyMsg.isGone = it.isNotEmpty() + adapter.setItems(it) + } } } @@ -100,7 +100,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 +121,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( 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..52763a41f 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 "" @@ -18,33 +22,38 @@ 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 = 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() } } 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 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 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'