From 23f6ce15508eeb327f6e66dc6f2a50068bbcdb2d Mon Sep 17 00:00:00 2001 From: YiFang Xiao Date: Tue, 7 Nov 2023 21:26:20 +0800 Subject: [PATCH 01/24] bump alpha version --- .github/workflows/AutoTag.yml | 2 +- .github/workflows/macos-homebrew-breakpad.yml | 2 +- .github/workflows/macos-homebrew.yml | 2 +- .github/workflows/ubuntu-6.2.yml | 2 +- .github/workflows/ubuntu.yml | 2 +- .github/workflows/windows-6.x.yml | 2 +- .github/workflows/windows.yml | 2 +- CMakeLists.txt | 2 +- goldendict.pro | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/AutoTag.yml b/.github/workflows/AutoTag.yml index f151d5fc..d8fdadc5 100644 --- a/.github/workflows/AutoTag.yml +++ b/.github/workflows/AutoTag.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest env: - version: 23.10.01 + version: 23.11.09 version-suffix: alpha prerelease: true diff --git a/.github/workflows/macos-homebrew-breakpad.yml b/.github/workflows/macos-homebrew-breakpad.yml index dbedcdae..b148e199 100644 --- a/.github/workflows/macos-homebrew-breakpad.yml +++ b/.github/workflows/macos-homebrew-breakpad.yml @@ -26,7 +26,7 @@ jobs: qt_arch: [clang_64] env: targetName: GoldenDict - version: 23.10.01 + version: 23.11.09 version-suffix: alpha prerelease: true steps: diff --git a/.github/workflows/macos-homebrew.yml b/.github/workflows/macos-homebrew.yml index 3522a2d6..bc80de26 100644 --- a/.github/workflows/macos-homebrew.yml +++ b/.github/workflows/macos-homebrew.yml @@ -26,7 +26,7 @@ jobs: qt_arch: [clang_64] env: targetName: GoldenDict - version: 23.10.01 + version: 23.11.09 version-suffix: alpha prerelease: true steps: diff --git a/.github/workflows/ubuntu-6.2.yml b/.github/workflows/ubuntu-6.2.yml index 3f302abd..78a041f2 100644 --- a/.github/workflows/ubuntu-6.2.yml +++ b/.github/workflows/ubuntu-6.2.yml @@ -24,7 +24,7 @@ jobs: qt_ver: [ 6.5.2,6.6.0 ] qt_arch: [gcc_64] env: - version: 23.10.01 + version: 23.11.09 version-suffix: alpha prerelease: true steps: diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 63c150fa..b4d2cf2c 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -25,7 +25,7 @@ jobs: qt_ver: [5.15.2] qt_arch: [gcc_64] env: - version: 23.10.01 + version: 23.11.09 version-suffix: alpha prerelease: true steps: diff --git a/.github/workflows/windows-6.x.yml b/.github/workflows/windows-6.x.yml index 69e7309a..95128601 100644 --- a/.github/workflows/windows-6.x.yml +++ b/.github/workflows/windows-6.x.yml @@ -31,7 +31,7 @@ jobs: qt_arch: [win64_msvc2019_64] env: targetName: GoldenDict.exe - version: 23.10.01 + version: 23.11.09 version-suffix: alpha prerelease: true steps: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 0aba38c1..73c3b7ff 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -27,7 +27,7 @@ jobs: qt_arch: [win64_msvc2019_64] env: targetName: GoldenDict.exe - version: 23.10.01 + version: 23.11.09 version-suffix: alpha prerelease: true # 步骤 diff --git a/CMakeLists.txt b/CMakeLists.txt index b2105d63..fed5e6d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ option(USE_ALTERNATIVE_NAME "Force the name goldendict-ng " OFF) include(FeatureSummary) project(goldendict-ng - VERSION 23.10.01 + VERSION 23.11.09 LANGUAGES CXX C) if (NOT USE_ALTERNATIVE_NAME) diff --git a/goldendict.pro b/goldendict.pro index 91e1a904..5ce531a1 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -1,6 +1,6 @@ TEMPLATE = app TARGET = goldendict -VERSION = 23.10.01 +VERSION = 23.11.09 # Generate version file. We do this here and in a build rule described later. # The build rule is required since qmake isn't run each time the project is @@ -133,7 +133,7 @@ win32 { win32-msvc* { # VS does not recognize 22.number.alpha,cause errors during compilation under MSVC++ - VERSION = 23.10.01 + VERSION = 23.11.09 DEFINES += __WIN32 _CRT_SECURE_NO_WARNINGS contains(QMAKE_TARGET.arch, x86_64) { DEFINES += NOMINMAX __WIN64 From 82d7f1cf76765e301547533cbbf9d6f5e32a356b Mon Sep 17 00:00:00 2001 From: shenleban tongying Date: Tue, 7 Nov 2023 21:23:51 -0500 Subject: [PATCH 02/24] remove `` from metadata.xml It has little to no value and requires unnecessary maintenance. --- redist/io.github.xiaoyifang.goldendict_ng.metainfo.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/redist/io.github.xiaoyifang.goldendict_ng.metainfo.xml b/redist/io.github.xiaoyifang.goldendict_ng.metainfo.xml index d82df10e..1e377798 100644 --- a/redist/io.github.xiaoyifang.goldendict_ng.metainfo.xml +++ b/redist/io.github.xiaoyifang.goldendict_ng.metainfo.xml @@ -41,9 +41,4 @@ org.goldendict_ng.desktop - - - - - From 8c8f40cb53bb7039eb5bd1b1afcbc61883d35753 Mon Sep 17 00:00:00 2001 From: YiFang Xiao Date: Fri, 10 Nov 2023 17:47:04 +0800 Subject: [PATCH 03/24] opt: remove log --- src/ftshelpers.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ftshelpers.cc b/src/ftshelpers.cc index 4d6affd1..cbb9301f 100644 --- a/src/ftshelpers.cc +++ b/src/ftshelpers.cc @@ -31,10 +31,8 @@ bool ftsIndexIsOldOrBad( BtreeIndexing::BtreeDictionary * dict ) auto document = db.get_document( docid ); string const lastDoc = document.get_data(); - bool const notFinished = lastDoc != finish_mark; - qDebug() << dict->ftsIndexName().c_str() << document.get_data().c_str() << notFinished; + return lastDoc != finish_mark; //use a special document to mark the end of the index. - return notFinished; } catch ( Xapian::Error & e ) { qWarning() << e.get_description().c_str(); From b2a8eec2a5c9afb853619e927db5fef7998e6daa Mon Sep 17 00:00:00 2001 From: xiaoyifang <105986+xiaoyifang@users.noreply.github.com> Date: Sat, 11 Nov 2023 16:17:58 +0800 Subject: [PATCH 04/24] fix: dsl encoding detection enhanced. (#1279) * fix: detect dsl encoding * [autofix.ci] apply automated fixes --------- Co-authored-by: YiFang Xiao Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- src/dict/dsl_details.cc | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/dict/dsl_details.cc b/src/dict/dsl_details.cc index cb3b1134..c09eb8e1 100644 --- a/src/dict/dsl_details.cc +++ b/src/dict/dsl_details.cc @@ -12,7 +12,7 @@ #include #include #include - +#include #include namespace Dsl { @@ -832,7 +832,33 @@ DslScanner::DslScanner( string const & fileName ): bool needExactEncoding = false; QByteArray ba = QByteArray::fromRawData( (const char *)firstBytes, 50 ); - codec = QTextCodec::codecForUtfText( ba, QTextCodec::codecForName( "UTF-8" ) ); + codec = QTextCodec::codecForUtfText( ba, nullptr ); + if ( !codec ) { + // the encoding has no bom. + // check the first char # (0x23). + auto hashTag = 0x0023; + + auto uci = qFromUnaligned< uint32_t >( firstBytes ); + if ( uci == qToBigEndian( hashTag ) ) { + codec = QTextCodec::codecForMib( 1018 ); // utf-32 be + } + else if ( uci == qToLittleEndian( hashTag ) ) { + codec = QTextCodec::codecForMib( 1019 ); // utf-32 le + } + else { + auto uc = qFromUnaligned< uint16_t >( firstBytes ); + if ( uc == qToBigEndian( uint16_t( hashTag ) ) ) { + codec = QTextCodec::codecForMib( 1013 ); // utf16 be + } + else if ( uc == qToLittleEndian( uint16_t( hashTag ) ) ) { + codec = QTextCodec::codecForMib( 1014 ); // utf16 le + } + else { + //default encoding + codec = QTextCodec::codecForName( "UTF-8" ); + } + } + } encoding = Utf8::getEncodingForName( codec->name() ); qDebug() << codec->name(); From 9a99b392e942b3ac696cf3441fa1cc1c269067f4 Mon Sep 17 00:00:00 2001 From: YiFang Xiao Date: Sun, 12 Nov 2023 19:21:57 +0800 Subject: [PATCH 05/24] opt: skip mdx 3.0 dictionary --- src/dict/mdictparser.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/dict/mdictparser.cc b/src/dict/mdictparser.cc index 61a5c042..99260676 100644 --- a/src/dict/mdictparser.cc +++ b/src/dict/mdictparser.cc @@ -320,7 +320,7 @@ bool MdictParser::readHeader( QDataStream & in ) if ( headerText.contains( "StyleSheet" ) ) { // a workaround to bypass https://bugreports.qt.io/browse/QTBUG-102612 - QRegularExpression rx( "StyleSheet=\"([^\"]*?)\"", QRegularExpression::CaseInsensitiveOption ); + QRegularExpression const rx( "StyleSheet=\"([^\"]*?)\"", QRegularExpression::CaseInsensitiveOption ); auto match = rx.match( headerText ); @@ -334,6 +334,9 @@ bool MdictParser::readHeader( QDataStream & in ) QDomNamedNodeMap headerAttributes = parseHeaderAttributes( headerText ); + if ( headerAttributes.isEmpty() ) + return false; + encoding_ = headerAttributes.namedItem( "Encoding" ).toAttr().value(); if ( encoding_ == "GBK" || encoding_ == "GB2312" ) { encoding_ = "GB18030"; From d3531b64778f6c9cd8732b912b942c24bb4b7086 Mon Sep 17 00:00:00 2001 From: YiFang Xiao Date: Mon, 13 Nov 2023 10:37:22 +0800 Subject: [PATCH 06/24] fix: website default port --- src/iframeschemehandler.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iframeschemehandler.cc b/src/iframeschemehandler.cc index 59d8d0d3..6f97e171 100644 --- a/src/iframeschemehandler.cc +++ b/src/iframeschemehandler.cc @@ -60,7 +60,7 @@ void IframeSchemeHandler::requestStarted( QWebEngineUrlRequestJob * requestJob ) QString root = reply->url().scheme() + "://" + reply->url().host(); - if ( reply->url().port() != 80 && reply->url().port() != 443 ) { + if ( reply->url().port() != 80 && reply->url().port() != 443 && reply->url().port() != -1 ) { root = root + ":" + QString::number( reply->url().port() ); } QString base = root + reply->url().path(); From faa3617521aeac709115b5b66b0ee23389164e9e Mon Sep 17 00:00:00 2001 From: xiaoyifang <105986+xiaoyifang@users.noreply.github.com> Date: Tue, 14 Nov 2023 13:30:46 +0800 Subject: [PATCH 07/24] action: disable macos upgrade check (#1285) * action: disable macos upgrade check * action: disable macos upgrade check --------- Co-authored-by: YiFang Xiao --- .github/workflows/macos-homebrew-PR-check.yml | 1 + .github/workflows/macos-homebrew.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/macos-homebrew-PR-check.yml b/.github/workflows/macos-homebrew-PR-check.yml index d564bd3a..ecd735fe 100644 --- a/.github/workflows/macos-homebrew-PR-check.yml +++ b/.github/workflows/macos-homebrew-PR-check.yml @@ -49,6 +49,7 @@ jobs: - name: install deps on macos run: | + export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=TRUE brew install cmake ninja brew install automake brew install autoconf diff --git a/.github/workflows/macos-homebrew.yml b/.github/workflows/macos-homebrew.yml index bc80de26..51102019 100644 --- a/.github/workflows/macos-homebrew.yml +++ b/.github/workflows/macos-homebrew.yml @@ -41,6 +41,7 @@ jobs: - name: install deps on macos run: | + export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=TRUE brew install cmake ninja brew install automake brew install autoconf From 5c32dba009faef23289f91e20f1a9775b91a8f12 Mon Sep 17 00:00:00 2001 From: YiFang Xiao Date: Tue, 14 Nov 2023 13:36:54 +0800 Subject: [PATCH 08/24] action: remove qt6.5.2 package --- .github/workflows/macos-homebrew-breakpad.yml | 2 +- .github/workflows/macos-homebrew.yml | 2 +- .github/workflows/ubuntu-6.2.yml | 2 +- .github/workflows/windows-6.x.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/macos-homebrew-breakpad.yml b/.github/workflows/macos-homebrew-breakpad.yml index b148e199..8270d5cd 100644 --- a/.github/workflows/macos-homebrew-breakpad.yml +++ b/.github/workflows/macos-homebrew-breakpad.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: os: [macos-13] - qt_ver: [ 6.5.2,6.6.0 ] + qt_ver: [ 6.6.0 ] qt_arch: [clang_64] env: targetName: GoldenDict diff --git a/.github/workflows/macos-homebrew.yml b/.github/workflows/macos-homebrew.yml index 51102019..94f6f8dd 100644 --- a/.github/workflows/macos-homebrew.yml +++ b/.github/workflows/macos-homebrew.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: os: [macos-12,macos-13] - qt_ver: [ 6.5.2,6.6.0 ] + qt_ver: [ 6.6.0 ] qt_arch: [clang_64] env: targetName: GoldenDict diff --git a/.github/workflows/ubuntu-6.2.yml b/.github/workflows/ubuntu-6.2.yml index 78a041f2..e794f95c 100644 --- a/.github/workflows/ubuntu-6.2.yml +++ b/.github/workflows/ubuntu-6.2.yml @@ -21,7 +21,7 @@ jobs: strategy: matrix: os: [ubuntu-20.04] - qt_ver: [ 6.5.2,6.6.0 ] + qt_ver: [ 6.6.0 ] qt_arch: [gcc_64] env: version: 23.11.09 diff --git a/.github/workflows/windows-6.x.yml b/.github/workflows/windows-6.x.yml index 95128601..158d56e8 100644 --- a/.github/workflows/windows-6.x.yml +++ b/.github/workflows/windows-6.x.yml @@ -27,7 +27,7 @@ jobs: strategy: matrix: os: [windows-2019] - qt_ver: [6.5.2,6.6.0 ] + qt_ver: [ 6.6.0 ] qt_arch: [win64_msvc2019_64] env: targetName: GoldenDict.exe From 4d48ab8f404288e7d221412ea343873428995bb3 Mon Sep 17 00:00:00 2001 From: YiFang Xiao Date: Sat, 18 Nov 2023 20:10:27 +0800 Subject: [PATCH 09/24] action: macos libzim fix --- .github/workflows/macos-homebrew.yml | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/.github/workflows/macos-homebrew.yml b/.github/workflows/macos-homebrew.yml index 94f6f8dd..6ce02f45 100644 --- a/.github/workflows/macos-homebrew.yml +++ b/.github/workflows/macos-homebrew.yml @@ -101,20 +101,12 @@ jobs: macdeployqt ${targetName}.app -qmldir=. -verbose=1 otool -L GoldenDict.app/Contents/MacOS/GoldenDict ls -al GoldenDict.app/Contents/Frameworks - otool -L GoldenDict.app/Contents/Frameworks/libzim.8.dylib - # otool -L GoldenDict.app/Contents/Frameworks/libav* - # otool -L GoldenDict.app/Contents/Frameworks/libjxl.0.8.dylib - # cp -r /usr/local/Cellar/icu4c/7*/lib/libicu*.dylib GoldenDict.app/Contents/Frameworks - # cp -r /usr/local/Cellar/zstd/1.5.5/lib/libzstd.1.dylib GoldenDict.app/Contents/Frameworks - dylibbundler -of -b -x GoldenDict.app/Contents/Frameworks/libzim.8.dylib -d GoldenDict.app/Contents/Frameworks/ -p @executable_path/../Frameworks -s /usr/local/ -s /opt/ - # dylibbundler -of -b -x GoldenDict.app/Contents/Frameworks/libicu* -d GoldenDict.app/Contents/Frameworks/ -p @executable_path/../Frameworks -s /usr/local/ -s /opt/ - + + find GoldenDict.app/Contents/Frameworks/ -maxdepth 1 -type f -name "libzim*" -exec dylibbundler -of -b -x {} -d GoldenDict.app/Contents/Frameworks/ -p @executable_path/../Frameworks -s /usr/local/ -s /opt/ \; find GoldenDict.app/Contents/Frameworks/ -maxdepth 1 -type f -name "libicu*" -exec dylibbundler -of -b -x {} -d GoldenDict.app/Contents/Frameworks/ -p @executable_path/../Frameworks -s /usr/local/ -s /opt/ \; - # dylibbundler -of -x GoldenDict.app/Contents/Frameworks/libavformat.60.dylib -x GoldenDict.app/Contents/Frameworks/libavcodec.60.dylib -d GoldenDict.app/Contents/Frameworks/ -p @executable_path/../Frameworks -s /usr/local/ -s /opt/ - # dylibbundler -of -b -x GoldenDict.app/Contents/Frameworks/libjxl.0.8.dylib -d GoldenDict.app/Contents/Frameworks/ -p @executable_path/../Frameworks -s /usr/local/ -s /opt/ otool -L GoldenDict.app/Contents/Frameworks/libicu* - otool -L GoldenDict.app/Contents/Frameworks/libzim.8.dylib + otool -L GoldenDict.app/Contents/Frameworks/libzim* otool -L GoldenDict.app/Contents/Frameworks/libxapian.30.dylib otool -L GoldenDict.app/Contents/Frameworks/liblzma.5.dylib From 30572321f7e51658be549ba6737123ed4e0a661f Mon Sep 17 00:00:00 2001 From: shenleban tongying Date: Sat, 18 Nov 2023 11:45:24 -0500 Subject: [PATCH 10/24] fix: force wayland should work for BSD, also remove a unnecessary env check for macOS --- src/main.cc | 2 +- src/ui/mainwindow.cc | 2 +- website/mkdocs.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.cc b/src/main.cc index db8a24f6..9fda4c60 100644 --- a/src/main.cc +++ b/src/main.cc @@ -307,7 +307,7 @@ void processCommandLine( QCoreApplication * app, GDOptions * result ) int main( int argc, char ** argv ) { -#ifdef Q_OS_UNIX +#if defined( Q_OS_UNIX ) && !defined( Q_OS_MACOS ) // GoldenDict use lots of X11 functions and it currently cannot work // natively on Wayland. This workaround will force GoldenDict to use // XWayland. diff --git a/src/ui/mainwindow.cc b/src/ui/mainwindow.cc index 771df2bb..8bff378b 100644 --- a/src/ui/mainwindow.cc +++ b/src/ui/mainwindow.cc @@ -2856,7 +2856,7 @@ void MainWindow::toggleMainWindow( bool onlyShow ) void MainWindow::installHotKeys() { -#if defined( Q_OS_LINUX ) +#if defined( Q_OS_UNIX ) && !defined( Q_OS_MACOS ) if ( !qEnvironmentVariableIsEmpty( "GOLDENDICT_FORCE_WAYLAND" ) ) { return; } diff --git a/website/mkdocs.yml b/website/mkdocs.yml index 5a592294..be344e76 100644 --- a/website/mkdocs.yml +++ b/website/mkdocs.yml @@ -44,7 +44,7 @@ nav: - Custom transliteration: topic_transliteration.md - Customize Dictionary: custom_dictionary.md - OCR Integration: howto/ocr.md - - Wayland/Linux: topic_wayland.md + - Wayland: topic_wayland.md - Report Bugs & Feedbacks: feedbacks.md - Contributor Guides: - Developer: developer.md From 7adfb2eb7abce95097b10b79b1a7a5bbfe5f9096 Mon Sep 17 00:00:00 2001 From: xiaoyifang <105986+xiaoyifang@users.noreply.github.com> Date: Mon, 20 Nov 2023 22:11:14 +0800 Subject: [PATCH 11/24] Action/macos icu (#1293) * action: macos libicudata --- .github/workflows/macos-homebrew.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/macos-homebrew.yml b/.github/workflows/macos-homebrew.yml index 6ce02f45..907bbd9d 100644 --- a/.github/workflows/macos-homebrew.yml +++ b/.github/workflows/macos-homebrew.yml @@ -101,14 +101,13 @@ jobs: macdeployqt ${targetName}.app -qmldir=. -verbose=1 otool -L GoldenDict.app/Contents/MacOS/GoldenDict ls -al GoldenDict.app/Contents/Frameworks - - find GoldenDict.app/Contents/Frameworks/ -maxdepth 1 -type f -name "libzim*" -exec dylibbundler -of -b -x {} -d GoldenDict.app/Contents/Frameworks/ -p @executable_path/../Frameworks -s /usr/local/ -s /opt/ \; - find GoldenDict.app/Contents/Frameworks/ -maxdepth 1 -type f -name "libicu*" -exec dylibbundler -of -b -x {} -d GoldenDict.app/Contents/Frameworks/ -p @executable_path/../Frameworks -s /usr/local/ -s /opt/ \; + find /usr/local/Cellar -name "libicudata.73.dylib" -exec cp {} GoldenDict.app/Contents/Frameworks/ \; + + dylibbundler -of -b -x GoldenDict.app/Contents/MacOS/GoldenDict -d GoldenDict.app/Contents/Frameworks/ -p @executable_path/../Frameworks -s /usr/local/ -s /opt/ + dylibbundler -of -cd -b -x GoldenDict.app/Contents/Frameworks/libicudata.73.dylib -p @executable_path/../Frameworks -s /usr/local/ -s /opt/ otool -L GoldenDict.app/Contents/Frameworks/libicu* otool -L GoldenDict.app/Contents/Frameworks/libzim* - otool -L GoldenDict.app/Contents/Frameworks/libxapian.30.dylib - otool -L GoldenDict.app/Contents/Frameworks/liblzma.5.dylib codesign --force --deep -s - GoldenDict.app From 8ea4ef84d9b90234e421cdce2c2c74495990cf81 Mon Sep 17 00:00:00 2001 From: YiFang Xiao Date: Thu, 23 Nov 2023 11:53:50 +0800 Subject: [PATCH 12/24] action: macos build --- .github/workflows/macos-homebrew.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/macos-homebrew.yml b/.github/workflows/macos-homebrew.yml index 907bbd9d..5c307356 100644 --- a/.github/workflows/macos-homebrew.yml +++ b/.github/workflows/macos-homebrew.yml @@ -103,8 +103,11 @@ jobs: ls -al GoldenDict.app/Contents/Frameworks find /usr/local/Cellar -name "libicudata.73.dylib" -exec cp {} GoldenDict.app/Contents/Frameworks/ \; - dylibbundler -of -b -x GoldenDict.app/Contents/MacOS/GoldenDict -d GoldenDict.app/Contents/Frameworks/ -p @executable_path/../Frameworks -s /usr/local/ -s /opt/ - dylibbundler -of -cd -b -x GoldenDict.app/Contents/Frameworks/libicudata.73.dylib -p @executable_path/../Frameworks -s /usr/local/ -s /opt/ + find GoldenDict.app/Contents/Frameworks/ -maxdepth 1 -name "libicu*" -exec ls -al {} \; + find GoldenDict.app/Contents/Frameworks/ -maxdepth 1 -type f -name "libicu*" -exec ls -al {} \; + + find GoldenDict.app/Contents/Frameworks/ -maxdepth 1 -type f -name "libzim*" -exec dylibbundler -of -b -x {} -d GoldenDict.app/Contents/Frameworks/ -p @executable_path/../Frameworks -s /usr/local/ -s /opt/ \; + find GoldenDict.app/Contents/Frameworks/ -maxdepth 1 -name "libicu*" -exec dylibbundler -of -b -x {} -d GoldenDict.app/Contents/Frameworks/ -p @executable_path/../Frameworks -s /usr/local/ -s /opt/ \; otool -L GoldenDict.app/Contents/Frameworks/libicu* otool -L GoldenDict.app/Contents/Frameworks/libzim* From fb85f68b2167f1c9884d1a358db87fa18964f3bc Mon Sep 17 00:00:00 2001 From: xiaoyifang <105986+xiaoyifang@users.noreply.github.com> Date: Thu, 23 Nov 2023 22:00:19 +0800 Subject: [PATCH 13/24] fix: js undefined error (#1298) * fix: js undefined error * [autofix.ci] apply automated fixes --------- Co-authored-by: YiFang Xiao Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- src/article_maker.cc | 4 ++-- src/ui/articleview.cc | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/article_maker.cc b/src/article_maker.cc index b530c3c3..b7febc78 100644 --- a/src/article_maker.cc +++ b/src/article_maker.cc @@ -674,8 +674,8 @@ void ArticleRequest::bodyFinished() fmt::format_to( std::back_inserter( head ), FMT_COMPILE( R"(
)" ), + onClick="if(typeof gdMakeArticleActive !='undefined') gdMakeArticleActive( '{3}', false );" + onContextMenu="if(typeof gdMakeArticleActive !='undefined') gdMakeArticleActive( '{3}', false );">)" ), closePrevSpan ? "" : " gdactivearticle", collapse ? " gdcollapsedarticle" : "", gdFrom, diff --git a/src/ui/articleview.cc b/src/ui/articleview.cc index 61927bcf..7e5f42a4 100644 --- a/src/ui/articleview.cc +++ b/src/ui/articleview.cc @@ -555,10 +555,11 @@ bool ArticleView::setCurrentArticle( QString const & id, bool moveToIt ) QString dictId = id.mid( 7 ); if ( dictId.isEmpty() ) return false; - QString script = QString( - "var elem=document.getElementById('%1'); " - "if(elem!=undefined){elem.scrollIntoView(true);} gdMakeArticleActive('%2',true);" ) - .arg( id, dictId ); + QString script = + QString( + "var elem=document.getElementById('%1'); " + "if(elem!=undefined){elem.scrollIntoView(true);} if(typeof gdMakeArticleActive !='undefined') gdMakeArticleActive('%2',true);" ) + .arg( id, dictId ); onJsActiveArticleChanged( id ); webview->page()->runJavaScript( script ); setActiveArticleId( dictId ); From 1e0823147ab66f86e8dee833a2af0a50299c18b6 Mon Sep 17 00:00:00 2001 From: xiaoyifang <105986+xiaoyifang@users.noreply.github.com> Date: Fri, 24 Nov 2023 11:03:21 +0800 Subject: [PATCH 14/24] opt: enable xapian position on default (#1299) * opt: enable xapian position on default * [autofix.ci] apply automated fixes --------- Co-authored-by: YiFang Xiao Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- src/ftshelpers.cc | 8 ++------ src/ui/preferences.cc | 1 + 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/ftshelpers.cc b/src/ftshelpers.cc index cbb9301f..ff9cf1b7 100644 --- a/src/ftshelpers.cc +++ b/src/ftshelpers.cc @@ -136,12 +136,8 @@ void makeFTSIndex( BtreeIndexing::BtreeDictionary * dict, QAtomicInt & isCancell indexer.set_document( doc ); - if ( GlobalBroadcaster::instance()->getPreference()->fts.enablePosition ) { - indexer.index_text( articleStr.toStdString() ); - } - else { - indexer.index_text_without_positions( articleStr.toStdString() ); - } + indexer.index_text( articleStr.toStdString() ); + doc.set_data( std::to_string( address ) ); // Add the document to the database. diff --git a/src/ui/preferences.cc b/src/ui/preferences.cc index fa50e892..d48794aa 100644 --- a/src/ui/preferences.cc +++ b/src/ui/preferences.cc @@ -376,6 +376,7 @@ Preferences::Preferences( QWidget * parent, Config::Class & cfg_ ): ui.allowGls->setChecked( !p.fts.disabledTypes.contains( "GLS", Qt::CaseInsensitive ) ); ui.enablePosition->setChecked( p.fts.enablePosition ); + ui.enablePosition->hide(); #ifndef MAKE_ZIM_SUPPORT ui.allowZim->hide(); #endif From 8c87b934d44182edb68c5845f6951454fb20d173 Mon Sep 17 00:00:00 2001 From: xiaoyifang <105986+xiaoyifang@users.noreply.github.com> Date: Fri, 24 Nov 2023 18:18:14 +0800 Subject: [PATCH 15/24] opt: refactor the escape(unescap)Amps (#1300) * opt: refactor the escape(unescap)Amps * [autofix.ci] apply automated fixes --------- Co-authored-by: YiFang Xiao Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- src/common/utils.cc | 14 ++++++++++++++ src/common/utils.hh | 4 ++++ src/ui/groups_widgets.cc | 28 ++++++---------------------- src/ui/mainwindow.cc | 11 +++-------- 4 files changed, 27 insertions(+), 30 deletions(-) diff --git a/src/common/utils.cc b/src/common/utils.cc index f5cd7b7e..73f0b379 100644 --- a/src/common/utils.cc +++ b/src/common/utils.cc @@ -35,6 +35,20 @@ bool endsWithIgnoreCase( const string & str1, string str2 ) return ( str1.size() >= (unsigned)str2.size() ) && ( strcasecmp( str1.c_str() + ( str1.size() - str2.size() ), str2.data() ) == 0 ); } + +QString escapeAmps( QString const & str ) +{ + QString result( str ); + result.replace( "&", "&&" ); + return result; +} + +QString unescapeAmps( QString const & str ) +{ + QString result( str ); + result.replace( "&&", "&" ); + return result; +} } // namespace Utils QString Utils::Path::combine( const QString & path1, const QString & path2 ) diff --git a/src/common/utils.hh b/src/common/utils.hh index d0f648d3..23dcd3ee 100644 --- a/src/common/utils.hh +++ b/src/common/utils.hh @@ -341,6 +341,10 @@ void removeDirectory( QString const & directory ); void removeDirectory( string const & directory ); } // namespace Fs +QString escapeAmps( QString const & str ); + +QString unescapeAmps( QString const & str ); + } // namespace Utils #endif // UTILS_HH diff --git a/src/ui/groups_widgets.cc b/src/ui/groups_widgets.cc index b1abfbec..5799e5c9 100644 --- a/src/ui/groups_widgets.cc +++ b/src/ui/groups_widgets.cc @@ -8,6 +8,7 @@ #include "langcoder.hh" #include "language.hh" #include "metadata.hh" +#include "utils.hh" #include #include @@ -517,23 +518,6 @@ DictGroupsWidget::DictGroupsWidget( QWidget * parent ): setUsesScrollButtons( true ); } -namespace { - -QString escapeAmps( QString const & str ) -{ - QString result( str ); - result.replace( "&", "&&" ); - return result; -} - -QString unescapeAmps( QString const & str ) -{ - QString result( str ); - result.replace( "&&", "&" ); - return result; -} - -} // namespace void DictGroupsWidget::populate( Config::Groups const & groups, vector< sptr< Dictionary::Class > > const & allDicts_, @@ -546,7 +530,7 @@ void DictGroupsWidget::populate( Config::Groups const & groups, for ( int x = 0; x < groups.size(); ++x ) { const auto gr = new DictGroupWidget( this, *allDicts, groups[ x ] ); - addTab( gr, escapeAmps( groups[ x ].name ) ); + addTab( gr, Utils::escapeAmps( groups[ x ].name ) ); connect( gr, &DictGroupWidget::showDictionaryInfo, this, &DictGroupsWidget::showDictionaryInfo ); connect( gr->getModel(), &DictListModel::contentChanged, this, &DictGroupsWidget::tabDataChanged ); @@ -569,7 +553,7 @@ Config::Groups DictGroupsWidget::makeGroups() const for ( int x = 0; x < count(); ++x ) { result.push_back( dynamic_cast< DictGroupWidget & >( *widget( x ) ).makeGroup() ); - result.back().name = unescapeAmps( tabText( x ) ); + result.back().name = Utils::unescapeAmps( tabText( x ) ); } return result; @@ -638,7 +622,7 @@ int DictGroupsWidget::addNewGroup( QString const & name ) newGroup.id = nextId++; const auto gr = new DictGroupWidget( this, *allDicts, newGroup ); - const int idx = insertTab( currentIndex() + 1, gr, escapeAmps( name ) ); + const int idx = insertTab( currentIndex() + 1, gr, Utils::escapeAmps( name ) ); connect( gr, &DictGroupWidget::showDictionaryInfo, this, &DictGroupsWidget::showDictionaryInfo ); connect( gr->getModel(), &DictListModel::contentChanged, this, &DictGroupsWidget::tabDataChanged ); @@ -897,7 +881,7 @@ QString DictGroupsWidget::getCurrentGroupName() const const int current = currentIndex(); if ( current >= 0 ) - return unescapeAmps( tabText( current ) ); + return Utils::unescapeAmps( tabText( current ) ); return QString(); } @@ -907,7 +891,7 @@ void DictGroupsWidget::renameCurrentGroup( QString const & name ) const int current = currentIndex(); if ( current >= 0 ) - setTabText( current, escapeAmps( name ) ); + setTabText( current, Utils::escapeAmps( name ) ); } void DictGroupsWidget::removeCurrentGroup() diff --git a/src/ui/mainwindow.cc b/src/ui/mainwindow.cc index 8bff378b..db94cff6 100644 --- a/src/ui/mainwindow.cc +++ b/src/ui/mainwindow.cc @@ -1814,8 +1814,7 @@ ArticleView * MainWindow::createNewTab( bool switchToIt, QString const & name ) int index = cfg.preferences.newTabsOpenAfterCurrentOne ? ui.tabWidget->currentIndex() + 1 : ui.tabWidget->count(); - QString escaped = name; - escaped.replace( "&", "&&" ); + QString escaped = Utils::escapeAmps( name ); ui.tabWidget->insertTab( index, view, escaped ); mruList.append( dynamic_cast< QWidget * >( view ) ); @@ -1943,7 +1942,7 @@ void MainWindow::titleChanged( ArticleView * view, QString const & title ) else { escaped = title; } - escaped.replace( "&", "&&" ); + escaped = Utils::escapeAmps( escaped ); int index = ui.tabWidget->indexOf( view ); if ( !escaped.isEmpty() ) @@ -4264,11 +4263,7 @@ void MainWindow::showFTSIndexingName( QString const & name ) QString MainWindow::unescapeTabHeader( QString const & header ) { // Reset table header to original headword - - QString escaped = header; - escaped.replace( "&&", "&" ); - - return escaped; + return Utils::unescapeAmps( header ); } void MainWindow::addCurrentTabToFavorites() From 439b780db529b0c97696a3f651c7fdc1e49c021e Mon Sep 17 00:00:00 2001 From: YiFang Xiao Date: Sat, 25 Nov 2023 17:13:33 +0800 Subject: [PATCH 16/24] opt: use a new way to handle the highlight matched terms in the fulltext search view page. --- src/article_maker.cc | 1 + src/scripts/mark.js | 1241 +++++++++++++++++++++++++++++++++++++++ src/scripts/mark.min.js | 13 + src/scripts/scripts.qrc | 1 + src/ui/articleview.cc | 56 +- 5 files changed, 1268 insertions(+), 44 deletions(-) create mode 100644 src/scripts/mark.js create mode 100644 src/scripts/mark.min.js diff --git a/src/article_maker.cc b/src/article_maker.cc index b7febc78..2bc1a774 100644 --- a/src/article_maker.cc +++ b/src/article_maker.cc @@ -141,6 +141,7 @@ std::string ArticleMaker::makeHtmlHeader( QString const & word, QString const & .toStdString(); result += R"()"; + result += R"()"; if ( GlobalBroadcaster::instance()->getPreference()->darkReaderMode ) { //only enable this darkmode on modern style. diff --git a/src/scripts/mark.js b/src/scripts/mark.js new file mode 100644 index 00000000..b42f2e97 --- /dev/null +++ b/src/scripts/mark.js @@ -0,0 +1,1241 @@ +/*!*************************************************** +* mark.js v9.0.0 +* https://markjs.io/ +* Copyright (c) 2014–2018, Julian Kühnel +* Released under the MIT license https://git.io/vwTVl +*****************************************************/ + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.Mark = factory()); +}(this, (function () { + 'use strict'; + + function _typeof(obj) { + if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { + _typeof = function (obj) { + return typeof obj; + }; + } else { + _typeof = function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + } + + return _typeof(obj); + } + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } + + function _extends() { + _extends = Object.assign || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; + }; + + return _extends.apply(this, arguments); + } + + var DOMIterator = + /*#__PURE__*/ + function () { + function DOMIterator(ctx) { + var iframes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + var exclude = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : []; + var iframesTimeout = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 5000; + + _classCallCheck(this, DOMIterator); + + this.ctx = ctx; + this.iframes = iframes; + this.exclude = exclude; + this.iframesTimeout = iframesTimeout; + } + + _createClass(DOMIterator, [{ + key: "getContexts", + value: function getContexts() { + var ctx, + filteredCtx = []; + + if (typeof this.ctx === 'undefined' || !this.ctx) { + ctx = []; + } else if (NodeList.prototype.isPrototypeOf(this.ctx)) { + ctx = Array.prototype.slice.call(this.ctx); + } else if (Array.isArray(this.ctx)) { + ctx = this.ctx; + } else if (typeof this.ctx === 'string') { + ctx = Array.prototype.slice.call(document.querySelectorAll(this.ctx)); + } else { + ctx = [this.ctx]; + } + + ctx.forEach(function (ctx) { + var isDescendant = filteredCtx.filter(function (contexts) { + return contexts.contains(ctx); + }).length > 0; + + if (filteredCtx.indexOf(ctx) === -1 && !isDescendant) { + filteredCtx.push(ctx); + } + }); + return filteredCtx; + } + }, { + key: "getIframeContents", + value: function getIframeContents(ifr, successFn) { + var errorFn = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function () { + }; + var doc; + + try { + var ifrWin = ifr.contentWindow; + doc = ifrWin.document; + + if (!ifrWin || !doc) { + throw new Error('iframe inaccessible'); + } + } catch (e) { + errorFn(); + } + + if (doc) { + successFn(doc); + } + } + }, { + key: "isIframeBlank", + value: function isIframeBlank(ifr) { + var bl = 'about:blank', + src = ifr.getAttribute('src').trim(), + href = ifr.contentWindow.location.href; + return href === bl && src !== bl && src; + } + }, { + key: "observeIframeLoad", + value: function observeIframeLoad(ifr, successFn, errorFn) { + var _this = this; + + var called = false, + tout = null; + + var listener = function listener() { + if (called) { + return; + } + + called = true; + clearTimeout(tout); + + try { + if (!_this.isIframeBlank(ifr)) { + ifr.removeEventListener('load', listener); + + _this.getIframeContents(ifr, successFn, errorFn); + } + } catch (e) { + errorFn(); + } + }; + + ifr.addEventListener('load', listener); + tout = setTimeout(listener, this.iframesTimeout); + } + }, { + key: "onIframeReady", + value: function onIframeReady(ifr, successFn, errorFn) { + try { + if (ifr.contentWindow.document.readyState === 'complete') { + if (this.isIframeBlank(ifr)) { + this.observeIframeLoad(ifr, successFn, errorFn); + } else { + this.getIframeContents(ifr, successFn, errorFn); + } + } else { + this.observeIframeLoad(ifr, successFn, errorFn); + } + } catch (e) { + errorFn(); + } + } + }, { + key: "waitForIframes", + value: function waitForIframes(ctx, done) { + var _this2 = this; + + var eachCalled = 0; + this.forEachIframe(ctx, function () { + return true; + }, function (ifr) { + eachCalled++; + + _this2.waitForIframes(ifr.querySelector('html'), function () { + if (!--eachCalled) { + done(); + } + }); + }, function (handled) { + if (!handled) { + done(); + } + }); + } + }, { + key: "forEachIframe", + value: function forEachIframe(ctx, filter, each) { + var _this3 = this; + + var end = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : function () { + }; + var ifr = ctx.querySelectorAll('iframe'), + open = ifr.length, + handled = 0; + ifr = Array.prototype.slice.call(ifr); + + var checkEnd = function checkEnd() { + if (--open <= 0) { + end(handled); + } + }; + + if (!open) { + checkEnd(); + } + + ifr.forEach(function (ifr) { + if (DOMIterator.matches(ifr, _this3.exclude)) { + checkEnd(); + } else { + _this3.onIframeReady(ifr, function (con) { + if (filter(ifr)) { + handled++; + each(con); + } + + checkEnd(); + }, checkEnd); + } + }); + } + }, { + key: "createIterator", + value: function createIterator(ctx, whatToShow, filter) { + return document.createNodeIterator(ctx, whatToShow, filter, false); + } + }, { + key: "createInstanceOnIframe", + value: function createInstanceOnIframe(contents) { + return new DOMIterator(contents.querySelector('html'), this.iframes); + } + }, { + key: "compareNodeIframe", + value: function compareNodeIframe(node, prevNode, ifr) { + var compCurr = node.compareDocumentPosition(ifr), + prev = Node.DOCUMENT_POSITION_PRECEDING; + + if (compCurr & prev) { + if (prevNode !== null) { + var compPrev = prevNode.compareDocumentPosition(ifr), + after = Node.DOCUMENT_POSITION_FOLLOWING; + + if (compPrev & after) { + return true; + } + } else { + return true; + } + } + + return false; + } + }, { + key: "getIteratorNode", + value: function getIteratorNode(itr) { + var prevNode = itr.previousNode(); + var node; + + if (prevNode === null) { + node = itr.nextNode(); + } else { + node = itr.nextNode() && itr.nextNode(); + } + + return { + prevNode: prevNode, + node: node + }; + } + }, { + key: "checkIframeFilter", + value: function checkIframeFilter(node, prevNode, currIfr, ifr) { + var key = false, + handled = false; + ifr.forEach(function (ifrDict, i) { + if (ifrDict.val === currIfr) { + key = i; + handled = ifrDict.handled; + } + }); + + if (this.compareNodeIframe(node, prevNode, currIfr)) { + if (key === false && !handled) { + ifr.push({ + val: currIfr, + handled: true + }); + } else if (key !== false && !handled) { + ifr[key].handled = true; + } + + return true; + } + + if (key === false) { + ifr.push({ + val: currIfr, + handled: false + }); + } + + return false; + } + }, { + key: "handleOpenIframes", + value: function handleOpenIframes(ifr, whatToShow, eCb, fCb) { + var _this4 = this; + + ifr.forEach(function (ifrDict) { + if (!ifrDict.handled) { + _this4.getIframeContents(ifrDict.val, function (con) { + _this4.createInstanceOnIframe(con).forEachNode(whatToShow, eCb, fCb); + }); + } + }); + } + }, { + key: "iterateThroughNodes", + value: function iterateThroughNodes(whatToShow, ctx, eachCb, filterCb, doneCb) { + var _this5 = this; + + var itr = this.createIterator(ctx, whatToShow, filterCb); + + var ifr = [], + elements = [], + node, + prevNode, + retrieveNodes = function retrieveNodes() { + var _this5$getIteratorNod = _this5.getIteratorNode(itr); + + prevNode = _this5$getIteratorNod.prevNode; + node = _this5$getIteratorNod.node; + return node; + }; + + while (retrieveNodes()) { + if (this.iframes) { + this.forEachIframe(ctx, function (currIfr) { + return _this5.checkIframeFilter(node, prevNode, currIfr, ifr); + }, function (con) { + _this5.createInstanceOnIframe(con).forEachNode(whatToShow, function (ifrNode) { + return elements.push(ifrNode); + }, filterCb); + }); + } + + elements.push(node); + } + + elements.forEach(function (node) { + eachCb(node); + }); + + if (this.iframes) { + this.handleOpenIframes(ifr, whatToShow, eachCb, filterCb); + } + + doneCb(); + } + }, { + key: "forEachNode", + value: function forEachNode(whatToShow, each, filter) { + var _this6 = this; + + var done = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : function () { + }; + var contexts = this.getContexts(); + var open = contexts.length; + + if (!open) { + done(); + } + + contexts.forEach(function (ctx) { + var ready = function ready() { + _this6.iterateThroughNodes(whatToShow, ctx, each, filter, function () { + if (--open <= 0) { + done(); + } + }); + }; + + if (_this6.iframes) { + _this6.waitForIframes(ctx, ready); + } else { + ready(); + } + }); + } + }], [{ + key: "matches", + value: function matches(element, selector) { + var selectors = typeof selector === 'string' ? [selector] : selector, + fn = element.matches || element.matchesSelector || element.msMatchesSelector || element.mozMatchesSelector || element.oMatchesSelector || element.webkitMatchesSelector; + + if (fn) { + var match = false; + selectors.every(function (sel) { + if (fn.call(element, sel)) { + match = true; + return false; + } + + return true; + }); + return match; + } else { + return false; + } + } + }]); + + return DOMIterator; + }(); + + var RegExpCreator = + /*#__PURE__*/ + function () { + function RegExpCreator(options) { + _classCallCheck(this, RegExpCreator); + + this.opt = _extends({}, { + 'diacritics': true, + 'synonyms': {}, + 'accuracy': 'partially', + 'caseSensitive': false, + 'ignoreJoiners': false, + 'ignorePunctuation': [], + 'wildcards': 'disabled' + }, options); + } + + _createClass(RegExpCreator, [{ + key: "create", + value: function create(str) { + if (this.opt.wildcards !== 'disabled') { + str = this.setupWildcardsRegExp(str); + } + + str = this.escapeStr(str); + + if (Object.keys(this.opt.synonyms).length) { + str = this.createSynonymsRegExp(str); + } + + if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) { + str = this.setupIgnoreJoinersRegExp(str); + } + + if (this.opt.diacritics) { + str = this.createDiacriticsRegExp(str); + } + + str = this.createMergedBlanksRegExp(str); + + if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) { + str = this.createJoinersRegExp(str); + } + + if (this.opt.wildcards !== 'disabled') { + str = this.createWildcardsRegExp(str); + } + + str = this.createAccuracyRegExp(str); + return new RegExp(str, "gm".concat(this.opt.caseSensitive ? '' : 'i')); + } + }, { + key: "sortByLength", + value: function sortByLength(arry) { + return arry.sort(function (a, b) { + return a.length === b.length ? a > b ? 1 : -1 : b.length - a.length; + }); + } + }, { + key: "escapeStr", + value: function escapeStr(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); + } + }, { + key: "createSynonymsRegExp", + value: function createSynonymsRegExp(str) { + var _this = this; + + var syn = this.opt.synonyms, + sens = this.opt.caseSensitive ? '' : 'i', + joinerPlaceholder = this.opt.ignoreJoiners || this.opt.ignorePunctuation.length ? "\0" : ''; + + for (var index in syn) { + if (syn.hasOwnProperty(index)) { + var keys = Array.isArray(syn[index]) ? syn[index] : [syn[index]]; + keys.unshift(index); + keys = this.sortByLength(keys).map(function (key) { + if (_this.opt.wildcards !== 'disabled') { + key = _this.setupWildcardsRegExp(key); + } + + key = _this.escapeStr(key); + return key; + }).filter(function (k) { + return k !== ''; + }); + + if (keys.length > 1) { + str = str.replace(new RegExp("(".concat(keys.map(function (k) { + return _this.escapeStr(k); + }).join('|'), ")"), "gm".concat(sens)), joinerPlaceholder + "(".concat(keys.map(function (k) { + return _this.processSynonyms(k); + }).join('|'), ")") + joinerPlaceholder); + } + } + } + + return str; + } + }, { + key: "processSynonyms", + value: function processSynonyms(str) { + if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) { + str = this.setupIgnoreJoinersRegExp(str); + } + + return str; + } + }, { + key: "setupWildcardsRegExp", + value: function setupWildcardsRegExp(str) { + str = str.replace(/(?:\\)*\?/g, function (val) { + return val.charAt(0) === '\\' ? '?' : "\x01"; + }); + return str.replace(/(?:\\)*\*/g, function (val) { + return val.charAt(0) === '\\' ? '*' : "\x02"; + }); + } + }, { + key: "createWildcardsRegExp", + value: function createWildcardsRegExp(str) { + var spaces = this.opt.wildcards === 'withSpaces'; + return str.replace(/\u0001/g, spaces ? '[\\S\\s]?' : '\\S?').replace(/\u0002/g, spaces ? '[\\S\\s]*?' : '\\S*'); + } + }, { + key: "setupIgnoreJoinersRegExp", + value: function setupIgnoreJoinersRegExp(str) { + return str.replace(/[^(|)\\]/g, function (val, indx, original) { + var nextChar = original.charAt(indx + 1); + + if (/[(|)\\]/.test(nextChar) || nextChar === '') { + return val; + } else { + return val + "\0"; + } + }); + } + }, { + key: "createJoinersRegExp", + value: function createJoinersRegExp(str) { + var joiner = []; + var ignorePunctuation = this.opt.ignorePunctuation; + + if (Array.isArray(ignorePunctuation) && ignorePunctuation.length) { + joiner.push(this.escapeStr(ignorePunctuation.join(''))); + } + + if (this.opt.ignoreJoiners) { + joiner.push("\\u00ad\\u200b\\u200c\\u200d"); + } + + return joiner.length ? str.split(/\u0000+/).join("[".concat(joiner.join(''), "]*")) : str; + } + }, { + key: "createDiacriticsRegExp", + value: function createDiacriticsRegExp(str) { + var sens = this.opt.caseSensitive ? '' : 'i', + dct = this.opt.caseSensitive ? ['aàáảãạăằắẳẵặâầấẩẫậäåāą', 'AÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ', 'cçćč', 'CÇĆČ', 'dđď', 'DĐĎ', 'eèéẻẽẹêềếểễệëěēę', 'EÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ', 'iìíỉĩịîïī', 'IÌÍỈĨỊÎÏĪ', 'lł', 'LŁ', 'nñňń', 'NÑŇŃ', 'oòóỏõọôồốổỗộơởỡớờợöøō', 'OÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ', 'rř', 'RŘ', 'sšśșş', 'SŠŚȘŞ', 'tťțţ', 'TŤȚŢ', 'uùúủũụưừứửữựûüůū', 'UÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ', 'yýỳỷỹỵÿ', 'YÝỲỶỸỴŸ', 'zžżź', 'ZŽŻŹ'] : ['aàáảãạăằắẳẵặâầấẩẫậäåāąAÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ', 'cçćčCÇĆČ', 'dđďDĐĎ', 'eèéẻẽẹêềếểễệëěēęEÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ', 'iìíỉĩịîïīIÌÍỈĨỊÎÏĪ', 'lłLŁ', 'nñňńNÑŇŃ', 'oòóỏõọôồốổỗộơởỡớờợöøōOÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ', 'rřRŘ', 'sšśșşSŠŚȘŞ', 'tťțţTŤȚŢ', 'uùúủũụưừứửữựûüůūUÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ', 'yýỳỷỹỵÿYÝỲỶỸỴŸ', 'zžżźZŽŻŹ']; + var handled = []; + str.split('').forEach(function (ch) { + dct.every(function (dct) { + if (dct.indexOf(ch) !== -1) { + if (handled.indexOf(dct) > -1) { + return false; + } + + str = str.replace(new RegExp("[".concat(dct, "]"), "gm".concat(sens)), "[".concat(dct, "]")); + handled.push(dct); + } + + return true; + }); + }); + return str; + } + }, { + key: "createMergedBlanksRegExp", + value: function createMergedBlanksRegExp(str) { + return str.replace(/[\s]+/gmi, '[\\s]+'); + } + }, { + key: "createAccuracyRegExp", + value: function createAccuracyRegExp(str) { + var _this2 = this; + + var chars = '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~¡¿'; + var acc = this.opt.accuracy, + val = typeof acc === 'string' ? acc : acc.value, + ls = typeof acc === 'string' ? [] : acc.limiters, + lsJoin = ''; + ls.forEach(function (limiter) { + lsJoin += "|".concat(_this2.escapeStr(limiter)); + }); + + switch (val) { + case 'partially': + default: + return "()(".concat(str, ")"); + + case 'complementary': + lsJoin = '\\s' + (lsJoin ? lsJoin : this.escapeStr(chars)); + return "()([^".concat(lsJoin, "]*").concat(str, "[^").concat(lsJoin, "]*)"); + + case 'exactly': + return "(^|\\s".concat(lsJoin, ")(").concat(str, ")(?=$|\\s").concat(lsJoin, ")"); + } + } + }]); + + return RegExpCreator; + }(); + + var Mark = + /*#__PURE__*/ + function () { + function Mark(ctx) { + _classCallCheck(this, Mark); + + this.ctx = ctx; + this.ie = false; + var ua = window.navigator.userAgent; + + if (ua.indexOf('MSIE') > -1 || ua.indexOf('Trident') > -1) { + this.ie = true; + } + } + + _createClass(Mark, [{ + key: "log", + value: function log(msg) { + var level = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'debug'; + var log = this.opt.log; + + if (!this.opt.debug) { + return; + } + + if (_typeof(log) === 'object' && typeof log[level] === 'function') { + log[level]("mark.js: ".concat(msg)); + } + } + }, { + key: "getSeparatedKeywords", + value: function getSeparatedKeywords(sv) { + var _this = this; + + var stack = []; + sv.forEach(function (kw) { + if (!_this.opt.separateWordSearch) { + if (kw.trim() && stack.indexOf(kw) === -1) { + stack.push(kw); + } + } else { + kw.split(' ').forEach(function (kwSplitted) { + if (kwSplitted.trim() && stack.indexOf(kwSplitted) === -1) { + stack.push(kwSplitted); + } + }); + } + }); + return { + 'keywords': stack.sort(function (a, b) { + return b.length - a.length; + }), + 'length': stack.length + }; + } + }, { + key: "isNumeric", + value: function isNumeric(value) { + return Number(parseFloat(value)) == value; + } + }, { + key: "checkRanges", + value: function checkRanges(array) { + var _this2 = this; + + if (!Array.isArray(array) || Object.prototype.toString.call(array[0]) !== '[object Object]') { + this.log('markRanges() will only accept an array of objects'); + this.opt.noMatch(array); + return []; + } + + var stack = []; + var last = 0; + array.sort(function (a, b) { + return a.start - b.start; + }).forEach(function (item) { + var _this2$callNoMatchOnI = _this2.callNoMatchOnInvalidRanges(item, last), + start = _this2$callNoMatchOnI.start, + end = _this2$callNoMatchOnI.end, + valid = _this2$callNoMatchOnI.valid; + + if (valid) { + item.start = start; + item.length = end - start; + stack.push(item); + last = end; + } + }); + return stack; + } + }, { + key: "callNoMatchOnInvalidRanges", + value: function callNoMatchOnInvalidRanges(range, last) { + var start, + end, + valid = false; + + if (range && typeof range.start !== 'undefined') { + start = parseInt(range.start, 10); + end = start + parseInt(range.length, 10); + + if (this.isNumeric(range.start) && this.isNumeric(range.length) && end - last > 0 && end - start > 0) { + valid = true; + } else { + this.log('Ignoring invalid or overlapping range: ' + "".concat(JSON.stringify(range))); + this.opt.noMatch(range); + } + } else { + this.log("Ignoring invalid range: ".concat(JSON.stringify(range))); + this.opt.noMatch(range); + } + + return { + start: start, + end: end, + valid: valid + }; + } + }, { + key: "checkWhitespaceRanges", + value: function checkWhitespaceRanges(range, originalLength, string) { + var end, + valid = true, + max = string.length, + offset = originalLength - max, + start = parseInt(range.start, 10) - offset; + start = start > max ? max : start; + end = start + parseInt(range.length, 10); + + if (end > max) { + end = max; + this.log("End range automatically set to the max value of ".concat(max)); + } + + if (start < 0 || end - start < 0 || start > max || end > max) { + valid = false; + this.log("Invalid range: ".concat(JSON.stringify(range))); + this.opt.noMatch(range); + } else if (string.substring(start, end).replace(/\s+/g, '') === '') { + valid = false; + this.log('Skipping whitespace only range: ' + JSON.stringify(range)); + this.opt.noMatch(range); + } + + return { + start: start, + end: end, + valid: valid + }; + } + }, { + key: "getTextNodes", + value: function getTextNodes(cb) { + var _this3 = this; + + var val = '', + nodes = []; + this.iterator.forEachNode(NodeFilter.SHOW_TEXT, function (node) { + nodes.push({ + start: val.length, + end: (val += node.textContent).length, + node: node + }); + }, function (node) { + if (_this3.matchesExclude(node.parentNode)) { + return NodeFilter.FILTER_REJECT; + } else { + return NodeFilter.FILTER_ACCEPT; + } + }, function () { + cb({ + value: val, + nodes: nodes + }); + }); + } + }, { + key: "matchesExclude", + value: function matchesExclude(el) { + return DOMIterator.matches(el, this.opt.exclude.concat(['script', 'style', 'title', 'head', 'html'])); + } + }, { + key: "wrapRangeInTextNode", + value: function wrapRangeInTextNode(node, start, end) { + var hEl = !this.opt.element ? 'mark' : this.opt.element, + startNode = node.splitText(start), + ret = startNode.splitText(end - start); + var repl = document.createElement(hEl); + repl.setAttribute('data-markjs', 'true'); + + if (this.opt.className) { + repl.setAttribute('class', this.opt.className); + } + + repl.textContent = startNode.textContent; + startNode.parentNode.replaceChild(repl, startNode); + return ret; + } + }, { + key: "wrapRangeInMappedTextNode", + value: function wrapRangeInMappedTextNode(dict, start, end, filterCb, eachCb) { + var _this4 = this; + + dict.nodes.every(function (n, i) { + var sibl = dict.nodes[i + 1]; + + if (typeof sibl === 'undefined' || sibl.start > start) { + if (!filterCb(n.node)) { + return false; + } + + var s = start - n.start, + e = (end > n.end ? n.end : end) - n.start, + startStr = dict.value.substr(0, n.start), + endStr = dict.value.substr(e + n.start); + n.node = _this4.wrapRangeInTextNode(n.node, s, e); + dict.value = startStr + endStr; + dict.nodes.forEach(function (k, j) { + if (j >= i) { + if (dict.nodes[j].start > 0 && j !== i) { + dict.nodes[j].start -= e; + } + + dict.nodes[j].end -= e; + } + }); + end -= e; + eachCb(n.node.previousSibling, n.start); + + if (end > n.end) { + start = n.end; + } else { + return false; + } + } + + return true; + }); + } + }, { + key: "wrapGroups", + value: function wrapGroups(node, pos, len, eachCb) { + node = this.wrapRangeInTextNode(node, pos, pos + len); + eachCb(node.previousSibling); + return node; + } + }, { + key: "separateGroups", + value: function separateGroups(node, match, matchIdx, filterCb, eachCb) { + var matchLen = match.length; + + for (var i = 1; i < matchLen; i++) { + var pos = node.textContent.indexOf(match[i]); + + if (match[i] && pos > -1 && filterCb(match[i], node)) { + node = this.wrapGroups(node, pos, match[i].length, eachCb); + } + } + + return node; + } + }, { + key: "wrapMatches", + value: function wrapMatches(regex, ignoreGroups, filterCb, eachCb, endCb) { + var _this5 = this; + + var matchIdx = ignoreGroups === 0 ? 0 : ignoreGroups + 1; + this.getTextNodes(function (dict) { + dict.nodes.forEach(function (node) { + node = node.node; + var match; + + while ((match = regex.exec(node.textContent)) !== null && match[matchIdx] !== '') { + if (_this5.opt.separateGroups) { + node = _this5.separateGroups(node, match, matchIdx, filterCb, eachCb); + } else { + if (!filterCb(match[matchIdx], node)) { + continue; + } + + var pos = match.index; + + if (matchIdx !== 0) { + for (var i = 1; i < matchIdx; i++) { + pos += match[i].length; + } + } + + node = _this5.wrapGroups(node, pos, match[matchIdx].length, eachCb); + } + + regex.lastIndex = 0; + } + }); + endCb(); + }); + } + }, { + key: "wrapMatchesAcrossElements", + value: function wrapMatchesAcrossElements(regex, ignoreGroups, filterCb, eachCb, endCb) { + var _this6 = this; + + var matchIdx = ignoreGroups === 0 ? 0 : ignoreGroups + 1; + this.getTextNodes(function (dict) { + var match; + + while ((match = regex.exec(dict.value)) !== null && match[matchIdx] !== '') { + var start = match.index; + + if (matchIdx !== 0) { + for (var i = 1; i < matchIdx; i++) { + start += match[i].length; + } + } + + var end = start + match[matchIdx].length; + + _this6.wrapRangeInMappedTextNode(dict, start, end, function (node) { + return filterCb(match[matchIdx], node); + }, function (node, lastIndex) { + regex.lastIndex = lastIndex; + eachCb(node); + }); + } + + endCb(); + }); + } + }, { + key: "wrapRangeFromIndex", + value: function wrapRangeFromIndex(ranges, filterCb, eachCb, endCb) { + var _this7 = this; + + this.getTextNodes(function (dict) { + var originalLength = dict.value.length; + ranges.forEach(function (range, counter) { + var _this7$checkWhitespac = _this7.checkWhitespaceRanges(range, originalLength, dict.value), + start = _this7$checkWhitespac.start, + end = _this7$checkWhitespac.end, + valid = _this7$checkWhitespac.valid; + + if (valid) { + _this7.wrapRangeInMappedTextNode(dict, start, end, function (node) { + return filterCb(node, range, dict.value.substring(start, end), counter); + }, function (node) { + eachCb(node, range); + }); + } + }); + endCb(); + }); + } + }, { + key: "unwrapMatches", + value: function unwrapMatches(node) { + var parent = node.parentNode; + var docFrag = document.createDocumentFragment(); + + while (node.firstChild) { + docFrag.appendChild(node.removeChild(node.firstChild)); + } + + parent.replaceChild(docFrag, node); + + if (!this.ie) { + parent.normalize(); + } else { + this.normalizeTextNode(parent); + } + } + }, { + key: "normalizeTextNode", + value: function normalizeTextNode(node) { + if (!node) { + return; + } + + if (node.nodeType === 3) { + while (node.nextSibling && node.nextSibling.nodeType === 3) { + node.nodeValue += node.nextSibling.nodeValue; + node.parentNode.removeChild(node.nextSibling); + } + } else { + this.normalizeTextNode(node.firstChild); + } + + this.normalizeTextNode(node.nextSibling); + } + }, { + key: "markRegExp", + value: function markRegExp(regexp, opt) { + var _this8 = this; + + this.opt = opt; + this.log("Searching with expression \"".concat(regexp, "\"")); + var totalMatches = 0, + fn = 'wrapMatches'; + + var eachCb = function eachCb(element) { + totalMatches++; + + _this8.opt.each(element); + }; + + if (this.opt.acrossElements) { + fn = 'wrapMatchesAcrossElements'; + } + + this[fn](regexp, this.opt.ignoreGroups, function (match, node) { + return _this8.opt.filter(node, match, totalMatches); + }, eachCb, function () { + if (totalMatches === 0) { + _this8.opt.noMatch(regexp); + } + + _this8.opt.done(totalMatches); + }); + } + }, { + key: "mark", + value: function mark(sv, opt) { + var _this9 = this; + + this.opt = opt; + var totalMatches = 0, + fn = 'wrapMatches'; + + var _this$getSeparatedKey = this.getSeparatedKeywords(typeof sv === 'string' ? [sv] : sv), + kwArr = _this$getSeparatedKey.keywords, + kwArrLen = _this$getSeparatedKey.length, + handler = function handler(kw) { + var regex = new RegExpCreator(_this9.opt).create(kw); + var matches = 0; + + _this9.log("Searching with expression \"".concat(regex, "\"")); + + _this9[fn](regex, 1, function (term, node) { + return _this9.opt.filter(node, kw, totalMatches, matches); + }, function (element) { + matches++; + totalMatches++; + + _this9.opt.each(element); + }, function () { + if (matches === 0) { + _this9.opt.noMatch(kw); + } + + if (kwArr[kwArrLen - 1] === kw) { + _this9.opt.done(totalMatches); + } else { + handler(kwArr[kwArr.indexOf(kw) + 1]); + } + }); + }; + + if (this.opt.acrossElements) { + fn = 'wrapMatchesAcrossElements'; + } + + if (kwArrLen === 0) { + this.opt.done(totalMatches); + } else { + handler(kwArr[0]); + } + } + }, { + key: "markRanges", + value: function markRanges(rawRanges, opt) { + var _this10 = this; + + this.opt = opt; + var totalMatches = 0, + ranges = this.checkRanges(rawRanges); + + if (ranges && ranges.length) { + this.log('Starting to mark with the following ranges: ' + JSON.stringify(ranges)); + this.wrapRangeFromIndex(ranges, function (node, range, match, counter) { + return _this10.opt.filter(node, range, match, counter); + }, function (element, range) { + totalMatches++; + + _this10.opt.each(element, range); + }, function () { + _this10.opt.done(totalMatches); + }); + } else { + this.opt.done(totalMatches); + } + } + }, { + key: "unmark", + value: function unmark(opt) { + var _this11 = this; + + this.opt = opt; + var sel = this.opt.element ? this.opt.element : '*'; + sel += '[data-markjs]'; + + if (this.opt.className) { + sel += ".".concat(this.opt.className); + } + + this.log("Removal selector \"".concat(sel, "\"")); + this.iterator.forEachNode(NodeFilter.SHOW_ELEMENT, function (node) { + _this11.unwrapMatches(node); + }, function (node) { + var matchesSel = DOMIterator.matches(node, sel), + matchesExclude = _this11.matchesExclude(node); + + if (!matchesSel || matchesExclude) { + return NodeFilter.FILTER_REJECT; + } else { + return NodeFilter.FILTER_ACCEPT; + } + }, this.opt.done); + } + }, { + key: "opt", + set: function set(val) { + this._opt = _extends({}, { + 'element': '', + 'className': '', + 'exclude': [], + 'iframes': false, + 'iframesTimeout': 5000, + 'separateWordSearch': true, + 'acrossElements': false, + 'ignoreGroups': 0, + 'each': function each() { + }, + 'noMatch': function noMatch() { + }, + 'filter': function filter() { + return true; + }, + 'done': function done() { + }, + 'debug': false, + 'log': window.console + }, val); + }, + get: function get() { + return this._opt; + } + }, { + key: "iterator", + get: function get() { + return new DOMIterator(this.ctx, this.opt.iframes, this.opt.exclude, this.opt.iframesTimeout); + } + }]); + + return Mark; + }(); + + function Mark$1(ctx) { + var _this = this; + + var instance = new Mark(ctx); + + this.mark = function (sv, opt) { + instance.mark(sv, opt); + return _this; + }; + + this.markRegExp = function (sv, opt) { + instance.markRegExp(sv, opt); + return _this; + }; + + this.markRanges = function (sv, opt) { + instance.markRanges(sv, opt); + return _this; + }; + + this.unmark = function (opt) { + instance.unmark(opt); + return _this; + }; + + return this; + } + + return Mark$1; + +}))); diff --git a/src/scripts/mark.min.js b/src/scripts/mark.min.js new file mode 100644 index 00000000..5608e6bb --- /dev/null +++ b/src/scripts/mark.min.js @@ -0,0 +1,13 @@ +/*!*************************************************** +* mark.js v9.0.0 +* https://markjs.io/ +* Copyright (c) 2014–2018, Julian Kühnel +* Released under the MIT license https://git.io/vwTVl +*****************************************************/ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Mark=t()}(this,function(){"use strict";function e(t){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(t)}function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var n=0;n1&&void 0!==arguments[1])||arguments[1],o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:5e3;t(this,e),this.ctx=n,this.iframes=r,this.exclude=o,this.iframesTimeout=i}return r(e,[{key:"getContexts",value:function(){var e=[];return(void 0!==this.ctx&&this.ctx?NodeList.prototype.isPrototypeOf(this.ctx)?Array.prototype.slice.call(this.ctx):Array.isArray(this.ctx)?this.ctx:"string"==typeof this.ctx?Array.prototype.slice.call(document.querySelectorAll(this.ctx)):[this.ctx]:[]).forEach(function(t){var n=e.filter(function(e){return e.contains(t)}).length>0;-1!==e.indexOf(t)||n||e.push(t)}),e}},{key:"getIframeContents",value:function(e,t){var n,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:function(){};try{var o=e.contentWindow;if(n=o.document,!o||!n)throw new Error("iframe inaccessible")}catch(e){r()}n&&t(n)}},{key:"isIframeBlank",value:function(e){var t="about:blank",n=e.getAttribute("src").trim();return e.contentWindow.location.href===t&&n!==t&&n}},{key:"observeIframeLoad",value:function(e,t,n){var r=this,o=!1,i=null,a=function a(){if(!o){o=!0,clearTimeout(i);try{r.isIframeBlank(e)||(e.removeEventListener("load",a),r.getIframeContents(e,t,n))}catch(e){n()}}};e.addEventListener("load",a),i=setTimeout(a,this.iframesTimeout)}},{key:"onIframeReady",value:function(e,t,n){try{"complete"===e.contentWindow.document.readyState?this.isIframeBlank(e)?this.observeIframeLoad(e,t,n):this.getIframeContents(e,t,n):this.observeIframeLoad(e,t,n)}catch(e){n()}}},{key:"waitForIframes",value:function(e,t){var n=this,r=0;this.forEachIframe(e,function(){return!0},function(e){r++,n.waitForIframes(e.querySelector("html"),function(){--r||t()})},function(e){e||t()})}},{key:"forEachIframe",value:function(t,n,r){var o=this,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:function(){},a=t.querySelectorAll("iframe"),s=a.length,c=0;a=Array.prototype.slice.call(a);var u=function(){--s<=0&&i(c)};s||u(),a.forEach(function(t){e.matches(t,o.exclude)?u():o.onIframeReady(t,function(e){n(t)&&(c++,r(e)),u()},u)})}},{key:"createIterator",value:function(e,t,n){return document.createNodeIterator(e,t,n,!1)}},{key:"createInstanceOnIframe",value:function(t){return new e(t.querySelector("html"),this.iframes)}},{key:"compareNodeIframe",value:function(e,t,n){if(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_PRECEDING){if(null===t)return!0;if(t.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING)return!0}return!1}},{key:"getIteratorNode",value:function(e){var t=e.previousNode();return{prevNode:t,node:null===t?e.nextNode():e.nextNode()&&e.nextNode()}}},{key:"checkIframeFilter",value:function(e,t,n,r){var o=!1,i=!1;return r.forEach(function(e,t){e.val===n&&(o=t,i=e.handled)}),this.compareNodeIframe(e,t,n)?(!1!==o||i?!1===o||i||(r[o].handled=!0):r.push({val:n,handled:!0}),!0):(!1===o&&r.push({val:n,handled:!1}),!1)}},{key:"handleOpenIframes",value:function(e,t,n,r){var o=this;e.forEach(function(e){e.handled||o.getIframeContents(e.val,function(e){o.createInstanceOnIframe(e).forEachNode(t,n,r)})})}},{key:"iterateThroughNodes",value:function(e,t,n,r,o){for(var i,a,s,c=this,u=this.createIterator(t,e,r),l=[],h=[];s=void 0,s=c.getIteratorNode(u),a=s.prevNode,i=s.node;)this.iframes&&this.forEachIframe(t,function(e){return c.checkIframeFilter(i,a,e,l)},function(t){c.createInstanceOnIframe(t).forEachNode(e,function(e){return h.push(e)},r)}),h.push(i);h.forEach(function(e){n(e)}),this.iframes&&this.handleOpenIframes(l,e,n,r),o()}},{key:"forEachNode",value:function(e,t,n){var r=this,o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:function(){},i=this.getContexts(),a=i.length;a||o(),i.forEach(function(i){var s=function(){r.iterateThroughNodes(e,i,t,n,function(){--a<=0&&o()})};r.iframes?r.waitForIframes(i,s):s()})}}],[{key:"matches",value:function(e,t){var n="string"==typeof t?[t]:t,r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.oMatchesSelector||e.webkitMatchesSelector;if(r){var o=!1;return n.every(function(t){return!r.call(e,t)||(o=!0,!1)}),o}return!1}}]),e}(),a= +/* */ +function(){function e(n){t(this,e),this.opt=o({},{diacritics:!0,synonyms:{},accuracy:"partially",caseSensitive:!1,ignoreJoiners:!1,ignorePunctuation:[],wildcards:"disabled"},n)}return r(e,[{key:"create",value:function(e){return"disabled"!==this.opt.wildcards&&(e=this.setupWildcardsRegExp(e)),e=this.escapeStr(e),Object.keys(this.opt.synonyms).length&&(e=this.createSynonymsRegExp(e)),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.setupIgnoreJoinersRegExp(e)),this.opt.diacritics&&(e=this.createDiacriticsRegExp(e)),e=this.createMergedBlanksRegExp(e),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.createJoinersRegExp(e)),"disabled"!==this.opt.wildcards&&(e=this.createWildcardsRegExp(e)),e=this.createAccuracyRegExp(e),new RegExp(e,"gm".concat(this.opt.caseSensitive?"":"i"))}},{key:"sortByLength",value:function(e){return e.sort(function(e,t){return e.length===t.length?e>t?1:-1:t.length-e.length})}},{key:"escapeStr",value:function(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}},{key:"createSynonymsRegExp",value:function(e){var t=this,n=this.opt.synonyms,r=this.opt.caseSensitive?"":"i",o=this.opt.ignoreJoiners||this.opt.ignorePunctuation.length?"\0":"";for(var i in n)if(n.hasOwnProperty(i)){var a=Array.isArray(n[i])?n[i]:[n[i]];a.unshift(i),(a=this.sortByLength(a).map(function(e){return"disabled"!==t.opt.wildcards&&(e=t.setupWildcardsRegExp(e)),e=t.escapeStr(e)}).filter(function(e){return""!==e})).length>1&&(e=e.replace(new RegExp("(".concat(a.map(function(e){return t.escapeStr(e)}).join("|"),")"),"gm".concat(r)),o+"(".concat(a.map(function(e){return t.processSynonyms(e)}).join("|"),")")+o))}return e}},{key:"processSynonyms",value:function(e){return(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.setupIgnoreJoinersRegExp(e)),e}},{key:"setupWildcardsRegExp",value:function(e){return(e=e.replace(/(?:\\)*\?/g,function(e){return"\\"===e.charAt(0)?"?":""})).replace(/(?:\\)*\*/g,function(e){return"\\"===e.charAt(0)?"*":""})}},{key:"createWildcardsRegExp",value:function(e){var t="withSpaces"===this.opt.wildcards;return e.replace(/\u0001/g,t?"[\\S\\s]?":"\\S?").replace(/\u0002/g,t?"[\\S\\s]*?":"\\S*")}},{key:"setupIgnoreJoinersRegExp",value:function(e){return e.replace(/[^(|)\\]/g,function(e,t,n){var r=n.charAt(t+1);return/[(|)\\]/.test(r)||""===r?e:e+"\0"})}},{key:"createJoinersRegExp",value:function(e){var t=[],n=this.opt.ignorePunctuation;return Array.isArray(n)&&n.length&&t.push(this.escapeStr(n.join(""))),this.opt.ignoreJoiners&&t.push("\\u00ad\\u200b\\u200c\\u200d"),t.length?e.split(/\u0000+/).join("[".concat(t.join(""),"]*")):e}},{key:"createDiacriticsRegExp",value:function(e){var t=this.opt.caseSensitive?"":"i",n=this.opt.caseSensitive?["aàáảãạăằắẳẵặâầấẩẫậäåāą","AÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćč","CÇĆČ","dđď","DĐĎ","eèéẻẽẹêềếểễệëěēę","EÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïī","IÌÍỈĨỊÎÏĪ","lł","LŁ","nñňń","NÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøō","OÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rř","RŘ","sšśșş","SŠŚȘŞ","tťțţ","TŤȚŢ","uùúủũụưừứửữựûüůū","UÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿ","YÝỲỶỸỴŸ","zžżź","ZŽŻŹ"]:["aàáảãạăằắẳẵặâầấẩẫậäåāąAÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćčCÇĆČ","dđďDĐĎ","eèéẻẽẹêềếểễệëěēęEÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïīIÌÍỈĨỊÎÏĪ","lłLŁ","nñňńNÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøōOÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rřRŘ","sšśșşSŠŚȘŞ","tťțţTŤȚŢ","uùúủũụưừứửữựûüůūUÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿYÝỲỶỸỴŸ","zžżźZŽŻŹ"],r=[];return e.split("").forEach(function(o){n.every(function(n){if(-1!==n.indexOf(o)){if(r.indexOf(n)>-1)return!1;e=e.replace(new RegExp("[".concat(n,"]"),"gm".concat(t)),"[".concat(n,"]")),r.push(n)}return!0})}),e}},{key:"createMergedBlanksRegExp",value:function(e){return e.replace(/[\s]+/gim,"[\\s]+")}},{key:"createAccuracyRegExp",value:function(e){var t=this,n=this.opt.accuracy,r="string"==typeof n?n:n.value,o="string"==typeof n?[]:n.limiters,i="";switch(o.forEach(function(e){i+="|".concat(t.escapeStr(e))}),r){case"partially":default:return"()(".concat(e,")");case"complementary":return i="\\s"+(i||this.escapeStr("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~¡¿")),"()([^".concat(i,"]*").concat(e,"[^").concat(i,"]*)");case"exactly":return"(^|\\s".concat(i,")(").concat(e,")(?=$|\\s").concat(i,")")}}}]),e}(),s= +/* */ +function(){function n(e){t(this,n),this.ctx=e,this.ie=!1;var r=window.navigator.userAgent;(r.indexOf("MSIE")>-1||r.indexOf("Trident")>-1)&&(this.ie=!0)}return r(n,[{key:"log",value:function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"debug",r=this.opt.log;this.opt.debug&&"object"===e(r)&&"function"==typeof r[n]&&r[n]("mark.js: ".concat(t))}},{key:"getSeparatedKeywords",value:function(e){var t=this,n=[];return e.forEach(function(e){t.opt.separateWordSearch?e.split(" ").forEach(function(e){e.trim()&&-1===n.indexOf(e)&&n.push(e)}):e.trim()&&-1===n.indexOf(e)&&n.push(e)}),{keywords:n.sort(function(e,t){return t.length-e.length}),length:n.length}}},{key:"isNumeric",value:function(e){return Number(parseFloat(e))==e}},{key:"checkRanges",value:function(e){var t=this;if(!Array.isArray(e)||"[object Object]"!==Object.prototype.toString.call(e[0]))return this.log("markRanges() will only accept an array of objects"),this.opt.noMatch(e),[];var n=[],r=0;return e.sort(function(e,t){return e.start-t.start}).forEach(function(e){var o=t.callNoMatchOnInvalidRanges(e,r),i=o.start,a=o.end;o.valid&&(e.start=i,e.length=a-i,n.push(e),r=a)}),n}},{key:"callNoMatchOnInvalidRanges",value:function(e,t){var n,r,o=!1;return e&&void 0!==e.start?(r=(n=parseInt(e.start,10))+parseInt(e.length,10),this.isNumeric(e.start)&&this.isNumeric(e.length)&&r-t>0&&r-n>0?o=!0:(this.log("Ignoring invalid or overlapping range: "+"".concat(JSON.stringify(e))),this.opt.noMatch(e))):(this.log("Ignoring invalid range: ".concat(JSON.stringify(e))),this.opt.noMatch(e)),{start:n,end:r,valid:o}}},{key:"checkWhitespaceRanges",value:function(e,t,n){var r,o=!0,i=n.length,a=t-i,s=parseInt(e.start,10)-a;return(r=(s=s>i?i:s)+parseInt(e.length,10))>i&&(r=i,this.log("End range automatically set to the max value of ".concat(i))),s<0||r-s<0||s>i||r>i?(o=!1,this.log("Invalid range: ".concat(JSON.stringify(e))),this.opt.noMatch(e)):""===n.substring(s,r).replace(/\s+/g,"")&&(o=!1,this.log("Skipping whitespace only range: "+JSON.stringify(e)),this.opt.noMatch(e)),{start:s,end:r,valid:o}}},{key:"getTextNodes",value:function(e){var t=this,n="",r=[];this.iterator.forEachNode(NodeFilter.SHOW_TEXT,function(e){r.push({start:n.length,end:(n+=e.textContent).length,node:e})},function(e){return t.matchesExclude(e.parentNode)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT},function(){e({value:n,nodes:r})})}},{key:"matchesExclude",value:function(e){return i.matches(e,this.opt.exclude.concat(["script","style","title","head","html"]))}},{key:"wrapRangeInTextNode",value:function(e,t,n){var r=this.opt.element?this.opt.element:"mark",o=e.splitText(t),i=o.splitText(n-t),a=document.createElement(r);return a.setAttribute("data-markjs","true"),this.opt.className&&a.setAttribute("class",this.opt.className),a.textContent=o.textContent,o.parentNode.replaceChild(a,o),i}},{key:"wrapRangeInMappedTextNode",value:function(e,t,n,r,o){var i=this;e.nodes.every(function(a,s){var c=e.nodes[s+1];if(void 0===c||c.start>t){if(!r(a.node))return!1;var u=t-a.start,l=(n>a.end?a.end:n)-a.start,h=e.value.substr(0,a.start),f=e.value.substr(l+a.start);if(a.node=i.wrapRangeInTextNode(a.node,u,l),e.value=h+f,e.nodes.forEach(function(t,n){n>=s&&(e.nodes[n].start>0&&n!==s&&(e.nodes[n].start-=l),e.nodes[n].end-=l)}),n-=l,o(a.node.previousSibling,a.start),!(n>a.end))return!1;t=a.end}return!0})}},{key:"wrapGroups",value:function(e,t,n,r){return r((e=this.wrapRangeInTextNode(e,t,t+n)).previousSibling),e}},{key:"separateGroups",value:function(e,t,n,r,o){for(var i=t.length,a=1;a-1&&r(t[a],e)&&(e=this.wrapGroups(e,s,t[a].length,o))}return e}},{key:"wrapMatches",value:function(e,t,n,r,o){var i=this,a=0===t?0:t+1;this.getTextNodes(function(t){t.nodes.forEach(function(t){var o;for(t=t.node;null!==(o=e.exec(t.textContent))&&""!==o[a];){if(i.opt.separateGroups)t=i.separateGroups(t,o,a,n,r);else{if(!n(o[a],t))continue;var s=o.index;if(0!==a)for(var c=1;cjquery-3.6.0.slim.min.js popper.min.js tippy.min.js + mark.min.js diff --git a/src/ui/articleview.cc b/src/ui/articleview.cc index 7e5f42a4..7acd93bb 100644 --- a/src/ui/articleview.cc +++ b/src/ui/articleview.cc @@ -477,8 +477,9 @@ void ArticleView::loadFinished( bool result ) if ( result ) { emit pageLoaded( this ); } - if ( Utils::Url::hasQueryItem( webview->url(), "regexp" ) ) + if ( Utils::Url::hasQueryItem( webview->url(), "regexp" ) ) { highlightFTSResults(); + } } void ArticleView::handleTitleChanged( QString const & title ) @@ -2137,54 +2138,21 @@ void ArticleView::highlightFTSResults() if ( regString.isEmpty() ) return; - //
watchout
to plainText will return "watchout". - //if application goes here,that means the article text must contains the search text. - //whole word match regString will contain \b . can not match the above senario. - //workaround ,remove \b from the regstring="(\bwatch\b)" - regString.remove( QRegularExpression( R"(\b)" ) ); - //make it simple ,and do not support too much complex cases. such as wildcard etc. - firstAvailableText = regString; + //replace any unicode Number ,Symbol ,Punctuation ,Mark character to whitespace + regString.replace( QRegularExpression( R"([\p{N}\p{S}\p{P}\p{M}])", QRegularExpression::UseUnicodePropertiesOption ), + " " ); - if ( firstAvailableText.isEmpty() ) { - return; - } - - //remove possible wildcard character. - auto cleaned = - firstAvailableText.split( QRegularExpression( "\\p{P}", QRegularExpression::UseUnicodePropertiesOption ) ); - - if ( cleaned.empty() ) + if ( regString.trimmed().isEmpty() ) return; - firstAvailableText = cleaned.at( 0 ); -#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) ) - webview->findText( firstAvailableText, - QWebEnginePage::FindBackward, - [ & ]( const QWebEngineFindTextResult & result ) { - qInfo() << result.activeMatch() << "of" << result.numberOfMatches() << "matches"; + QString script = QString( + "var context = document.querySelector(\"body\");\n" + "var instance = new Mark(context);\n" + "instance.mark(\"%1\");" ) + .arg( regString ); - if ( result.numberOfMatches() == 0 ) { - ftsSearchPanel->statusLabel->setText( searchStatusMessageNoMatches() ); - } - else { - ftsSearchPanel->statusLabel->setText( - searchStatusMessage( result.activeMatch(), result.numberOfMatches() ) ); - } - - ftsSearchPanel->show(); - ftsSearchPanel->previous->setEnabled( result.numberOfMatches() > 1 ); - ftsSearchPanel->next->setEnabled( result.numberOfMatches() > 1 ); - - ftsSearchIsOpened = true; - } ); -#else - webview->findText( firstAvailableText, QWebEnginePage::FindBackward, [ this ]( bool res ) { - ftsSearchPanel->previous->setEnabled( res ); - if ( !ftsSearchPanel->next->isEnabled() ) - ftsSearchPanel->next->setEnabled( res ); - } ); -#endif + webview->page()->runJavaScript( script ); } void ArticleView::setActiveDictIds( const ActiveDictIds & ad ) From f03d8b5ea1f065fa3d4f80312fc31a756650259d Mon Sep 17 00:00:00 2001 From: YiFang Xiao Date: Wed, 29 Nov 2023 17:14:51 +0800 Subject: [PATCH 17/24] opt: do not set the default text --- src/ui/mainwindow.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/mainwindow.cc b/src/ui/mainwindow.cc index db94cff6..81bda91e 100644 --- a/src/ui/mainwindow.cc +++ b/src/ui/mainwindow.cc @@ -4217,7 +4217,7 @@ void MainWindow::showFullTextSearchDialog() { if ( !ftsDlg ) { ftsDlg = new FTS::FullTextSearchDialog( this, cfg, dictionaries, groupInstances, ftsIndexing ); - ftsDlg->setSearchText( translateLine->text() ); + // ftsDlg->setSearchText( translateLine->text() ); addGlobalActionsToDialog( ftsDlg ); addGroupComboBoxActionsToDialog( ftsDlg, groupList ); From 46e8af5f36a8b3c5fa2d19388908e070ae6078aa Mon Sep 17 00:00:00 2001 From: xiaoyifang <105986+xiaoyifang@users.noreply.github.com> Date: Thu, 30 Nov 2023 09:49:09 +0800 Subject: [PATCH 18/24] opt: xapian optimize search experience (#1304) * opt: xapian optimize search experience * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- src/ftshelpers.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/ftshelpers.cc b/src/ftshelpers.cc index ff9cf1b7..c0138f02 100644 --- a/src/ftshelpers.cc +++ b/src/ftshelpers.cc @@ -193,10 +193,14 @@ void FTSResultsRequest::run() // Parse the query string to produce a Xapian::Query object. Xapian::QueryParser qp; qp.set_database( db ); - Xapian::QueryParser::feature_flag flag = Xapian::QueryParser::FLAG_DEFAULT; - if ( searchMode == FTS::Wildcards ) - flag = Xapian::QueryParser::FLAG_WILDCARD; - Xapian::Query query = qp.parse_query( query_string, flag | Xapian::QueryParser::FLAG_CJK_NGRAM ); + qp.set_default_op( Xapian::Query::op::OP_AND ); + int flag = + Xapian::QueryParser::FLAG_DEFAULT | Xapian::QueryParser::FLAG_PURE_NOT | Xapian::QueryParser::FLAG_CJK_NGRAM; + if ( searchMode == FTS::Wildcards ) { + flag = flag | Xapian::QueryParser::FLAG_WILDCARD; + qp.set_max_expansion( 1 ); + } + Xapian::Query query = qp.parse_query( query_string, flag ); qDebug() << "Parsed query is: " << query.get_description().c_str(); // Find the top 100 results for the query. From e7042b181dc1e7a745818ad8fceba0f4a2bb2e7e Mon Sep 17 00:00:00 2001 From: YiFang Xiao Date: Thu, 30 Nov 2023 15:55:00 +0800 Subject: [PATCH 19/24] action: add qt 6.6.1 support --- .github/workflows/macos-homebrew.yml | 2 +- .github/workflows/windows-6.x.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/macos-homebrew.yml b/.github/workflows/macos-homebrew.yml index 5c307356..45c7a5ed 100644 --- a/.github/workflows/macos-homebrew.yml +++ b/.github/workflows/macos-homebrew.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: os: [macos-12,macos-13] - qt_ver: [ 6.6.0 ] + qt_ver: [ 6.6.0,6.6.1 ] qt_arch: [clang_64] env: targetName: GoldenDict diff --git a/.github/workflows/windows-6.x.yml b/.github/workflows/windows-6.x.yml index 158d56e8..5856f74a 100644 --- a/.github/workflows/windows-6.x.yml +++ b/.github/workflows/windows-6.x.yml @@ -27,7 +27,7 @@ jobs: strategy: matrix: os: [windows-2019] - qt_ver: [ 6.6.0 ] + qt_ver: [ 6.6.0,6.6.1 ] qt_arch: [win64_msvc2019_64] env: targetName: GoldenDict.exe From 64f69d223574217aae148f17bb61ac6be44047e5 Mon Sep 17 00:00:00 2001 From: xiaoyifang <105986+xiaoyifang@users.noreply.github.com> Date: Tue, 5 Dec 2023 14:59:45 +0800 Subject: [PATCH 20/24] opt: open image in external tool (#1308) * opt: open image in external tool * [autofix.ci] apply automated fixes --------- Co-authored-by: YiFang Xiao Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- src/ui/articleview.cc | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/ui/articleview.cc b/src/ui/articleview.cc index 7acd93bb..a62fce52 100644 --- a/src/ui/articleview.cc +++ b/src/ui/articleview.cc @@ -30,6 +30,7 @@ #include #include #include +#include #if ( QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 ) && QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) ) #include @@ -1493,6 +1494,7 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) QAction * addHeaderToHistoryAction = nullptr; QAction * sendWordToInputLineAction = nullptr; QAction * saveImageAction = nullptr; + QAction * openImageAction = nullptr; QAction * saveSoundAction = nullptr; QAction * saveBookmark = nullptr; @@ -1536,6 +1538,9 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) menu.addAction( webview->pageAction( QWebEnginePage::CopyImageToClipboard ) ); saveImageAction = new QAction( tr( "Save &image..." ), &menu ); menu.addAction( saveImageAction ); + + openImageAction = new QAction( tr( "Open image in system viewer..." ), &menu ); + menu.addAction( openImageAction ); } } @@ -1748,6 +1753,29 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) saveResource( url, webview->url(), fileName ); } } + else if ( result == openImageAction ) { + QUrl url = imageUrl; + QString fileName; + + + QString name = Utils::Url::path( url ).section( '/', -1 ); + // Image data + + // Check for babylon image name + if ( name[ 0 ] == '\x1E' ) + name.remove( 0, 1 ); + if ( name.length() && name[ name.length() - 1 ] == '\x1F' ) + name.chop( 1 ); + + fileName = QString::number( QRandomGenerator::global()->generate() ) + name; + + + if ( !fileName.isEmpty() ) { + QFileInfo fileInfo( fileName ); + saveResource( url, webview->url(), fileName ); + QDesktopServices::openUrl( fileName ); + } + } else { if ( !popupView && result == maxDictionaryRefsAction ) emit showDictsPane(); From 9c5e25971bc6bb9e2abe418166ddd0b79745a050 Mon Sep 17 00:00:00 2001 From: xiaoyifang <105986+xiaoyifang@users.noreply.github.com> Date: Wed, 6 Dec 2023 09:44:17 +0800 Subject: [PATCH 21/24] opt: support to open image in system viewer (#1309) * opt: open image in external fix have to wait the resource downloaded * [autofix.ci] apply automated fixes --------- Co-authored-by: YiFang Xiao Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- src/ui/articleview.cc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/ui/articleview.cc b/src/ui/articleview.cc index a62fce52..b160610d 100644 --- a/src/ui/articleview.cc +++ b/src/ui/articleview.cc @@ -1772,8 +1772,16 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) if ( !fileName.isEmpty() ) { QFileInfo fileInfo( fileName ); - saveResource( url, webview->url(), fileName ); - QDesktopServices::openUrl( fileName ); + auto handler = saveResource( url, webview->url(), fileName ); + + if ( !handler->isEmpty() ) { + connect( handler, &ResourceToSaveHandler::done, this, [ fileName ]() { + QDesktopServices::openUrl( fileName ); + } ); + } + else { + QDesktopServices::openUrl( fileName ); + } } } else { From d15cafed49927db5298a333959049c0d85984442 Mon Sep 17 00:00:00 2001 From: YiFang Xiao Date: Wed, 6 Dec 2023 11:16:35 +0800 Subject: [PATCH 22/24] fix: image temp path --- src/ui/articleview.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ui/articleview.cc b/src/ui/articleview.cc index b160610d..628cb24a 100644 --- a/src/ui/articleview.cc +++ b/src/ui/articleview.cc @@ -1767,8 +1767,7 @@ void ArticleView::contextMenuRequested( QPoint const & pos ) if ( name.length() && name[ name.length() - 1 ] == '\x1F' ) name.chop( 1 ); - fileName = QString::number( QRandomGenerator::global()->generate() ) + name; - + fileName = QDir::temp().filePath( QString::number( QRandomGenerator::global()->generate() ) + name ); if ( !fileName.isEmpty() ) { QFileInfo fileInfo( fileName ); From 923a46217b72ce3ecd09413667c37f525efd1a11 Mon Sep 17 00:00:00 2001 From: YiFang Xiao Date: Wed, 6 Dec 2023 14:29:59 +0800 Subject: [PATCH 23/24] opt: mark exact word --- src/ui/articleview.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/articleview.cc b/src/ui/articleview.cc index b160610d..f4074c36 100644 --- a/src/ui/articleview.cc +++ b/src/ui/articleview.cc @@ -2185,7 +2185,7 @@ void ArticleView::highlightFTSResults() QString script = QString( "var context = document.querySelector(\"body\");\n" "var instance = new Mark(context);\n" - "instance.mark(\"%1\");" ) + "instance.mark(\"%1\",{\"accuracy\": \"exactly\"});" ) .arg( regString ); webview->page()->runJavaScript( script ); From df594e01085bd2cd199e14e4081f40e9d473e46f Mon Sep 17 00:00:00 2001 From: YiFang Xiao Date: Wed, 6 Dec 2023 14:40:17 +0800 Subject: [PATCH 24/24] opt: the address maybe not ordered should not use equal( == ) comparision --- src/ftshelpers.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ftshelpers.cc b/src/ftshelpers.cc index c0138f02..a84ff3f9 100644 --- a/src/ftshelpers.cc +++ b/src/ftshelpers.cc @@ -116,8 +116,9 @@ void makeFTSIndex( BtreeIndexing::BtreeDictionary * dict, QAtomicInt & isCancell for ( auto const & address : offsets ) { indexedDoc++; - if ( address > lastAddress && skip ) { + if ( address == lastAddress && skip ) { skip = false; + continue; } //skip until to the lastAddress; if ( skip ) {