summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bumpversion.cfg2
-rw-r--r--.codecov.yml8
-rw-r--r--.flake817
-rw-r--r--.github/CONTRIBUTING.asciidoc6
-rw-r--r--.github/workflows/bleeding.yml4
-rw-r--r--.github/workflows/ci.yml43
-rw-r--r--.gitignore1
-rw-r--r--.mypy.ini3
-rw-r--r--.pylintrc28
-rw-r--r--MANIFEST.in2
-rw-r--r--README.asciidoc39
-rw-r--r--doc/changelog.asciidoc280
-rw-r--r--doc/contributing.asciidoc21
-rw-r--r--doc/faq.asciidoc6
-rw-r--r--doc/help/commands.asciidoc58
-rw-r--r--doc/help/configuring.asciidoc36
-rw-r--r--doc/help/index.asciidoc6
-rw-r--r--doc/help/settings.asciidoc338
-rw-r--r--doc/img/cheatsheet-big.pngbin781120 -> 708315 bytes
-rw-r--r--doc/img/cheatsheet-small.pngbin30252 -> 26611 bytes
-rw-r--r--doc/install.asciidoc16
-rw-r--r--doc/quickstart.asciidoc6
-rw-r--r--doc/qutebrowser.1.asciidoc15
-rw-r--r--doc/stacktrace.asciidoc48
-rw-r--r--doc/userscripts.asciidoc1
-rw-r--r--misc/Makefile4
-rwxr-xr-xmisc/nsis/install.nsh15
-rwxr-xr-xmisc/nsis/qutebrowser.nsi5
-rw-r--r--misc/org.qutebrowser.qutebrowser.appdata.xml4
-rw-r--r--misc/org.qutebrowser.qutebrowser.desktop8
-rw-r--r--misc/qutebrowser.rcc13
-rw-r--r--misc/qutebrowser.spec5
-rw-r--r--misc/requirements/requirements-check-manifest.txt12
-rw-r--r--misc/requirements/requirements-dev.txt59
-rw-r--r--misc/requirements/requirements-dev.txt-raw2
-rw-r--r--misc/requirements/requirements-flake8.txt23
-rw-r--r--misc/requirements/requirements-flake8.txt-raw1
-rw-r--r--misc/requirements/requirements-mypy.txt31
-rw-r--r--misc/requirements/requirements-mypy.txt-raw1
-rw-r--r--misc/requirements/requirements-pip.txt8
-rw-r--r--misc/requirements/requirements-pyinstaller.txt6
-rw-r--r--misc/requirements/requirements-pylint.txt46
-rw-r--r--misc/requirements/requirements-pylint.txt-raw4
-rw-r--r--misc/requirements/requirements-pyqt-5.12.txt2
-rw-r--r--misc/requirements/requirements-pyqt-5.13.txt2
-rw-r--r--misc/requirements/requirements-pyqt-5.14.txt2
-rw-r--r--misc/requirements/requirements-pyqt-5.15.0.txt2
-rw-r--r--misc/requirements/requirements-pyqt-5.15.txt6
-rw-r--r--misc/requirements/requirements-pyqt-pyinstaller.txt7
-rw-r--r--misc/requirements/requirements-pyqt-pyinstaller.txt-raw2
-rw-r--r--misc/requirements/requirements-pyqt.txt6
-rw-r--r--misc/requirements/requirements-pyroma.txt21
-rw-r--r--misc/requirements/requirements-qutebrowser.txt-raw6
-rw-r--r--misc/requirements/requirements-sphinx.txt34
-rw-r--r--misc/requirements/requirements-tests-bleeding.txt9
-rw-r--r--misc/requirements/requirements-tests.txt82
-rw-r--r--misc/requirements/requirements-tests.txt-raw5
-rw-r--r--misc/requirements/requirements-tox.txt24
-rw-r--r--misc/requirements/requirements-vulture.txt2
-rw-r--r--misc/requirements/requirements-yamllint.txt6
-rw-r--r--misc/userscripts/README.md13
-rwxr-xr-xmisc/userscripts/cast31
-rwxr-xr-xmisc/userscripts/open_download23
-rwxr-xr-xmisc/userscripts/password_fill6
-rwxr-xr-xmisc/userscripts/qute-bitwarden59
-rwxr-xr-xmisc/userscripts/qute-keepassxc111
-rwxr-xr-xmisc/userscripts/qute-lastpass4
-rwxr-xr-xmisc/userscripts/qute-pass38
-rwxr-xr-xmisc/userscripts/qutedmenu6
-rwxr-xr-xmisc/userscripts/readability1
-rwxr-xr-xmisc/userscripts/readability-js3
-rwxr-xr-xmisc/userscripts/ripbang18
-rw-r--r--pytest.ini5
-rw-r--r--qutebrowser/__init__.py6
-rw-r--r--qutebrowser/app.py9
-rw-r--r--qutebrowser/browser/browsertab.py64
-rw-r--r--qutebrowser/browser/commands.py40
-rw-r--r--qutebrowser/browser/downloads.py36
-rw-r--r--qutebrowser/browser/downloadview.py17
-rw-r--r--qutebrowser/browser/greasemonkey.py4
-rw-r--r--qutebrowser/browser/hints.py48
-rw-r--r--qutebrowser/browser/history.py1
-rw-r--r--qutebrowser/browser/inspector.py18
-rw-r--r--qutebrowser/browser/navigate.py3
-rw-r--r--qutebrowser/browser/network/pac.py3
-rw-r--r--qutebrowser/browser/qtnetworkdownloads.py20
-rw-r--r--qutebrowser/browser/qutescheme.py4
-rw-r--r--qutebrowser/browser/shared.py15
-rw-r--r--qutebrowser/browser/signalfilter.py1
-rw-r--r--qutebrowser/browser/urlmarks.py2
-rw-r--r--qutebrowser/browser/webelem.py18
-rw-r--r--qutebrowser/browser/webengine/notification.py77
-rw-r--r--qutebrowser/browser/webengine/webenginedownloads.py17
-rw-r--r--qutebrowser/browser/webengine/webengineelem.py4
-rw-r--r--qutebrowser/browser/webengine/webengineinspector.py11
-rw-r--r--qutebrowser/browser/webengine/webenginequtescheme.py2
-rw-r--r--qutebrowser/browser/webengine/webenginesettings.py60
-rw-r--r--qutebrowser/browser/webengine/webenginetab.py91
-rw-r--r--qutebrowser/browser/webengine/webview.py5
-rw-r--r--qutebrowser/browser/webkit/http.py23
-rw-r--r--qutebrowser/browser/webkit/network/networkmanager.py11
-rw-r--r--qutebrowser/browser/webkit/network/networkreply.py21
-rw-r--r--qutebrowser/browser/webkit/webkithistory.py2
-rw-r--r--qutebrowser/browser/webkit/webkitinspector.py2
-rw-r--r--qutebrowser/browser/webkit/webkitsettings.py5
-rw-r--r--qutebrowser/browser/webkit/webkittab.py97
-rw-r--r--qutebrowser/browser/webkit/webpage.py11
-rw-r--r--qutebrowser/browser/webkit/webview.py13
-rw-r--r--qutebrowser/commands/argparser.py2
-rw-r--r--qutebrowser/commands/cmdexc.py21
-rw-r--r--qutebrowser/commands/command.py12
-rw-r--r--qutebrowser/commands/parser.py19
-rw-r--r--qutebrowser/commands/runners.py7
-rw-r--r--qutebrowser/commands/userscripts.py8
-rw-r--r--qutebrowser/completion/completer.py16
-rw-r--r--qutebrowser/completion/completiondelegate.py4
-rw-r--r--qutebrowser/completion/completionwidget.py42
-rw-r--r--qutebrowser/completion/models/completionmodel.py8
-rw-r--r--qutebrowser/completion/models/configmodel.py21
-rw-r--r--qutebrowser/completion/models/filepathcategory.py2
-rw-r--r--qutebrowser/completion/models/miscmodels.py20
-rw-r--r--qutebrowser/components/braveadblock.py20
-rw-r--r--qutebrowser/components/hostblock.py21
-rw-r--r--qutebrowser/components/misccommands.py86
-rw-r--r--qutebrowser/components/readlinecommands.py137
-rw-r--r--qutebrowser/components/scrollcommands.py4
-rw-r--r--qutebrowser/components/utils/blockutils.py2
-rw-r--r--qutebrowser/config/config.py8
-rw-r--r--qutebrowser/config/configcommands.py13
-rw-r--r--qutebrowser/config/configdata.yml156
-rw-r--r--qutebrowser/config/configexc.py10
-rw-r--r--qutebrowser/config/configfiles.py10
-rw-r--r--qutebrowser/config/configtypes.py8
-rw-r--r--qutebrowser/config/qtargs.py14
-rw-r--r--qutebrowser/config/stylesheet.py7
-rw-r--r--qutebrowser/config/websettings.py10
-rw-r--r--qutebrowser/extensions/loader.py42
-rw-r--r--qutebrowser/html/bindings.html2
-rw-r--r--qutebrowser/html/settings.html198
-rw-r--r--qutebrowser/html/warning-webkit.html6
-rw-r--r--qutebrowser/icons/qutebrowser-128x128.png (renamed from icons/qutebrowser-128x128.png)bin6426 -> 6426 bytes
-rw-r--r--qutebrowser/icons/qutebrowser-16x16.png (renamed from icons/qutebrowser-16x16.png)bin856 -> 856 bytes
-rw-r--r--qutebrowser/icons/qutebrowser-24x24.png (renamed from icons/qutebrowser-24x24.png)bin1339 -> 1339 bytes
-rw-r--r--qutebrowser/icons/qutebrowser-256x256.png (renamed from icons/qutebrowser-256x256.png)bin13690 -> 13690 bytes
-rw-r--r--qutebrowser/icons/qutebrowser-32x32.png (renamed from icons/qutebrowser-32x32.png)bin1624 -> 1624 bytes
-rw-r--r--qutebrowser/icons/qutebrowser-48x48.png (renamed from icons/qutebrowser-48x48.png)bin2399 -> 2399 bytes
-rw-r--r--qutebrowser/icons/qutebrowser-512x512.png (renamed from icons/qutebrowser-512x512.png)bin30109 -> 30109 bytes
-rw-r--r--qutebrowser/icons/qutebrowser-64x64.png (renamed from icons/qutebrowser-64x64.png)bin3179 -> 3179 bytes
-rw-r--r--qutebrowser/icons/qutebrowser-96x96.png (renamed from icons/qutebrowser-96x96.png)bin4776 -> 4776 bytes
-rw-r--r--qutebrowser/icons/qutebrowser-all.svg (renamed from icons/qutebrowser-all.svg)0
-rw-r--r--qutebrowser/icons/qutebrowser-favicon.svg (renamed from icons/qutebrowser-favicon.svg)0
-rw-r--r--qutebrowser/icons/qutebrowser.icns (renamed from icons/qutebrowser.icns)bin196205 -> 196205 bytes
-rw-r--r--qutebrowser/icons/qutebrowser.ico (renamed from icons/qutebrowser.ico)bin115490 -> 115490 bytes
-rw-r--r--qutebrowser/icons/qutebrowser.svg (renamed from icons/qutebrowser.svg)0
-rw-r--r--qutebrowser/icons/qutebrowser.xpm (renamed from icons/qutebrowser.xpm)0
-rw-r--r--qutebrowser/javascript/greasemonkey_wrapper.js20
-rw-r--r--qutebrowser/javascript/quirks/globalthis.user.js2
-rw-r--r--qutebrowser/keyinput/basekeyparser.py3
-rw-r--r--qutebrowser/keyinput/eventfilter.py2
-rw-r--r--qutebrowser/keyinput/keyutils.py19
-rw-r--r--qutebrowser/keyinput/modeman.py1
-rw-r--r--qutebrowser/mainwindow/mainwindow.py22
-rw-r--r--qutebrowser/mainwindow/prompt.py22
-rw-r--r--qutebrowser/mainwindow/statusbar/bar.py5
-rw-r--r--qutebrowser/mainwindow/statusbar/command.py5
-rw-r--r--qutebrowser/mainwindow/statusbar/url.py5
-rw-r--r--qutebrowser/mainwindow/tabbedbrowser.py112
-rw-r--r--qutebrowser/mainwindow/tabwidget.py121
-rw-r--r--qutebrowser/misc/checkpyver.py4
-rw-r--r--qutebrowser/misc/crashsignal.py8
-rw-r--r--qutebrowser/misc/debugcachestats.py26
-rw-r--r--qutebrowser/misc/earlyinit.py21
-rw-r--r--qutebrowser/misc/editor.py5
-rw-r--r--qutebrowser/misc/elf.py2
-rw-r--r--qutebrowser/misc/guiprocess.py42
-rw-r--r--qutebrowser/misc/ipc.py17
-rw-r--r--qutebrowser/misc/keyhintwidget.py1
-rw-r--r--qutebrowser/misc/lineparser.py1
-rw-r--r--qutebrowser/misc/miscwidgets.py2
-rw-r--r--qutebrowser/misc/msgbox.py1
-rw-r--r--qutebrowser/misc/objects.py4
-rw-r--r--qutebrowser/misc/quitter.py2
-rw-r--r--qutebrowser/misc/savemanager.py1
-rw-r--r--qutebrowser/misc/sessions.py9
-rw-r--r--qutebrowser/misc/split.py1
-rw-r--r--qutebrowser/misc/sql.py5
-rw-r--r--qutebrowser/misc/throttle.py1
-rw-r--r--qutebrowser/misc/utilcmds.py5
-rw-r--r--qutebrowser/qutebrowser.py25
-rw-r--r--qutebrowser/resources.py5474
-rw-r--r--qutebrowser/utils/debug.py4
-rw-r--r--qutebrowser/utils/log.py14
-rw-r--r--qutebrowser/utils/message.py9
-rw-r--r--qutebrowser/utils/objreg.py4
-rw-r--r--qutebrowser/utils/resources.py21
-rw-r--r--qutebrowser/utils/standarddir.py3
-rw-r--r--qutebrowser/utils/urlutils.py2
-rw-r--r--qutebrowser/utils/usertypes.py1
-rw-r--r--qutebrowser/utils/utils.py54
-rw-r--r--qutebrowser/utils/version.py8
-rw-r--r--requirements.txt19
-rwxr-xr-xscripts/asciidoc2html.py11
-rwxr-xr-xscripts/dev/build_release.py78
-rw-r--r--scripts/dev/change_release.py4
-rw-r--r--scripts/dev/changelog_urls.json159
-rw-r--r--scripts/dev/check_coverage.py4
-rw-r--r--scripts/dev/gen_resources.py26
-rw-r--r--scripts/dev/misc_checks.py15
-rw-r--r--scripts/dev/pylint_checkers/qute_pylint/modeline.py6
-rw-r--r--scripts/dev/pylint_checkers/qute_pylint/openencoding.py83
-rw-r--r--scripts/dev/recompile_requirements.py264
-rw-r--r--scripts/dev/run_pylint_on_tests.py5
-rwxr-xr-xscripts/dev/src2asciidoc.py22
-rwxr-xr-xscripts/dev/update_3rdparty.py51
-rwxr-xr-xscripts/dictcli.py14
-rw-r--r--scripts/hostblock_blame.py4
-rw-r--r--scripts/link_pyqt.py4
-rwxr-xr-xscripts/mkvenv.py2
-rw-r--r--scripts/opengl_info.py49
-rw-r--r--scripts/setupcommon.py2
-rwxr-xr-xsetup.py4
-rw-r--r--tests/conftest.py95
-rw-r--r--tests/end2end/conftest.py45
-rw-r--r--tests/end2end/data/brave-adblock/generate.py6
-rw-r--r--tests/end2end/data/click_element.html6
-rw-r--r--tests/end2end/data/darkmode/mathml-display.html23
-rw-r--r--tests/end2end/data/darkmode/mathml-inline.html (renamed from tests/end2end/data/darkmode/mathml.html)0
-rw-r--r--tests/end2end/data/hints/html/README.md2
-rw-r--r--tests/end2end/data/hints/html/invisible.html (renamed from tests/end2end/data/hints/invisible.html)2
-rw-r--r--tests/end2end/data/hints/html/tabindex-negative.html13
-rw-r--r--tests/end2end/features/hints.feature5
-rw-r--r--tests/end2end/features/misc.feature47
-rw-r--r--tests/end2end/features/notifications.feature8
-rw-r--r--tests/end2end/features/search.feature20
-rw-r--r--tests/end2end/features/tabs.feature99
-rw-r--r--tests/end2end/features/utilcmds.feature1
-rw-r--r--tests/end2end/fixtures/quteprocess.py18
-rw-r--r--tests/end2end/fixtures/test_webserver.py5
-rw-r--r--tests/end2end/fixtures/webserver.py10
-rw-r--r--tests/end2end/fixtures/webserver_sub.py6
-rw-r--r--tests/end2end/test_hints_html.py17
-rw-r--r--tests/end2end/test_insert_mode.py2
-rw-r--r--tests/end2end/test_invocations.py91
-rw-r--r--tests/helpers/fixtures.py4
-rw-r--r--tests/helpers/logfail.py3
-rw-r--r--tests/helpers/testutils.py42
-rw-r--r--tests/unit/api/test_cmdutils.py3
-rw-r--r--tests/unit/browser/test_caret.py2
-rw-r--r--tests/unit/browser/test_history.py2
-rw-r--r--tests/unit/browser/test_inspector.py3
-rw-r--r--tests/unit/browser/webkit/http/test_http.py15
-rw-r--r--tests/unit/browser/webkit/test_webkitelem.py22
-rw-r--r--tests/unit/commands/test_cmdexc.py40
-rw-r--r--tests/unit/commands/test_parser.py14
-rw-r--r--tests/unit/completion/test_completer.py7
-rw-r--r--tests/unit/completion/test_completionwidget.py15
-rw-r--r--tests/unit/completion/test_models.py32
-rw-r--r--tests/unit/components/test_hostblock.py10
-rw-r--r--tests/unit/components/test_readlinecommands.py61
-rw-r--r--tests/unit/config/test_config.py4
-rw-r--r--tests/unit/config/test_configcommands.py14
-rw-r--r--tests/unit/config/test_configexc.py26
-rw-r--r--tests/unit/config/test_configfiles.py5
-rw-r--r--tests/unit/config/test_configtypes.py11
-rw-r--r--tests/unit/config/test_configutils.py12
-rw-r--r--tests/unit/config/test_qtargs.py28
-rw-r--r--tests/unit/config/test_websettings.py2
-rw-r--r--tests/unit/extensions/test_loader.py8
-rw-r--r--tests/unit/keyinput/test_basekeyparser.py11
-rw-r--r--tests/unit/mainwindow/statusbar/test_textbase.py6
-rw-r--r--tests/unit/mainwindow/test_prompt.py18
-rw-r--r--tests/unit/misc/test_checkpyver.py7
-rw-r--r--tests/unit/misc/test_editor.py15
-rw-r--r--tests/unit/misc/test_elf.py17
-rw-r--r--tests/unit/misc/test_guiprocess.py22
-rw-r--r--tests/unit/misc/test_keyhints.py4
-rw-r--r--tests/unit/misc/test_miscwidgets.py2
-rw-r--r--tests/unit/misc/userscripts/test_qute_lastpass.py12
-rw-r--r--tests/unit/test_qutebrowser.py60
-rw-r--r--tests/unit/utils/test_log.py14
-rw-r--r--tests/unit/utils/test_qtutils.py10
-rw-r--r--tests/unit/utils/test_resources.py2
-rw-r--r--tests/unit/utils/test_standarddir.py14
-rw-r--r--tests/unit/utils/test_urlmatch.py157
-rw-r--r--tests/unit/utils/test_urlutils.py1
-rw-r--r--tests/unit/utils/test_utils.py67
-rw-r--r--tests/unit/utils/test_version.py29
-rw-r--r--tox.ini10
288 files changed, 4293 insertions, 7497 deletions
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index a95ba918d..5ae20070b 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 2.3.0
+current_version = 2.5.1
commit = True
message = Release v{new_version}
tag = True
diff --git a/.codecov.yml b/.codecov.yml
index 8646cac9a..3ceb4eeec 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -1,7 +1,11 @@
coverage:
status:
- project: false
- patch: false
+ project:
+ default:
+ informational: true
+ patch:
+ default:
+ informational: true
changes: false
comment: false
diff --git a/.flake8 b/.flake8
index 5cfa2e76c..34a829a4b 100644
--- a/.flake8
+++ b/.flake8
@@ -40,7 +40,10 @@ exclude = .*,__pycache__,resources.py
# A003: Builtin name for class attribute (needed for overridden methods)
# W503: like break before binary operator
# W504: line break after binary operator
-# FI15: __future__ import "generator_stop" missing
+# FI18: __future__ import "annotations" missing
+# PT004: fixture '{name}' does not return anything, add leading underscore
+# PT011: pytest.raises(ValueError) is too broad, set the match parameter or use a more specific exception
+# PT012: pytest.raises() block should contain a single simple statement
ignore =
B001,B008,B305,
E128,E226,E265,E501,E402,E266,E722,E731,
@@ -49,9 +52,12 @@ ignore =
P101,P102,P103,
D102,D103,D106,D107,D104,D105,D209,D211,D401,D402,D403,D412,D413,
A003,
- W503, W504
- FI15
-min-version = 3.6.1
+ W503, W504,
+ FI18,
+ PT004,
+ PT011,
+ PT012
+min-version = 3.7.0
max-complexity = 12
per-file-ignores =
qutebrowser/api/hook.py : N801
@@ -62,3 +68,6 @@ per-file-ignores =
copyright-check = True
copyright-regexp = # Copyright [\d-]+ .*
copyright-min-file-size = 110
+pytest-fixture-no-parentheses = True
+pytest-mark-no-parentheses = True
+pytest-parametrize-names-type = csv
diff --git a/.github/CONTRIBUTING.asciidoc b/.github/CONTRIBUTING.asciidoc
index bf9f0e66b..0d03af336 100644
--- a/.github/CONTRIBUTING.asciidoc
+++ b/.github/CONTRIBUTING.asciidoc
@@ -5,11 +5,11 @@ open pull requests.
- Before you start to work on something, please leave a comment on the relevant
issue (or open one). This makes sure there is no duplicate work done.
-- Either run the testsuite locally, or keep an eye on Travis CI / AppVeyor
- after pushing changes.
+- Either run the testsuite locally, or keep an eye on the CI at the end of the
+ pull request page after pushing changes.
- If you are stuck somewhere or have questions,
- https://github.com/qutebrowser/qutebrowser#getting-help[please ask]!
+ https://github.com/qutebrowser/qutebrowser/blob/master/doc/help/index.asciidoc#getting-help[please ask]!
See the link:../doc/contributing.asciidoc[full contribution documentation] for
details and other useful hints.
diff --git a/.github/workflows/bleeding.yml b/.github/workflows/bleeding.yml
index 766f535d7..b2370357f 100644
--- a/.github/workflows/bleeding.yml
+++ b/.github/workflows/bleeding.yml
@@ -15,7 +15,7 @@ jobs:
container:
image: "qutebrowser/ci:archlinux-webengine-unstable"
env:
- PY_COLORS: "1"
+ FORCE_COLOR: "1"
DOCKER: "archlinux-webengine-unstable"
CI: true
PYTEST_ADDOPTS: "--color=yes"
@@ -58,7 +58,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v2
with:
- python-version: 3.9
+ python-version: "3.10"
- name: Get asciidoc
uses: actions/checkout@v2
with:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e50ba2c60..288efbc02 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -6,7 +6,7 @@ on:
- 'dependabot/*'
pull_request:
env:
- PY_COLORS: "1"
+ FORCE_COLOR: "1"
MYPY_FORCE_TERMINAL_WIDTH: "180"
jobs:
@@ -43,7 +43,7 @@ jobs:
key: "${{ matrix.testenv }}-${{ hashFiles('misc/requirements/requirements-*.txt') }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('scripts/dev/pylint_checkers/qute_pylint/*.py') }}"
- uses: actions/setup-python@v2
with:
- python-version: '3.8'
+ python-version: '3.10'
- uses: actions/setup-node@v2-beta
with:
node-version: '12.x'
@@ -53,7 +53,7 @@ jobs:
- name: Install dependencies
run: |
[[ ${{ matrix.testenv }} == eslint ]] && npm install -g eslint
- [[ ${{ matrix.testenv }} == docs ]] && sudo apt-get install --no-install-recommends asciidoc
+ [[ ${{ matrix.testenv }} == docs ]] && sudo apt-get update && sudo apt-get install --no-install-recommends asciidoc
if [[ ${{ matrix.testenv }} == shellcheck ]]; then
scversion="stable"
bindir="$HOME/.local/bin"
@@ -105,44 +105,45 @@ jobs:
fail-fast: false
matrix:
include:
- ### PyQt 5.12 (Python 3.6)
- - testenv: py36-pyqt512
+ ### PyQt 5.12 (Python 3.7)
+ - testenv: py37-pyqt512
os: ubuntu-18.04
- python: 3.6
+ python: "3.7"
### PyQt 5.13 (Python 3.7)
- testenv: py37-pyqt513
os: ubuntu-20.04
- python: 3.7
+ python: "3.7"
### PyQt 5.14 (Python 3.8)
- testenv: py38-pyqt514
os: ubuntu-20.04
- python: 3.8
+ python: "3.8"
### PyQt 5.15.0 (Python 3.9)
- testenv: py39-pyqt5150
os: ubuntu-20.04
- python: 3.9
- ### PyQt 5.15 (Python 3.9, with coverage)
- - testenv: py39-pyqt515-cov
+ python: "3.9"
+ ### PyQt 5.15 (Python 3.10, with coverage)
+ - testenv: py310-pyqt515-cov
os: ubuntu-20.04
- python: 3.9
- ### PyQt 5.15 (Python 3.10)
- - testenv: py310-pyqt515
- os: ubuntu-20.04
- python: 3.10-dev
+ python: "3.10"
+ ### PyQt 5.15 (Python 3.11)
+ # - testenv: py311-pyqt515
+ # os: ubuntu-20.04
+ # python: "3.11-dev"
+ # experimental: true
### macOS: PyQt 5.15 (Python 3.9 to match PyInstaller env)
- testenv: py39-pyqt515
os: macos-10.15
- python: 3.7
+ python: "3.9"
args: "tests/unit" # Only run unit tests on macOS
### macOS Big Sur
# - testenv: py39-pyqt515
# os: macos-11.0
- # python: 3.9
+ # python: "3.9"
# args: "tests/unit" # Only run unit tests on macOS
### Windows: PyQt 5.15 (Python 3.9 to match PyInstaller env)
- testenv: py39-pyqt515
os: windows-2019
- python: 3.9
+ python: "3.9"
runs-on: "${{ matrix.os }}"
steps:
- uses: actions/checkout@v2
@@ -197,12 +198,12 @@ jobs:
with:
persist-credentials: false
- name: Initialize CodeQL
- uses: github/codeql-action/init@v1
+ uses: github/codeql-action/init@v2
with:
languages: javascript, python
queries: +security-extended
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v1
+ uses: github/codeql-action/analyze@v2
irc:
timeout-minutes: 2
diff --git a/.gitignore b/.gitignore
index 31c4ca3b7..ccfc12ccb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,6 +49,7 @@ TODO
/scripts/testbrowser/cpp/webengine/testbrowser
/scripts/testbrowser/cpp/webengine/.qmake.stash
/scripts/dev/pylint_checkers/qute_pylint.egg-info
+/scripts/dev/pylint_checkers/build
/misc/file_version_info.txt
/doc/extapi/_build
/misc/nsis/include
diff --git a/.mypy.ini b/.mypy.ini
index 289f3eb87..b347323f5 100644
--- a/.mypy.ini
+++ b/.mypy.ini
@@ -1,5 +1,5 @@
[mypy]
-python_version = 3.6
+python_version = 3.7
### --strict
warn_unused_configs = True
@@ -20,6 +20,7 @@ strict_equality = True
### Other strictness flags
warn_unreachable = True
disallow_any_unimported = True
+enable_error_code = ignore-without-code
### Output
show_error_codes = True
diff --git a/.pylintrc b/.pylintrc
index b771078f3..47d3a163d 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -5,23 +5,26 @@ ignore=resources.py
extension-pkg-whitelist=PyQt5,sip
load-plugins=qute_pylint.config,
qute_pylint.modeline,
- qute_pylint.openencoding,
pylint.extensions.docstyle,
pylint.extensions.emptystring,
- pylint.extensions.broad_try_clause,
pylint.extensions.overlapping_exceptions,
-persistent=n
+ pylint.extensions.code_style,
+ pylint.extensions.comparison_placement,
+ pylint.extensions.for_any_all,
+ pylint.extensions.docstyle,
+ pylint.extensions.check_elif,
+ pylint.extensions.typing,
+ pylint.extensions.docparams,
+ pylint.extensions.private_import,
-[broad_try_clause]
-max-try-statements=7
+persistent=n
+py-version=3.7
[MESSAGES CONTROL]
enable=all
disable=locally-disabled,
- locally-enabled,
suppressed-message,
fixme,
- no-self-use,
cyclic-import,
blacklisted-name,
logging-format-interpolation,
@@ -46,7 +49,15 @@ disable=locally-disabled,
too-many-statements,
too-few-public-methods,
import-outside-toplevel,
- bad-continuation # This lint disagrees with Black
+ consider-using-f-string,
+ logging-fstring-interpolation,
+ raise-missing-from,
+ consider-using-tuple,
+ consider-using-namedtuple-or-dataclass,
+ missing-raises-doc,
+ missing-type-doc,
+ missing-param-doc,
+ useless-param-doc,
[BASIC]
function-rgx=[a-z_][a-z0-9_]{2,50}$
@@ -58,6 +69,7 @@ argument-rgx=[a-z_][a-z0-9_]{0,30}$
variable-rgx=[a-z_][a-z0-9_]{0,30}$
docstring-min-length=3
no-docstring-rgx=(^_|^main$)
+class-const-naming-style = snake_case
[FORMAT]
max-line-length=88
diff --git a/MANIFEST.in b/MANIFEST.in
index ed4b5e5b1..81bf824b7 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -4,7 +4,7 @@ recursive-include qutebrowser/javascript *.js
graft tests
graft qutebrowser/html
graft qutebrowser/3rdparty
-graft icons
+graft qutebrowser/icons
graft doc/img
graft misc/apparmor
graft misc/userscripts
diff --git a/README.asciidoc b/README.asciidoc
index 5c87df857..fe0c23f2d 100644
--- a/README.asciidoc
+++ b/README.asciidoc
@@ -7,7 +7,7 @@ qutebrowser
===========
// QUTE_WEB_HIDE
-image:icons/qutebrowser-64x64.png[qutebrowser logo] *A keyboard-driven, vim-like browser based on PyQt5 and Qt.*
+image:qutebrowser/icons/qutebrowser-64x64.png[qutebrowser logo] *A keyboard-driven, vim-like browser based on Python and Qt.*
image:https://github.com/qutebrowser/qutebrowser/workflows/CI/badge.svg["Build Status", link="https://github.com/qutebrowser/qutebrowser/actions?query=workflow%3ACI"]
image:https://codecov.io/github/qutebrowser/qutebrowser/coverage.svg?branch=master["coverage badge",link="https://codecov.io/github/qutebrowser/qutebrowser?branch=master"]
@@ -16,7 +16,7 @@ link:https://www.qutebrowser.org[website] | link:https://blog.qutebrowser.org[bl
// QUTE_WEB_HIDE_END
qutebrowser is a keyboard-focused browser with a minimal GUI. It's based
-on Python and PyQt5 and free software, licensed under the GPL.
+on Python and Qt and free software, licensed under the GPL.
It was inspired by other browsers/addons like dwb and Vimperator/Pentadactyl.
@@ -63,7 +63,7 @@ ways:
* Use the built-in `:report` command or the automatic crash dialog.
* Open an issue in the Github issue tracker.
* Write a mail to the
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[mailinglist] at
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser[mailinglist] at
mailto:qutebrowser@lists.qutebrowser.org[].
For security bugs, please contact me directly at mail@qutebrowser.org, GPG ID
@@ -74,7 +74,7 @@ Requirements
The following software and libraries are required to run qutebrowser:
-* https://www.python.org/[Python] 3.6.1 or newer
+* https://www.python.org/[Python] 3.7 or newer
* https://www.qt.io/[Qt] 5.12.0 or newer (5.12 LTS or 5.15 recommended, Qt 6 is
not supported yet) with the following modules:
- QtCore / qtbase
@@ -96,10 +96,9 @@ websites and using it for transmission of sensitive data._
* https://palletsprojects.com/p/jinja/[jinja2]
* https://github.com/yaml/pyyaml[PyYAML]
-On older Python versions (3.6/3.7/3.8), the following backports are also required:
+On older Python versions (3.7/3.8), the following backports are also required:
-* https://importlib-resources.readthedocs.io/[importlib_resources] (Python 3.8 or older)
-* https://github.com/ericvsmith/dataclasses[dataclasses] (Python 3.6 only)
+* https://importlib-resources.readthedocs.io/[importlib_resources]
The following libraries are optional:
@@ -109,8 +108,8 @@ The following libraries are optional:
QtWebEngine backend.
* On Windows, https://pypi.python.org/pypi/colorama/[colorama] for colored log
output.
-* https://importlib-metadata.readthedocs.io/[importlib_resources] on Python 3.7
- or older, to improve QtWebEngine version detection when PyQtWebEngine is
+* https://importlib-metadata.readthedocs.io/[importlib_metadata] on Python 3.7,
+ to improve QtWebEngine version detection when PyQtWebEngine is
installed via pip (thus, this dependency usually isn't relevant for
packagers).
* https://asciidoc.org/[asciidoc] to generate the documentation for the `:help`
@@ -203,20 +202,16 @@ Active
* https://fanglingsu.github.io/vimb/[vimb] (C, GTK+ with WebKit2)
* https://luakit.github.io/luakit/[luakit] (C/Lua, GTK+ with WebKit2)
-* https://nyxt.atlas.engineer/[Nyxt browser] (formerly "Next browser", Lisp, Emacs-like but also offers Vim bindings, QtWebKit or GTK+/WebKit2 - note there was a https://jgkamat.gitlab.io/blog/next-rce.html[critical remote code execution] which was handled quite badly)
+* https://nyxt.atlas.engineer/[Nyxt browser] (formerly "Next browser", Lisp, Emacs-like but also offers Vim bindings, QtWebEngine or GTK+/WebKit2 - note there was a https://jgkamat.gitlab.io/blog/next-rce.html[critical remote code execution in 2019] which was handled quite badly)
* https://vieb.dev/[Vieb] (JavaScript, Electron)
* https://surf.suckless.org/[surf] (C, GTK+ with WebKit1/WebKit2)
* Chrome/Chromium addons:
- https://vimium.github.io/[Vimium],
- https://github.com/dcchambers/vb4c[vb4c] (fork of cVim)
+ https://vimium.github.io/[Vimium]
* Firefox addons (based on WebExtensions):
- https://github.com/tridactyl/tridactyl[Tridactyl],
- https://addons.mozilla.org/en-GB/firefox/addon/vimium-ff/[Vimium-FF] (experimental),
- https://github.com/ueokande/vim-vixen[Vim Vixen],
- https://github.com/amedama41/vvimpulation[VVimpulation]
+ https://tridactyl.xyz/[Tridactyl],
+ https://addons.mozilla.org/en-GB/firefox/addon/vimium-ff/[Vimium-FF]
* Addons for Firefox and Chrome:
https://github.com/brookhong/Surfingkeys[Surfingkeys],
- https://krabby.netlify.com/[Krabby],
https://lydell.github.io/LinkHints/[Link Hints] (hinting only)
* Addons for Safari:
https://televator.net/vimari/[Vimari]
@@ -231,23 +226,27 @@ main inspiration for qutebrowser)
QtWebEngine, https://github.com/parkouss/webmacs/issues/137[unmaintained])
* https://sourceforge.net/p/vimprobable/wiki/Home/[vimprobable] (C, GTK+ with
WebKit1)
-* https://wiki.archlinux.org/index.php?title=Jumanji[jumanji] (C, GTK+ with WebKit1,
-original site is gone but the Arch Linux wiki has some data)
+* https://pwmt.org/projects/jumanji/[jumanji] (C, GTK+ with WebKit1)
* http://conkeror.org/[conkeror] (Javascript, Emacs-like, XULRunner/Gecko)
* https://www.uzbl.org/[uzbl] (C, GTK+ with WebKit1/WebKit2)
* https://github.com/conformal/xombrero[xombrero] (C, GTK+ with WebKit1)
* https://github.com/linkdd/cream-browser[Cream Browser] (C, GTK+ with WebKit1)
+* https://github.com/jun7/wyeb[wyeb] (C, GTK+ with WebKit2)
* Firefox addons (not based on WebExtensions or no recent activity):
http://www.vimperator.org/[Vimperator],
http://bug.5digits.org/pentadactyl/index[Pentadactyl],
https://github.com/akhodakivskiy/VimFx[VimFx] (seems to offer a
https://gir.st/blog/legacyfox.htm[hack] to run on modern Firefox releases),
- https://github.com/shinglyu/QuantumVim[QuantumVim]
+ https://github.com/shinglyu/QuantumVim[QuantumVim],
+ https://github.com/ueokande/vim-vixen[Vim Vixen] (ESR only),
+ https://github.com/amedama41/vvimpulation[VVimpulation],
+ https://krabby.netlify.com/[Krabby]
* Chrome/Chromium addons:
https://github.com/k2nr/ViChrome/[ViChrome],
https://github.com/jinzhu/vrome[Vrome],
https://github.com/lusakasa/saka-key[Saka Key] (https://github.com/lusakasa/saka-key/issues/171[unmaintained]),
https://github.com/1995eaton/chromium-vim[cVim],
+ https://github.com/dcchambers/vb4c[vb4c] (fork of cVim, https://github.com/dcchambers/vb4c/issues/23#issuecomment-810694017[unmaintained]),
https://glee.github.io/[GleeBox]
License
diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc
index 008c693a3..f75f3faa9 100644
--- a/doc/changelog.asciidoc
+++ b/doc/changelog.asciidoc
@@ -15,17 +15,269 @@ breaking changes (such as renamed commands) can happen in minor releases.
// `Fixed` for any bug fixes.
// `Security` to invite users to upgrade in case of vulnerabilities.
+[[v3.0.0]]
+v3.0.0 (unreleased)
+-------------------
+
+Added
+~~~~~
+
+- On invalid commands/settings with a similarly spelled match, qutebrowser now
+ suggests the correct name in its error messages.
+- New features in userscripts:
+ * `qutedmenu` gained new `window` and `private` options.
+ * `qute-keepassxc` now supports unlock-on-demand, multiple account
+ selection via rofi, and inserting TOTP-codes (experimental).
+
+Removed
+~~~~~~~
+
+- Support for Python 3.6 is dropped, as it's been
+ https://discuss.python.org/t/python-3-6-rides-into-the-sunset/12964[end-of-life upstream]
+ since December 2021. Python 3.7.0 or newer is now required.
+- It's planned to drop support for various legacy platforms and libraries which
+ are unsupported upstream, such as:
+ * Qt before 5.15 LTS (plus adding support for Qt 6.2+)
+ * The QtWebKit backend
+ * macOS 10.14 (via Homebrew)
+ * 32-bit Windows (via Qt)
+ * Windows 8 (via Qt)
+ * Windows 10 before 1809 (via Qt)
+ * Possibly other more minor dependency changes
+
+Changed
+~~~~~~~
+
+- The qutebrowser icons got moved from `icons/` to `qutebrowser/icons` in the
+ repository, so that it's possible for qutebrowser to load them using Python's
+ resource system (rather than compiling them into a Qt resource file).
+ Packagers are advised to use `misc/Makefile` if possible, which has been
+ updated with the new paths.
+- The `content.javascript.can_access_clipboard` setting got renamed to
+ `content.javascript.clipboard` and now understands three different values
+ rather than being a boolean: `none` (formerly `false`), `access` (formerly
+ `true`) and `access-paste` (additionally allows pasting content, needed for
+ websites like Photopea or GitHub Codespaces).
+- The default `hints.selectors` now also match the `treeitem` ARIA roles.
+- The `:click-element` command now can also click elements based on its ID
+ (`id`), a CSS selector (`css`), a position (`position`), or click the
+ currently focused element (`focused`).
+- The `:click-element` command now can select the first found element via
+ `--select-first`.
+
+Fixed
+~~~~~
+
+- When the devtools are clicked but `input.insert_mode.auto_enter` is set to
+ `false`, insert mode now isn't entered anymore.
+
+[[v2.5.2]]
+v2.5.2 (unreleased)
+-------------------
+
+Fixed
+~~~~~
+
+- The `install` and `stacktrace` help pages are now included in the docs
+ shipped with qutebrowser when using the recommended packaging workflow.
+- The Windows installer now more consistently uses the configured Windows colors
+- The Windows installer now bases the desktop/start menu icon choices on the
+ existing install, if upgrading.
+
+[[v2.5.1]]
+v2.5.1 (2022-05-26)
+-------------------
+
+Fixed
+~~~~~
+
+- The `qute-pass` userscript is marked as executable again.
+- PDF.js now works properly again with the macOS and Windows releases.
+- The MathML workaround for darkmode (e.g. black on black Wikipedia formula)
+ now also works for display (rather than inline) math.
+- The `content.proxy` setting can now correctly be set to arbitrary values via
+ the `qute://settings` page again.
+- Fixed issues with Chromium version detection on Archlinux with
+ qt5-webengine 5.15.9-3.
+- Fixed a rare possible crash with invalid `Content-Disposition` headers.
+- Fixes for various notification-related crashes:
+ * With the `tiramisu` notification server (due to invalid behavior of the server, now a non-fatal error)
+ * With the `budgie` notification server when closing a notification (due to invalid behavior of the server, now worked around)
+ * When a server exits with an unsuccessful exit status (now a non-fatal error)
+ * When a server couldn't be started successfully (now a non-fatal error)
+ * With the `herbe` notification presenter, when the website tries to close
+ the notification after the user accepting (right-clicking) it.
+- Fixes in userscripts:
+ * The `qute-bitwarden` userscript now correctly searches for entries for
+ sites on a subdomain of an unrecognized TLD. subdomain names. Previously
+ `my.site.local` would have searched in bitwarden for `my.sitelocal`,
+ losing the rightmost dot.
+
+[[v2.5.0]]
+v2.5.0 (2022-04-01)
+-------------------
+
+Deprecated
+~~~~~~~~~~
+
+- v2.5.x will be the last release of qutebrowser 2.
+ **For the upcoming 3.0.0 release**, it's planned to drop support for various
+ legacy platforms and libraries which are unsupported upstream, such as:
+ * Qt before 5.15 LTS (plus adding support for Qt 6.2+)
+ * Python 3.6
+ * The QtWebKit backend
+ * macOS 10.14 (via Homebrew)
+ * 32-bit Windows (via Qt)
+ * Windows 8 (via Qt)
+ * Windows 10 before 1809 (via Qt)
+ * Possibly other more minor dependency changes
+- The `:rl-unix-word-rubout` command (`<Ctrl-W>` in command/prompt modes) has
+ been deprecated. Use `:rl-rubout " "` instead.
+- The `:rl-unix-filename-rubout` command has been deprecated. Use either
+ `:rl-rubout "/ "` (classic readline behavior) or `:rl-filename-rubout` (using
+ OS path separator and ignoring spaces) instead.
+
+Changed
+~~~~~~~
+
+- Improved message if a spawned process wasn't found and a Flatpak container is
+ in use.
+- The `:tab-move` command now takes `start` and `end` as `index` to move a tab
+ to the first/last position.
+- Tests now automatically pick the backend (QtWebKit/QtWebEngine) based on
+ what's available. The `QUTE_BDD_WEBENGINE` environment variable and
+ `--qute-bdd-webengine` argument got replaced by `QUTE_TESTS_BACKEND` and
+ `--qute-backend` respectively, which can be set to either `webengine` or
+ `webkit`.
+- Using `:tab-give` or `:tab-take` on the last tab in a window now always
+ closes that window, no matter what `tabs.last_close` is set to.
+- Redesigned `qute://settings` (`:set`) page with buttons for options with
+ fixed values.
+- The default `hint.selectors` now match more ARIA roles (`tab`, `checkbox`,
+ `menuitem`, `menuitemcheckbox` and `menuitemradio`).
+- Using e.g. `:bind --mode=passthrough` now scrolls to the passthrough section
+ on the `qute://bindings` page.
+- Clicking on a notification now tries to focus the tab where the notification
+ is coming from. Note this might not work properly if there is more than one
+ tab from the same host open.
+- Improvements to userscripts:
+ * `qute-bitwarden` understands a new `--password-prompt-invocation`, which can
+ be used to specify a tool other than `rofi` to ask for a password.
+ * `cast` now uses `yt-dlp` if available (falling back to `youtube-dl` if not).
+ It also lets users override the tool to use via a `QUTE_CAST_YTDL_PROGRAM`
+ environment variable.
+ * `qute-pass` now understands a new `--prefix` argument if used in gopass
+ mode, which gets passed as subfolder prefix to `gopass`.
+ * `open_download` now supports Flatpak by using its XDG Desktop Portal.
+ * `open_download` now waits for the exit status of `xdg-open`, causing
+ qutebrowser to report any issues with it.
+- The `content.headers.custom` setting now accepts empty strings as values,
+ resulting in an empty header being sent.
+- Renamed settings:
+ * `qt.low_end_device_mode` -> `qt.chromium.low_end_device_mode`
+ * `qt.process_model` -> `qt.chromium.process_model`
+- System-wide userscripts are now discovered from the correct location when
+ running via Flatpak (`/app/share` rather than `/usr/share`).
+- Filename prompts now don't display a `..` entry in the list of files anymore.
+ To get back to the parent directory, either type `../` manually, or use the new
+ `:rl-filename-rubout` command, bound to `<Ctrl-Shift-W>` by default.
+
+Added
+~~~~~
+
+- New `input.match_counts` option which allows to turn off count matching for
+ more emacs-like bindings.
+- New `{relative_index}` field for `tabs.title.format` (and `.pinned_format`)
+ which shows relative tab numbers.
+- New `input.mode_override` option which allows overriding the current mode
+ based on the new URL when navigating or switching tabs.
+- New `qt.chromium.sandboxing` setting which allows to disable Chromium's
+ sandboxing (mainly intended for development and testing).
+- New `QUTE_TAB_INDEX` variable for userscripts, containing the index of the
+ current tab.
+- New `editor.remove_file` setting which can be set to `False` to keep all
+ temporary editor files after closing the external editor.
+- New `:rl-rubout` command replacing `:rl-unix-word-rubout` (and optionally
+ `:rl-unix-filename-rubout`), taking a delimiter as argument.
+- New `:rl-filename-rubout` command, using the OS path separator and ignoring
+ spaces. The command also gets shown in the suggested commands for a download
+ filename prompt now.
+
+Fixed
+~~~~~
+
+- When `search.incremental` is disabled, searching using `/text` followed by a
+ backwards search via `?text` (or vice-versa) now correctly changes the search
+ direction.
+- Elements getting a hint due to a `tabindex` now are skipped if it's set to
+ `-1`, reducing some false-positives.
+- The audible indicator (`[A]`) now uses a 2s cooldown when the audio goes
+ silent, equivalent with the behavior of older QtWebEngine versions.
+- With `confirm_quit` set to `downloads`, the confirmation dialog is now only
+ shown when closing the last window (rather than closing any window, which
+ would continue running that window's downloads). Unfortunately, more issues
+ with `confirm_quit` and multiple windows remain.
+- Crash when a previous crash-log file contains non-ASCII characters (which
+ should never happen unless it was edited manually)
+- Due to changes in Debian, an old workaround (for broken QtWebEngine patching
+ on Debian) caused the inferior qutebrowser error page to be displayed, when
+ Chromium's would have worked fine. The workaround was now dropped.
+- Crash when using `<Ctrl-D>` (`:completion-item-del`) in the `:tab-focus`
+ list, rather than `:tab-select`.
+- Work around a Qt issue causing `:spawn` to run executables from the current
+ directory if no system-wide executable was found. The underlying Qt bug is
+ tracked as https://lists.qt-project.org/pipermail/announce/2022-February/000333.html[CVE-2022-25255],
+ though the impact with typical qutebrowser usage is low: Normally,
+ qutebrowser is run from a fixed location (usually the users home directory),
+ and `:spawn` is not typically used with executables that don't exist. The main
+ security impact of this bug is in tools like text editors, which are often
+ executed in untrusted directories and might attempt to run auxiliary tools
+ automatically.
+- When `:rl-rubout` or `:rl-filename-rubout` (formerly `:rl-unix-word-rubout`
+ and `:rl-unix-filename-rubout`) were used on a string not starting with the
+ given delimiter, they failed to delete the first character, which is now fixed.
+- Fixes in userscripts:
+ * `ripbang` now works again (it got blocked due to a missing user agent and
+ used outdated qutebrowser commands before)
+ * `keepassxc` now has a properly working `--insecure` flag
+- Speculative fix for an immediate crash at start with the macOS/Windows
+ binaries (in certain rare environments).
+- Speculative fix for a qutebrowser crash when the notification daemon crashes
+ while showing the notification.
+- Fix crash when using `:screenshot` with an invalid `--rect` argument.
+- Added a site-specific quirk to make cookie dialogs on StackExchange pages
+ (such as Stack Overflow) work on Qt 5.12.
+
[[v2.4.0]]
-v2.4.0 (unreleased)
+v2.4.0 (2021-10-21)
-------------------
+Security
+~~~~~~~~
+
+- **CVE-2021-41146**: Fix arbitrary command execution on Windows via URL handler
+ argument injection. See the
+ https://github.com/qutebrowser/qutebrowser/security/advisories/GHSA-vw27-fwjf-5qxm[security advisory]
+ for details.
+
+Added
+~~~~~
+
+- New `content.blocking.hosts.block_subdomains` setting which can be used to
+ disable the subdomain blocking for the hosts-based adblocker introduced in
+ v2.3.0.
+- New `downloads.prevent_mixed_content` setting to prevent insecure
+ mixed-content downloads (true by default).
+- New `--private` flag for `:tab-clone`, which clones a tab into a new private
+ window, mirroring the same flags for `:open` and `:tab-give`.
+
Fixed
~~~~~
- Switching tabs via mouse wheel scrolling now works properly on macOS. Set
`tabs.mousewheel_switching` to false if you prefer the previous behavior.
-- Crash when entering unicode surrogates into the filename prompt.
-- `UnboundLocalError` in `qute-keepass` when the database couldn't be opened.
+- Speculative fix for a crash when closing qutebrowser while a systray
+ notification is shown.
Changed
~~~~~~~
@@ -34,15 +286,33 @@ Changed
- When opening a file qutebrowser can't handle from a `file:///` directory
listing, qutebrowser now opens it with the default application rather than
displaying a download prompt.
+- In Greasemonkey scripts, using "overrideMimeType" with GM_xmlhttpRequest is
+ now supported.
+- `:hint --rapid` is now supported for the `tab` hinting target no matter what
+ `tabs.background` is set to, as there are various scenarios where tabs can
+ open in the background.
+- New flags for the `qute-pass` userscript:
+ * `--unfiltered` to show all secrets, not just the one matching the current
+ URL.
+ * `--always-show-selection` to confirm the password to be entered even if
+ there's only a single match.
+- In insert mode, `<Shift-Escape>` is now bound to `fake-key <Escape>` by
+ default, i.e., sends an Escape keypress to the website.
+- Using `GM_setClipboard` in Greasemonkey scripts is now supported.
[[v2.3.1]]
-v2.3.1 (unreleased)
+v2.3.1 (2021-07-28)
-------------------
Fixed
~~~~~
+- Updated the workaround for Google Account log in claiming that this browser
+ isn't secure. For an equivalent workaround on older versions, run:
+ `:set -u https://accounts.google.com/* content.headers.user_agent "Mozilla/5.0 ({os_info}; rv:90.0) Gecko/20100101 Firefox/90.0"`
- Corrupt cache file exceptions with `adblock` 0.5.0+ are now handled properly.
+- Crash when entering unicode surrogates into the filename prompt.
+- `UnboundLocalError` in `qute-keepass` when the database couldn't be opened.
[[v2.3.0]]
v2.3.0 (2021-06-28)
@@ -4138,7 +4408,7 @@ v0.1.4 (2015-03-19)
Changed
~~~~~~~
-* The Windows builds come with Qt 5.4.1 which has some https://lists.schokokeks.org/pipermail/qutebrowser/2015-March/000054.html[related bugfixes].
+* The Windows builds come with Qt 5.4.1 which has some https://listi.jpberlin.de/pipermail/qutebrowser/2015-March/000054.html[related bugfixes].
* Improvements to CPU usage when idle.
* Ensure there's no size for `font-family` settings.
* Handle URLs with double-colon as search strings.
diff --git a/doc/contributing.asciidoc b/doc/contributing.asciidoc
index 1f87e9163..70447d8c5 100644
--- a/doc/contributing.asciidoc
+++ b/doc/contributing.asciidoc
@@ -24,7 +24,7 @@ several ways:
* Send a mail to the mailing list at mailto:qutebrowser@lists.qutebrowser.org[]
(optionally
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[subscribe]
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser[subscribe]
first).
* Join the IRC channel link:ircs://irc.libera.chat:6697/#qutebrowser[`#qutebrowser`] on
https://libera.chat/[Libera Chat] (https://web.libera.chat/#qutebrowser[webchat],
@@ -111,9 +111,9 @@ unittests and several linters/checkers.
Currently, the following tox environments are available:
* Tests using https://www.pytest.org[pytest]:
- - `py36`, `py37`, ...: Run pytest for python 3.6/3.7/... with the system-wide PyQt.
- - `py36-pyqt512`, ..., `py36-pyqt515`: Run pytest with the given PyQt version (`py35-*` also works).
- - `py36-pyqt515-cov`: Run with coverage support (other Python/PyQt versions work too).
+ - `py37`, `py38`, ...: Run pytest for python 3.7/3.8/... with the system-wide PyQt.
+ - `py37-pyqt512`, ..., `py37-pyqt515`: Run pytest with the given PyQt version (`py37-*` also works).
+ - `py37-pyqt515-cov`: Run with coverage support (other Python/PyQt versions work too).
* `flake8`: Run various linting checks via https://pypi.python.org/pypi/flake8[flake8].
* `vulture`: Run https://pypi.python.org/pypi/vulture[vulture] to find
unused code portions.
@@ -168,16 +168,16 @@ Examples:
----
# run only pytest tests which failed in last run:
-tox -e py35 -- --lf
+tox -e py37 -- --lf
# run only the end2end feature tests:
-tox -e py35 -- tests/end2end/features
+tox -e py37 -- tests/end2end/features
# run everything with undo in the generated name, based on the scenario text
-tox -e py35 -- tests/end2end/features/test_tabs_bdd.py -k undo
+tox -e py37 -- tests/end2end/features/test_tabs_bdd.py -k undo
# run coverage test for specific file (updates htmlcov/index.html)
-tox -e py35-cov -- tests/unit/browser/test_webelem.py
+tox -e py37-cov -- tests/unit/browser/test_webelem.py
----
Profiling
@@ -544,11 +544,8 @@ ____
Setting up a Windows Development Environment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-* Install https://www.python.org/downloads/release/python-362/[Python 3.6].
+* Install https://www.python.org/downloads/release/python-3911/[Python 3.9].
* Install PyQt via `pip install PyQt5`.
-* Create a file at `C:\Windows\system32\python3.bat` with the following content (adjust the path as necessary):
- `@C:\Python36\python %*`.
- This will make the Python 3.6 interpreter available as `python3`, which is used by various development scripts.
* Install git from the https://git-scm.com/download/win[git-scm downloads page].
Try not to enable `core.autocrlf`, since that will cause `flake8` to complain a lot. Use an editor that can deal with plain line feeds instead.
* Clone your favourite qutebrowser repository.
diff --git a/doc/faq.asciidoc b/doc/faq.asciidoc
index 503893b66..4b3596285 100644
--- a/doc/faq.asciidoc
+++ b/doc/faq.asciidoc
@@ -354,9 +354,9 @@ There is a total of four possible approaches to get dark websites:
of the Dark Reader extension. This is mostly untested, though.
How do I make copy to clipboard buttons work?::
-You can `:set content.javascript.can_access_clipboard true`, or
-`:set -u some.domain content.javascript.can_access_clipboard true` if you want to limit
-the setting to `some.domain`.
+You can `:set content.javascript.clipboard access` to allow this globally (not
+recommended!), or `:set -u some.domain content.javascript.clipboad access` if
+you want to limit the setting to `some.domain`.
== Troubleshooting
diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc
index f570a3ffd..e84e36c3a 100644
--- a/doc/help/commands.asciidoc
+++ b/doc/help/commands.asciidoc
@@ -254,20 +254,27 @@ Clear all message notifications.
[[click-element]]
=== click-element
-Syntax: +:click-element [*--target* 'target'] [*--force-event*] 'filter' 'value'+
+Syntax: +:click-element [*--target* 'target'] [*--force-event*] [*--select-first*] 'filter' ['value']+
Click the element matching the given filter.
The given filter needs to result in exactly one element, otherwise, an error is shown.
==== positional arguments
-* +'filter'+: How to filter the elements. id: Get an element based on its ID.
+* +'filter'+: How to filter the elements.
-* +'value'+: The value to filter for.
+ - id: Get an element based on its ID.
+ - css: Filter by a CSS selector.
+ - position: Click the element at specified position.
+ Specify `value` as 'x,y'.
+ - focused: Click the currently focused element.
+
+* +'value'+: The value to filter for. Optional for 'focused' filter.
==== optional arguments
* +*-t*+, +*--target*+: How to open the clicked element (normal/tab/tab-bg/window).
* +*-f*+, +*--force-event*+: Force generating a fake click event.
+* +*-s*+, +*--select-first*+: Select first matching element if there are multiple.
[[close]]
=== close
@@ -294,7 +301,7 @@ Cycle an option between multiple values.
* +'values'+: The values to cycle through.
==== optional arguments
-* +*-u*+, +*--pattern*+: The URL pattern to use.
+* +*-u*+, +*--pattern*+: The link:configuring{outfilesuffix}#patterns[URL pattern] to use.
* +*-t*+, +*--temp*+: Set value temporarily until qutebrowser is closed.
* +*-p*+, +*--print*+: Print the value after setting.
@@ -391,7 +398,7 @@ This sets an option back to its default and removes it from autoconfig.yml.
* +'option'+: The name of the option.
==== optional arguments
-* +*-u*+, +*--pattern*+: The URL pattern to use.
+* +*-u*+, +*--pattern*+: The link:configuring{outfilesuffix}#patterns[URL pattern] to use.
* +*-t*+, +*--temp*+: Set value temporarily until qutebrowser is closed.
[[config-write-py]]
@@ -679,9 +686,8 @@ Start hinting.
* +*-a*+, +*--add-history*+: Whether to add the spawned or yanked link to the browsing history.
* +*-r*+, +*--rapid*+: Whether to do rapid hinting. With rapid hinting, the hint mode isn't left after a hint is followed, so you can easily
- open multiple links. This is only possible with targets
- `tab` (with `tabs.background=true`), `tab-bg`,
- `window`, `run`, `hover`, `userscript` and `spawn`.
+ open multiple links. Note this won't work with targets
+ `tab-fg`, `fill`, `delete` and `right-click`.
* +*-f*+, +*--first*+: Click the first hinted element without prompting.
@@ -1286,7 +1292,7 @@ If the option name ends with '?' or no value is provided, the value of the optio
==== optional arguments
* +*-t*+, +*--temp*+: Set value temporarily until qutebrowser is closed.
* +*-p*+, +*--print*+: Print the value after setting.
-* +*-u*+, +*--pattern*+: The URL pattern to use.
+* +*-u*+, +*--pattern*+: The link:configuring{outfilesuffix}#patterns[URL pattern] to use.
[[set-cmd-text]]
=== set-cmd-text
@@ -1357,13 +1363,14 @@ The tab index to stop.
[[tab-clone]]
=== tab-clone
-Syntax: +:tab-clone [*--bg*] [*--window*]+
+Syntax: +:tab-clone [*--bg*] [*--window*] [*--private*]+
Duplicate the current tab.
==== optional arguments
* +*-b*+, +*--bg*+: Open in a background tab.
* +*-w*+, +*--window*+: Open in a new window.
+* +*-p*+, +*--private*+: Open in a new private window.
[[tab-close]]
=== tab-close
@@ -1431,6 +1438,7 @@ If neither is given, move it to the first position.
==== positional arguments
* +'index'+: `+` or `-` to move relative to the current tab by count, or a default of 1 space.
A tab index to move to that index.
+ `start` and `end` to move to the start and the end.
==== count
@@ -1680,13 +1688,13 @@ How many steps to zoom out.
|<<rl-beginning-of-line,rl-beginning-of-line>>|Move to the start of the line.
|<<rl-delete-char,rl-delete-char>>|Delete the character after the cursor.
|<<rl-end-of-line,rl-end-of-line>>|Move to the end of the line.
+|<<rl-filename-rubout,rl-filename-rubout>>|Delete backwards using the OS path separator as boundary.
|<<rl-forward-char,rl-forward-char>>|Move forward a character.
|<<rl-forward-word,rl-forward-word>>|Move forward to the end of the next word.
|<<rl-kill-line,rl-kill-line>>|Remove chars from the cursor to the end of the line.
|<<rl-kill-word,rl-kill-word>>|Remove chars from the cursor to the end of the current word.
-|<<rl-unix-filename-rubout,rl-unix-filename-rubout>>|Remove chars from the cursor to the previous path separator.
+|<<rl-rubout,rl-rubout>>|Delete backwards using the given characters as boundaries.
|<<rl-unix-line-discard,rl-unix-line-discard>>|Remove chars backward from the cursor to the beginning of the line.
-|<<rl-unix-word-rubout,rl-unix-word-rubout>>|Remove chars from the cursor to the beginning of the word.
|<<rl-yank,rl-yank>>|Paste the most recently deleted text.
|<<selection-drop,selection-drop>>|Drop selection and keep selection mode enabled.
|<<selection-reverse,selection-reverse>>|Swap the stationary and moving end of the current selection.
@@ -1938,6 +1946,12 @@ Move to the end of the line.
This acts like readline's end-of-line.
+[[rl-filename-rubout]]
+=== rl-filename-rubout
+Delete backwards using the OS path separator as boundary.
+
+For behavior that matches readline's `unix-filename-rubout` exactly, use `:rl-rubout "/ "` instead. This command uses the OS path separator (i.e. `\` on Windows) and ignores spaces.
+
[[rl-forward-char]]
=== rl-forward-char
Move forward a character.
@@ -1962,11 +1976,17 @@ Remove chars from the cursor to the end of the current word.
This acts like readline's kill-word.
-[[rl-unix-filename-rubout]]
-=== rl-unix-filename-rubout
-Remove chars from the cursor to the previous path separator.
+[[rl-rubout]]
+=== rl-rubout
+Syntax: +:rl-rubout 'delim'+
+
+Delete backwards using the given characters as boundaries.
+
+With " ", this acts like readline's `unix-word-rubout`. With " /", this acts like readline's `unix-filename-rubout`, but consider using `:rl-filename-rubout` instead: It uses the OS path separator (i.e. `\` on Windows) and ignores spaces.
+
+==== positional arguments
+* +'delim'+: A string of characters (or a single character) until which text will be deleted.
-This acts like readline's unix-filename-rubout.
[[rl-unix-line-discard]]
=== rl-unix-line-discard
@@ -1974,12 +1994,6 @@ Remove chars backward from the cursor to the beginning of the line.
This acts like readline's unix-line-discard.
-[[rl-unix-word-rubout]]
-=== rl-unix-word-rubout
-Remove chars from the cursor to the beginning of the word.
-
-This acts like readline's unix-word-rubout. Whitespace is used as a word delimiter.
-
[[rl-yank]]
=== rl-yank
Paste the most recently deleted text.
diff --git a/doc/help/configuring.asciidoc b/doc/help/configuring.asciidoc
index e1a57cdfb..3ecef8ecf 100644
--- a/doc/help/configuring.asciidoc
+++ b/doc/help/configuring.asciidoc
@@ -22,6 +22,19 @@ exists, the `autoconfig.yml` file **is not read anymore** by default. You need
to <<configpy-autoconfig,load it from `config.py`>> if you want settings changed via
`:set`/`:bind` to persist between restarts.
+[[patterns]]
+URL pattern support
+-------------------
+
+Many settings are customizable depending on the page being visited by using URL
+patterns. The link:settings{outfilesuffix}[settings documentation] marks such
+settings with "This setting supports URL patterns.
+
+The syntax is based on Chromium's
+https://developer.chrome.com/docs/extensions/mv3/match_patterns/[URL pattern syntax].
+As an extension, the scheme and path can be left off as a short-hand syntax, so
+`example.com` is equivalent to `*://example.com/*`.
+
[[autoconfig]]
Configuring qutebrowser via the user interface
----------------------------------------------
@@ -45,9 +58,10 @@ customizable.
Using the link:commands{outfilesuffix}#set[`:set`] command and command completion, you
can quickly set settings interactively, for example `:set tabs.position left`.
-Some settings are also customizable for a given
-https://developer.chrome.com/apps/match_patterns[URL pattern] by doing e.g.
-`:set --pattern=*://example.com/ content.images false`.
+<<patterns,URL patterns>> can be used via
+`:set --pattern *://example.com/* content.images false`, or with shorthand
+syntax for both argument and pattern, `:set -u example.com content.images
+false`.
To get more help about a setting, use e.g. `:help tabs.position`.
@@ -160,8 +174,8 @@ color = config.get('colors.completion.fg')
Per-domain settings
~~~~~~~~~~~~~~~~~~~
-Using `config.set`, some settings are also customizable for a given
-https://developer.chrome.com/apps/match_patterns[URL pattern]:
+Using `config.set` instead of the `c.` shorthand, many settings are also
+customizable for a given <<patterns,URL patterns>>.
[source,python]
----
@@ -377,8 +391,8 @@ import subprocess
def read_xresources(prefix):
props = {}
- x = subprocess.run(['xrdb', '-query'], stdout=subprocess.PIPE)
- lines = x.stdout.decode().split('\n')
+ x = subprocess.run(['xrdb', '-query'], capture_output=True, check=True, text=True)
+ lines = x.stdout.split('\n')
for line in filter(lambda l : l.startswith(prefix), lines):
prop, _, value = line.partition(':\t')
props[prop] = value
@@ -392,11 +406,13 @@ Pre-built colorschemes
^^^^^^^^^^^^^^^^^^^^^^
- A collection of https://github.com/chriskempson/base16[base16] color-schemes can be found in https://github.com/theova/base16-qutebrowser[base16-qutebrowser] and used with https://github.com/AuditeMarlow/base16-manager[base16-manager].
-- https://gitlab.com/jjzmajic/qutewal[Pywal integration]
+- Another collection: https://github.com/leosolid/qutebrowser-themes[qutebrowser-themes]
+- Pywal integration: https://gitlab.com/jjzmajic/qutewal[qutewal], https://github.com/makman12/pywalQute[pywalQute]
- https://github.com/arcticicestudio/nord[Nord]: https://github.com/Linuus/nord-qutebrowser[Linuus], https://github.com/KnownAsDon/QuteBrowser-Nord-Theme[KnownAsDon]
- https://github.com/dracula/qutebrowser-dracula-theme[Dracula]
- https://gitlab.com/lovetocode999/selenized-qutebrowser[Selenized]
-- https://github.com/morhetz/gruvbox[gruvbox]: https://github.com/The-Compiler/dotfiles/blob/master/qutebrowser/gruvbox.py[The-Compiler], https://gitlab.com/shaneyost/dots-popos-september-2020/-/blob/master/qutebrowser/config.py[Shane Yost]
+- https://github.com/The-Compiler/dotfiles/blob/master/qutebrowser/gruvbox.py[gruvbox]
+- https://www.opencode.net/wakellor957/qb-breath/-/blob/main/qb-breath.py[Manjaro Breath-like]
Avoiding flake8 errors
^^^^^^^^^^^^^^^^^^^^^^
@@ -431,7 +447,7 @@ Various emacs/conkeror-like keybinding configs exist:
- https://gitlab.com/jgkamat/qutemacs/blob/master/qutemacs.py[jgkamat]
- https://gitlab.com/Kaligule/qutebrowser-emacs-config/blob/master/config.py[Kaligule]
-- https://me0w.net/pit/1540882719[nm0i]
+- https://web.archive.org/web/20210512185023/https://me0w.net/pit/1540882719[nm0i]
- https://www.reddit.com/r/qutebrowser/comments/eh10i7/config_share_qute_with_emacs_keybindings/[jasonsun0310]
It's also mostly possible to get rid of modal keybindings by setting
diff --git a/doc/help/index.asciidoc b/doc/help/index.asciidoc
index c7fb88c8d..127cc5d86 100644
--- a/doc/help/index.asciidoc
+++ b/doc/help/index.asciidoc
@@ -26,10 +26,10 @@ link:ircs://irc.libera.chat:6697/#qutebrowser[`#qutebrowser`] on
https://libera.chat/[Libera Chat]
(https://web.libera.chat/#qutebrowser[webchat], https://matrix.to/#qutebrowser:libera.chat[via Matrix]),
or by writing a message to the
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[mailinglist] at
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser[mailinglist] at
mailto:qutebrowser@lists.qutebrowser.org[].
-There's also an https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce[announce-only mailinglist]
+There's also an https://listi.jpberlin.de/mailman/listinfo/qutebrowser-announce[announce-only mailinglist]
at mailto:qutebrowser-announce@lists.qutebrowser.org[] (the announcements also
get sent to the general qutebrowser@ list).
@@ -49,7 +49,7 @@ ways:
* Use the built-in `:report` command or the automatic crash dialog.
* Open an issue in the Github issue tracker.
* Write a mail to the
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[mailinglist] at
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser[mailinglist] at
mailto:qutebrowser@lists.qutebrowser.org[].
Other resources
diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc
index 3c00eb083..c086ed783 100644
--- a/doc/help/settings.asciidoc
+++ b/doc/help/settings.asciidoc
@@ -142,6 +142,7 @@
|<<content.autoplay,content.autoplay>>|Automatically start playing `<video>` elements.
|<<content.blocking.adblock.lists,content.blocking.adblock.lists>>|List of URLs to ABP-style adblocking rulesets.
|<<content.blocking.enabled,content.blocking.enabled>>|Enable the ad/host blocker
+|<<content.blocking.hosts.block_subdomains,content.blocking.hosts.block_subdomains>>|Block subdomains of blocked hosts.
|<<content.blocking.hosts.lists,content.blocking.hosts.lists>>|List of URLs to host blocklists for the host blocker.
|<<content.blocking.method,content.blocking.method>>|Which method of blocking ads should be used.
|<<content.blocking.whitelist,content.blocking.whitelist>>|A list of patterns that should always be loaded, despite being blocked by the ad-/host-blocker.
@@ -166,9 +167,9 @@
|<<content.hyperlink_auditing,content.hyperlink_auditing>>|Enable hyperlink auditing (`<a ping>`).
|<<content.images,content.images>>|Load images automatically in web pages.
|<<content.javascript.alert,content.javascript.alert>>|Show javascript alerts.
-|<<content.javascript.can_access_clipboard,content.javascript.can_access_clipboard>>|Allow JavaScript to read from or write to the clipboard.
|<<content.javascript.can_close_tabs,content.javascript.can_close_tabs>>|Allow JavaScript to close tabs.
|<<content.javascript.can_open_tabs_automatically,content.javascript.can_open_tabs_automatically>>|Allow JavaScript to open new tabs without user interaction.
+|<<content.javascript.clipboard,content.javascript.clipboard>>|Allow JavaScript to read from or write to the clipboard.
|<<content.javascript.enabled,content.javascript.enabled>>|Enable JavaScript.
|<<content.javascript.log,content.javascript.log>>|Log levels to use for JavaScript console logging messages.
|<<content.javascript.modal_dialog,content.javascript.modal_dialog>>|Use the standard JavaScript modal dialog for `alert()` and `confirm()`.
@@ -185,7 +186,7 @@
|<<content.notifications.enabled,content.notifications.enabled>>|Allow websites to show notifications.
|<<content.notifications.presenter,content.notifications.presenter>>|What notification presenter to use for web notifications.
|<<content.notifications.show_origin,content.notifications.show_origin>>|Whether to show the origin URL for notifications.
-|<<content.pdfjs,content.pdfjs>>|Allow pdf.js to view PDF files in the browser.
+|<<content.pdfjs,content.pdfjs>>|Display PDF files via PDF.js in the browser without showing a download prompt.
|<<content.persistent_storage,content.persistent_storage>>|Allow websites to request persistent storage quota via `navigator.webkitPersistentStorage.requestQuota`.
|<<content.plugins,content.plugins>>|Enable plugins in Web pages.
|<<content.prefers_reduced_motion,content.prefers_reduced_motion>>|Request websites to minimize non-essentials animations and motion.
@@ -208,9 +209,11 @@
|<<downloads.location.suggestion,downloads.location.suggestion>>|What to display in the download filename input.
|<<downloads.open_dispatcher,downloads.open_dispatcher>>|Default program used to open downloads.
|<<downloads.position,downloads.position>>|Where to show the downloaded files.
+|<<downloads.prevent_mixed_content,downloads.prevent_mixed_content>>|Automatically abort insecure (HTTP) downloads originating from secure (HTTPS) pages.
|<<downloads.remove_finished,downloads.remove_finished>>|Duration (in milliseconds) to wait before removing finished downloads.
|<<editor.command,editor.command>>|Editor (and arguments) to use for the `edit-*` commands.
|<<editor.encoding,editor.encoding>>|Encoding to use for the editor.
+|<<editor.remove_file,editor.remove_file>>|Delete the temporary file upon closing the editor.
|<<fileselect.folder.command,fileselect.folder.command>>|Command (and arguments) to use for selecting a single folder in forms. The command should write the selected folder path to the specified file or stdout.
|<<fileselect.handler,fileselect.handler>>|Handler for selecting file(s) in forms. If `external`, then the commands specified by `fileselect.single_file.command` and `fileselect.multiple_files.command` are used to select one or multiple files respectively.
|<<fileselect.multiple_files.command,fileselect.multiple_files.command>>|Command (and arguments) to use for selecting multiple files in forms. The command should write the selected file paths to the specified file or to stdout, separated by newlines.
@@ -267,7 +270,9 @@
|<<input.insert_mode.leave_on_load,input.insert_mode.leave_on_load>>|Leave insert mode when starting a new page load.
|<<input.insert_mode.plugins,input.insert_mode.plugins>>|Switch to insert mode when clicking flash and other plugins.
|<<input.links_included_in_focus_chain,input.links_included_in_focus_chain>>|Include hyperlinks in the keyboard focus chain when tabbing.
+|<<input.match_counts,input.match_counts>>|Interpret number prefixes as counts for bindings.
|<<input.media_keys,input.media_keys>>|Whether the underlying Chromium should handle media keys.
+|<<input.mode_override,input.mode_override>>|Mode to change to when focusing on a tab/URL changes.
|<<input.mouse.back_forward_buttons,input.mouse.back_forward_buttons>>|Enable back and forward buttons on the mouse.
|<<input.mouse.rocker_gestures,input.mouse.rocker_gestures>>|Enable Opera-like mouse rocker gestures.
|<<input.partial_timeout,input.partial_timeout>>|Timeout (in milliseconds) for partially typed key bindings.
@@ -283,13 +288,14 @@
|<<prompt.filebrowser,prompt.filebrowser>>|Show a filebrowser in download prompts.
|<<prompt.radius,prompt.radius>>|Rounding radius (in pixels) for the edges of prompts.
|<<qt.args,qt.args>>|Additional arguments to pass to Qt, without leading `--`.
+|<<qt.chromium.low_end_device_mode,qt.chromium.low_end_device_mode>>|When to use Chromium's low-end device mode.
+|<<qt.chromium.process_model,qt.chromium.process_model>>|Which Chromium process model to use.
+|<<qt.chromium.sandboxing,qt.chromium.sandboxing>>|What sandboxing mechanisms in Chromium to use.
|<<qt.environ,qt.environ>>|Additional environment variables to set.
|<<qt.force_platform,qt.force_platform>>|Force a Qt platform to use.
|<<qt.force_platformtheme,qt.force_platformtheme>>|Force a Qt platformtheme to use.
|<<qt.force_software_rendering,qt.force_software_rendering>>|Force software rendering for QtWebEngine.
|<<qt.highdpi,qt.highdpi>>|Turn on Qt HighDPI scaling.
-|<<qt.low_end_device_mode,qt.low_end_device_mode>>|When to use Chromium's low-end device mode.
-|<<qt.process_model,qt.process_model>>|Which Chromium process model to use.
|<<qt.workarounds.locale,qt.workarounds.locale>>|Work around locale parsing issues in QtWebEngine 5.15.3.
|<<qt.workarounds.remove_service_workers,qt.workarounds.remove_service_workers>>|Delete the QtWebEngine Service Worker directory on every start.
|<<scrolling.bar,scrolling.bar>>|When/how to show the scrollbar.
@@ -435,9 +441,7 @@ The following modes are available:
that single keys can't be bound in this mode.
* passthrough: Similar to insert mode, but passes through all keypresses
- except `<Escape>` to leave the mode. It might be useful to bind
- `<Escape>` to some other key in this mode if you want to be able to send
- an Escape key to the website as well. Note that single keys can't be
+ except `<Shift+Escape>` to leave the mode. Note that single keys can't be
bound in this mode.
* command: Entered when pressing the `:` key in order to enter a command.
@@ -520,9 +524,10 @@ Default:
* +pass:[&lt;Ctrl-Return&gt;]+: +pass:[command-accept --rapid]+
* +pass:[&lt;Ctrl-Shift-C&gt;]+: +pass:[completion-item-yank --sel]+
* +pass:[&lt;Ctrl-Shift-Tab&gt;]+: +pass:[completion-item-focus prev-category]+
+* +pass:[&lt;Ctrl-Shift-W&gt;]+: +pass:[rl-filename-rubout]+
* +pass:[&lt;Ctrl-Tab&gt;]+: +pass:[completion-item-focus next-category]+
* +pass:[&lt;Ctrl-U&gt;]+: +pass:[rl-unix-line-discard]+
-* +pass:[&lt;Ctrl-W&gt;]+: +pass:[rl-unix-word-rubout]+
+* +pass:[&lt;Ctrl-W&gt;]+: +pass:[rl-rubout &quot; &quot;]+
* +pass:[&lt;Ctrl-Y&gt;]+: +pass:[rl-yank]+
* +pass:[&lt;Down&gt;]+: +pass:[completion-item-focus --history next]+
* +pass:[&lt;Escape&gt;]+: +pass:[mode-leave]+
@@ -544,6 +549,7 @@ Default:
* +pass:[&lt;Ctrl-E&gt;]+: +pass:[edit-text]+
* +pass:[&lt;Escape&gt;]+: +pass:[mode-leave]+
+* +pass:[&lt;Shift-Escape&gt;]+: +pass:[fake-key &lt;Escape&gt;]+
* +pass:[&lt;Shift-Ins&gt;]+: +pass:[insert-text -- {primary}]+
- +pass:[normal]+:
@@ -755,8 +761,9 @@ Default:
* +pass:[&lt;Ctrl-H&gt;]+: +pass:[rl-backward-delete-char]+
* +pass:[&lt;Ctrl-K&gt;]+: +pass:[rl-kill-line]+
* +pass:[&lt;Ctrl-P&gt;]+: +pass:[prompt-open-download --pdfjs]+
+* +pass:[&lt;Ctrl-Shift-W&gt;]+: +pass:[rl-filename-rubout]+
* +pass:[&lt;Ctrl-U&gt;]+: +pass:[rl-unix-line-discard]+
-* +pass:[&lt;Ctrl-W&gt;]+: +pass:[rl-unix-word-rubout]+
+* +pass:[&lt;Ctrl-W&gt;]+: +pass:[rl-rubout &quot; &quot;]+
* +pass:[&lt;Ctrl-X&gt;]+: +pass:[prompt-open-download]+
* +pass:[&lt;Ctrl-Y&gt;]+: +pass:[rl-yank]+
* +pass:[&lt;Down&gt;]+: +pass:[prompt-item-focus next]+
@@ -1956,7 +1963,7 @@ Default:
=== content.autoplay
Automatically start playing `<video>` elements.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
This setting is only available with the QtWebEngine backend.
@@ -1988,7 +1995,16 @@ Default:
=== content.blocking.enabled
Enable the ad/host blocker
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
+
+Type: <<types,Bool>>
+
+Default: +pass:[true]+
+
+[[content.blocking.hosts.block_subdomains]]
+=== content.blocking.hosts.block_subdomains
+Block subdomains of blocked hosts.
+Note: If only a single subdomain is blocked but should be allowed, consider using `content.blocking.whitelist` instead.
Type: <<types,Bool>>
@@ -2054,7 +2070,7 @@ Default: empty
Enable support for the HTML 5 web application cache feature.
An application cache acts like an HTTP cache in some sense. For documents that use the application cache via JavaScript, the loader engine will first ask the application cache for the contents, before hitting the network.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
This setting is only available with the QtWebKit backend.
@@ -2105,7 +2121,7 @@ If this setting is used with URL patterns, the pattern gets applied to the origi
With QtWebEngine 5.15.0+, paths will be stripped from URLs, so URL patterns using paths will not match. With QtWebEngine 5.15.2+, subdomains are additionally stripped as well, so you will typically need to set this setting for `example.com` when the cookie is set on `somesubdomain.example.com` for it to work properly.
To debug issues with this setting, start qutebrowser with `--debug --logfilter network --debug-flag log-cookies` which will show all cookies being set.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,String>>
@@ -2139,7 +2155,7 @@ Default: +pass:[iso-8859-1]+
=== content.desktop_capture
Allow websites to share screen content.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,BoolAsk>>
@@ -2155,7 +2171,7 @@ Default: +pass:[ask]+
=== content.dns_prefetch
Try to pre-fetch DNS entries to speed up browsing.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
This setting is only available with the QtWebEngine backend.
@@ -2168,7 +2184,7 @@ Default: +pass:[true]+
Expand each subframe to its contents.
This will flatten all the frames to become one scrollable page.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
This setting is only available with the QtWebKit backend.
@@ -2197,7 +2213,7 @@ Default: +pass:[false]+
=== content.geolocation
Allow websites to request geolocations.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,BoolAsk>>
@@ -2214,7 +2230,7 @@ Default: +pass:[ask]+
Value to send in the `Accept-Language` header.
Note that the value read from JavaScript is always the global value.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,String>>
@@ -2224,7 +2240,7 @@ Default: +pass:[en-US,en;q=0.9]+
=== content.headers.custom
Custom headers for qutebrowser HTTP requests.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Dict>>
@@ -2235,7 +2251,7 @@ Default: empty
Value to send in the `DNT` header.
When this is set to true, qutebrowser asks websites to not track your identity. If set to null, the DNT header is not sent at all.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Bool>>
@@ -2244,7 +2260,7 @@ Default: +pass:[true]+
[[content.headers.referer]]
=== content.headers.referer
When to send the Referer header.
-The Referer header tells websites from which website you were coming from when visiting them.
+The Referer header tells websites from which website you were coming from when visiting them. Note that with QtWebEngine, websites can override this preference by setting the `Referrer-Policy:` header, so that any websites visited from them get the full referer.
No restart is needed with QtWebKit.
This setting requires a restart.
@@ -2283,7 +2299,7 @@ QtWebEngine between 5.12 and 5.14 (inclusive), changing the value exposed
to JavaScript requires a restart.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,FormatString>>
@@ -2293,7 +2309,7 @@ Default: +pass:[Mozilla/5.0 ({os_info}) AppleWebKit/{webkit_version} (KHTML, lik
=== content.hyperlink_auditing
Enable hyperlink auditing (`<a ping>`).
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Bool>>
@@ -2303,7 +2319,7 @@ Default: +pass:[false]+
=== content.images
Load images automatically in web pages.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Bool>>
@@ -2317,22 +2333,11 @@ Type: <<types,Bool>>
Default: +pass:[true]+
-[[content.javascript.can_access_clipboard]]
-=== content.javascript.can_access_clipboard
-Allow JavaScript to read from or write to the clipboard.
-With QtWebEngine, writing the clipboard as response to a user interaction is always allowed.
-
-This setting supports URL patterns.
-
-Type: <<types,Bool>>
-
-Default: +pass:[false]+
-
[[content.javascript.can_close_tabs]]
=== content.javascript.can_close_tabs
Allow JavaScript to close tabs.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
This setting is only available with the QtWebKit backend.
@@ -2344,17 +2349,34 @@ Default: +pass:[false]+
=== content.javascript.can_open_tabs_automatically
Allow JavaScript to open new tabs without user interaction.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Bool>>
Default: +pass:[false]+
+[[content.javascript.clipboard]]
+=== content.javascript.clipboard
+Allow JavaScript to read from or write to the clipboard.
+With QtWebEngine, writing the clipboard as response to a user interaction is always allowed.
+
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
+
+Type: <<types,String>>
+
+Valid values:
+
+ * +none+: Disable access to clipboard.
+ * +access+: Allow reading from and writing to the clipboard.
+ * +access-paste+: Allow accessing the clipboard and pasting clipboard content.
+
+Default: +pass:[none]+
+
[[content.javascript.enabled]]
=== content.javascript.enabled
Enable JavaScript.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Bool>>
@@ -2396,7 +2418,7 @@ Default: +pass:[true]+
=== content.local_content_can_access_file_urls
Allow locally loaded documents to access other local URLs.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Bool>>
@@ -2406,7 +2428,7 @@ Default: +pass:[true]+
=== content.local_content_can_access_remote_urls
Allow locally loaded documents to access remote URLs.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Bool>>
@@ -2416,7 +2438,7 @@ Default: +pass:[false]+
=== content.local_storage
Enable support for HTML 5 local storage and Web SQL.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Bool>>
@@ -2426,7 +2448,7 @@ Default: +pass:[true]+
=== content.media.audio_capture
Allow websites to record audio.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
This setting is only available with the QtWebEngine backend.
@@ -2444,7 +2466,7 @@ Default: +pass:[ask]+
=== content.media.audio_video_capture
Allow websites to record audio and video.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
This setting is only available with the QtWebEngine backend.
@@ -2462,7 +2484,7 @@ Default: +pass:[ask]+
=== content.media.video_capture
Allow websites to record video.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
This setting is only available with the QtWebEngine backend.
@@ -2480,7 +2502,7 @@ Default: +pass:[ask]+
=== content.mouse_lock
Allow websites to lock your mouse pointer.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
This setting is only available with the QtWebEngine backend.
@@ -2499,7 +2521,7 @@ Default: +pass:[ask]+
Automatically mute tabs.
Note that if the `:tab-mute` command is used, the mute status for the affected tab is now controlled manually, and this setting doesn't have any effect.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Bool>>
@@ -2518,7 +2540,7 @@ Default: empty
=== content.notifications.enabled
Allow websites to show notifications.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
On QtWebEngine, this setting requires Qt 5.13 or newer.
@@ -2568,7 +2590,7 @@ Whether to show the origin URL for notifications.
Note that URL patterns with this setting only get matched against the origin part of the URL, so e.g. paths in patterns will never match.
Note that with the `qt` presenter, origins are never shown.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
On QtWebEngine, this setting requires Qt 5.14 or newer.
@@ -2580,10 +2602,10 @@ Default: +pass:[true]+
[[content.pdfjs]]
=== content.pdfjs
-Allow pdf.js to view PDF files in the browser.
-Note that the files can still be downloaded by clicking the download button in the pdf.js viewer.
+Display PDF files via PDF.js in the browser without showing a download prompt.
+Note that the files can still be downloaded by clicking the download button in the pdf.js viewer. With this set to `false`, the `:prompt-open-download --pdfjs` command (bound to `<Ctrl-p>` by default) can be used in the download prompt.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Bool>>
@@ -2593,7 +2615,7 @@ Default: +pass:[false]+
=== content.persistent_storage
Allow websites to request persistent storage quota via `navigator.webkitPersistentStorage.requestQuota`.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
This setting is only available with the QtWebEngine backend.
@@ -2611,7 +2633,7 @@ Default: +pass:[ask]+
=== content.plugins
Enable plugins in Web pages.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Bool>>
@@ -2637,7 +2659,7 @@ Default: +pass:[false]+
=== content.print_element_backgrounds
Draw the background color and images also when the page is printed.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
This setting is only available with the QtWebEngine backend.
@@ -2657,7 +2679,7 @@ Default: +pass:[false]+
=== content.proxy
Proxy to use.
In addition to the listed values, you can use a `socks://...` or `http://...` URL.
-Note that with QtWebEngine, it will take a couple of seconds until the change is applied, if this value is changed at runtime.
+Note that with QtWebEngine, it will take a couple of seconds until the change is applied, if this value is changed at runtime. Authentication for SOCKS proxies isn't supported due to Chromium limitations.
Type: <<types,Proxy>>
@@ -2682,7 +2704,7 @@ Default: +pass:[true]+
=== content.register_protocol_handler
Allow websites to register protocol handlers via `navigator.registerProtocolHandler`.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
This setting is only available with the QtWebEngine backend.
@@ -2735,7 +2757,7 @@ Default:
=== content.tls.certificate_errors
How to proceed on TLS certificate errors.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,String>>
@@ -2752,7 +2774,7 @@ Default: +pass:[ask]+
=== content.unknown_url_scheme_policy
How navigation requests to URLs with unknown schemes are handled.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
This setting is only available with the QtWebEngine backend.
@@ -2778,7 +2800,7 @@ Default: empty
=== content.webgl
Enable WebGL.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Bool>>
@@ -2809,7 +2831,7 @@ Monitor load requests for cross-site scripting attempts.
Suspicious scripts will be blocked and reported in the devtools JavaScript console.
Note that bypasses for the XSS auditor are widely known and it can be abused for cross-site info leaks in some scenarios, see: https://www.chromium.org/developers/design-documents/xss-auditor
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Bool>>
@@ -2878,6 +2900,17 @@ Valid values:
Default: +pass:[top]+
+[[downloads.prevent_mixed_content]]
+=== downloads.prevent_mixed_content
+Automatically abort insecure (HTTP) downloads originating from secure (HTTPS) pages.
+For per-domain settings, the relevant URL is the URL initiating the download, not the URL the download itself is coming from. It's not recommended to set this setting to false globally.
+
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
+
+Type: <<types,Bool>>
+
+Default: +pass:[true]+
+
[[downloads.remove_finished]]
=== downloads.remove_finished
Duration (in milliseconds) to wait before removing finished downloads.
@@ -2917,6 +2950,14 @@ Type: <<types,Encoding>>
Default: +pass:[utf-8]+
+[[editor.remove_file]]
+=== editor.remove_file
+Delete the temporary file upon closing the editor.
+
+Type: <<types,Bool>>
+
+Default: +pass:[true]+
+
[[fileselect.folder.command]]
=== fileselect.folder.command
Command (and arguments) to use for selecting a single folder in forms. The command should write the selected folder path to the specified file or stdout.
@@ -3115,7 +3156,7 @@ Default: +pass:[default_size default_family]+
=== fonts.web.family.cursive
Font family for cursive fonts.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,FontFamily>>
@@ -3125,7 +3166,7 @@ Default: empty
=== fonts.web.family.fantasy
Font family for fantasy fonts.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,FontFamily>>
@@ -3135,7 +3176,7 @@ Default: empty
=== fonts.web.family.fixed
Font family for fixed fonts.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,FontFamily>>
@@ -3145,7 +3186,7 @@ Default: empty
=== fonts.web.family.sans_serif
Font family for sans-serif fonts.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,FontFamily>>
@@ -3155,7 +3196,7 @@ Default: empty
=== fonts.web.family.serif
Font family for serif fonts.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,FontFamily>>
@@ -3165,7 +3206,7 @@ Default: empty
=== fonts.web.family.standard
Font family for standard fonts.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,FontFamily>>
@@ -3175,7 +3216,7 @@ Default: empty
=== fonts.web.size.default
Default font size (in pixels) for regular text.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Int>>
@@ -3185,7 +3226,7 @@ Default: +pass:[16]+
=== fonts.web.size.default_fixed
Default font size (in pixels) for fixed-pitch text.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Int>>
@@ -3195,7 +3236,7 @@ Default: +pass:[13]+
=== fonts.web.size.minimum
Hard minimum font size (in pixels).
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Int>>
@@ -3205,7 +3246,7 @@ Default: +pass:[0]+
=== fonts.web.size.minimum_logical
Minimum logical font size (in pixels) that is applied when zooming out.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Int>>
@@ -3374,7 +3415,7 @@ Default: +pass:[true]+
=== hints.selectors
CSS selectors used to determine which elements on a page should have hints.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
This setting can only be set in config.py.
@@ -3401,11 +3442,17 @@ Default:
* +pass:[[role=&quot;link&quot;\]]+
* +pass:[[role=&quot;option&quot;\]]+
* +pass:[[role=&quot;button&quot;\]]+
+* +pass:[[role=&quot;tab&quot;\]]+
+* +pass:[[role=&quot;checkbox&quot;\]]+
+* +pass:[[role=&quot;menuitem&quot;\]]+
+* +pass:[[role=&quot;menuitemcheckbox&quot;\]]+
+* +pass:[[role=&quot;menuitemradio&quot;\]]+
+* +pass:[[role=&quot;treeitem&quot;\]]+
* +pass:[[ng-click\]]+
* +pass:[[ngClick\]]+
* +pass:[[data-ng-click\]]+
* +pass:[[x-ng-click\]]+
-* +pass:[[tabindex\]]+
+* +pass:[[tabindex\]:not([tabindex=&quot;-1&quot;\])]+
- +pass:[images]+:
* +pass:[img]+
@@ -3510,7 +3557,7 @@ Default: +pass:[false]+
Leave insert mode when starting a new page load.
Patterns may be unreliable on this setting, and they may match the url you are navigating to, or the URL you are navigating from.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Bool>>
@@ -3528,7 +3575,16 @@ Default: +pass:[false]+
=== input.links_included_in_focus_chain
Include hyperlinks in the keyboard focus chain when tabbing.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
+
+Type: <<types,Bool>>
+
+Default: +pass:[true]+
+
+[[input.match_counts]]
+=== input.match_counts
+Interpret number prefixes as counts for bindings.
+This enables for vi-like bindings that can be prefixed with a number to indicate a count. Disabling it allows for emacs-like bindings where number keys are passed through (according to `input.forward_unbound_keys`) instead.
Type: <<types,Bool>>
@@ -3549,6 +3605,22 @@ Type: <<types,Bool>>
Default: +pass:[true]+
+[[input.mode_override]]
+=== input.mode_override
+Mode to change to when focusing on a tab/URL changes.
+
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
+
+Type: <<types,String>>
+
+Valid values:
+
+ * +normal+
+ * +insert+
+ * +passthrough+
+
+Default: empty
+
[[input.mouse.back_forward_buttons]]
=== input.mouse.back_forward_buttons
Enable back and forward buttons on the mouse.
@@ -3579,9 +3651,9 @@ Default: +pass:[0]+
[[input.spatial_navigation]]
=== input.spatial_navigation
Enable spatial navigation.
-Spatial navigation consists in the ability to navigate between focusable elements in a Web page, such as hyperlinks and form controls, by using Left, Right, Up and Down arrow keys. For example, if the user presses the Right key, heuristics determine whether there is an element he might be trying to reach towards the right and which element he probably wants.
+Spatial navigation consists in the ability to navigate between focusable elements, such as hyperlinks and form controls, on a web page by using the Left, Right, Up and Down arrow keys. For example, if a user presses the Right key, heuristics determine whether there is an element they might be trying to reach towards the right and which element they probably want.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Bool>>
@@ -3717,6 +3789,73 @@ Type: <<types,List of String>>
Default: empty
+[[qt.chromium.low_end_device_mode]]
+=== qt.chromium.low_end_device_mode
+When to use Chromium's low-end device mode.
+This improves the RAM usage of renderer processes, at the expense of performance.
+
+This setting requires a restart.
+
+This setting is only available with the QtWebEngine backend.
+
+Type: <<types,String>>
+
+Valid values:
+
+ * +always+: Always use low-end device mode.
+ * +auto+: Decide automatically (uses low-end mode with < 1 GB available RAM).
+ * +never+: Never use low-end device mode.
+
+Default: +pass:[auto]+
+
+[[qt.chromium.process_model]]
+=== qt.chromium.process_model
+Which Chromium process model to use.
+Alternative process models use less resources, but decrease security and robustness.
+See the following pages for more details:
+
+ - https://www.chromium.org/developers/design-documents/process-models
+ - https://doc.qt.io/qt-5/qtwebengine-features.html#process-models
+
+This setting requires a restart.
+
+This setting is only available with the QtWebEngine backend.
+
+Type: <<types,String>>
+
+Valid values:
+
+ * +process-per-site-instance+: Pages from separate sites are put into separate processes and separate visits to the same site are also isolated.
+ * +process-per-site+: Pages from separate sites are put into separate processes. Unlike Process per Site Instance, all visits to the same site will share an OS process. The benefit of this model is reduced memory consumption, because more web pages will share processes. The drawbacks include reduced security, robustness, and responsiveness.
+ * +single-process+: Run all tabs in a single process. This should be used for debugging purposes only, and it disables `:open --private`.
+
+Default: +pass:[process-per-site-instance]+
+
+[[qt.chromium.sandboxing]]
+=== qt.chromium.sandboxing
+What sandboxing mechanisms in Chromium to use.
+Chromium has various sandboxing layers, which should be enabled for normal browser usage. Mainly for testing and development, it's possible to disable individual sandboxing layers via this setting.
+Open `chrome://sandbox` to see the current sandbox status.
+Changing this setting is only recommended if you know what you're doing, as it **disables one of Chromium's security layers**. To avoid sandboxing being accidentally disabled persistently, this setting can only be set via `config.py`, not via `:set`.
+See the Chromium documentation for more details:
+- https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/linux/sandboxing.md[Linux] - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox.md[Windows] - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox_faq.md[FAQ (Windows-centric)]
+
+This setting requires a restart.
+
+This setting can only be set in config.py.
+
+This setting is only available with the QtWebEngine backend.
+
+Type: <<types,String>>
+
+Valid values:
+
+ * +enable-all+: Enable all available sandboxing mechanisms.
+ * +disable-seccomp-bpf+: Disable the Seccomp BPF filter sandbox (Linux only).
+ * +disable-all+: Disable all sandboxing (**not recommended!**).
+
+Default: +pass:[enable-all]+
+
[[qt.environ]]
=== qt.environ
Additional environment variables to set.
@@ -3782,48 +3921,6 @@ Type: <<types,Bool>>
Default: +pass:[false]+
-[[qt.low_end_device_mode]]
-=== qt.low_end_device_mode
-When to use Chromium's low-end device mode.
-This improves the RAM usage of renderer processes, at the expense of performance.
-
-This setting requires a restart.
-
-This setting is only available with the QtWebEngine backend.
-
-Type: <<types,String>>
-
-Valid values:
-
- * +always+: Always use low-end device mode.
- * +auto+: Decide automatically (uses low-end mode with < 1 GB available RAM).
- * +never+: Never use low-end device mode.
-
-Default: +pass:[auto]+
-
-[[qt.process_model]]
-=== qt.process_model
-Which Chromium process model to use.
-Alternative process models use less resources, but decrease security and robustness.
-See the following pages for more details:
-
- - https://www.chromium.org/developers/design-documents/process-models
- - https://doc.qt.io/qt-5/qtwebengine-features.html#process-models
-
-This setting requires a restart.
-
-This setting is only available with the QtWebEngine backend.
-
-Type: <<types,String>>
-
-Valid values:
-
- * +process-per-site-instance+: Pages from separate sites are put into separate processes and separate visits to the same site are also isolated.
- * +process-per-site+: Pages from separate sites are put into separate processes. Unlike Process per Site Instance, all visits to the same site will share an OS process. The benefit of this model is reduced memory consumption, because more web pages will share processes. The drawbacks include reduced security, robustness, and responsiveness.
- * +single-process+: Run all tabs in a single process. This should be used for debugging purposes only, and it disables `:open --private`.
-
-Default: +pass:[process-per-site-instance]+
-
[[qt.workarounds.locale]]
=== qt.workarounds.locale
Work around locale parsing issues in QtWebEngine 5.15.3.
@@ -3866,7 +3963,7 @@ Default: +pass:[overlay]+
Enable smooth scrolling for web pages.
Note smooth scrolling does not work with the `:scroll-px` command.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
Type: <<types,Bool>>
@@ -4351,6 +4448,7 @@ The following placeholders are defined:
* `{index}`: Index of this tab.
* `{aligned_index}`: Index of this tab padded with spaces to have the same
width.
+* `{relative_index}`: Index of this tab relative to the current tab.
* `{id}`: Internal tab ID of this tab.
* `{scroll_pos}`: Page scroll position.
* `{host}`: Host of the current web page.
@@ -4595,7 +4693,7 @@ Default: +pass:[512]+
=== zoom.text_only
Apply the zoom factor on a frame only to the text or to all content.
-This setting supports URL patterns.
+This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
This setting is only available with the QtWebKit backend.
diff --git a/doc/img/cheatsheet-big.png b/doc/img/cheatsheet-big.png
index 75e2abb89..d47ca913f 100644
--- a/doc/img/cheatsheet-big.png
+++ b/doc/img/cheatsheet-big.png
Binary files differ
diff --git a/doc/img/cheatsheet-small.png b/doc/img/cheatsheet-small.png
index e97d63367..49e5e33df 100644
--- a/doc/img/cheatsheet-small.png
+++ b/doc/img/cheatsheet-small.png
Binary files differ
diff --git a/doc/install.asciidoc b/doc/install.asciidoc
index 83c332b4d..0f44e5a91 100644
--- a/doc/install.asciidoc
+++ b/doc/install.asciidoc
@@ -38,7 +38,7 @@ version (Qt 5.7, based on a Chromium from March 2016). Furthermore, it packages
Ubuntu 16.04 doesn't come with an up-to-date engine (a new enough QtWebKit, or
QtWebEngine) and also comes with Python 3.5.
-You should be able to install a newer Python (3.6+) using the
+You should be able to install a newer Python (3.7+) using the
https://launchpad.net/~deadsnakes/+archive/ubuntu/ppa[deadsnakes PPA] or
https://github.com/pyenv/pyenv[pyenv], and then proceed to
<<tox,install qutebrowser in a virtualenv>>. However, this is currently untested. If you
@@ -48,7 +48,7 @@ instructions!
Note you'll need some basic libraries to use the virtualenv-installed PyQt:
----
-# apt install --no-install-recommends git ca-certificates python3 python3-venv asciidoc libglib2.0-0 libgl1 libfontconfig1 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxcb-xfixes0 libxcb-xinerama0 libxcb-xkb1 libxkbcommon-x11-0 libdbus-1-3 libyaml-dev gcc python3-dev libnss3
+# apt install --no-install-recommends git ca-certificates python3 python3-venv asciidoc libglib2.0-0 libgl1 libfontconfig1 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxcb-xfixes0 libxcb-xinerama0 libxcb-xkb1 libxkbcommon-x11-0 libdbus-1-3 libyaml-dev gcc python3-dev libnss3 libasound2
----
// FIXME not needed anymore?
@@ -69,6 +69,12 @@ from January 2018 is packaged, with QtWebEngine 5.9 based on a Chromium from Jan
2017. It's recommended to either upgrade to Ubuntu 20.04 LTS or <<tox,install
qutebrowser in a virtualenv>> with a newer PyQt/Qt binary instead.
+Note you'll need some basic libraries to use the virtualenv-installed PyQt:
+
+----
+# apt install --no-install-recommends git ca-certificates python3 python3-venv asciidoc libglib2.0-0 libgl1 libfontconfig1 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxcb-xfixes0 libxcb-xinerama0 libxcb-xkb1 libxkbcommon-x11-0 libdbus-1-3 libyaml-dev gcc python3-dev libnss3 libasound2
+----
+
Ubuntu 20.04 LTS / Linux Mint 20 (or newer)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -278,7 +284,7 @@ https://github.com/qutebrowser/qutebrowser/releases[are built] for every
release.
Note that you'll need to upgrade to new versions manually (subscribe to the
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce[qutebrowser-announce
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser-announce[qutebrowser-announce
mailinglist] to get notified on new releases). You can install a newer version
without uninstalling the older one.
@@ -335,7 +341,7 @@ files from the
https://github.com/qutebrowser/qutebrowser/releases[release page].
Note that you'll need to upgrade to new versions manually (subscribe to the
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce[qutebrowser-announce
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser-announce[qutebrowser-announce
mailinglist] to get notified on new releases).
The binary release ships with a QtWebEngine built without proprietary codec
@@ -447,7 +453,7 @@ This installs all needed Python dependencies in a `.venv` subfolder
This comes with an up-to-date Qt/PyQt including a pre-compiled QtWebEngine
binary, but has a few caveats:
-- Make sure your `python3` is Python 3.6 or newer, otherwise you'll get a "No
+- Make sure your `python3` is Python 3.7 or newer, otherwise you'll get a "No
matching distribution found" error and/or qutebrowser will not run.
- It only works on 64-bit x86 systems, with other architectures you'll get the
same error.
diff --git a/doc/quickstart.asciidoc b/doc/quickstart.asciidoc
index 2e61e442d..0c42880ab 100644
--- a/doc/quickstart.asciidoc
+++ b/doc/quickstart.asciidoc
@@ -33,8 +33,8 @@ image:https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/c
`scripts/asciidoc2html.py` to generate the documentation.
* Go to the link:qute://settings[settings page] to set up qutebrowser the way you want it.
* Subscribe to
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[the mailinglist] or
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce[the announce-only mailinglist].
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser[the mailinglist] or
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser-announce[the announce-only mailinglist].
* Let me know what features you are missing or things that need (even small!)
improvements.
@@ -52,7 +52,7 @@ or https://matrix.to/#qutebrowser:libera.chat[via Matrix])
* On Reddit: https://www.reddit.com/r/qutebrowser/[/r/qutebrowser]
* Via https://github.com/qutebrowser/qutebrowser/discussions[GitHub Discussions]
* Using the mailinglist: mailto:qutebrowser@lists.qutebrowser.org[]
-(https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[subscribe])
+(https://listi.jpberlin.de/mailman/listinfo/qutebrowser[subscribe])
Donating
--------
diff --git a/doc/qutebrowser.1.asciidoc b/doc/qutebrowser.1.asciidoc
index 8db231add..e83a4da0b 100644
--- a/doc/qutebrowser.1.asciidoc
+++ b/doc/qutebrowser.1.asciidoc
@@ -10,14 +10,14 @@
:homepage: https://www.qutebrowser.org/
== NAME
-qutebrowser - a keyboard-driven, vim-like browser based on PyQt5.
+qutebrowser - a keyboard-driven, vim-like browser based on Python and Qt.
== SYNOPSIS
*qutebrowser* ['-OPTION' ['...']] [':COMMAND' ['...']] ['URL' ['...']]
== DESCRIPTION
qutebrowser is a keyboard-focused browser with a minimal GUI. It's based
-on Python and Qt5 and is free software, licensed under the GPL.
+on Python and Qt and is free software, licensed under the GPL.
It was inspired by other browsers/addons like dwb and Vimperator/Pentadactyl.
@@ -34,7 +34,7 @@ show it.
*'URL'*::
URLs to open on startup (empty as a window separator).
-=== optional arguments
+=== options
*-h*, *--help*::
show this help message and exit
@@ -65,6 +65,9 @@ show it.
*--desktop-file-name* 'DESKTOP_FILE_NAME'::
Set the base name of the desktop entry for this application. Used to set the app_id under Wayland. See https://doc.qt.io/qt-5/qguiapplication.html#desktopFileName-prop
+*--untrusted-args*::
+ Mark all following arguments as untrusted, which enforces that they are URLs/search terms (and not flags or commands)
+
=== debug arguments
*-l* '{critical,error,warning,info,debug,vdebug}', *--loglevel* '{critical,error,warning,info,debug,vdebug}'::
Override the configured console loglevel
@@ -127,7 +130,7 @@ If you found a bug, use the built-in ':report' command to create a bug report
with all information needed.
If you prefer, you can also write to the
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[mailinglist] at
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser[mailinglist] at
mailto:qutebrowser@lists.qutebrowser.org[] instead.
For security bugs, please contact me directly at me@the-compiler.org, GPG ID
@@ -149,9 +152,9 @@ this program. If not, see <https://www.gnu.org/licenses/>.
== RESOURCES
* Website: https://www.qutebrowser.org/
* Mailinglist: mailto:qutebrowser@lists.qutebrowser.org[] /
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser
* Announce-only mailinglist: mailto:qutebrowser-announce@lists.qutebrowser.org[] /
-https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce
+https://listi.jpberlin.de/mailman/listinfo/qutebrowser-announce
* IRC: link:ircs://irc.libera.chat:6697/#qutebrowser[`#qutebrowser`] on
https://libera.chat/[Libera Chat] (https://web.libera.chat/#qutebrowser[webchat],
https://matrix.to/#qutebrowser:libera.chat[via Matrix])
diff --git a/doc/stacktrace.asciidoc b/doc/stacktrace.asciidoc
index 3d95aa25e..87410bd48 100644
--- a/doc/stacktrace.asciidoc
+++ b/doc/stacktrace.asciidoc
@@ -68,55 +68,13 @@ Then install the needed debuginfo packages:
Archlinux
^^^^^^^^^
-For Archlinux, no debug information is provided. You can either compile Qt
-yourself (which will take a few hours even on a modern machine) or use
-debugging symbols compiled/packaged by me (x86_64 only).
-
-To install my pre-built packages
-++++++++++++++++++++++++++++++++
-
-First download and sign the key:
-
-----
-# pacman-key -r 0xD6A1C70FE80A0C82
-$ pacman-key -f 0xD6A1C70FE80A0C82
- Key fingerprint = 14AF EC28 70C6 4863 C5C7 ACCB D6A1 C70F E80A 0C82
-# pacman-key --lsign-key 0xD6A1C70FE80A0C82
-----
-
-Then edit your `/etc/pacman.conf` to add the repository to the bottom:
+For Archlinux, debug information is provided via their https://wiki.archlinux.org/title/Debuginfod[Debuginfod instance]. To use it, set the following in your environment:
----
-[qt-debug]
-Server = https://qutebrowser.org/qt-debug/$arch
+DEBUGINFOD_URLS="https://debuginfod.archlinux.org/"
----
-Then install the packages:
-
-----
-# pacman -Suy python-pyqt5-debug qt5-base-debug qt5-webkit-debug qt5-webengine-debug
-----
-
-The `-debug` packages conflict with the non-debug variants - it's safe to
-remove them.
-
-To compile by yourself
-++++++++++++++++++++++
-
-Note that building Qt will likely take multiple hours, even on a recent system.
-I'd also expect it to take around 6 GB of RAM and 30 GB of disk space for a
-successful compile run.
-
-----
-$ git clone https://github.com/qutebrowser/qt-debug-pkgbuild.git
-$ cd qt-debug-pkgbuild
-$ export DEBUG_CFLAGS='-ggdb3 -fvar-tracking-assignments -Og'
-$ export DEBUG_CXXFLAGS='-ggdb3 -fvar-tracking-assignments -Og'
-$ cd qt5
-$ makepkg -si --pkg qt5-base-debug,qt5-webkit-debug,qt5-webengine-debug
-$ cd ../pyqt5
-$ makepkg -si --pkg python-pyqt5-debug
-----
+(Until early 2021, there was a custom [`qt-debug` repository](https://github.com/qutebrowser/qt-debug-pkgbuild). This is now archived.)
Getting the stack trace
~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/doc/userscripts.asciidoc b/doc/userscripts.asciidoc
index 5ff358ee0..4539a0ca8 100644
--- a/doc/userscripts.asciidoc
+++ b/doc/userscripts.asciidoc
@@ -56,6 +56,7 @@ In `command` mode:
- `QUTE_TITLE`: The title of the current page.
- `QUTE_SELECTED_TEXT`: The text currently selected on the page.
- `QUTE_COUNT`: The `count` from the spawn command running the userscript.
+- `QUTE_TAB_INDEX`: The current tab's index.
In `hints` mode:
diff --git a/misc/Makefile b/misc/Makefile
index b916a20d5..62294ba61 100644
--- a/misc/Makefile
+++ b/misc/Makefile
@@ -24,9 +24,9 @@ install: man
"$(DESTDIR)$(MANDIR)/man1/qutebrowser.1"
install -Dm644 misc/org.qutebrowser.qutebrowser.desktop \
"$(DESTDIR)$(DATADIR)/applications/org.qutebrowser.qutebrowser.desktop"
- $(foreach i,$(ICONSIZES),install -Dm644 "icons/qutebrowser-$(i)x$(i).png" \
+ $(foreach i,$(ICONSIZES),install -Dm644 "qutebrowser/icons/qutebrowser-$(i)x$(i).png" \
"$(DESTDIR)$(DATADIR)/icons/hicolor/$(i)x$(i)/apps/qutebrowser.png";)
- install -Dm644 icons/qutebrowser.svg \
+ install -Dm644 qutebrowser/icons/qutebrowser.svg \
"$(DESTDIR)$(DATADIR)/icons/hicolor/scalable/apps/qutebrowser.svg"
install -Dm755 -t "$(DESTDIR)$(DATADIR)/qutebrowser/userscripts/" \
$(filter-out misc/userscripts/__pycache__,$(wildcard misc/userscripts/*))
diff --git a/misc/nsis/install.nsh b/misc/nsis/install.nsh
index f29a0a9a8..8233ab5f0 100755
--- a/misc/nsis/install.nsh
+++ b/misc/nsis/install.nsh
@@ -351,13 +351,12 @@ Section "Register with Windows" SectionWindowsRegister
!insertmacro UpdateRegDWORD SHCTX "SOFTWARE\Classes\$2" "EditFlags" 0x00000002
!insertmacro UpdateRegStr SHCTX "SOFTWARE\Classes\$2\DefaultIcon" "" "$1,0"
!insertmacro UpdateRegStr SHCTX "SOFTWARE\Classes\$2\shell" "" "open"
- !insertmacro UpdateRegStr SHCTX "SOFTWARE\Classes\$2\shell\open\command" "" "$\"$1$\" $\"%1$\""
+ !insertmacro UpdateRegStr SHCTX "SOFTWARE\Classes\$2\shell\open\command" "" "$\"$1$\" --untrusted-args $\"%1$\""
!insertmacro UpdateRegStr SHCTX "SOFTWARE\Classes\$2\shell\open\ddeexec" "" ""
StrCmp $2 "${PRODUCT_NAME}HTML" 0 +4
StrCpy $2 "${PRODUCT_NAME}URL"
StrCpy $3 "${PRODUCT_NAME} URL"
Goto WriteRegHandler
- !insertmacro UpdateRegStr SHCTX "SOFTWARE\Classes\$2" "URL Protocol" ""
${endif}
SectionEnd
@@ -543,8 +542,16 @@ Function PageInstallModeChangeMode
FunctionEnd
Function PageComponentsPre
- GetDlgItem $0 $HWNDPARENT 1
- SendMessage $0 ${BCM_SETSHIELD} 0 0 ; hide SHIELD (Windows Vista and above)
+ SendMessage $mui.Button.Next ${BCM_SETSHIELD} 0 0
+ StrCmpS $HasCurrentModeInstallation 0 +9
+ IfFileExists "$DESKTOP\${PRODUCT_NAME}.lnk" +4
+ SectionGetFlags ${SectionDesktopIcon} $1
+ IntOp $1 $1 & 0xFFFFFFFE
+ SectionSetFlags ${SectionDesktopIcon} $1
+ IfFileExists "$STARTMENU\${PRODUCT_NAME}.lnk" +4
+ SectionGetFlags ${SectionStartMenuIcon} $1
+ IntOp $1 $1 & 0xFFFFFFFE
+ SectionSetFlags ${SectionStartMenuIcon} $1
FunctionEnd
Function PageDirectoryPre
diff --git a/misc/nsis/qutebrowser.nsi b/misc/nsis/qutebrowser.nsi
index 51f3b623d..7623d8cb2 100755
--- a/misc/nsis/qutebrowser.nsi
+++ b/misc/nsis/qutebrowser.nsi
@@ -43,6 +43,9 @@ ShowUninstDetails hide
!addplugindir /x86-unicode ".\plugins\x86-unicode"
!addincludedir ".\include"
+!define MUI_BGCOLOR "SYSCLR:Window"
+!define MUI_TEXTCOLOR "SYSCLR:WindowText"
+
!include MUI2.nsh
!include NsisMultiUser.nsh
!include StdUtils.nsh
@@ -57,7 +60,7 @@ ShowUninstDetails hide
!define URL_UPDATE "https://qutebrowser.org/doc/install.html"
!define HELP_LINK "https://qutebrowser.org/doc/help/"
!define CONTACT "mail@qutebrowser.org"
-!define COMMENTS "A keyboard-driven, vim-like browser based on PyQt5."
+!define COMMENTS "A keyboard-driven, vim-like browser based on Python and Qt."
!define LANGID "1033" ; U.S. English
!define MIN_WIN_VER "8"
!define SETUP_MUTEX "${PRODUCT_NAME} Setup Mutex" ; do not change this between program versions!
diff --git a/misc/org.qutebrowser.qutebrowser.appdata.xml b/misc/org.qutebrowser.qutebrowser.appdata.xml
index 49bc09786..f506bd5a8 100644
--- a/misc/org.qutebrowser.qutebrowser.appdata.xml
+++ b/misc/org.qutebrowser.qutebrowser.appdata.xml
@@ -44,6 +44,10 @@
</content_rating>
<releases>
<!-- Add new releases here -->
+<release version="2.5.1" date="2022-05-26"/>
+<release version="2.5.0" date="2022-04-01"/>
+<release version="2.4.0" date="2021-10-21"/>
+<release version="2.3.1" date="2021-07-28"/>
<release version="2.3.0" date="2021-06-28"/>
<release version="2.2.3" date="2021-06-01"/>
<release version="2.2.2" date="2021-05-20"/>
diff --git a/misc/org.qutebrowser.qutebrowser.desktop b/misc/org.qutebrowser.qutebrowser.desktop
index 52144b3c5..741a00371 100644
--- a/misc/org.qutebrowser.qutebrowser.desktop
+++ b/misc/org.qutebrowser.qutebrowser.desktop
@@ -39,13 +39,13 @@ GenericName[ta]=இணைய உலாவி
GenericName[th]=เว็บเบราว์เซอร์
GenericName[tr]=Web Tarayıcı
GenericName[uk]=Навігатор Тенет瀏覽器
-Comment=A keyboard-driven, vim-like browser based on PyQt5
-Comment[de]=Ein Tastatur-gesteuerter, vim-ähnlicher Browser basierend auf PyQt5
-Comment[it]= Un browser web vim-like utilizzabile da tastiera basato su PyQt5
+Comment=A keyboard-driven, vim-like browser based on Python and Qt
+Comment[de]=Ein Tastatur-gesteuerter, vim-ähnlicher Browser basierend auf Python und Qt
+Comment[it]= Un browser web vim-like utilizzabile da tastiera basato su Python e Qt
Icon=qutebrowser
Type=Application
Categories=Network;WebBrowser;
-Exec=qutebrowser %u
+Exec=qutebrowser --untrusted-args %u
Terminal=false
StartupNotify=true
MimeType=text/html;text/xml;application/xhtml+xml;application/xml;application/rdf+xml;image/gif;image/jpeg;image/png;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/qute;
diff --git a/misc/qutebrowser.rcc b/misc/qutebrowser.rcc
deleted file mode 100644
index 2f73b3726..000000000
--- a/misc/qutebrowser.rcc
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE RCC><RCC version="1.0">
-<qresource>
- <file>icons/qutebrowser-16x16.png</file>
- <file>icons/qutebrowser-24x24.png</file>
- <file>icons/qutebrowser-32x32.png</file>
- <file>icons/qutebrowser-48x48.png</file>
- <file>icons/qutebrowser-64x64.png</file>
- <file>icons/qutebrowser-96x96.png</file>
- <file>icons/qutebrowser-128x128.png</file>
- <file>icons/qutebrowser-256x256.png</file>
- <file>icons/qutebrowser-512x512.png</file>
-</qresource>
-</RCC>
diff --git a/misc/qutebrowser.spec b/misc/qutebrowser.spec
index ffb17d371..60729266e 100644
--- a/misc/qutebrowser.spec
+++ b/misc/qutebrowser.spec
@@ -15,6 +15,7 @@ def get_data_files():
data_files = [
('../qutebrowser/html', 'html'),
('../qutebrowser/img', 'img'),
+ ('../qutebrowser/icons', 'icons'),
('../qutebrowser/javascript', 'javascript'),
('../qutebrowser/html/doc', 'html/doc'),
('../qutebrowser/git-commit-id', '.'),
@@ -40,9 +41,9 @@ setupcommon.write_git_file()
if os.name == 'nt':
- icon = '../icons/qutebrowser.ico'
+ icon = '../qutebrowser/icons/qutebrowser.ico'
elif sys.platform == 'darwin':
- icon = '../icons/qutebrowser.icns'
+ icon = '../qutebrowser/icons/qutebrowser.icns'
else:
icon = None
diff --git a/misc/requirements/requirements-check-manifest.txt b/misc/requirements/requirements-check-manifest.txt
index 8b7067709..c51ef3d0e 100644
--- a/misc/requirements/requirements-check-manifest.txt
+++ b/misc/requirements/requirements-check-manifest.txt
@@ -1,8 +1,8 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-build==0.5.1
-check-manifest==0.46
-packaging==21.0
-pep517==0.10.0
-pyparsing==2.4.7
-toml==0.10.2
+build==0.8.0
+check-manifest==0.48
+packaging==21.3
+pep517==0.12.0
+pyparsing==3.0.9
+tomli==2.0.1
diff --git a/misc/requirements/requirements-dev.txt b/misc/requirements/requirements-dev.txt
index 2963888b6..e4e768353 100644
--- a/misc/requirements/requirements-dev.txt
+++ b/misc/requirements/requirements-dev.txt
@@ -1,26 +1,45 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
+bleach==5.0.0
+build==0.8.0
bump2version==1.0.1
-certifi==2021.5.30
-cffi==1.14.6
-chardet==4.0.0
-cryptography==3.4.7
-Deprecated==1.2.12
-github3.py==2.0.0
-hunter==3.3.8
-idna==2.10
-jwcrypto==0.9.1
+certifi==2022.5.18.1
+cffi==1.15.0
+charset-normalizer==2.0.12
+commonmark==0.9.1
+cryptography==37.0.2
+docutils==0.18.1
+github3.py==3.2.0
+hunter==3.4.3
+idna==3.3
+importlib-metadata==4.11.4
+jeepney==0.8.0
+keyring==23.6.0
manhole==1.8.0
-packaging==21.0
-pycparser==2.20
-Pympler==0.9
-pyparsing==2.4.7
-PyQt-builder==1.10.3
-python-dateutil==2.8.1
-requests==2.25.1
-sip==6.1.1
+packaging==21.3
+pep517==0.12.0
+pkginfo==1.8.3
+ply==3.11
+pycparser==2.21
+Pygments==2.12.0
+PyJWT==2.4.0
+Pympler==1.0.1
+pyparsing==3.0.9
+PyQt-builder==1.12.2
+python-dateutil==2.8.2
+readme-renderer==35.0
+requests==2.28.0
+requests-toolbelt==0.9.1
+rfc3986==2.0.0
+rich==12.4.4
+SecretStorage==3.3.2
+sip==6.6.1
six==1.16.0
toml==0.10.2
-uritemplate==3.0.1
-# urllib3==1.26.6
-wrapt==1.12.1
+tomli==2.0.1
+twine==4.0.1
+typing_extensions==4.2.0
+uritemplate==4.1.1
+# urllib3==1.26.9
+webencodings==0.5.1
+zipp==3.8.0
diff --git a/misc/requirements/requirements-dev.txt-raw b/misc/requirements/requirements-dev.txt-raw
index fd840bab1..261f4459f 100644
--- a/misc/requirements/requirements-dev.txt-raw
+++ b/misc/requirements/requirements-dev.txt-raw
@@ -4,6 +4,8 @@ github3.py
bump2version
requests
pyqt-builder
+build
+twine
# Already included via test requirements
#@ ignore: urllib3
diff --git a/misc/requirements/requirements-flake8.txt b/misc/requirements/requirements-flake8.txt
index cc041b538..217089191 100644
--- a/misc/requirements/requirements-flake8.txt
+++ b/misc/requirements/requirements-flake8.txt
@@ -1,24 +1,25 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-attrs==21.2.0
-flake8==3.9.2
-flake8-bugbear==21.4.3
+attrs==21.4.0
+flake8==4.0.1
+flake8-bugbear==22.4.25
flake8-builtins==1.5.3
-flake8-comprehensions==3.5.0
+flake8-comprehensions==3.10.0
flake8-copyright==0.2.2
-flake8-debugger==4.0.0
+flake8-debugger==4.1.2
flake8-deprecated==1.3
flake8-docstrings==1.6.0
flake8-future-import==0.4.6
flake8-mock==0.3
-flake8-polyfill==1.0.2
+flake8-plugin-utils==1.3.2
+flake8-pytest-style==1.6.0
flake8-string-format==0.3.0
-flake8-tidy-imports==4.3.0
+flake8-tidy-imports==4.8.0
flake8-tuple==0.4.1
mccabe==0.6.1
-pep8-naming==0.12.0
-pycodestyle==2.7.0
+pep8-naming==0.13.0
+pycodestyle==2.8.0
pydocstyle==6.1.1
-pyflakes==2.3.1
+pyflakes==2.4.0
six==1.16.0
-snowballstemmer==2.1.0
+snowballstemmer==2.2.0
diff --git a/misc/requirements/requirements-flake8.txt-raw b/misc/requirements/requirements-flake8.txt-raw
index 1bdca6974..59908897c 100644
--- a/misc/requirements/requirements-flake8.txt-raw
+++ b/misc/requirements/requirements-flake8.txt-raw
@@ -11,6 +11,7 @@ flake8-mock
flake8-string-format
flake8-tidy-imports
flake8-tuple
+flake8-pytest-style
pep8-naming
pydocstyle
pyflakes
diff --git a/misc/requirements/requirements-mypy.txt b/misc/requirements/requirements-mypy.txt
index e8b09d628..a4b555cf3 100644
--- a/misc/requirements/requirements-mypy.txt
+++ b/misc/requirements/requirements-mypy.txt
@@ -1,21 +1,18 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
chardet==4.0.0
-diff-cover==6.1.1
-importlib-metadata==4.6.1
-importlib-resources==5.2.0
-inflect==5.3.0
-Jinja2==3.0.1
-jinja2-pluralize==0.3.0
-lxml==4.6.3
-MarkupSafe==2.0.1
-mypy==0.910
+diff-cover==6.5.0
+importlib-metadata==4.11.4
+importlib-resources==5.7.1
+Jinja2==3.1.2
+lxml==4.9.0
+MarkupSafe==2.1.1
+mypy==0.961
mypy-extensions==0.4.3
-pluggy==0.13.1
-Pygments==2.9.0
-PyQt5-stubs==5.15.2.0
-toml==0.10.2
-types-dataclasses==0.1.5
-types-PyYAML==5.4.3
-typing-extensions==3.10.0.0
-zipp==3.5.0
+pluggy==1.0.0
+Pygments==2.12.0
+PyQt5-stubs==5.15.6.0
+tomli==2.0.1
+types-PyYAML==6.0.8
+typing_extensions==4.2.0
+zipp==3.8.0
diff --git a/misc/requirements/requirements-mypy.txt-raw b/misc/requirements/requirements-mypy.txt-raw
index 4baeec11f..dd00d3219 100644
--- a/misc/requirements/requirements-mypy.txt-raw
+++ b/misc/requirements/requirements-mypy.txt-raw
@@ -3,7 +3,6 @@ lxml # For HTML reports
diff-cover
PyQt5-stubs
-types-dataclasses
types-PyYAML
# So stubs are available even on newer Python versions
diff --git a/misc/requirements/requirements-pip.txt b/misc/requirements/requirements-pip.txt
deleted file mode 100644
index aba943496..000000000
--- a/misc/requirements/requirements-pip.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-# This file is automatically generated by scripts/dev/recompile_requirements.py
-
-appdirs==1.4.4
-packaging==20.4
-pyparsing==2.4.7
-setuptools==47.3.1
-six==1.15.0
-wheel==0.34.2
diff --git a/misc/requirements/requirements-pyinstaller.txt b/misc/requirements/requirements-pyinstaller.txt
index 86d73655d..35e65b6da 100644
--- a/misc/requirements/requirements-pyinstaller.txt
+++ b/misc/requirements/requirements-pyinstaller.txt
@@ -1,5 +1,5 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-altgraph==0.17
-pyinstaller==4.3
-pyinstaller-hooks-contrib==2021.2
+altgraph==0.17.2
+pyinstaller==5.1
+pyinstaller-hooks-contrib==2022.7
diff --git a/misc/requirements/requirements-pylint.txt b/misc/requirements/requirements-pylint.txt
index f0a1c2cb4..38231fa12 100644
--- a/misc/requirements/requirements-pylint.txt
+++ b/misc/requirements/requirements-pylint.txt
@@ -1,26 +1,30 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-astroid==2.3.3 # rq.filter: < 2.4
-certifi==2021.5.30
-cffi==1.14.6
-chardet==4.0.0
-cryptography==3.4.7
-Deprecated==1.2.12
+astroid==2.11.5
+certifi==2022.5.18.1
+cffi==1.15.0
+charset-normalizer==2.0.12
+cryptography==37.0.2
+dill==0.3.5.1
future==0.18.2
-github3.py==2.0.0
-idna==2.10
-isort==4.3.21
-jwcrypto==0.9.1
-lazy-object-proxy==1.4.3
-mccabe==0.6.1
-pefile==2021.5.24
-pycparser==2.20
-pylint==2.4.4 # rq.filter: < 2.5
-python-dateutil==2.8.1
+github3.py==3.2.0
+idna==3.3
+isort==5.10.1
+lazy-object-proxy==1.7.1
+mccabe==0.7.0
+pefile==2022.5.30
+platformdirs==2.5.2
+pycparser==2.21
+PyJWT==2.4.0
+pylint==2.14.1
+python-dateutil==2.8.2
./scripts/dev/pylint_checkers
-requests==2.25.1
+requests==2.28.0
six==1.16.0
-typed-ast==1.4.3 ; python_version<"3.8"
-uritemplate==3.0.1
-# urllib3==1.26.6
-wrapt==1.11.2
+tomli==2.0.1
+tomlkit==0.11.0
+typed-ast==1.5.4 ; python_version<"3.8"
+typing_extensions==4.2.0
+uritemplate==4.1.1
+# urllib3==1.26.9
+wrapt==1.14.1
diff --git a/misc/requirements/requirements-pylint.txt-raw b/misc/requirements/requirements-pylint.txt-raw
index 08d340665..54e12a02a 100644
--- a/misc/requirements/requirements-pylint.txt-raw
+++ b/misc/requirements/requirements-pylint.txt-raw
@@ -1,4 +1,4 @@
-pylint<2.5
+pylint
./scripts/dev/pylint_checkers
requests
github3.py
@@ -7,8 +7,6 @@ pefile
# fix qute-pylint location
#@ replace: qute-pylint.* ./scripts/dev/pylint_checkers
#@ markers: typed-ast python_version<"3.8"
-#@ filter: pylint < 2.5
-#@ filter: astroid < 2.4
# Already included via test requirements
#@ ignore: urllib3
diff --git a/misc/requirements/requirements-pyqt-5.12.txt b/misc/requirements/requirements-pyqt-5.12.txt
index 890306127..499f420e5 100644
--- a/misc/requirements/requirements-pyqt-5.12.txt
+++ b/misc/requirements/requirements-pyqt-5.12.txt
@@ -1,5 +1,5 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
PyQt5==5.12.3 # rq.filter: < 5.13
-PyQt5-sip==12.9.0
+PyQt5-sip==12.10.1
PyQtWebEngine==5.12.1 # rq.filter: < 5.13
diff --git a/misc/requirements/requirements-pyqt-5.13.txt b/misc/requirements/requirements-pyqt-5.13.txt
index 5f4da4758..0fb28e312 100644
--- a/misc/requirements/requirements-pyqt-5.13.txt
+++ b/misc/requirements/requirements-pyqt-5.13.txt
@@ -1,5 +1,5 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
PyQt5==5.13.2 # rq.filter: < 5.14
-PyQt5-sip==12.9.0
+PyQt5-sip==12.10.1
PyQtWebEngine==5.13.2 # rq.filter: < 5.14
diff --git a/misc/requirements/requirements-pyqt-5.14.txt b/misc/requirements/requirements-pyqt-5.14.txt
index 9ce643666..953b3ded6 100644
--- a/misc/requirements/requirements-pyqt-5.14.txt
+++ b/misc/requirements/requirements-pyqt-5.14.txt
@@ -1,5 +1,5 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
PyQt5==5.14.2 # rq.filter: < 5.15
-PyQt5-sip==12.9.0
+PyQt5-sip==12.10.1
PyQtWebEngine==5.14.0 # rq.filter: < 5.15
diff --git a/misc/requirements/requirements-pyqt-5.15.0.txt b/misc/requirements/requirements-pyqt-5.15.0.txt
index b111a93f3..703a8b248 100644
--- a/misc/requirements/requirements-pyqt-5.15.0.txt
+++ b/misc/requirements/requirements-pyqt-5.15.0.txt
@@ -1,5 +1,5 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
PyQt5==5.15.0 # rq.filter: == 5.15.0
-PyQt5-sip==12.9.0
+PyQt5-sip==12.10.1
PyQtWebEngine==5.15.0 # rq.filter: == 5.15.0
diff --git a/misc/requirements/requirements-pyqt-5.15.txt b/misc/requirements/requirements-pyqt-5.15.txt
index 8b7a53c44..daa7e7136 100644
--- a/misc/requirements/requirements-pyqt-5.15.txt
+++ b/misc/requirements/requirements-pyqt-5.15.txt
@@ -1,7 +1,7 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-PyQt5==5.15.4 # rq.filter: < 5.16
+PyQt5==5.15.6 # rq.filter: < 5.16
PyQt5-Qt5==5.15.2
-PyQt5-sip==12.9.0
-PyQtWebEngine==5.15.4 # rq.filter: < 5.16
+PyQt5-sip==12.10.1
+PyQtWebEngine==5.15.5 # rq.filter: < 5.16
PyQtWebEngine-Qt5==5.15.2
diff --git a/misc/requirements/requirements-pyqt-pyinstaller.txt b/misc/requirements/requirements-pyqt-pyinstaller.txt
deleted file mode 100644
index 678a1d7ea..000000000
--- a/misc/requirements/requirements-pyqt-pyinstaller.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-# This file is automatically generated by scripts/dev/recompile_requirements.py
-
-PyQt5==5.15.3
-PyQt5-Qt==5.15.2
-PyQt5-sip==12.9.0
-PyQtWebEngine==5.15.3
-PyQtWebEngine-Qt==5.15.2
diff --git a/misc/requirements/requirements-pyqt-pyinstaller.txt-raw b/misc/requirements/requirements-pyqt-pyinstaller.txt-raw
deleted file mode 100644
index 89b5644da..000000000
--- a/misc/requirements/requirements-pyqt-pyinstaller.txt-raw
+++ /dev/null
@@ -1,2 +0,0 @@
-PyQt5==5.15.3
-PyQtWebEngine==5.15.3
diff --git a/misc/requirements/requirements-pyqt.txt b/misc/requirements/requirements-pyqt.txt
index 75ef27bf4..1641b4fcd 100644
--- a/misc/requirements/requirements-pyqt.txt
+++ b/misc/requirements/requirements-pyqt.txt
@@ -1,7 +1,7 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-PyQt5==5.15.4
+PyQt5==5.15.6
PyQt5-Qt5==5.15.2
-PyQt5-sip==12.9.0
-PyQtWebEngine==5.15.4
+PyQt5-sip==12.10.1
+PyQtWebEngine==5.15.5
PyQtWebEngine-Qt5==5.15.2
diff --git a/misc/requirements/requirements-pyroma.txt b/misc/requirements/requirements-pyroma.txt
index a83804b9e..382418dd9 100644
--- a/misc/requirements/requirements-pyroma.txt
+++ b/misc/requirements/requirements-pyroma.txt
@@ -1,10 +1,15 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-certifi==2021.5.30
-chardet==4.0.0
-docutils==0.17.1
-idna==2.10
-Pygments==2.9.0
-pyroma==3.2
-requests==2.25.1
-urllib3==1.26.6
+build==0.8.0
+certifi==2022.5.18.1
+charset-normalizer==2.0.12
+docutils==0.18.1
+idna==3.3
+packaging==21.3
+pep517==0.12.0
+Pygments==2.12.0
+pyparsing==3.0.9
+pyroma==4.0
+requests==2.28.0
+tomli==2.0.1
+urllib3==1.26.9
diff --git a/misc/requirements/requirements-qutebrowser.txt-raw b/misc/requirements/requirements-qutebrowser.txt-raw
index e07ea992a..2025280fc 100644
--- a/misc/requirements/requirements-qutebrowser.txt-raw
+++ b/misc/requirements/requirements-qutebrowser.txt-raw
@@ -3,7 +3,6 @@ PyYAML
## stdlib backports
importlib-resources
-dataclasses
## Optional dependencies
Pygments # For :view-source --pygments or on QtWebKit
@@ -14,7 +13,6 @@ adblock # Improved adblocking
importlib-metadata # Determining PyQt version
typing_extensions # from importlib-metadata
-#@ markers: importlib-resources python_version<"3.9"
-#@ markers: importlib-metadata python_version<"3.8"
+#@ markers: importlib-resources python_version=="3.7.*" or python_version=="3.8.*"
+#@ markers: importlib-metadata python_version=="3.7.*"
#@ markers: typing_extensions python_version<"3.8"
-#@ markers: dataclasses python_version<"3.7"
diff --git a/misc/requirements/requirements-sphinx.txt b/misc/requirements/requirements-sphinx.txt
index f5a7cb3ca..f100b6dc0 100644
--- a/misc/requirements/requirements-sphinx.txt
+++ b/misc/requirements/requirements-sphinx.txt
@@ -1,25 +1,27 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
alabaster==0.7.12
-Babel==2.9.1
-certifi==2021.5.30
-chardet==4.0.0
-docutils==0.17.1
-idna==2.10
-imagesize==1.2.0
-Jinja2==3.0.1
-MarkupSafe==2.0.1
-packaging==21.0
-Pygments==2.9.0
-pyparsing==2.4.7
-pytz==2021.1
-requests==2.25.1
-snowballstemmer==2.1.0
-Sphinx==4.1.0
+Babel==2.10.1
+certifi==2022.5.18.1
+charset-normalizer==2.0.12
+docutils==0.18.1
+idna==3.3
+imagesize==1.3.0
+importlib-metadata==4.11.4
+Jinja2==3.1.2
+MarkupSafe==2.1.1
+packaging==21.3
+Pygments==2.12.0
+pyparsing==3.0.9
+pytz==2022.1
+requests==2.28.0
+snowballstemmer==2.2.0
+Sphinx==5.0.1
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
-urllib3==1.26.6
+urllib3==1.26.9
+zipp==3.8.0
diff --git a/misc/requirements/requirements-tests-bleeding.txt b/misc/requirements/requirements-tests-bleeding.txt
index 5fbb05f05..fca7328f8 100644
--- a/misc/requirements/requirements-tests-bleeding.txt
+++ b/misc/requirements/requirements-tests-bleeding.txt
@@ -7,7 +7,9 @@ git+https://github.com/pallets/flask.git
git+https://github.com/pallets/werkzeug.git # transitive dep, but needed to work
git+https://github.com/HypothesisWorks/hypothesis.git#subdirectory=hypothesis-python
git+https://github.com/pytest-dev/pytest.git
-git+https://github.com/pytest-dev/pytest-bdd.git
+# Problematic: https://github.com/pytest-dev/pytest-bdd/issues/447
+# git+https://github.com/pytest-dev/pytest-bdd.git
+pytest-bdd<5
git+https://github.com/ionelmc/pytest-benchmark.git
git+https://github.com/pytest-dev/pytest-instafail.git
git+https://github.com/pytest-dev/pytest-mock.git
@@ -22,8 +24,6 @@ git+https://github.com/pytest-dev/pytest-cov.git
git+https://github.com/The-Compiler/pytest-xvfb.git
git+https://github.com/pytest-dev/pytest-xdist.git
git+https://github.com/john-kurkowski/tldextract
-# https://github.com/hjwp/pytest-icdiff/pull/20
-# git+https://github.com/hjwp/pytest-icdiff.git
# Problematic: needs rust (and some time to build)
# git+https://github.com/ArniDagur/python-adblock.git
@@ -32,6 +32,7 @@ adblock
## qutebrowser dependencies
git+https://github.com/pallets/jinja.git
-git+https://github.com/yaml/pyyaml.git
+# Problematic: https://github.com/yaml/pyyaml/issues/601
+PyYAML
git+https://github.com/tartley/colorama.git
git+https://github.com/pyparsing/pyparsing.git
diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt
index 0d4db4579..3e9f3233d 100644
--- a/misc/requirements/requirements-tests.txt
+++ b/misc/requirements/requirements-tests.txt
@@ -1,59 +1,59 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-attrs==21.2.0
-beautifulsoup4==4.9.3
-certifi==2021.5.30
-chardet==4.0.0
-cheroot==8.5.2
-click==8.0.1
-coverage==5.5
-EasyProcess==0.3
+attrs==21.4.0
+beautifulsoup4==4.11.1
+certifi==2022.5.18.1
+charset-normalizer==2.0.12
+cheroot==8.6.0
+click==8.1.3
+coverage==6.4.1
+exceptiongroup==1.0.0rc8
execnet==1.9.0
-filelock==3.0.12
-Flask==2.0.1
+filelock==3.7.1
+Flask==2.1.2
glob2==0.7
-hunter==3.3.8
-hypothesis==6.14.2
-icdiff==1.9.1
-idna==2.10
+hunter==3.4.3
+hypothesis==6.47.2
+idna==3.3
+importlib-metadata==4.11.4
iniconfig==1.1.1
-itsdangerous==2.0.1
-jaraco.functools==3.3.0
-# Jinja2==3.0.1
-Mako==1.1.4
+itsdangerous==2.1.2
+jaraco.functools==3.5.0
+# Jinja2==3.1.2
+Mako==1.2.0
manhole==1.8.0
-# MarkupSafe==2.0.1
-more-itertools==8.8.0
-packaging==21.0
+# MarkupSafe==2.1.1
+more-itertools==8.13.0
+packaging==21.3
parse==1.19.0
-parse-type==0.5.2
-pluggy==0.13.1
-pprintpp==0.4.0
-py==1.10.0
+parse-type==0.6.0
+pluggy==1.0.0
+py==1.11.0
py-cpuinfo==8.0.0
-Pygments==2.9.0
-pyparsing==2.4.7
-pytest==6.2.4
+Pygments==2.12.0
+pyparsing==3.0.9
+pytest==7.1.2
pytest-bdd==4.1.0
pytest-benchmark==3.4.1
-pytest-cov==2.12.1
-pytest-forked==1.3.0
-pytest-icdiff==0.5
+pytest-cov==3.0.0
+pytest-forked==1.4.0
pytest-instafail==0.4.2
-pytest-mock==3.6.1
+pytest-mock==3.7.0
pytest-qt==4.0.2
pytest-repeat==0.9.1
-pytest-rerunfailures==10.1
-pytest-xdist==2.3.0
+pytest-rerunfailures==10.2
+pytest-xdist==2.5.0
pytest-xvfb==2.0.0
-PyVirtualDisplay==2.2
-requests==2.25.1
+PyVirtualDisplay==3.0
+requests==2.28.0
requests-file==1.5.1
six==1.16.0
sortedcontainers==2.4.0
-soupsieve==2.2.1
-tldextract==3.1.0
+soupsieve==2.3.2.post1
+tldextract==3.3.0
toml==0.10.2
-urllib3==1.26.6
-vulture==2.3
-Werkzeug==2.0.1
+tomli==2.0.1
+urllib3==1.26.9
+vulture==2.4
+Werkzeug==2.1.2
+zipp==3.8.0
diff --git a/misc/requirements/requirements-tests.txt-raw b/misc/requirements/requirements-tests.txt-raw
index ab580ac4b..f5edc9b6d 100644
--- a/misc/requirements/requirements-tests.txt-raw
+++ b/misc/requirements/requirements-tests.txt-raw
@@ -4,7 +4,8 @@ coverage
Flask
hypothesis
pytest
-pytest-bdd
+# https://github.com/pytest-dev/pytest-bdd/issues/447
+pytest-bdd<5
pytest-benchmark
pytest-instafail
pytest-mock
@@ -27,8 +28,6 @@ pytest-xvfb
PyVirtualDisplay
# To run on multiple cores with -n
pytest-xdist
-# For nicer output
-pytest-icdiff
# Needed to test misc/userscripts/qute-lastpass
tldextract
diff --git a/misc/requirements/requirements-tox.txt b/misc/requirements/requirements-tox.txt
index 82eb5b25c..533e91e82 100644
--- a/misc/requirements/requirements-tox.txt
+++ b/misc/requirements/requirements-tox.txt
@@ -1,16 +1,16 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-appdirs==1.4.4
-distlib==0.3.2
-filelock==3.0.12
-packaging==21.0
-pip==21.1.3
-pluggy==0.13.1
-py==1.10.0
-pyparsing==2.4.7
-setuptools==57.1.0
+distlib==0.3.4
+filelock==3.7.1
+packaging==21.3
+pip==22.1.2
+platformdirs==2.5.2
+pluggy==1.0.0
+py==1.11.0
+pyparsing==3.0.9
+setuptools==62.3.4
six==1.16.0
toml==0.10.2
-tox==3.23.1
-virtualenv==20.4.7
-wheel==0.36.2
+tox==3.25.0
+virtualenv==20.14.1
+wheel==0.37.1
diff --git a/misc/requirements/requirements-vulture.txt b/misc/requirements/requirements-vulture.txt
index 433500bf0..ac5016c99 100644
--- a/misc/requirements/requirements-vulture.txt
+++ b/misc/requirements/requirements-vulture.txt
@@ -1,4 +1,4 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
toml==0.10.2
-vulture==2.3
+vulture==2.4
diff --git a/misc/requirements/requirements-yamllint.txt b/misc/requirements/requirements-yamllint.txt
index a911b9d2b..12553f2b2 100644
--- a/misc/requirements/requirements-yamllint.txt
+++ b/misc/requirements/requirements-yamllint.txt
@@ -1,5 +1,5 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-pathspec==0.8.1
-PyYAML==5.4.1
-yamllint==1.26.1
+pathspec==0.9.0
+PyYAML==6.0
+yamllint==1.26.3
diff --git a/misc/userscripts/README.md b/misc/userscripts/README.md
index f5325127b..5a7a3d51d 100644
--- a/misc/userscripts/README.md
+++ b/misc/userscripts/README.md
@@ -74,6 +74,8 @@ The following userscripts can be found on their own repositories.
and retrieve they when you want.
- [doi](https://github.com/cadadr/configuration/blob/master/qutebrowser/userscripts/doi):
Opens DOIs on Sci-Hub.
+- [qute-1password](https://github.com/fmartingr/qute-1password):
+ Qutebrowser userscript to fill 1password credentials
- [1password](https://github.com/tomoakley/dotfiles/blob/master/qutebrowser/userscripts/1password):
Integration with 1password on macOS.
- [localhost](https://github.com/SidharthArya/.qutebrowser/blob/master/userscripts/localhost):
@@ -83,6 +85,17 @@ The following userscripts can be found on their own repositories.
services (Invidious, Teddit, Nitter, Bibliogram, OpenStreetMap).
- [CIAvash/qutebrowser-userscripts](https://github.com/CIAvash/qutebrowser-userscripts),
various small userscripts written in Raku.
+- [bitwarden-rofi](https://github.com/haztecaso/bitwarden-rofi), rofi wrapper for bitwarden cli
+ interface using gpg instead of keyctl.
+- [yomichad](https://github.com/potamides/yomichad): Japanese pop-up dictionary
+ for looking up readings and definitions of currently selected words, kanji
+ and names
+- [~palb91/qutescripts](https://git.sr.ht/~palb91/qutescripts): A list of
+ personal userscripts for qutebrowser (`domcycle`: settings per domain,
+ `gitclone`, `jsdownload`: smart download, and `substiqute`: bash-like url
+ substitution)
+- [pseudo per domain stylesheets](https://github.com/bschnitz/qute) Userscript
+ for generating url restricted greasemonkey scripts to apply user styles.
[Zotero]: https://www.zotero.org/
[Pocket]: https://getpocket.com/
diff --git a/misc/userscripts/cast b/misc/userscripts/cast
index df74fe97e..ec703d5fb 100755
--- a/misc/userscripts/cast
+++ b/misc/userscripts/cast
@@ -20,6 +20,14 @@
#
# Dependencies
# - castnow, https://github.com/xat/castnow
+# - youtube-dl (https://youtube-dl.org/) or a drop-in replacement such as
+# yt-dlp (https://github.com/yt-dlp/yt-dlp).
+#
+# Configuration:
+# This script looks at the optional QUTE_CAST_YTDL_PROGRAM environment
+# variable (if it exists) to decide which program to use for downloading
+# videos. If specified, this should be youtube-dl or a drop-in replacement
+# for it.
#
# Author
# Simon Désaulniers <sim.desaulniers@gmail.com>
@@ -133,23 +141,34 @@ echo "jseval -q $(printjs)" >> "$QUTE_FIFO"
tmpdir=$(mktemp -d)
file_to_cast=${tmpdir}/qutecast
-program_=$(command -v castnow)
+cast_program=$(command -v castnow)
+
+# pick a ytdl program
+for p in "$QUTE_CAST_YTDL_PROGRAM" yt-dlp youtube-dl; do
+ ytdl_program=$(command -v -- "$p")
+ [ "$ytdl_program" == "" ] || break
+done
-if [[ "${program_}" == "" ]]; then
- msg error "castnow can't be found..."
+if [[ "${cast_program}" == "" ]]; then
+ msg error "castnow can't be found"
+ exit 1
+fi
+if [[ "${ytdl_program}" == "" ]]; then
+ msg error "youtube-dl or a drop-in replacement can't be found in PATH, and no installed program " \
+ "specified in QUTE_CAST_YTDL_PROGRAM (currently \"$QUTE_CAST_YTDL_PROGRAM\")"
exit 1
fi
# kill any running instance of castnow
-pkill -f "${program_}"
+pkill -f -- "${cast_program}"
# start youtube download in stream mode (-o -) into temporary file
-youtube-dl -qo - "$1" > "${file_to_cast}" &
+"${ytdl_program}" -qo - "$1" > "${file_to_cast}" &
ytdl_pid=$!
msg info "Casting $1" >> "$QUTE_FIFO"
# start castnow in stream mode to cast on ChromeCast
-tail -F "${file_to_cast}" | ${program_} -
+tail -F "${file_to_cast}" | ${cast_program} -
# cleanup remaining background process and file on disk
kill ${ytdl_pid}
diff --git a/misc/userscripts/open_download b/misc/userscripts/open_download
index 62730f37c..e118765f7 100755
--- a/misc/userscripts/open_download
+++ b/misc/userscripts/open_download
@@ -103,13 +103,22 @@ fi
file="${entries[$line]}"
file="${file%%$'\t'*}"
path="$DOWNLOAD_DIR/$file"
-filetype=$(xdg-mime query filetype "$path")
-application=$(xdg-mime query default "$filetype")
-if [ -z "$application" ] ; then
- die "Do not know how to open »$file« of type $filetype"
-fi
+if [ -f /.flatpak-info ]; then
+ # with the help of the appchooser portal, flatpak let the user select the
+ # app associated with a mime type after executing xdg-open.
+ # we can't know ahead of time which app will be launched, and the sandbox
+ # doesn't have access to mime types known to the host.
+ msg info "Opening »$file« with XDG Desktop Portal"
+else
+ filetype=$(xdg-mime query filetype "$path")
+ application=$(xdg-mime query default "$filetype")
+
+ if [ -z "$application" ] ; then
+ die "Do not know how to open »$file« of type $filetype"
+ fi
-msg info "Opening »$file« (of type $filetype) with ${application%.desktop}"
+ msg info "Opening »$file« (of type $filetype) with ${application%.desktop}"
+fi
-xdg-open "$path" &
+xdg-open "$path"
diff --git a/misc/userscripts/password_fill b/misc/userscripts/password_fill
index c46253d41..3ea8fd9f6 100755
--- a/misc/userscripts/password_fill
+++ b/misc/userscripts/password_fill
@@ -241,7 +241,7 @@ pass_backend() {
if $GPG "${GPG_OPTS[@]}" -d "$passfile" \
| grep --max-count=1 -iE "${match_line_pattern}${url}" > /dev/null
then
- passfile="${passfile#$PREFIX}"
+ passfile="${passfile#"$PREFIX"}"
passfile="${passfile#/}"
files+=( "${passfile%.gpg}" )
fi
@@ -250,7 +250,7 @@ pass_backend() {
if ((match_filename)) ; then
# add entries with matching filepath
while read -r passfile ; do
- passfile="${passfile#$PREFIX}"
+ passfile="${passfile#"$PREFIX"}"
passfile="${passfile#/}"
files+=( "${passfile%.gpg}" )
done < <(find -L "$PREFIX" -iname '*.gpg' | grep "$url")
@@ -267,7 +267,7 @@ pass_backend() {
else
if [[ $line =~ $user_pattern ]] ; then
# remove the matching prefix "user: " from the beginning of the line
- username=${line#${BASH_REMATCH[0]}}
+ username=${line#"${BASH_REMATCH[0]}"}
break
fi
fi
diff --git a/misc/userscripts/qute-bitwarden b/misc/userscripts/qute-bitwarden
index c6407127a..a30f734f2 100755
--- a/misc/userscripts/qute-bitwarden
+++ b/misc/userscripts/qute-bitwarden
@@ -6,7 +6,7 @@
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published bjy
+# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
@@ -62,6 +62,8 @@ argument_parser = argparse.ArgumentParser(
argument_parser.add_argument('url', nargs='?', default=os.getenv('QUTE_URL'))
argument_parser.add_argument('--dmenu-invocation', '-d', default='rofi -dmenu -i -p Bitwarden',
help='Invocation used to execute a dmenu-provider')
+argument_parser.add_argument('--password-prompt-invocation', '-p', default='rofi -dmenu -p "Master Password" -password -lines 0',
+ help='Invocation used to prompt the user for their Bitwarden password')
argument_parser.add_argument('--no-insert-mode', '-n', dest='insert_mode', action='store_false',
help="Don't automatically enter insert mode")
argument_parser.add_argument('--totp', '-t', action='store_true',
@@ -98,59 +100,54 @@ def qute_command(command):
fifo.flush()
-def ask_password():
- process = subprocess.run([
- 'rofi',
- '-dmenu',
- '-p',
- 'Master Password',
- '-password',
- '-lines',
- '0',
- ], universal_newlines=True, stdout=subprocess.PIPE)
+def ask_password(password_prompt_invocation):
+ process = subprocess.run(
+ shlex.split(password_prompt_invocation),
+ text=True,
+ stdout=subprocess.PIPE,
+ )
if process.returncode > 0:
raise Exception('Could not unlock vault')
master_pass = process.stdout.strip()
return subprocess.check_output(
['bw', 'unlock', '--raw', master_pass],
- universal_newlines=True,
+ text=True,
).strip()
-def get_session_key(auto_lock):
+def get_session_key(auto_lock, password_prompt_invocation):
if auto_lock == 0:
subprocess.call(['keyctl', 'purge', 'user', 'bw_session'])
- return ask_password()
+ return ask_password(password_prompt_invocation)
else:
process = subprocess.run(
['keyctl', 'request', 'user', 'bw_session'],
- universal_newlines=True,
+ text=True,
stdout=subprocess.PIPE,
)
key_id = process.stdout.strip()
if process.returncode > 0:
- session = ask_password()
+ session = ask_password(password_prompt_invocation)
if not session:
raise Exception('Could not unlock vault')
key_id = subprocess.check_output(
['keyctl', 'add', 'user', 'bw_session', session, '@u'],
- universal_newlines=True,
+ text=True,
).strip()
if auto_lock > 0:
subprocess.call(['keyctl', 'timeout', str(key_id), str(auto_lock)])
return subprocess.check_output(
['keyctl', 'pipe', str(key_id)],
- universal_newlines=True,
+ text=True,
).strip()
-def pass_(domain, encoding, auto_lock):
- session_key = get_session_key(auto_lock)
+def pass_(domain, encoding, auto_lock, password_prompt_invocation):
+ session_key = get_session_key(auto_lock, password_prompt_invocation)
process = subprocess.run(
['bw', 'list', 'items', '--session', session_key, '--url', domain],
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
+ capture_output=True,
)
err = process.stderr.decode(encoding).strip()
@@ -166,12 +163,11 @@ def pass_(domain, encoding, auto_lock):
return out
-def get_totp_code(selection_id, domain_name, encoding, auto_lock):
- session_key = get_session_key(auto_lock)
+def get_totp_code(selection_id, domain_name, encoding, auto_lock, password_prompt_invocation):
+ session_key = get_session_key(auto_lock, password_prompt_invocation)
process = subprocess.run(
['bw', 'get', 'totp', '--session', session_key, selection_id],
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
+ capture_output=True,
)
err = process.stderr.decode(encoding).strip()
@@ -198,7 +194,7 @@ def dmenu(items, invocation, encoding):
def fake_key_raw(text):
for character in text:
# Escape all characters by default, space requires special handling
- sequence = '" "' if character == ' ' else '\{}'.format(character)
+ sequence = '" "' if character == ' ' else r'\{}'.format(character)
qute_command('fake-key {}'.format(sequence))
@@ -216,7 +212,7 @@ def main(arguments):
for target in filter(None, [
extract_result.fqdn,
extract_result.registered_domain,
- extract_result.subdomain + extract_result.domain,
+ extract_result.subdomain + '.' + extract_result.domain,
extract_result.domain,
extract_result.ipv4]):
target_candidates = json.loads(
@@ -224,6 +220,7 @@ def main(arguments):
target,
arguments.io_encoding,
arguments.auto_lock,
+ arguments.password_prompt_invocation,
)
)
if not target_candidates:
@@ -270,7 +267,8 @@ def main(arguments):
selection['id'],
selection['name'],
arguments.io_encoding,
- arguments.auto_lock
+ arguments.auto_lock,
+ arguments.password_prompt_invocation,
)
)
else:
@@ -294,7 +292,8 @@ def main(arguments):
selection['id'],
selection['name'],
arguments.io_encoding,
- arguments.auto_lock
+ arguments.auto_lock,
+ arguments.password_prompt_invocation,
)
)
diff --git a/misc/userscripts/qute-keepassxc b/misc/userscripts/qute-keepassxc
index 11d0a3384..61a6c7bce 100755
--- a/misc/userscripts/qute-keepassxc
+++ b/misc/userscripts/qute-keepassxc
@@ -43,6 +43,8 @@ config.bind('<Alt-Shift-u>', 'spawn --userscript qute-keepassxc --key ABC1234',
config.bind('pw', 'spawn --userscript qute-keepassxc --key ABC1234', mode='normal')
```
+To manage multiple accounts you also need [rofi](https://github.com/davatorium/rofi) installed.
+
# Usage
@@ -62,7 +64,27 @@ Unlike most browser extensions which only use plain local storage, this one atte
by storing the key in encrypted form using GPG.
Therefore you need to have a public-key-pair readily set up.
-GPG might then ask for your private-key passwort whenever you query the database for login credentials.
+GPG might then ask for your private-key password whenever you query the database for login credentials.
+
+
+# TOTP
+
+This script recently received experimental TOTP support.
+To use it, you need to have working TOTP authentication within KeepassXC.
+Then call `qute-keepassxc` with the `--totp` flags.
+
+For example, I have the following line in my `config.py`:
+
+```python
+config.bind('pt', 'spawn --userscript qute-keepassxc --key ABC1234 --totp', mode='normal')
+```
+
+For now this script will simply insert the TOTP-token into the currently selected
+input field, since I have not yet found a reliable way to identify the correct field
+within all existing login forms.
+Thus you need to manually select the TOTP input field, press escape to leave input
+mode and then enter `pt` to fill in the token (or configure another key-binding for
+insert mode if you prefer that).
[1]: https://keepassxc.org/
@@ -88,6 +110,8 @@ import nacl.public
def parse_args():
parser = argparse.ArgumentParser(description="Full passwords from KeepassXC")
parser.add_argument('url', nargs='?', default=os.environ.get('QUTE_URL'))
+ parser.add_argument('--totp', action='store_true',
+ help="Fill in current TOTP field instead of username/password")
parser.add_argument('--socket', '-s', default='/run/user/{}/org.keepassxc.KeePassXC.BrowserServer'.format(os.getuid()),
help='Path to KeepassXC browser socket')
parser.add_argument('--key', '-k', default='alice@example.com',
@@ -160,7 +184,7 @@ class KeepassXC:
action = 'test-associate',
id = self.id,
key = base64.b64encode(self.id_key.public_key.encode()).decode('utf-8')
- ))
+ ), triggerUnlock = 'true')
return self.recv_msg()['success'] == 'true'
def associate(self):
@@ -180,6 +204,16 @@ class KeepassXC:
))
return self.recv_msg()['entries']
+ def get_totp(self, uuid):
+ self.send_msg(dict(
+ action = 'get-totp',
+ uuid = uuid
+ ))
+ response = self.recv_msg()
+ if response['success'] != 'true' or not response['totp']:
+ return None
+ return response['totp']
+
def send_raw_msg(self, msg):
self.sock.send( json.dumps(msg).encode('utf-8') )
@@ -206,16 +240,17 @@ class KeepassXC:
class SecretKeyStore:
- def __init__(self, gpgkey):
+ def __init__(self, gpgkey, insecure):
self.gpgkey = gpgkey
- if gpgkey is None:
+ self.insecure = insecure
+ if self.insecure:
self.path = os.path.join(os.environ['QUTE_DATA_DIR'], 'keepassxc.key')
else:
self.path = os.path.join(os.environ['QUTE_DATA_DIR'], 'keepassxc.key.gpg')
def load(self):
"Load existing association key from file"
- if self.gpgkey is None:
+ if self.insecure:
jsondata = open(self.path, 'r').read()
else:
jsondata = subprocess.check_output(['gpg', '--decrypt', self.path]).decode('utf-8')
@@ -232,7 +267,7 @@ class SecretKeyStore:
"Store newly created association key in file"
self.id = id
jsondata = json.dumps({'id':self.id, 'key':base64.b64encode(self.key).decode('utf-8')})
- if self.gpgkey is None:
+ if self.insecure:
open(self.path, "w").write(jsondata)
else:
subprocess.run(['gpg', '--encrypt', '-o', self.path, '-r', self.gpgkey], input=jsondata.encode('utf-8'), check=True)
@@ -250,8 +285,10 @@ def error(msg):
def connect_to_keepassxc(args):
- assert args.key or args.insecure, "Missing GPG key to use for auth key encryption"
- keystore = SecretKeyStore(args.key)
+ if not args.insecure and not args.key:
+ error("Missing GPG key to use for auth key encryption")
+ return
+ keystore = SecretKeyStore(args.key, args.insecure)
if os.path.isfile(keystore.path):
keystore.load()
kp = KeepassXC(keystore.id, key=keystore.key, socket_path=args.socket)
@@ -271,6 +308,30 @@ def connect_to_keepassxc(args):
return kp
+def select_account(creds):
+ try:
+ if len(creds) == 1:
+ return creds[0]
+ idx = subprocess.check_output(
+ ['rofi', '-dmenu', '-format', 'i', '-matching', 'fuzzy',
+ '-p', 'Search',
+ '-mesg', '<b>qute-keepassxc</b>: select an account, please!'],
+ input=b"\n".join(c['login'].encode('utf-8') for c in creds)
+ )
+ idx = int(idx)
+ if idx < 0:
+ return None
+ return creds[idx]
+ except subprocess.CalledProcessError:
+ return None
+ except FileNotFoundError:
+ error("rofi not found. Please install rofi to select from multiple credentials")
+ return creds[0]
+ except Exception as e:
+ error(f"Error while picking account: {e}")
+ return None
+
+
def make_js_code(username, password):
return ' '.join("""
function isVisible(elem) {
@@ -332,6 +393,21 @@ def make_js_code(username, password):
""".splitlines()) % (json.dumps(username), json.dumps(password))
+def make_js_totp_code(totp):
+ return ' '.join("""
+ (function () {
+ var input = document.activeElement;
+ if (!input || input.tagName !== "INPUT") {
+ alert("No TOTP input field selected");
+ return;
+ }
+ input.value = %s;
+ input.dispatchEvent(new Event('input', { 'bubbles': true }));
+ input.dispatchEvent(new Event('change', { 'bubbles': true }));
+ })();
+ """.splitlines()) % (json.dumps(totp),)
+
+
def main():
if 'QUTE_FIFO' not in os.environ:
print(f"No QUTE_FIFO found - {sys.argv[0]} must be run as a qutebrowser userscript")
@@ -348,10 +424,21 @@ def main():
if not creds:
error('No credentials found')
return
- # TODO: handle multiple matches
- name, pw = creds[0]['login'], creds[0]['password']
- if name and pw:
- qute('jseval -q ' + make_js_code(name, pw))
+ cred = select_account(creds)
+ if not cred:
+ error('No credentials selected')
+ return
+ if args.totp:
+ uuid = cred['uuid']
+ totp = kp.get_totp(uuid)
+ if not totp:
+ error('No TOTP key found')
+ return
+ qute('jseval -q ' + make_js_totp_code(totp))
+ else:
+ name, pw = cred['login'], cred['password']
+ if name and pw:
+ qute('jseval -q ' + make_js_code(name, pw))
except Exception as e:
error(str(e))
diff --git a/misc/userscripts/qute-lastpass b/misc/userscripts/qute-lastpass
index d2a72f077..e99a51a0f 100755
--- a/misc/userscripts/qute-lastpass
+++ b/misc/userscripts/qute-lastpass
@@ -6,7 +6,7 @@
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published bjy
+# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
@@ -86,7 +86,7 @@ def qute_command(command):
def pass_(domain, encoding):
domain = re.escape(domain)
args = ['lpass', 'show', '-x', '-j', '-G', '\\b{:s}'.format(domain)]
- process = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ process = subprocess.run(args, capture_output=True)
candidates = json.loads(process.stdout.decode(encoding).strip() or '[]')
err = process.stderr.decode(encoding).strip()
diff --git a/misc/userscripts/qute-pass b/misc/userscripts/qute-pass
index 71646eb69..573644dc4 100755
--- a/misc/userscripts/qute-pass
+++ b/misc/userscripts/qute-pass
@@ -23,11 +23,12 @@ demonstration can be seen here: https://i.imgur.com/KN3XuZP.gif.
"""
USAGE = """The domain of the site has to appear as a segment in the pass path,
-for example: "github.com/cryzed" or "websites/github.com". How the username and
-password are determined is freely configurable using the CLI arguments. As an
-example, if you instead store the username as part of the secret (and use a
-site's name as filename), instead of the default configuration, use
-`--username-target secret` and `--username-pattern "username: (.+)"`.
+for example: "github.com/cryzed" or "websites/github.com". Alternatively the
+parameter `--unfiltered` may be used to get a list of all passwords. How the
+username and password are determined is freely configurable using the CLI
+arguments. As an example, if you instead store the username as part of the
+secret (and use a site's name as filename), instead of the default configuration,
+use `--username-target secret` and `--username-pattern "username: (.+)"`.
The login information is inserted by emulating key events using qutebrowser's
fake-key command in this manner: [USERNAME]<Tab>[PASSWORD], which is compatible
@@ -76,6 +77,8 @@ argument_parser.add_argument('--password-store', '-p',
help='Path to your pass password-store (only used in pass-mode)', type=expanded_path)
argument_parser.add_argument('--mode', '-M', choices=['pass', 'gopass'], default="pass",
help='Select mode [gopass] to use gopass instead of the standard pass.')
+argument_parser.add_argument('--prefix', type=str,
+ help='Search only the given subfolder of the store (only used in gopass-mode)')
argument_parser.add_argument('--username-pattern', '-u', default=r'.*/(.+)',
help='Regular expression that matches the username')
argument_parser.add_argument('--username-target', '-U', choices=['path', 'secret'], default='path',
@@ -92,6 +95,10 @@ argument_parser.add_argument('--merge-candidates', '-m', action='store_true',
help='Merge pass candidates for fully-qualified and registered domain name')
argument_parser.add_argument('--extra-url-suffixes', '-s', default='',
help='Comma-separated string containing extra suffixes (e.g local)')
+argument_parser.add_argument('--unfiltered', dest='unfiltered', action='store_true',
+ help='Show an unfiltered selection of all passwords in the store')
+argument_parser.add_argument('--always-show-selection', dest='always_show_selection', action='store_true',
+ help='Always show selection, even if there is only a single match')
group = argument_parser.add_mutually_exclusive_group()
group.add_argument('--username-only', '-e', action='store_true', help='Only insert username')
group.add_argument('--password-only', '-w', action='store_true', help='Only insert password')
@@ -123,14 +130,17 @@ def qute_command(command):
fifo.flush()
-def find_pass_candidates(domain):
+def find_pass_candidates(domain, unfiltered=False):
candidates = []
if arguments.mode == "gopass":
- all_passwords = subprocess.run(["gopass", "list", "--flat" ], stdout=subprocess.PIPE).stdout.decode("UTF-8").splitlines()
+ gopass_args = ["gopass", "list", "--flat"]
+ if arguments.prefix:
+ gopass_args.append(arguments.prefix)
+ all_passwords = subprocess.run(gopass_args, stdout=subprocess.PIPE).stdout.decode("UTF-8").splitlines()
for password in all_passwords:
- if domain in password:
+ if unfiltered or domain in password:
candidates.append(password)
else:
for path, directories, file_names in os.walk(arguments.password_store, followlinks=True):
@@ -143,7 +153,7 @@ def find_pass_candidates(domain):
split_path = pass_path.split(os.path.sep)
for secret in secrets:
secret_base = os.path.splitext(secret)[0]
- if domain not in (split_path + [secret_base]):
+ if not unfiltered and domain not in (split_path + [secret_base]):
continue
candidates.append(os.path.join(pass_path, secret_base))
@@ -178,7 +188,7 @@ def dmenu(items, invocation):
def fake_key_raw(text):
for character in text:
# Escape all characters by default, space requires special handling
- sequence = '" "' if character == ' ' else '\{}'.format(character)
+ sequence = '" "' if character == ' ' else r'\{}'.format(character)
qute_command('fake-key {}'.format(sequence))
@@ -223,7 +233,7 @@ def main(arguments):
for target in filter(None, [extract_result.fqdn, extract_result.registered_domain, extract_result.ipv4, private_domain]):
attempted_targets.append(target)
- target_candidates = find_pass_candidates(target)
+ target_candidates = find_pass_candidates(target, unfiltered=arguments.unfiltered)
if not target_candidates:
continue
@@ -235,7 +245,11 @@ def main(arguments):
stderr('No pass candidates for URL {!r} found! (I tried {!r})'.format(arguments.url, attempted_targets))
return ExitCodes.NO_PASS_CANDIDATES
- selection = candidates.pop() if len(candidates) == 1 else dmenu(sorted(candidates), arguments.dmenu_invocation)
+ if len(candidates) == 1 and not arguments.always_show_selection:
+ selection = candidates.pop()
+ else:
+ selection = dmenu(sorted(candidates), arguments.dmenu_invocation)
+
# Nothing was selected, simply return
if not selection:
return ExitCodes.SUCCESS
diff --git a/misc/userscripts/qutedmenu b/misc/userscripts/qutedmenu
index bdd0d9b27..7f326916b 100755
--- a/misc/userscripts/qutedmenu
+++ b/misc/userscripts/qutedmenu
@@ -48,6 +48,8 @@ url=${url/*http/http}
[[ -z $url ]] && exit 0
case $1 in
- open) printf '%s' "open $url" >> "$QUTE_FIFO" || qutebrowser "$url" ;;
- tab) printf '%s' "open -t $url" >> "$QUTE_FIFO" || qutebrowser "$url" ;;
+ open) printf '%s' "open $url" >> "$QUTE_FIFO" || qutebrowser "$url" ;;
+ tab) printf '%s' "open -t $url" >> "$QUTE_FIFO" || qutebrowser "$url" ;;
+ window) printf '%s' "open -w $url" >> "$QUTE_FIFO" || qutebrowser "$url --target window" ;;
+ private) printf '%s' "open -p $url" >> "$QUTE_FIFO" || qutebrowser "$url --target private-window" ;;
esac
diff --git a/misc/userscripts/readability b/misc/userscripts/readability
index a6a6f2d52..19b687939 100755
--- a/misc/userscripts/readability
+++ b/misc/userscripts/readability
@@ -10,7 +10,6 @@
# Usage:
# :spawn --userscript readability
#
-from __future__ import absolute_import
import codecs, os
tmpfile = os.path.join(
diff --git a/misc/userscripts/readability-js b/misc/userscripts/readability-js
index 485957ddb..752b759bb 100755
--- a/misc/userscripts/readability-js
+++ b/misc/userscripts/readability-js
@@ -59,9 +59,6 @@ const HEADER = `
width: 100%;
margin: 0 0;
}
- a.reader-title {
- color: #FFFFFF !important;
- }
img {
max-width:100%;
height:auto;
diff --git a/misc/userscripts/ripbang b/misc/userscripts/ripbang
index df9cb2fbc..2f867c838 100755
--- a/misc/userscripts/ripbang
+++ b/misc/userscripts/ripbang
@@ -9,26 +9,22 @@
# :spawn --userscript ripbang amazon maps
#
-from __future__ import print_function
import os, re, requests, sys
-
-try:
- from urllib.parse import unquote
-except ImportError:
- from urllib import unquote
+from urllib.parse import urlparse, parse_qs
for argument in sys.argv[1:]:
bang = '!' + argument
r = requests.get('https://duckduckgo.com/',
- params={'q': bang + ' SEARCHTEXT'})
+ params={'q': bang + ' SEARCHTEXT'},
+ headers={'user-agent': 'qutebrowser ripbang'})
- searchengine = unquote(re.search("url=[^']+", r.text).group(0))
- searchengine = searchengine.replace('url=', '')
- searchengine = searchengine.replace('/l/?kh=-1&uddg=', '')
+ searchengine = re.search("url=([^']+)", r.text).group(1)
+ searchengine = urlparse(searchengine).query
+ searchengine = parse_qs(searchengine)['uddg'][0]
searchengine = searchengine.replace('SEARCHTEXT', '{}')
if os.getenv('QUTE_FIFO'):
with open(os.environ['QUTE_FIFO'], 'w') as fifo:
- fifo.write('set searchengines %s %s' % (bang, searchengine))
+ fifo.write('config-dict-add url.searchengines %s %s' % (bang, searchengine))
else:
print('%s %s' % (bang, searchengine))
diff --git a/pytest.ini b/pytest.ini
index 72ad24002..d32746281 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -86,4 +86,9 @@ filterwarnings =
ignore:_SixMetaPathImporter\.find_spec\(\) not found; falling back to find_module\(\):ImportWarning
# https://github.com/ionelmc/python-hunter/issues/97
ignore:The distutils\.sysconfig module is deprecated, use sysconfig instead:DeprecationWarning
+ # https://github.com/certifi/python-certifi/issues/170
+ ignore:path is deprecated\. Use files\(\) instead\. Refer to https.//importlib-resources\.readthedocs\.io/en/latest/using\.html#migrating-from-legacy for migration advice\.:DeprecationWarning:certifi.core
+ # https://github.com/HypothesisWorks/hypothesis/issues/3309
+ ignore:module 'sre_constants' is deprecated:DeprecationWarning
+ ignore:module 'sre_parse' is deprecated:DeprecationWarning
faulthandler_timeout = 90
diff --git a/qutebrowser/__init__.py b/qutebrowser/__init__.py
index d146eb8bf..75d4e0532 100644
--- a/qutebrowser/__init__.py
+++ b/qutebrowser/__init__.py
@@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>.
-"""A keyboard-driven, vim-like browser based on PyQt5."""
+"""A keyboard-driven, vim-like browser based on Python and Qt."""
import os.path
@@ -26,8 +26,8 @@ __copyright__ = "Copyright 2014-2021 Florian Bruhin (The Compiler)"
__license__ = "GPL"
__maintainer__ = __author__
__email__ = "mail@qutebrowser.org"
-__version__ = "2.3.0"
+__version__ = "2.5.1"
__version_info__ = tuple(int(part) for part in __version__.split('.'))
-__description__ = "A keyboard-driven, vim-like browser based on PyQt5."
+__description__ = "A keyboard-driven, vim-like browser based on Python and Qt."
basedir = os.path.dirname(os.path.realpath(__file__))
diff --git a/qutebrowser/app.py b/qutebrowser/app.py
index 1ab28e8d0..f74617133 100644
--- a/qutebrowser/app.py
+++ b/qutebrowser/app.py
@@ -51,7 +51,6 @@ from PyQt5.QtGui import QDesktopServices, QPixmap, QIcon
from PyQt5.QtCore import pyqtSlot, QUrl, QObject, QEvent, pyqtSignal, Qt
import qutebrowser
-import qutebrowser.resources
from qutebrowser.commands import runners
from qutebrowser.config import (config, websettings, configfiles, configinit,
qtargs)
@@ -182,8 +181,9 @@ def _init_icon():
"""Initialize the icon of qutebrowser."""
fallback_icon = QIcon()
for size in [16, 24, 32, 48, 64, 96, 128, 256, 512]:
- filename = ':/icons/qutebrowser-{size}x{size}.png'.format(size=size)
- pixmap = QPixmap(filename)
+ filename = 'icons/qutebrowser-{size}x{size}.png'.format(size=size)
+ pixmap = QPixmap()
+ pixmap.loadFromData(resources.read_file_binary(filename))
if pixmap.isNull():
log.init.warning("Failed to load {}".format(filename))
else:
@@ -562,8 +562,7 @@ class Application(QApplication):
log.init.debug("Initializing application...")
self.launch_time = datetime.datetime.now()
- self.focusObjectChanged.connect( # type: ignore[attr-defined]
- self.on_focus_object_changed)
+ self.focusObjectChanged.connect(self.on_focus_object_changed)
self.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
self.new_window.connect(self._on_new_window)
diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py
index b1827dbf4..699fe1b0b 100644
--- a/qutebrowser/browser/browsertab.py
+++ b/qutebrowser/browser/browsertab.py
@@ -24,7 +24,7 @@ import itertools
import functools
import dataclasses
from typing import (cast, TYPE_CHECKING, Any, Callable, Iterable, List, Optional,
- Sequence, Set, Type, Union)
+ Sequence, Set, Type, Union, Tuple)
from PyQt5.QtCore import (pyqtSignal, pyqtSlot, QUrl, QObject, QSizeF, Qt,
QEvent, QPoint, QRect)
@@ -35,12 +35,12 @@ from PyQt5.QtNetwork import QNetworkAccessManager
if TYPE_CHECKING:
from PyQt5.QtWebKit import QWebHistory, QWebHistoryItem
- from PyQt5.QtWebKitWidgets import QWebPage
+ from PyQt5.QtWebKitWidgets import QWebPage, QWebView
from PyQt5.QtWebEngineWidgets import (
- QWebEngineHistory, QWebEngineHistoryItem, QWebEnginePage)
+ QWebEngineHistory, QWebEngineHistoryItem, QWebEnginePage, QWebEngineView)
from qutebrowser.keyinput import modeman
-from qutebrowser.config import config
+from qutebrowser.config import config, websettings
from qutebrowser.utils import (utils, objreg, usertypes, log, qtutils,
urlutils, message, jinja)
from qutebrowser.misc import miscwidgets, objects, sessions
@@ -53,6 +53,7 @@ if TYPE_CHECKING:
tab_id_gen = itertools.count(0)
+_WidgetType = Union["QWebView", "QWebEngineView"]
def create(win_id: int,
@@ -156,7 +157,7 @@ class AbstractAction:
action_base: Type[Union['QWebPage.WebAction', 'QWebEnginePage.WebAction']]
def __init__(self, tab: 'AbstractTab') -> None:
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
self._tab = tab
def exit_fullscreen(self) -> None:
@@ -172,6 +173,7 @@ class AbstractAction:
member = getattr(self.action_class, name, None)
if not isinstance(member, self.action_base):
raise WebTabError("{} is not a valid web action!".format(name))
+ assert member is not None # for mypy
self._widget.triggerPageAction(member)
def show_source(self, pygments: bool = False) -> None:
@@ -229,7 +231,7 @@ class AbstractPrinting:
"""Attribute ``printing`` of AbstractTab for printing the page."""
def __init__(self, tab: 'AbstractTab') -> None:
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
self._tab = tab
def check_pdf_support(self) -> None:
@@ -308,7 +310,7 @@ class AbstractSearch(QObject):
def __init__(self, tab: 'AbstractTab', parent: QWidget = None):
super().__init__(parent)
self._tab = tab
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
self.text: Optional[str] = None
self.search_displayed = False
@@ -372,7 +374,7 @@ class AbstractZoom(QObject):
def __init__(self, tab: 'AbstractTab', parent: QWidget = None) -> None:
super().__init__(parent)
self._tab = tab
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
# Whether zoom was changed from the default.
self._default_zoom_changed = False
self._init_neighborlist()
@@ -466,7 +468,7 @@ class AbstractCaret(QObject):
mode_manager: modeman.ModeManager,
parent: QWidget = None) -> None:
super().__init__(parent)
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
self._mode_manager = mode_manager
mode_manager.entered.connect(self._on_mode_entered)
mode_manager.left.connect(self._on_mode_left)
@@ -559,7 +561,7 @@ class AbstractScroller(QObject):
def __init__(self, tab: 'AbstractTab', parent: QWidget = None):
super().__init__(parent)
self._tab = tab
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
if 'log-scroll-pos' in objects.debug_flags:
self.perc_changed.connect(self._log_scroll_pos_change)
@@ -568,16 +570,16 @@ class AbstractScroller(QObject):
log.webview.vdebug( # type: ignore[attr-defined]
"Scroll position changed to {}".format(self.pos_px()))
- def _init_widget(self, widget: QWidget) -> None:
+ def _init_widget(self, widget: _WidgetType) -> None:
self._widget = widget
- def pos_px(self) -> int:
+ def pos_px(self) -> QPoint:
raise NotImplementedError
- def pos_perc(self) -> int:
+ def pos_perc(self) -> Tuple[int, int]:
raise NotImplementedError
- def to_perc(self, x: int = None, y: int = None) -> None:
+ def to_perc(self, x: float = None, y: float = None) -> None:
raise NotImplementedError
def to_point(self, point: QPoint) -> None:
@@ -627,6 +629,8 @@ class AbstractHistoryPrivate:
"""Private API related to the history."""
+ _history: Union["QWebHistory", "QWebEngineHistory"]
+
def serialize(self) -> bytes:
"""Serialize into an opaque format understood by self.deserialize."""
raise NotImplementedError
@@ -711,7 +715,7 @@ class AbstractElements:
_ErrorCallback = Callable[[Exception], None]
def __init__(self, tab: 'AbstractTab') -> None:
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
self._tab = tab
def find_css(self, selector: str,
@@ -772,13 +776,14 @@ class AbstractAudio(QObject):
def __init__(self, tab: 'AbstractTab', parent: QWidget = None) -> None:
super().__init__(parent)
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
self._tab = tab
def set_muted(self, muted: bool, override: bool = False) -> None:
"""Set this tab as muted or not.
Arguments:
+ muted: Whether the tab is currently muted.
override: If set to True, muting/unmuting was done manually and
overrides future automatic mute/unmute changes based on
the URL.
@@ -803,11 +808,11 @@ class AbstractTabPrivate:
def __init__(self, mode_manager: modeman.ModeManager,
tab: 'AbstractTab') -> None:
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
self._tab = tab
self._mode_manager = mode_manager
- def event_target(self) -> QWidget:
+ def event_target(self) -> Optional[QWidget]:
"""Return the widget events should be sent to."""
raise NotImplementedError
@@ -847,7 +852,7 @@ class AbstractTabPrivate:
def shutdown(self) -> None:
raise NotImplementedError
- def run_js_sync(self, code: str) -> None:
+ def run_js_sync(self, code: str) -> Any:
"""Run javascript sync.
Result will be returned when running JS is complete.
@@ -866,7 +871,7 @@ class AbstractTabPrivate:
self._tab.data.inspector = None
self.toggle_inspector(inspector.Position.window)
- def toggle_inspector(self, position: inspector.Position) -> None:
+ def toggle_inspector(self, position: Optional[inspector.Position]) -> None:
"""Show/hide (and if needed, create) the web inspector for this tab."""
tabdata = self._tab.data
if tabdata.inspector is None:
@@ -943,6 +948,19 @@ class AbstractTab(QWidget):
# for a given hostname anyways.
_insecure_hosts: Set[str] = set()
+ # Sub-APIs initialized by subclasses
+ history: AbstractHistory
+ scroller: AbstractScroller
+ caret: AbstractCaret
+ zoom: AbstractZoom
+ search: AbstractSearch
+ printing: AbstractPrinting
+ action: AbstractAction
+ elements: AbstractElements
+ audio: AbstractAudio
+ private_api: AbstractTabPrivate
+ settings: websettings.AbstractSettings
+
def __init__(self, *, win_id: int,
mode_manager: 'modeman.ModeManager',
private: bool,
@@ -961,7 +979,7 @@ class AbstractTab(QWidget):
self.data = TabData()
self._layout = miscwidgets.WrapperLayout(self)
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
self._progress = 0
self._load_status = usertypes.LoadStatus.none
self._tab_event_filter = eventfilter.TabEventFilter(
@@ -975,7 +993,7 @@ class AbstractTab(QWidget):
self.before_load_started.connect(self._on_before_load_started)
- def _set_widget(self, widget: QWidget) -> None:
+ def _set_widget(self, widget: Union["QWebView", "QWebEngineView"]) -> None:
# pylint: disable=protected-access
self._widget = widget
self.data.splitter = miscwidgets.InspectorSplitter(
@@ -1194,7 +1212,7 @@ class AbstractTab(QWidget):
def title(self) -> str:
raise NotImplementedError
- def icon(self) -> None:
+ def icon(self) -> QIcon:
raise NotImplementedError
def set_html(self, html: str, base_url: QUrl = QUrl()) -> None:
diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py
index 8cd73ae4f..4f782c3ee 100644
--- a/qutebrowser/browser/commands.py
+++ b/qutebrowser/browser/commands.py
@@ -383,17 +383,18 @@ class CommandDispatcher:
yield parsed
@cmdutils.register(instance='command-dispatcher', scope='window')
- def tab_clone(self, bg=False, window=False):
+ def tab_clone(self, bg=False, window=False, private=False):
"""Duplicate the current tab.
Args:
bg: Open in a background tab.
window: Open in a new window.
+ private: Open in a new private window.
Return:
The new QWebView.
"""
- cmdutils.check_exclusive((bg, window), 'bw')
+ cmdutils.check_exclusive((bg, window, private), 'bwp')
curtab = self._current_widget()
cur_title = self._tabbed_browser.widget.page_title(
self._current_index())
@@ -404,9 +405,9 @@ class CommandDispatcher:
# The new tab could be in a new tabbed_browser (e.g. because of
# tabs.tabs_are_windows being set)
- if window:
+ if window or private:
new_tabbed_browser = self._new_tabbed_browser(
- private=self._tabbed_browser.is_private)
+ private=self._tabbed_browser.is_private or private)
else:
new_tabbed_browser = self._tabbed_browser
newtab = new_tabbed_browser.tabopen(background=bg)
@@ -450,7 +451,7 @@ class CommandDispatcher:
self._open(tab.url(), tab=True)
if not keep:
- tabbed_browser.close_tab(tab, add_undo=False)
+ tabbed_browser.close_tab(tab, add_undo=False, transfer=True)
@cmdutils.register(instance='command-dispatcher', scope='window')
@cmdutils.argument('win_id', completion=miscmodels.window)
@@ -499,7 +500,8 @@ class CommandDispatcher:
tabbed_browser.tabopen(self._current_url())
if not keep:
self._tabbed_browser.close_tab(self._current_widget(),
- add_undo=False)
+ add_undo=False,
+ transfer=True)
def _back_forward(self, tab, bg, window, count, forward, index=None):
"""Helper function for :back/:forward."""
@@ -911,6 +913,7 @@ class CommandDispatcher:
# Not sure how you enter a command without an active window...
raise cmdutils.CommandError(
"No window specified and couldn't find active window!")
+ assert isinstance(active_win, mainwindow.MainWindow), active_win
win_id = active_win.win_id
if win_id not in objreg.window_registry:
@@ -1003,11 +1006,10 @@ class CommandDispatcher:
raise cmdutils.CommandError("There's no tab with index {}!".format(
index))
- @cmdutils.register(instance='command-dispatcher', scope='window')
- @cmdutils.argument('index', choices=['+', '-'])
- @cmdutils.argument('count', value=cmdutils.Value.count)
- def tab_move(self, index: Union[str, int] = None,
- count: int = None) -> None:
+ @cmdutils.register(instance="command-dispatcher", scope="window")
+ @cmdutils.argument("index", choices=["+", "-", "start", "end"])
+ @cmdutils.argument("count", value=cmdutils.Value.count)
+ def tab_move(self, index: Union[str, int] = None, count: int = None) -> None:
"""Move the current tab according to the argument and [count].
If neither is given, move it to the first position.
@@ -1016,24 +1018,30 @@ class CommandDispatcher:
index: `+` or `-` to move relative to the current tab by
count, or a default of 1 space.
A tab index to move to that index.
+ `start` and `end` to move to the start and the end.
count: If moving relatively: Offset.
If moving absolutely: New position (default: 0). This
overrides the index argument, if given.
"""
- if index in ['+', '-']:
+ if index in ["+", "-"]:
# relative moving
new_idx = self._current_index()
delta = 1 if count is None else count
- if index == '-':
+ if index == "-":
new_idx -= delta
- elif index == '+': # pragma: no branch
+ elif index == "+": # pragma: no branch
new_idx += delta
if config.val.tabs.wrap:
new_idx %= self._count()
else:
+ # pylint: disable=else-if-used
# absolute moving
- if count is not None:
+ if index == "start":
+ new_idx = 0
+ elif index == "end":
+ new_idx = self._count() - 1
+ elif count is not None:
new_idx = count - 1
elif index is not None:
assert isinstance(index, int)
@@ -1145,6 +1153,7 @@ class CommandDispatcher:
idx = self._current_index()
if idx != -1:
+ env['QUTE_TAB_INDEX'] = str(idx + 1)
env['QUTE_TITLE'] = self._tabbed_browser.widget.page_title(idx)
# FIXME:qtwebengine: If tab is None, run_async will fail!
@@ -1514,6 +1523,7 @@ class CommandDispatcher:
Callback for GUIProcess when the edited text was updated.
Args:
+ ed: The editor.ExternalEditor instance
elem: The WebElementWrapper which was modified.
text: The new text to insert.
"""
diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py
index fa4ac9da2..32bfd2693 100644
--- a/qutebrowser/browser/downloads.py
+++ b/qutebrowser/browser/downloads.py
@@ -611,7 +611,11 @@ class AbstractDownloadItem(QObject):
message.error(str(e))
def url(self) -> QUrl:
- """Get the download's origin URL."""
+ """Get the download's URL (i.e. where the file is downloaded from)."""
+ raise NotImplementedError
+
+ def origin(self) -> QUrl:
+ """Get the download's origin URL (i.e. the page starting the download)."""
raise NotImplementedError
def _get_open_filename(self):
@@ -819,6 +823,34 @@ class AbstractDownloadItem(QObject):
self.pdfjs_requested.emit(os.path.basename(filename),
self.url())
+ def cancel_for_origin(self) -> bool:
+ """Cancel the download based on URL/origin.
+
+ For some special cases, we want to cancel downloads immediately, before
+ downloading:
+
+ - file:// downloads from file:// URLs (open the file instead)
+ - http:// downloads from https:// URLs (mixed content)
+ """
+ origin = self.origin()
+ url = self.url()
+ if not origin.isValid():
+ return False
+
+ if url.scheme() == "file" and origin.scheme() == "file":
+ utils.open_file(url.toLocalFile())
+ self.cancel()
+ return True
+
+ if (url.scheme() == "http" and
+ origin.isValid() and origin.scheme() == "https" and
+ config.instance.get("downloads.prevent_mixed_content", url=origin)):
+ self._die("Aborting insecure download from secure page "
+ "(see downloads.prevent_mixed_content).")
+ return True
+
+ return False
+
def set_target(self, target):
"""Set the target for a given download.
@@ -1320,6 +1352,7 @@ class TempDownloadManager:
The tempfile.TemporaryDirectory that is used.
"""
if self._tmpdir is None:
+ # pylint: disable=consider-using-with
self._tmpdir = tempfile.TemporaryDirectory(
prefix='qutebrowser-downloads-')
return self._tmpdir
@@ -1341,6 +1374,7 @@ class TempDownloadManager:
suggested_name = utils.sanitize_filename(suggested_name)
# Make sure that the filename is not too long
suggested_name = utils.elide_filename(suggested_name, 50)
+ # pylint: disable=consider-using-with
fobj = tempfile.NamedTemporaryFile(dir=tmpdir.name, delete=False,
suffix='_' + suggested_name)
self.files.append(fobj)
diff --git a/qutebrowser/browser/downloadview.py b/qutebrowser/browser/downloadview.py
index 69c58741a..8187a1002 100644
--- a/qutebrowser/browser/downloadview.py
+++ b/qutebrowser/browser/downloadview.py
@@ -86,6 +86,15 @@ class DownloadView(QListView):
count = model.rowCount()
return utils.get_repr(self, count=count)
+ def _model(self) -> downloads.DownloadModel:
+ """Get the current download model.
+
+ Ensures the model is not None.
+ """
+ model = self.model()
+ assert isinstance(model, downloads.DownloadModel), model
+ return model
+
@pyqtSlot()
def _update_geometry(self):
"""Wrapper to call updateGeometry.
@@ -112,7 +121,7 @@ class DownloadView(QListView):
"""
if not index.isValid():
return
- item = self.model().data(index, downloads.ModelRole.item)
+ item = self._model().data(index, downloads.ModelRole.item)
if item.done and item.successful:
item.open_file()
item.remove()
@@ -126,7 +135,7 @@ class DownloadView(QListView):
Args:
item: The DownloadItem to get the actions for, or None.
"""
- model = self.model()
+ model = self._model()
actions: _ActionListType = []
if item is None:
pass
@@ -154,7 +163,7 @@ class DownloadView(QListView):
"""Show the context menu."""
index = self.indexAt(point)
if index.isValid():
- item = self.model().data(index, downloads.ModelRole.item)
+ item = self._model().data(index, downloads.ModelRole.item)
else:
item = None
self._menu = QMenu(self)
@@ -176,7 +185,7 @@ class DownloadView(QListView):
def sizeHint(self):
"""Return sizeHint based on the view contents."""
- idx = self.model().last_index()
+ idx = self._model().last_index()
bottom = self.visualRect(idx).bottom()
if bottom != -1:
margins = self.contentsMargins()
diff --git a/qutebrowser/browser/greasemonkey.py b/qutebrowser/browser/greasemonkey.py
index d0245937f..3b9c321e3 100644
--- a/qutebrowser/browser/greasemonkey.py
+++ b/qutebrowser/browser/greasemonkey.py
@@ -324,6 +324,7 @@ class GreasemonkeyManager(QObject):
"""Add a GreasemonkeyScript to this manager.
Args:
+ script: The GreasemonkeyScript to add.
force: Fetch and overwrite any dependencies which are
already locally cached.
"""
@@ -379,7 +380,7 @@ class GreasemonkeyManager(QObject):
"""
# See if we are still waiting on any required scripts for this one
for dl in self._in_progress_dls:
- if dl.requested_url in script.requires:
+ if dl.requested_url in script.requires: # type: ignore[attr-defined]
return False
# Need to add the required scripts to the IIFE now
@@ -413,6 +414,7 @@ class GreasemonkeyManager(QObject):
force_overwrite=True)
download = download_manager.get(QUrl(url), target=target,
auto_remove=True)
+ # FIXME:mypy Build this into downloads instead of patching here?
download.requested_url = url
self._in_progress_dls.append(download)
if download.successful:
diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py
index e127cd10a..ed933e10c 100644
--- a/qutebrowser/browser/hints.py
+++ b/qutebrowser/browser/hints.py
@@ -254,8 +254,8 @@ class HintActions:
flags = QUrl.FullyEncoded | QUrl.RemovePassword
if url.scheme() == 'mailto':
- flags |= QUrl.RemoveScheme
- urlstr = url.toString(flags) # type: ignore[arg-type]
+ flags |= QUrl.RemoveScheme # type: ignore[operator]
+ urlstr = url.toString(flags)
new_content = urlstr
@@ -299,7 +299,7 @@ class HintActions:
Args:
elem: The QWebElement to download.
- _context: The HintContext to use.
+ context: The HintContext to use.
"""
url = elem.resolve_url(context.baseurl)
if url is None:
@@ -356,8 +356,7 @@ class HintActions:
url: The URL to open as a QUrl.
context: The HintContext to use.
"""
- urlstr = url.toString(
- QUrl.FullyEncoded | QUrl.RemovePassword) # type: ignore[arg-type]
+ urlstr = url.toString(QUrl.FullyEncoded | QUrl.RemovePassword)
args = context.get_args(urlstr)
commandrunner = runners.CommandRunner(self._win_id)
commandrunner.run_safely('spawn ' + ' '.join(args))
@@ -596,6 +595,7 @@ class HintManager(QObject):
"'args' is required with target userscript/spawn/run/"
"fill.")
else:
+ # pylint: disable=else-if-used
if args:
raise cmdutils.CommandError(
"'args' is only allowed with target userscript/spawn.")
@@ -656,6 +656,7 @@ class HintManager(QObject):
self._context.labels[string] = label
keyparser = self._get_keyparser(usertypes.KeyMode.hint)
+ assert isinstance(keyparser, modeparsers.HintKeyParser), keyparser
keyparser.update_bindings(strings)
modeman.enter(self._win_id, usertypes.KeyMode.hint,
@@ -684,9 +685,8 @@ class HintManager(QObject):
Args:
rapid: Whether to do rapid hinting. With rapid hinting, the hint
mode isn't left after a hint is followed, so you can easily
- open multiple links. This is only possible with targets
- `tab` (with `tabs.background=true`), `tab-bg`,
- `window`, `run`, `hover`, `userscript` and `spawn`.
+ open multiple links. Note this won't work with targets
+ `tab-fg`, `fill`, `delete` and `right-click`.
add_history: Whether to add the spawned or yanked link to the
browsing history.
first: Click the first hinted element without prompting.
@@ -754,18 +754,16 @@ class HintManager(QObject):
if mode_manager.mode == usertypes.KeyMode.hint:
modeman.leave(self._win_id, usertypes.KeyMode.hint, 're-hinting')
- if rapid:
- if target in [Target.tab_bg, Target.window, Target.run,
- Target.hover, Target.userscript, Target.spawn,
- Target.download, Target.normal, Target.current,
- Target.yank, Target.yank_primary]:
- pass
- elif target == Target.tab and config.val.tabs.background:
- pass
- else:
- name = target.name.replace('_', '-')
- raise cmdutils.CommandError("Rapid hinting makes no sense "
- "with target {}!".format(name))
+ no_rapid_targets = [
+ Target.tab_fg, # opens new tab
+ Target.fill, # exits hint mode
+ Target.right_click, # opens multiple context menus
+ Target.delete, # deleting elements shifts them
+ ]
+ if rapid and target in no_rapid_targets:
+ name = target.name.replace('_', '-')
+ raise cmdutils.CommandError(
+ f"Rapid hinting makes no sense with target {name}!")
self._check_args(target, *args)
@@ -854,6 +852,7 @@ class HintManager(QObject):
# apply auto_follow_timeout
timeout = config.val.hints.auto_follow_timeout
normal_parser = self._get_keyparser(usertypes.KeyMode.normal)
+ assert isinstance(normal_parser, modeparsers.NormalKeyParser), normal_parser
normal_parser.set_inhibited_timeout(timeout)
# unpacking gets us the first (and only) key in the dict.
self._fire(*visible)
@@ -873,12 +872,11 @@ class HintManager(QObject):
label.update_text(matched, rest)
# Show label again if it was hidden before
label.show()
- else:
+ elif (not self._context.rapid or
+ config.val.hints.hide_unmatched_rapid_hints):
# element doesn't match anymore -> hide it, unless in rapid
# mode and hide_unmatched_rapid_hints is false (see #1799)
- if (not self._context.rapid or
- config.val.hints.hide_unmatched_rapid_hints):
- label.hide()
+ label.hide()
except webelem.Error:
pass
self._handle_auto_follow(keystr=keystr)
@@ -930,6 +928,7 @@ class HintManager(QObject):
self._context.labels[string] = label
keyparser = self._get_keyparser(usertypes.KeyMode.hint)
+ assert isinstance(keyparser, modeparsers.HintKeyParser), keyparser
keyparser.update_bindings(strings, preserve_filter=True)
# Note: filter_hints can be called with non-None filterstr only
@@ -1157,7 +1156,6 @@ class WordHinter:
from the words arg as fallback.
Args:
- words: Words to use as fallback when no link text can be used.
elems: The elements to get hint strings for.
Return:
diff --git a/qutebrowser/browser/history.py b/qutebrowser/browser/history.py
index 559992327..d2046345f 100644
--- a/qutebrowser/browser/history.py
+++ b/qutebrowser/browser/history.py
@@ -405,6 +405,7 @@ class WebHistory(sql.SqlTable):
Args:
url: A url (as QUrl) to add to the history.
+ title: The tab title to add.
redirect: Whether the entry was redirected to another URL
(hidden in completion)
atime: Override the atime used to add the entry
diff --git a/qutebrowser/browser/inspector.py b/qutebrowser/browser/inspector.py
index 2b40e97e4..0eafa0536 100644
--- a/qutebrowser/browser/inspector.py
+++ b/qutebrowser/browser/inspector.py
@@ -22,19 +22,23 @@
import base64
import binascii
import enum
-from typing import cast, Optional
+from typing import cast, Optional, Any
from PyQt5.QtWidgets import QWidget
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QEvent
from PyQt5.QtGui import QCloseEvent
from qutebrowser.browser import eventfilter
-from qutebrowser.config import configfiles
+from qutebrowser.config import configfiles, config
from qutebrowser.utils import log, usertypes
from qutebrowser.keyinput import modeman
from qutebrowser.misc import miscwidgets
+# FIXME:mypy How to annotate this properly without running into Liskov issues?
+_WidgetType = Any
+
+
class Position(enum.Enum):
"""Where the inspector is shown."""
@@ -93,7 +97,7 @@ class AbstractWebInspector(QWidget):
win_id: int,
parent: QWidget = None) -> None:
super().__init__(parent)
- self._widget = cast(QWidget, None)
+ self._widget = cast(_WidgetType, None)
self._layout = miscwidgets.WrapperLayout(self)
self._splitter = splitter
self._position: Optional[Position] = None
@@ -105,7 +109,7 @@ class AbstractWebInspector(QWidget):
eventfilter=self._event_filter,
parent=self)
- def _set_widget(self, widget: QWidget) -> None:
+ def _set_widget(self, widget: _WidgetType) -> None:
self._widget = widget
self._widget.setWindowTitle("Web Inspector")
self._widget.installEventFilter(self._child_event_filter)
@@ -133,7 +137,8 @@ class AbstractWebInspector(QWidget):
@pyqtSlot()
def _on_clicked(self) -> None:
"""Enter insert mode if a docked inspector was clicked."""
- if self._position != Position.window:
+ if (self._position != Position.window and
+ config.val.input.insert_mode.auto_enter):
modeman.enter(self._win_id, usertypes.KeyMode.insert,
reason='Inspector clicked', only_if_normal=True)
@@ -198,7 +203,8 @@ class AbstractWebInspector(QWidget):
geom = base64.b64encode(data).decode('ASCII')
configfiles.state['inspector']['window'] = geom
- def inspect(self, page: QWidget) -> None:
+ # FIXME:mypy How to annotate 'page' properly without running into Liskov issues?
+ def inspect(self, page: Any) -> None:
"""Inspect the given QWeb(Engine)Page."""
raise NotImplementedError
diff --git a/qutebrowser/browser/navigate.py b/qutebrowser/browser/navigate.py
index 82bf57136..6217c8d00 100644
--- a/qutebrowser/browser/navigate.py
+++ b/qutebrowser/browser/navigate.py
@@ -92,9 +92,6 @@ def incdec(url, count, inc_or_dec):
url: The current url.
count: How much to increment or decrement by.
inc_or_dec: Either 'increment' or 'decrement'.
- tab: Whether to open the link in a new tab.
- background: Open the link in a new background tab.
- window: Open the link in a new window.
"""
urlutils.ensure_valid(url)
segments: Optional[Set[str]] = (
diff --git a/qutebrowser/browser/network/pac.py b/qutebrowser/browser/network/pac.py
index 3a544c78f..99078ab20 100644
--- a/qutebrowser/browser/network/pac.py
+++ b/qutebrowser/browser/network/pac.py
@@ -270,8 +270,7 @@ class PACFetcher(QObject):
"""Fetch the proxy from the remote URL."""
assert self._manager is not None
self._reply = self._manager.get(QNetworkRequest(self._pac_url))
- self._reply.finished.connect( # type: ignore[attr-defined]
- self._finish)
+ self._reply.finished.connect(self._finish)
@pyqtSlot()
def _finish(self):
diff --git a/qutebrowser/browser/qtnetworkdownloads.py b/qutebrowser/browser/qtnetworkdownloads.py
index e178fb0e5..a720402f5 100644
--- a/qutebrowser/browser/qtnetworkdownloads.py
+++ b/qutebrowser/browser/qtnetworkdownloads.py
@@ -110,6 +110,7 @@ class DownloadItem(downloads.AbstractDownloadItem):
"""Create a file object using the internal filename."""
assert self._filename is not None
try:
+ # pylint: disable=consider-using-with
fileobj = open(self._filename, 'wb')
except OSError as e:
self._die(e.strerror)
@@ -202,6 +203,17 @@ class DownloadItem(downloads.AbstractDownloadItem):
# Note: self._reply is deleted when the download finishes
return self._url
+ def origin(self) -> QUrl:
+ if self._reply is None:
+ return QUrl()
+ origin = self._reply.request().originatingObject()
+ try:
+ return origin.url()
+ except AttributeError:
+ # Raised either if origin is None or some object that doesn't
+ # have its own url.
+ return QUrl()
+
def _ensure_can_set_filename(self, filename):
if self.fileobj is not None: # pragma: no cover
raise ValueError("fileobj was already set! filename: {}, "
@@ -491,6 +503,7 @@ class DownloadManager(downloads.AbstractDownloadManager):
Args:
request: The QNetworkRequest to download.
target: Where to save the download as downloads.DownloadTarget.
+ suggested_fn: The filename to use for the file.
**kwargs: Passed to _fetch_request.
Return:
@@ -535,6 +548,9 @@ class DownloadManager(downloads.AbstractDownloadManager):
target: Where to save the download as downloads.DownloadTarget.
auto_remove: Whether to remove the download even if
downloads.remove_finished is set to -1.
+ suggested_filename: The filename to use for the file.
+ prompt_download_directory: Whether to prompt for a location to
+ download the file to.
Return:
The created DownloadItem.
@@ -549,6 +565,9 @@ class DownloadManager(downloads.AbstractDownloadManager):
download = DownloadItem(reply, manager=self)
self._init_item(download, auto_remove, suggested_filename)
+ if download.cancel_for_origin():
+ return download
+
if target is not None:
download.set_target(target)
return download
@@ -582,6 +601,7 @@ class DownloadManager(downloads.AbstractDownloadManager):
"""
assert nam.adopted_downloads == 0
for download in self.downloads:
+ assert isinstance(download, DownloadItem), download
if download._uses_nam(nam): # pylint: disable=protected-access
nam.adopt_download(download)
return nam.adopted_downloads
diff --git a/qutebrowser/browser/qutescheme.py b/qutebrowser/browser/qutescheme.py
index 68e36d249..c0da8ac94 100644
--- a/qutebrowser/browser/qutescheme.py
+++ b/qutebrowser/browser/qutescheme.py
@@ -128,9 +128,7 @@ def data_for_url(url: QUrl) -> Tuple[str, bytes]:
Return:
A (mimetype, data) tuple.
"""
- norm_url = url.adjusted(
- QUrl.NormalizePathSegments | # type: ignore[arg-type]
- QUrl.StripTrailingSlash)
+ norm_url = url.adjusted(QUrl.NormalizePathSegments | QUrl.StripTrailingSlash)
if norm_url != url:
raise Redirect(norm_url)
diff --git a/qutebrowser/browser/shared.py b/qutebrowser/browser/shared.py
index 8d3ebe730..3ea323d96 100644
--- a/qutebrowser/browser/shared.py
+++ b/qutebrowser/browser/shared.py
@@ -51,7 +51,9 @@ def custom_headers(url):
conf_headers = config.instance.get('content.headers.custom', url=url)
for header, value in conf_headers.items():
- headers[header.encode('ascii')] = value.encode('ascii')
+ encoded_header = header.encode('ascii')
+ encoded_value = b"" if value is None else value.encode('ascii')
+ headers[encoded_header] = encoded_value
accept_language = config.instance.get('content.headers.accept_language',
url=url)
@@ -339,7 +341,8 @@ def get_user_stylesheet(searching=False):
'misc-mathml-darkmode' not in config.val.content.site_specific_quirks.skip):
# WORKAROUND for MathML-output on Wikipedia being black on black.
# See https://bugs.chromium.org/p/chromium/issues/detail?id=1126606
- css += '\nimg.mwe-math-fallback-image-inline { filter: invert(100%); }'
+ css += ('\nimg.mwe-math-fallback-image-inline, '
+ 'img.mwe-math-fallback-image-display { filter: invert(100%); }')
return css
@@ -418,12 +421,11 @@ def choose_file(qb_mode: FileSelectionMode) -> List[str]:
}[qb_mode]
use_tmp_file = any('{}' in arg for arg in command[1:])
if use_tmp_file:
- handle = tempfile.NamedTemporaryFile(
+ with tempfile.NamedTemporaryFile(
prefix='qutebrowser-fileselect-',
delete=False,
- )
- handle.close()
- tmpfilename = handle.name
+ ) as handle:
+ tmpfilename = handle.name
with utils.cleanup_file(tmpfilename):
command = (
command[:1] +
@@ -509,6 +511,7 @@ def _validated_selected_files(
)
continue
else:
+ # pylint: disable=else-if-used
if not os.path.isfile(selected_file):
message.warning(
f"Expected file but got folder, ignoring '{selected_file}'"
diff --git a/qutebrowser/browser/signalfilter.py b/qutebrowser/browser/signalfilter.py
index 0b002e345..88ac4a65d 100644
--- a/qutebrowser/browser/signalfilter.py
+++ b/qutebrowser/browser/signalfilter.py
@@ -88,6 +88,7 @@ class SignalFilter(QObject):
debug.dbg_signal(signal, args), tabidx))
signal.emit(*args)
else:
+ # pylint: disable=else-if-used
if log_signal:
log.signals.debug("ignoring: {} (tab {})".format(
debug.dbg_signal(signal, args), tabidx))
diff --git a/qutebrowser/browser/urlmarks.py b/qutebrowser/browser/urlmarks.py
index 944ec23d4..4e173f293 100644
--- a/qutebrowser/browser/urlmarks.py
+++ b/qutebrowser/browser/urlmarks.py
@@ -74,6 +74,8 @@ class UrlMarkManager(QObject):
changed = pyqtSignal()
+ _lineparser: lineparser.LineParser
+
def __init__(self, parent=None):
"""Initialize and read quickmarks."""
super().__init__(parent)
diff --git a/qutebrowser/browser/webelem.py b/qutebrowser/browser/webelem.py
index 9ec29ce07..344292166 100644
--- a/qutebrowser/browser/webelem.py
+++ b/qutebrowser/browser/webelem.py
@@ -19,7 +19,7 @@
"""Generic web element related code."""
-from typing import cast, TYPE_CHECKING, Iterator, Optional, Set, Union
+from typing import cast, TYPE_CHECKING, Iterator, Optional, Set, Union, Dict
import collections.abc
from PyQt5.QtCore import QUrl, Qt, QEvent, QTimer, QRect, QPoint
@@ -132,6 +132,7 @@ class AbstractWebElement(collections.abc.MutableMapping): # type: ignore[type-a
"""Dispatch an event to the element.
Args:
+ event: The name of the event.
bubbles: Whether this event should bubble.
cancelable: Whether this event can be cancelled.
composed: Whether the event will trigger listeners outside of a
@@ -160,9 +161,6 @@ class AbstractWebElement(collections.abc.MutableMapping): # type: ignore[type-a
def is_content_editable(self) -> bool:
"""Check if an element has a contenteditable attribute.
- Args:
- elem: The QWebElement to check.
-
Return:
True if the element has a contenteditable attribute,
False otherwise.
@@ -233,6 +231,7 @@ class AbstractWebElement(collections.abc.MutableMapping): # type: ignore[type-a
'span': ['cm-'], # Jupyter Notebook
}
relevant_classes = classes[self.tag_name()]
+ # pylint: disable=consider-using-any-or-all
for klass in self.classes():
if any(klass.strip().startswith(e) for e in relevant_classes):
return True
@@ -346,18 +345,19 @@ class AbstractWebElement(collections.abc.MutableMapping): # type: ignore[type-a
log.webelem.debug("Sending fake click to {!r} at position {} with "
"target {}".format(self, pos, click_target))
- target_modifiers = {
- usertypes.ClickTarget.normal: Qt.NoModifier,
+ target_modifiers: Dict[usertypes.ClickTarget, Qt.KeyboardModifiers] = {
+ usertypes.ClickTarget.normal: cast(Qt.KeyboardModifiers, Qt.NoModifier),
usertypes.ClickTarget.window: Qt.AltModifier | Qt.ShiftModifier,
- usertypes.ClickTarget.tab: Qt.ControlModifier,
- usertypes.ClickTarget.tab_bg: Qt.ControlModifier,
+ usertypes.ClickTarget.tab: cast(Qt.KeyboardModifiers, Qt.ControlModifier),
+ usertypes.ClickTarget.tab_bg:
+ cast(Qt.KeyboardModifiers, Qt.ControlModifier),
}
if config.val.tabs.background:
target_modifiers[usertypes.ClickTarget.tab] |= Qt.ShiftModifier
else:
target_modifiers[usertypes.ClickTarget.tab_bg] |= Qt.ShiftModifier
- modifiers = cast(Qt.KeyboardModifiers, target_modifiers[click_target])
+ modifiers = target_modifiers[click_target]
events = [
QMouseEvent(QEvent.MouseMove, pos, Qt.NoButton, Qt.NoButton, Qt.NoModifier),
diff --git a/qutebrowser/browser/webengine/notification.py b/qutebrowser/browser/webengine/notification.py
index 6b26157e6..3571bb24d 100644
--- a/qutebrowser/browser/webengine/notification.py
+++ b/qutebrowser/browser/webengine/notification.py
@@ -65,7 +65,10 @@ if TYPE_CHECKING:
from qutebrowser.config import config
from qutebrowser.misc import objects
-from qutebrowser.utils import qtutils, log, utils, debug, message, version
+from qutebrowser.utils import (
+ qtutils, log, utils, debug, message, version, objreg, resources,
+)
+from qutebrowser.qt import sip
bridge: Optional['NotificationBridgePresenter'] = None
@@ -275,9 +278,6 @@ class NotificationBridgePresenter(QObject):
log.misc.debug("Adapter vanished, bailing out") # type: ignore[unreachable]
return
- if notification_id <= 0:
- raise Error(f"Got invalid notification id {notification_id}")
-
if replaces_id is None:
if notification_id in self._active_notifications:
raise Error(f"Got duplicate id {notification_id}")
@@ -285,7 +285,7 @@ class NotificationBridgePresenter(QObject):
qt_notification.show()
self._active_notifications[notification_id] = qt_notification
- qt_notification.closed.connect( # type: ignore[attr-defined]
+ qt_notification.closed.connect(
functools.partial(self._adapter.on_web_closed, notification_id))
def _find_replaces_id(
@@ -367,6 +367,17 @@ class NotificationBridgePresenter(QObject):
# https://www.riverbankcomputing.com/pipermail/pyqt/2020-May/042918.html
log.misc.debug(f"Ignoring click request for notification {notification_id} "
"due to PyQt bug")
+ return
+ self._focus_first_matching_tab(notification)
+
+ def _focus_first_matching_tab(self, notification: "QWebEngineNotification") -> None:
+ for win_id in objreg.window_registry:
+ tabbedbrowser = objreg.get("tabbed-browser", window=win_id, scope="window")
+ for idx, tab in enumerate(tabbedbrowser.widgets()):
+ if tab.url().matches(notification.origin(), QUrl.RemovePath):
+ tabbedbrowser.widget.setCurrentIndex(idx)
+ return
+ log.misc.debug(f"No matching tab found for {notification.origin()}")
def _drop_adapter(self) -> None:
"""Drop the currently active adapter (if any).
@@ -477,7 +488,9 @@ class SystrayNotificationAdapter(AbstractNotificationAdapter):
@pyqtSlot(int)
def on_web_closed(self, notification_id: int) -> None:
assert notification_id == self.NOTIFICATION_ID, notification_id
- self._systray.hide()
+ if not sip.isdeleted(self._systray):
+ # This can get called during shutdown
+ self._systray.hide()
class MessagesNotificationAdapter(AbstractNotificationAdapter):
@@ -608,17 +621,19 @@ class HerbeNotificationAdapter(AbstractNotificationAdapter):
so there's no point.
"""
if status == QProcess.CrashExit:
- return
-
- if code == 0:
+ pass
+ elif code == 0:
self.click_id.emit(pid)
elif code == 2:
- self.close_id.emit(pid)
+ pass
else:
proc = self.sender()
+ assert isinstance(proc, QProcess), proc
stderr = proc.readAllStandardError()
raise Error(f'herbe exited with status {code}: {stderr}')
+ self.close_id.emit(pid)
+
@pyqtSlot(QProcess.ProcessError)
def _on_error(self, error: QProcess.ProcessError) -> None:
if error == QProcess.Crashed:
@@ -651,6 +666,7 @@ class _ServerQuirks:
skip_capabilities: bool = False
wrong_replaces_id: bool = False
no_padded_images: bool = False
+ wrong_closes_type: bool = False
@dataclasses.dataclass
@@ -712,10 +728,23 @@ class DBusNotificationAdapter(AbstractNotificationAdapter):
# https://github.com/KDE/plasma-workspace/blob/v5.21.4/libnotificationmanager/server_p.cpp#L227-L237
# Created too many similar notifications in quick succession
"org.freedesktop.Notifications.Error.ExcessNotificationGeneration",
+
+ # From https://crashes.qutebrowser.org/view/b8c9838a
+ # Process org.freedesktop.Notifications received signal 5
+ # probably when notification daemon crashes?
+ "org.freedesktop.DBus.Error.Spawn.ChildSignaled",
+
+ # https://crashes.qutebrowser.org/view/f76f58ae
+ # Process org.freedesktop.Notifications exited with status 1
+ "org.freedesktop.DBus.Error.Spawn.ChildExited",
+
+ # https://crashes.qutebrowser.org/view/8889d0b5
+ # Could not activate remote peer.
+ "org.freedesktop.DBus.Error.NameHasNoOwner",
}
def __init__(self, parent: QObject = None) -> None:
- super().__init__(bridge)
+ super().__init__(parent)
assert _notifications_supported()
if utils.is_windows:
@@ -737,8 +766,7 @@ class DBusNotificationAdapter(AbstractNotificationAdapter):
QDBusServiceWatcher.WatchForUnregistration,
self,
)
- self._watcher.serviceUnregistered.connect( # type: ignore[attr-defined]
- self._on_service_unregistered)
+ self._watcher.serviceUnregistered.connect(self._on_service_unregistered)
test_service = 'test-notification-service' in objects.debug_flags
service = self.TEST_SERVICE if test_service else self.SERVICE
@@ -837,12 +865,20 @@ class DBusNotificationAdapter(AbstractNotificationAdapter):
wrong_replaces_id=True,
)
elif (name, vendor) == ("Raven", "Budgie Desktop Developers"):
+ # Before refactor
return _ServerQuirks(
# https://github.com/solus-project/budgie-desktop/issues/2114
escape_title=True,
# https://github.com/solus-project/budgie-desktop/issues/2115
wrong_replaces_id=True,
)
+ elif (name, vendor) == (
+ "Budgie Notification Server", "Budgie Desktop Developers"):
+ # After refactor: https://github.com/BuddiesOfBudgie/budgie-desktop/pull/36
+ return _ServerQuirks(
+ # https://github.com/BuddiesOfBudgie/budgie-desktop/issues/118
+ wrong_closes_type=True,
+ )
return None
def _get_server_info(self) -> None:
@@ -947,8 +983,8 @@ class DBusNotificationAdapter(AbstractNotificationAdapter):
icon = qt_notification.icon()
if icon.isNull():
- filename = ':/icons/qutebrowser-64x64.png'
- icon = QImage(filename)
+ filename = 'icons/qutebrowser-64x64.png'
+ icon = QImage.fromData(resources.read_file_binary(filename))
key = self._quirks.icon_key or "image-data"
data = self._convert_image(icon)
@@ -986,6 +1022,9 @@ class DBusNotificationAdapter(AbstractNotificationAdapter):
else:
log.misc.error(msg)
+ if notification_id <= 0:
+ self.error.emit(f"Got invalid notification id {notification_id}")
+
return notification_id
def _convert_image(self, qimage: QImage) -> Optional[QDBusArgument]:
@@ -1064,7 +1103,13 @@ class DBusNotificationAdapter(AbstractNotificationAdapter):
@pyqtSlot(QDBusMessage)
def _handle_close(self, msg: QDBusMessage) -> None:
"""Handle NotificationClosed from DBus."""
- self._verify_message(msg, "uu", QDBusMessage.SignalMessage)
+ try:
+ self._verify_message(msg, "uu", QDBusMessage.SignalMessage)
+ except Error:
+ if not self._quirks.wrong_closes_type:
+ raise
+ self._verify_message(msg, "ui", QDBusMessage.SignalMessage)
+
notification_id, _close_reason = msg.arguments()
self.close_id.emit(notification_id)
diff --git a/qutebrowser/browser/webengine/webenginedownloads.py b/qutebrowser/browser/webengine/webenginedownloads.py
index fc7ed8ca2..a96f49d6b 100644
--- a/qutebrowser/browser/webengine/webenginedownloads.py
+++ b/qutebrowser/browser/webengine/webenginedownloads.py
@@ -44,10 +44,8 @@ class DownloadItem(downloads.AbstractDownloadItem):
parent: QObject = None) -> None:
super().__init__(manager=manager, parent=manager)
self._qt_item = qt_item
- qt_item.downloadProgress.connect( # type: ignore[attr-defined]
- self.stats.on_download_progress)
- qt_item.stateChanged.connect( # type: ignore[attr-defined]
- self._on_state_changed)
+ qt_item.downloadProgress.connect(self.stats.on_download_progress)
+ qt_item.stateChanged.connect(self._on_state_changed)
# Ensure wrapped qt_item is deleted manually when the wrapper object
# is deleted. See https://github.com/qutebrowser/qutebrowser/issues/3373
@@ -92,7 +90,7 @@ class DownloadItem(downloads.AbstractDownloadItem):
def _do_die(self):
progress_signal = self._qt_item.downloadProgress
- progress_signal.disconnect() # type: ignore[attr-defined]
+ progress_signal.disconnect()
if self._qt_item.state() != QWebEngineDownloadItem.DownloadInterrupted:
self._qt_item.cancel()
@@ -119,6 +117,10 @@ class DownloadItem(downloads.AbstractDownloadItem):
def url(self) -> QUrl:
return self._qt_item.url()
+ def origin(self) -> QUrl:
+ page = self._qt_item.page()
+ return page.url() if page else QUrl()
+
def _set_fileobj(self, fileobj, *, autoclose=True):
raise downloads.UnsupportedOperationError
@@ -251,7 +253,6 @@ class DownloadManager(downloads.AbstractDownloadManager):
qt_filename = os.path.basename(qt_item.path()) # FIXME use 5.14 API
mime_type = qt_item.mimeType()
url = qt_item.url()
- origin = qt_item.page().url() if qt_item.page() else QUrl()
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-90355
if version.qtwebengine_versions().webengine >= utils.VersionNumber(5, 15, 3):
@@ -293,9 +294,7 @@ class DownloadManager(downloads.AbstractDownloadManager):
download.set_target(target)
return
- if url.scheme() == "file" and origin.isValid() and origin.scheme() == "file":
- utils.open_file(url.toLocalFile())
- qt_item.cancel()
+ if download.cancel_for_origin():
return
# Ask the user for a filename - needs to be blocking!
diff --git a/qutebrowser/browser/webengine/webengineelem.py b/qutebrowser/browser/webengine/webengineelem.py
index 5d4c6ad9a..75b7a51ba 100644
--- a/qutebrowser/browser/webengine/webengineelem.py
+++ b/qutebrowser/browser/webengine/webengineelem.py
@@ -37,6 +37,8 @@ class WebEngineElement(webelem.AbstractWebElement):
"""A web element for QtWebEngine, using JS under the hood."""
+ _tab: "webenginetab.WebEngineTab"
+
def __init__(self, js_dict: Dict[str, Any],
tab: 'webenginetab.WebEngineTab') -> None:
super().__init__(tab)
@@ -248,7 +250,7 @@ class WebEngineElement(webelem.AbstractWebElement):
# (it does so with a 0ms QTimer...)
# This is also used in Qt's tests:
# https://github.com/qt/qtwebengine/commit/5e572e88efa7ba7c2b9138ec19e606d3e345ac90
- QApplication.processEvents( # type: ignore[call-overload]
+ QApplication.processEvents(
QEventLoop.ExcludeSocketNotifiers |
QEventLoop.ExcludeUserInputEvents)
diff --git a/qutebrowser/browser/webengine/webengineinspector.py b/qutebrowser/browser/webengine/webengineinspector.py
index ae31c0bee..58babc70c 100644
--- a/qutebrowser/browser/webengine/webengineinspector.py
+++ b/qutebrowser/browser/webengine/webengineinspector.py
@@ -49,13 +49,17 @@ class WebEngineInspectorView(QWebEngineView):
See WebEngineView.createWindow for details.
"""
- return self.page().inspectedPage().view().createWindow(wintype)
+ view = self.page().inspectedPage().view()
+ assert isinstance(view, QWebEngineView), view
+ return view.createWindow(wintype)
class WebEngineInspector(inspector.AbstractWebInspector):
"""A web inspector for QtWebEngine with Qt API support."""
+ _widget: WebEngineInspectorView
+
def __init__(self, splitter: miscwidgets.InspectorSplitter,
win_id: int,
parent: QWidget = None) -> None:
@@ -66,8 +70,7 @@ class WebEngineInspector(inspector.AbstractWebInspector):
self._settings = webenginesettings.WebEngineSettings(view.settings())
self._set_widget(view)
page = view.page()
- page.windowCloseRequested.connect( # type: ignore[attr-defined]
- self._on_window_close_requested)
+ page.windowCloseRequested.connect(self._on_window_close_requested)
def _on_window_close_requested(self) -> None:
"""Called when the 'x' was clicked in the devtools."""
@@ -96,7 +99,7 @@ class WebEngineInspector(inspector.AbstractWebInspector):
"please install the qt5-qtwebengine-devtools "
"Fedora package.")
- def inspect(self, page: QWebEnginePage) -> None: # type: ignore[override]
+ def inspect(self, page: QWebEnginePage) -> None:
inspector_page = self._widget.page()
inspector_page.setInspectedPage(page)
self._settings.update_for_url(inspector_page.requestedUrl())
diff --git a/qutebrowser/browser/webengine/webenginequtescheme.py b/qutebrowser/browser/webengine/webenginequtescheme.py
index 64361f7c4..9e073951a 100644
--- a/qutebrowser/browser/webengine/webenginequtescheme.py
+++ b/qutebrowser/browser/webengine/webenginequtescheme.py
@@ -138,6 +138,6 @@ def init():
assert not QWebEngineUrlScheme.schemeByName(b'qute').name()
scheme = QWebEngineUrlScheme(b'qute')
scheme.setFlags(
- QWebEngineUrlScheme.LocalScheme | # type: ignore[arg-type]
+ QWebEngineUrlScheme.LocalScheme |
QWebEngineUrlScheme.LocalAccessAllowed)
QWebEngineUrlScheme.registerScheme(scheme)
diff --git a/qutebrowser/browser/webengine/webenginesettings.py b/qutebrowser/browser/webengine/webenginesettings.py
index d20a92014..ad374ed9b 100644
--- a/qutebrowser/browser/webengine/webenginesettings.py
+++ b/qutebrowser/browser/webengine/webenginesettings.py
@@ -117,8 +117,6 @@ class WebEngineSettings(websettings.AbstractSettings):
Attr(QWebEngineSettings.JavascriptEnabled),
'content.javascript.can_open_tabs_automatically':
Attr(QWebEngineSettings.JavascriptCanOpenWindows),
- 'content.javascript.can_access_clipboard':
- Attr(QWebEngineSettings.JavascriptCanAccessClipboard),
'content.plugins':
Attr(QWebEngineSettings.PluginsEnabled),
'content.hyperlink_auditing':
@@ -199,26 +197,47 @@ class WebEngineSettings(websettings.AbstractSettings):
QWebEngineSettings.FantasyFont: QFont.Fantasy,
}
- def set_unknown_url_scheme_policy(
- self, policy: Union[str, usertypes.Unset]) -> bool:
- """Set the UnknownUrlSchemePolicy to use.
+ _JS_CLIPBOARD_SETTINGS = {
+ 'none': {
+ QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard: False,
+ QWebEngineSettings.WebAttribute.JavascriptCanPaste: False,
+ },
+ 'access': {
+ QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard: True,
+ QWebEngineSettings.WebAttribute.JavascriptCanPaste: False,
+ },
+ 'access-paste': {
+ QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard: True,
+ QWebEngineSettings.WebAttribute.JavascriptCanPaste: True,
+ },
+ }
- Return:
- True if there was a change, False otherwise.
- """
- old_value = self._settings.unknownUrlSchemePolicy()
+ def set_unknown_url_scheme_policy(
+ self, policy: Union[str, usertypes.Unset]) -> None:
+ """Set the UnknownUrlSchemePolicy to use."""
if isinstance(policy, usertypes.Unset):
self._settings.resetUnknownUrlSchemePolicy()
- new_value = self._settings.unknownUrlSchemePolicy()
else:
new_value = self._UNKNOWN_URL_SCHEME_POLICY[policy]
self._settings.setUnknownUrlSchemePolicy(new_value)
- return old_value != new_value
+
+ def _set_js_clipboard(self, value: Union[str, usertypes.Unset]) -> None:
+ attr_access = QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard
+ attr_paste = QWebEngineSettings.WebAttribute.JavascriptCanPaste
+
+ if isinstance(value, usertypes.Unset):
+ self._settings.resetAttribute(attr_access)
+ self._settings.resetAttribute(attr_paste)
+ else:
+ for attr, attr_val in self._JS_CLIPBOARD_SETTINGS[value].items():
+ self._settings.setAttribute(attr, attr_val)
def _update_setting(self, setting, value):
if setting == 'content.unknown_url_scheme_policy':
- return self.set_unknown_url_scheme_policy(value)
- return super()._update_setting(setting, value)
+ self.set_unknown_url_scheme_policy(value)
+ elif setting == 'content.javascript.clipboard':
+ self._set_js_clipboard(value)
+ super()._update_setting(setting, value)
def init_settings(self):
super().init_settings()
@@ -332,9 +351,9 @@ class ProfileSetter:
def _update_settings(option):
"""Update global settings when qwebsettings changed."""
_global_settings.update_setting(option)
- default_profile.setter.update_setting(option)
+ default_profile.setter.update_setting(option) # type: ignore[attr-defined]
if private_profile:
- private_profile.setter.update_setting(option)
+ private_profile.setter.update_setting(option) # type: ignore[attr-defined]
def _init_user_agent_str(ua):
@@ -352,8 +371,9 @@ def _init_profile(profile: QWebEngineProfile) -> None:
This currently only contains the steps which are shared between a private and a
non-private profile (at the moment, only the default profile).
"""
+ # FIXME:mypy subclass QWebEngineProfile instead?
profile.setter = ProfileSetter(profile) # type: ignore[attr-defined]
- profile.setter.init_profile()
+ profile.setter.init_profile() # type: ignore[attr-defined]
_qute_scheme_handler.install(profile)
_req_interceptor.install(profile)
@@ -431,11 +451,7 @@ def _init_site_specific_quirks():
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/99 "
"Safari/537.36")
- edge_ua = ("Mozilla/5.0 ({os_info}) "
- "AppleWebKit/{webkit_version} (KHTML, like Gecko) "
- "{upstream_browser_key}/{upstream_browser_version} "
- "Safari/{webkit_version} "
- "Edg/{upstream_browser_version}")
+ firefox_ua = "Mozilla/5.0 ({os_info}; rv:90.0) Gecko/20100101 Firefox/90.0"
user_agents = [
# Needed to avoid a ""WhatsApp works with Google Chrome 36+" error
@@ -447,7 +463,7 @@ def _init_site_specific_quirks():
# Needed to avoid a "you're using a browser [...] that doesn't allow us
# to keep your account secure" error.
# https://github.com/qutebrowser/qutebrowser/issues/5182
- ("ua-google", 'https://accounts.google.com/*', edge_ua),
+ ("ua-google", 'https://accounts.google.com/*', firefox_ua),
# Needed because Slack adds an error which prevents using it relatively
# aggressively, despite things actually working fine.
diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py
index ace23d14a..0a2333afc 100644
--- a/qutebrowser/browser/webengine/webenginetab.py
+++ b/qutebrowser/browser/webengine/webenginetab.py
@@ -26,10 +26,11 @@ import re
import html as html_utils
from typing import cast, Union, Optional
-from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QPoint, QPointF, QUrl, QObject
+from PyQt5.QtCore import (pyqtSignal, pyqtSlot, Qt, QPoint, QPointF, QTimer, QUrl,
+ QObject)
from PyQt5.QtNetwork import QAuthenticator
-from PyQt5.QtWidgets import QWidget
-from PyQt5.QtWebEngineWidgets import QWebEnginePage, QWebEngineScript, QWebEngineHistory
+from PyQt5.QtWebEngineWidgets import (QWebEnginePage, QWebEngineView, QWebEngineScript,
+ QWebEngineHistory)
from qutebrowser.config import config
from qutebrowser.browser import browsertab, eventfilter, shared, webelem, greasemonkey
@@ -56,6 +57,8 @@ class WebEngineAction(browsertab.AbstractAction):
"""QtWebEngine implementations related to web actions."""
+ _widget: webview.WebEngineView
+
action_class = QWebEnginePage
action_base = QWebEnginePage.WebAction
@@ -78,6 +81,8 @@ class WebEnginePrinting(browsertab.AbstractPrinting):
"""QtWebEngine implementations related to printing."""
+ _widget: webview.WebEngineView
+
def check_pdf_support(self):
pass
@@ -88,9 +93,7 @@ class WebEnginePrinting(browsertab.AbstractPrinting):
def to_pdf(self, filename):
self._widget.page().printToPdf(filename)
- def to_printer(self, printer, callback=None):
- if callback is None:
- callback = lambda _ok: None
+ def to_printer(self, printer, callback=lambda ok: None):
self._widget.page().print(printer, callback)
@@ -190,6 +193,8 @@ class WebEngineSearch(browsertab.AbstractSearch):
back yet.
"""
+ _widget: webview.WebEngineView
+
def __init__(self, tab, parent=None):
super().__init__(tab, parent)
self._flags = self._empty_flags()
@@ -198,7 +203,15 @@ class WebEngineSearch(browsertab.AbstractSearch):
self._wrap_handler = _WebEngineSearchWrapHandler()
def _empty_flags(self):
- return QWebEnginePage.FindFlags(0) # type: ignore[call-overload]
+ return QWebEnginePage.FindFlags(0)
+
+ def _args_to_flags(self, reverse, ignore_case):
+ flags = self._empty_flags()
+ if self._is_case_sensitive(ignore_case):
+ flags |= QWebEnginePage.FindCaseSensitively
+ if reverse:
+ flags |= QWebEnginePage.FindBackward
+ return flags
def connect_signals(self):
self._wrap_handler.connect_signal(self._widget.page())
@@ -246,17 +259,14 @@ class WebEngineSearch(browsertab.AbstractSearch):
# Don't go to next entry on duplicate search
if self.text == text and self.search_displayed:
log.webview.debug("Ignoring duplicate search request"
- " for {}".format(text))
+ " for {}, but resetting flags".format(text))
+ self._flags = self._args_to_flags(reverse, ignore_case)
return
self.text = text
- self._flags = self._empty_flags()
+ self._flags = self._args_to_flags(reverse, ignore_case)
self._wrap_handler.reset_match_data()
self._wrap_handler.flag_wrap = wrap
- if self._is_case_sensitive(ignore_case):
- self._flags |= QWebEnginePage.FindCaseSensitively
- if reverse:
- self._flags |= QWebEnginePage.FindBackward
self._find(text, self._flags, result_cb, 'search')
@@ -269,8 +279,7 @@ class WebEngineSearch(browsertab.AbstractSearch):
def prev_result(self, *, result_cb=None):
# The int() here makes sure we get a copy of the flags.
- flags = QWebEnginePage.FindFlags(
- int(self._flags)) # type: ignore[call-overload]
+ flags = QWebEnginePage.FindFlags(int(self._flags))
if flags & QWebEnginePage.FindBackward:
if self._wrap_handler.prevent_wrapping(going_up=False):
return
@@ -487,6 +496,8 @@ class WebEngineScroller(browsertab.AbstractScroller):
"""QtWebEngine implementations related to scrolling."""
+ _widget: webview.WebEngineView
+
def __init__(self, tab, parent=None):
super().__init__(tab, parent)
self._pos_perc = (0, 0)
@@ -707,6 +718,8 @@ class WebEngineZoom(browsertab.AbstractZoom):
"""QtWebEngine implementations related to zooming."""
+ _widget: webview.WebEngineView
+
def _set_factor_internal(self, factor):
self._widget.setZoomFactor(factor)
@@ -793,17 +806,44 @@ class WebEngineAudio(browsertab.AbstractAudio):
If that's the case, we leave it alone.
"""
+ _widget: webview.WebEngineView
+
def __init__(self, tab, parent=None):
super().__init__(tab, parent)
self._overridden = False
+ # Implements the intended two-second delay specified at
+ # https://doc.qt.io/qt-5/qwebenginepage.html#recentlyAudibleChanged
+ delay_ms = 2000
+ self._silence_timer = QTimer(self)
+ self._silence_timer.setSingleShot(True)
+ self._silence_timer.setInterval(delay_ms)
+
def _connect_signals(self):
page = self._widget.page()
page.audioMutedChanged.connect(self.muted_changed)
- page.recentlyAudibleChanged.connect(self.recently_audible_changed)
+ page.recentlyAudibleChanged.connect(self._delayed_recently_audible_changed)
self._tab.url_changed.connect(self._on_url_changed)
config.instance.changed.connect(self._on_config_changed)
+ # WORKAROUND for recentlyAudibleChanged being emitted without delay from the moment
+ # that audio is dropped.
+ def _delayed_recently_audible_changed(self, recently_audible):
+ timer = self._silence_timer
+ # Stop any active timer and immediately display [A] if tab is audible,
+ # otherwise start a timer to update audio field
+ if recently_audible:
+ if timer.isActive():
+ timer.stop()
+ self.recently_audible_changed.emit(recently_audible)
+ else:
+ # Ignore all subsequent calls while the tab is muted with an active timer
+ if timer.isActive():
+ return
+ timer.timeout.connect(
+ functools.partial(self.recently_audible_changed.emit, recently_audible))
+ timer.start()
+
def set_muted(self, muted: bool, override: bool = False) -> None:
was_muted = self.is_muted()
self._overridden = override
@@ -839,6 +879,8 @@ class _WebEnginePermissions(QObject):
"""Handling of various permission-related signals."""
+ _widget: webview.WebEngineView
+
# Using 0 as WORKAROUND for:
# https://www.riverbankcomputing.com/pipermail/pyqt/2019-July/041903.html
@@ -867,7 +909,7 @@ class _WebEnginePermissions(QObject):
def __init__(self, tab, parent=None):
super().__init__(parent)
self._tab = tab
- self._widget = cast(QWidget, None)
+ self._widget = cast(webview.WebEngineView, None)
assert self._options.keys() == self._messages.keys()
def connect_signals(self):
@@ -1002,10 +1044,12 @@ class _Quirk:
class _WebEngineScripts(QObject):
+ _widget: webview.WebEngineView
+
def __init__(self, tab, parent=None):
super().__init__(parent)
self._tab = tab
- self._widget = cast(QWidget, None)
+ self._widget = cast(webview.WebEngineView, None)
self._greasemonkey = greasemonkey.gm_manager
def connect_signals(self):
@@ -1209,6 +1253,8 @@ class WebEngineTabPrivate(browsertab.AbstractTabPrivate):
"""QtWebEngine-related methods which aren't part of the public API."""
+ _widget: webview.WebEngineView
+
def networkaccessmanager(self):
return None
@@ -1244,6 +1290,10 @@ class WebEngineTab(browsertab.AbstractTab):
abort_questions = pyqtSignal()
+ _widget: QWebEngineView
+ search: WebEngineSearch
+ audio: WebEngineAudio
+
def __init__(self, *, win_id, mode_manager, private, parent=None):
super().__init__(win_id=win_id,
mode_manager=mode_manager,
@@ -1499,7 +1549,6 @@ class WebEngineTab(browsertab.AbstractTab):
displayed.
WORKAROUND for https://bugreports.qt.io/browse/QTBUG-66643
- WORKAROUND for https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=882805
"""
match = re.search(r'"errorCode":"([^"]*)"', html)
if match is None:
@@ -1508,8 +1557,7 @@ class WebEngineTab(browsertab.AbstractTab):
error = match.group(1)
log.webview.error("Load error: {}".format(error))
- missing_jst = 'jstProcess(' in html and 'jstProcess=' not in html
- if js_enabled and not missing_jst:
+ if js_enabled:
return
self._show_error_page(self.url(), error=error)
@@ -1656,6 +1704,7 @@ class WebEngineTab(browsertab.AbstractTab):
def _connect_signals(self):
view = self._widget
page = view.page()
+ assert isinstance(page, webview.WebEnginePage), page
page.windowCloseRequested.connect(self.window_close_requested)
page.linkHovered.connect(self.link_hovered)
diff --git a/qutebrowser/browser/webengine/webview.py b/qutebrowser/browser/webengine/webview.py
index 76ce1a42e..4dd1b2a71 100644
--- a/qutebrowser/browser/webengine/webview.py
+++ b/qutebrowser/browser/webengine/webview.py
@@ -70,7 +70,10 @@ class WebEngineView(QWebEngineView):
return self.focusProxy()
def shutdown(self):
- self.page().shutdown()
+ """Shut down the underlying page."""
+ page = self.page()
+ assert isinstance(page, WebEnginePage), page
+ page.shutdown()
def createWindow(self, wintype):
"""Called by Qt when a page wants to create a new window.
diff --git a/qutebrowser/browser/webkit/http.py b/qutebrowser/browser/webkit/http.py
index eacb95679..a38cd358a 100644
--- a/qutebrowser/browser/webkit/http.py
+++ b/qutebrowser/browser/webkit/http.py
@@ -89,20 +89,31 @@ class ContentDisposition:
try:
parsed = reg('Content-Disposition', decoded)
except IndexError: # pragma: no cover
- # WORKAROUND for https://bugs.python.org/issue37491
+ # WORKAROUND for https://github.com/python/cpython/issues/81672
# Fixed in Python 3.7.5 and 3.8.0.
+ # Still getting failures on 3.10 on CI though
raise ContentDispositionError("Missing closing quote character")
except ValueError: # pragma: no cover
- # WORKAROUND for https://bugs.python.org/issue42946
+ # WORKAROUND for https://github.com/python/cpython/issues/87112
raise ContentDispositionError("Non-ASCII digit")
+ except AttributeError:
+ # WORKAROUND for https://github.com/python/cpython/issues/93010
+ raise ContentDispositionError("Section number has an invalid leading 0")
if parsed.defects:
defects = list(parsed.defects)
if defects != [cls._IGNORED_DEFECT]: # type: ignore[comparison-overlap]
raise ContentDispositionError(defects)
- assert isinstance(parsed, email.headerregistry.ContentDispositionHeader), parsed
- return cls(disposition=parsed.content_disposition, params=parsed.params)
+ # https://github.com/python/mypy/issues/12314
+ assert isinstance(
+ parsed, # type: ignore[unreachable]
+ email.headerregistry.ContentDispositionHeader,
+ ), parsed
+ return cls( # type: ignore[unreachable]
+ disposition=parsed.content_disposition,
+ params=parsed.params,
+ )
def filename(self):
"""The filename from the Content-Disposition header or None.
@@ -143,7 +154,7 @@ def parse_content_disposition(reply):
"""
is_inline = True
filename = None
- content_disposition_header = 'Content-Disposition'.encode('iso-8859-1')
+ content_disposition_header = b'Content-Disposition'
# First check if the Content-Disposition header has a filename
# attribute.
if reply.hasRawHeader(content_disposition_header):
@@ -151,7 +162,7 @@ def parse_content_disposition(reply):
# os.path.basename later.
try:
value = bytes(reply.rawHeader(content_disposition_header))
- log.network.debug("Parsing Content-Disposition: {value!r}")
+ log.network.debug(f"Parsing Content-Disposition: {value!r}")
content_disposition = ContentDisposition.parse(value)
filename = content_disposition.filename()
except ContentDispositionError as e:
diff --git a/qutebrowser/browser/webkit/network/networkmanager.py b/qutebrowser/browser/webkit/network/networkmanager.py
index c97c91d66..83eaabda7 100644
--- a/qutebrowser/browser/webkit/network/networkmanager.py
+++ b/qutebrowser/browser/webkit/network/networkmanager.py
@@ -25,7 +25,7 @@ import dataclasses
from typing import TYPE_CHECKING, Dict, MutableMapping, Optional, Set
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QUrl, QByteArray
-from PyQt5.QtNetwork import (QNetworkAccessManager, QNetworkReply, QSslSocket,
+from PyQt5.QtNetwork import (QNetworkAccessManager, QNetworkReply, QSslConfiguration,
QNetworkProxy)
from qutebrowser.config import config
@@ -103,7 +103,8 @@ def _is_secure_cipher(cipher):
def init():
"""Disable insecure SSL ciphers on old Qt versions."""
- default_ciphers = QSslSocket.defaultCiphers()
+ sslconfig = QSslConfiguration.defaultConfiguration()
+ default_ciphers = sslconfig.ciphers()
log.init.vdebug( # type: ignore[attr-defined]
"Default Qt ciphers: {}".format(
', '.join(c.name() for c in default_ciphers)))
@@ -119,7 +120,7 @@ def init():
if bad_ciphers:
log.init.debug("Disabling bad ciphers: {}".format(
', '.join(c.name() for c in bad_ciphers)))
- QSslSocket.setDefaultCiphers(good_ciphers)
+ sslconfig.setCiphers(good_ciphers)
_SavedErrorsType = MutableMapping[
@@ -379,10 +380,10 @@ class NetworkManager(QNetworkAccessManager):
if referer_header_conf == 'never':
# Note: using ''.encode('ascii') sends a header with no value,
# instead of no header at all
- req.setRawHeader('Referer'.encode('ascii'), QByteArray())
+ req.setRawHeader(b'Referer', QByteArray())
elif (referer_header_conf == 'same-domain' and
not urlutils.same_domain(req.url(), current_url)):
- req.setRawHeader('Referer'.encode('ascii'), QByteArray())
+ req.setRawHeader(b'Referer', QByteArray())
# If refer_header_conf is set to 'always', we leave the header
# alone as QtWebKit did set it.
except urlutils.InvalidUrlError:
diff --git a/qutebrowser/browser/webkit/network/networkreply.py b/qutebrowser/browser/webkit/network/networkreply.py
index c1ead3209..d5c724d63 100644
--- a/qutebrowser/browser/webkit/network/networkreply.py
+++ b/qutebrowser/browser/webkit/network/networkreply.py
@@ -59,15 +59,9 @@ class FixedDataNetworkReply(QNetworkReply):
# For some reason, a segfault will be triggered if these lambdas aren't
# there.
# pylint: disable=unnecessary-lambda
- QTimer.singleShot(
- 0,
- lambda: self.metaDataChanged.emit()) # type: ignore[attr-defined]
- QTimer.singleShot(
- 0,
- lambda: self.readyRead.emit()) # type: ignore[attr-defined]
- QTimer.singleShot(
- 0,
- lambda: self.finished.emit()) # type: ignore[attr-defined]
+ QTimer.singleShot(0, lambda: self.metaDataChanged.emit())
+ QTimer.singleShot(0, lambda: self.readyRead.emit())
+ QTimer.singleShot(0, lambda: self.finished.emit())
@pyqtSlot()
def abort(self):
@@ -122,10 +116,8 @@ class ErrorNetworkReply(QNetworkReply):
# the device to avoid getting a warning.
self.setOpenMode(QIODevice.ReadOnly)
self.setError(error, errorstring)
- QTimer.singleShot(0, lambda:
- self.error.emit(error)) # type: ignore[attr-defined]
- QTimer.singleShot(0, lambda:
- self.finished.emit()) # type: ignore[attr-defined]
+ QTimer.singleShot(0, lambda: self.error.emit(error))
+ QTimer.singleShot(0, lambda: self.finished.emit())
def abort(self):
"""Do nothing since it's a fake reply."""
@@ -152,8 +144,7 @@ class RedirectNetworkReply(QNetworkReply):
def __init__(self, new_url, parent=None):
super().__init__(parent)
self.setAttribute(QNetworkRequest.RedirectionTargetAttribute, new_url)
- QTimer.singleShot(0, lambda:
- self.finished.emit()) # type: ignore[attr-defined]
+ QTimer.singleShot(0, lambda: self.finished.emit())
def abort(self):
"""Called when there's e.g. a redirection limit."""
diff --git a/qutebrowser/browser/webkit/webkithistory.py b/qutebrowser/browser/webkit/webkithistory.py
index 4149bda88..6ffe65193 100644
--- a/qutebrowser/browser/webkit/webkithistory.py
+++ b/qutebrowser/browser/webkit/webkithistory.py
@@ -44,7 +44,7 @@ class WebHistoryInterface(QWebHistoryInterface):
"""Required for a QWebHistoryInterface impl, obsoleted by add_url."""
@debugcachestats.register(name='history')
- @functools.lru_cache(maxsize=32768)
+ @functools.lru_cache(maxsize=32768) # noqa: B019
def historyContains(self, url_string):
"""Called by WebKit to determine if a URL is contained in the history.
diff --git a/qutebrowser/browser/webkit/webkitinspector.py b/qutebrowser/browser/webkit/webkitinspector.py
index 57bcd40ea..4d9785cbe 100644
--- a/qutebrowser/browser/webkit/webkitinspector.py
+++ b/qutebrowser/browser/webkit/webkitinspector.py
@@ -38,7 +38,7 @@ class WebKitInspector(inspector.AbstractWebInspector):
qwebinspector = QWebInspector()
self._set_widget(qwebinspector)
- def inspect(self, page: QWebPage) -> None: # type: ignore[override]
+ def inspect(self, page: QWebPage) -> None:
settings = QWebSettings.globalSettings()
settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True)
self._widget.setPage(page)
diff --git a/qutebrowser/browser/webkit/webkitsettings.py b/qutebrowser/browser/webkit/webkitsettings.py
index cac6236ce..1348c849b 100644
--- a/qutebrowser/browser/webkit/webkitsettings.py
+++ b/qutebrowser/browser/webkit/webkitsettings.py
@@ -57,8 +57,9 @@ class WebKitSettings(websettings.AbstractSettings):
Attr(QWebSettings.JavascriptCanOpenWindows),
'content.javascript.can_close_tabs':
Attr(QWebSettings.JavascriptCanCloseWindows),
- 'content.javascript.can_access_clipboard':
- Attr(QWebSettings.JavascriptCanAccessClipboard),
+ 'content.javascript.clipboard':
+ Attr(QWebSettings.JavascriptCanAccessClipboard,
+ converter=lambda val: val != "none"),
'content.plugins':
Attr(QWebSettings.PluginsEnabled),
'content.webgl':
diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py
index df3491ec2..24d232c9c 100644
--- a/qutebrowser/browser/webkit/webkittab.py
+++ b/qutebrowser/browser/webkit/webkittab.py
@@ -32,8 +32,9 @@ from PyQt5.QtWebKit import QWebSettings, QWebHistory, QWebElement
from PyQt5.QtPrintSupport import QPrinter
from qutebrowser.browser import browsertab, shared
-from qutebrowser.browser.webkit import (webview, tabhistory, webkitelem,
+from qutebrowser.browser.webkit import (webview, webpage, tabhistory, webkitelem,
webkitsettings, webkitinspector)
+from qutebrowser.browser.webkit.network import networkmanager
from qutebrowser.utils import qtutils, usertypes, utils, log, debug, resources
from qutebrowser.keyinput import modeman
from qutebrowser.qt import sip
@@ -46,6 +47,8 @@ class WebKitAction(browsertab.AbstractAction):
action_class = QWebPage
action_base = QWebPage.WebAction
+ _widget: webview.WebView
+
def exit_fullscreen(self):
raise browsertab.UnsupportedOperationError
@@ -69,7 +72,7 @@ class WebKitAction(browsertab.AbstractAction):
'Unselect': QWebPage.ToggleVideoFullscreen + 2,
}
if name in new_actions:
- self._widget.triggerPageAction(new_actions[name])
+ self._widget.triggerPageAction(new_actions[name]) # type: ignore[arg-type]
return
super().run_string(name)
@@ -79,6 +82,8 @@ class WebKitPrinting(browsertab.AbstractPrinting):
"""QtWebKit implementations related to printing."""
+ _widget: webview.WebView
+
def check_pdf_support(self):
pass
@@ -101,6 +106,8 @@ class WebKitSearch(browsertab.AbstractSearch):
"""QtWebKit implementations related to searching on the page."""
+ _widget: webview.WebView
+
def __init__(self, tab, parent=None):
super().__init__(tab, parent)
self._flags = self._empty_flags()
@@ -108,6 +115,16 @@ class WebKitSearch(browsertab.AbstractSearch):
def _empty_flags(self):
return QWebPage.FindFlags(0) # type: ignore[call-overload]
+ def _args_to_flags(self, reverse, ignore_case, wrap):
+ flags = self._empty_flags()
+ if self._is_case_sensitive(ignore_case):
+ flags |= QWebPage.FindCaseSensitively
+ if reverse:
+ flags |= QWebPage.FindBackward
+ if wrap:
+ flags |= QWebPage.FindWrapsAroundDocument
+ return flags
+
def _call_cb(self, callback, found, text, flags, caller):
"""Call the given callback if it's non-None.
@@ -143,14 +160,16 @@ class WebKitSearch(browsertab.AbstractSearch):
self.search_displayed = False
# We first clear the marked text, then the highlights
self._widget.findText('')
- self._widget.findText('', QWebPage.HighlightAllOccurrences)
+ self._widget.findText(
+ '', QWebPage.HighlightAllOccurrences) # type: ignore[arg-type]
def search(self, text, *, ignore_case=usertypes.IgnoreCase.never,
reverse=False, wrap=True, result_cb=None):
# Don't go to next entry on duplicate search
if self.text == text and self.search_displayed:
log.webview.debug("Ignoring duplicate search request"
- " for {}".format(text))
+ " for {}, but resetting flags".format(text))
+ self._flags = self._args_to_flags(reverse, ignore_case, wrap)
return
# Clear old search results, this is done automatically on QtWebEngine.
@@ -158,13 +177,7 @@ class WebKitSearch(browsertab.AbstractSearch):
self.text = text
self.search_displayed = True
- self._flags = self._empty_flags()
- if self._is_case_sensitive(ignore_case):
- self._flags |= QWebPage.FindCaseSensitively
- if reverse:
- self._flags |= QWebPage.FindBackward
- if wrap:
- self._flags |= QWebPage.FindWrapsAroundDocument
+ self._flags = self._args_to_flags(reverse, ignore_case, wrap)
# We actually search *twice* - once to highlight everything, then again
# to get a mark so we can navigate.
found = self._widget.findText(text, self._flags)
@@ -174,7 +187,7 @@ class WebKitSearch(browsertab.AbstractSearch):
def next_result(self, *, result_cb=None):
self.search_displayed = True
- found = self._widget.findText(self.text, self._flags)
+ found = self._widget.findText(self.text, self._flags) # type: ignore[arg-type]
self._call_cb(result_cb, found, self.text, self._flags, 'next_result')
def prev_result(self, *, result_cb=None):
@@ -186,7 +199,7 @@ class WebKitSearch(browsertab.AbstractSearch):
flags &= ~QWebPage.FindBackward
else:
flags |= QWebPage.FindBackward
- found = self._widget.findText(self.text, flags)
+ found = self._widget.findText(self.text, flags) # type: ignore[arg-type]
self._call_cb(result_cb, found, self.text, flags, 'prev_result')
@@ -194,6 +207,8 @@ class WebKitCaret(browsertab.AbstractCaret):
"""QtWebKit implementations related to moving the cursor/selection."""
+ _widget: webview.WebView
+
def __init__(self,
tab: 'WebKitTab',
mode_manager: modeman.ModeManager,
@@ -510,6 +525,8 @@ class WebKitZoom(browsertab.AbstractZoom):
"""QtWebKit implementations related to zooming."""
+ _widget: webview.WebView
+
def _set_factor_internal(self, factor):
self._widget.setZoomFactor(factor)
@@ -520,6 +537,8 @@ class WebKitScroller(browsertab.AbstractScroller):
# FIXME:qtwebengine When to use the main frame, when the current one?
+ _widget: webview.WebView
+
def pos_px(self):
return self._widget.page().mainFrame().scrollPosition()
@@ -619,6 +638,8 @@ class WebKitHistoryPrivate(browsertab.AbstractHistoryPrivate):
"""History-related methods which are not part of the extension API."""
+ _history: QWebHistory
+
def __init__(self, tab: 'WebKitTab') -> None:
self._tab = tab
self._history = cast(QWebHistory, None)
@@ -690,6 +711,7 @@ class WebKitElements(browsertab.AbstractElements):
"""QtWebKit implementations related to elements on the page."""
_tab: 'WebKitTab'
+ _widget: webview.WebView
def find_css(self, selector, callback, error_cb, *, only_visible=False):
utils.unused(error_cb)
@@ -725,7 +747,7 @@ class WebKitElements(browsertab.AbstractElements):
self.find_css('#' + elem_id, find_id_cb, error_cb=lambda exc: None)
def find_focused(self, callback):
- frame = self._widget.page().currentFrame()
+ frame = cast(Optional[QWebFrame], self._widget.page().currentFrame())
if frame is None:
callback(None)
return
@@ -739,7 +761,7 @@ class WebKitElements(browsertab.AbstractElements):
def find_at_pos(self, pos, callback):
assert pos.x() >= 0
assert pos.y() >= 0
- frame = self._widget.page().frameAt(pos)
+ frame = cast(Optional[QWebFrame], self._widget.page().frameAt(pos))
if frame is None:
# This happens when we click inside the webview, but not actually
# on the QWebPage - for example when clicking the scrollbar
@@ -791,6 +813,8 @@ class WebKitTabPrivate(browsertab.AbstractTabPrivate):
"""QtWebKit-related methods which aren't part of the public API."""
+ _widget: webview.WebView
+
def networkaccessmanager(self):
return self._widget.page().networkAccessManager()
@@ -816,6 +840,8 @@ class WebKitTab(browsertab.AbstractTab):
"""A QtWebKit tab in the browser."""
+ _widget: webview.WebView
+
def __init__(self, *, win_id, mode_manager, private, parent=None):
super().__init__(win_id=win_id,
mode_manager=mode_manager,
@@ -907,6 +933,7 @@ class WebKitTab(browsertab.AbstractTab):
def _on_load_started(self):
super()._on_load_started()
nam = self._widget.page().networkAccessManager()
+ assert isinstance(nam, networkmanager.NetworkManager), nam
nam.netrc_used = False
# Make sure the icon is cleared when navigating to a page without one.
self.icon_changed.emit(QIcon())
@@ -924,7 +951,9 @@ class WebKitTab(browsertab.AbstractTab):
when using error pages... See
https://github.com/qutebrowser/qutebrowser/issues/84
"""
- self._on_load_finished(not self._widget.page().error_occurred)
+ page = self._widget.page()
+ assert isinstance(page, webpage.BrowserPage), page
+ self._on_load_finished(not page.error_occurred)
@pyqtSlot()
def _on_webkit_icon_changed(self):
@@ -979,18 +1008,30 @@ class WebKitTab(browsertab.AbstractTab):
view = self._widget
page = view.page()
frame = page.mainFrame()
- page.windowCloseRequested.connect(self.window_close_requested)
- page.linkHovered.connect(self.link_hovered)
- page.loadProgress.connect(self._on_load_progress)
- frame.loadStarted.connect(self._on_load_started)
+ page.windowCloseRequested.connect( # type: ignore[attr-defined]
+ self.window_close_requested)
+ page.linkHovered.connect( # type: ignore[attr-defined]
+ self.link_hovered)
+ page.loadProgress.connect( # type: ignore[attr-defined]
+ self._on_load_progress)
+ frame.loadStarted.connect( # type: ignore[attr-defined]
+ self._on_load_started)
view.scroll_pos_changed.connect(self.scroller.perc_changed)
- view.titleChanged.connect(self.title_changed)
- view.urlChanged.connect(self._on_url_changed)
+ view.titleChanged.connect( # type: ignore[attr-defined]
+ self.title_changed)
+ view.urlChanged.connect( # type: ignore[attr-defined]
+ self._on_url_changed)
view.shutting_down.connect(self.shutting_down)
page.networkAccessManager().sslErrors.connect(self._on_ssl_errors)
- frame.loadFinished.connect(self._on_frame_load_finished)
- view.iconChanged.connect(self._on_webkit_icon_changed)
- page.frameCreated.connect(self._on_frame_created)
- frame.contentsSizeChanged.connect(self._on_contents_size_changed)
- frame.initialLayoutCompleted.connect(self._on_history_trigger)
- page.navigation_request.connect(self._on_navigation_request)
+ frame.loadFinished.connect( # type: ignore[attr-defined]
+ self._on_frame_load_finished)
+ view.iconChanged.connect( # type: ignore[attr-defined]
+ self._on_webkit_icon_changed)
+ page.frameCreated.connect( # type: ignore[attr-defined]
+ self._on_frame_created)
+ frame.contentsSizeChanged.connect( # type: ignore[attr-defined]
+ self._on_contents_size_changed)
+ frame.initialLayoutCompleted.connect( # type: ignore[attr-defined]
+ self._on_history_trigger)
+ page.navigation_request.connect( # type: ignore[attr-defined]
+ self._on_navigation_request)
diff --git a/qutebrowser/browser/webkit/webpage.py b/qutebrowser/browser/webkit/webpage.py
index ddbd78de2..9c7495478 100644
--- a/qutebrowser/browser/webkit/webpage.py
+++ b/qutebrowser/browser/webkit/webpage.py
@@ -21,7 +21,6 @@
import html
import functools
-from typing import cast
from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QUrl, QPoint
from PyQt5.QtGui import QDesktopServices
@@ -241,6 +240,7 @@ class BrowserPage(QWebPage):
if download_manager.has_downloads_with_nam(nam):
nam.setParent(download_manager)
else:
+ assert isinstance(nam, networkmanager.NetworkManager), nam
nam.shutdown()
def display_content(self, reply, mimetype):
@@ -371,11 +371,10 @@ class BrowserPage(QWebPage):
self.setFeaturePermission, frame, feature,
QWebPage.PermissionDeniedByUser)
- url = frame.url().adjusted(cast(QUrl.FormattingOptions,
- QUrl.RemoveUserInfo |
- QUrl.RemovePath |
- QUrl.RemoveQuery |
- QUrl.RemoveFragment))
+ url = frame.url().adjusted(QUrl.RemoveUserInfo | # type: ignore[operator]
+ QUrl.RemovePath |
+ QUrl.RemoveQuery |
+ QUrl.RemoveFragment)
question = shared.feature_permission(
url=url,
option=options[feature], msg=messages[feature],
diff --git a/qutebrowser/browser/webkit/webview.py b/qutebrowser/browser/webkit/webview.py
index 0242bed0c..be087c855 100644
--- a/qutebrowser/browser/webkit/webview.py
+++ b/qutebrowser/browser/webkit/webview.py
@@ -109,7 +109,9 @@ class WebView(QWebView):
settings = self.settings()
settings.setAttribute(QWebSettings.JavascriptEnabled, False)
self.stop()
- self.page().shutdown()
+ page = self.page()
+ assert isinstance(page, webpage.BrowserPage), page
+ page.shutdown()
def createWindow(self, wintype):
"""Called by Qt when a page wants to create a new window.
@@ -152,9 +154,6 @@ class WebView(QWebView):
Args:
e: The QPaintEvent.
-
- Return:
- The superclass event return value.
"""
frame = self.page().mainFrame()
new_pos = (frame.scrollBarValue(Qt.Horizontal),
@@ -186,9 +185,6 @@ class WebView(QWebView):
Args:
e: The QShowEvent.
-
- Return:
- The superclass event return value.
"""
super().showEvent(e)
self.page().setVisibilityState(QWebPage.VisibilityStateVisible)
@@ -198,9 +194,6 @@ class WebView(QWebView):
Args:
e: The QHideEvent.
-
- Return:
- The superclass event return value.
"""
super().hideEvent(e)
self.page().setVisibilityState(QWebPage.VisibilityStateHidden)
diff --git a/qutebrowser/commands/argparser.py b/qutebrowser/commands/argparser.py
index f8f083b72..2a11589f9 100644
--- a/qutebrowser/commands/argparser.py
+++ b/qutebrowser/commands/argparser.py
@@ -101,7 +101,7 @@ def type_conv(param, typ, value, *, str_choices=None):
Args:
param: The argparse.Parameter we're checking
- types: The allowed type
+ typ: The allowed type
value: The value to convert
str_choices: The allowed choices if the type ends up being a string
diff --git a/qutebrowser/commands/cmdexc.py b/qutebrowser/commands/cmdexc.py
index fdd06537f..50488f1e5 100644
--- a/qutebrowser/commands/cmdexc.py
+++ b/qutebrowser/commands/cmdexc.py
@@ -22,6 +22,9 @@
Defined here to avoid circular dependency hell.
"""
+from typing import List
+import difflib
+
class Error(Exception):
@@ -32,6 +35,24 @@ class NoSuchCommandError(Error):
"""Raised when a command isn't found."""
+ @classmethod
+ def for_cmd(cls, cmd: str, all_commands: List[str] = None) -> "NoSuchCommandError":
+ """Raise an exception for the given command."""
+ suffix = ''
+ if all_commands:
+ matches = difflib.get_close_matches(cmd, all_commands, n=1)
+ if matches:
+ suffix = f' (did you mean :{matches[0]}?)'
+ return cls(f"{cmd}: no such command{suffix}")
+
+
+class EmptyCommandError(NoSuchCommandError):
+
+ """Raised when no command was given."""
+
+ def __init__(self):
+ super().__init__("No command given")
+
class ArgumentTypeError(Error):
diff --git a/qutebrowser/commands/command.py b/qutebrowser/commands/command.py
index 653f551ff..eee5b7cde 100644
--- a/qutebrowser/commands/command.py
+++ b/qutebrowser/commands/command.py
@@ -272,11 +272,10 @@ class Command:
if is_bool:
kwargs['action'] = 'store_true'
+ elif arg_info.metavar is not None:
+ kwargs['metavar'] = arg_info.metavar
else:
- if arg_info.metavar is not None:
- kwargs['metavar'] = arg_info.metavar
- else:
- kwargs['metavar'] = argparser.arg_name(param.name)
+ kwargs['metavar'] = argparser.arg_name(param.name)
if param.kind == inspect.Parameter.VAR_POSITIONAL:
kwargs['nargs'] = '*' if self._star_args_optional else '+'
@@ -320,9 +319,8 @@ class Command:
self.opt_args[param.name] = long_flag, short_flag
if not is_bool:
self.flags_with_args += [short_flag, long_flag]
- else:
- if not arg_info.hide:
- self.pos_args.append((param.name, name))
+ elif not arg_info.hide:
+ self.pos_args.append((param.name, name))
return args
def _get_type(self, param):
diff --git a/qutebrowser/commands/parser.py b/qutebrowser/commands/parser.py
index 06a20cdf6..5ef46f5e5 100644
--- a/qutebrowser/commands/parser.py
+++ b/qutebrowser/commands/parser.py
@@ -43,10 +43,18 @@ class CommandParser:
Attributes:
_partial_match: Whether to allow partial command matches.
+ _find_similar: Whether to find similar matches on unknown commands.
+ If we use this for completion, errors are not shown in the UI,
+ so we don't need to search.
"""
- def __init__(self, partial_match: bool = False) -> None:
+ def __init__(
+ self,
+ partial_match: bool = False,
+ find_similar: bool = False,
+ ) -> None:
self._partial_match = partial_match
+ self._find_similar = find_similar
def _get_alias(self, text: str, *, default: str) -> str:
"""Get an alias from the config.
@@ -95,7 +103,7 @@ class CommandParser:
"""
text = text.strip().lstrip(':').strip()
if not text:
- raise cmdexc.NoSuchCommandError("No command given")
+ raise cmdexc.EmptyCommandError
if aliases:
text = self._get_alias(text, default=text)
@@ -128,7 +136,7 @@ class CommandParser:
cmdstr, sep, argstr = text.partition(' ')
if not cmdstr:
- raise cmdexc.NoSuchCommandError("No command given")
+ raise cmdexc.EmptyCommandError
if self._partial_match:
cmdstr = self._completion_match(cmdstr)
@@ -136,7 +144,10 @@ class CommandParser:
try:
cmd = objects.commands[cmdstr]
except KeyError:
- raise cmdexc.NoSuchCommandError(f'{cmdstr}: no such command')
+ raise cmdexc.NoSuchCommandError.for_cmd(
+ cmdstr,
+ all_commands=list(objects.commands) if self._find_similar else [],
+ )
args = self._split_args(cmd, argstr, keep)
if keep and args:
diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py
index 5fb054455..e3cd0cc97 100644
--- a/qutebrowser/commands/runners.py
+++ b/qutebrowser/commands/runners.py
@@ -138,9 +138,12 @@ class CommandRunner(AbstractCommandRunner):
_win_id: The window this CommandRunner is associated with.
"""
- def __init__(self, win_id, partial_match=False, parent=None):
+ def __init__(self, win_id, partial_match=False, find_similar=True, parent=None):
super().__init__(parent)
- self._parser = parser.CommandParser(partial_match=partial_match)
+ self._parser = parser.CommandParser(
+ partial_match=partial_match,
+ find_similar=find_similar,
+ )
self._win_id = win_id
@contextlib.contextmanager
diff --git a/qutebrowser/commands/userscripts.py b/qutebrowser/commands/userscripts.py
index 70c639207..c776324ac 100644
--- a/qutebrowser/commands/userscripts.py
+++ b/qutebrowser/commands/userscripts.py
@@ -63,8 +63,7 @@ class _QtFIFOReader(QObject):
self._fifo = os.fdopen(fd, 'r')
self._notifier = QSocketNotifier(cast(sip.voidptr, fd),
QSocketNotifier.Read, self)
- self._notifier.activated.connect( # type: ignore[attr-defined]
- self.read_line)
+ self._notifier.activated.connect(self.read_line)
@pyqtSlot()
def read_line(self):
@@ -342,9 +341,8 @@ class _WindowsUserscriptRunner(_BaseUserscriptRunner):
self._kwargs = kwargs
try:
- handle = tempfile.NamedTemporaryFile(delete=False)
- handle.close()
- self._filepath = handle.name
+ with tempfile.NamedTemporaryFile(delete=False) as handle:
+ self._filepath = handle.name
except OSError as e:
message.error("Error while creating tempfile: {}".format(e))
return
diff --git a/qutebrowser/completion/completer.py b/qutebrowser/completion/completer.py
index 778333854..cf6984288 100644
--- a/qutebrowser/completion/completer.py
+++ b/qutebrowser/completion/completer.py
@@ -29,6 +29,7 @@ from qutebrowser.commands import parser, cmdexc
from qutebrowser.misc import objects, split
from qutebrowser.utils import log, utils, debug, objreg
from qutebrowser.completion.models import miscmodels
+from qutebrowser.completion import completionwidget
if TYPE_CHECKING:
from qutebrowser.browser import browsertab
@@ -39,7 +40,8 @@ class CompletionInfo:
"""Context passed into all completion functions."""
config: config.Config
- keyconf: config.KeyConfig
+ # pylint: disable-next=used-before-assignment
+ keyconf: config.KeyConfig # type: ignore[name-defined]
win_id: int
cur_tab: 'browsertab.AbstractTab'
@@ -74,10 +76,15 @@ class Completer(QObject):
def __repr__(self):
return utils.get_repr(self)
+ def _completion(self) -> completionwidget.CompletionView:
+ """Convenience method to get the current completion."""
+ completion = self.parent()
+ assert isinstance(completion, completionwidget.CompletionView), completion
+ return completion
+
def _model(self):
"""Convenience method to get the current completion model."""
- completion = self.parent()
- return completion.model()
+ return self._completion().model()
def _get_new_completion(self, before_cursor, under_cursor):
"""Get the completion function based on the current command text.
@@ -158,6 +165,7 @@ class Completer(QObject):
# cursor is in a space between two existing words
parts.insert(i, '')
prefix = [x.strip() for x in parts[:i]]
+ # pylint: disable-next=unnecessary-list-index-lookup
center = parts[i].strip()
# strip trailing whitespace included as a separate token
postfix = [x.strip() for x in parts[i+1:] if not x.isspace()]
@@ -230,7 +238,7 @@ class Completer(QObject):
@pyqtSlot()
def _update_completion(self):
"""Check if completions are available and activate them."""
- completion = self.parent()
+ completion = self._completion()
if self._cmd.prefix() != ':':
# This is a search or gibberish, so we don't need to complete
diff --git a/qutebrowser/completion/completiondelegate.py b/qutebrowser/completion/completiondelegate.py
index 9ea82e876..ae02426a5 100644
--- a/qutebrowser/completion/completiondelegate.py
+++ b/qutebrowser/completion/completiondelegate.py
@@ -33,6 +33,7 @@ from PyQt5.QtGui import (QIcon, QPalette, QTextDocument, QTextOption,
from qutebrowser.config import config
from qutebrowser.utils import qtutils
+from qutebrowser.completion import completionwidget
class _Highlighter(QSyntaxHighlighter):
@@ -233,6 +234,7 @@ class CompletionItemDelegate(QStyledItemDelegate):
if index.parent().isValid():
view = self.parent()
+ assert isinstance(view, completionwidget.CompletionView), view
pattern = view.pattern
columns_to_filter = index.model().columns_to_filter(index)
if index.column() in columns_to_filter and pattern:
@@ -299,7 +301,7 @@ class CompletionItemDelegate(QStyledItemDelegate):
size = self._style.sizeFromContents(QStyle.CT_ItemViewItem, self._opt,
docsize, self._opt.widget)
qtutils.ensure_valid(size)
- return size + QSize(10, 3) # type: ignore[operator]
+ return size + QSize(10, 3)
def paint(self, painter, option, index):
"""Override the QStyledItemDelegate paint function.
diff --git a/qutebrowser/completion/completionwidget.py b/qutebrowser/completion/completionwidget.py
index dd8e267fe..de9100a2e 100644
--- a/qutebrowser/completion/completionwidget.py
+++ b/qutebrowser/completion/completionwidget.py
@@ -30,6 +30,7 @@ from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QItemSelectionModel, QSize
from qutebrowser.config import config, stylesheet
from qutebrowser.completion import completiondelegate
+from qutebrowser.completion.models import completionmodel
from qutebrowser.utils import utils, usertypes, debug, log, qtutils
from qutebrowser.api import cmdutils
if TYPE_CHECKING:
@@ -148,6 +149,15 @@ class CompletionView(QTreeView):
def __repr__(self):
return utils.get_repr(self)
+ def _model(self) -> completionmodel.CompletionModel:
+ """Get the current completion model.
+
+ Ensures the model is not None.
+ """
+ model = self.model()
+ assert isinstance(model, completionmodel.CompletionModel), model
+ return model
+
@pyqtSlot(str)
def _on_config_changed(self, option):
if option in ['completion.height', 'completion.shrink']:
@@ -158,7 +168,7 @@ class CompletionView(QTreeView):
if self.model() is None:
return
width = self.size().width()
- column_widths = self.model().column_widths
+ column_widths = self._model().column_widths
pixel_widths = [(width * perc // 100) for perc in column_widths]
delta = self.verticalScrollBar().sizeHint().width()
@@ -182,21 +192,22 @@ class CompletionView(QTreeView):
Return:
A QModelIndex.
"""
+ model = self._model()
idx = self.selectionModel().currentIndex()
if not idx.isValid():
# No item selected yet
if upwards:
- return self.model().last_item()
+ return model.last_item()
else:
- return self.model().first_item()
+ return model.first_item()
while True:
idx = self.indexAbove(idx) if upwards else self.indexBelow(idx)
# wrap around if we arrived at beginning/end
if not idx.isValid() and upwards:
- return self.model().last_item()
+ return model.last_item()
elif not idx.isValid() and not upwards:
- idx = self.model().first_item()
+ idx = model.first_item()
self.scrollTo(idx.parent())
return idx
elif idx.parent().isValid():
@@ -216,7 +227,7 @@ class CompletionView(QTreeView):
"""
old_idx = self.selectionModel().currentIndex()
idx = old_idx
- model = self.model()
+ model = self._model()
if not idx.isValid():
# No item selected yet
@@ -259,6 +270,7 @@ class CompletionView(QTreeView):
A QModelIndex.
"""
idx = self.selectionModel().currentIndex()
+ model = self._model()
if not idx.isValid():
return self._next_idx(upwards).sibling(0, 0)
idx = idx.parent()
@@ -267,10 +279,10 @@ class CompletionView(QTreeView):
idx = idx.sibling(idx.row() + direction, 0)
if not idx.isValid() and upwards:
# wrap around to the first item of the last category
- return self.model().last_item().sibling(0, 0)
+ return model.last_item().sibling(0, 0)
elif not idx.isValid() and not upwards:
# wrap around to the first item of the first category
- idx = self.model().first_item()
+ idx = model.first_item()
self.scrollTo(idx.parent())
return idx
elif idx.isValid() and idx.child(0, 0).isValid():
@@ -327,7 +339,7 @@ class CompletionView(QTreeView):
selmodel.setCurrentIndex(
idx,
- QItemSelectionModel.ClearAndSelect | # type: ignore[arg-type]
+ QItemSelectionModel.ClearAndSelect |
QItemSelectionModel.Rows)
# if the last item is focused, try to fetch more
@@ -335,7 +347,7 @@ class CompletionView(QTreeView):
if not self.visualRect(next_idx).isValid():
self.expandAll()
- count = self.model().count()
+ count = self._model().count()
if count == 0:
self.hide()
elif count == 1 and config.val.completion.quick:
@@ -382,14 +394,14 @@ class CompletionView(QTreeView):
return
self.pattern = pattern
with debug.log_time(log.completion, 'Set pattern {}'.format(pattern)):
- self.model().set_pattern(pattern)
+ self._model().set_pattern(pattern)
self.selectionModel().clear()
self._maybe_update_geometry()
self._maybe_show()
def _maybe_show(self):
if (config.val.completion.show == 'always' and
- self.model().count() > 0):
+ self._model().count() > 0):
self.show()
else:
self.hide()
@@ -435,7 +447,7 @@ class CompletionView(QTreeView):
indexes = selected.indexes()
if not indexes:
return
- data = str(self.model().data(indexes[0]))
+ data = str(self._model().data(indexes[0]))
self.selection_changed.emit(data)
def resizeEvent(self, e):
@@ -458,7 +470,7 @@ class CompletionView(QTreeView):
index = self.currentIndex()
if not index.isValid():
raise cmdutils.CommandError("No item selected!")
- self.model().delete_cur_item(index)
+ self._model().delete_cur_item(index)
@cmdutils.register(instance='completion',
modes=[usertypes.KeyMode.command], scope='window')
@@ -473,7 +485,7 @@ class CompletionView(QTreeView):
index = self.currentIndex()
if not index.isValid():
raise cmdutils.CommandError("No item selected!")
- text = self.model().data(index)
+ text = self._model().data(index)
if not utils.supports_selection():
sel = False
diff --git a/qutebrowser/completion/models/completionmodel.py b/qutebrowser/completion/models/completionmodel.py
index 81f4bba8e..4b8fee299 100644
--- a/qutebrowser/completion/models/completionmodel.py
+++ b/qutebrowser/completion/models/completionmodel.py
@@ -70,6 +70,7 @@ class CompletionModel(QAbstractItemModel):
Args:
index: The QModelIndex to get item flags for.
+ role: The Qt ItemRole to get the data for.
Return: The item data, or None on an invalid index.
"""
@@ -179,10 +180,11 @@ class CompletionModel(QAbstractItemModel):
pattern: The filter pattern to set.
"""
log.completion.debug("Setting completion pattern '{}'".format(pattern))
- self.layoutAboutToBeChanged.emit() # type: ignore[attr-defined]
+ self.layoutAboutToBeChanged.emit()
for cat in self._categories:
- cat.set_pattern(pattern)
- self.layoutChanged.emit() # type: ignore[attr-defined]
+ # FIXME:mypy define a Protocol for set_pattern?
+ cat.set_pattern(pattern) # type: ignore[attr-defined]
+ self.layoutChanged.emit()
def first_item(self):
"""Return the index of the first child (non-category) in the model."""
diff --git a/qutebrowser/completion/models/configmodel.py b/qutebrowser/completion/models/configmodel.py
index 736d09644..6c85fbb29 100644
--- a/qutebrowser/completion/models/configmodel.py
+++ b/qutebrowser/completion/models/configmodel.py
@@ -44,16 +44,22 @@ def customized_option(*, info):
def list_option(*, info):
"""A CompletionModel filled with settings whose values are lists."""
- predicate = lambda opt: (isinstance(info.config.get_obj(opt.name),
- list) and not opt.no_autoconfig)
- return _option(info, "List options", predicate)
+ return _option(
+ info,
+ "List options",
+ lambda opt: (isinstance(info.config.get_obj(opt.name), list) and
+ not opt.no_autoconfig)
+ )
def dict_option(*, info):
"""A CompletionModel filled with settings whose values are dicts."""
- predicate = lambda opt: (isinstance(info.config.get_obj(opt.name),
- dict) and not opt.no_autoconfig)
- return _option(info, "Dict options", predicate)
+ return _option(
+ info,
+ "Dict options",
+ lambda opt: (isinstance(info.config.get_obj(opt.name), dict) and
+ not opt.no_autoconfig)
+ )
def _option(info, title, predicate):
@@ -78,7 +84,7 @@ def value(optname, *values, info):
Args:
optname: The name of the config option this model shows.
- values: The values already provided on the command line.
+ *values: The values already provided on the command line.
info: A CompletionInfo instance.
"""
model = completionmodel.CompletionModel(column_widths=(30, 70, 0))
@@ -137,6 +143,7 @@ def bind(key, *, info):
Args:
key: the key being bound.
+ info: A CompletionInfo instance.
"""
model = completionmodel.CompletionModel(column_widths=(20, 60, 20))
data = _bind_current_default(key, info)
diff --git a/qutebrowser/completion/models/filepathcategory.py b/qutebrowser/completion/models/filepathcategory.py
index b7d74f57a..fd2cb58ce 100644
--- a/qutebrowser/completion/models/filepathcategory.py
+++ b/qutebrowser/completion/models/filepathcategory.py
@@ -65,7 +65,7 @@ class FilePathCategory(QAbstractListModel):
try:
return glob.glob(glob.escape(val) + '*')
except ValueError as e: # pragma: no cover
- # e.g. "embedded null byte" with \x00 on Python 3.6 and 3.7
+ # e.g. "embedded null byte" with \x00 on Python 3.7
log.completion.debug(f"Failed to glob: {e}")
return []
diff --git a/qutebrowser/completion/models/miscmodels.py b/qutebrowser/completion/models/miscmodels.py
index 7642bf904..77072c720 100644
--- a/qutebrowser/completion/models/miscmodels.py
+++ b/qutebrowser/completion/models/miscmodels.py
@@ -103,17 +103,23 @@ def session(*, info=None):
return model
-def _tabs(*, win_id_filter=lambda _win_id: True, add_win_id=True):
+def _tabs(*, win_id_filter=lambda _win_id: True, add_win_id=True, cur_win_id=None):
"""Helper to get the completion model for tabs/other_tabs.
Args:
win_id_filter: A filter function for window IDs to include.
Should return True for all included windows.
add_win_id: Whether to add the window ID to the completion items.
+ cur_win_id: Window ID to be passed from info.win_id
"""
def delete_tab(data):
"""Close the selected tab."""
- win_id, tab_index = data[0].split('/')
+ if cur_win_id is None:
+ win_id, tab_index = data[0].split('/')
+ else:
+ win_id = cur_win_id
+ tab_index = data[0]
+
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=int(win_id))
tabbed_browser.on_tab_close_requested(int(tab_index) - 1)
@@ -177,13 +183,15 @@ def other_tabs(*, info):
Used for the tab-take command.
"""
- return _tabs(win_id_filter=lambda win_id: win_id != info.win_id)
+ return _tabs(
+ win_id_filter=lambda win_id: win_id != info.win_id,
+ cur_win_id=info.win_id)
def tab_focus(*, info):
"""A model to complete on open tabs in the current window."""
model = _tabs(win_id_filter=lambda win_id: win_id == info.win_id,
- add_win_id=False)
+ add_win_id=False, cur_win_id=info.win_id)
special = [
("last", "Focus the last-focused tab"),
@@ -230,9 +238,7 @@ def _qdatetime_to_completion_format(qdate):
if not qdate.isValid():
ts = 0
else:
- ts = qdate.toMSecsSinceEpoch()
- if ts < 0:
- ts = 0
+ ts = max(qdate.toMSecsSinceEpoch(), 0)
pydate = datetime.datetime.fromtimestamp(ts / 1000)
return pydate.strftime(config.val.completion.timestamp_format)
diff --git a/qutebrowser/components/braveadblock.py b/qutebrowser/components/braveadblock.py
index 21319cb1b..977aa3ef2 100644
--- a/qutebrowser/components/braveadblock.py
+++ b/qutebrowser/components/braveadblock.py
@@ -175,11 +175,12 @@ class BraveAdBlocker:
hasattr(adblock, "__file__")):
proc = subprocess.run(
['pacman', '-Qo', adblock.__file__],
- stdout=subprocess.PIPE,
- universal_newlines=True,
+ capture_output=True,
+ text=True,
check=False,
)
logger.debug(proc.stdout)
+ logger.debug(proc.stderr)
raise
def _is_blocked(
@@ -266,14 +267,13 @@ class BraveAdBlocker:
except DeserializationError:
message.error("Reading adblock filter data failed (corrupted data?). "
"Please run :adblock-update.")
- else:
- if (
- config.val.content.blocking.adblock.lists
- and not self._has_basedir
- and config.val.content.blocking.enabled
- and self.enabled
- ):
- message.info("Run :adblock-update to get adblock lists.")
+ elif (
+ config.val.content.blocking.adblock.lists
+ and not self._has_basedir
+ and config.val.content.blocking.enabled
+ and self.enabled
+ ):
+ message.info("Run :adblock-update to get adblock lists.")
def adblock_update(self) -> blockutils.BlocklistDownloads:
"""Update the adblock block lists."""
diff --git a/qutebrowser/components/hostblock.py b/qutebrowser/components/hostblock.py
index 2d6086245..191719f10 100644
--- a/qutebrowser/components/hostblock.py
+++ b/qutebrowser/components/hostblock.py
@@ -64,9 +64,10 @@ def get_fileobj(byte_io: IO[bytes]) -> IO[bytes]:
byte_io.seek(0) # rewind downloaded file
if zipfile.is_zipfile(byte_io):
byte_io.seek(0) # rewind what zipfile.is_zipfile did
- zf = zipfile.ZipFile(byte_io)
- filename = _guess_zip_filename(zf)
- byte_io = zf.open(filename, mode="r")
+ with zipfile.ZipFile(byte_io) as zf:
+ filename = _guess_zip_filename(zf)
+ # pylint: disable=consider-using-with
+ byte_io = zf.open(filename, mode="r")
else:
byte_io.seek(0) # rewind what zipfile.is_zipfile did
return byte_io
@@ -132,10 +133,16 @@ class HostBlocker:
host = request_url.host()
- return any(
- hostname in self._blocked_hosts or hostname in self._config_blocked_hosts
- for hostname in urlutils.widened_hostnames(host)
- )
+ if config.get("content.blocking.hosts.block_subdomains"):
+ return any(
+ hostname in self._blocked_hosts
+ or hostname in self._config_blocked_hosts
+ for hostname in urlutils.widened_hostnames(host)
+ )
+ else:
+ return (
+ host in self._blocked_hosts or host in self._config_blocked_hosts
+ )
def filter_request(self, info: interceptor.Request) -> None:
"""Block the given request if necessary."""
diff --git a/qutebrowser/components/misccommands.py b/qutebrowser/components/misccommands.py
index 120806bfe..53c057e26 100644
--- a/qutebrowser/components/misccommands.py
+++ b/qutebrowser/components/misccommands.py
@@ -17,6 +17,9 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>.
+# To allow count being documented
+# pylint: disable=differing-param-doc
+
"""Various commands."""
import os
@@ -24,7 +27,7 @@ import signal
import functools
import logging
import pathlib
-from typing import Optional
+from typing import Optional, List, Callable, cast
try:
import hunter
@@ -80,7 +83,7 @@ def _print_preview(tab: apitypes.Tab) -> None:
diag = QPrintPreviewDialog(tab)
diag.setAttribute(Qt.WA_DeleteOnClose)
diag.setWindowFlags(
- diag.windowFlags() | # type: ignore[operator, arg-type]
+ diag.windowFlags() |
Qt.WindowMaximizeButtonHint |
Qt.WindowMinimizeButtonHint)
diag.paintRequested.connect(functools.partial(
@@ -183,7 +186,10 @@ def screenshot(
raise cmdutils.CommandError(
f"File {filename} already exists (use --force to overwrite)")
- qrect = None if rect is None else utils.parse_rect(rect)
+ try:
+ qrect = None if rect is None else utils.parse_rect(rect)
+ except ValueError as e:
+ raise cmdutils.CommandError(str(e))
pic = tab.grab_pixmap(qrect)
if pic is None:
@@ -217,13 +223,33 @@ def insert_text(tab: apitypes.Tab, text: str) -> None:
tab.elements.find_focused(_insert_text_cb)
+def _wrap_find_at_pos(value: str, tab: apitypes.Tab,
+ callback: Callable[[Optional[apitypes.WebElement]], None]
+ ) -> None:
+ try:
+ point = utils.parse_point(value)
+ except ValueError as e:
+ message.error(str(e))
+ return
+ tab.elements.find_at_pos(point, callback)
+
+
+_FILTER_ERRORS = {
+ 'id': lambda x: f'with ID "{x}"',
+ 'css': lambda x: f'matching CSS selector "{x}"',
+ 'focused': lambda _: 'with focus',
+ 'position': lambda x: 'at position {x}',
+}
+
+
@cmdutils.register()
@cmdutils.argument('tab', value=cmdutils.Value.cur_tab)
-@cmdutils.argument('filter_', choices=['id'])
-def click_element(tab: apitypes.Tab, filter_: str, value: str, *,
+@cmdutils.argument('filter_', choices=['id', 'css', 'position', 'focused'])
+def click_element(tab: apitypes.Tab, filter_: str, value: str = None, *,
target: apitypes.ClickTarget =
apitypes.ClickTarget.normal,
- force_event: bool = False) -> None:
+ force_event: bool = False,
+ select_first: bool = False) -> None:
"""Click the element matching the given filter.
The given filter needs to result in exactly one element, otherwise, an
@@ -231,27 +257,55 @@ def click_element(tab: apitypes.Tab, filter_: str, value: str, *,
Args:
filter_: How to filter the elements.
- id: Get an element based on its ID.
- value: The value to filter for.
+
+ - id: Get an element based on its ID.
+ - css: Filter by a CSS selector.
+ - position: Click the element at specified position.
+ Specify `value` as 'x,y'.
+ - focused: Click the currently focused element.
+ value: The value to filter for. Optional for 'focused' filter.
target: How to open the clicked element (normal/tab/tab-bg/window).
force_event: Force generating a fake click event.
+ select_first: Select first matching element if there are multiple.
"""
- def single_cb(elem: Optional[apitypes.WebElement]) -> None:
- """Click a single element."""
- if elem is None:
- message.error("No element found with id {}!".format(value))
- return
+ def do_click(elem: apitypes.WebElement) -> None:
try:
elem.click(target, force_event=force_event)
except apitypes.WebElemError as e:
message.error(str(e))
+
+ def single_cb(elem: Optional[apitypes.WebElement]) -> None:
+ """Click a single element."""
+ if elem is None:
+ message.error(f"No element found {_FILTER_ERRORS[filter_](value)}!")
+ return
+
+ do_click(elem)
+
+ def multiple_cb(elems: List[apitypes.WebElement]) -> None:
+ if not elems:
+ message.error(f"No element found {_FILTER_ERRORS[filter_](value)}!")
+ return
+
+ if not select_first and len(elems) > 1:
+ message.error(f"Multiple elements found {_FILTER_ERRORS[filter_](value)}!")
return
+ do_click(elems[0])
+
+ if value is None and filter_ != 'focused':
+ raise cmdutils.CommandError("Argument 'value' is only "
+ "optional with filter 'focused'!")
+
handlers = {
- 'id': (tab.elements.find_id, single_cb),
+ 'id': lambda: tab.elements.find_id(elem_id=value, callback=single_cb),
+ 'css': lambda:
+ tab.elements.find_css(value, callback=multiple_cb, error_cb=message.error),
+ 'position': lambda:
+ _wrap_find_at_pos(cast(str, value), tab=tab, callback=single_cb),
+ 'focused': lambda: tab.elements.find_focused(callback=single_cb),
}
- handler, callback = handlers[filter_]
- handler(value, callback)
+ handlers[filter_]()
@cmdutils.register(debug=True)
diff --git a/qutebrowser/components/readlinecommands.py b/qutebrowser/components/readlinecommands.py
index da9c515dd..37fa92127 100644
--- a/qutebrowser/components/readlinecommands.py
+++ b/qutebrowser/components/readlinecommands.py
@@ -19,7 +19,8 @@
"""Bridge to provide readline-like shortcuts for QLineEdits."""
-from typing import Iterable, Optional, MutableMapping
+import os
+from typing import Iterable, Optional, MutableMapping, Any, Callable
from PyQt5.QtWidgets import QApplication, QLineEdit
@@ -41,7 +42,7 @@ class _ReadlineBridge:
"""Get the currently active QLineEdit."""
# FIXME add this to api.utils or so
qapp = QApplication.instance()
- assert qapp is not None
+ assert isinstance(qapp, QApplication), qapp
w = qapp.focusWidget()
if isinstance(w, QLineEdit):
@@ -90,8 +91,13 @@ class _ReadlineBridge:
def kill_line(self) -> None:
self._dispatch('end', mark=True, delete=True)
- def _rubout(self, delim: Iterable[str]) -> None:
- """Delete backwards using the characters in delim as boundaries."""
+ def rubout(self, delim: Iterable[str]) -> None:
+ """Delete backwards using the characters in delim as boundaries.
+
+ With delim=[' '], this acts like unix-word-rubout.
+ With delim=[' ', '/'], this acts like unix-filename-rubout.
+ With delim=[os.sep], this serves as a more useful filename-rubout.
+ """
widget = self._widget()
if widget is None:
return
@@ -100,27 +106,65 @@ class _ReadlineBridge:
target_position = cursor_position
+ # First scan any trailing boundaries, e.g.:
+ # /some/path//| -> /some/path[//]
+ # 0 ^ 12 0 ^ 9
+ # (cursor) (target)
is_boundary = True
while is_boundary and target_position > 0:
is_boundary = text[target_position - 1] in delim
target_position -= 1
+ # Then scan anything not a boundary, e.g.
+ # /some/path -> /some/[path//]
+ # 0 ^ 9 0 ^ 5
+ # (old target) (target)
is_boundary = False
while not is_boundary and target_position > 0:
is_boundary = text[target_position - 1] in delim
target_position -= 1
+ # Account for the last remaining character.
+ # With e.g.:
+ #
+ # somepath|
+ # 0 8
+ #
+ # We exit the loop above with cursor_position=8 and target_position=0.
+ # However, we want to *keep* the found boundary usually, thus only
+ # trying to delete 7 chars:
+ #
+ # s[omepath]
+ #
+ # However, that would be wrong: We also want to remove the *initial*
+ # character, if it was not a boundary.
+ # We can't say "target_position >= 0" above, because that'd falsely
+ # check whether text[-1] was a boundary.
+ if not is_boundary:
+ # target_position can never be negative, and if it's > 0, then the
+ # loop above could only have exited because of is_boundary=True,
+ # thus we can only end up here if target_position=0.
+ assert target_position == 0, (text, delim)
+ target_position -= 1
+
+ # Finally, move back as calculated - in the example above:
+ #
+ # vvvvvv---- 12 - 5 - 1 = 6 chars to delete.
+ # /some/[path//]|
+ # ^ 5 ^ 12
+ # (target) (cursor)
+ #
+ # If we have a text without an initial boundary:
+ #
+ # vvvvvvvv---- 8 - (-1) - 1 = 8 chars to delete.
+ # [somepath]|
+ # ^ -1 ^ 8
+ # (target) (cursor)
moveby = cursor_position - target_position - 1
widget.cursorBackward(True, moveby)
self._deleted[widget] = widget.selectedText()
widget.del_()
- def unix_word_rubout(self) -> None:
- self._rubout([' '])
-
- def unix_filename_rubout(self) -> None:
- self._rubout([' ', '/'])
-
def backward_kill_word(self) -> None:
self._dispatch('cursorWordBackward', mark=True, delete=True)
@@ -142,11 +186,15 @@ class _ReadlineBridge:
bridge = _ReadlineBridge()
-_register = cmdutils.register(
- modes=[cmdutils.KeyMode.command, cmdutils.KeyMode.prompt])
-@_register
+def _register(**kwargs: Any) -> Callable[..., Any]:
+ return cmdutils.register(
+ modes=[cmdutils.KeyMode.command, cmdutils.KeyMode.prompt],
+ **kwargs)
+
+
+@_register()
def rl_backward_char() -> None:
"""Move back a character.
@@ -155,7 +203,7 @@ def rl_backward_char() -> None:
bridge.backward_char()
-@_register
+@_register()
def rl_forward_char() -> None:
"""Move forward a character.
@@ -164,7 +212,7 @@ def rl_forward_char() -> None:
bridge.forward_char()
-@_register
+@_register()
def rl_backward_word() -> None:
"""Move back to the start of the current or previous word.
@@ -173,7 +221,7 @@ def rl_backward_word() -> None:
bridge.backward_word()
-@_register
+@_register()
def rl_forward_word() -> None:
"""Move forward to the end of the next word.
@@ -182,7 +230,7 @@ def rl_forward_word() -> None:
bridge.forward_word()
-@_register
+@_register()
def rl_beginning_of_line() -> None:
"""Move to the start of the line.
@@ -191,7 +239,7 @@ def rl_beginning_of_line() -> None:
bridge.beginning_of_line()
-@_register
+@_register()
def rl_end_of_line() -> None:
"""Move to the end of the line.
@@ -200,7 +248,7 @@ def rl_end_of_line() -> None:
bridge.end_of_line()
-@_register
+@_register()
def rl_unix_line_discard() -> None:
"""Remove chars backward from the cursor to the beginning of the line.
@@ -209,7 +257,7 @@ def rl_unix_line_discard() -> None:
bridge.unix_line_discard()
-@_register
+@_register()
def rl_kill_line() -> None:
"""Remove chars from the cursor to the end of the line.
@@ -218,26 +266,57 @@ def rl_kill_line() -> None:
bridge.kill_line()
-@_register
+@_register(deprecated="Use :rl-rubout ' ' instead.")
def rl_unix_word_rubout() -> None:
"""Remove chars from the cursor to the beginning of the word.
This acts like readline's unix-word-rubout. Whitespace is used as a
word delimiter.
"""
- bridge.unix_word_rubout()
+ bridge.rubout([" "])
-@_register
+@_register(
+ deprecated='Use :rl-filename-rubout or :rl-rubout " /" instead '
+ '(see their `:help` for details).'
+)
def rl_unix_filename_rubout() -> None:
"""Remove chars from the cursor to the previous path separator.
This acts like readline's unix-filename-rubout.
"""
- bridge.unix_filename_rubout()
+ bridge.rubout([" ", "/"])
+
+
+@_register()
+def rl_rubout(delim: str) -> None:
+ r"""Delete backwards using the given characters as boundaries.
+
+ With " ", this acts like readline's `unix-word-rubout`.
+
+ With " /", this acts like readline's `unix-filename-rubout`, but consider
+ using `:rl-filename-rubout` instead: It uses the OS path separator (i.e. `\`
+ on Windows) and ignores spaces.
+
+ Args:
+ delim: A string of characters (or a single character) until which text
+ will be deleted.
+ """
+ bridge.rubout(list(delim))
+
+
+@_register()
+def rl_filename_rubout() -> None:
+ r"""Delete backwards using the OS path separator as boundary.
+
+ For behavior that matches readline's `unix-filename-rubout` exactly, use
+ `:rl-rubout "/ "` instead. This command uses the OS path separator (i.e.
+ `\` on Windows) and ignores spaces.
+ """
+ bridge.rubout(os.sep)
-@_register
+@_register()
def rl_backward_kill_word() -> None:
"""Remove chars from the cursor to the beginning of the word.
@@ -247,7 +326,7 @@ def rl_backward_kill_word() -> None:
bridge.backward_kill_word()
-@_register
+@_register()
def rl_kill_word() -> None:
"""Remove chars from the cursor to the end of the current word.
@@ -256,7 +335,7 @@ def rl_kill_word() -> None:
bridge.kill_word()
-@_register
+@_register()
def rl_yank() -> None:
"""Paste the most recently deleted text.
@@ -265,7 +344,7 @@ def rl_yank() -> None:
bridge.yank()
-@_register
+@_register()
def rl_delete_char() -> None:
"""Delete the character after the cursor.
@@ -274,7 +353,7 @@ def rl_delete_char() -> None:
bridge.delete_char()
-@_register
+@_register()
def rl_backward_delete_char() -> None:
"""Delete the character before the cursor.
diff --git a/qutebrowser/components/scrollcommands.py b/qutebrowser/components/scrollcommands.py
index 757c23b2b..2287041d7 100644
--- a/qutebrowser/components/scrollcommands.py
+++ b/qutebrowser/components/scrollcommands.py
@@ -19,6 +19,7 @@
"""Scrolling-related commands."""
+from typing import Dict, Callable
from qutebrowser.api import cmdutils, apitypes
@@ -54,7 +55,8 @@ def scroll(tab: apitypes.Tab, direction: str, count: int = 1) -> None:
(up/down/left/right/top/bottom).
count: multiplier
"""
- funcs = {
+ # FIXME:mypy Use a callback protocol to enforce having 'count'?
+ funcs: Dict[str, Callable[..., None]] = {
'up': tab.scroller.up,
'down': tab.scroller.down,
'left': tab.scroller.left,
diff --git a/qutebrowser/components/utils/blockutils.py b/qutebrowser/components/utils/blockutils.py
index bd27baece..98681a488 100644
--- a/qutebrowser/components/utils/blockutils.py
+++ b/qutebrowser/components/utils/blockutils.py
@@ -125,7 +125,7 @@ class BlocklistDownloads(QObject):
filename: path to a local file to import.
"""
try:
- fileobj = open(filename, "rb")
+ fileobj = open(filename, "rb") # pylint: disable=consider-using-with
except OSError as e:
message.error(
"blockutils: Error while reading {}: {}".format(filename, e.strerror)
diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py
index 437a54a33..918c9b894 100644
--- a/qutebrowser/config/config.py
+++ b/qutebrowser/config/config.py
@@ -373,7 +373,11 @@ class Config(QObject):
deleted = name in configdata.MIGRATIONS.deleted
renamed = configdata.MIGRATIONS.renamed.get(name)
exception = configexc.NoOptionError(
- name, deleted=deleted, renamed=renamed)
+ name,
+ deleted=deleted,
+ renamed=renamed,
+ all_names=list(configdata.DATA),
+ )
raise exception from None
def ensure_has_opt(self, name: str) -> None:
@@ -387,6 +391,8 @@ class Config(QObject):
"""Get the given setting converted for Python code.
Args:
+ name: The name of the setting to get.
+ url: The URL to get the value for.
fallback: Use the global value if there's no URL-specific one.
"""
opt = self.get_opt(name)
diff --git a/qutebrowser/config/configcommands.py b/qutebrowser/config/configcommands.py
index 143b02fca..407c74214 100644
--- a/qutebrowser/config/configcommands.py
+++ b/qutebrowser/config/configcommands.py
@@ -101,7 +101,7 @@ class ConfigCommands:
Args:
option: The name of the option.
value: The value to set.
- pattern: The URL pattern to use.
+ pattern: The link:configuring{outfilesuffix}#patterns[URL pattern] to use.
temp: Set value temporarily until qutebrowser is closed.
print_: Print the value after setting.
"""
@@ -151,9 +151,12 @@ class ConfigCommands:
default: If given, restore a default binding.
"""
if key is None:
+ url = QUrl('qute://bindings')
+ if mode != "normal":
+ url.setFragment(mode)
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=win_id)
- tabbed_browser.load_url(QUrl('qute://bindings'), newtab=True)
+ tabbed_browser.load_url(url, newtab=True)
return
seq = self._parse_key(key)
@@ -204,8 +207,8 @@ class ConfigCommands:
Args:
option: The name of the option.
- values: The values to cycle through.
- pattern: The URL pattern to use.
+ *values: The values to cycle through.
+ pattern: The link:configuring{outfilesuffix}#patterns[URL pattern] to use.
temp: Set value temporarily until qutebrowser is closed.
print_: Print the value after setting.
"""
@@ -259,7 +262,7 @@ class ConfigCommands:
Args:
option: The name of the option.
- pattern: The URL pattern to use.
+ pattern: The link:configuring{outfilesuffix}#patterns[URL pattern] to use.
temp: Set value temporarily until qutebrowser is closed.
"""
parsed_pattern = self._parse_pattern(pattern)
diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml
index a24da4e82..7b79e761d 100644
--- a/qutebrowser/config/configdata.yml
+++ b/qutebrowser/config/configdata.yml
@@ -239,6 +239,9 @@ qt.force_platformtheme:
based on the desktop environment.
qt.process_model:
+ renamed: qt.chromium.process_model
+
+qt.chromium.process_model:
type:
name: String
valid_values:
@@ -268,6 +271,9 @@ qt.process_model:
- https://doc.qt.io/qt-5/qtwebengine-features.html#process-models
qt.low_end_device_mode:
+ renamed: qt.chromium.low_end_device_mode
+
+qt.chromium.low_end_device_mode:
type:
name: String
valid_values:
@@ -284,6 +290,39 @@ qt.low_end_device_mode:
This improves the RAM usage of renderer processes, at the expense of
performance.
+qt.chromium.sandboxing:
+ type:
+ name: String
+ valid_values:
+ - enable-all: Enable all available sandboxing mechanisms.
+ - disable-seccomp-bpf: Disable the Seccomp BPF filter sandbox (Linux only).
+ - disable-all: Disable all sandboxing (**not recommended!**).
+ default: enable-all
+ backend: QtWebEngine
+ restart: true
+ no_autoconfig: true # due to it being dangerous
+ # yamllint disable rule:line-length
+ desc: >-
+ What sandboxing mechanisms in Chromium to use.
+
+ Chromium has various sandboxing layers, which should be enabled for normal
+ browser usage. Mainly for testing and development, it's possible to disable
+ individual sandboxing layers via this setting.
+
+ Open `chrome://sandbox` to see the current sandbox status.
+
+ Changing this setting is only recommended if you know what you're doing, as
+ it **disables one of Chromium's security layers**. To avoid sandboxing being
+ accidentally disabled persistently, this setting can only be set via
+ `config.py`, not via `:set`.
+
+ See the Chromium documentation for more details:
+
+ - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/linux/sandboxing.md[Linux]
+ - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox.md[Windows]
+ - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox_faq.md[FAQ (Windows-centric)]
+ # yamllint enable rule:line-length
+
qt.highdpi:
type: Bool
default: false
@@ -616,6 +655,7 @@ content.headers.custom:
valtype:
name: String
encoding: ascii
+ none_ok: true
none_ok: true
supports_pattern: true
desc: Custom headers for qutebrowser HTTP requests.
@@ -649,7 +689,9 @@ content.headers.referer:
When to send the Referer header.
The Referer header tells websites from which website you were coming from
- when visiting them.
+ when visiting them. Note that with QtWebEngine, websites can override this
+ preference by setting the `Referrer-Policy:` header, so that any websites
+ visited from them get the full referer.
No restart is needed with QtWebKit.
@@ -677,14 +719,14 @@ content.headers.user_agent:
# Vim-protip: Place your cursor below this comment and run
# :r!python scripts/dev/ua_fetch.py
- - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
- like Gecko) Chrome/90.0.4430.93 Safari/537.36"
- - Chrome 90 Win10
+ like Gecko) Chrome/97.0.4692.99 Safari/537.36"
+ - Chrome 97 Win10
- - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36
- (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"
- - Chrome 90 macOS
+ (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36"
+ - Chrome 97 macOS
- - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like
- Gecko) Chrome/90.0.4430.93 Safari/537.36"
- - Chrome 90 Linux
+ Gecko) Chrome/97.0.4692.99 Safari/537.36"
+ - Chrome 97 Linux
supports_pattern: true
desc: |
User agent to send.
@@ -747,6 +789,15 @@ content.blocking.hosts.lists:
The file `~/.config/qutebrowser/blocked-hosts` is always read if it exists.
+content.blocking.hosts.block_subdomains:
+ default: true
+ type: Bool
+ desc: >-
+ Block subdomains of blocked hosts.
+
+ Note: If only a single subdomain is blocked but should be allowed, consider
+ using `content.blocking.whitelist` instead.
+
content.blocking.method:
default: auto
type:
@@ -818,9 +869,14 @@ content.javascript.alert:
type: Bool
desc: Show javascript alerts.
-content.javascript.can_access_clipboard:
- default: false
- type: Bool
+content.javascript.clipboard:
+ default: none
+ type:
+ name: String
+ valid_values:
+ - none: Disable access to clipboard.
+ - access: Allow reading from and writing to the clipboard.
+ - access-paste: Allow accessing the clipboard and pasting clipboard content.
supports_pattern: true
desc: >-
Allow JavaScript to read from or write to the clipboard.
@@ -1006,10 +1062,12 @@ content.pdfjs:
type: Bool
supports_pattern: true
desc: >-
- Allow pdf.js to view PDF files in the browser.
+ Display PDF files via PDF.js in the browser without showing a download prompt.
Note that the files can still be downloaded by clicking the download button
- in the pdf.js viewer.
+ in the pdf.js viewer. With this set to `false`, the `:prompt-open-download
+ --pdfjs` command (bound to `<Ctrl-p>` by default) can be used in the
+ download prompt.
content.persistent_storage:
default: ask
@@ -1049,7 +1107,8 @@ content.proxy:
`http://...` URL.
Note that with QtWebEngine, it will take a couple of seconds until the
- change is applied, if this value is changed at runtime.
+ change is applied, if this value is changed at runtime. Authentication for
+ SOCKS proxies isn't supported due to Chromium limitations.
content.proxy_dns_requests:
default: true
@@ -1344,6 +1403,18 @@ downloads.position:
default: top
desc: Where to show the downloaded files.
+downloads.prevent_mixed_content:
+ type: Bool
+ default: true
+ supports_pattern: true
+ desc:
+ Automatically abort insecure (HTTP) downloads originating from secure
+ (HTTPS) pages.
+
+ For per-domain settings, the relevant URL is the URL initiating the
+ download, not the URL the download itself is coming from. It's not
+ recommended to set this setting to false globally.
+
downloads.remove_finished:
default: -1
type:
@@ -1371,12 +1442,16 @@ editor.command:
* `{line0}`: Same as `{line}`, but starting from index 0.
* `{column0}`: Same as `{column}`, but starting from index 0.
-
editor.encoding:
type: Encoding
default: utf-8
desc: Encoding to use for the editor.
+editor.remove_file:
+ default: true
+ type: Bool
+ desc: Delete the temporary file upon closing the editor.
+
## fileselect
fileselect.handler:
@@ -1605,11 +1680,17 @@ hints.selectors:
- '[role="link"]'
- '[role="option"]'
- '[role="button"]'
+ - '[role="tab"]'
+ - '[role="checkbox"]'
+ - '[role="menuitem"]'
+ - '[role="menuitemcheckbox"]'
+ - '[role="menuitemradio"]'
+ - '[role="treeitem"]'
- '[ng-click]'
- '[ngClick]'
- '[data-ng-click]'
- '[x-ng-click]'
- - '[tabindex]'
+ - '[tabindex]:not([tabindex="-1"])'
links:
- 'a[href]'
- 'area[href]'
@@ -1753,10 +1834,10 @@ input.spatial_navigation:
Enable spatial navigation.
Spatial navigation consists in the ability to navigate between focusable
- elements in a Web page, such as hyperlinks and form controls, by using
- Left, Right, Up and Down arrow keys. For example, if the user presses the
- Right key, heuristics determine whether there is an element he might be
- trying to reach towards the right and which element he probably wants.
+ elements, such as hyperlinks and form controls, on a web page by using the
+ Left, Right, Up and Down arrow keys. For example, if a user presses the
+ Right key, heuristics determine whether there is an element they might be
+ trying to reach towards the right and which element they probably want.
input.media_keys:
default: true
@@ -1770,6 +1851,29 @@ input.media_keys:
On Linux, disabling this also disables Chromium's MPRIS integration.
+input.match_counts:
+ default: true
+ type: Bool
+ desc: >-
+ Interpret number prefixes as counts for bindings.
+
+ This enables for vi-like bindings that can be prefixed with a number to
+ indicate a count.
+ Disabling it allows for emacs-like bindings where number keys are passed
+ through (according to `input.forward_unbound_keys`) instead.
+
+input.mode_override:
+ default: null
+ type:
+ name: String
+ none_ok: true
+ valid_values:
+ - normal
+ - insert
+ - passthrough
+ supports_pattern: true
+ desc: Mode to change to when focusing on a tab/URL changes.
+
## keyhint
keyhint.blacklist:
@@ -2134,6 +2238,7 @@ tabs.title.format:
- title_sep
- index
- aligned_index
+ - relative_index
- id
- scroll_pos
- host
@@ -2153,6 +2258,7 @@ tabs.title.format:
* `{index}`: Index of this tab.
* `{aligned_index}`: Index of this tab padded with spaces to have the same
width.
+ * `{relative_index}`: Index of this tab relative to the current tab.
* `{id}`: Internal tab ID of this tab.
* `{scroll_pos}`: Page scroll position.
* `{host}`: Host of the current web page.
@@ -2173,6 +2279,7 @@ tabs.title.format_pinned:
- title_sep
- index
- aligned_index
+ - relative_index
- id
- scroll_pos
- host
@@ -3633,6 +3740,7 @@ bindings.default:
<Ctrl-E>: edit-text
<Shift-Ins>: insert-text -- {primary}
<Escape>: mode-leave
+ <Shift-Escape>: fake-key <Escape>
hint:
<Return>: hint-follow
<Ctrl-R>: hint --rapid links tab-bg
@@ -3667,7 +3775,8 @@ bindings.default:
<Ctrl-U>: rl-unix-line-discard
<Ctrl-K>: rl-kill-line
<Alt-D>: rl-kill-word
- <Ctrl-W>: rl-unix-word-rubout
+ <Ctrl-W>: rl-rubout " "
+ <Ctrl-Shift-W>: rl-filename-rubout
<Alt-Backspace>: rl-backward-kill-word
<Ctrl-Y>: rl-yank
<Ctrl-?>: rl-delete-char
@@ -3692,7 +3801,8 @@ bindings.default:
<Ctrl-U>: rl-unix-line-discard
<Ctrl-K>: rl-kill-line
<Alt-D>: rl-kill-word
- <Ctrl-W>: rl-unix-word-rubout
+ <Ctrl-W>: rl-rubout " "
+ <Ctrl-Shift-W>: rl-filename-rubout
<Alt-Backspace>: rl-backward-kill-word
<Ctrl-?>: rl-delete-char
<Ctrl-H>: rl-backward-delete-char
@@ -3829,9 +3939,7 @@ bindings.commands:
that single keys can't be bound in this mode.
* passthrough: Similar to insert mode, but passes through all keypresses
- except `<Escape>` to leave the mode. It might be useful to bind
- `<Escape>` to some other key in this mode if you want to be able to send
- an Escape key to the website as well. Note that single keys can't be
+ except `<Shift+Escape>` to leave the mode. Note that single keys can't be
bound in this mode.
* command: Entered when pressing the `:` key in order to enter a command.
diff --git a/qutebrowser/config/configexc.py b/qutebrowser/config/configexc.py
index 27a1001b5..58e5ad67a 100644
--- a/qutebrowser/config/configexc.py
+++ b/qutebrowser/config/configexc.py
@@ -19,8 +19,9 @@
"""Exceptions related to config parsing."""
+import difflib
import dataclasses
-from typing import Any, Mapping, Optional, Sequence, Union
+from typing import Any, Mapping, Optional, Sequence, Union, List
from qutebrowser.utils import usertypes, log
@@ -91,6 +92,7 @@ class NoOptionError(Error):
"""Raised when an option was not found."""
def __init__(self, option: str, *,
+ all_names: List[str] = None,
deleted: bool = False,
renamed: str = None) -> None:
if deleted:
@@ -98,6 +100,12 @@ class NoOptionError(Error):
suffix = ' (this option was removed from qutebrowser)'
elif renamed is not None:
suffix = ' (this option was renamed to {!r})'.format(renamed)
+ elif all_names:
+ matches = difflib.get_close_matches(option, all_names, n=1)
+ if matches:
+ suffix = f' (did you mean {matches[0]!r}?)'
+ else:
+ suffix = ''
else:
suffix = ''
diff --git a/qutebrowser/config/configfiles.py b/qutebrowser/config/configfiles.py
index 6f0d0b13c..f2651d1cd 100644
--- a/qutebrowser/config/configfiles.py
+++ b/qutebrowser/config/configfiles.py
@@ -384,8 +384,8 @@ class YamlMigrations(QObject):
changed = pyqtSignal()
- def __init__(self, settings: _SettingsType,
- parent: QObject = None) -> None:
+ # Note: settings is Any because it's not validated yet.
+ def __init__(self, settings: Any, parent: QObject = None) -> None:
super().__init__(parent)
self._settings = settings
@@ -422,6 +422,12 @@ class YamlMigrations(QObject):
false_value='load-insecurely',
ask_value='ask',
)
+ self._migrate_renamed_bool(
+ old_name='content.javascript.can_access_clipboard',
+ new_name='content.javascript.clipboard',
+ true_value='access',
+ false_value='none',
+ )
for setting in ['colors.webpage.force_dark_color_scheme',
'colors.webpage.prefers_color_scheme_dark']:
diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py
index 6873af7cc..eef43ded4 100644
--- a/qutebrowser/config/configtypes.py
+++ b/qutebrowser/config/configtypes.py
@@ -109,6 +109,8 @@ class ValidValues:
values: A list with the allowed untransformed values.
descriptions: A dict with value/desc mappings.
generate_docs: Whether to show the values in the docs.
+ others_permitted: Whether arbitrary values are permitted.
+ Used to show buttons in qute://settings.
"""
def __init__(
@@ -119,12 +121,14 @@ class ValidValues:
Tuple[str, Optional[str]],
],
generate_docs: bool = True,
+ others_permitted: bool = False
) -> None:
if not values:
raise ValueError("ValidValues with no values makes no sense!")
self.descriptions: DictType[str, str] = {}
self.values: ListType[str] = []
self.generate_docs = generate_docs
+ self.others_permitted = others_permitted
for value in values:
if isinstance(value, str):
# Value without description
@@ -1638,7 +1642,9 @@ class Proxy(BaseType):
super().__init__(none_ok=none_ok, completions=completions)
self.valid_values = ValidValues(
('system', "Use the system wide proxy."),
- ('none', "Don't use any proxy"))
+ ('none', "Don't use any proxy"),
+ others_permitted=True,
+ )
def to_py(
self,
diff --git a/qutebrowser/config/qtargs.py b/qutebrowser/config/qtargs.py
index c38ef5b01..9e7f2620d 100644
--- a/qutebrowser/config/qtargs.py
+++ b/qutebrowser/config/qtargs.py
@@ -260,9 +260,8 @@ def _qtwebengine_args(
# Only actually available in Qt 5.12.5, but let's save another
# check, as passing the option won't hurt.
yield '--enable-in-process-stack-traces'
- else:
- if 'stack' not in namespace.debug_flags:
- yield '--disable-in-process-stack-traces'
+ elif 'stack' not in namespace.debug_flags:
+ yield '--disable-in-process-stack-traces'
lang_override = _get_lang_override(
webengine_version=versions.webengine,
@@ -322,12 +321,12 @@ def _qtwebengine_settings_args(versions: version.WebEngineVersions) -> Iterator[
'--force-webrtc-ip-handling-policy='
'disable_non_proxied_udp',
},
- 'qt.process_model': {
+ 'qt.chromium.process_model': {
'process-per-site-instance': None,
'process-per-site': '--process-per-site',
'single-process': '--single-process',
},
- 'qt.low_end_device_mode': {
+ 'qt.chromium.low_end_device_mode': {
'auto': None,
'always': '--enable-low-end-device-mode',
'never': '--disable-low-end-device-mode',
@@ -339,6 +338,11 @@ def _qtwebengine_settings_args(versions: version.WebEngineVersions) -> Iterator[
True: '--force-prefers-reduced-motion',
False: None,
},
+ 'qt.chromium.sandboxing': {
+ 'enable-all': None,
+ 'disable-seccomp-bpf': '--disable-seccomp-filter-sandbox',
+ 'disable-all': '--no-sandbox',
+ }
}
qt_514_ver = utils.VersionNumber(5, 14)
diff --git a/qutebrowser/config/stylesheet.py b/qutebrowser/config/stylesheet.py
index 2927aec08..b08d67a3b 100644
--- a/qutebrowser/config/stylesheet.py
+++ b/qutebrowser/config/stylesheet.py
@@ -23,13 +23,14 @@ import functools
from typing import Optional, FrozenSet
from PyQt5.QtCore import pyqtSlot, QObject
+from PyQt5.QtWidgets import QWidget
from qutebrowser.config import config
from qutebrowser.misc import debugcachestats
from qutebrowser.utils import jinja, log
-def set_register(obj: QObject,
+def set_register(obj: QWidget,
stylesheet: str = None, *,
update: bool = True) -> None:
"""Set the stylesheet for an object.
@@ -71,7 +72,7 @@ class _StyleSheetObserver(QObject):
None.
"""
- def __init__(self, obj: QObject,
+ def __init__(self, obj: QWidget,
stylesheet: Optional[str], update: bool) -> None:
super().__init__()
self._obj = obj
@@ -81,7 +82,7 @@ class _StyleSheetObserver(QObject):
if update:
self.setParent(self._obj)
if stylesheet is None:
- self._stylesheet: str = obj.STYLESHEET
+ self._stylesheet: str = obj.STYLESHEET # type: ignore[attr-defined]
else:
self._stylesheet = stylesheet
diff --git a/qutebrowser/config/websettings.py b/qutebrowser/config/websettings.py
index 7556d2b6d..779ed0b6b 100644
--- a/qutebrowser/config/websettings.py
+++ b/qutebrowser/config/websettings.py
@@ -164,14 +164,10 @@ class AbstractSettings:
assert encoding is not usertypes.UNSET # unclear how to reset
self._settings.setDefaultTextEncoding(encoding)
- def _update_setting(self, setting: str, value: Any) -> bool:
+ def _update_setting(self, setting: str, value: Any) -> None:
"""Update the given setting/value.
- Unknown settings are ignored.
-
- Return:
- True if there was a change, False otherwise.
- """
+ Unknown settings are ignored."""
if setting in self._ATTRIBUTES:
self.set_attribute(setting, value)
elif setting in self._FONT_SIZES:
@@ -180,7 +176,6 @@ class AbstractSettings:
self.set_font_family(setting, value)
elif setting == 'content.default_encoding':
self.set_default_text_encoding(value)
- return False
def update_setting(self, setting: str) -> None:
"""Update the given setting."""
@@ -238,6 +233,7 @@ def user_agent(url: QUrl = None) -> str:
def init(args: argparse.Namespace) -> None:
"""Initialize all QWeb(Engine)Settings."""
+ utils.unused(args)
if objects.backend == usertypes.Backend.QtWebEngine:
from qutebrowser.browser.webengine import webenginesettings
webenginesettings.init()
diff --git a/qutebrowser/extensions/loader.py b/qutebrowser/extensions/loader.py
index 7ae45023b..793f131c8 100644
--- a/qutebrowser/extensions/loader.py
+++ b/qutebrowser/extensions/loader.py
@@ -21,12 +21,11 @@
import pkgutil
import types
-import sys
import pathlib
import importlib
import argparse
import dataclasses
-from typing import Callable, Iterator, List, Optional, Set, Tuple
+from typing import Callable, Iterator, List, Optional, Tuple
from PyQt5.QtCore import pyqtSlot
@@ -84,7 +83,7 @@ def add_module_info(module: types.ModuleType) -> ModuleInfo:
# pylint: disable=protected-access
if not hasattr(module, '__qute_module_info'):
module.__qute_module_info = ModuleInfo() # type: ignore[attr-defined]
- return module.__qute_module_info # type: ignore[attr-defined]
+ return module.__qute_module_info
def load_components(*, skip_hooks: bool = False) -> None:
@@ -95,22 +94,8 @@ def load_components(*, skip_hooks: bool = False) -> None:
def walk_components() -> Iterator[ExtensionInfo]:
"""Yield ExtensionInfo objects for all modules."""
- if hasattr(sys, 'frozen'):
- yield from _walk_pyinstaller()
- else:
- yield from _walk_normal()
-
-
-def _on_walk_error(name: str) -> None:
- raise ImportError("Failed to import {}".format(name))
-
-
-def _walk_normal() -> Iterator[ExtensionInfo]:
- """Walk extensions when not using PyInstaller."""
for _finder, name, ispkg in pkgutil.walk_packages(
- # Only packages have a __path__ attribute,
- # but we're sure this is one.
- path=components.__path__, # type: ignore[attr-defined]
+ path=components.__path__,
prefix=components.__name__ + '.',
onerror=_on_walk_error):
if ispkg:
@@ -123,23 +108,6 @@ def _walk_normal() -> Iterator[ExtensionInfo]:
yield ExtensionInfo(name=name)
-def _walk_pyinstaller() -> Iterator[ExtensionInfo]:
- """Walk extensions when using PyInstaller.
-
- See https://github.com/pyinstaller/pyinstaller/issues/1905
-
- Inspired by:
- https://github.com/webcomics/dosage/blob/master/dosagelib/loader.py
- """
- toc: Set[str] = set()
- for importer in pkgutil.iter_importers('qutebrowser'):
- if hasattr(importer, 'toc'):
- toc |= importer.toc # type: ignore[union-attr]
- for name in toc:
- if name.startswith(components.__name__ + '.'):
- yield ExtensionInfo(name=name)
-
-
def _get_init_context() -> InitContext:
"""Get an InitContext object."""
return InitContext(data_dir=pathlib.Path(standarddir.data()),
@@ -190,3 +158,7 @@ def _on_config_changed(changed_name: str) -> None:
def init() -> None:
config.instance.changed.connect(_on_config_changed)
+
+
+def _on_walk_error(name: str) -> None:
+ raise ImportError("Failed to import {}".format(name))
diff --git a/qutebrowser/html/bindings.html b/qutebrowser/html/bindings.html
index fe6913402..7bc441cbd 100644
--- a/qutebrowser/html/bindings.html
+++ b/qutebrowser/html/bindings.html
@@ -10,7 +10,7 @@ th { text-align:left; }
{% block content %}
<header><h1>{{ title }}</h1></header>
{% for mode, binding in bindings.items() %}
-<h2>{{ mode | capitalize }} mode</h2>
+<h2 id="{{ mode }}">{{ mode | capitalize }} mode</h2>
<table>
<tr>
<th>Key</th>
diff --git a/qutebrowser/html/settings.html b/qutebrowser/html/settings.html
index 44824eeac..f89aaa610 100644
--- a/qutebrowser/html/settings.html
+++ b/qutebrowser/html/settings.html
@@ -13,22 +13,145 @@ var cset = function(option, value) {
{% endblock %}
{% block style %}
-table { border: 1px solid grey; border-collapse: collapse; }
-pre { margin: 2px; }
-th, td { border: 1px solid grey; padding: 0px 5px; }
-th { background: lightgrey; }
-th pre { color: grey; text-align: left; }
-input { width: 98%; }
-.setting { width: 75%; }
-.value { width: 25%; text-align: center; }
-.noscript, .noscript-text { color:red; }
-.noscript-text { margin-bottom: 5cm; }
-.option_description { margin: .5ex 0; color: grey; font-size: 80%; font-style: italic; white-space: pre-line; }
+table {
+ border-spacing: 10px;
+}
+
+tbody tr:nth-child(odd) {
+ background: #eaf4fb;
+}
+
+pre {
+ margin: 2px;
+}
+
+th {
+ padding: 10px;
+ border-radius: 5px;
+ background: #a6dfff;
+ text-align: left;
+ font-weight: normal;
+ font-size: 1.5rem;
+ color: #084c88;
+}
+
+td {
+ padding: 5px 5px;
+}
+
+th pre {
+ color: grey;
+ text-align: left;
+}
+
+input {
+ padding: 8px;
+ width: 98%;
+ box-sizing: border-box;
+ border-radius: 4px;
+ border: 1px solid #01cdd0;
+ font-size: 0.9rem;
+ font-family: DejaVu, serif;
+}
+
+input:focus {
+ outline: none;
+ border: 2px solid #7a589ea6;
+}
+
+input[type="radio"] {
+ position: absolute; /* Positions the radio button relative to the edges of its containing element */
+ -webkit-appearance: none; /* Removes its native styling */
+ width: min-content;
+ margin: 0;
+ border: none;
+ cursor: pointer;
+}
+
+label {
+ cursor: pointer;
+ margin-bottom: 2px;
+ padding: 5px 10px;
+ border-radius: 5px;
+ background-color: #dddddd;
+ color: #666666;
+}
+
+input[type="radio"]:checked + label {
+ background-color: #a6dfff;
+ color: #084c88;
+}
+
+.radio-button {
+ position: relative; /* The absolutely positioned element inside this tag (the radio button) gets positioned relative to it. */
+ display: inline-flex;
+ margin: 3px 1px;
+}
+
+.setting {
+ width: 60%;
+}
+
+.value {
+ width: 25%;
+ text-align: center;
+}
+
+.valid-value {
+ text-align: center;
+}
+
+.noscript, .noscript-text {
+ color: red;
+}
+
+.noscript-text {
+ margin-bottom: 5cm;
+}
+
+.option-description {
+ margin: .5ex 0;
+ color: #635d5dcf;
+ font-size: 80%;
+ font-style: italic;
+}
+
+.option-description p {
+ margin: 0;
+}
+
+.long-description {
+ white-space: pre-line;
+}
+
+details summary > * {
+ display: inline;
+}
+
+details[open] .details {
+ display: none;
+}
+
+summary {
+ margin: .5ex 0;
+ width: fit-content;
+ color: #1887c5;
+ outline: none;
+ font-size: 105%;
+ cursor: pointer;
+}
+
+summary .short-description {
+ color: #635d5dcf;
+}
+
+summary::selection {
+ background-color: inherit;
+}
{% endblock %}
{% block content %}
<noscript><h1 class="noscript">View Only</h1><p class="noscript-text">Changing settings requires javascript to be enabled!</p></noscript>
-<header><h1>{{ title }}</h1></header>
<table>
<tr>
<th>Setting</th>
@@ -36,19 +159,52 @@ input { width: 98%; }
</tr>
{% for option in configdata.DATA.values()|sort(attribute='name') if not option.no_autoconfig %}
<tr>
+ {% set loopIndex = loop.index0 %}
<!-- FIXME: convert to string properly -->
- <td class="setting">{{ option.name }} (Current: {{ confget(option.name) | string |truncate(100) }})
+ <td class="setting">{{ option.name }}
{% if option.description %}
- <p class="option_description">{{ option.description|e }}</p>
+ {% set description = option.description.split('\n', 1) %}
+ <div class="option-description">
+ {% if description|length > 1 %}
+ <details>
+ <summary>
+ <p class="short-description">{{ description[0]|e }}</p>
+ <span class="details">Details</span>
+ </summary>
+ <p class="long-description">{{ description[1]|e }}</p>
+ </details>
+ {% else %}
+ <p>{{ description[0]|e }}</p>
+ {% endif %}
+ </div>
{% endif %}
</td>
- <td class="value">
- <input type="text"
- id="input-{{ option.name }}"
- onblur="cset('{{ option.name }}', this.value)"
- value="{{ confget(option.name) }}">
- </input>
- </td>
+ {% set valid_values = option.typ.valid_values %}
+ {% if valid_values is not none and not valid_values.others_permitted %}
+ <td class="valid-value">
+ {% for value in valid_values.values %}
+ <div class="radio-button">
+ <input type="radio" id="input-{{ option.name }}-{{ loop.index0 }}"
+ name="{{ option.name }}" value="{{ value }}"
+ onclick="cset('{{ option.name }}', this.value)"
+ {% if confget(option.name) == value %}
+ checked
+ {% endif %}>
+ <label for="input-{{ option.name }}-{{ loop.index0 }}">
+ {{ value }}
+ </label>
+ </div>
+ {% endfor %}
+ </td>
+ {% else %}
+ <td class="value">
+ <input type="text"
+ id="input-{{ option.name }}"
+ onblur="cset('{{ option.name }}', this.value)"
+ value="{{ confget(option.name) }}">
+ </input>
+ </td>
+ {% endif %}
</tr>
{% endfor %}
</table>
diff --git a/qutebrowser/html/warning-webkit.html b/qutebrowser/html/warning-webkit.html
index 975f98c1b..f5cf9bf01 100644
--- a/qutebrowser/html/warning-webkit.html
+++ b/qutebrowser/html/warning-webkit.html
@@ -41,8 +41,8 @@ hopefully help.</p>
notification support was added for Qt 5.13.0.</p>
<p><b>Resource usage</b>: qutebrowser v1.5.0 added the <span
-class="mono">qt.process_model</span> and <span
-class="mono">qt.low_end_device_mode</span> settings which can be used to
+class="mono">qt.chromium.process_model</span> and <span
+class="mono">qt.chromium.low_end_device_mode</span> settings which can be used to
decrease the resource usage of QtWebEngine (but come with other drawbacks).</p>
<p><b>Not trusting Google</b>: Various people have checked the connections made
@@ -78,7 +78,7 @@ security fixes took months to arrive there). You might be better off choosing an
method</a>.</p>
<p><b>White flashing between loads with a custom stylesheet</b>: This doesn't
-seem to happen with <span class="mono">qt.process_model = single-process</span>
+seem to happen with <span class="mono">qt.chromium.process_model = single-process</span>
set. However, note that that setting comes with decreased security and
stability, but QtWebKit doesn't have any process isolation at all.</p>
{% endblock %}
diff --git a/icons/qutebrowser-128x128.png b/qutebrowser/icons/qutebrowser-128x128.png
index ca88ccc42..ca88ccc42 100644
--- a/icons/qutebrowser-128x128.png
+++ b/qutebrowser/icons/qutebrowser-128x128.png
Binary files differ
diff --git a/icons/qutebrowser-16x16.png b/qutebrowser/icons/qutebrowser-16x16.png
index 062fe402c..062fe402c 100644
--- a/icons/qutebrowser-16x16.png
+++ b/qutebrowser/icons/qutebrowser-16x16.png
Binary files differ
diff --git a/icons/qutebrowser-24x24.png b/qutebrowser/icons/qutebrowser-24x24.png
index 6aa2b5cd3..6aa2b5cd3 100644
--- a/icons/qutebrowser-24x24.png
+++ b/qutebrowser/icons/qutebrowser-24x24.png
Binary files differ
diff --git a/icons/qutebrowser-256x256.png b/qutebrowser/icons/qutebrowser-256x256.png
index 705d314fc..705d314fc 100644
--- a/icons/qutebrowser-256x256.png
+++ b/qutebrowser/icons/qutebrowser-256x256.png
Binary files differ
diff --git a/icons/qutebrowser-32x32.png b/qutebrowser/icons/qutebrowser-32x32.png
index c03d91092..c03d91092 100644
--- a/icons/qutebrowser-32x32.png
+++ b/qutebrowser/icons/qutebrowser-32x32.png
Binary files differ
diff --git a/icons/qutebrowser-48x48.png b/qutebrowser/icons/qutebrowser-48x48.png
index 1dd9e79a5..1dd9e79a5 100644
--- a/icons/qutebrowser-48x48.png
+++ b/qutebrowser/icons/qutebrowser-48x48.png
Binary files differ
diff --git a/icons/qutebrowser-512x512.png b/qutebrowser/icons/qutebrowser-512x512.png
index 08a273507..08a273507 100644
--- a/icons/qutebrowser-512x512.png
+++ b/qutebrowser/icons/qutebrowser-512x512.png
Binary files differ
diff --git a/icons/qutebrowser-64x64.png b/qutebrowser/icons/qutebrowser-64x64.png
index 72dc3febb..72dc3febb 100644
--- a/icons/qutebrowser-64x64.png
+++ b/qutebrowser/icons/qutebrowser-64x64.png
Binary files differ
diff --git a/icons/qutebrowser-96x96.png b/qutebrowser/icons/qutebrowser-96x96.png
index bc24982c5..bc24982c5 100644
--- a/icons/qutebrowser-96x96.png
+++ b/qutebrowser/icons/qutebrowser-96x96.png
Binary files differ
diff --git a/icons/qutebrowser-all.svg b/qutebrowser/icons/qutebrowser-all.svg
index da9ab4d6d..da9ab4d6d 100644
--- a/icons/qutebrowser-all.svg
+++ b/qutebrowser/icons/qutebrowser-all.svg
diff --git a/icons/qutebrowser-favicon.svg b/qutebrowser/icons/qutebrowser-favicon.svg
index 0cceab56c..0cceab56c 100644
--- a/icons/qutebrowser-favicon.svg
+++ b/qutebrowser/icons/qutebrowser-favicon.svg
diff --git a/icons/qutebrowser.icns b/qutebrowser/icons/qutebrowser.icns
index 928b6ca89..928b6ca89 100644
--- a/icons/qutebrowser.icns
+++ b/qutebrowser/icons/qutebrowser.icns
Binary files differ
diff --git a/icons/qutebrowser.ico b/qutebrowser/icons/qutebrowser.ico
index 42419d944..42419d944 100644
--- a/icons/qutebrowser.ico
+++ b/qutebrowser/icons/qutebrowser.ico
Binary files differ
diff --git a/icons/qutebrowser.svg b/qutebrowser/icons/qutebrowser.svg
index b26c80c2b..b26c80c2b 100644
--- a/icons/qutebrowser.svg
+++ b/qutebrowser/icons/qutebrowser.svg
diff --git a/icons/qutebrowser.xpm b/qutebrowser/icons/qutebrowser.xpm
index 04e183154..04e183154 100644
--- a/icons/qutebrowser.xpm
+++ b/qutebrowser/icons/qutebrowser.xpm
diff --git a/qutebrowser/javascript/greasemonkey_wrapper.js b/qutebrowser/javascript/greasemonkey_wrapper.js
index 7bfabc635..ced4e1063 100644
--- a/qutebrowser/javascript/greasemonkey_wrapper.js
+++ b/qutebrowser/javascript/greasemonkey_wrapper.js
@@ -77,6 +77,9 @@
if ("onerror" in details) {
oXhr.onerror = function () { details.onerror(oXhr); };
}
+ if ("overrideMimeType" in details) {
+ oXhr.overrideMimeType(details.overrideMimeType);
+ }
oXhr.open(details.method, details.url, true);
@@ -107,6 +110,22 @@
}
}
+ // Based on GreaseMonkey:
+ // https://github.com/greasemonkey/greasemonkey/blob/4.11/src/bg/api-provider-source.js#L232-L249
+ function GM_setClipboard(text) {
+ function onCopy(event) {
+ document.removeEventListener('copy', onCopy, true);
+
+ event.stopImmediatePropagation();
+ event.preventDefault();
+
+ event.clipboardData.setData('text/plain', text);
+ }
+
+ document.addEventListener('copy', onCopy, true);
+ document.execCommand('copy');
+ }
+
// Stub these two so that the gm4 polyfill script doesn't try to
// create broken versions as attributes of window.
function GM_getResourceText(caption, commandFunc, accessKey) {
@@ -123,6 +142,7 @@
const entries = {
'log': GM_log,
'addStyle': GM_addStyle,
+ 'setClipboard': GM_setClipboard,
'deleteValue': GM_deleteValue,
'getValue': GM_getValue,
'listValues': GM_listValues,
diff --git a/qutebrowser/javascript/quirks/globalthis.user.js b/qutebrowser/javascript/quirks/globalthis.user.js
index 103681fda..b87a956e5 100644
--- a/qutebrowser/javascript/quirks/globalthis.user.js
+++ b/qutebrowser/javascript/quirks/globalthis.user.js
@@ -1,6 +1,8 @@
// ==UserScript==
// @include https://www.reddit.com/*
// @include https://open.spotify.com/*
+// @include https://*.stackexchange.com/*
+// @include https://stackoverflow.com/*
// @include https://test.qutebrowser.org/*
// ==/UserScript==
diff --git a/qutebrowser/keyinput/basekeyparser.py b/qutebrowser/keyinput/basekeyparser.py
index 7e688dab1..4db1d5d76 100644
--- a/qutebrowser/keyinput/basekeyparser.py
+++ b/qutebrowser/keyinput/basekeyparser.py
@@ -254,6 +254,9 @@ class BaseKeyParser(QObject):
def _match_count(self, sequence: keyutils.KeySequence,
dry_run: bool) -> bool:
"""Try to match a key as count."""
+ if not config.val.input.match_counts:
+ return False
+
txt = str(sequence[-1]) # To account for sequences changed above.
if (txt in string.digits and self._supports_count and
not (not self._count and txt == '0')):
diff --git a/qutebrowser/keyinput/eventfilter.py b/qutebrowser/keyinput/eventfilter.py
index 4d016bea6..f0d85b0ec 100644
--- a/qutebrowser/keyinput/eventfilter.py
+++ b/qutebrowser/keyinput/eventfilter.py
@@ -35,7 +35,7 @@ class EventFilter(QObject):
Attributes:
_activated: Whether the EventFilter is currently active.
- _handlers; A {QEvent.Type: callable} dict with the handlers for an
+ _handlers: A {QEvent.Type: callable} dict with the handlers for an
event.
"""
diff --git a/qutebrowser/keyinput/keyutils.py b/qutebrowser/keyinput/keyutils.py
index 6bd8c99b8..0362e09f3 100644
--- a/qutebrowser/keyinput/keyutils.py
+++ b/qutebrowser/keyinput/keyutils.py
@@ -156,7 +156,7 @@ def _assert_plain_key(key: Qt.Key) -> None:
def _assert_plain_modifier(key: _ModifierType) -> None:
"""Make sure this is a modifier without a key mixed in."""
mask = Qt.KeyboardModifierMask
- assert not key & ~mask, hex(key) # type: ignore[operator]
+ assert not key & ~mask, hex(key)
def _is_printable(key: Qt.Key) -> bool:
@@ -254,9 +254,9 @@ def _modifiers_to_string(modifiers: _ModifierType) -> str:
modifier.
"""
_assert_plain_modifier(modifiers)
- altgr = Qt.GroupSwitchModifier
- if modifiers & altgr: # type: ignore[operator]
- modifiers &= ~altgr # type: ignore[operator, assignment]
+ altgr = cast(Qt.KeyboardModifiers, Qt.GroupSwitchModifier)
+ if modifiers & altgr:
+ modifiers &= ~altgr
result = 'AltGr+'
else:
result = ''
@@ -416,7 +416,7 @@ class KeyInfo:
return ''
text = QKeySequence(self.key).toString()
- if not self.modifiers & Qt.ShiftModifier: # type: ignore[operator]
+ if not self.modifiers & Qt.ShiftModifier:
text = text.lower()
return text
@@ -473,7 +473,7 @@ class KeySequence:
"""Iterate over KeyInfo objects."""
for key_and_modifiers in self._iter_keys():
key = Qt.Key(int(key_and_modifiers) & ~Qt.KeyboardModifierMask)
- modifiers = Qt.KeyboardModifiers( # type: ignore[call-overload]
+ modifiers = Qt.KeyboardModifiers(
int(key_and_modifiers) & Qt.KeyboardModifierMask)
yield KeyInfo(key=key, modifiers=modifiers)
@@ -619,14 +619,15 @@ class KeySequence:
# (or "Cmd") in a key binding name to actually represent what's on the
# keyboard.
if utils.is_mac:
+ # FIXME:qt6 Reevaluate the type ignores below
if modifiers & Qt.ControlModifier and modifiers & Qt.MetaModifier:
pass
elif modifiers & Qt.ControlModifier:
modifiers &= ~Qt.ControlModifier
- modifiers |= Qt.MetaModifier
+ modifiers |= Qt.MetaModifier # type: ignore[assignment]
elif modifiers & Qt.MetaModifier:
modifiers &= ~Qt.MetaModifier
- modifiers |= Qt.ControlModifier
+ modifiers |= Qt.ControlModifier # type: ignore[assignment]
keys = list(self._iter_keys())
keys.append(key | int(modifiers))
@@ -656,7 +657,6 @@ class KeySequence:
@classmethod
def parse(cls, keystr: str) -> 'KeySequence':
"""Parse a keystring like <Ctrl-x> or xyz and return a KeySequence."""
- # pylint: disable=protected-access
new = cls()
strings = list(_parse_keystring(keystr))
for sub in utils.chunk(strings, cls._MAX_LEN):
@@ -666,6 +666,5 @@ class KeySequence:
if keystr:
assert new, keystr
- # pylint: disable=protected-access
new._validate(keystr)
return new
diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py
index 3c47fafe3..24c701401 100644
--- a/qutebrowser/keyinput/modeman.py
+++ b/qutebrowser/keyinput/modeman.py
@@ -37,6 +37,7 @@ from qutebrowser.misc import objects
INPUT_MODES = [usertypes.KeyMode.insert, usertypes.KeyMode.passthrough]
PROMPT_MODES = [usertypes.KeyMode.prompt, usertypes.KeyMode.yesno]
+# FIXME:mypy TypedDict?
ParserDictType = MutableMapping[usertypes.KeyMode, basekeyparser.BaseKeyParser]
diff --git a/qutebrowser/mainwindow/mainwindow.py b/qutebrowser/mainwindow/mainwindow.py
index d0723742a..b247da632 100644
--- a/qutebrowser/mainwindow/mainwindow.py
+++ b/qutebrowser/mainwindow/mainwindow.py
@@ -92,7 +92,7 @@ def raise_window(window, alert=True):
window.setWindowState(window.windowState() | Qt.WindowActive)
window.raise_()
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-69568
- QCoreApplication.processEvents( # type: ignore[call-overload]
+ QCoreApplication.processEvents(
QEventLoop.ExcludeUserInputEvents | QEventLoop.ExcludeSocketNotifiers)
if not sip.isdeleted(window):
@@ -249,8 +249,8 @@ class MainWindow(QWidget):
log.init.debug("Initializing modes...")
modeman.init(win_id=self.win_id, parent=self)
- self._commandrunner = runners.CommandRunner(self.win_id,
- partial_match=True)
+ self._commandrunner = runners.CommandRunner(
+ self.win_id, partial_match=True, find_similar=True)
self._keyhint = keyhintwidget.KeyHintView(self.win_id, self)
self._add_overlay(self._keyhint, self._keyhint.update_geometry)
@@ -561,11 +561,11 @@ class MainWindow(QWidget):
def _set_decoration(self, hidden):
"""Set the visibility of the window decoration via Qt."""
- window_flags: int = Qt.Window
+ window_flags = cast(Qt.WindowFlags, Qt.Window)
refresh_window = self.isVisible()
if hidden:
window_flags |= Qt.CustomizeWindowHint | Qt.NoDropShadowWindowHint
- self.setWindowFlags(cast(Qt.WindowFlags, window_flags))
+ self.setWindowFlags(window_flags)
if refresh_window:
self.show()
@@ -574,9 +574,7 @@ class MainWindow(QWidget):
if not config.val.content.fullscreen.window:
if on:
self.state_before_fullscreen = self.windowState()
- self.setWindowState(
- Qt.WindowFullScreen | # type: ignore[arg-type]
- self.state_before_fullscreen) # type: ignore[operator]
+ self.setWindowState(Qt.WindowFullScreen | self.state_before_fullscreen)
elif self.isFullScreen():
self.setWindowState(self.state_before_fullscreen)
log.misc.debug('on: {}, state before fullscreen: {}'.format(
@@ -602,7 +600,7 @@ class MainWindow(QWidget):
super().resizeEvent(e)
self._update_overlay_geometries()
self._downloadview.updateGeometry()
- self.tabbed_browser.widget.tabBar().refresh()
+ self.tabbed_browser.widget.tab_bar().refresh()
def showEvent(self, e):
"""Extend showEvent to register us as the last-visible-main-window.
@@ -620,13 +618,15 @@ class MainWindow(QWidget):
True if closing is okay, False if a closeEvent should be ignored.
"""
tab_count = self.tabbed_browser.widget.count()
+ window_count = len(objreg.window_registry)
download_count = self._download_model.running_downloads()
quit_texts = []
# Ask if multiple-tabs are open
if 'multiple-tabs' in config.val.confirm_quit and tab_count > 1:
quit_texts.append("{} tabs are open.".format(tab_count))
- # Ask if multiple downloads running
- if 'downloads' in config.val.confirm_quit and download_count > 0:
+ # Ask if downloads running
+ if ('downloads' in config.val.confirm_quit and download_count > 0 and
+ window_count <= 1):
quit_texts.append("{} {} running.".format(
download_count,
"download is" if download_count == 1 else "downloads are"))
diff --git a/qutebrowser/mainwindow/prompt.py b/qutebrowser/mainwindow/prompt.py
index c8cbe572b..f7a04bee0 100644
--- a/qutebrowser/mainwindow/prompt.py
+++ b/qutebrowser/mainwindow/prompt.py
@@ -536,9 +536,11 @@ class _BasePrompt(QWidget):
self.KEY_MODE.name)
labels = []
+ has_bindings = False
for cmd, text in self._allowed_commands():
bindings = all_bindings.get(cmd, [])
if bindings:
+ has_bindings = True
binding = None
preferred = ['<enter>', '<escape>']
for pref in preferred:
@@ -547,8 +549,11 @@ class _BasePrompt(QWidget):
if binding is None:
binding = bindings[0]
key_label = QLabel('<b>{}</b>'.format(html.escape(binding)))
- text_label = QLabel(text)
- labels.append((key_label, text_label))
+ else:
+ key_label = QLabel(f'<b>unbound</b> (<tt>{html.escape(cmd)}</tt>)')
+
+ text_label = QLabel(text)
+ labels.append((key_label, text_label))
for i, (key_label, text_label) in enumerate(labels):
self._key_grid.addWidget(key_label, i, 0)
@@ -559,6 +564,14 @@ class _BasePrompt(QWidget):
self._vbox.addLayout(self._key_grid)
+ if not has_bindings:
+ label = QLabel(
+ "<b>Note:</b> You seem to have unbound all keys for this prompt "
+ f"(<tt>{self.KEY_MODE.name}</tt> key mode)."
+ "<br/>Run <tt>qutebrowser :CMD</tt> with a command from above to "
+ "close this prompt, then fix this in your config.")
+ self._vbox.addWidget(label)
+
def _check_save_support(self, save):
if save:
raise UnsupportedOperationError("Saving answers is only possible "
@@ -771,7 +784,7 @@ class FilenamePrompt(_BasePrompt):
selmodel.setCurrentIndex(
idx,
- QItemSelectionModel.ClearAndSelect | # type: ignore[arg-type]
+ QItemSelectionModel.ClearAndSelect |
QItemSelectionModel.Rows)
self._insert_path(idx, clicked=False)
@@ -795,7 +808,7 @@ class DownloadFilenamePrompt(FilenamePrompt):
def __init__(self, question, parent=None):
super().__init__(question, parent)
self._file_model.setFilter(
- QDir.AllDirs | QDir.Drives | QDir.NoDot) # type: ignore[arg-type]
+ QDir.AllDirs | QDir.Drives | QDir.NoDotAndDotDot)
def accept(self, value=None, save=False):
done = super().accept(value, save)
@@ -818,6 +831,7 @@ class DownloadFilenamePrompt(FilenamePrompt):
cmds = [
('prompt-accept', 'Accept'),
('mode-leave', 'Abort'),
+ ('rl-filename-rubout', "Go to parent directory"),
('prompt-open-download', "Open download"),
('prompt-open-download --pdfjs', "Open download via PDF.js"),
('prompt-yank', "Yank URL"),
diff --git a/qutebrowser/mainwindow/statusbar/bar.py b/qutebrowser/mainwindow/statusbar/bar.py
index f4b81b7e6..ae33a386a 100644
--- a/qutebrowser/mainwindow/statusbar/bar.py
+++ b/qutebrowser/mainwindow/statusbar/bar.py
@@ -22,8 +22,7 @@
import enum
import dataclasses
-from PyQt5.QtCore import (pyqtSignal, pyqtSlot, # type: ignore[attr-defined]
- pyqtProperty, Qt, QSize, QTimer)
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, pyqtProperty, Qt, QSize, QTimer
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QStackedLayout, QSizePolicy
from qutebrowser.browser import browsertab
@@ -314,7 +313,7 @@ class StatusBar(QWidget):
padding = config.val.statusbar.padding
self._hbox.setContentsMargins(padding.left, 0, padding.right, 0)
- @pyqtProperty('QStringList')
+ @pyqtProperty('QStringList') # type: ignore[type-var]
def color_flags(self):
"""Getter for self.color_flags, so it can be used as Qt property."""
return self._color_flags.to_stringlist()
diff --git a/qutebrowser/mainwindow/statusbar/command.py b/qutebrowser/mainwindow/statusbar/command.py
index 92408d34f..95076380a 100644
--- a/qutebrowser/mainwindow/statusbar/command.py
+++ b/qutebrowser/mainwindow/statusbar/command.py
@@ -19,6 +19,7 @@
"""The commandline in the statusbar."""
+from typing import Optional
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QSize
from PyQt5.QtGui import QKeyEvent
@@ -240,7 +241,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit):
self.clear_completion_selection.emit()
self.hide_completion.emit()
- def setText(self, text: str) -> None:
+ def setText(self, text: Optional[str]) -> None:
"""Extend setText to set prefix and make sure the prompt is ok."""
if not text:
pass
@@ -276,7 +277,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit):
text = self.text()
if not text:
text = 'x'
- width = self.fontMetrics().width(text)
+ width = self.fontMetrics().boundingRect(text).width()
return QSize(width, height)
@pyqtSlot()
diff --git a/qutebrowser/mainwindow/statusbar/url.py b/qutebrowser/mainwindow/statusbar/url.py
index 99818e284..a971d76aa 100644
--- a/qutebrowser/mainwindow/statusbar/url.py
+++ b/qutebrowser/mainwindow/statusbar/url.py
@@ -21,8 +21,7 @@
import enum
-from PyQt5.QtCore import (pyqtSlot, pyqtProperty, # type: ignore[attr-defined]
- QUrl)
+from PyQt5.QtCore import pyqtSlot, pyqtProperty, QUrl
from qutebrowser.mainwindow.statusbar import textbase
from qutebrowser.config import stylesheet
@@ -92,7 +91,7 @@ class UrlText(textbase.TextBase):
self._normal_url = None
self._normal_url_type = UrlType.normal
- @pyqtProperty(str)
+ @pyqtProperty(str) # type: ignore[type-var]
def urltype(self):
"""Getter for self.urltype, so it can be used as Qt property.
diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py
index e081284ee..68b4adfdb 100644
--- a/qutebrowser/mainwindow/tabbedbrowser.py
+++ b/qutebrowser/mainwindow/tabbedbrowser.py
@@ -28,7 +28,7 @@ from typing import (
Any, Deque, List, Mapping, MutableMapping, MutableSequence, Optional, Tuple)
from PyQt5.QtWidgets import QSizePolicy, QWidget, QApplication
-from PyQt5.QtCore import pyqtSignal, pyqtSlot, QTimer, QUrl
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QTimer, QUrl, QPoint
from qutebrowser.config import config
from qutebrowser.keyinput import modeman
@@ -73,14 +73,14 @@ class TabDeque:
size = config.val.tabs.focus_stack_size
if size < 0:
size = None
- self._stack: Deque[weakref.ReferenceType[QWidget]] = collections.deque(
- maxlen=size)
+ self._stack: Deque[weakref.ReferenceType[browsertab.AbstractTab]] = (
+ collections.deque(maxlen=size))
# Items that have been removed from the primary stack.
- self._stack_deleted: List[weakref.ReferenceType[QWidget]] = []
+ self._stack_deleted: List[weakref.ReferenceType[browsertab.AbstractTab]] = []
self._ignore_next = False
self._keep_deleted_next = False
- def on_switch(self, old_tab: QWidget) -> None:
+ def on_switch(self, old_tab: browsertab.AbstractTab) -> None:
"""Record tab switch events."""
if self._ignore_next:
self._ignore_next = False
@@ -92,24 +92,29 @@ class TabDeque:
self._keep_deleted_next = False
self._stack.append(tab)
- def prev(self, cur_tab: QWidget) -> QWidget:
+ def prev(self, cur_tab: browsertab.AbstractTab) -> browsertab.AbstractTab:
"""Get the 'previous' tab in the stack.
Throws IndexError on failure.
"""
- tab: Optional[QWidget] = None
+ tab: Optional[browsertab.AbstractTab] = None
while tab is None or tab.pending_removal or tab is cur_tab:
tab = self._stack.pop()()
self._stack_deleted.append(weakref.ref(cur_tab))
self._ignore_next = True
return tab
- def next(self, cur_tab: QWidget, *, keep_overflow: bool = True) -> QWidget:
+ def next(
+ self,
+ cur_tab: browsertab.AbstractTab,
+ *,
+ keep_overflow: bool = True,
+ ) -> browsertab.AbstractTab:
"""Get the 'next' tab in the stack.
Throws IndexError on failure.
"""
- tab: Optional[QWidget] = None
+ tab: Optional[browsertab.AbstractTab] = None
while tab is None or tab.pending_removal or tab is cur_tab:
tab = self._stack_deleted.pop()()
# On next tab-switch, current tab will be added to stack as normal.
@@ -118,7 +123,7 @@ class TabDeque:
self._keep_deleted_next = True
return tab
- def last(self, cur_tab: QWidget) -> QWidget:
+ def last(self, cur_tab: browsertab.AbstractTab) -> browsertab.AbstractTab:
"""Get the last tab.
Throws IndexError on failure.
@@ -216,13 +221,18 @@ class TabbedBrowser(QWidget):
self.widget.tabCloseRequested.connect(self.on_tab_close_requested)
self.widget.new_tab_requested.connect(self.tabopen)
self.widget.currentChanged.connect(self._on_current_changed)
- self.cur_fullscreen_requested.connect(self.widget.tabBar().maybe_hide)
+ self.cur_fullscreen_requested.connect(self.widget.tab_bar().maybe_hide)
+
self.widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
# load_finished instead of load_started as WORKAROUND for
# https://bugreports.qt.io/browse/QTBUG-65223
self.cur_load_finished.connect(self._leave_modes_on_load)
+ # handle mode_override
+ self.current_tab_changed.connect(lambda tab: self._mode_override(tab.url()))
+ self.cur_url_changed.connect(self._mode_override)
+
# This init is never used, it is immediately thrown away in the next
# line.
self.undo_stack: UndoStackType = collections.deque()
@@ -231,8 +241,8 @@ class TabbedBrowser(QWidget):
self._now_focused = None
self.search_text = None
self.search_options: Mapping[str, Any] = {}
- self._local_marks: MutableMapping[QUrl, MutableMapping[str, int]] = {}
- self._global_marks: MutableMapping[str, Tuple[int, QUrl]] = {}
+ self._local_marks: MutableMapping[QUrl, MutableMapping[str, QPoint]] = {}
+ self._global_marks: MutableMapping[str, Tuple[QPoint, QUrl]] = {}
self.default_window_icon = self.widget.window().windowIcon()
self.is_private = private
self.tab_deque = TabDeque()
@@ -369,6 +379,25 @@ class TabbedBrowser(QWidget):
tab.history_item_triggered.connect(
history.web_history.add_from_tab)
+ def _current_tab(self) -> browsertab.AbstractTab:
+ """Get the current browser tab.
+
+ Note: The assert ensures the current tab is never None.
+ """
+ tab = self.widget.currentWidget()
+ assert isinstance(tab, browsertab.AbstractTab), tab
+ return tab
+
+ def _tab_by_idx(self, idx: int) -> Optional[browsertab.AbstractTab]:
+ """Get a browser tab by index.
+
+ If no tab was found at the given index, None is returned.
+ """
+ tab = self.widget.widget(idx)
+ if tab is not None:
+ assert isinstance(tab, browsertab.AbstractTab), tab
+ return tab
+
def current_url(self):
"""Get the URL of the current tab.
@@ -406,15 +435,16 @@ class TabbedBrowser(QWidget):
else:
yes_action()
- def close_tab(self, tab, *, add_undo=True, new_undo=True):
+ def close_tab(self, tab, *, add_undo=True, new_undo=True, transfer=False):
"""Close a tab.
Args:
tab: The QWebView to be closed.
add_undo: Whether the tab close can be undone.
new_undo: Whether the undo entry should be a new item in the stack.
+ transfer: Whether the tab is closing because it is moving to a new window.
"""
- if config.val.tabs.tabs_are_windows:
+ if config.val.tabs.tabs_are_windows or transfer:
last_close = 'close'
else:
last_close = config.val.tabs.last_close
@@ -498,25 +528,27 @@ class TabbedBrowser(QWidget):
]
only_one_tab_open = self.widget.count() == 1
if only_one_tab_open and last_close_replaces:
- no_history = len(self.widget.widget(0).history) == 1
+ tab = self._tab_by_idx(0)
+ assert tab is not None
+ no_history = len(tab.history) == 1
urls = {
'blank': QUrl('about:blank'),
'startpage': config.val.url.start_pages[0],
'default-page': config.val.url.default_page,
}
- first_tab_url = self.widget.widget(0).url()
+ first_tab_url = tab.url()
last_close_urlstr = urls[last_close].toString().rstrip('/')
first_tab_urlstr = first_tab_url.toString().rstrip('/')
last_close_url_used = first_tab_urlstr == last_close_urlstr
- use_current_tab = (only_one_tab_open and no_history and
- last_close_url_used)
+ use_current_tab = no_history and last_close_url_used
entries = self.undo_stack[-depth]
del self.undo_stack[-depth]
for entry in reversed(entries):
if use_current_tab:
- newtab = self.widget.widget(0)
+ newtab = self._tab_by_idx(0)
+ assert newtab is not None
use_current_tab = False
else:
newtab = self.tabopen(background=False, idx=entry.index)
@@ -536,14 +568,14 @@ class TabbedBrowser(QWidget):
if newtab or self.widget.currentWidget() is None:
self.tabopen(url, background=False)
else:
- self.widget.currentWidget().load_url(url)
+ self._current_tab().load_url(url)
@pyqtSlot(int)
def on_tab_close_requested(self, idx):
"""Close a tab via an index."""
- tab = self.widget.widget(idx)
+ tab = self._tab_by_idx(idx)
if tab is None:
- log.webview.debug( # type: ignore[unreachable]
+ log.webview.debug(
"Got invalid tab {} for index {}!".format(tab, idx))
return
self.tab_close_prompt_if_pinned(
@@ -697,10 +729,9 @@ class TabbedBrowser(QWidget):
"""
if tab.data.keep_icon:
tab.data.keep_icon = False
- else:
- if (config.cache['tabs.tabs_are_windows'] and
- tab.data.should_show_icon()):
- self.widget.window().setWindowIcon(self.default_window_icon)
+ elif (config.cache['tabs.tabs_are_windows'] and
+ tab.data.should_show_icon()):
+ self.widget.window().setWindowIcon(self.default_window_icon)
@pyqtSlot()
def _on_load_status_changed(self, tab):
@@ -777,6 +808,23 @@ class TabbedBrowser(QWidget):
if not self.widget.page_title(idx):
self.widget.set_page_title(idx, url.toDisplayString())
+ def _mode_override(self, url: QUrl) -> None:
+ """Override mode if url matches pattern.
+
+ Args:
+ url: The QUrl to match for
+ """
+ if not url.isValid():
+ return
+ mode = config.instance.get('input.mode_override', url=url)
+ if mode:
+ log.modes.debug(f"Mode change to {mode} triggered for url {url}")
+ modeman.enter(
+ self._win_id,
+ usertypes.KeyMode[mode],
+ reason='mode_override',
+ )
+
@pyqtSlot(browsertab.AbstractTab)
def _on_icon_changed(self, tab):
"""Set the icon of a tab.
@@ -800,6 +848,7 @@ class TabbedBrowser(QWidget):
mode in modeman.INPUT_MODES):
tab = self.widget.currentWidget()
if tab is not None:
+ assert isinstance(tab, browsertab.AbstractTab), tab
tab.data.input_mode = mode
@pyqtSlot(usertypes.KeyMode)
@@ -813,6 +862,7 @@ class TabbedBrowser(QWidget):
widget))
widget.setFocus()
if config.val.tabs.mode_on_change == 'restore':
+ assert isinstance(widget, browsertab.AbstractTab), widget
widget.data.input_mode = usertypes.KeyMode.normal
@pyqtSlot(int)
@@ -822,9 +872,9 @@ class TabbedBrowser(QWidget):
if idx == -1 or self.is_shutting_down:
# closing the last tab (before quitting) or shutting down
return
- tab = self.widget.widget(idx)
+ tab = self._tab_by_idx(idx)
if tab is None:
- log.webview.debug( # type: ignore[unreachable]
+ log.webview.debug(
"on_current_changed got called with invalid index {}"
.format(idx))
return
@@ -1002,7 +1052,7 @@ class TabbedBrowser(QWidget):
if key != "'":
message.error("Failed to set mark: url invalid")
return
- point = self.widget.currentWidget().scroller.pos_px()
+ point = self._current_tab().scroller.pos_px()
if key.isupper():
self._global_marks[key] = point, url
@@ -1023,7 +1073,7 @@ class TabbedBrowser(QWidget):
except qtutils.QtValueError:
urlkey = None
- tab = self.widget.currentWidget()
+ tab = self._current_tab()
if key.isupper():
if key in self._global_marks:
diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py
index 7983127d5..bbd5af8a0 100644
--- a/qutebrowser/mainwindow/tabwidget.py
+++ b/qutebrowser/mainwindow/tabwidget.py
@@ -22,13 +22,13 @@
import functools
import contextlib
import dataclasses
-from typing import Optional, cast
+from typing import Optional, Dict, Any
from PyQt5.QtCore import (pyqtSignal, pyqtSlot, Qt, QSize, QRect, QPoint,
QTimer, QUrl)
from PyQt5.QtWidgets import (QTabWidget, QTabBar, QSizePolicy, QCommonStyle,
QStyle, QStylePainter, QStyleOptionTab,
- QStyleFactory, QWidget)
+ QStyleFactory)
from PyQt5.QtGui import QIcon, QPalette, QColor
from qutebrowser.utils import qtutils, objreg, utils, usertypes, log
@@ -76,17 +76,30 @@ class TabWidget(QTabWidget):
@config.change_filter('tabs')
def _init_config(self):
"""Initialize attributes based on the config."""
- tabbar = self.tabBar()
self.setMovable(True)
self.setTabsClosable(False)
position = config.val.tabs.position
selection_behavior = config.val.tabs.select_on_remove
self.setTabPosition(position)
- tabbar.vertical = position in [ # type: ignore[attr-defined]
- QTabWidget.West, QTabWidget.East]
+
+ tabbar = self.tab_bar()
+ tabbar.vertical = position in [QTabWidget.West, QTabWidget.East]
tabbar.setSelectionBehaviorOnRemove(selection_behavior)
tabbar.refresh()
+ def tab_bar(self) -> "TabBar":
+ """Get the TabBar for this TabWidget."""
+ bar = self.tabBar()
+ assert isinstance(bar, TabBar), bar
+ return bar
+
+ def _tab_by_idx(self, idx: int) -> Optional[browsertab.AbstractTab]:
+ """Get the tab at the given index."""
+ tab = self.widget(idx)
+ if tab is not None:
+ assert isinstance(tab, browsertab.AbstractTab), tab
+ return tab
+
def set_tab_indicator_color(self, idx, color):
"""Set the tab indicator color.
@@ -94,17 +107,17 @@ class TabWidget(QTabWidget):
idx: The tab index.
color: A QColor.
"""
- bar = self.tabBar()
+ bar = self.tab_bar()
bar.set_tab_data(idx, 'indicator-color', color)
bar.update(bar.tabRect(idx))
def tab_indicator_color(self, idx):
"""Get the tab indicator color for the given index."""
- return self.tabBar().tab_indicator_color(idx)
+ return self.tab_bar().tab_indicator_color(idx)
def set_page_title(self, idx, title):
"""Set the tab title user data."""
- tabbar = self.tabBar()
+ tabbar = self.tab_bar()
if config.cache['tabs.tooltips']:
# always show only plain title in tooltips
@@ -115,7 +128,7 @@ class TabWidget(QTabWidget):
def page_title(self, idx):
"""Get the tab title user data."""
- return self.tabBar().page_title(idx)
+ return self.tab_bar().page_title(idx)
def update_tab_title(self, idx, field=None):
"""Update the tab text for the given tab.
@@ -126,7 +139,8 @@ class TabWidget(QTabWidget):
is only set if the given field is in the template.
"""
assert idx != -1
- tab = self.widget(idx)
+ tab = self._tab_by_idx(idx)
+ assert tab is not None
if tab.data.pinned:
fmt = config.cache['tabs.title.format_pinned']
else:
@@ -136,29 +150,40 @@ class TabWidget(QTabWidget):
(fmt is None or ('{' + field + '}') not in fmt)):
return
+ def right_align(num):
+ return str(num).rjust(len(str(self.count())))
+
+ def left_align(num):
+ return str(num).ljust(len(str(self.count())))
+
+ bar = self.tab_bar()
+ cur_idx = bar.currentIndex()
+ if idx == cur_idx:
+ rel_idx = left_align(idx + 1) + " "
+ else:
+ rel_idx = " " + right_align(abs(idx - cur_idx))
+
fields = self.get_tab_fields(idx)
fields['current_title'] = fields['current_title'].replace('&', '&&')
fields['index'] = idx + 1
- fields['aligned_index'] = str(idx + 1).rjust(len(str(self.count())))
+ fields['aligned_index'] = right_align(idx + 1)
+ fields['relative_index'] = rel_idx
title = '' if fmt is None else fmt.format(**fields)
- tabbar = self.tabBar()
# Only change the tab title if it changes, setting the tab title causes
# a size recalculation which is slow.
- if tabbar.tabText(idx) != title:
- tabbar.setTabText(idx, title)
+ if bar.tabText(idx) != title:
+ bar.setTabText(idx, title)
def get_tab_fields(self, idx):
"""Get the tab field data."""
- tab = self.widget(idx)
- if tab is None:
- log.misc.debug( # type: ignore[unreachable]
- "Got None-tab in get_tab_fields!")
+ tab = self._tab_by_idx(idx)
+ assert tab is not None
page_title = self.page_title(idx)
- fields = {}
+ fields: Dict[str, Any] = {}
fields['id'] = tab.tab_id
fields['current_title'] = page_title
fields['title_sep'] = ' - ' if page_title else ''
@@ -193,9 +218,7 @@ class TabWidget(QTabWidget):
fields['protocol'] = url.scheme()
y = tab.scroller.pos_perc()[1]
- if y is None:
- scroll_pos = '???'
- elif y <= 0:
+ if y <= 0:
scroll_pos = 'top'
elif y >= 100:
scroll_pos = 'bot'
@@ -215,7 +238,7 @@ class TabWidget(QTabWidget):
non-visible. To avoid flickering, disable repaint updates while we
work.
"""
- bar = self.tabBar()
+ bar = self.tab_bar()
toggle = (self.count() > 10 and
not bar.drag_in_progress and
bar.isVisible())
@@ -304,7 +327,8 @@ class TabWidget(QTabWidget):
@pyqtSlot(int)
def _on_current_changed(self, index):
"""Emit the tab_index_changed signal if the current tab changed."""
- self.tabBar().on_current_changed()
+ self.tab_bar().on_current_changed()
+ self.update_tab_titles()
self.tab_index_changed.emit(index, self.count())
@pyqtSlot()
@@ -318,16 +342,13 @@ class TabWidget(QTabWidget):
Return:
The tab URL as QUrl.
"""
- tab = self.widget(idx)
- if tab is None:
- url = QUrl() # type: ignore[unreachable]
- else:
- url = tab.url()
+ tab = self._tab_by_idx(idx)
+ url = QUrl() if tab is None else tab.url()
# It's possible for url to be invalid, but the caller will handle that.
qtutils.ensure_valid(url)
return url
- def update_tab_favicon(self, tab: QWidget) -> None:
+ def update_tab_favicon(self, tab: browsertab.AbstractTab) -> None:
"""Update favicon of the given tab."""
idx = self.indexOf(tab)
@@ -339,11 +360,11 @@ class TabWidget(QTabWidget):
def setTabIcon(self, idx: int, icon: QIcon) -> None:
"""Always show tab icons for pinned tabs in some circumstances."""
- tab = cast(Optional[browsertab.AbstractTab], self.widget(idx))
+ tab = self._tab_by_idx(idx)
if (icon.isNull() and
config.cache['tabs.favicons.show'] != 'never' and
config.cache['tabs.pinned.shrink'] and
- not self.tabBar().vertical and
+ not self.tab_bar().vertical and
tab is not None and tab.data.pinned):
icon = self.style().standardIcon(QStyle.SP_FileIcon)
super().setTabIcon(idx, icon)
@@ -390,19 +411,37 @@ class TabBar(QTabBar):
self._auto_hide_timer.timeout.connect(self.maybe_hide)
self._on_show_switching_delay_changed()
self.setAutoFillBackground(True)
- self.drag_in_progress = False
+ # FIXME:mypy Is it a mypy bug that we need to specify bool here?
+ # Otherwise, we get "Cannot determine type of "drag_in_progress" in
+ # TabWidget._toggle_visibility below.
+ self.drag_in_progress: bool = False
stylesheet.set_register(self)
self.ensurePolished()
config.instance.changed.connect(self._on_config_changed)
self._set_icon_size()
QTimer.singleShot(0, self.maybe_hide)
+ self._minimum_tab_size_hint_helper = functools.lru_cache(maxsize=2**9)(
+ self._minimum_tab_size_hint_helper_uncached
+ )
+ debugcachestats.register(name=f'tab width cache (win_id={win_id})')(
+ self._minimum_tab_size_hint_helper
+ )
+ self._minimum_tab_height = functools.lru_cache(maxsize=1)(
+ self._minimum_tab_height_uncached
+ )
def __repr__(self):
return utils.get_repr(self, count=self.count())
+ def _tab_widget(self):
+ """Get the TabWidget we're in."""
+ parent = self.parent()
+ assert isinstance(parent, TabWidget), parent
+ return parent
+
def _current_tab(self):
"""Get the current tab object."""
- return self.parent().currentWidget()
+ return self._tab_widget().currentWidget()
@pyqtSlot(str)
def _on_config_changed(self, option: str) -> None:
@@ -486,7 +525,6 @@ class TabBar(QTabBar):
Args:
idx: The tab index to get the title for.
- handle_unset: Whether to return an empty string on KeyError.
"""
try:
return self.tab_data(idx, 'page-title')
@@ -562,11 +600,9 @@ class TabBar(QTabBar):
icon_width, ellipsis,
pinned)
- @debugcachestats.register(name='tab width cache')
- @functools.lru_cache(maxsize=2**9)
- def _minimum_tab_size_hint_helper(self, tab_text: str,
- icon_width: int,
- ellipsis: bool, pinned: bool) -> QSize:
+ def _minimum_tab_size_hint_helper_uncached(self, tab_text: str,
+ icon_width: int,
+ ellipsis: bool, pinned: bool) -> QSize:
"""Helper function to cache tab results.
Config values accessed in here should be added to _on_config_changed to
@@ -597,8 +633,7 @@ class TabBar(QTabBar):
width = max(min_width, width)
return QSize(width, height)
- @functools.lru_cache(maxsize=1)
- def _minimum_tab_height(self):
+ def _minimum_tab_height_uncached(self):
padding = config.cache['tabs.padding']
return self.fontMetrics().height() + padding.top + padding.bottom
@@ -608,7 +643,7 @@ class TabBar(QTabBar):
raise IndexError("Tab index ({}) out of range ({})!".format(
index, self.count()))
- widget = self.parent().widget(index)
+ widget = self._tab_widget().widget(index)
if widget is None:
# This could happen when Qt calls tabSizeHint while initializing
# tabs.
diff --git a/qutebrowser/misc/checkpyver.py b/qutebrowser/misc/checkpyver.py
index e9a7a1eef..7d6a524c3 100644
--- a/qutebrowser/misc/checkpyver.py
+++ b/qutebrowser/misc/checkpyver.py
@@ -43,11 +43,11 @@ except ImportError: # pragma: no cover
# to stderr.
def check_python_version():
"""Check if correct python version is run."""
- if sys.hexversion < 0x03060100:
+ if sys.hexversion < 0x03070000:
# We don't use .format() and print_function here just in case someone
# still has < 2.6 installed.
version_str = '.'.join(map(str, sys.version_info[:3]))
- text = ("At least Python 3.6.1 is required to run qutebrowser, but " +
+ text = ("At least Python 3.7 is required to run qutebrowser, but " +
"it's running with " + version_str + ".\n")
if Tk and '--no-err-windows' not in sys.argv: # pragma: no cover
root = Tk()
diff --git a/qutebrowser/misc/crashsignal.py b/qutebrowser/misc/crashsignal.py
index 45c52f54c..a79118044 100644
--- a/qutebrowser/misc/crashsignal.py
+++ b/qutebrowser/misc/crashsignal.py
@@ -100,7 +100,7 @@ class CrashHandler(QObject):
# There's no log file, so we can use this to display crashes to
# the user on the next start.
self._init_crashlogfile()
- except OSError:
+ except (OSError, UnicodeDecodeError):
log.init.exception("Error while handling crash log file!")
self._init_crashlogfile()
@@ -150,6 +150,7 @@ class CrashHandler(QObject):
"""Start a new logfile and redirect faulthandler to it."""
logname = os.path.join(standarddir.data(), 'crash.log')
try:
+ # pylint: disable=consider-using-with
self._crash_log_file = open(logname, 'w', encoding='ascii')
except OSError:
log.init.exception("Error while opening crash log file!")
@@ -244,7 +245,7 @@ class CrashHandler(QObject):
if 'pdb-postmortem' in objects.debug_flags:
if tb is None:
- pdb.set_trace() # noqa: T100
+ pdb.set_trace() # noqa: T100 pylint: disable=forgotten-debug-statement
else:
pdb.post_mortem(tb)
@@ -360,8 +361,7 @@ class SignalHandler(QObject):
self._notifier = QSocketNotifier(cast(sip.voidptr, read_fd),
QSocketNotifier.Read,
self)
- self._notifier.activated.connect( # type: ignore[attr-defined]
- self.handle_signal_wakeup)
+ self._notifier.activated.connect(self.handle_signal_wakeup)
self._orig_wakeup_fd = signal.set_wakeup_fd(write_fd)
# pylint: enable=import-error,no-member,useless-suppression
else:
diff --git a/qutebrowser/misc/debugcachestats.py b/qutebrowser/misc/debugcachestats.py
index 2004ad7ab..9090bd0ea 100644
--- a/qutebrowser/misc/debugcachestats.py
+++ b/qutebrowser/misc/debugcachestats.py
@@ -23,11 +23,15 @@ Because many modules depend on this command, this needs to have as few
dependencies as possible to avoid cyclic dependencies.
"""
-from typing import Any, Callable, List, Optional, Tuple, TypeVar
+import weakref
+import sys
+from typing import Any, Callable, Optional, TypeVar, Mapping
+from qutebrowser.utils import log
-# The second element of each tuple should be a lru_cache wrapped function
-_CACHE_FUNCTIONS: List[Tuple[str, Any]] = []
+
+# The callable should be a lru_cache wrapped function
+_CACHE_FUNCTIONS: Mapping[str, Any] = weakref.WeakValueDictionary()
_T = TypeVar('_T', bound=Callable[..., Any])
@@ -36,13 +40,21 @@ _T = TypeVar('_T', bound=Callable[..., Any])
def register(name: Optional[str] = None) -> Callable[[_T], _T]:
"""Register a lru_cache wrapped function for debug_cache_stats."""
def wrapper(fn: _T) -> _T:
- _CACHE_FUNCTIONS.append((fn.__name__ if name is None else name, fn))
- return fn
+ fn_name = fn.__name__ if name is None else name
+ if sys.version_info < (3, 9):
+ log.misc.vdebug( # type: ignore[attr-defined]
+ "debugcachestats not supported on python < 3.9, not adding '%s'",
+ fn_name,
+ )
+ return fn
+
+ else:
+ _CACHE_FUNCTIONS[fn_name] = fn
+ return fn
return wrapper
def debug_cache_stats() -> None:
"""Print LRU cache stats."""
- from qutebrowser.utils import log
- for name, fn in _CACHE_FUNCTIONS:
+ for name, fn in _CACHE_FUNCTIONS.items():
log.misc.info('{}: {}'.format(name, fn.cache_info()))
diff --git a/qutebrowser/misc/earlyinit.py b/qutebrowser/misc/earlyinit.py
index c4ff0bb85..4b3df4db2 100644
--- a/qutebrowser/misc/earlyinit.py
+++ b/qutebrowser/misc/earlyinit.py
@@ -19,7 +19,7 @@
"""Things which need to be done really early (e.g. before importing Qt).
-At this point we can be sure we have all python 3.6.1 features available.
+At this point we can be sure we have all python 3.7 features available.
"""
try:
@@ -109,19 +109,25 @@ def init_faulthandler(fileobj=sys.__stderr__):
when sys.stderr got replaced, e.g. by "Python Tools for Visual Studio".
Args:
- fobj: An opened file object to write the traceback to.
+ fileobj: An opened file object to write the traceback to.
"""
- if fileobj is None:
+ try:
+ faulthandler.enable(fileobj)
+ except (RuntimeError, AttributeError):
# When run with pythonw.exe, sys.__stderr__ can be None:
# https://docs.python.org/3/library/sys.html#sys.__stderr__
- # If we'd enable faulthandler in that case, we just get a weird
- # exception, so we don't enable faulthandler if we have no stdout.
+ #
+ # With PyInstaller, it can be a NullWriter raising AttributeError on
+ # fileno: https://github.com/pyinstaller/pyinstaller/issues/4481
#
# Later when we have our data dir available we re-enable faulthandler
# to write to a file so we can display a crash to the user at the next
# start.
+ #
+ # Note that we don't have any logging initialized yet at this point, so
+ # this is a silent error.
return
- faulthandler.enable(fileobj)
+
if (hasattr(faulthandler, 'register') and hasattr(signal, 'SIGUSR1') and
sys.stderr is not None):
# If available, we also want a traceback on SIGUSR1.
@@ -205,7 +211,6 @@ def _check_modules(modules):
for name, text in modules.items():
try:
- # pylint: disable=bad-continuation
with log.py_warning_filter(
category=DeprecationWarning,
message=r'invalid escape sequence'
@@ -220,7 +225,6 @@ def _check_modules(modules):
category=DeprecationWarning,
message=r'Creating a LegacyVersion has been deprecated',
):
- # pylint: enable=bad-continuation
importlib.import_module(name)
except ImportError as e:
_die(text, e)
@@ -231,7 +235,6 @@ def check_libraries():
modules = {
'jinja2': _missing_str("jinja2"),
'yaml': _missing_str("PyYAML"),
- 'dataclasses': _missing_str("dataclasses"),
'PyQt5.QtQml': _missing_str("PyQt5.QtQml"),
'PyQt5.QtSql': _missing_str("PyQt5.QtSql"),
'PyQt5.QtOpenGL': _missing_str("PyQt5.QtOpenGL"),
diff --git a/qutebrowser/misc/editor.py b/qutebrowser/misc/editor.py
index d561a7b96..a4bc1d8ab 100644
--- a/qutebrowser/misc/editor.py
+++ b/qutebrowser/misc/editor.py
@@ -135,7 +135,7 @@ class ExternalEditor(QObject):
message.error("Failed to create initial file: {}".format(e))
return
- self._remove_file = True
+ self._remove_file = config.val.editor.remove_file
line, column = self._calc_line_and_column(text, caret_position)
self._start_editor(line=line, column=column)
@@ -207,8 +207,7 @@ class ExternalEditor(QObject):
if not ok:
log.procs.error("Failed to watch path: {}"
.format(self._filename))
- self._watcher.fileChanged.connect( # type: ignore[attr-defined]
- self._on_file_changed)
+ self._watcher.fileChanged.connect(self._on_file_changed)
args = [self._sub_placeholder(arg, line, column) for arg in editor[1:]]
log.procs.debug("Calling \"{}\" with args {}".format(executable, args))
diff --git a/qutebrowser/misc/elf.py b/qutebrowser/misc/elf.py
index bf824880a..8fadbcffd 100644
--- a/qutebrowser/misc/elf.py
+++ b/qutebrowser/misc/elf.py
@@ -270,7 +270,7 @@ def _find_versions(data: bytes) -> Versions:
correctly: https://github.com/python/typeshed/issues/1467
"""
match = re.search(
- br'QtWebEngine/([0-9.]+) Chrome/([0-9.]+)',
+ br'\x00QtWebEngine/([0-9.]+) Chrome/([0-9.]+)\x00',
data,
)
if match is None:
diff --git a/qutebrowser/misc/guiprocess.py b/qutebrowser/misc/guiprocess.py
index e5ccd1b8b..e14169f93 100644
--- a/qutebrowser/misc/guiprocess.py
+++ b/qutebrowser/misc/guiprocess.py
@@ -22,12 +22,13 @@
import dataclasses
import locale
import shlex
+import shutil
from typing import Mapping, Sequence, Dict, Optional
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, QObject, QProcess,
QProcessEnvironment, QByteArray, QUrl, Qt)
-from qutebrowser.utils import message, log, utils, usertypes
+from qutebrowser.utils import message, log, utils, usertypes, version
from qutebrowser.api import cmdutils, apitypes
from qutebrowser.completion.models import miscmodels
@@ -168,6 +169,7 @@ class GUIProcess(QObject):
self._output_messages = output_messages
self.outcome = ProcessOutcome(what=what)
self.cmd: Optional[str] = None
+ self.resolved_cmd: Optional[str] = None
self.args: Optional[Sequence[str]] = None
self.pid: Optional[int] = None
@@ -262,18 +264,19 @@ class GUIProcess(QObject):
QProcess.Crashed: f"{what.capitalize()} crashed",
QProcess.Timedout: f"{what.capitalize()} timed out",
QProcess.WriteError: f"Write error for {what}",
- QProcess.WriteError: f"Read error for {what}",
+ QProcess.ReadError: f"Read error for {what}",
}
error_string = self._proc.errorString()
msg = ': '.join([error_descriptions[error], error_string])
# We can't get some kind of error code from Qt...
# https://bugreports.qt.io/browse/QTBUG-44769
- # However, it looks like those strings aren't actually translated?
- known_errors = ['No such file or directory', 'Permission denied']
- if (': ' in error_string and # pragma: no branch
- error_string.split(': ', maxsplit=1)[1] in known_errors):
- msg += f'\n(Hint: Make sure {self.cmd!r} exists and is executable)'
+ # but we pre-resolve the executable in Python, which also checks if it's
+ # runnable.
+ if self.resolved_cmd is None: # pragma: no branch
+ msg += f'\nHint: Make sure {self.cmd!r} exists and is executable'
+ if version.is_flatpak():
+ msg += ' inside the Flatpak container'
message.error(msg)
@@ -332,10 +335,23 @@ class GUIProcess(QObject):
self.outcome.running = True
def _pre_start(self, cmd: str, args: Sequence[str]) -> None:
- """Prepare starting of a QProcess."""
+ """Resolve the given command and prepare starting of a QProcess.
+
+ Doing the resolving in Python here instead of letting Qt do it serves
+ two purposes:
+
+ - Being able to show a nicer error message without having to parse the
+ string we get from Qt: https://bugreports.qt.io/browse/QTBUG-44769
+ - Not running the file from the current directory on Unix with
+ Qt < 5.15.? and 6.2.4, as a WORKAROUND for CVE-2022-25255:
+ https://invent.kde.org/qt/qt/qtbase/-/merge_requests/139
+ https://www.qt.io/blog/security-advisory-qprocess
+ https://lists.qt-project.org/pipermail/announce/2022-February/000333.html
+ """
if self.outcome.running:
raise ValueError("Trying to start a running QProcess!")
self.cmd = cmd
+ self.resolved_cmd = shutil.which(cmd)
self.args = args
log.procs.debug(f"Executing: {self}")
if self.verbose:
@@ -345,7 +361,10 @@ class GUIProcess(QObject):
"""Convenience wrapper around QProcess::start."""
log.procs.debug("Starting process.")
self._pre_start(cmd, args)
- self._proc.start(cmd, args)
+ self._proc.start(
+ self.resolved_cmd, # type: ignore[arg-type]
+ args,
+ )
self._post_start()
self._proc.closeWriteChannel()
@@ -354,7 +373,10 @@ class GUIProcess(QObject):
log.procs.debug("Starting detached.")
self._pre_start(cmd, args)
ok, self.pid = self._proc.startDetached(
- cmd, args, None) # type: ignore[call-arg]
+ self.resolved_cmd,
+ args,
+ None, # workingDirectory
+ ) # type: ignore[call-arg]
if not ok:
message.error("Error while spawning {}".format(self.what))
diff --git a/qutebrowser/misc/ipc.py b/qutebrowser/misc/ipc.py
index 77b9e8f6c..8c5c05046 100644
--- a/qutebrowser/misc/ipc.py
+++ b/qutebrowser/misc/ipc.py
@@ -191,8 +191,7 @@ class IPCServer(QObject):
self._atime_timer.setTimerType(Qt.VeryCoarseTimer)
self._server = QLocalServer(self)
- self._server.newConnection.connect( # type: ignore[attr-defined]
- self.handle_connection)
+ self._server.newConnection.connect(self.handle_connection)
self._socket = None
self._old_socket = None
@@ -270,18 +269,16 @@ class IPCServer(QObject):
log.ipc.debug("Client connected (socket 0x{:x}).".format(id(socket)))
self._socket = socket
self._timer.start()
- socket.readyRead.connect( # type: ignore[attr-defined]
- self.on_ready_read)
+ socket.readyRead.connect(self.on_ready_read)
if socket.canReadLine():
log.ipc.debug("We can read a line immediately.")
self.on_ready_read()
- socket.error.connect(self.on_error) # type: ignore[attr-defined]
- if socket.error() not in [QLocalSocket.UnknownSocketError,
- QLocalSocket.PeerClosedError]:
+ socket.error.connect(self.on_error)
+ if socket.error() not in [ # type: ignore[operator]
+ QLocalSocket.UnknownSocketError, QLocalSocket.PeerClosedError]:
log.ipc.debug("We got an error immediately.")
- self.on_error(socket.error())
- socket.disconnected.connect( # type: ignore[attr-defined]
- self.on_disconnected)
+ self.on_error(socket.error()) # type: ignore[operator]
+ socket.disconnected.connect(self.on_disconnected)
if socket.state() == QLocalSocket.UnconnectedState:
log.ipc.debug("Socket was disconnected immediately.")
self.on_disconnected()
diff --git a/qutebrowser/misc/keyhintwidget.py b/qutebrowser/misc/keyhintwidget.py
index 4fcef72e4..93d9af09d 100644
--- a/qutebrowser/misc/keyhintwidget.py
+++ b/qutebrowser/misc/keyhintwidget.py
@@ -87,6 +87,7 @@ class KeyHintView(QLabel):
"""Show hints for the given prefix (or hide if prefix is empty).
Args:
+ mode: The key mode to show the keyhints for.
prefix: The current partial keystring.
"""
match = re.fullmatch(r'(\d*)(.*)', prefix)
diff --git a/qutebrowser/misc/lineparser.py b/qutebrowser/misc/lineparser.py
index 9d35692e9..fee87354f 100644
--- a/qutebrowser/misc/lineparser.py
+++ b/qutebrowser/misc/lineparser.py
@@ -99,6 +99,7 @@ class BaseLineParser(QObject):
self._opened = True
try:
if self._binary:
+ # pylint: disable=unspecified-encoding
with open(self._configfile, mode + 'b') as f:
yield f
else:
diff --git a/qutebrowser/misc/miscwidgets.py b/qutebrowser/misc/miscwidgets.py
index b89288fc7..4354ed2ab 100644
--- a/qutebrowser/misc/miscwidgets.py
+++ b/qutebrowser/misc/miscwidgets.py
@@ -213,7 +213,7 @@ class _FoldArrow(QWidget):
"""Paint the arrow.
Args:
- _paint: The QPaintEvent (unused).
+ _event: The QPaintEvent (unused).
"""
opt = QStyleOption()
opt.initFrom(self)
diff --git a/qutebrowser/misc/msgbox.py b/qutebrowser/misc/msgbox.py
index 9d5fbf601..4271c2639 100644
--- a/qutebrowser/misc/msgbox.py
+++ b/qutebrowser/misc/msgbox.py
@@ -42,6 +42,7 @@ def msgbox(parent, title, text, *, icon, buttons=QMessageBox.Ok,
parent: The parent to set for the message box.
title: The title to set.
text: The text to set.
+ icon: The QIcon to show in the box.
buttons: The buttons to set (QMessageBox::StandardButtons)
on_finished: A slot to connect to the 'finished' signal.
plain_text: Whether to force plain text (True) or rich text (False).
diff --git a/qutebrowser/misc/objects.py b/qutebrowser/misc/objects.py
index 00a1ef35d..63a9cb2dd 100644
--- a/qutebrowser/misc/objects.py
+++ b/qutebrowser/misc/objects.py
@@ -26,7 +26,7 @@ import argparse
from typing import TYPE_CHECKING, Any, Dict, Set, Union, cast
if TYPE_CHECKING:
- from PyQt5.QtWidgets import QApplication
+ from qutebrowser import app
from qutebrowser.utils import usertypes
from qutebrowser.commands import command
@@ -47,4 +47,4 @@ backend: Union['usertypes.Backend', NoBackend] = NoBackend()
commands: Dict[str, 'command.Command'] = {}
debug_flags: Set[str] = set()
args = cast(argparse.Namespace, None)
-qapp = cast('QApplication', None)
+qapp = cast('app.Application', None)
diff --git a/qutebrowser/misc/quitter.py b/qutebrowser/misc/quitter.py
index a51891685..905429989 100644
--- a/qutebrowser/misc/quitter.py
+++ b/qutebrowser/misc/quitter.py
@@ -194,7 +194,7 @@ class Quitter(QObject):
# Open a new process and immediately shutdown the existing one
try:
args = self._get_restart_args(pages, session, override_args)
- subprocess.Popen(args)
+ subprocess.Popen(args) # pylint: disable=consider-using-with
except OSError:
log.destroy.exception("Failed to restart")
return False
diff --git a/qutebrowser/misc/savemanager.py b/qutebrowser/misc/savemanager.py
index ee22ba14d..1b72734cb 100644
--- a/qutebrowser/misc/savemanager.py
+++ b/qutebrowser/misc/savemanager.py
@@ -157,6 +157,7 @@ class SaveManager(QObject):
"""Save a saveable by name.
Args:
+ name: The name of the saveable to save.
is_exit: Whether we're currently exiting qutebrowser.
explicit: Whether this save operation was triggered explicitly.
silent: Don't write information to log. Used to reduce log spam
diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py
index 11af329e0..a28f3a848 100644
--- a/qutebrowser/misc/sessions.py
+++ b/qutebrowser/misc/sessions.py
@@ -206,12 +206,11 @@ class SessionManager(QObject):
if item.title():
data['title'] = item.title()
- else:
+ elif tab.history.current_idx() == idx:
# https://github.com/qutebrowser/qutebrowser/issues/879
- if tab.history.current_idx() == idx:
- data['title'] = tab.title()
- else:
- data['title'] = data['url']
+ data['title'] = tab.title()
+ else:
+ data['title'] = data['url']
if item.originalUrl() != item.url():
encoded = item.originalUrl().toEncoded()
diff --git a/qutebrowser/misc/split.py b/qutebrowser/misc/split.py
index 4db91360e..c7d93e76d 100644
--- a/qutebrowser/misc/split.py
+++ b/qutebrowser/misc/split.py
@@ -128,6 +128,7 @@ def split(s, keep=False):
"""Split a string via ShellLexer.
Args:
+ s: The string to split.
keep: Whether to keep special chars in the split output.
"""
lexer = ShellLexer(s)
diff --git a/qutebrowser/misc/sql.py b/qutebrowser/misc/sql.py
index 814eb2bb0..062da39de 100644
--- a/qutebrowser/misc/sql.py
+++ b/qutebrowser/misc/sql.py
@@ -479,14 +479,11 @@ class SqlTable(QObject):
Args:
field: Field to use as the key.
value: Key value to delete.
-
- Return:
- The number of rows deleted.
"""
q = self.database.query(f"DELETE FROM {self._name} where {field} = :val")
q.run(val=value)
if not q.rows_affected():
- raise KeyError('No row with {field} = "{value}"')
+ raise KeyError(f'No row with {field} = {value!r}')
self.changed.emit()
def _insert_query(self, values: Mapping[str, Any], replace: bool) -> Query:
diff --git a/qutebrowser/misc/throttle.py b/qutebrowser/misc/throttle.py
index ac565b68d..16a14d4bd 100644
--- a/qutebrowser/misc/throttle.py
+++ b/qutebrowser/misc/throttle.py
@@ -85,6 +85,7 @@ class Throttle(QObject):
(cur_time_ms - self._last_call_ms))
# Disconnect any existing calls, continue if no connections.
try:
+ # pylint: disable=no-value-for-parameter
self._timer.timeout.disconnect()
except TypeError:
pass
diff --git a/qutebrowser/misc/utilcmds.py b/qutebrowser/misc/utilcmds.py
index 7ba45bdc3..14c02864e 100644
--- a/qutebrowser/misc/utilcmds.py
+++ b/qutebrowser/misc/utilcmds.py
@@ -23,6 +23,7 @@
import functools
import os
+import sys
import traceback
from typing import Optional
@@ -125,7 +126,9 @@ def debug_all_objects() -> None:
@cmdutils.register(debug=True)
def debug_cache_stats() -> None:
"""Print LRU cache stats."""
- debugcachestats.debug_cache_stats()
+ if sys.version_info < (3, 9):
+ raise cmdutils.CommandError('debugcachestats not supported on python < 3.9')
+ debugcachestats.debug_cache_stats() # type: ignore[unreachable]
@cmdutils.register(debug=True)
diff --git a/qutebrowser/qutebrowser.py b/qutebrowser/qutebrowser.py
index d0819f832..c576c4a06 100644
--- a/qutebrowser/qutebrowser.py
+++ b/qutebrowser/qutebrowser.py
@@ -87,6 +87,11 @@ def get_argparser():
help="Set the base name of the desktop entry for this "
"application. Used to set the app_id under Wayland. See "
"https://doc.qt.io/qt-5/qguiapplication.html#desktopFileName-prop")
+ parser.add_argument('--untrusted-args',
+ action='store_true',
+ help="Mark all following arguments as untrusted, which "
+ "enforces that they are URLs/search terms (and not flags or "
+ "commands)")
parser.add_argument('--json-args', help=argparse.SUPPRESS)
parser.add_argument('--temp-basedir-restarted',
@@ -207,7 +212,27 @@ def _unpack_json_args(args):
return argparse.Namespace(**new_args)
+def _validate_untrusted_args(argv):
+ # NOTE: Do not use f-strings here, as this should run with older Python
+ # versions (so that a proper error can be displayed)
+ try:
+ untrusted_idx = argv.index('--untrusted-args')
+ except ValueError:
+ return
+
+ rest = argv[untrusted_idx + 1:]
+ if len(rest) > 1:
+ sys.exit(
+ "Found multiple arguments ({}) after --untrusted-args, "
+ "aborting.".format(' '.join(rest)))
+
+ for arg in rest:
+ if arg.startswith(('-', ':')):
+ sys.exit("Found {} after --untrusted-args, aborting.".format(arg))
+
+
def main():
+ _validate_untrusted_args(sys.argv)
parser = get_argparser()
argv = sys.argv[1:]
args = parser.parse_args(argv)
diff --git a/qutebrowser/resources.py b/qutebrowser/resources.py
deleted file mode 100644
index ebe256809..000000000
--- a/qutebrowser/resources.py
+++ /dev/null
@@ -1,5474 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Resource object code
-#
-# Created by: The Resource Compiler for PyQt5 (Qt v5.6.0)
-#
-# WARNING! All changes made in this file will be lost!
-
-from PyQt5 import QtCore
-
-qt_resource_data = b"\
-\x00\x00\x22\xfe\
-\x89\
-\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
-\x00\x00\x80\x00\x00\x00\x80\x08\x06\x00\x00\x00\xc3\x3e\x61\xcb\
-\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
-\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\
-\x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
-\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
-\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x20\x00\x49\x44\
-\x41\x54\x78\x9c\xed\x9d\x77\x7c\x5c\xd5\x95\xf8\xbf\xf7\xcd\x8c\
-\xa4\x51\xef\xdd\x72\xaf\x72\x93\x3b\xc6\x18\x1b\xdb\x31\xc5\x10\
-\x4c\xc0\x21\x64\x03\x84\xb0\xc9\x26\x6c\xd8\x90\x6c\x60\x7f\xd9\
-\x7c\x36\x24\xd9\x92\xfc\xb2\x69\x64\x61\x37\xc1\xa1\x98\xc0\x62\
-\x02\x36\x60\x83\x0d\xb8\x60\x83\xbb\x2d\xc9\xd8\x72\xc1\x45\xb6\
-\xac\x66\xd5\x51\x9d\xd1\x94\x77\xf6\x8f\xc1\x46\xb2\x66\x34\x33\
-\x7a\x33\x92\x9c\xf0\xfd\x7c\xfc\xb1\xf4\xde\xbb\x45\x73\xcf\xdc\
-\x7b\xde\xb9\xe7\x9c\xab\xf8\x0b\x43\x44\xd4\x8b\x1f\x31\x17\x97\
-\xbe\xc8\xa9\x33\xc5\xa9\xab\x31\x5d\xba\xca\xd5\x3d\x12\xef\x11\
-\x15\xed\x11\x2c\xba\x07\xcd\x03\xca\xe5\x41\x69\x80\xc9\x84\x98\
-\x35\x11\x4d\xa1\x6b\x0a\x97\xd5\xac\xda\xe6\x14\xc8\x4e\x93\x26\
-\x15\x08\xe7\x94\xc8\x59\xb7\xc5\x74\x7c\x56\x26\xe5\x4a\x29\x19\
-\xec\xbf\x31\x9c\xa8\xc1\xee\x80\x51\x36\x1e\x91\x89\x8d\x5d\xfa\
-\x7d\x0e\x8f\x5a\xd2\xde\xc5\xf8\x0e\xa7\x8a\x77\xeb\xc6\xff\xae\
-\x09\x19\xc2\x84\xcc\x9e\x63\x2d\xd0\xa6\x90\x23\x22\xea\x80\x86\
-\xec\x12\xdd\xb4\x6b\x46\x81\xaa\x36\xda\xd6\x60\x72\xd5\x09\xc0\
-\x9a\xc3\x12\x87\x47\xff\x76\x47\x97\x76\x77\x7b\x17\x13\xda\x5d\
-\x44\x4b\x84\xbe\x93\x37\x8c\xd1\x49\x8c\xee\xfb\x19\x81\x93\x9a\
-\xb0\x49\xd7\xf4\xcd\x89\x1d\xe6\xf7\xc7\x8e\x55\x5d\x91\xe9\x4d\
-\x64\xb8\x2a\x04\xe0\x95\xdd\x62\x75\x44\xeb\xdf\x6d\x77\x69\xf7\
-\x36\x77\x32\xb6\xcb\x1d\xf9\x7e\x47\x9b\x61\xd9\x58\x1d\xb3\x16\
-\x42\x21\x45\x0b\xf0\x86\xd2\xe5\x15\x4f\xad\xe9\xdd\x59\xb3\x94\
-\x2b\x52\xfd\x0b\x17\x43\x5a\x00\xb6\x9c\x95\xa9\x95\xcd\xf2\xcb\
-\x4a\x9b\xba\xc1\xe9\x21\x94\xa1\x30\xcc\xac\x7c\x21\x3f\xc9\xd0\
-\xd4\x72\x11\x58\xab\x69\xda\x1f\xa6\xe7\xa8\xb2\x30\x75\x2b\xec\
-\x0c\x39\x01\x78\xfc\x71\xd1\x72\x6f\xe4\xb1\x96\x2e\x1e\x6d\xef\
-\x22\x79\x20\x34\x2e\x93\x82\x11\xa9\xc2\xc8\x54\xa1\xa4\x5a\xc3\
-\x66\x87\xcf\x8d\xd5\x89\x36\x87\xab\x05\xd9\x25\xf0\xf3\x19\xb9\
-\xa6\x8d\x43\x4d\x89\x1c\x32\x02\xf0\x78\x99\x44\xe5\x74\xf0\x0b\
-\x5b\x27\x5f\xef\x70\x12\x13\x8e\x3a\x2d\x1a\xc4\x45\x83\x08\xb4\
-\x3b\xc1\xa3\x43\x42\x34\x98\xba\xcd\x25\x69\xb1\xc2\xa8\x34\x21\
-\xd6\x02\x27\xeb\x14\x27\x1b\x14\xf3\x0a\x84\xac\xf8\x88\x8c\x53\
-\xb1\x42\x7e\x32\x3d\xd7\xf4\xe6\x50\x11\x84\xc1\x17\x00\x11\xf5\
-\x3f\x7b\xf5\xff\x6c\xb2\xab\xbf\x77\xb8\x55\x54\xb8\xaa\xcd\x49\
-\x14\x66\xe5\xc9\xe5\xc1\x6e\xeb\x82\x03\x95\x1a\xf3\x86\xe9\xc4\
-\x5e\xd1\x8a\x08\x1c\xae\x55\x9c\x6b\xf2\x7e\x1c\x63\xd3\x84\xac\
-\x04\x21\x35\x16\xb4\x88\x7c\x42\x72\x50\x94\xfc\x70\x66\xae\xe5\
-\x9d\x48\xd4\x1e\x0a\x83\x2a\x00\xcf\xee\x97\x3b\x1a\xec\xac\x6e\
-\xeb\x22\x25\xdc\x75\x27\xc6\x40\x7c\x94\x20\xc0\xc4\x0c\x21\x31\
-\x06\x6a\x5a\x15\x26\x0d\x32\xbb\x7d\xbb\x05\x28\xa9\x52\x54\xd8\
-\x7a\x7f\x14\x23\x53\x84\x69\xb9\x91\xfc\xa2\xaa\x2d\x26\x93\x7a\
-\x64\x5a\xb6\x3a\x1a\xc1\x46\xfa\xee\xc1\x60\x34\xfa\xdb\x0f\xa5\
-\x40\x53\xb2\xb1\xa1\x53\x4d\x89\xd4\x2b\xdc\x25\xac\x16\x58\x34\
-\xca\xff\x7a\x7e\xa2\x5e\x71\xa2\xce\xf7\xc7\x30\x31\x53\x18\x9f\
-\x11\xe1\x0e\x2a\x5c\x4a\xf8\xb5\x5d\xd7\x1e\x9f\x3f\x4c\xd9\x23\
-\xdb\x58\x6f\x06\x54\xb3\x06\x28\xae\x72\x2d\xf5\x08\x27\xeb\x3b\
-\x22\x3f\xf8\x26\x05\xd7\x0c\xf7\x3f\xf8\x3a\xde\xe9\x3f\x3f\x49\
-\xc8\x49\x14\xaf\x7e\xd0\x4d\x16\xba\xdc\x91\xed\x1f\x00\x82\x45\
-\xe0\xd1\x18\x4d\xff\xe8\x50\xa5\xeb\x86\x01\x68\xb1\x07\x61\xd3\
-\x73\x03\xb1\xbd\x5c\x62\x92\xa2\xf4\xc7\x75\xe1\xfb\x6a\x80\x04\
-\x2f\x39\x96\x3e\x0d\x39\x1a\xde\x6f\xf9\xa7\x78\x97\x0c\x9b\x1d\
-\x8a\xab\xb4\x1e\xca\xe2\x00\x30\x46\x29\x6d\x4b\x49\xa5\xe7\x69\
-\x8f\xa6\x3d\x32\x2b\x57\x75\x0e\x44\xa3\x03\xb2\x04\xbc\x50\x2c\
-\xb7\x35\x77\xca\x8b\x0e\x8f\x8a\xb7\x3b\xc1\x23\x5e\xe5\x4a\x8f\
-\xf0\x0c\x60\x36\xc1\x92\xd1\x9f\xcc\x00\x0a\x3a\x9d\xb0\xaf\x42\
-\x63\x6a\x8e\x90\x11\x37\x24\x94\x70\xdf\x28\x8e\x68\x4a\xfb\xd2\
-\x40\xd8\x0f\x22\x2e\x00\xff\x7b\x58\xfe\x4d\x53\xf2\x8f\x29\x56\
-\x89\x2a\x6f\x52\x54\xb5\x28\xa6\x64\x7b\x5f\xbd\x5a\xed\xd0\xee\
-\x52\x34\x77\xc2\xe9\xc6\xc8\x74\xc5\xa2\x81\x28\x88\xb7\x40\xa7\
-\x0b\x32\xe2\x84\xe9\xb9\x82\xc5\x14\x91\xe6\xc2\x89\x5d\x89\x7c\
-\xb3\x28\xdf\xfc\x7c\x24\x1b\x89\xd8\x12\xf0\xc4\x29\x89\xb6\x34\
-\xb2\x2b\x25\x46\x66\x26\xc5\x08\xc7\x2e\x2a\xaa\x5b\xbd\x83\x2c\
-\x78\x25\x2f\xd1\x0a\x66\x93\x60\x56\x8a\xd3\x8d\xc6\xda\x53\x40\
-\x6a\x1c\xa4\xc6\x08\xe7\x6d\x0a\xa7\xc7\x7b\xdd\xa3\x43\x6a\xac\
-\x90\x99\x00\x79\x89\x42\x5c\xd8\x5e\x34\x23\x8e\x55\x94\x7a\xae\
-\xb8\xda\x33\xa5\x28\x47\x7b\x54\x29\xa5\x47\xa2\x91\x88\x7c\xed\
-\x9e\xfa\x40\x52\xba\x90\xb2\x9c\x44\x72\x86\x27\x0b\x3b\xce\x6a\
-\x74\x76\xb3\x8a\x5f\xda\x64\x29\xab\x55\x9c\x32\xf8\xcd\x8f\x36\
-\x7b\x07\x76\x44\x8a\xf7\x55\xef\x50\x95\xe2\x82\x4d\x91\x1e\x07\
-\xa3\x52\x85\xcc\x38\xc1\x1c\xe1\x6f\xbb\xae\x0b\x95\xb5\xf5\x14\
-\xe4\x66\x46\xa4\x7e\x85\x5a\x6f\x73\xaa\x7b\x16\x8f\x54\x8e\xf0\
-\xd7\x1d\x66\x9e\xde\x27\x23\x6d\x76\x4a\xd2\xe3\x24\x69\x5a\xae\
-\x50\x52\xa5\x38\x7f\xc5\x3b\x76\x51\xae\x60\xb5\xc0\x9e\xf3\x8a\
-\x4b\x2b\x71\x76\xbc\x30\x2e\x53\x88\x36\x43\x79\xa3\x0a\xb8\x24\
-\xe4\x26\x7a\x97\x91\x34\x2b\xa8\x6e\x8f\x96\x5d\x54\x24\x44\xc3\
-\xb0\x64\x19\x10\x05\xe7\xe3\xb3\x95\xfc\x6a\xf5\x6b\x4c\x1a\x5b\
-\xc0\xc3\x5f\x5d\x19\xb1\x76\x44\xa9\xed\xae\x68\x75\xfb\xbc\x34\
-\xd5\x1a\xce\x7a\xc3\xba\x04\x3c\xbb\x5f\x66\x5f\x6c\xe7\x03\xb7\
-\x10\x5d\x98\x25\xb8\x75\xa8\x6c\xe9\x3d\x0c\xa5\x35\x9f\x2e\x05\
-\x97\xa8\x6d\x57\x34\x74\x2a\x34\xc5\xe5\xe9\xdb\x1f\x33\xf3\x84\
-\x61\xdd\x76\x09\x3a\x3a\xed\xe8\x22\xb4\xb5\xdb\x49\x46\x70\xb7\
-\x7b\xa8\x75\x5a\x88\x8f\x8b\x21\x3a\x3a\x8a\x28\x73\xf8\x57\xba\
-\xd6\xb6\x0e\xfe\xe7\xa5\xb7\xd8\xb8\x75\x2f\xba\x2e\x4c\x1a\x5b\
-\x10\xf6\x36\xba\xa3\x44\x16\x47\x39\x64\xdb\xe1\x5a\xb9\x79\x5a\
-\xb6\xaa\x0b\x57\xbd\x61\xfb\x64\x5e\x3a\x28\x0b\xce\xb7\xca\x76\
-\xbb\x5b\x99\xf3\x92\xbc\x26\xd8\xe2\x6a\x85\xa7\xdb\x28\x9b\x14\
-\xa4\xc7\x0b\xf1\x51\x90\x1e\xeb\x1d\xe8\x23\xb5\x0a\xf7\x27\xab\
-\x9b\xfb\x8a\x55\x4e\x44\xc7\xd6\xdc\x44\x53\x7d\x2d\x4d\xf5\x17\
-\x69\x6a\xaa\xc7\xde\xd6\xc2\xfa\x2e\x1b\xcd\x2d\x6d\xb4\xb4\x75\
-\xe0\xf1\x04\x5e\x1a\x35\x4d\x11\x67\xb5\x92\x10\x6f\x25\x3b\x23\
-\x95\x9c\xcc\x54\xee\xbe\x75\x11\x23\x87\x65\x87\xfc\x77\xea\xba\
-\xf0\xe6\x96\x3d\xfc\xe1\xa5\xb7\x68\x6d\x1f\x90\x37\xb5\xee\xcc\
-\xf4\x78\xf4\x0f\x4b\x6a\xe4\x73\x45\x39\xea\x5c\x38\x2a\x0c\x8b\
-\x00\xac\x2e\x95\xa2\xf3\xcd\x6c\xb3\xbb\x95\x19\xa0\xae\x4d\xb1\
-\xfd\xac\xa2\xf5\x8a\x15\x2b\x3e\x06\x26\x65\x08\x2d\x5d\x8a\x33\
-\x4d\xd0\xd8\xa9\xe8\x6e\x0c\x72\xd8\x3b\xa9\x3c\x7f\x86\xea\xca\
-\x73\xd4\x5c\x38\x47\x6d\x75\x05\x2e\xa7\xd3\x70\xff\x74\x5d\x68\
-\xeb\xe8\xa4\xad\xa3\x93\xea\x8b\x5e\x6d\xf3\xda\x59\x93\x43\x16\
-\x80\xb2\x53\xe7\xf8\xe5\xd3\xaf\xf1\xf1\xd9\x4a\xc3\x7d\x32\xc0\
-\x58\x11\x7d\xcb\xfe\x3a\x59\x30\x27\x53\xd5\x1a\xad\xcc\xb0\x00\
-\x3c\x75\x40\xc6\xd7\x35\xc9\x5e\x87\x5b\x59\x2e\x5d\x73\xe9\xe0\
-\xf2\xa1\xae\xb4\xd8\x61\xfb\xd9\x9e\xd6\x95\xa6\x86\x8b\x9c\x39\
-\x79\x94\xb3\x27\xcb\xa8\xba\x50\x8e\xae\x47\x44\xd9\x35\x44\x73\
-\x4b\x1b\xff\xfd\xa7\x8d\x6c\x7a\xff\x00\x12\x69\xf3\x65\x30\x08\
-\xa3\xcd\x2e\xfd\x9d\x92\x72\xb9\xbe\x68\xa4\xb2\x19\xa9\xca\x90\
-\x00\xac\xde\x2d\xa9\x4d\x76\xf6\x87\xba\x8b\xe7\xec\x72\x70\xe2\
-\x68\x31\x65\xa5\x07\xa8\xaa\x38\x6b\xa4\x0b\x11\x45\xd7\x85\x77\
-\x3f\x38\xc4\xef\x9e\x7f\x9d\x96\xd6\x8e\xc1\xee\xce\x95\x4c\xd5\
-\xa3\x65\xdd\xa9\x53\x72\x93\x11\x37\xb4\x7e\x0b\xc0\xef\x0f\x8a\
-\xa5\xdd\x2e\x47\xda\x1d\x2a\x31\xd8\x32\xcd\x4d\x0d\xec\xdb\xf9\
-\x2e\x27\x8e\x16\xe3\x76\x0d\x6d\x6f\xa9\x8f\x8e\x9f\xe5\x57\xab\
-\xd7\x71\xfa\x7c\xd5\x60\x77\xc5\x2f\x4a\x64\x71\x6b\x2c\x2f\xbf\
-\x22\x72\xe7\x2a\xa5\x02\xa8\xce\xbe\xe9\xb7\x00\x78\xba\xe4\x40\
-\x93\x5d\xe5\x06\xf3\x6c\x8b\xad\x89\x7d\x3b\xdf\xe3\x68\xc9\xde\
-\x21\x39\xc5\x77\xa7\xa9\xb9\x95\x27\xff\xb4\x81\x77\x77\x1e\x1a\
-\x1a\xd3\x7d\x00\x14\x72\x7b\xeb\x01\xb6\x01\xd7\xf7\xa7\x7c\xbf\
-\x04\x60\xf5\x7e\xcf\x53\x15\x36\x35\x2d\xd0\x73\x2e\x97\x93\x5d\
-\xdb\xde\xa6\x78\xef\x8e\x21\x3f\xf0\x6e\x8f\x87\xd7\x36\x7d\xc8\
-\x1f\x5f\xd9\x4c\x67\x67\xd8\xed\x2d\x91\x45\x58\xf8\xcc\x41\xcf\
-\xbf\x3f\x30\xcb\xf4\x83\x50\x8b\x86\x2c\x00\x2f\x95\xc8\xed\xa7\
-\x1b\xe4\x9b\x81\x9e\x3b\x7f\xe6\x24\xef\x6d\x58\x8b\xad\xd9\xa0\
-\x8d\x77\x00\xb8\x50\x53\xcf\x0f\xfe\xff\x33\x94\x5f\x30\xac\x54\
-\x0f\x0a\x49\x56\x38\x51\xa7\xfd\xd3\x73\x25\xf2\xd6\xfd\x45\x6a\
-\x57\x28\x65\x43\x12\x80\xff\xda\x27\x69\x17\x6c\xf2\xb2\x5b\xfc\
-\xdb\xd8\x74\x8f\x87\x6d\x9b\xd7\x51\x7a\x60\x17\x5c\x05\x53\x28\
-\x40\xf9\x85\x8b\x57\xed\xe0\x03\x64\x27\x0a\x4e\x0f\x2a\xd9\x2a\
-\xab\x0f\x1e\x94\xa9\xa1\xb8\xa3\x87\x24\x00\xca\x23\xdb\x3b\x5c\
-\xca\xef\x0e\xbb\xa3\xb3\x83\x37\xd6\x3e\xcb\x85\x73\xa7\x42\xa9\
-\xf6\x33\x0c\x12\x67\xb9\xec\xd7\x30\x81\x04\xfd\xa7\xc0\x3f\x05\
-\x5b\x36\x68\x97\x87\xe7\x8b\x3d\xdf\xad\xeb\x50\x53\xfc\xdd\x6f\
-\xac\xaf\xe5\x4f\x7f\xf8\xe5\x67\x83\x3f\xf8\x3c\xba\xe5\x63\x59\
-\x11\xec\xc3\x41\x09\xc0\x4b\x07\x25\xbd\xa6\x55\xfb\x99\xbf\x19\
-\xbd\xb9\xb1\x9e\xb5\xcf\xfd\xd7\x55\xb1\xde\xff\xa5\xe3\xf2\xa0\
-\x0e\xd7\xf2\xca\x1f\x3f\x94\x84\x60\x9e\x0f\x4a\x00\x5a\xdc\xb2\
-\xa9\xd3\x89\xc5\xd7\xbd\xb6\x56\x1b\x7f\x5e\xf3\x14\x9d\xed\x6d\
-\xa1\xf4\xf3\x33\x22\xc4\xb1\x3a\x45\x8b\x03\xab\xd3\xc4\x6b\xc1\
-\x3c\x1f\x50\x00\x5e\x2c\x95\xe5\x17\xdb\xd4\x2c\x5f\xf7\xec\x9d\
-\xed\xbc\xf2\xdc\x93\xb4\xda\x9a\x42\xed\xe7\x67\x84\x19\x9b\x03\
-\xf6\x55\x28\xca\x3f\x89\x6d\xb8\xd8\xce\xb2\x67\x8a\x25\xa0\x6d\
-\x20\xa0\x12\xd8\xd8\x2e\x6b\x74\x1f\x5a\xbf\x88\xce\x9b\xaf\x3c\
-\x4b\x73\x63\xd8\x76\x26\x03\x92\x92\x94\x80\x35\x26\x8a\x96\xb6\
-\x4e\x3a\x3a\x07\xdc\x83\x7a\x48\x22\x02\xa5\xd5\xbd\x7d\x2e\x74\
-\x01\x5b\x3b\x2f\x03\x39\x7d\x95\xef\x53\x00\x9e\x2b\xf1\xfc\xbf\
-\xb3\xf5\xca\xa7\x9b\xcb\x9e\x1d\xef\x70\xa1\xfc\x74\xa8\xfd\x0d\
-\x9a\x28\x8b\x85\x59\x53\xc7\x32\xaf\x68\x22\x85\x63\x87\x33\x72\
-\x78\xce\xe5\x7d\xfd\x27\x9e\x5d\xcf\x2b\x6f\xed\x8c\x58\xdb\x57\
-\x13\x67\x9a\x7a\x0f\xfe\x25\x6c\x0e\xb2\xd7\x1c\x92\x47\xee\x9d\
-\xa9\x7e\xed\xaf\xbc\x5f\x01\x10\x11\xf5\xcb\x9d\xf2\xcf\xbe\xee\
-\xd5\xd7\x56\xb3\x77\xe7\x7b\x21\x77\x36\x18\x72\xb3\xd2\xb8\xfb\
-\xd6\x45\x2c\xbb\x6e\x06\x09\x71\xb1\x11\x69\xe3\x2f\x05\xa7\xc7\
-\x1b\xd8\xd2\x17\xf5\x1d\xf2\x63\x20\x74\x01\x78\xa1\x44\x7f\xbc\
-\xb5\x4b\x8b\xeb\x75\x43\x84\x77\x37\xbc\x8c\xee\xe9\xd7\xde\x83\
-\x5f\x92\x12\xe3\xf8\xc6\x3d\x2b\xb8\x65\xf1\x1c\x4c\x03\xec\x90\
-\x7f\xb5\x52\xde\xa4\x70\x07\x18\x86\x16\x87\x4a\x78\x7a\x9f\x7c\
-\xff\x6f\xe7\xaa\x5f\xf8\xba\xef\x57\x00\x1a\xda\xd5\xf7\x7c\x5d\
-\xff\xb8\xac\x98\x9a\xca\xf3\xa1\xf4\x33\x20\x4b\xae\x9d\xce\xf7\
-\x1e\xbc\x93\xc4\x84\xde\xf2\xf6\x19\xfe\xb1\x07\x69\xef\x6b\x76\
-\xf0\x43\xc0\xa7\x00\xf8\xfc\xaa\xbd\x75\xd4\xfd\x25\xab\x85\xb8\
-\x2b\x5d\xa8\x93\xa2\x75\x0e\xec\x78\x2b\xa4\x4e\xf6\x85\xa6\x69\
-\x3c\xfc\xd5\x95\xfc\xf8\x91\xfb\x3e\x1b\xfc\x10\xd1\x05\x9a\xec\
-\xc1\xb9\xbd\xb6\x77\x91\xf8\xdc\x01\xf9\xba\xaf\x7b\x3e\x67\x80\
-\x9c\x14\xed\xde\x9c\x14\x01\x84\x56\x07\xd4\xb4\x29\xa2\x4c\x70\
-\xaa\xac\x98\x9a\xba\xf0\x18\x7b\x2c\x66\x13\xff\xf2\x9d\xaf\xb0\
-\x78\x5e\xc0\x4d\xc5\xcf\xb8\x02\x11\x28\xad\xea\xed\x72\xd7\x17\
-\xad\x5d\xfc\x08\xf8\xc3\x95\xd7\x7b\xcd\x00\x25\x17\x64\x2c\xc8\
-\xf2\x4b\xbf\x27\xc6\xc0\xf8\x0c\x6f\xf6\x8c\x57\xc3\xa4\x79\x6b\
-\x9a\xe2\xc7\xdf\xbd\xff\xb3\xc1\xef\x07\xad\x0e\xd8\x75\x5e\xa3\
-\xc2\x87\xb7\x75\x5f\x34\xda\xc9\x7d\xb1\x58\x16\x5e\x79\xbd\xf7\
-\x0c\xa0\xe9\x0f\xe2\x23\x5e\xe0\x6c\x45\x0d\xc7\x4e\x87\x67\xed\
-\xff\xd6\x57\x6e\x63\xe1\x9c\xc9\x61\xa9\xeb\xaf\x85\xa6\x4e\xaf\
-\xc6\x5f\xdf\xa1\xfa\xb5\xc9\x2a\x02\xad\x4e\x7e\x06\xcc\xef\x7e\
-\xbd\x87\x00\x1c\x3c\x28\x16\x41\xbf\xd7\x57\x05\x9b\xde\x3f\x10\
-\x7a\xab\x3e\x98\x3f\x73\x12\x77\xdf\xba\x28\x2c\x75\x0d\x15\xd2\
-\x92\x13\x99\x57\x34\x29\xa4\x32\x9d\x8e\x2e\x6c\x2d\xed\xb4\x77\
-\xd8\x69\x6d\xef\xc4\x6a\x8d\x22\x3a\xca\x42\x7c\xac\x95\xcc\xb4\
-\x64\x34\xed\xd3\xc9\xd9\xe1\x86\xbd\x15\x5a\xc0\x78\x89\x40\x34\
-\x75\xc8\xdc\xb7\xdf\x96\xe8\x9b\x6f\xfe\xd4\x87\xb0\x87\x00\xa8\
-\x1c\xcf\x8d\xa0\x7c\xfa\x4a\xef\xdc\x77\xc4\x58\xeb\x40\x42\x5c\
-\x2c\x8f\xfe\xdd\x2a\xc3\xf5\x0c\x15\xcc\x26\x13\x77\xde\x7c\x1d\
-\x0f\xdc\xb5\x9c\xd8\x58\xff\x69\x8d\xda\x3a\x3a\x29\x3d\x76\x96\
-\xd2\xb2\xd3\x9c\xa9\xa8\xa1\xbc\xa2\x96\x46\x9b\xff\x00\x9f\xe8\
-\x28\x0b\xa3\x0b\x72\x19\x33\x32\x97\x59\xd3\x26\xa1\xa7\x14\x1a\
-\x1e\x7c\x80\x2e\x8f\xd2\xea\x33\xf5\xef\x00\x3f\xbf\xfc\x37\x74\
-\x7f\x40\x13\xb5\xca\x97\xaf\x47\x45\xd5\x45\xaa\x2e\x36\x18\xee\
-\xc0\x7d\x77\x2e\x23\x3d\x25\xc9\x70\x3d\x43\x81\x19\x85\x63\x78\
-\xe4\xc1\x2f\xf8\x8d\x2d\xa8\x6f\x6a\x61\xfb\xee\x52\xb6\xed\x2e\
-\xe5\xd8\xe9\xf3\xe8\x21\xc4\xc2\x77\x39\x5d\x1c\x3b\x7d\x9e\x63\
-\xa7\xcf\xf3\xe6\x7b\x7b\x88\xb1\xc6\x32\xbe\xb0\x88\x19\x73\x17\
-\x92\x96\x19\x7a\x30\x4b\x77\xda\x5d\xda\x03\xf8\x12\x80\xed\xe5\
-\x12\x23\x4a\xbf\xcd\x57\xa1\xfd\x87\x3f\x36\xd4\x28\x78\xa7\xc9\
-\x95\xcb\xaf\x35\x5c\xcf\x60\x93\x99\x96\xcc\x43\xf7\x7e\x9e\x25\
-\xd7\x4e\xf7\x79\xbf\xf8\xe8\x29\xfe\xfc\xd6\x4e\x76\x1d\x3a\x16\
-\x36\x3f\x48\x87\xbd\x93\xc3\x07\x77\xf1\xd1\xa1\x3d\x14\x4e\x9f\
-\xc3\xa2\xe5\x9f\x27\xc6\xda\x3f\x2b\x69\x73\x27\x63\x5f\xd9\x2e\
-\xf1\xab\x16\xab\x76\xe8\x26\x00\x89\xd1\xee\x45\x88\xe6\xd3\xc5\
-\xbb\xec\xe3\x73\xfd\x6a\xac\x3b\x77\xdd\xb2\x90\xe8\x28\x9f\x3b\
-\xca\x57\x05\x16\xb3\x89\x55\x2b\x16\x71\xdf\x9d\xcb\x88\x8d\xe9\
-\xed\x14\x75\xfc\x74\x05\x4f\xbd\xb0\x81\x92\xb2\xc8\xed\x8f\x88\
-\xe8\x1c\x2d\xd9\x4b\xf9\xe9\x63\xac\xb8\xf3\x5e\x86\x8d\x18\x1b\
-\x72\x1d\x4e\x0f\xaa\x23\x5e\x7f\x88\x4f\x66\x81\xcb\x02\xa0\x44\
-\xbb\xc9\x5f\xa1\xb2\x53\xc6\xb4\x7f\x93\x49\xe3\xc6\x45\x3e\x77\
-\x94\xaf\x0a\x66\x4f\x1b\xc7\x23\x5f\xfb\x82\xcf\xf0\xef\x96\xd6\
-\x0e\x7e\xff\xd2\x46\x36\x6e\xdb\x17\xd2\x34\x6f\x84\x8e\xb6\x56\
-\xfe\xfc\xfc\x53\x2c\x5d\xb1\x8a\xa9\x33\xaf\x09\xb9\xbc\x03\xed\
-\x0b\x5c\x29\x00\x80\x4f\x01\xe8\x74\x74\x51\x53\x67\x6c\xbf\x7f\
-\xce\xd4\xf1\x57\xe5\xda\x9f\x95\x9e\xc2\xb7\xef\xbf\x9d\x45\xf3\
-\xa6\xf6\xba\xa7\xeb\x3a\xaf\xbf\xb7\x87\xd5\xff\xfb\xf6\x60\x04\
-\x89\xa2\xeb\x3a\xef\x6e\x58\x8b\xee\xf1\x30\x7d\xce\x82\x90\xca\
-\xb6\x3b\xe4\xb2\x6b\x9f\x19\xa0\xf4\x82\xe4\xe9\xe8\x3e\xe7\x93\
-\xca\xea\x7a\xc3\x01\x12\x73\x8b\x26\x1a\x2a\x3f\xd0\x98\x4d\x26\
-\x56\x2e\xbf\x96\xbf\xbd\xe7\x66\xbf\xd3\xfd\xaf\x57\xaf\x0b\x9b\
-\x5d\xa4\xdf\x88\xb0\xf5\xed\x57\x49\x48\x4a\x66\xf4\xf8\xe0\xed\
-\x2a\xed\x4e\x15\xf3\x52\xa9\x14\xde\x33\x5d\x95\x99\x01\x3c\x9a\
-\x67\x81\xf2\x93\x4e\xe1\x42\x8d\x71\xed\x7f\xe6\x94\xd0\xd7\xaa\
-\xc1\xe2\x9a\xa2\x89\xfc\xc3\xd7\xee\x20\x3f\x3b\xbd\xd7\xbd\xa6\
-\xe6\x56\x9e\xfa\xd3\x06\xde\x19\x42\x51\x43\x22\xc2\x5b\xeb\xfe\
-\xc4\xfd\xdf\x7a\x8c\xc4\xa4\xe0\xf2\x6d\x8a\x80\xc3\xcd\x57\x81\
-\x7f\x34\x03\x28\xd4\x7c\x5f\x0f\x7a\x04\x4e\x56\xb5\x18\xea\x60\
-\x62\x7c\x2c\x23\xf2\xb3\x0c\xd5\x31\x10\xe4\x66\xa5\xf1\xf0\xfd\
-\x2b\x59\x30\xbb\xb0\xd7\xbd\xa1\x1e\x35\xe4\x74\xd8\xd9\xbc\xfe\
-\x25\x56\xdd\xf7\xad\x9e\xe9\x52\xfa\xa0\xcb\x25\x8b\xe0\xb2\x0e\
-\x20\xb3\x7d\x65\x8b\xd9\x57\xa1\x38\x5d\xd3\x6e\xa8\x73\x05\xb9\
-\x99\xa8\x20\x3b\x35\x58\x4c\x1e\x37\x9c\x17\x7e\xfd\x98\xcf\xb7\
-\x94\xe2\xb2\xd3\xfc\x7a\xf5\x6b\x43\x3e\x70\xa4\xa2\xfc\x63\x4e\
-\x96\x95\x32\x7e\x72\x51\x50\xcf\x3b\xdc\x6a\x34\x80\x59\x44\xb4\
-\x92\x6a\xbd\x97\xbf\x7f\x6d\xbb\xa2\xae\x5d\x61\xef\x34\x16\x16\
-\x9d\x9f\xd3\x7b\x2a\x1d\x6a\xa4\x26\xf7\xf6\xa0\xae\x6b\xb4\xf1\
-\xe4\x9a\x37\xd8\xba\xab\x74\x10\x7a\xd4\x3f\x3e\xd8\xb2\x81\xb1\
-\x13\xa7\xa2\x99\x02\x67\xc5\xea\x70\x92\x24\x22\xca\x5c\x5c\xc9\
-\x28\xa5\x11\xdf\xfd\xa6\x47\xf7\x66\xf0\x02\x0c\x87\x71\xe7\x66\
-\x0d\x7d\x01\xe8\x8e\xcb\xed\x61\xed\x86\xf7\x79\xfe\xb5\x77\xb1\
-\x3b\x8c\x67\x27\x19\x48\x6c\xcd\x8d\x9c\x2c\x2b\x61\xe2\xd4\xc0\
-\xaf\xdc\x4e\x0f\xea\xc5\x8f\x98\x6b\x46\xf3\x4c\x04\x45\x5b\x17\
-\xd8\xec\x8a\x76\x27\x54\xb7\x7a\x7f\x07\xd0\xdd\xc6\x12\xe6\xc6\
-\xf7\x61\x23\x1f\x2c\x8a\x0a\xc7\x30\x61\x54\x7e\xaf\xeb\xfb\x4b\
-\x4f\xf2\x9b\x67\xd6\x51\x51\x3d\x70\x9e\xce\xe1\xe6\xd0\x9e\x1d\
-\x41\x09\x00\x80\xcb\xa5\x2f\x33\x97\x56\xab\x7b\xaa\x5b\x34\x5c\
-\x7e\xac\x96\x1e\xdd\x98\x00\xc4\xf8\x78\x8d\x1a\x2c\xfc\x99\x71\
-\x6b\xea\x9a\xf8\xdd\x73\xeb\xd9\xb9\x7f\xd0\xb2\xb6\x87\x8d\xda\
-\xea\x0a\x1a\x2e\x56\x93\x9e\x15\x38\x75\x83\xee\x61\xa2\xd9\xad\
-\x6b\xa3\xfd\x0d\x3e\x80\xa6\x19\xcb\xb2\x68\x8d\x1e\xfc\xd4\x9c\
-\x51\x66\x33\x77\x7f\x7e\x11\x5f\x59\xb9\x0c\x6b\xcc\xa7\xfd\xe9\
-\x72\xba\x78\xf1\xf5\x6d\xbc\xf8\xfa\x56\xba\x9c\x91\xcb\x58\x12\
-\x17\x6b\x65\x46\xe1\x68\x46\x0f\xcf\x25\x2b\x3d\x85\xb8\x38\x2b\
-\x1d\x1d\x76\x6a\xea\x1b\x39\x72\xe2\x1c\x87\x8f\x9f\x09\xab\x15\
-\xf1\xf8\x91\x62\xae\x0b\x42\x00\x5c\xa2\x46\x9a\x9d\x6e\xc9\xee\
-\x2b\x5f\x64\xf7\x7d\xe9\xfe\x30\xd8\x89\x21\xe6\xcf\x9c\xc4\xc3\
-\x5f\x5d\xd9\xeb\xbd\xfe\xc3\x03\x65\x3c\xf1\xdc\xfa\xcb\x59\xc3\
-\xc2\x4d\x6c\x4c\x34\x8b\xae\x99\xc6\xe2\xf9\xd3\x99\x35\x65\x1c\
-\x96\x3e\xd2\x95\x56\x5f\x6c\xe4\xf7\x2f\x6d\x0c\x9b\xc2\x79\xea\
-\xf8\x47\x5c\xb7\x34\x70\x7c\xa8\x47\x57\xd9\xe6\xf4\x38\x1c\xcd\
-\x0e\xfc\xba\x17\x2b\x83\x33\x80\xbd\x6b\x70\x14\xa9\xfc\xec\x74\
-\x1e\x7e\x60\x25\xf3\x67\xf4\x74\xd4\xb8\x50\x53\xcf\x6f\x9f\x5d\
-\xcf\xde\xe2\xe3\x11\x69\x37\x36\x26\x9a\x95\x37\x2e\xe0\x4b\xb7\
-\x2d\x22\x39\x31\x3e\x70\x01\xbc\x36\x88\x1f\x3f\x72\x1f\xb3\xa7\
-\x4e\xe0\xe7\xbf\x5f\x8b\x18\x9c\x0d\x9a\x1a\x2e\xd2\xd6\x62\x23\
-\x21\x29\xb9\xcf\xe7\x9c\x3a\x49\xe6\xf1\x19\xe2\xcc\x4a\x10\x76\
-\x95\xfb\xd6\x03\x2c\x16\x63\x53\xb8\xdd\x31\xb0\xe7\x28\x5a\x63\
-\xa2\xb8\xf7\x8e\x65\x7c\xf1\xb6\x45\x3d\x32\x84\xda\x1d\x4e\xd6\
-\xac\x7b\x8f\xb5\x6f\xbe\x8f\xd3\xa0\x62\xeb\x0b\x4d\x53\xac\xb8\
-\x61\x2e\x5f\xbf\xe7\x96\xa0\x07\xfe\x4a\x96\x5e\x3f\x97\x92\x4a\
-\x37\xef\x6c\x78\xd5\x70\x7f\x2a\xce\x9e\xa4\xb0\x68\x6e\x9f\xcf\
-\x38\x3d\x58\xcd\x28\x52\x93\x63\x60\x6e\x81\xb0\xfb\xbc\xea\x95\
-\xc3\x3f\x26\xd6\x58\x74\x4e\x93\x6d\xe0\xa2\x86\x97\x5e\x5b\xc4\
-\xb7\xee\xbd\x8d\xcc\xb4\x9e\x92\xbf\x6d\xcf\x61\xfe\xeb\xf9\xd7\
-\xa9\x6b\x30\x94\x52\xcf\x2f\x93\xc6\x0c\xe7\x91\x07\xef\x60\xe2\
-\x18\x63\xe9\x62\x6b\x5b\x15\x53\x66\x2e\xe0\xd4\x89\x63\x9c\x3d\
-\x75\xcc\x50\x5d\x35\xd5\x15\x01\x05\x40\x74\xcc\x66\x20\x16\x20\
-\x3d\x4e\x58\x30\x52\x38\x5a\xab\xd1\x62\xe7\x72\x8a\x57\x6b\x3f\
-\x1d\x0f\x2e\x71\xa1\x26\xf2\xaf\x54\x05\x79\x59\x7c\xe7\x81\x95\
-\xcc\x99\x36\xbe\xc7\xf5\x8a\xea\x3a\x7e\xfb\xec\x7a\xf6\x95\x9c\
-\x88\x48\xbb\x89\xf1\xb1\xdc\x7f\xd7\x72\xee\xbc\x69\x81\x61\x5d\
-\x09\xbc\x87\x59\x29\xa5\xb1\xf8\xa6\x95\x94\x9f\x3e\x6e\x68\xbf\
-\xe1\x62\xf5\x85\x80\xcf\x78\x74\x34\x33\xc2\xe5\x39\x3e\xd5\x0a\
-\x0b\x47\xea\x78\xc4\x9b\xee\xb5\xb1\x13\xce\x25\xf7\x6f\x3a\xbb\
-\x44\x45\x55\xbd\xa1\xf2\x7d\x91\x10\x17\xcb\x83\x77\xdf\xc4\xed\
-\x9f\x9b\xdf\x23\x9c\xac\xb3\xd3\xc1\x33\x7f\x7e\x87\x57\xdf\xfe\
-\x00\x77\x98\x43\xd8\xc0\xab\x18\xaf\x5c\x3e\x9f\x07\xef\xbe\x29\
-\xac\xf1\x8b\xd6\x28\xef\x49\x2a\x29\x69\x99\x8c\x18\x33\x91\x72\
-\x03\xb3\x40\xc3\xc5\x1a\x44\xa4\x4f\x33\xbc\x0e\xca\x0c\xf4\x5a\
-\xe4\x4d\xca\x7b\xee\x5e\x4e\x22\xb8\x0a\x53\x30\xb2\x22\x5d\xa8\
-\xa9\xa7\xc9\xd6\xe6\xd3\xdc\xda\x6f\x94\xe2\xb6\xa5\xf3\x7a\xad\
-\xb7\x22\xc2\x7b\x1f\x14\xf3\xe4\x9a\x37\xfb\x74\xba\x34\xc2\xd4\
-\x89\xa3\xf8\xee\x83\x77\x30\x66\x78\x5e\x8f\xeb\x9d\x8e\x2e\x9f\
-\x5b\xc7\xa1\xa0\x8b\xf7\x20\x2b\x80\x09\x93\x8b\x0c\x09\x80\xcb\
-\xe5\xa4\xa3\xad\x95\xf8\x44\xff\x7e\x18\x1e\x8f\x57\x00\xfa\x24\
-\x27\x33\xb5\xdf\x9d\x00\xef\xa0\x1c\x38\x7c\x92\xe5\xd7\xfb\xb6\
-\x4e\x9d\x6b\x56\xd8\x5d\xde\x24\x47\x3a\xde\x17\xd2\x40\x5b\x47\
-\x0f\xde\x7d\x53\xaf\x0f\xfb\xf4\xf9\x2a\x7e\xb5\x7a\x1d\x1f\x1d\
-\x8f\x4c\xea\xd9\xb4\xe4\x44\x1e\xba\xf7\x36\x96\x5d\x37\xa3\xc7\
-\xb7\xca\xd6\xda\xce\x1f\x5e\x7a\x8b\x98\x98\x68\x1e\xbe\xff\x76\
-\x43\x6d\x98\x14\x8c\x4b\x17\x4e\xd6\x2b\x46\x8c\x9e\xe0\xdd\xd9\
-\x33\xb0\x0c\xd8\x6c\x0d\x7d\x0a\x00\x78\x77\x03\x9d\x80\xd5\xdf\
-\x03\x99\x69\xc9\x44\x99\xcd\x86\x34\xe7\xad\xbb\x4a\x7a\x08\x80\
-\xd3\x03\xcd\x9d\xde\x63\x62\xea\x3b\x14\xb3\xf3\x85\xd6\x2e\xaf\
-\xef\xbb\x49\x11\xf0\x50\xa7\xee\x83\xdf\xd6\xd1\xc9\xea\x97\x37\
-\xb1\xfe\x9d\xdd\x11\xb1\x39\xf8\x73\xfd\xf6\x78\x74\x5e\x7f\x77\
-\x37\xab\x5f\xde\x44\x5b\x47\x27\xab\x6e\xe9\x15\x74\xd3\x2f\x26\
-\x66\x0a\x9a\x82\x13\x24\x92\x9a\x96\x41\x53\x43\xff\x75\xa8\xd6\
-\xe6\x26\x28\x18\xed\xf7\xbe\xd9\x84\x98\x51\x38\x11\xff\x02\xa0\
-\x69\x1a\xa3\x86\xe7\x72\xe2\x4c\x45\xbf\x3b\xb2\xa7\xe4\x38\xe7\
-\x2e\xd4\x92\x9c\x9e\x4d\x71\xb5\xd6\x2b\xa6\xed\x70\x8d\xc2\xad\
-\x7f\xfa\x06\xb2\xfb\xbc\xa2\x30\x0b\xb2\x13\x04\xb7\x07\xe2\xa2\
-\xb8\x7c\xc8\x53\x5d\xbb\xa2\xc3\x05\xe9\xb1\x3a\xbb\xf6\x1e\xe2\
-\xc9\x35\x6f\xd2\xdc\x12\x99\x37\x8d\xa2\xc2\xd1\x3c\xf2\xb5\x2f\
-\x30\xaa\xa0\x67\x92\x8d\xd2\x63\x67\xf8\xf5\x1f\xd7\x71\xe6\x7c\
-\x75\x44\xda\x1d\x9f\x21\xc4\x46\xc1\x3a\x83\x02\xd0\xd9\xd1\xf7\
-\x56\xbe\x02\x31\x23\x74\x00\x7d\xce\x13\xe3\x46\xe5\x19\x12\x00\
-\x11\xe1\xc9\x17\x36\x70\xd3\xaa\x6f\xd0\x7a\x85\x59\x20\xca\x04\
-\xc9\x56\xa1\xae\x5d\x75\x7b\x1e\x8e\xd6\x2a\x8e\x7e\xb2\x23\x19\
-\x65\xc2\x7b\xca\x98\xc3\xbb\x51\x55\x5b\x55\xc1\xb6\xb7\x5f\xa5\
-\x3a\xcc\x61\xea\x97\xc8\x4c\x4f\xe6\xef\xef\xbb\x9d\x1b\xae\xe9\
-\x19\xbb\x58\xd7\x68\xe3\xa9\x35\x6f\xb2\x65\x57\x49\x44\xda\xed\
-\xce\xb0\x24\x61\xf2\xa8\x0c\xce\x9c\xec\x7f\x1d\x0e\x47\xdf\xbe\
-\x8a\x9a\x86\x6e\x16\xa1\x49\x29\xfa\x34\x1c\x4f\x1a\x33\x9c\x37\
-\xdf\xdb\xd3\xff\x9e\x00\x7b\x8a\x8f\x11\x97\xfb\x21\xd3\x67\xf7\
-\x74\x60\x8c\x36\x43\x8a\x15\xea\xfa\x10\x56\xa7\x07\x4e\xd4\x29\
-\xec\x9d\xed\x7c\xb0\x65\x23\x47\x8a\xf7\x46\xc4\x25\xcb\xdf\x9e\
-\x81\xd3\xed\x66\xed\x9b\xef\xb3\x66\xdd\x7b\x03\xba\x45\x9c\x97\
-\x6e\x4c\x71\x76\xd8\x03\x08\x80\xc2\x6d\x56\x9a\x34\xd0\x47\xea\
-\x57\x08\x9f\x4f\xdf\xd6\xb7\x5e\xc5\xa4\x99\x98\xd2\xcd\x95\xb9\
-\xad\x0b\x4e\x06\x48\x73\xd2\xd9\xd1\xc6\x81\x5d\xdb\x39\x7c\xe0\
-\x43\x9c\xce\xc8\x58\x16\xaf\x29\x9a\xc8\xc3\x0f\xac\x64\x58\x4e\
-\x46\x8f\xeb\xbb\x8b\x8f\xf1\xc4\x33\xeb\xa9\xac\x35\xee\x1b\x19\
-\x2a\x56\x83\x6f\x15\x81\x3e\x2b\xb3\x49\xec\x66\xd0\x1a\x7a\x1e\
-\xdf\xd4\x9b\x9c\xcc\x54\xf2\xb3\xd3\x0d\x7f\x08\x22\xc2\x3b\x6f\
-\xbe\xcc\xb9\x33\x27\x58\xb0\xf4\x56\x52\x52\xfd\x3b\x8b\x78\x3c\
-\x6e\xce\x9d\x3e\xc1\xc7\xc7\x0e\xf3\x71\x59\x29\x2e\x57\x64\xbe\
-\x79\xfe\x7c\x01\x2b\x6b\x1b\x78\xe2\xd9\xf5\xec\x3e\x64\xcc\x22\
-\x67\x04\xa3\x02\xa0\x07\xc8\x1f\x13\x63\x52\x2d\x66\x90\xa0\x16\
-\xf7\x85\x73\xa7\xf2\xd2\x1b\xdb\x0c\x75\xe8\x12\x27\xcb\x4a\x39\
-\x79\xec\x30\xf9\x05\x23\xc9\x1d\x36\x8a\xe4\xd4\x34\x2c\x51\xd1\
-\x74\x39\xec\xb4\xb5\xda\x68\xb8\x58\xc3\x85\x73\xa7\x71\x76\x45\
-\xce\x01\x33\x3a\xca\xc2\xdf\xac\x5c\xc2\x97\x6f\xbf\x81\x28\xcb\
-\xa7\xbe\x80\x76\x87\x93\x17\xd6\xbf\xc7\xcb\x6f\x44\x66\xcf\x20\
-\x14\x4c\x86\x77\x62\xfb\x16\x00\xb3\x46\x8d\x19\xe1\x5c\x30\x95\
-\x2d\x5d\x50\x14\x36\x01\x00\x40\x84\xca\xf3\x67\xa9\x3c\x3f\xf0\
-\x47\xc6\x2c\x9c\x3b\x85\x6f\xdf\x77\x7b\x2f\x1b\xc7\xd6\x5d\xa5\
-\x3c\xb9\xe6\x0d\xea\x1a\x23\xb3\x67\x10\x2a\x46\x37\xd2\x02\x0a\
-\x80\xd2\xcf\x99\x45\xa4\x3c\x18\xaf\xdd\x71\x23\xf3\x19\x37\x32\
-\x9f\x8f\xcb\x07\xf5\xc4\x2c\x43\x14\xe4\x66\xf2\x9d\x07\xee\x60\
-\xce\xf4\x9e\x7b\x06\x75\x0d\x36\x7e\xfa\xbb\x17\x23\x1a\xd7\xd7\
-\x1f\x3a\x0d\x0a\x80\xc9\xe4\xdf\xce\x17\x1f\x05\x09\xd1\xec\x36\
-\xeb\x16\xd3\x31\xd3\x95\x07\xf6\xf9\x61\xd5\x8a\xeb\xf9\xd7\xdf\
-\xbd\x68\xa8\x53\x83\x41\x54\x54\x34\xd7\x5c\xff\x39\x96\x2e\x5e\
-\xcc\xc8\x3c\x8d\x2b\x75\x9e\x13\x67\x2b\x87\xdc\xe0\x03\x74\x18\
-\x8c\x41\x30\x59\x7a\xba\xb9\x9b\x14\x0c\x4f\xf1\x1e\xba\x99\x62\
-\x05\x37\xda\x1b\xe6\x59\x99\x94\x97\x54\xd3\x0a\x04\x3c\xfc\x69\
-\xc9\xb5\x45\xfc\x71\xed\x26\xc3\xb1\x82\x03\xc9\x84\xc9\x33\x58\
-\xb4\xfc\x76\xe2\x13\x93\x68\x71\xc1\xee\xf3\x30\x3a\x15\x26\x66\
-\x09\xa6\xa1\x1d\xae\x60\x38\x27\xc3\x88\x34\x0b\xb3\x87\x09\x36\
-\x3b\xc4\x46\x41\x6e\x82\xf7\x68\x5e\x00\x14\x2d\xb3\x73\xa8\xd4\
-\x94\x52\x02\x12\x94\x37\xa4\xc5\x6c\xe2\xeb\xf7\xdc\x62\xa8\x53\
-\x03\x45\x7a\x66\x0e\x5f\xbc\xff\xdb\xac\xb8\xeb\xbe\x1e\xf6\x70\
-\x11\x38\x51\xeb\x66\xf3\x47\x1d\x01\x93\x2c\x0e\x36\x95\x06\xc3\
-\xf2\x32\x92\xe3\xc9\x4b\x14\x0a\xb3\x84\x91\x29\xdd\x06\x1f\x50\
-\x22\x47\x94\x52\xa2\x01\x88\xa8\xa0\x13\x00\x2d\xbd\xb6\x88\x19\
-\x85\x63\x0c\x75\x2c\x92\x44\xc5\x58\x59\x7c\xe3\x4a\xee\xfd\xe6\
-\xa3\x0c\x1b\xd9\xbb\x9f\xa7\x4f\x1c\xe5\xb9\x27\xff\x83\x63\xa7\
-\xcf\x19\x3e\xb9\x3c\x92\x88\x08\x15\x55\xc6\x7c\x29\xfa\xda\x81\
-\x15\xa5\xf6\xc1\x27\xa1\x61\x1a\xb2\x4b\x50\xff\x10\x4c\xa5\x4a\
-\x29\x7e\xf0\xf7\x5f\xe2\xde\xef\xfd\x62\x68\xc5\xc9\x29\x45\xe1\
-\xb4\xd9\x2c\x5c\x7a\x2b\x71\x09\xbd\x57\xb3\xe6\xa6\x06\xb6\xbf\
-\xfd\x5a\x0f\x4f\x1b\xdb\x10\xea\xfe\x95\x9c\xa9\xa8\xa6\xdd\x60\
-\x46\xf4\xb4\x14\xff\xab\xba\x20\xbb\xe0\x13\x01\x10\xdd\xb4\x0b\
-\x53\xf0\x3b\x69\xd9\x19\xa9\xfc\xf4\x91\xfb\xf8\xfe\x7f\x3c\x3d\
-\xe8\x5e\xbf\x00\x99\xd9\x79\xdc\x70\xf3\x9d\xe4\x0f\x1f\xd5\xeb\
-\x9e\xdb\xe5\x62\xff\xae\x2d\xec\xff\x60\x2b\x6e\x77\x4f\xd7\xef\
-\xba\x76\x45\x63\xc7\xd0\x88\xf2\xbd\x92\x83\x1f\x19\x3f\x7a\xa7\
-\x20\xb7\x0f\x43\x9b\xd9\xb4\x07\x3e\x11\x80\x19\x05\xaa\xba\xb8\
-\xca\xf3\x31\x30\x2e\xd8\xca\xe7\x16\x4d\xe0\x91\xaf\xdd\xc1\xaf\
-\x56\xbf\x36\x68\xa1\xd2\x31\xd6\x58\x16\xdc\x70\x0b\xd3\x66\xcf\
-\x47\xa9\xde\x46\x93\x8f\x8f\x1d\xe6\xfd\xcd\xeb\x69\x6d\x69\xf6\
-\x59\x5e\x04\x0e\x56\x69\x44\x0f\x41\x19\xd8\x57\x6a\xcc\x6b\x59\
-\xd3\x14\x79\xd9\x19\xfe\x6e\x97\x5d\x3a\x78\xfa\xb2\x5a\x20\xc2\
-\x26\xa5\x82\x17\x00\x80\x95\xcb\xaf\xc5\x6c\x32\xf1\x9f\x4f\xff\
-\x39\xa8\x63\xdc\xc3\x85\x52\x8a\x29\x33\xae\xe1\xba\x25\x2b\xb0\
-\xc6\xf5\xce\x31\xdc\xdc\x58\xc7\x96\xb7\x5e\xe5\x7c\x10\x5b\x69\
-\x76\x17\x98\x86\x98\x32\x58\xd7\x68\xe3\xd0\x11\x63\xaf\xa5\xf9\
-\xd9\x19\x7e\x73\x32\x09\x6c\xba\xf4\xf3\xa7\x7a\xa1\xa6\x6f\x42\
-\xb4\xa0\xf4\x80\xee\xdc\xba\x74\x1e\xf9\x39\xe9\xfc\xe8\x37\x2f\
-\xd0\xd4\x1c\x19\x37\xac\xee\x14\x8c\x1c\xc7\xc2\xcf\xdd\x4a\x76\
-\x6e\x6f\x0f\x5c\xa7\xb3\x8b\xbd\x3b\xde\xe5\xd0\x9e\xed\x78\x42\
-\xf0\x05\xec\x6b\x27\x72\x30\xd8\xfc\xfe\x01\xc3\x4b\xeb\xa4\xb1\
-\xfe\x3d\x94\x95\xe8\x9b\x2f\xfd\xfc\x69\x96\xb0\x0e\xf3\xfb\x6d\
-\x71\x7a\x0b\xd2\xb7\x6f\x80\x2f\x8a\x0a\xc7\xf0\xe2\x6f\x1e\xe3\
-\x5f\xff\x67\x23\xbb\xf7\xee\x43\x24\xbc\xb3\x81\xa6\x69\x8c\x9d\
-\x30\x95\xe9\xf3\x16\x32\x6c\xb8\x6f\x0f\x97\x13\x47\x4b\xd8\xf1\
-\xce\xeb\xb4\xb5\x86\x6e\xc6\xad\x6a\x1b\x3a\x6f\x03\x76\x87\x93\
-\x57\x37\x7d\x60\xb8\x9e\xc2\x71\x23\xfc\xdd\xb2\x45\xb7\x98\x2f\
-\x37\x70\x59\x00\xc6\x8e\x55\x5d\xc5\xd5\x9e\x37\x00\x9f\xa9\x62\
-\x03\x11\x15\x1d\xcb\xb5\x37\xdd\xcd\xf8\x59\x8b\x29\xd9\xf7\x01\
-\xc7\x8f\x16\xe3\x30\x90\x5b\x40\xd3\x34\xf2\x86\x8d\x64\x5c\x61\
-\x11\xe3\x26\x4d\xf3\xa9\xd9\x83\xf7\xbc\xc2\x2d\x1b\x5f\xfd\x8b\
-\x39\xaf\xf0\xd5\xb7\x77\x86\x25\x96\xe2\x4a\x73\x77\x37\xd6\x15\
-\x16\xaa\xcb\x5b\xab\x3d\x53\xc5\xea\xf2\x8a\x28\xd5\x2f\x01\xe8\
-\x74\x79\xbd\x5a\x53\xd3\xb3\x58\x72\xcb\x9d\x2c\xbe\xd1\xeb\xdb\
-\x7e\xf6\xd4\x31\xea\x6b\xaa\xa8\xaf\xab\xc6\xe5\xf4\xbf\xa5\x9b\
-\x98\x9c\x4a\x6a\x7a\x26\x19\xd9\x79\x0c\x1b\x3e\x9a\x61\x23\xc6\
-\x60\x89\xf2\xbf\x1d\xea\xec\x72\xb0\xfb\xfd\xcd\x14\xef\xdb\x19\
-\xf6\xd3\x4b\x06\x8b\x8b\x0d\xcd\xbc\xf0\xfa\x56\xc3\xf5\x14\xe4\
-\x65\x91\xe7\x27\x2f\x83\xa6\xf4\xb5\xdd\x7f\xef\x21\x00\xb6\x3c\
-\xd3\x3b\x49\x35\x7a\x0d\xd2\xf7\x49\x53\xbe\x48\x8a\xf1\x7a\xf6\
-\x34\x7f\xf2\xea\xaa\x99\x4c\x8c\x1e\x3f\xf9\x72\xf6\x2a\x11\xa1\
-\xad\xd5\x86\xdb\xe9\xc4\xe5\x72\xe2\xec\xea\x22\xc6\x1a\xeb\xfd\
-\x17\x1b\x1b\x52\x08\xda\xb9\xd3\x27\xd8\xb4\xfe\x45\x3a\xda\x23\
-\xaf\x73\x0c\x14\x22\xc2\xcf\xff\x7b\x6d\x58\x6c\x2b\x8b\xe6\xf9\
-\x3d\xe0\xb5\xd6\x5d\x6d\xde\xde\xfd\x42\x0f\x01\x58\xac\x94\xbb\
-\xb8\xca\xb3\x06\x78\x2c\xd4\x46\x35\x05\x85\x59\x3a\x1f\x9e\xf3\
-\xbd\x87\xad\x94\x0a\x3a\x8b\x55\x20\xca\x4f\x1d\xfb\x8b\x1a\x7c\
-\x80\x35\xeb\xde\x63\xff\x61\x03\x0e\x80\xdd\xb8\x71\xa1\x6f\x17\
-\x7c\xa5\xf8\xe3\x95\x07\x4b\xf7\x1a\x2d\xd1\xb5\xd5\x04\x72\x11\
-\xf2\x81\xcb\x03\xc7\xeb\xff\x7a\x0f\x7b\x0a\x72\x43\xd5\x27\x5b\
-\x77\x95\xb2\xfa\xe5\xcd\x81\x1f\x0c\x82\xa9\x13\x46\x51\x90\xe7\
-\x33\x2b\x9b\xb8\x4d\xda\x33\x57\x5e\xec\x35\x62\x33\x87\xa9\xd3\
-\x28\xb5\xe9\xca\xeb\x81\xf8\xa8\x56\xd1\x68\x2c\x9f\xd4\x55\x4d\
-\x55\x8b\xc2\xd6\x0f\xcb\xed\xd6\x5d\xa5\xfc\xf4\x89\x3f\x85\xcd\
-\x98\xe6\xff\x2c\x06\xb5\x71\x76\x96\xea\xe5\x7d\xe3\xd3\x63\x40\
-\x74\xcf\x2f\x95\xd2\x6e\x0e\xa5\xe1\x01\x4a\x93\x3b\x64\x71\xeb\
-\xb0\xa3\x5c\xa3\xe0\x93\xbd\xf6\xa4\x18\xef\xff\xfe\x10\x11\xd6\
-\x6e\xdc\xc1\x53\x2f\x6c\x08\x9b\x39\xbd\x20\x37\x93\x05\xb3\x7d\
-\x67\x0c\x15\x15\xc2\xb1\x71\x33\xf3\x2d\xdb\x8a\xab\x3c\xc5\xc0\
-\x8c\x60\x1b\xcf\x4d\x04\x83\x39\x25\xaf\x7a\x44\xe0\x7c\xb3\xe2\
-\x7c\xb3\x57\xe7\xb9\x7e\xa4\x4e\xb2\x0f\x21\x68\xb2\xb5\xf1\x9f\
-\x7f\x78\x25\xec\x39\x89\xbe\x7e\xcf\x2d\x68\x5a\x6f\x9b\x86\x52\
-\xb2\x6f\x46\xae\xf2\x69\x5c\xf0\xeb\x33\xa4\x90\x9f\x08\xea\xf5\
-\x60\x1b\xcf\x4e\x10\xd2\xe3\xa0\xa1\x63\xe8\x18\x55\x06\x13\x11\
-\x6f\xdc\xe3\x74\xeb\xa7\x53\xa3\xcb\xed\x61\xc3\xd6\xbd\x3c\xfd\
-\xd2\xdb\xb4\x75\x84\x37\xc1\xf4\xd4\x09\xa3\xb8\x7e\xae\x1f\xed\
-\xdf\xc3\x4f\xfc\x95\xf3\x2b\x00\x45\x79\xe6\x37\x8a\xab\xdd\xfb\
-\x11\x35\x27\x98\x0e\x98\x14\x5c\x3b\x5c\x38\x7a\x11\xce\x0c\xe1\
-\x7d\xf6\x81\xa4\xa9\x53\x01\x42\x6b\x5b\x07\x9b\x77\x1c\x64\xed\
-\xc6\x1d\x5c\x6c\xf0\xbd\x31\x65\x84\x28\xb3\x99\x47\xbf\x71\x97\
-\xef\x50\x70\x91\x03\xd3\xf3\x4d\x7e\x75\xba\x3e\xa3\x83\x05\xf9\
-\x17\x85\x0a\x5a\x3d\x55\x0a\x26\x67\x09\x4d\x9d\xea\xb2\x3d\xe0\
-\xaf\x11\xb7\xcb\x45\x7d\x5d\x35\x25\x95\xe7\xd8\xb4\xf6\x38\x87\
-\x8e\x9c\x8a\xa8\x8b\xf9\xd7\xee\xbe\x91\x11\x7e\x8e\xae\x11\xe4\
-\x9f\xbc\x5e\x5f\xbe\xe9\x53\x00\x66\xe6\x5a\xde\x29\xae\xd6\xdf\
-\x46\x24\x68\x85\x50\x29\x98\x9e\xab\xf3\xfe\x59\xcd\x48\x64\xf3\
-\x55\xc7\x91\xd2\xfd\x9c\x39\x79\x14\x87\xc3\x1e\x30\x24\x2b\x9c\
-\xcc\x99\x3e\x9e\x2f\xdd\x76\x83\xbf\xdb\x6b\x67\xe6\x5b\xfa\xf4\
-\xe5\x0f\xf8\xe2\x2e\x1e\xf5\x0f\x78\x43\xc8\x83\x26\x29\xc6\xeb\
-\x7d\xfa\xd7\x84\xd3\x61\xc7\xd6\xdc\x38\xa0\x83\x3f\x72\x58\x36\
-\x8f\x7f\xe7\x5e\x9f\x8a\x9f\x40\x9b\xa6\x6b\x3e\xcf\x7f\xee\x4e\
-\x40\x01\x98\x39\x4c\x9d\x56\xf0\x9b\x50\x3b\x37\x21\x43\xe8\x23\
-\x35\xde\x67\x18\x24\x33\x3d\x99\x5f\xfe\xf3\x37\x48\x8c\xf7\x9d\
-\xa2\x46\x09\x8f\x4f\x1f\xa6\xaa\x02\xd5\x13\x94\xe9\xce\xae\x6b\
-\x8f\x03\x21\x79\x28\xc4\x98\xbd\xd9\x2e\x3e\x23\xfc\x24\x27\xc6\
-\xf3\x9b\x7f\xf9\x26\x99\xe9\x7e\xf3\x00\x96\xe9\xb5\xda\xef\x82\
-\xa9\x2b\x28\x01\x98\x3f\x4c\xd9\x45\xf4\x6f\x10\xa2\x89\x78\x4c\
-\x9a\x10\x77\xf5\x1e\x14\x36\x24\x19\x96\x93\xc1\x53\xff\xfa\x6d\
-\x9f\x07\x58\x7d\x82\x2e\x4a\xfb\xe6\x95\x36\x7f\x7f\x04\x6d\xbc\
-\x9f\x99\x6f\xd9\x26\xc2\x13\xc1\x3e\x0f\xde\x0d\xa2\x49\x59\x9f\
-\xcd\x02\xe1\x62\xfa\xa4\xd1\xfc\xf7\xbf\x3d\xdc\xd7\xe0\x23\xc2\
-\xcf\x66\xfa\x31\xfa\xf8\x22\xa4\xdd\x9b\x44\xbb\xf6\x18\x70\x38\
-\x94\x32\x79\x49\x42\x5e\xd2\x67\x42\x60\x04\xa5\x14\xab\x6e\x59\
-\xc8\x6f\x7f\xf4\xad\x00\x59\x48\x65\xaf\xd4\x6a\x8f\x87\x52\x77\
-\xc0\x2c\x61\xdd\x19\x3b\x56\x75\x95\xd6\xc8\x97\x75\x5d\xdf\x07\
-\xf4\xf6\xc6\xf4\xc3\xf4\x5c\xa1\xb1\x43\xe1\x18\xdc\x68\xeb\xcb\
-\xa4\x65\x64\x93\x9e\x61\xec\x08\xd6\x81\x62\x58\x4e\x06\x8f\xfe\
-\xdd\x2a\x8a\x02\x07\xe3\x34\x98\xc4\x74\xd7\x8c\x20\xa7\xfe\x4b\
-\x84\x24\x00\x00\xd3\x73\x54\xd9\xa1\x6a\xf7\x57\x94\xa8\xd7\x08\
-\x9c\xd1\x0d\x00\x8b\x06\x63\xd3\x85\x23\xb5\x83\x6b\x21\x8c\x8a\
-\x8e\x61\xfe\xe2\x9b\x98\x31\xe7\xba\xa0\x8e\x55\x19\x4c\xcc\x26\
-\x13\x5f\xbc\x75\x11\x0f\xac\x5a\x1e\xcc\x89\xab\x02\xf2\xb5\x69\
-\xf9\x2a\xe4\xd0\xed\x90\x05\x00\x60\x66\xae\x79\x7d\x71\xb5\xe7\
-\x57\x08\x01\xdf\x33\x2f\x91\x95\x20\x94\xd5\x2a\x06\x2b\x8c\x64\
-\xf4\xb8\x42\x96\xae\x58\x15\x30\x83\xf6\x60\x63\x36\x99\x58\xb2\
-\xa0\x88\xaf\xde\xb5\xdc\xe7\xd1\x75\x3e\x11\x7e\x36\x23\xdf\xfc\
-\x66\xbf\xda\xeb\x4f\x21\x80\xa2\x1c\xed\xd1\x92\x6a\x3d\x1f\xf8\
-\x62\x30\xcf\xc7\x47\x79\xf3\xff\x1d\xae\x55\x03\x6a\x21\xcc\xc8\
-\xca\x63\xc9\x2d\xbe\xa3\x86\x86\x12\xb1\x31\xd1\x2c\xbb\x6e\x26\
-\x7f\xb3\x72\x49\xa8\xc9\x39\x5f\x2e\xca\xd3\x7e\xd8\xdf\x76\xfb\
-\x2d\x00\x4a\x29\xfd\xe0\x41\xf9\x8a\x96\x23\xc9\x20\xcb\x83\x29\
-\x33\x22\x55\x88\x8f\x86\xd2\x6a\xef\xd9\x44\x91\x24\x3a\xc6\xca\
-\xfc\xc5\x37\x51\x34\xe7\xba\xb0\x24\x72\x8e\x14\xe3\x47\x0f\xe3\
-\xf3\x4b\xaf\x61\xe9\x82\x19\xc4\x5a\x43\xcd\x09\xa4\xb6\x25\x74\
-\xaa\xfb\x95\x52\xfd\x9e\x58\xfb\x2d\x00\x00\xb3\x66\x29\xd7\x87\
-\xf5\x72\x57\xac\x53\xb6\x03\x33\x83\x29\x93\x1e\x27\xdc\x30\x46\
-\x28\x6f\x52\x9c\xac\x57\x38\xc3\xed\xd0\xab\x14\x93\xa7\xcf\x61\
-\xe1\xb2\x5b\x89\x8d\x0b\x63\x7e\xe2\x30\x61\x31\x9b\x98\x3a\x71\
-\x14\xd7\xce\x2c\x64\xfe\xac\xc2\xe0\xa7\xf9\x5e\xc8\xc1\x68\x8b\
-\xf6\xf9\xb1\x63\x95\xa1\x34\x22\x86\x04\x00\x60\x41\x86\x6a\x3b\
-\x58\x2d\x37\x2a\xd1\x3f\x54\xe0\xd7\x19\xbd\x3b\x9a\x82\xd1\x69\
-\xc2\x88\x14\xa1\xaa\x4d\x71\xba\x5e\xf5\x4a\x20\xd9\x1f\xb2\x72\
-\xf2\x59\xb2\xe2\x2e\x72\xf3\x47\x18\xaf\x2c\x4c\x58\xad\xd1\x4c\
-\x1a\x53\xc0\xe4\x71\x23\x99\x3c\x61\x04\xd3\xc6\x8f\xec\x91\x72\
-\xb6\x5f\x28\xce\x58\x4c\xa6\x15\x85\x99\xca\x70\x4c\x93\x61\x01\
-\x00\x98\x95\xab\x1a\x4a\x6a\xe4\x46\xd1\xf5\x77\x81\xa0\x93\x0a\
-\x9a\x34\x28\x48\x12\xf2\x13\x85\x13\xf5\x8a\x53\x0d\xfd\xd3\x0f\
-\x62\xac\xb1\x5c\xb7\x64\x05\x53\x67\xcd\xef\xd7\x29\xa5\xa9\x69\
-\x19\x4c\x2e\x9a\x47\xab\xad\x89\x56\x5b\x23\x76\xbb\x1d\x67\x97\
-\x23\xa8\x08\x27\xb3\xc5\x42\x74\xb4\x95\xec\x8c\x24\xb2\xd3\x12\
-\xc9\x4a\x4b\x26\x2f\x27\x83\x11\x79\x59\x0c\xcb\xcd\x20\x3b\x23\
-\xd5\xe7\x66\x8d\x01\x4e\x81\xb6\x6c\x4a\x96\xba\x18\x8e\xca\xc2\
-\xda\xb3\x7d\x95\x92\x66\x51\x9e\x8d\xa0\xe6\xf5\xa7\x7c\x65\x8b\
-\xe2\x50\x55\x60\x21\xd8\xbe\x69\x1d\x87\xf6\xee\xb8\x1c\x24\xba\
-\x70\xe9\x0a\x62\x62\x83\x36\x4b\x04\x8d\xc7\xe3\xc1\xed\xea\xc2\
-\xe1\x70\xe0\x71\xb9\xd0\xcc\x66\x34\xa5\x88\x8e\x89\x41\xd3\xcc\
-\x58\xa2\xbc\xb1\x0c\xf1\x51\xb0\x64\x8c\x1e\xec\xb1\xbd\x06\x90\
-\x83\x26\x93\xe9\x96\x69\xd9\x2a\x6c\xa7\x70\x84\x65\x06\xb8\xc4\
-\xdc\x7c\xd5\x58\x56\x27\xcb\xba\x5c\xf2\x6a\xb0\x8a\x61\x77\xf2\
-\x93\x84\xba\x76\xa8\xb0\x05\xfe\x24\x07\x62\xba\x37\x99\x4c\x98\
-\x4c\xb1\x44\xc7\xf4\x7d\x28\x44\x7a\x9c\x0c\xc0\xe0\xab\x6d\xce\
-\x18\x6d\xe5\xbc\x34\x15\xd6\x80\x88\x88\x74\xbb\xac\x4c\xa2\xba\
-\x92\xf5\xe7\x81\xbb\x43\x2d\x5b\xdb\xa6\xd8\x5b\xd1\x77\xb7\x9a\
-\x1a\xea\x48\x49\xcb\x18\x32\x87\x52\x8f\x48\x11\xa6\xe7\x46\xf0\
-\xdd\x56\xd4\xba\x16\x97\xfa\xf2\xe2\x91\x2a\xec\x39\x4d\xc2\x3a\
-\x03\x5c\xa2\xb0\x50\x39\x45\xe4\xcb\xa5\xd5\x7a\x85\xc0\xf7\x09\
-\x41\xd0\x52\xac\x12\xf0\xf1\xd4\x74\xff\x9b\x21\x03\x8d\x22\xa2\
-\xce\x2f\x82\xf0\xb3\xa2\x3c\xf5\x43\x23\xaf\x7a\x7d\x11\xb1\x17\
-\x64\xa5\x94\x5e\x94\x67\x7a\x0c\xf4\xcf\x01\x41\x2b\x2c\xd1\x66\
-\xaf\x2f\x8d\x61\x08\x64\x00\x00\x02\x6f\x49\x44\x41\x54\xc1\xd5\
-\x42\x4e\x62\xdf\xfe\xff\x06\x68\x50\xba\xac\x98\x91\x6f\xfa\x41\
-\xa4\x06\x1f\x22\x28\x00\x97\x98\x91\x67\xd9\x62\x12\x6d\x96\x42\
-\x3e\x0c\xb6\x4c\xd4\x55\x24\x00\x63\x23\xe1\xf4\xa2\x64\xbf\xd2\
-\xb4\xd9\x45\xc3\xcc\x6f\x87\xbf\xf2\x9e\x0c\x88\x89\x6c\x5a\xbe\
-\xaa\xb4\xe5\x9a\x16\x0b\xfc\x18\x02\x6f\x07\x0c\xf5\x04\x8e\x97\
-\xc8\x88\x0b\xfb\xb7\x5f\x80\x27\xf4\x6a\xd3\x82\xa2\x1c\x75\x2e\
-\xac\x35\xfb\x61\xc0\x3f\xea\xe2\x1a\xb9\x1e\x5d\x7f\x12\x28\xf4\
-\xf7\xcc\x8e\xb3\xda\x90\x76\x2b\x57\x0a\xd2\x63\x85\x19\x79\x82\
-\x35\x5c\x1e\x4f\x8a\x23\x82\xf6\x50\x28\xce\x1c\xe1\x69\x76\x10\
-\xd8\x2e\x62\x4e\xae\xd1\x1f\xd2\x85\x9f\x2a\xe8\x65\xaf\x1d\x8a\
-\x02\x90\x11\x27\x8c\x48\x85\x84\x28\x21\x2e\x3a\xac\xb3\x54\xa7\
-\xc0\x2f\x62\x6c\xda\xbf\x77\xcf\xdc\x31\x50\x0c\xea\x64\x5b\x5c\
-\x21\xb9\x68\xfa\xcf\x50\x7c\xa5\xfb\xf5\x7d\x17\x14\x35\xad\x43\
-\x63\x1d\x48\xb1\x7a\xdd\xda\xfa\x3a\xc5\xac\xbf\x28\xd4\x46\x8f\
-\x52\x0f\xcd\xca\x55\xfd\x3f\x90\xc9\x70\x1f\x86\x00\xc5\x95\xae\
-\x25\x28\xf5\x23\x50\xd7\x01\x1c\xa9\x55\x83\x1e\x5e\x66\x36\x41\
-\x51\x4e\xa4\xdc\xd9\x64\xaf\x88\xfc\x73\xa0\xa0\x8d\x81\x60\x48\
-\x08\xc0\x25\x8a\xab\x65\x21\x22\x3f\x28\xab\x65\xf9\x60\xe7\xf1\
-\xbd\xa6\x40\xc8\x4a\x08\xf3\xe0\x2b\xd9\xaf\x3c\xfc\x78\x20\xb4\
-\xfb\x60\x19\x52\x02\x70\x89\x67\x0f\xca\x3d\x2d\x0e\xfe\xa3\xd9\
-\x4e\xc1\x60\x84\x97\x59\x2d\xb0\x7c\x5c\xd8\x5e\xbd\x05\xd4\x56\
-\x41\x7f\x62\x66\x9e\x79\x43\xb8\x2a\x0d\x17\x43\x52\x00\x2e\xf1\
-\xec\x61\x99\x67\xb7\xf3\xef\xb6\x4e\x59\xe8\x70\xab\x01\x73\xe2\
-\x8b\x31\xc3\x8d\xe3\x0d\x0a\x80\xa2\x06\x61\x8d\xee\xd1\x9e\x9e\
-\x55\xa0\xce\x84\xa7\x67\xe1\x67\x48\x0b\xc0\x25\x5e\x29\x93\x28\
-\x87\x53\xff\x5e\xab\x5d\x7b\xc0\x66\x67\xb4\xd3\x13\xf9\x7e\xdf\
-\x3c\x41\x27\x2a\x74\x91\x6b\x56\xf0\xba\xae\xf4\xb5\xad\x39\xe6\
-\xad\x8b\x95\x1a\x22\x7e\xd0\xfe\xb9\x2a\x04\xa0\x3b\x1b\x0e\x4a\
-\xac\x4d\xe9\x0f\xb5\x3b\xd5\xdd\xad\x0e\x55\xd8\xe9\x8a\x4c\xae\
-\xe7\x25\x63\x74\x12\x82\xf3\xd0\x2a\x43\xb1\x19\xd1\x37\x47\xdb\
-\xcc\x3b\x07\xe3\x55\xce\x08\x57\x9d\x00\x5c\xc9\x9a\xc3\x32\xd9\
-\xe9\xd2\xbf\xea\x74\xa9\xc5\x76\x37\xa3\x3b\x9d\x2a\xc1\x65\x70\
-\x86\x48\xb1\xc2\x75\xa3\xf4\xde\x66\x52\x45\x8b\x12\x39\x82\x52\
-\x07\x74\xe4\xc3\x28\x93\x69\x57\xb8\x1c\x33\x06\x8b\xab\x5e\x00\
-\x7a\x21\xa2\x9e\x29\x61\xa1\x86\x7e\x9d\xcb\xc5\x14\xa7\x68\xa3\
-\x3c\x3a\x99\x2e\xb7\x24\xba\x45\xc5\xb8\xdd\x58\x04\x34\x5d\xc0\
-\xad\x7b\x77\x94\x2d\x1a\xa2\x29\x44\x53\xe8\x9a\x26\xee\x59\xf9\
-\x1c\x4e\x8e\x91\x72\x81\x0b\x4a\x28\x17\x4d\x2f\x57\x98\x8f\xcf\
-\xc8\x55\x91\x39\xac\x78\x10\xf9\x3f\x06\x8e\x97\x48\x80\x7c\xb0\
-\xcc\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
-\x00\x00\x9a\xb3\
-\x89\
-\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
-\x00\x02\x00\x00\x00\x02\x00\x08\x06\x00\x00\x00\xf4\x78\xd4\xfa\
-\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
-\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\
-\x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
-\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
-\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x20\x00\x49\x44\
-\x41\x54\x78\x9c\xec\xdd\x77\x78\x5c\xe5\x95\x3f\xf0\xef\x7b\x67\
-\xd4\xbb\x2c\xc9\x2a\x96\xdc\x3b\x6e\xb2\xc1\xc6\xa6\x98\xde\xc1\
-\x18\x30\xa1\xf7\x40\x1a\x09\xd9\xdd\xb4\xcd\x66\xbd\xc9\x66\xb3\
-\xbf\x4d\x02\x29\xd4\xd8\xc6\x14\x63\xc0\x60\x63\xc0\x31\xee\x06\
-\xf7\x22\xc9\x72\x91\x9b\x24\xcb\xb2\x7a\xd7\xf4\x76\xef\x3d\xbf\
-\x3f\x06\x83\x8b\xca\x48\x9a\xb9\x77\xca\xf9\x3c\x4f\x9e\x80\x34\
-\xba\xef\x41\x65\xde\x73\xdf\x7b\xde\xf3\x0a\x30\xc6\x82\xda\xda\
-\xb5\x14\x63\xce\xc3\x68\x08\x8c\x54\x14\x75\x98\x42\xc8\x96\x15\
-\x0c\x56\x49\xca\x50\x55\x4a\x57\x48\xa4\xc8\x2a\x25\x79\x54\x11\
-\x07\xc0\x08\xc0\xa8\x10\x19\x05\xc1\xa0\x02\x92\x4a\x30\x00\x80\
-\xa2\x08\x10\x20\x08\x10\xb2\x0a\x10\x01\x42\x00\x51\x12\x40\x00\
-\x24\x40\x35\x18\x08\x00\x60\x14\x82\x84\x80\x4c\x44\x30\x1a\x84\
-\x8b\x54\x22\x49\x12\xee\x28\x03\x39\x8d\x06\x61\x33\x08\x32\x1b\
-\x25\x74\x18\x04\xb5\x19\x0d\x52\xb3\x41\xa8\x8d\x31\x46\xaa\x8f\
-\x8d\x36\x9c\xca\x88\x46\x95\x29\x1f\xcd\xd7\x08\x21\xeb\xf7\x5d\
-\x63\x8c\xf5\x46\xe8\x1d\x00\x63\x91\x6c\x75\x05\xe5\xbb\xad\x98\
-\xe1\x94\xd5\x49\xb2\x8a\xb1\xb2\x2a\x0d\x75\xab\xc8\x71\x2b\x94\
-\xee\xf6\x20\xde\xa3\x8a\x28\x8f\x1a\x5a\x7f\xa7\x42\x00\x31\x06\
-\xe0\x8a\x61\x6a\x7b\x62\x0c\x1a\x20\xa8\x45\xa8\xa2\x96\x04\xea\
-\x08\xa8\x05\xa8\x5a\x22\x43\x8d\x22\xa1\x76\x46\xae\x68\xd5\x3b\
-\x5e\xc6\x22\x55\x48\xbd\xb1\x30\x16\x6a\x56\x10\x19\x94\x43\x98\
-\xe1\xf6\xa8\x73\x5d\x0a\x0a\x3d\xaa\x34\x4a\x56\x28\xc7\xa3\x8a\
-\x54\x97\x8c\x58\x97\x1c\xbe\x7f\x83\x39\xc9\x84\x99\xf9\xd4\xdb\
-\xcb\x1c\x10\xa8\x20\x12\x95\x00\x55\x80\x50\x21\x84\x5a\x29\x24\
-\x63\xc5\xd4\x6c\x9c\x11\x42\xa8\x5a\xc4\xca\x58\x24\x0a\xdb\x37\
-\x1f\xc6\xb4\xf6\xee\x41\x2a\x94\x9d\xea\x0d\x1e\x21\xcd\x74\x7b\
-\x68\xbc\x5b\x15\xb9\x76\x0f\x92\xdc\x61\x3c\xc9\xf7\x44\x08\xe0\
-\xba\x51\x2a\x12\xa3\xfb\x7d\x09\x37\x80\x0a\x08\x51\x46\x44\x47\
-\x25\xa2\x32\x61\x30\x1c\x3d\x99\x8d\xe3\x0b\x84\x50\xfc\x17\x29\
-\x63\x91\x29\x22\xdf\x98\x18\x1b\x88\x37\x8a\x28\x3e\x5a\xc1\x6d\
-\x32\x70\x9b\x4b\xa6\x19\x4e\x19\x05\x0e\x59\x24\x7a\x14\xfe\x7b\
-\xba\xd0\xd0\x54\xc2\xb4\xbc\x5e\x57\x01\xfa\x46\xc0\x03\x42\x39\
-\x08\xc5\x42\x42\xb1\x02\xa9\x18\x40\xc9\x8c\x5c\x61\xf7\xef\x40\
-\x8c\x85\x37\x7e\xc3\x62\xac\x07\x2b\x88\x0c\x9e\xc3\xb8\x49\xf6\
-\xa8\xf3\x1c\x2e\x31\xdb\xae\x60\xb8\xc5\x29\xe2\x15\x3f\xcf\x69\
-\xe1\x4a\x08\xe0\xb2\x21\x84\x9c\xe4\x80\x7f\xc3\x64\x00\x47\x01\
-\xec\x03\x68\x8f\xc1\x60\xd8\x3b\x79\x30\x8e\xf2\x23\x04\xc6\xba\
-\xc7\x09\x00\x63\xe7\x78\xa3\x88\x0a\x62\x49\x7d\xc2\xa1\x48\xd7\
-\x39\x15\x4c\xb0\x3a\x91\xee\xe6\x3b\xfb\x01\x91\x04\x70\xa9\x36\
-\x49\xc0\x85\xcc\x80\xd8\x2f\x04\xed\x21\x41\x3b\xed\x46\xc3\x8e\
-\x2b\x32\x85\x45\xeb\x20\x18\x0b\x56\xfc\xc6\xc6\x22\xda\xf2\xc3\
-\x34\xd8\xe5\x52\x9f\x70\x79\xc4\x3c\xab\x5b\x4c\xb4\xb8\x91\x48\
-\x7c\x77\xef\x77\x02\xc0\x98\x4c\xc2\xf8\x2c\x5d\xbf\xb9\x0a\x80\
-\x13\x82\xb0\x03\xa0\x4d\x8a\x64\xd8\xca\xbb\x10\x58\x24\xe3\x04\
-\x80\x45\x94\x65\xe5\x94\x2c\xcc\xea\x53\x76\x45\xba\xc7\xe2\xa4\
-\xa9\x66\x97\x48\x50\x79\xc2\xd7\xcc\xc4\xc1\x84\xd1\x19\x41\xf3\
-\x0d\x57\x09\x38\x28\x01\x1b\x09\xea\x46\x93\xdb\xb8\xe3\x9a\xe1\
-\xc2\xa9\x77\x50\x8c\x69\x85\x13\x00\x16\xf6\xde\x2d\xa6\x05\x0e\
-\x0f\x3d\x66\x77\x8b\x99\x66\x37\x06\xc9\xfc\x54\x58\x37\x71\x51\
-\xc0\x4d\x63\x82\xf6\x07\xe0\x20\x88\xed\x12\x68\xa3\xa2\x4a\xeb\
-\x67\xe4\x8b\xc3\x7a\x07\xc4\x58\x20\x71\x02\xc0\xc2\xce\x3b\x07\
-\x29\x41\x52\xd5\x67\x6c\x2e\xf1\x70\x87\x03\x53\xec\x1e\x61\xd4\
-\x3b\x26\xe6\x65\x94\x80\xdb\xc7\x07\x6d\x02\x70\xa1\x6a\x41\x58\
-\xaf\x0a\x5a\x63\x76\x1b\x36\xf2\xea\x00\x0b\x37\x9c\x00\xb0\xb0\
-\xf0\xce\x41\x1a\xae\xb8\xd5\x9f\xdb\x3c\xd2\x6d\x1d\x0e\xe4\xf1\
-\x96\xbc\xe0\x14\x6d\x00\x6e\x1d\x17\x32\x09\xc0\xb9\x6c\x02\x62\
-\x03\x48\xfd\x2c\x9a\x0c\x9f\x4d\xcc\x17\xed\x7a\x07\xc4\xd8\x40\
-\xf1\x9b\x24\x0b\x59\xcb\x8b\x68\x9c\x83\xf0\x82\xc5\x8d\xbb\x4c\
-\x76\x0c\xe6\xad\x79\xc1\xcf\xc7\xee\x80\xc1\x4e\x01\x68\x8f\x10\
-\xe2\x23\xc5\x2d\xad\x98\x31\x4c\x34\xe8\x1d\x10\x63\xfd\xc1\x09\
-\x00\x0b\x29\xcb\x0e\xd1\x24\x9b\x13\x0b\x2d\x76\xba\xd1\xec\x16\
-\x89\x7a\xc7\xc3\xfa\xe6\xf2\x02\xc2\xe0\xa4\x90\x4f\x00\xce\xa5\
-\x00\xb4\x93\x20\x56\x08\x49\x5a\x51\x98\x23\x5a\xf4\x0e\x88\x31\
-\x5f\x71\x02\xc0\x82\xde\x6b\x3b\x29\xcb\x18\xa5\xfe\xc6\xea\x92\
-\x16\x74\x3a\x91\xc9\xdb\xf4\x42\x53\x4e\x12\x61\x66\x41\x58\xff\
-\xf0\x14\x80\xf6\x90\x10\xef\x38\xa2\xa4\xf7\xb9\xe7\x00\x0b\x76\
-\x9c\x00\xb0\xa0\xf4\x46\x11\xc5\x47\xab\xea\x2f\xac\x1e\xf1\x78\
-\xbb\x5d\xe4\xf3\xf2\x7e\x68\x8b\x8f\x02\xe6\x8e\x54\x11\x6d\xd0\
-\x3b\x12\xcd\xd8\x01\x7c\x26\x88\x96\x95\xe7\x19\xd6\xf1\xd9\x05\
-\x2c\x18\x71\x02\xc0\x82\xca\xd2\x7d\x34\xdf\xa6\xe0\x67\x26\x3b\
-\x66\xb8\x14\x44\xce\x74\x11\xc6\x24\x00\x57\x0c\x57\x91\x1e\xaf\
-\x77\x24\x3a\x11\x68\x00\xe1\x23\x52\xa5\x25\xd3\xf3\xc5\x21\xbd\
-\xc3\x61\xec\x2c\x4e\x00\x98\xee\x76\xb4\x50\x12\xd9\xd5\x27\x0e\
-\xd4\x88\x85\xad\x76\x91\xa6\x77\x3c\xcc\xbf\xc6\xea\xdf\x01\x30\
-\x88\xd0\x4e\x00\x4b\x63\xa2\x0c\x1f\x4e\xcc\x12\x56\xbd\xa3\x61\
-\x91\x8d\x13\x00\xa6\x9b\xa2\x7a\x1a\x27\x91\xfa\x78\xb3\x4d\x7c\
-\xff\x50\xbd\x48\xb2\xba\xf5\x8e\x88\xf9\x5b\x94\x04\xdc\x34\x56\
-\x85\x51\xd2\x3b\x92\xe0\x42\x80\x45\x22\xbc\x4f\x90\x5e\x2d\x1c\
-\x22\x0e\xea\x1d\x0f\x8b\x4c\x9c\x00\x30\xcd\xdc\xb7\x82\x0c\x57\
-\x66\x23\xfd\xca\x11\xca\x5c\x59\x48\xcf\xb6\x5a\x70\x5d\x55\x07\
-\xd0\x68\xe1\x5f\xc3\x70\x35\x24\x85\x30\x63\x08\xdf\xfd\xf7\xa2\
-\x98\x04\xfe\xe1\x52\xa4\x77\x67\xe7\x0b\x87\xde\xc1\xb0\xc8\xc1\
-\xef\xbc\x2c\xe0\x7e\xf6\x29\x25\xa5\x27\xe1\x05\x99\xf0\x82\x42\
-\x48\x89\x96\x20\x5c\x32\x10\x92\xed\x60\x22\x5c\x7c\x34\x30\x24\
-\x99\x90\x12\x0b\x78\x14\xa0\xde\x02\x34\x5b\xbb\x7f\x1b\x99\x94\
-\x4d\x18\x39\x88\x13\x00\x1f\x35\x43\xe0\x1f\xaa\x5b\x7a\x95\x7b\
-\x0b\x30\x2d\x70\x02\xc0\x02\x66\xe1\x46\x2a\x88\x33\xe0\xbf\x9d\
-\x0a\xee\x57\x08\xd1\x7a\xc7\xc3\xfa\x2f\x29\x06\x18\x93\x41\x18\
-\x92\x42\x10\x17\xbc\x6b\x1c\x6a\x10\x38\xd5\xde\xf5\x5b\xc9\x94\
-\x5c\xc2\xf0\x34\x4e\x00\xfa\xc8\x0d\x21\x3e\x85\x2a\x5e\x2a\x1c\
-\x22\x76\xeb\x1d\x0c\x0b\x5f\x9c\x00\x30\xbf\xfb\xed\x16\x9a\x19\
-\x6b\xc0\x9f\xad\x6e\xcc\x06\xf1\xef\x58\x28\x33\x1a\x80\x71\x99\
-\x84\x11\x83\x08\xdd\x3d\xc6\x3f\xde\x22\x70\xbc\xf9\xe2\x1f\xb3\
-\x80\xb7\xfa\x7f\x50\xa4\x56\xff\xfb\x81\x00\xed\x50\x05\x5e\x2a\
-\xcc\x31\xac\x16\x42\xf0\xa2\x19\xf3\x2b\x7e\x73\x66\x7e\xb1\x70\
-\x21\x49\x62\x36\xee\x89\x8e\xc2\xef\x1c\x1e\x8c\xd5\x3b\x1e\x36\
-\x30\x42\x00\x43\xd3\x08\xe3\x32\x09\xb1\x3d\x1c\xa5\xe4\x56\x80\
-\x2f\x4f\x49\xb0\x5f\x50\xc0\x29\x00\x4c\x08\xae\xa3\x7f\x43\x9a\
-\x20\x9c\x82\x84\xbf\x75\xba\xa4\x37\xf8\x50\x22\xe6\x2f\x9c\x00\
-\xb0\x01\x59\xb8\x82\xa2\xa5\x41\xf8\xbe\x00\xfe\xdd\xa3\x20\x43\
-\xef\x78\xd8\xc0\x65\x25\x12\x2e\xc9\x26\x24\xc7\xf4\xfc\x3a\xb3\
-\x13\x28\xaa\x95\x60\x76\x9d\xff\xf1\x28\x09\x98\x3e\x84\x90\x1d\
-\x5e\x2d\x7f\x83\x45\x13\x01\xaf\x4b\x6e\xe9\x2f\xd3\x86\x8b\x4e\
-\xbd\x83\x61\xa1\x8d\x13\x00\xd6\x2f\x0b\x57\x50\x74\x54\x06\x7e\
-\xa0\x12\xfe\xd3\xa3\x20\x45\xef\x78\xd8\xc0\xa5\xc5\x01\xe3\xb2\
-\x08\x83\x13\x7b\x9e\xb8\x89\x80\x8a\x76\x81\xe3\x4d\x02\x17\x76\
-\x68\x34\x08\xe0\xaa\x11\x2a\x52\x62\x03\x18\x28\x03\x80\x4e\x00\
-\x7f\x33\xca\xd2\x5f\x26\x0f\x15\x1d\x7a\x07\xc3\x42\x13\x27\x00\
-\xac\x4f\xbe\x5b\x44\x51\x05\x26\xfc\x80\x80\xff\xf4\x28\x48\xd5\
-\x3b\x1e\x36\x30\xb1\x46\xef\x1d\xff\xd0\x34\xf2\xe9\x59\xbd\xac\
-\x02\xfb\x6b\x04\x9a\xba\xa9\xfc\x1f\x9a\x46\x98\x96\xcb\x77\xfe\
-\x1a\xb2\x02\x78\x25\x46\x95\xfe\x8f\x8f\x28\x66\x7d\xc5\x09\x00\
-\xf3\xc9\x77\x8b\x28\x6a\x98\x19\xdf\x53\x88\xfe\xcb\xa3\x08\x9e\
-\xf8\x07\xc0\x28\x01\x83\x12\x08\x83\x13\x81\xd4\x38\x42\x8c\x01\
-\x88\x36\x7a\xef\xac\x01\x40\x56\x00\x97\x02\x98\x9d\x02\x56\x37\
-\xd0\x6a\x13\xe8\xe8\x62\x77\x78\xac\x11\xc8\x4b\x21\xa4\xc7\x01\
-\x49\x31\x84\x18\x23\xce\xab\xd0\x97\x15\xc0\xad\x7a\xff\x5f\x56\
-\xcf\xbf\x5b\x8f\x92\x80\xb8\x28\x42\x52\x2f\xcb\xfc\xe7\x72\x2b\
-\xc0\xee\x6a\xa9\xcb\x58\xce\x9a\x30\x98\x30\x86\x9f\xfb\xeb\xc1\
-\x0a\xe0\xcd\x28\xa3\xf4\x3f\x93\x06\x8b\x26\xbd\x83\x61\xa1\x81\
-\x13\x00\xd6\xa3\x85\x5b\xc9\x68\x00\x9e\x82\x82\xff\xf1\xa8\x48\
-\xd7\x3b\x9e\x50\x66\x10\xc0\xa8\x0c\xc2\xa8\x0c\x42\x54\x1f\x3b\
-\xe3\xb5\x3b\x80\xbd\x67\x24\xb8\x64\xef\xbf\x8f\x1c\x44\x98\x30\
-\x98\x60\xd0\xe8\x2f\xd8\xe1\x01\x76\x55\x4b\xb0\xb8\x7a\x7e\xdd\
-\x88\x74\xc2\xe4\x1c\x4e\x00\x74\x64\x05\xf0\x8a\xea\x94\xfe\x30\
-\x63\xa4\x30\xe9\x1d\x0c\x0b\x6e\xdc\xa0\x93\x75\x8d\x48\xfc\xd7\
-\x7a\x7a\xcc\xa0\xa2\xce\xe3\xc1\xeb\x3c\xf9\x0f\x4c\x8c\x11\xb8\
-\x72\xb8\x8a\xf1\x59\x7d\x9f\xfc\x01\x20\x3d\xce\xdb\x55\xef\x2c\
-\x01\x68\x36\xf9\x5b\x5c\xc0\xb6\xaa\xde\x27\x7f\x00\xa8\x35\x09\
-\x38\x3c\x81\x8f\x89\x75\x2b\x11\xc0\xcf\xa5\x58\xb5\xb2\xa4\x4e\
-\xf9\xf9\xd6\x2a\xe2\x6a\x0c\xd6\x2d\x5e\x01\x60\x17\xf9\xaf\x4d\
-\x74\x4d\x94\x84\x37\x9d\x32\x86\xe9\x1d\x4b\x38\x90\x04\x70\xd5\
-\x70\x15\xa9\x71\x03\xbb\x4e\x55\x87\xc0\xc1\xfa\x6f\xff\x64\x53\
-\xe3\x80\xb1\x19\x84\xec\xe4\xc0\x35\x5b\xb0\xb8\x80\x1d\xa7\xbf\
-\x5d\x79\xf0\x45\x8c\x11\xc8\x4f\x21\x64\x26\x02\xf1\x51\xde\x47\
-\x13\x11\x74\x0c\x70\xb0\x39\x43\xa0\x85\x95\xb9\x86\x77\xf8\x48\
-\x62\x76\x21\x4e\x00\xd8\x37\x7e\xbd\x81\x46\xc7\x1b\x69\xa9\x53\
-\x16\x73\xf4\x8e\x25\x9c\x0c\x4b\x23\x4c\xf5\x43\x61\x9c\xc9\x09\
-\x6c\xad\xbc\x78\xf9\x20\x23\x01\x98\x59\xa0\xf6\x6b\x65\xa1\x27\
-\x66\xa7\x77\xd9\xdf\xd9\x87\xc9\xbf\x3b\x06\x09\x48\x8e\xf1\xae\
-\x62\x0c\x4d\x23\x3e\x1c\x48\x7b\xc7\x49\xa8\x2f\x4c\xcf\x8d\x5a\
-\xa7\x77\x20\x2c\x78\x70\x02\xc0\xb0\x70\x1d\xa5\xc7\xc6\xe2\x25\
-\x87\x07\x0f\x53\xf7\x0d\xdf\x58\x3f\x4d\xcf\x23\xe4\xa7\xfa\xe7\
-\xb9\x78\x69\x83\xc0\xe9\x2e\xda\xee\xfa\xbb\xf8\xce\xec\x02\x76\
-\xf6\xf1\xce\xdf\x57\xa9\xb1\xc0\x95\x23\x54\xcd\x1e\x61\xb0\x6f\
-\x09\x88\x35\x8a\x22\x7e\x32\xa3\x40\x54\xea\x1d\x0b\xd3\x1f\xff\
-\x09\x46\xb0\x85\x2b\x28\x3a\x26\x13\xbf\xf4\x28\xf8\xa5\xac\xa2\
-\x0f\xf5\xe0\xac\xaf\x92\x63\x81\x84\x68\xef\x9d\xaf\xa2\x7a\x0f\
-\xd2\xf1\xa8\x02\x1e\x19\x20\x01\xc4\x18\x80\xb4\x38\xc2\xc8\x0c\
-\x42\x42\x54\xcf\xd7\xaa\x33\x79\x7b\xef\x77\x38\x00\x95\xbc\xcb\
-\xeb\xa3\x06\x11\xc6\x64\xfa\x27\x01\x30\x3b\xbd\xcb\xfe\xee\x00\
-\x2e\x18\xf3\x21\x41\xba\x72\x01\xf8\x8b\x3d\x5a\xfa\xfd\x15\x99\
-\xc2\xa2\x77\x30\x4c\x3f\x9c\x00\x44\xa8\xdf\x6d\xa2\x7b\x85\x84\
-\x57\xdd\x32\x32\xf5\x8e\x85\x7d\x2b\x4a\x02\xe6\x8e\x54\x91\xa0\
-\xd3\xd1\x49\x76\x0f\xb0\xfd\x94\x04\x47\x00\xee\xfc\xcf\x95\x9b\
-\x4c\xb8\x2c\x9f\x13\x00\x9d\xb5\x0a\x81\xff\x2e\xcf\x91\x5e\xe6\
-\xfa\x80\xc8\xc4\x09\x40\x84\xf9\xf5\x17\x34\x36\x36\x16\xef\xbb\
-\x3d\x98\xa6\x77\x2c\xac\x6b\xc3\xd2\x09\x53\x75\xd8\x4a\xe7\x92\
-\x81\xed\x55\x12\xac\xee\xde\x5f\x3b\x50\xfe\xaa\x8b\x60\x7e\x51\
-\x02\x21\xbd\x50\x98\x2b\xb6\xe9\x1d\x08\xd3\x16\x27\x00\x11\x62\
-\xe1\x56\x8a\x8d\x06\xfe\xe0\x91\xf1\x23\x85\xc0\x35\xd9\x41\x4a\
-\x08\xe0\x8a\xa1\x2a\x06\x25\x68\x3b\xae\xac\x00\xdb\xab\x25\x98\
-\x7a\x68\xf2\xe3\x4f\x53\x72\x08\xc3\xd3\x39\x01\x08\x26\x02\x62\
-\x8d\x64\x10\xcf\x4f\xc9\x16\x55\x7a\xc7\xc2\xb4\xc1\x05\x5f\x11\
-\xe0\x77\x1b\xe8\x56\xa3\x8a\x5a\xa7\x07\x3f\xe1\xc9\x3f\xb8\x8d\
-\xcb\x24\xcd\x27\x7f\x22\x60\x5f\xad\xd0\x6c\xf2\x17\x00\x1f\x14\
-\x14\x84\x08\x74\xbb\xa2\xa8\x47\x0e\xd4\x29\xff\xb6\x95\xa8\x87\
-\x33\x20\x59\xb8\xe0\x15\x80\x30\xf6\xab\x75\x94\x93\x18\x8d\x37\
-\x9d\x32\x6e\xd6\x3b\x16\xd6\xbb\xa1\xa9\x84\xa9\x79\x81\xdb\xd3\
-\xdf\x9d\xc3\x8d\x02\x95\x6d\xda\x8d\x9a\x16\x07\x5c\x3d\x82\x8f\
-\xb6\x0f\x72\x07\x85\x24\x3d\x33\x2d\x47\xec\xd7\x3b\x10\x16\x38\
-\x9c\x00\x84\xa1\x85\x0b\x49\xc2\x95\x78\x52\x22\xfc\x4d\x56\x31\
-\xc0\xf6\x33\x4c\x0b\x39\x49\xde\xa2\x38\xa1\xf1\x5f\x64\x4d\xa7\
-\x40\x71\x9d\xb6\x83\x4e\xc8\xf2\xdf\x8e\x05\x16\x50\x32\x80\x57\
-\x0d\x06\xe9\x57\x53\xb2\x85\x4d\xef\x60\x98\xff\xf1\x23\x80\x30\
-\xb3\x70\x03\x4d\x95\xae\xc0\x61\x55\xc1\x22\x9e\xfc\x43\x43\x66\
-\x02\xe1\x52\x1d\x26\x7f\x93\x03\x28\xad\xd7\xfe\x1e\xa0\xc3\xa9\
-\xf9\x90\xac\x7f\x8c\x00\x9e\x57\x65\xf5\x50\x49\x9d\xe7\x7a\xbd\
-\x83\x61\xfe\xc7\x09\x40\x18\x39\x50\x45\xa9\x83\x93\xe8\x5d\x59\
-\xc5\x04\xbd\x63\x61\xbe\x49\x8e\x05\x66\x16\x10\x24\x1d\xd6\xe2\
-\xa2\x0c\xde\x53\x09\xb5\xd6\x60\x16\x68\xb1\xf1\xe2\x63\xa8\x20\
-\x81\x11\x80\xb4\xa1\xa4\x56\x79\x67\x6f\x2d\x0d\xd2\x3b\x1e\xe6\
-\x3f\xfc\x57\x18\x26\x8a\xeb\xe4\x3b\x04\xc4\x6b\x0e\x0f\xf2\x36\
-\x96\x4b\x50\x79\x85\x35\xe8\x49\x02\xb8\x76\x94\x8a\x44\x9d\xf6\
-\xfc\x9f\x65\xf3\x00\x1d\x76\xef\x21\x3e\x17\x36\xff\x91\x55\x6f\
-\xb3\x21\xa7\x07\x30\xbb\xfc\x77\xd0\x4f\x4e\x32\x61\x26\xf7\x01\
-\x08\x45\x4d\x20\xfa\x59\xe1\x10\xe3\x3b\x7a\x07\xc2\x06\x8e\x13\
-\x80\x10\x57\x72\x86\x72\x61\xa0\xd7\x01\xba\xe3\xec\xc7\x76\x55\
-\x0b\x34\x5b\xf9\x47\x1b\xec\x46\x0f\x22\x4c\xcc\x0e\xad\x49\xd0\
-\xad\x00\x66\xa7\x80\xc9\xe9\x6d\x17\xec\xf0\x00\x4e\x59\xc0\xe1\
-\x06\x3c\x7d\xa8\xeb\x8b\x31\x02\xb7\x8c\xe5\x42\xc0\x50\x45\x10\
-\xab\xc9\x23\xbe\x3f\x63\x98\x68\xd0\x3b\x16\xd6\x7f\x3c\x4b\x84\
-\xb0\xe2\x7a\xf9\x6e\x41\x62\x11\x80\xf3\x96\xe5\x0e\x35\x78\x5b\
-\xc5\xb2\xe0\x76\xc3\x18\xb5\xd7\xb6\xbf\xa1\xc4\xad\x00\x76\x37\
-\xd0\x66\x17\xa8\x6c\x17\xb0\xf7\xd0\x50\xc8\x20\x80\x3b\x26\x70\
-\x02\x10\xe2\x5a\x05\xe8\xe9\x69\x79\xc6\x4f\xf5\x0e\x84\xf5\x0f\
-\xd7\x00\x84\xa0\x5d\x35\x14\x57\x52\xa7\xfc\x55\x90\x58\x85\x0b\
-\x26\x7f\x00\x01\x39\xc0\x85\xf9\x57\x7c\x34\xc2\x6a\xf2\x07\xbc\
-\x67\x12\xa4\xc6\x01\x23\x07\x11\xae\x1d\xa9\x62\x70\x0f\x7b\xfd\
-\xf5\x6a\x75\xcc\xfc\x2a\x83\x20\x56\x97\xd4\x2a\xef\x94\x35\x53\
-\xa2\xde\xc1\xb0\xbe\xe3\x04\x20\xc4\x94\xd4\xd3\x8c\x58\x49\x2d\
-\x05\xf0\x7c\x57\x9f\xb7\xb9\x81\x06\x0b\xdf\xfd\x07\x3b\x63\x98\
-\xff\x88\x8c\x12\x30\x2b\x9f\x30\xa2\x9b\x6e\x7f\x63\x79\x1b\x60\
-\xf8\x10\x78\xc4\xe9\x51\x8b\x8a\xeb\x69\xba\xde\xa1\xb0\xbe\x09\
-\xf3\xb7\xa1\xf0\x41\x44\xa2\xb4\x41\x7d\x9e\x08\xff\x07\xa0\xcb\
-\xfb\xa7\x56\x9b\x77\x4f\xb7\xbf\x0a\xb5\x58\xe0\x24\x46\x03\xd7\
-\x8f\xf6\xef\x12\xb8\x4a\x00\x44\xf0\x65\xf5\x75\x66\x81\x53\x6d\
-\x02\x2e\x19\x88\x8f\x26\x8c\x1c\x04\x0c\x4e\xe4\x04\x20\x0c\xc9\
-\x04\xfc\xbe\x32\x57\xfa\x1d\x1f\x2e\x14\x1a\x38\x01\x08\x01\xfb\
-\xea\x28\xdf\x48\xca\xbb\x10\xe2\x6a\x59\xf5\x56\x64\x3b\x65\x01\
-\xab\x0b\xb0\xb8\x01\x8b\x0b\xb0\xba\x7b\x7e\xe6\xca\x82\x8b\x10\
-\xc0\x8d\x63\x54\xc4\xf9\xa1\xe1\x6a\x93\x45\xe0\x58\x8b\x40\xa7\
-\x03\xc8\x4f\x25\x4c\xcf\xe3\xc9\x95\xe9\x87\x84\xd8\x6a\x54\xc5\
-\xa3\x53\x86\x88\x5a\xbd\x63\x61\x3d\xe3\x04\x20\xc8\x2d\xde\xa7\
-\xfc\xa5\xcd\x2e\xfd\x50\x56\x61\x50\x54\xef\xb6\x2c\x16\x1e\x06\
-\x7a\x22\x1e\x01\x38\xda\x28\x50\x7e\x4e\x1b\x5f\x01\xe0\x9a\x91\
-\x2a\x92\x63\x07\x1e\x1f\x63\xfd\x26\x60\x12\xa0\xef\x4f\xcb\x35\
-\x2e\xd7\x3b\x14\xd6\x3d\x4e\x00\x82\xd4\x3b\x15\x94\x65\x6d\xc5\
-\xf6\x46\x33\xc6\xe8\x1d\x0b\x0b\x9c\x59\x05\xd4\xaf\x83\x71\x88\
-\x80\xd2\x06\x81\xea\x8e\x8b\xff\x84\xa7\xe6\x12\x86\xa5\xf1\x2a\
-\x00\x0b\x0a\x6f\xc6\x44\x49\x3f\x9e\x98\x25\xac\x7a\x07\xc2\x2e\
-\x16\x6c\x8f\x0b\x19\x80\x77\x8a\xe9\xa6\xa6\x3a\x54\xfb\x63\xf2\
-\x8f\x36\x00\x19\x09\x84\xdc\x64\xc2\xa0\x78\x68\xde\x6e\x96\xf5\
-\x6c\x7f\xad\x40\x53\x17\x45\x9b\x66\x17\x50\xde\x26\x50\xd3\x29\
-\x40\x17\xcc\xe5\x44\xc0\x81\xfa\xae\x27\x7f\x00\x88\x0d\xb3\xdd\
-\x05\x2c\xa4\x3d\xe9\xf4\xa8\x45\x25\x0d\xc4\xdd\x49\x83\x10\x4f\
-\x07\x41\x66\xd1\x3e\xfa\x53\xa3\x05\x3f\xf5\x28\xdf\xfe\x6c\xa2\
-\x24\x20\x3e\x06\x88\x96\x08\x06\x01\x98\x7c\xe8\xc8\x96\x95\x48\
-\x18\x9b\x41\x48\xbf\x60\xd2\xb7\xb9\x81\xf2\x56\x81\x33\x9d\xe2\
-\xa2\x6e\x81\x67\x5f\x77\xe1\x84\xc3\x02\x4b\x08\x60\x64\x3a\x21\
-\x2f\x85\x20\xab\x02\xa7\xda\x81\x46\xb3\xc0\xd9\x1f\xc3\xf0\x74\
-\xc2\xe4\x1c\xef\x29\x81\x1e\x15\x28\xea\x26\x69\x00\xbc\xdb\xf0\
-\xae\x1e\xae\x72\xa2\xc7\x82\x0a\x01\x16\x41\xf4\x64\xe1\x10\xe3\
-\xc7\x7a\xc7\xc2\xbe\xc5\x6f\x13\x41\x62\x61\x19\x25\xa6\x77\xe2\
-\xab\x56\x1b\x0a\x25\xe1\x9d\xc0\x73\x92\x80\xf4\x78\x42\x62\xf4\
-\xf9\x93\x38\x11\xb0\xb5\x52\x82\xd9\x75\xf1\x75\x84\x00\xa6\xe6\
-\x10\x86\xf6\xb2\x04\xec\x94\x81\x3a\x93\x80\xd9\xe5\xed\xca\x96\
-\x1e\x0f\x0c\x8a\x23\x18\x25\xa0\xc6\x2c\x70\xa8\x5e\x70\xbd\x41\
-\x10\xc9\x49\x22\x24\xc7\x02\xa7\x3b\x44\xb7\x7d\x1e\xd2\xe2\x80\
-\xcb\x87\xaa\x88\x36\x68\x1b\x1b\x63\x3e\x22\x00\xff\x57\x91\x2b\
-\xfd\x3b\xef\x12\x08\x0e\x9c\x00\x04\x81\x37\x8a\xa8\xd0\x64\xc3\
-\x97\x1e\x05\x49\xa3\x06\x79\x9f\xdf\x46\xf5\xf0\x26\xee\x51\x81\
-\xf5\x27\xa4\x2e\x27\xe8\x58\x23\x70\xb3\x1f\x5a\xac\x36\x98\x05\
-\xf6\xd6\xf0\xaf\x47\x28\x30\x48\xc0\x98\x0c\xc2\xe8\x0c\x7d\x0e\
-\x15\x62\xac\x4f\x04\x7d\x69\x90\x0c\xf7\x4f\xc9\x16\xcd\x7a\x87\
-\x12\xe9\xb8\x06\x40\x67\xaf\xef\xa5\xe7\x9a\x3b\x69\xbf\x00\x92\
-\xae\x19\xa9\x62\x74\x46\xcf\x93\x3f\x00\x94\x35\x75\x7f\x77\xee\
-\x94\x81\x56\xfb\xc0\xe3\xca\x49\x26\x24\xc7\x0c\xfc\x3a\x2c\x70\
-\xa2\x24\x60\x74\x06\xe1\x86\xd1\x2a\xc6\x66\xf2\xe4\xcf\x42\x04\
-\x89\xb9\x8a\xa2\x16\x95\xd6\xd0\x65\x7a\x87\x12\xe9\xf8\x2d\x43\
-\x47\xaf\xed\x52\x96\x37\x5a\xa5\x07\x0c\x12\x70\xcd\xa8\xde\xfb\
-\xc2\x77\x38\x80\x63\xcd\xbd\x1f\xf4\x93\x12\x07\xcc\xf5\xc3\x73\
-\xe0\x9e\x0a\xcd\x98\x3e\x84\x00\x32\xe2\x09\x05\xa9\x40\x6e\x32\
-\xc1\xc0\x29\x3c\x0b\x5d\x2e\x21\xe8\x87\xd3\x72\x8d\x8b\xf5\x0e\
-\x24\x52\xf9\xa1\x0d\x09\xeb\xab\xa5\x07\x28\xd5\x62\xa7\xfd\x0d\
-\x56\x31\x0a\x00\x26\x66\x53\xb7\x93\x3f\x01\x68\xb6\x08\x9c\x68\
-\x15\x68\xf7\xf1\xce\xde\xe4\xf0\x36\x08\x1a\xe8\x1d\xbc\xca\x35\
-\x00\x3e\x33\x4a\xde\xa5\x78\xb7\xe2\xff\x22\x4a\x01\x6f\x71\x5f\
-\x4e\x12\x61\x48\x2a\x21\x9e\xab\xfc\x59\x78\x88\x21\x12\x8b\x4a\
-\x6a\x95\xab\x9c\x24\x3d\x3b\x3b\x5f\x38\xf4\x0e\x28\xd2\x70\x02\
-\xa0\xb1\xa5\xfb\xe8\xd2\x16\x33\x7d\x69\x75\x89\x78\x00\x18\x94\
-\x00\x0c\x4b\xed\x7a\xc6\x30\xbb\x80\x92\x3a\x09\x9d\x7d\xfc\xb3\
-\x48\x8e\xf5\xd6\x02\x0c\x84\xa2\x02\xcd\x36\xbe\xfb\xef\x4e\x72\
-\x0c\x90\x9b\x42\xc8\x48\x20\xa4\xc6\x7a\x13\x00\xc0\xfb\x7d\xdb\
-\x50\x2e\x0d\xf8\x40\x26\xa3\x01\xc8\x88\x23\x64\x27\x01\xd9\xc9\
-\x34\xe0\x9f\x27\x63\x41\x4b\xe0\x91\x58\xa1\x8c\xde\xd7\x4c\x77\
-\x5f\x96\x25\x1a\xf5\x0e\x27\x92\xf0\x3b\xbc\x86\x16\xef\xa7\x47\
-\x9a\x2c\x58\xea\x92\x61\x00\xbc\xcf\x70\xe7\x8e\x54\xbb\x3c\x19\
-\xad\xb2\x4d\xa0\xac\xe9\xe2\xad\x7a\xbe\xb8\x66\xa4\x8a\x14\x1f\
-\x3a\xc1\xc9\x2a\x70\xaa\x5d\xa0\xc9\x2a\x10\x25\x08\x83\x12\x80\
-\xac\x04\x6f\x0d\xc2\xb1\x16\xef\x1e\x74\xf6\x2d\x83\x00\x86\xa4\
-\x7a\x0f\xb8\xe9\xee\xfb\x7b\xb8\x51\xa0\xb2\xad\xef\xdf\x37\xa3\
-\xe4\xdd\xf1\x91\x11\x0f\x64\x26\x7a\x93\x0a\xde\xca\xc7\x22\x4c\
-\x1d\x48\xba\xad\x70\x88\x38\xa8\x77\x20\x91\x82\xdf\x62\x34\xf2\
-\xda\x5e\xfa\x7d\x93\x19\xbf\x3a\x3b\xa1\x0b\x01\xcc\xcc\xbf\xb8\
-\x0b\x1c\x01\x38\xd4\x20\x50\xd5\xde\xbf\x1f\x4d\x5a\x1c\x70\xf5\
-\x88\xde\xd7\xee\x9d\x32\xb0\xbb\x5a\x82\xc9\xd9\xaf\x61\x22\x4a\
-\x7c\x94\x77\x2f\xfe\xd0\x34\xea\x76\x8b\x9d\xc5\xe5\xfd\xb9\xb5\
-\xf8\xb8\x6a\x22\x09\xef\xd6\xcb\x8c\x04\x42\x56\x02\x21\x35\x0e\
-\x5c\xc4\xc7\x22\x1e\x01\x16\x08\x75\xc1\xf4\xdc\xa8\x75\x7a\xc7\
-\x12\x09\xf8\x2d\x27\xd0\x88\xc4\x2b\xbb\x69\x65\xb3\x55\xdc\x7d\
-\x76\xaa\x17\x02\x28\xcc\x23\xe4\xa7\x5c\x7c\x7b\xdf\xdf\x3b\xc8\
-\xb3\x86\xa5\x13\xa6\xe6\xf4\xbc\x6c\xe0\x51\x80\x6d\x55\x12\x2c\
-\x5d\xf4\x11\x60\xdf\xca\x88\xf7\x9e\x5c\x97\x9d\x44\xdd\xde\x8d\
-\x13\x80\x63\x4d\x02\x15\xad\x02\xbd\xa5\x5d\x42\x00\x99\x09\x84\
-\xbc\x64\xef\x2e\x0b\xde\xaf\xcf\x58\x97\x64\x12\xf8\xc1\xf4\x5c\
-\xc3\x3f\xf4\x0e\x24\xdc\xf1\x93\xc5\x00\xfa\xdb\x5a\x8a\xa1\x1d\
-\xd8\xdb\xe4\x10\x53\xce\x7e\x4c\x00\x98\x92\xdd\xf5\xe4\x5f\xd5\
-\x31\xb0\xc9\x1f\x80\x4f\x93\x4a\x69\x83\xe0\xc9\xbf\x0b\xdf\x14\
-\xdb\x25\x7b\x5b\x27\x27\x76\x79\xe8\xf2\xf9\xaa\xda\x05\x4e\xb6\
-\xf6\xfe\x33\x1b\x92\x42\x98\x98\x4d\x7e\x39\xfd\x8f\x69\xab\xe4\
-\x48\x39\x52\x93\x13\x31\xa2\x20\x47\xef\x50\x22\x85\x51\x10\xde\
-\x28\xa9\x53\x26\x4e\xcb\x95\x7e\x22\x84\xe0\xde\xa4\x01\xc2\x6f\
-\x47\x01\xf2\xca\x56\xca\x76\x4a\x28\x35\x39\x30\xf8\xdc\x8f\x4f\
-\xc8\x26\x0c\x4b\xbf\xf8\xf7\xd9\xec\x04\x0e\x37\x0c\x7c\x41\xc6\
-\xdd\x4b\xf1\x59\xbd\x59\xa0\xce\xc4\x0b\x3f\x67\x89\xaf\x97\xe2\
-\x73\x93\xbc\x93\x7e\x5c\x1f\x2b\xec\x9b\x7b\x39\xe2\x24\xda\x00\
-\x4c\xc9\x25\xe4\x25\xf3\x7b\x58\xa8\x69\xed\x30\xe1\xb5\x65\x6b\
-\xb0\x61\x5b\x31\x5e\xfc\xf5\x73\x9c\x00\x68\xef\xf9\xd2\x7a\x35\
-\x7b\x6b\x15\x3d\x76\xcd\x70\xc1\x0f\x2b\x03\x80\x13\x80\x00\x78\
-\x79\x37\x15\x5a\x5c\xd8\x6e\x77\x21\xfe\xdc\x8f\x8f\xcd\x24\x8c\
-\x1e\x74\xf1\x44\x40\x00\x0e\x36\x4a\xfd\x2a\xf8\xbb\x90\xc9\x29\
-\xbe\xbe\xe2\xc5\xdc\x0a\x70\x70\x80\x49\x86\x10\xde\x67\xe2\xb1\
-\x51\x40\xb4\x81\x10\x63\xf0\x16\xb0\x49\xc2\xfb\x3f\xa7\xec\x3d\
-\x6f\xa0\xdd\x2e\xa0\x04\xe9\x9c\x27\xce\x69\xb5\x9c\x93\x44\x88\
-\x19\xc0\x5f\x41\x4f\x4d\x9b\x06\x27\x11\xa6\xe5\x72\x05\x7f\xa8\
-\x91\x15\x05\xab\xd6\xed\xc4\xa2\x0f\xd6\xc2\xe1\xe0\xa5\x32\x3d\
-\x11\xb0\x20\x25\x5a\xc9\x2b\xaa\xa7\x79\x33\x72\x45\xab\xde\xf1\
-\x84\x1b\x7e\x6b\xf2\xb3\xc5\x7b\xe9\xb6\x46\x2b\x56\xbb\x95\xf3\
-\xbf\xb7\x79\x29\x84\xf1\x59\x5d\xcf\x88\x8d\x66\x81\x36\x9b\x7f\
-\xc6\xef\x74\x7a\x27\xe1\xae\x26\x9d\xb2\xa6\xee\xfb\xc8\xf7\x26\
-\x27\x99\x30\x22\xdd\x5b\xa9\x6e\xf0\x21\x87\x70\x78\x08\x1b\xcb\
-\xfd\x93\xd4\xf8\x4b\x52\x0c\x50\x90\xe6\x7d\xfc\xe2\xaf\x49\x39\
-\x33\x11\xa8\xe9\x3c\xff\x63\x71\x51\xc0\xe4\x6c\x42\x0e\xdf\xf5\
-\x87\x9c\x92\xb2\x0a\xbc\xb4\x78\x25\xaa\x6a\x78\x37\x5a\xf0\x10\
-\x73\x24\x52\x77\x1f\xa8\xa1\x5b\xa7\xe5\x8b\x72\xbd\xa3\x09\x27\
-\x9c\x00\xf8\xd1\x92\xbd\xf4\x5c\xbd\x05\xaf\x7a\xd4\xf3\x8b\x2b\
-\xe3\xa2\x80\x29\x3d\x14\xe6\xf9\xf2\x0c\xd9\x57\x44\x40\x45\xab\
-\xc0\x25\xd9\xe7\x8f\x57\x6f\xee\x7f\x57\xbf\x31\x19\x84\x09\x83\
-\xfb\x36\x99\x35\x5a\xfb\xb7\x85\x31\x10\x52\xe3\x80\x71\x99\x84\
-\xc1\x49\xe4\xf7\xaa\xd7\xfc\x64\x82\x65\x90\xf7\xbf\x37\x3e\x8a\
-\x90\x9f\xea\xed\x0f\xc0\x0d\xfa\x42\x4b\x4b\xbb\x09\xaf\xbf\xb7\
-\x06\xeb\xbf\x2a\xd2\x3b\x14\xd6\xb5\x51\x24\xa9\xdb\x8b\xea\xe9\
-\xd6\x19\xb9\xa2\x44\xef\x60\xc2\x05\x27\x00\x7e\xb2\x68\xaf\xf2\
-\xef\x35\x66\xfa\x6f\x95\x2e\x9e\x62\xa6\xe5\x76\x5f\xf1\xdd\xee\
-\xf0\xb6\xf8\xf5\xa7\x53\x6d\x02\x19\x09\xf8\x66\x8b\x61\xab\x4d\
-\xa0\xa4\xbe\xff\x53\x5f\xbd\x59\xc0\x68\xf0\xf6\x08\x48\xe9\x65\
-\x7f\xba\x43\xf6\x8e\x5f\x31\xc0\x62\x46\x7f\x88\x31\x7a\x4f\x46\
-\x0c\xe4\x9d\xb8\x10\xde\x4e\x8e\x13\xbb\x79\xec\xc2\x82\xdb\xd9\
-\xe5\xfe\xc5\xef\xaf\x85\xdd\xc9\xcb\xfd\x41\x6e\xb0\x04\x75\xcb\
-\x81\x3a\xba\x6d\x5a\x9e\xd8\xa9\x77\x30\xe1\x80\x13\x00\x3f\x78\
-\x6d\x37\xfd\xb5\xd6\x84\xe7\xbb\x9a\x02\xf2\x92\x09\x59\x89\xdd\
-\x4f\x0e\x35\x01\xe8\xb5\xaf\x02\xd8\x5b\x23\x90\x9d\xe4\x3d\xed\
-\xa9\xc1\xdc\xfb\x16\xb5\x9e\x58\xdd\xc0\xd1\x26\x81\xa3\x10\x30\
-\x4a\x40\x42\x0c\x90\x18\x45\x88\x32\x7a\x9b\x19\xa9\x04\xb8\x64\
-\xc0\xec\x14\x5d\x1e\x51\xac\x87\xac\x44\xc2\xf4\xbc\x81\x3d\xdf\
-\x67\xe1\xad\xf8\x70\x39\x5e\x5c\xb2\x12\xd5\xb5\x4d\x7a\x87\xc2\
-\x7c\x45\x48\x21\xa8\x1b\x0e\xd4\x79\xe6\x4d\xcb\x8b\xda\xa8\x77\
-\x38\xa1\x8e\xdf\x1e\x07\xe8\x95\xdd\xf4\x41\xa3\x05\xf7\x77\x35\
-\xc5\x4b\x02\x17\x2d\xc5\x9f\x8b\x08\xa8\x33\x07\xe6\x4e\x99\xc8\
-\x3b\xf1\xfb\x9b\xac\x7a\xcf\x1a\x30\x39\xf4\xbf\xc3\xef\x4e\x7e\
-\x2a\xa1\x30\xb7\xfb\xbd\xfb\x2c\xb2\x35\xb7\x75\xe2\x8d\xe5\xff\
-\xe4\xe5\xfe\xd0\x15\x4f\x90\x3e\x2f\xae\x97\x1f\x98\x9e\x6b\xfc\
-\x44\xef\x60\x42\x19\x27\x00\x03\xf0\xf2\x2e\x75\x53\x93\x05\xd7\
-\x75\xf7\xf9\x82\xd4\x9e\xb7\x95\x75\x3a\xbd\x95\xf9\xcc\x7f\xd2\
-\xe2\x80\x69\x79\xfe\x7f\xd6\xcf\x42\x9f\xcb\xed\xc1\x7b\xab\x37\
-\x63\xd9\x27\x5b\xe0\xf6\x78\xf4\x0e\x87\x0d\x4c\x8c\x80\xf8\xb0\
-\xb8\x56\x7e\x78\xfa\x10\xe3\x0a\xbd\x83\x09\x55\x9c\x00\xf4\x07\
-\x91\x78\x65\x17\xbe\x6a\xb2\xe2\xca\x9e\x5e\x36\x2a\xa3\xe7\xe7\
-\xc2\x81\xb8\x43\x8f\x64\x02\xc0\xd4\x5c\xd5\xa7\x02\x3c\x55\x25\
-\x58\x6d\x76\x98\x6d\x0e\x58\xac\x76\xd8\xec\x4e\xd8\x1d\x2e\xb8\
-\x3c\x1e\x38\x1c\x2e\xd8\x9d\x2e\xb8\xdd\x1e\xd8\xbe\xde\x06\xa6\
-\x28\xca\x45\xcf\x88\xdd\x6e\x0f\xdc\xee\xf3\x27\x12\xa7\xcb\x03\
-\x83\x41\x42\x94\xf1\xdb\xa2\x8f\xa8\xe8\x28\xc4\x44\x7b\x33\xc1\
-\x98\xa8\x28\x44\x47\x47\x21\x3e\x2e\x1a\x46\x83\x01\x49\x09\xf1\
-\x30\x18\x24\x24\xc4\xc7\x21\x39\x29\x1e\x49\x09\x71\x48\x4a\x88\
-\x43\x72\x62\x02\xe2\xe3\x06\x78\x9c\x23\xfb\xc6\xf6\xfd\x47\xf0\
-\xf7\xb7\x56\xa3\xbe\xa9\x4d\xef\x50\x98\xbf\x10\xa2\x84\x10\xcb\
-\x4b\xea\xe4\xc4\xc2\x3c\xe3\x9b\x7a\x87\x13\x8a\x38\x01\xe8\x2b\
-\x22\xf1\xb7\x5d\xd8\xd9\x64\xc3\xe5\x3d\xbd\x2c\x2d\x0e\x3d\x76\
-\x92\x73\x78\xd0\xef\x7e\xff\xfd\x21\x04\x90\x14\x0d\x24\xc6\x10\
-\x12\xa2\xbd\x8f\x27\x5c\x32\xd0\xe9\x14\x30\x39\xfd\x7f\x84\xad\
-\x1e\x12\x25\x3b\xda\x9a\x3b\x50\x61\xb2\xa2\xad\xc3\x8c\x4e\xb3\
-\x15\x6d\x9d\x16\xb4\x77\x9a\xd1\x69\xb2\xa2\xc3\x6c\x83\xd9\x6a\
-\x83\xd9\x62\x87\xd5\x1e\xfc\x27\x8f\x1a\x0c\x12\x92\x13\x13\xbe\
-\x4e\x0a\xe2\x91\x9c\x18\x87\xa4\xa4\x78\xa4\xa7\x24\x21\x2b\x23\
-\x0d\x99\x83\x52\x90\x91\x96\x8c\xec\xcc\x74\xa4\xa7\x24\xc1\x60\
-\xe0\xbd\x07\x17\xaa\x69\x68\xc1\x5f\x97\x7e\x82\x3d\x25\xc7\xf4\
-\x0e\x85\x05\x86\x01\x10\x8b\x8b\xeb\x95\xe4\xe9\xb9\x86\xbf\xe8\
-\x1d\x4c\xa8\xe1\x04\xa0\x0f\x16\x2e\x24\x29\x7d\x17\xed\x6f\xb5\
-\x89\xc2\xde\x5e\xdb\x53\xe5\xb9\x47\x05\xf6\xd4\x48\xf0\x0c\xa4\
-\x32\xcf\x07\xd1\x06\x6f\x33\x9a\xc1\x89\xde\xa2\xb8\xae\x76\x22\
-\xd4\x98\xbc\xcd\x81\xe4\x10\x78\x14\xe1\xb0\xd9\xd0\xd1\xde\x02\
-\x53\x47\x2b\x4c\x1d\xed\xb0\x58\x3a\x61\x31\x75\xc2\xd4\xd1\x06\
-\x8b\xb9\x13\x6e\x57\x78\x35\x0b\x53\x14\x15\x1d\x26\x0b\x3a\x4c\
-\x96\x5e\x5f\x2b\x49\xd2\xd7\x89\x41\x2a\x86\xe4\x64\x60\x58\x5e\
-\x36\x0a\xf2\xb2\x30\x2c\x2f\x0b\xf9\xb9\x59\x11\x97\x1c\x38\x5d\
-\x6e\xbc\xb3\x6a\x23\x3e\xf8\xf4\x4b\xb8\xe5\x01\x9e\xcd\xcc\x82\
-\x9d\x10\x84\x97\x4a\xea\x94\xe4\xc2\x3c\xc3\x6f\xf5\x0e\x26\x94\
-\x70\x02\xe0\xa3\xfb\x56\x90\x21\x3d\x17\xc5\xad\xb6\x6f\xfb\xfa\
-\xf7\xa4\xab\x23\x7e\x01\xef\x79\xf1\xbb\xab\x25\x98\xfc\x74\x03\
-\x2a\x7d\xdd\x99\x2f\xda\x08\x44\x49\x84\x68\x23\x90\x12\xeb\x6d\
-\xd8\x93\xd6\xc3\x96\x3d\xb7\x02\x1c\x69\x14\x38\x13\x64\x47\xfe\
-\x7a\x3c\x6e\xb4\xb5\x34\xa2\xbd\xa5\x09\x1d\x6d\x2d\xe8\x68\x6b\
-\x41\x67\x7b\x2b\x3a\xda\x5b\xe0\x72\x06\xff\x5d\xbb\x5e\x54\x55\
-\x45\x6b\x87\x09\xad\x1d\x26\x1c\x2d\xaf\x3e\xef\x73\x7f\xff\xaf\
-\x1f\x60\xda\xc4\x51\x3a\x45\xa6\xbd\xad\x7b\x0e\xe2\xe5\xb7\x3e\
-\x45\x53\x6b\x87\xde\xa1\x30\x6d\xfd\x57\x49\x9d\x12\x5f\x98\x67\
-\xf8\x85\xde\x81\x84\x0a\x4e\x00\x7c\xb0\x70\x2b\x19\x53\x8d\x28\
-\x6d\xb5\x63\xa2\xaf\x5f\xd3\x55\x71\x9f\x4b\x06\xf6\xd5\x48\x68\
-\xb7\x0f\x3c\x26\x21\xbc\xcd\x85\xf2\x53\x7d\xeb\xcc\x77\x96\x42\
-\x5f\x1f\x60\xd3\x22\x74\x2d\x40\x54\x55\x15\x1d\xad\xcd\x68\x6d\
-\x6e\x40\x4b\x53\x03\xda\x5a\x1a\xd0\xda\x54\x8f\xce\x8e\x36\x50\
-\x38\x3c\x8f\x60\x9a\x3b\x53\xd7\x84\x17\x97\xac\x42\xd1\xa1\x93\
-\x7a\x87\xc2\xf4\xf3\xf3\x92\x3a\xc5\x55\x98\x67\xf8\x4f\xbd\x03\
-\x09\x05\x9c\x00\xf4\x62\xe1\x42\x92\x52\xa2\x51\xd2\x6e\xf3\x7d\
-\xf2\x07\x80\xca\x36\x81\x9c\x24\x6f\xcb\x59\x22\xa0\xd6\x2c\x50\
-\xd6\x28\xe0\xf4\xd3\x6a\x64\x66\x02\x61\x58\x9a\xef\x13\xa5\x4b\
-\x06\xaa\x3b\x05\xaa\xda\x05\x1c\x1a\x17\x40\x13\xa9\x68\x6f\x6d\
-\x46\x53\x5d\x0d\x1a\xeb\x6b\xd0\xd4\x50\x83\xe6\x86\x5a\x78\x3c\
-\x6e\x6d\x03\x61\x61\xc9\xe9\x72\x63\xf9\xa7\x5b\xb0\x6c\xd5\x66\
-\x5e\xee\x67\x00\xf0\x9b\x92\x7a\xc5\x51\x98\x6b\xf8\x5f\xbd\x03\
-\x09\x76\x9c\x00\xf4\x84\x48\xa4\xed\xc4\xae\x36\x1b\x26\xf5\xf5\
-\x4b\xad\x2e\x60\x63\xb9\x84\xf4\x38\x82\xd5\x25\xe0\xf0\xf3\xfb\
-\x92\x2f\x8f\x74\x55\x00\x2d\x56\x81\x5a\x13\x50\x6f\xd2\xee\x70\
-\x1e\xbb\xd5\x82\xda\x33\xa7\x50\x7f\xe6\x14\x1a\xeb\x6a\xd0\xdc\
-\x58\x0b\xb7\x3b\x48\x3a\x04\xb1\xb0\xb2\x69\xe7\x01\xbc\xf2\xf6\
-\xa7\x68\x69\x37\xe9\x1d\x0a\x0b\x26\x84\x3f\x94\xd4\x2a\xee\xc2\
-\x21\x86\x17\xf5\x0e\x25\x98\x71\x02\xd0\x83\xbf\xed\xa4\xaf\x5a\
-\xed\x62\x66\x7f\xbf\x5e\x51\x81\x16\x5b\x60\x9e\xb1\xb7\x58\x05\
-\x2c\x2e\x42\xd2\x05\x3b\xc5\x3c\x8a\xf7\x24\xbe\x06\x8b\xb7\x85\
-\xaf\x16\xcb\xfc\x56\x8b\x09\x75\x67\xaa\x50\x57\x7d\x0a\x75\x35\
-\xa7\xd0\xd4\x50\x1b\x1e\xdb\x0a\x58\xd0\x3a\x53\xdf\x8c\xbf\x2c\
-\x59\x85\x7d\x07\x4f\xe8\x1d\x0a\x0b\x56\x02\x7f\x3a\x50\x2f\x5b\
-\xa6\xe5\x1a\x17\xe9\x1d\x4a\xb0\xe2\x04\xa0\x1b\xaf\xec\xa6\x8d\
-\x4d\x96\x9e\xf7\xf9\xeb\x49\x56\x81\x2f\x2b\x25\x64\x24\x7a\x9b\
-\x0d\x39\x3d\x80\xc3\xe3\x6d\xc5\x1b\xe8\xb9\xd7\x6e\xb5\xe0\x74\
-\xe5\x71\x54\x55\x1c\x47\xcd\xe9\x72\x58\xcd\x7c\xf7\xc5\xb4\x61\
-\xb7\x3b\xb1\x64\xc5\x3a\xac\xfc\x62\x07\x64\x25\x04\xb6\xae\x30\
-\x3d\x09\x22\xf1\x5a\x71\xad\x6c\xe2\x66\x41\x5d\xe3\x04\xa0\x0b\
-\xaf\xec\xa2\x8f\x9a\x2c\xb8\x5e\xef\x38\x7a\xa3\x10\xd0\x64\x09\
-\x7c\x15\xbf\xaa\xaa\x68\x69\xac\x43\xe5\xc9\x23\xa8\x3c\x51\x86\
-\xe6\x86\x5a\x2e\xd4\x63\x9a\xdb\x59\x54\x86\x3f\x2f\xfe\x18\xcd\
-\xad\x9d\xbd\xbf\x98\x31\x2f\x83\x90\xc4\xb2\xe2\x3a\xd9\x31\x3d\
-\xcf\xf8\xb9\xde\xc1\x04\x1b\x4e\x00\x2e\xf0\xf7\x5d\xca\xe2\x26\
-\x2b\xee\xd5\x3b\x0e\xbd\x39\xec\x56\x54\x1c\x3b\x8c\x8a\xe3\x87\
-\x71\xe6\x74\x39\x3c\x6e\x2e\xd8\x63\xfa\xa8\xa8\xae\xc3\x8b\x8b\
-\x57\xe1\xd0\xb1\x53\x7a\x87\xc2\x42\x11\x21\x4a\x40\x7c\x5c\x5c\
-\xef\xb9\x6b\x7a\x6e\xd4\x3a\xbd\xc3\x09\x26\x9c\x00\x9c\xe3\x95\
-\x3d\xca\x1f\x9a\xcd\xd2\x53\x7a\xc7\xa1\x17\xb3\xa9\x03\xa7\x2b\
-\x8e\xa1\xf2\xf8\x11\x54\x55\x1c\x83\xaa\x06\xb8\x53\x11\x63\x3d\
-\xb0\xda\x1d\x58\xf2\xc1\x3a\xac\x5a\xbf\x03\x8a\xc2\xbf\x8b\x6c\
-\x40\xa2\x05\x49\x2b\x4b\xea\xe9\x96\xc2\x5c\xb1\x4d\xef\x60\x82\
-\x05\x27\x00\x5f\x5b\xbc\x87\x7e\x5c\x6b\xc6\x2f\x22\x6d\x61\xbb\
-\xa3\xad\x05\x27\x8e\x1c\x40\xf9\xf1\x43\x68\xaa\xaf\xd1\x3b\x1c\
-\xc6\x40\x44\xf8\xe2\xcb\xfd\x78\x6d\xd9\x1a\x9f\xba\x20\x32\xe6\
-\xa3\x78\x40\xfd\xac\xa8\x9e\xae\x9d\x91\x2b\x4a\xf4\x0e\x26\x18\
-\x70\x02\x00\xe0\xf5\xfd\x74\x57\xbd\x09\x2f\x45\xca\x3d\x86\xcb\
-\x69\xc7\x89\xb2\x52\x94\x95\xee\x47\x5d\x4d\x15\x57\xec\xb3\xa0\
-\x71\xb2\xaa\x16\x2f\x2d\x5e\x85\xc3\x27\xaa\xf4\x0e\x85\x85\x23\
-\x42\x8a\x04\x75\x7d\x71\x0d\x5d\x3e\x3d\x5f\x54\xe8\x1d\x8e\xde\
-\x22\x3e\x01\x78\x7d\x2f\xcd\x68\x35\x63\xa5\xac\x86\xf7\x09\xb2\
-\x8a\x22\xe3\x74\xc5\x71\x1c\x3d\xb8\x1f\x15\xc7\x0f\x43\xe1\x0a\
-\x6a\x16\x44\x2c\x36\x3b\xde\xfc\x70\x3d\x56\xae\xdb\xc1\x8f\x9e\
-\x58\xa0\x65\x08\x49\xfd\xec\x50\x35\xcd\x99\x3c\x54\x44\x74\xbf\
-\xe8\x88\x4e\x00\x5e\xd9\x47\xf9\xed\x36\x6c\x73\x2b\xe8\xe2\x98\
-\x9c\xf0\xd0\xdc\x58\x87\x83\xfb\x77\xe2\xf8\x91\x12\xee\xa5\xcf\
-\x82\x8e\xaa\x12\xd6\x6c\xde\x83\xd7\x97\xff\x13\x66\x8b\x4d\xef\
-\x70\x58\xe4\x18\x2f\x1b\x95\xd5\xe5\xe5\x74\xe3\xe8\xd1\x22\x62\
-\xbb\x94\x45\x6c\x02\xb0\x70\x2d\x25\x5b\xed\x28\x75\x7a\x10\xa7\
-\x77\x2c\xfe\xa6\x28\x0a\x2a\x8e\x1f\xc6\xa1\xe2\x5d\xa8\xae\xe4\
-\x46\x29\x2c\x38\x9d\x3c\x55\x8b\x3f\x2f\x5a\x89\xb2\xf2\xd3\x7a\
-\x87\xc2\x22\x92\xb8\xca\x12\xaf\xbe\x45\x44\x0f\x0a\x21\x22\xf2\
-\x39\x68\x44\x26\x00\xf7\xad\x20\x43\x42\x22\x4a\x2c\x4e\xa4\xeb\
-\x1d\x8b\x3f\x59\xcd\x26\x1c\x2a\xd9\x85\x03\x7b\x77\xc0\x61\xb7\
-\xea\x1d\x0e\x63\x5d\x32\x5b\xed\x58\xba\x82\x97\xfb\x59\x50\xf8\
-\x4e\x49\xbd\x7a\x02\xc0\x42\xbd\x03\xd1\x43\x44\x26\x00\x73\x72\
-\x68\x7d\xbb\x43\x8c\xd4\x3b\x0e\x7f\xa9\xa9\xaa\x40\xd1\xae\x2d\
-\x38\x55\x7e\x94\x1b\xf4\xb0\xa0\xa5\xaa\x2a\x56\x6f\xdc\x8d\x45\
-\xcb\xd7\xc2\x62\xf3\xc3\x91\x98\x8c\xf9\x81\x00\x7e\x73\xa0\x56\
-\xae\x9a\x36\xc4\xf8\xb6\xde\xb1\x68\x2d\xe2\x12\x80\x57\x77\xd1\
-\xff\x35\x5a\x71\x9d\xde\x71\x0c\x18\x11\x2a\x4f\x96\x61\xdf\xf6\
-\x4d\xde\x4a\x7e\xc6\x82\xd8\xf1\xca\x33\x78\x71\xd1\x2a\x1c\xad\
-\xa8\xd6\x3b\x14\xc6\x2e\x24\x48\x12\x8b\x4a\xea\x3c\x75\x85\x79\
-\x51\x9b\xf4\x0e\x46\x4b\x11\x95\x00\xbc\xb6\x87\x9e\x6c\x32\xe3\
-\xdf\xf4\x8e\x63\x20\x54\x45\xc1\xf1\xc3\x25\xd8\xbb\x63\x13\xda\
-\x5a\x1a\xf5\x0e\x87\xb1\x1e\x99\x2d\x36\x2c\xfd\x68\x03\x56\xae\
-\xdb\x0e\x55\xe5\xd5\x29\x16\xa4\x08\x51\x80\xb4\x72\x4f\x0d\x5d\
-\x39\x2b\x5f\x1c\xd2\x3b\x1c\xad\x44\x4c\x02\xf0\xc6\x1e\xba\xa2\
-\xd9\x8a\x45\xa1\xfa\xc4\x51\xf6\x78\x70\xa8\x64\x17\x8a\x76\x6e\
-\x85\xd9\x14\xd1\x3b\x57\x58\x08\x50\x14\x15\x2b\xd7\xed\xc0\x92\
-\x0f\xd7\xc1\x66\xe7\xdd\x27\x2c\x24\x24\x1f\x6f\xa6\x6d\x6f\x14\
-\xd1\x4d\xcf\xce\x10\x7b\xf5\x0e\x46\x0b\x11\x91\x00\xbc\xb1\x8d\
-\x72\x3a\x1c\xd8\xe8\x51\x21\xe9\x1d\x4b\x5f\x11\x11\x4e\x1e\x3d\
-\x88\x6d\x1b\x3f\x83\xa9\xa3\x4d\xef\x70\x18\xeb\x55\xe9\xd1\x4a\
-\xbc\xb4\x64\x15\x2a\xab\xeb\xf5\x0e\x85\xb1\x3e\x69\x30\x8b\x14\
-\x83\x84\xcd\x4b\xb7\xd2\x90\x27\xae\x11\x61\x7f\xea\x54\xd8\x27\
-\x00\x0b\xb7\x92\xd1\x02\x2a\xb2\x7b\x44\xac\xde\xb1\xf4\x55\x75\
-\xe5\x09\x6c\x5d\xbf\x1a\xad\x4d\xfc\x46\xca\x82\x5f\x5b\xa7\x19\
-\xaf\xbe\xfb\x39\x36\x6c\x2b\xe6\x62\x54\x16\x72\x08\x80\x4b\x06\
-\x54\x42\x42\x5c\x02\xf6\x02\x18\xab\x77\x4c\x81\x16\xf6\x09\xc0\
-\xa0\x18\xac\x69\xb1\x8a\x5c\xbd\xe3\xe8\x8b\xba\x33\xa7\xb0\x7d\
-\xd3\xe7\xa8\xad\xe6\xd3\xcf\x58\xf0\x3b\xbb\xdc\xbf\xf8\xc3\x2f\
-\x60\xb7\x3b\xf5\x0e\x87\xb1\x7e\x11\x00\x06\xc5\x13\x5a\x6c\x02\
-\x2d\x36\x8c\x79\x63\x2f\xbd\xf7\xec\x4c\xf1\x90\xde\x71\x05\x52\
-\x58\x27\x00\xaf\xec\xa4\x9f\x35\x5b\x71\x93\xde\x71\xf8\xaa\xb5\
-\xa9\x1e\x5f\xae\xff\x14\xa7\x2b\x8f\xeb\x1d\x0a\x63\x3e\x29\x3e\
-\x5c\x8e\x97\xde\x5c\x85\xd3\x35\x5c\x90\xca\x42\xdf\x98\x4c\xa0\
-\xe5\xeb\x86\x94\x0d\x26\x3c\xf8\x66\x11\x6d\x7d\x72\x86\x58\xac\
-\x6f\x54\x81\x13\xb6\x09\xc0\x2b\xbb\x68\x76\xab\x0d\x7f\x08\x85\
-\x85\x48\x8f\xdb\x8d\xfd\xbb\x36\x63\xef\xb6\x8d\xdc\xa3\x9f\x85\
-\x84\xd6\x0e\x13\x5e\x5b\xb6\x86\x97\xfb\x59\x58\xc9\x4c\x20\x64\
-\x27\x01\x8d\x16\x01\x15\x40\xa3\x19\xaf\xff\x63\x1f\xed\xfe\xee\
-\x65\xa2\x4c\xef\xd8\x02\x21\x2c\x13\x80\x85\xbb\x28\xdd\xe2\xc2\
-\x06\x85\x82\xbf\xe8\xaf\xf2\xc4\x11\x6c\xfa\xe7\x47\xb0\x98\xc2\
-\xbe\xde\x84\x85\x01\x59\x51\xb0\x6a\xdd\x4e\x2c\xfa\x60\x2d\x1c\
-\x8e\x88\x6d\xa1\xce\xc2\xd8\xf8\x2c\x42\xa3\xc5\x7b\x36\x9c\x53\
-\x86\xc1\xe2\xc4\x8e\x85\x55\x94\xb3\x70\xb8\x08\xbb\xe7\x5b\xe1\
-\x97\x00\x10\x89\xa4\x6d\xb4\xd7\xe4\x11\x09\x7a\x87\xd2\x93\xf6\
-\xd6\x66\x6c\x59\xbb\x92\x97\xfb\x59\xc8\x28\x39\x52\x8e\x17\x97\
-\xf0\x72\x3f\x0b\x6f\x29\xb1\x40\x72\x2c\x60\xfe\x7a\xba\x37\x39\
-\x91\x9a\xd3\x84\x2d\x00\x66\xeb\x1a\x58\x00\x84\x5d\x02\xf0\xb7\
-\x5d\xea\xbb\xad\x2e\x69\x94\xde\x71\x74\xc7\xe3\x71\x63\xe7\x96\
-\xb5\x28\xd9\xbb\x0d\x2a\x2f\xf7\xb3\x10\xd0\xd8\xd2\x8e\xbf\xbd\
-\xb5\x1a\xdb\xf6\x1e\xd6\x3b\x14\xc6\x34\x91\x10\x4d\x30\x3b\xbf\
-\x3d\x21\xbe\xd1\x82\xcb\xdf\x2b\x51\x7e\xfb\x50\xa1\xe1\x37\x3a\
-\x86\xe5\x77\x61\x95\x00\xbc\xbe\x87\x1e\x6e\xb0\x20\x68\xab\x36\
-\x1b\x6a\xab\xb1\x76\xd5\x32\x74\xb4\x35\xeb\x1d\x0a\x63\xbd\x3a\
-\xbb\xdc\xbf\xf8\xfd\xb5\xb0\x3b\x79\xb9\x9f\x45\x0e\xf9\x82\x7b\
-\x33\x22\xa0\xba\x53\xfa\xf5\xea\x13\xb4\x71\xde\x58\xb1\x5d\x9f\
-\xa8\xfc\x2f\x6c\x12\x80\xd7\x0e\xd0\xb0\xb6\x0e\x5a\x4a\x24\x7a\
-\x7f\xb1\xc6\x14\x45\xc1\xae\xad\x5f\x60\xdf\x8e\xcd\x20\x0a\xd5\
-\x5e\x84\x25\xdb\xf2\xe7\x00\x00\x20\x00\x49\x44\x41\x54\x2c\x92\
-\xd4\x37\xb5\xe1\x85\xdf\xbe\x8e\xba\xa6\x56\xbd\x43\x61\x4c\x73\
-\x2e\xf9\xe2\x79\xc4\x29\x43\x9c\x6e\xa6\x2f\xf6\x94\x53\xee\xac\
-\xd1\xc2\xac\x43\x58\x7e\x17\x1e\x09\x00\x91\x70\xee\xa0\xed\x2e\
-\x59\x04\xdd\x7f\x4f\x6b\x73\x03\xd6\xae\x5a\x86\xe6\x86\x5a\xbd\
-\x43\x61\xcc\x67\x4d\xad\x1d\x3c\xf9\xb3\x88\xa5\x74\xb3\xb1\xa5\
-\xdd\x21\x12\x8e\x76\xd2\x66\x00\x97\x6a\x1a\x50\x80\x04\x7d\x95\
-\xbc\x2f\x5e\xdd\xa3\x2e\xe9\x70\x88\x21\x7a\xc7\x71\x2e\x22\x15\
-\xfb\x76\x6c\xc2\xbb\xaf\xff\x89\x27\x7f\xc6\x18\x0b\x21\xe9\xf1\
-\xdd\x6f\x6d\xad\xee\x14\x33\x3e\x2c\x55\xfe\x43\xc3\x70\x02\x26\
-\xe8\xee\x98\xfb\xea\xd5\x1d\x74\x4b\xb3\x95\x9e\xd0\x3b\x8e\x73\
-\x59\xcd\x26\x7c\xfe\xd1\x5b\xa8\x3b\xc3\x9d\xfc\x18\x63\x2c\xd4\
-\x4c\xca\x26\x38\x3d\x40\x8b\xed\xdb\x47\x01\x51\x12\x90\x18\xe3\
-\x2d\x10\x54\x20\x7e\x55\x54\x4f\x6f\xcf\xc8\x15\x67\x74\x0c\x73\
-\xc0\x42\x3a\x01\xf8\xdf\x8d\x94\x62\xf2\xd0\x4a\x35\x88\x9e\xfb\
-\xd7\x54\x57\x62\xcd\x47\x6f\xc1\x66\x09\x8b\x47\x44\x8c\x31\x16\
-\x71\xa2\x0d\xc0\x9c\x61\x04\x97\x4c\xf0\xa8\x80\x51\x02\x62\xcf\
-\x9f\x2d\x63\x41\xea\xbb\x2b\x88\xae\x5d\x20\x44\xc8\x6e\xe7\x0a\
-\xe9\x47\x00\xb1\x71\xd8\xea\xf0\x88\x38\xbd\xe3\x00\x00\x10\xa1\
-\x78\xcf\x57\xf8\xe8\xed\x57\x78\xf2\x67\x8c\xb1\x30\x10\x63\x04\
-\x12\xa3\x2f\x9a\xfc\xbf\x26\xae\x1a\x5d\xaf\xfe\x54\xeb\x98\xfc\
-\x29\x64\x13\x80\xd7\xf6\xd0\xbf\xb4\xdb\x31\x4d\xef\x38\x00\xc0\
-\xe3\x76\xe1\xf3\x15\x6f\x61\xeb\x17\xab\x78\x6f\x3f\x63\x8c\x45\
-\x08\x12\xf8\xfd\x81\x06\x0a\xd9\x82\xc0\x90\x4c\x00\xfe\xbe\x97\
-\x86\xb7\x59\xf1\xff\xf4\x8e\x03\x00\xda\x5b\x9a\xf0\xee\x3f\xfe\
-\x8c\x13\x47\x4b\xf5\x0e\x85\x31\xc6\x98\x96\x08\x51\xa4\xaa\x6f\
-\x17\xd5\x53\xbc\xde\xa1\xf4\x47\x48\x26\x00\xb2\x07\x5b\xdc\x2a\
-\x0c\x7a\xc7\x51\x7e\xfc\x30\xde\x7d\xe3\x4f\x68\x6f\x69\xd2\x3b\
-\x14\xc6\x18\x63\xfa\x18\x2f\x41\xfd\x93\xde\x41\xf4\x47\xc8\x25\
-\x00\x6f\xec\xa6\xbf\x77\xd8\x31\x4c\xef\x38\x0e\x17\xef\xc6\xe7\
-\x1f\xbe\x09\x8f\xc7\xad\x77\x28\x8c\x31\xc6\xf4\x44\xf8\x5e\x49\
-\x9d\x7c\xa7\xde\x61\xf4\x55\x48\x25\x00\x4b\xf7\xd0\xac\x26\x3b\
-\x7e\xa0\x6b\x10\x44\xd8\xf5\xe5\x17\x58\xff\xd9\x07\x50\x55\xee\
-\xea\xc7\x18\x63\x0c\x00\xc4\xe2\x7d\xcd\x94\xad\x77\x14\x7d\x11\
-\x32\x09\xc0\xc2\x85\x24\xb5\xbb\xf1\xb9\xac\x40\xb7\x3d\x7f\x8a\
-\xa2\x60\xcd\xaa\x77\xb1\x6b\xeb\x3a\xbd\x42\x60\x8c\x31\x16\x9c\
-\x32\xa3\x3c\xb4\x48\xef\x20\xfa\x22\x64\x12\x80\xc1\x37\x61\x89\
-\xc9\x89\x0c\xbd\xc6\xf7\xb8\x5d\xf8\xe4\xbd\x7f\xe0\xf8\xa1\x62\
-\xbd\x42\x60\x8c\x31\x16\xc4\x08\x74\xfb\xfa\x93\xca\x8f\xf4\x8e\
-\xc3\x57\x21\x91\x00\x2c\xda\x43\x93\x5b\xed\x78\x4c\xaf\xf1\xad\
-\x16\x13\xde\x5b\xf4\x12\x4e\x57\x1e\xd7\x2b\x04\xc6\x18\x63\x21\
-\xe0\x70\xbd\x78\xe9\xbd\x03\x34\x55\xef\x38\x7c\x11\x12\x09\x80\
-\xc5\x8d\x7f\x7a\x74\x5a\xfa\xb7\x5b\x2d\xf8\xe8\x9d\x57\xd1\xda\
-\xdc\xa0\xc7\xf0\x8c\x31\xc6\x42\x44\x87\x03\x30\xbb\x85\xa1\xc5\
-\x8e\x35\x7a\xc7\xe2\x8b\xa0\x4f\x00\x5e\xdf\xad\xfc\xae\xd3\x09\
-\x5d\x0e\xfa\xb1\xdb\x2c\xf8\xf0\xed\x97\xd1\xd6\xdc\xa8\xc7\xf0\
-\x8c\x31\xc6\x42\xc8\xa9\x76\xef\x7d\x6a\xbb\x0d\x79\x6f\x15\x29\
-\x7f\xd4\x39\x9c\x5e\x05\x75\x02\xf0\xda\x2e\xca\x6b\xb5\x4b\xbf\
-\xd4\x63\x6c\xbb\xcd\x82\x0f\x97\xf2\xe4\xcf\x18\x63\xac\x77\x76\
-\x37\x50\x67\xfa\x76\xa1\xba\xce\x24\x7e\xfa\xfe\x01\x1a\xa6\x5f\
-\x44\xbd\x0b\xea\x04\x40\x56\x69\xbd\x5b\xd1\xbe\xe1\xcf\x37\x93\
-\x7f\x0b\x4f\xfe\x8c\x31\xc6\x7a\x57\xd6\x24\xa0\x9e\x73\x8a\xb0\
-\x4b\x11\x52\x9b\x13\xff\xd4\x2f\xa2\xde\x05\x6d\x02\xb0\x78\x2f\
-\x3d\xdd\x6a\x17\x13\xb5\x1e\xd7\x61\xb7\x62\xc5\x5b\xaf\xf0\xe4\
-\xcf\x18\x63\xcc\x27\xed\x0e\xa0\xde\x7c\x71\x99\x5a\x8b\x05\x13\
-\x96\x16\x51\xd0\xee\x0a\x08\xca\x04\xe0\x8f\x07\x29\xa1\xc5\x8e\
-\x97\xa9\xf7\x97\xfa\x95\xcb\x69\xc7\x87\x4b\x5f\xe6\x82\x3f\xc6\
-\x18\x63\x3e\x21\x02\x0e\x37\x48\xe8\x6a\xbe\x22\x00\xcd\x56\xfc\
-\x71\xc9\x0e\x4a\xd2\x3a\x2e\x5f\x04\x65\x02\x90\xe2\xc2\x27\x0e\
-\x0f\x62\xb4\x1c\x53\x55\x14\x7c\xfa\xe1\x52\x9e\xfc\x19\x63\x8c\
-\xf9\xec\x68\xb3\x40\x87\xa3\xfb\xcf\xdb\xdc\x88\xf1\x18\xb1\x52\
-\xbb\x88\x7c\x17\x74\x09\xc0\xf2\x52\xba\xa2\xc1\x8c\x1b\x34\x1d\
-\x94\x08\xeb\x3e\x7b\x1f\x67\x4e\x9d\xd4\x74\x58\xc6\x18\x63\xa1\
-\xab\xbc\x4d\xa0\xa2\xb5\xf7\x1d\xea\x4d\x16\xdc\xf0\xee\x01\x9a\
-\xad\x41\x48\x7d\x12\x74\x09\x40\xab\x15\x2b\x14\x8d\x5b\xec\xef\
-\xfa\x6a\x1d\x8e\x96\xee\xd7\x76\x50\xc6\x18\x63\x21\x49\x56\x81\
-\xd2\x7a\x81\xb2\x46\xd1\xe5\xd2\xff\x85\x14\x02\xda\xec\x58\x11\
-\xf0\xc0\xfa\x28\xa8\x12\x80\xb7\x8b\x94\x9f\xb7\xda\x91\xa3\xe5\
-\x98\xc7\x8f\x94\x60\xd7\x97\xeb\xb5\x1c\x92\x31\xc6\x58\x08\x52\
-\x01\x54\x77\x0a\x6c\xae\x90\x70\xba\xa3\x6f\xbd\xe9\xda\x6d\xc8\
-\x7b\xa7\x44\xf9\x45\x60\x22\xeb\x9f\xa0\x49\x00\xde\x39\x48\x09\
-\x8d\x56\xf1\x5b\x2d\xc7\xac\xa9\xae\xc4\x17\xab\xde\xf3\x56\x71\
-\x30\xc6\x18\x63\x5d\x70\xc9\xc0\x89\x16\x81\x8d\x27\x24\x1c\xa8\
-\x13\x70\x78\xfa\x77\x9d\x46\xb3\xb4\xf0\x8d\x22\x8a\xf7\x6f\x74\
-\xfd\x67\xd4\x3b\x80\xb3\x5c\x6e\xac\xb0\xb9\x45\xb4\x56\xe3\xb5\
-\xb7\x34\x61\xf5\xf2\xc5\x50\x14\x59\xab\x21\x19\x63\x8c\x85\x10\
-\xbb\x07\x28\x6f\x15\x38\xd3\x21\xa0\xf8\xe1\x3e\xd1\xea\x46\x4c\
-\xaa\x82\xe5\x00\xe6\x0d\xfc\x6a\x03\x17\x14\x2b\x00\x2b\xca\x68\
-\x6a\xa3\x19\xb7\x68\x35\x9e\xdb\xed\xc2\xea\x0f\x97\xc0\xe5\xb4\
-\x6b\x35\x24\x63\x8c\xb1\x10\xe1\x92\x81\x92\x3a\x81\x4d\x27\x25\
-\x54\xb5\xfb\x67\xf2\x3f\xab\xc9\x8a\x3b\x97\x1d\xa2\x49\xfe\xbb\
-\x62\xff\x05\xc5\x0a\x40\xab\x05\x1f\x68\x76\xd8\x0f\x11\xd6\x7f\
-\xb2\x1c\xed\x2d\x4d\x9a\x0c\xc7\x18\x63\x2c\x74\xb4\xd8\x04\xf6\
-\xd7\x08\xb8\x95\xc0\x5c\xdf\xa3\x40\x74\x58\xf0\x31\x80\xb1\x81\
-\x19\xc1\x77\xba\xaf\x00\x2c\x3f\x40\xf3\x9a\x2c\xda\x7d\x23\xf6\
-\xef\xda\x82\x13\x47\x4b\xb5\x1a\x8e\x31\xc6\x58\x88\x68\xb6\x0a\
-\xec\x3e\x1d\xb8\xc9\xff\xac\x56\x1b\xc6\xbc\xb9\x8f\xee\x0f\xec\
-\x28\xbd\xd3\x7d\x05\xa0\xc5\x8e\x7f\x68\x55\x83\x57\x77\xe6\x14\
-\xb6\x6f\x0a\x89\x53\x1a\xd9\x05\x24\x49\x60\x50\x6a\x32\x72\xb2\
-\x06\x21\x2d\x35\x11\xc9\x89\xf1\x48\x4a\x8c\x47\x72\x42\x3c\x84\
-\x00\x0c\x06\x03\x14\xc5\xfb\x57\x5b\x79\xa6\x01\x1b\xb6\x15\xeb\
-\x1c\x31\x63\x2c\x94\x58\x5d\xc0\xfe\x1a\x01\x2d\x76\xa1\x13\x80\
-\x0e\x27\x5e\x05\xf0\xa1\x06\xc3\x75\x4b\xd7\x04\x60\x59\x89\xf2\
-\x8b\x93\xad\xc8\xd4\x62\x2c\xb7\xd3\x81\xb5\x2b\xdf\x85\xaa\x6a\
-\xdc\x64\x80\xf5\x89\x10\x02\xf9\x39\x99\x18\x3b\x72\x08\x46\xe4\
-\xe7\x60\x58\x7e\x36\x86\xe7\x67\x63\x70\x46\x1a\xa2\x8c\xbe\x9d\
-\x0b\xb5\xff\xe0\x49\x4e\x00\x18\x63\x3e\x53\x09\xd8\x73\x46\x82\
-\x47\xc3\xe9\xc1\xe4\x44\xfa\xd2\x62\xfa\xd5\x13\xd3\xc5\xff\x68\
-\x37\xea\xf9\x74\x4b\x00\x16\x12\x19\x1b\xb7\xe3\x37\x5a\x8d\xb7\
-\xe1\xf3\x15\x30\x75\xb6\x6b\x35\x1c\xf3\x91\xc1\x20\x61\xc2\xa8\
-\xa1\x28\xbc\x64\x34\xa6\x8c\x1f\x81\xf1\xa3\xf3\x91\x94\x10\x34\
-\xbb\x64\x18\x63\x11\xe0\x78\x8b\x80\xd5\xad\xfd\xb8\x2d\x16\xfc\
-\xc7\x8a\x32\xfa\xd3\x82\x89\x42\x87\xd1\x75\x4c\x00\x86\x15\xab\
-\xaf\x9d\x72\x4a\x71\x5a\x8c\x55\x56\xba\x0f\xc7\x8f\x94\x68\x31\
-\x14\xf3\x41\x5a\x4a\x12\xae\xb8\x74\x22\xe6\x4c\xbf\x04\x85\x97\
-\x8c\x42\x7c\x9c\xa6\xc7\x3e\x30\xc6\xd8\x37\xcc\x4e\xa0\xa2\x45\
-\x9b\x1a\xf4\x0b\x59\xdd\x88\xb5\x39\xd4\x97\x01\x7c\x57\x8f\xf1\
-\x75\x49\x00\x96\xed\xa1\xe4\x5a\x0b\x1e\xd7\x62\x2c\x8b\xb9\x13\
-\x9b\xbf\x58\xa5\xc5\x50\xac\x07\xe9\xa9\x49\xb8\x6e\xf6\x34\x5c\
-\x33\x7b\x0a\x2e\x19\x33\x1c\x92\xa4\xcf\x1f\x1c\x63\x8c\x9d\xeb\
-\x50\xa3\x36\xcf\xfd\xbb\xd3\x68\x91\x9e\x58\xb6\x87\xfe\xf5\xe1\
-\x59\xc2\xac\xf5\xd8\xba\x24\x00\x2e\x89\xde\xb2\x7b\x84\x26\x63\
-\x6f\xf8\xec\x43\xb8\x9d\x3d\x1c\xd5\xc4\x02\x26\xda\x68\xc4\xd5\
-\xb3\x26\xe3\xe6\xab\x2f\xc5\x8c\xc9\x63\x60\x30\xe8\xbe\xe9\x84\
-\x31\xc6\xbe\xd1\x60\x16\x68\xb5\xe9\x7b\x33\xe2\xf0\xc0\x68\x8f\
-\xc7\x5b\x00\xe6\x6b\x3d\xb6\xe6\x09\xc0\x8a\x72\xca\xac\xa8\xa1\
-\xbb\xb4\x18\xeb\x70\xc9\x1e\x54\x95\x1f\xd5\x62\x28\x76\x8e\xcc\
-\xf4\x14\xdc\x71\xfd\x2c\xdc\x7d\xd3\x1c\xa4\xa5\x04\xe5\x31\xd8\
-\x8c\xb1\x08\xa7\x10\x70\xa4\x29\x38\x56\x22\x9b\xad\x34\xef\x8d\
-\x22\x2a\x78\x76\x86\x38\xa3\xe5\xb8\x9a\x27\x00\x56\x13\xde\x75\
-\xca\x22\xe0\xb7\x82\x36\xab\x19\x5b\xd7\xaf\x0e\xf4\x30\xec\x1c\
-\x13\x47\x0f\xc3\x43\xf3\xae\xc5\x15\x97\x5e\xc2\x4b\xfc\x8c\xb1\
-\xa0\x56\xd9\x2a\x60\xd3\xa5\xf4\xee\x62\x6e\x45\x08\xd5\x43\x6f\
-\x03\xb8\x46\xcb\x71\x35\x4d\x00\x56\x1f\xa6\xfc\xb2\x66\xdc\xa8\
-\xc5\x58\x5b\xbf\xf8\x84\x97\xfe\x35\x32\xab\x70\x3c\x1e\x9e\x77\
-\x1d\xa6\x4e\x18\xa9\x77\x28\x8c\x31\xd6\x2b\x97\x0c\x9c\x6c\x0d\
-\xae\x9b\x94\x56\xbb\xb8\xfa\x9d\xbd\x34\xfc\xd1\x99\xa2\x4a\xab\
-\x31\x35\x4d\x00\xda\xec\x78\xdb\xa5\x41\xcb\xdf\xea\xca\x13\x5c\
-\xf5\xaf\x81\x4b\xc6\x0c\xc3\xb3\x0f\xdd\x86\x69\x13\x47\xe9\x1d\
-\x0a\x63\x8c\xf9\xec\x74\x87\x80\x1c\x64\x2d\x61\x3c\x0a\x84\x03\
-\x58\x0a\x60\xae\x56\x63\x6a\x96\x00\xbc\x7f\x98\xf2\x4f\xb5\x04\
-\xfe\x3f\x4c\x51\x64\x6c\xfa\xe7\x47\x81\x1e\x26\xa2\x8d\x28\xc8\
-\xc1\xf7\x1f\xbe\x03\xb3\x0a\xc7\xeb\x1d\x0a\x63\x8c\xf5\x09\x11\
-\x50\xdd\x11\x5c\x77\xff\x67\x35\x5b\x71\x95\x96\xab\x00\x9a\x25\
-\x00\x56\x07\xde\x71\xc9\x81\xbf\xfb\x2f\xda\xb9\x15\x1d\x6d\x2d\
-\x81\x1e\x26\x22\x25\x25\xc4\xe3\xa9\xfb\x6f\xc6\xdd\x37\xcd\xe1\
-\x8a\x7e\xc6\x58\x48\xea\x70\x7a\x8f\xf9\x0d\x46\x1e\x05\xc2\x29\
-\xf0\x26\x34\xaa\x05\xd0\x24\x01\x58\x79\x90\x86\x1c\x6f\xc5\xd5\
-\x81\x1e\xc7\x66\x35\x63\xcf\xf6\x8d\x81\x1e\x26\xe2\x08\x21\x70\
-\xfb\x75\x33\xf1\xdc\x83\xb7\x23\x25\x39\x41\xef\x70\x18\x63\xac\
-\xdf\x3a\x1d\xc1\x79\xf7\x7f\x56\x93\x05\x57\xbf\x57\x42\x43\x1f\
-\x2a\x14\xd5\x81\x1e\x4b\x93\x04\xa0\xd3\x83\x25\x5a\x3c\xfb\xdf\
-\xbe\x69\x0d\x3c\x6e\x57\xa0\x87\x89\x28\xb9\x83\x07\xe1\x67\xcf\
-\x2e\xc0\x8c\xc9\x63\xf4\x0e\x85\x31\xc6\x06\xcc\x25\xeb\x1d\x41\
-\xcf\x3c\x0a\x84\xd5\x83\xa5\x00\xae\x0d\xf4\x58\x01\x5f\xc7\x5d\
-\x55\x4b\x83\x9a\xcd\xb8\x3e\xd0\xe3\x34\x35\xd4\xa2\xac\x74\x5f\
-\xa0\x87\x89\x18\x42\x08\x2c\xb8\xed\x2a\xbc\xfd\xe7\x9f\xf1\xe4\
-\xcf\x18\x0b\x1b\x5a\x9d\x3e\x3b\x10\xcd\x56\xcc\x7d\xbb\x94\xf2\
-\x02\x3d\x4e\xc0\x57\x00\x2c\xcd\x78\xcd\x21\x07\x3e\xd1\xd8\xb6\
-\xf1\x33\x50\x28\xfc\x64\x43\x40\x5a\x4a\x12\xfe\xfd\x07\x0f\x70\
-\x91\x1f\x63\x2c\xec\x44\xf9\x76\xa8\xa8\xae\x3c\x0a\x84\xdd\x41\
-\xaf\x03\xb8\x23\x90\xe3\x04\x74\x62\xde\xb5\x8b\xe2\x9a\xcc\x74\
-\x77\x20\xc7\x00\x80\xda\xea\x53\xa8\xae\x3c\x11\xe8\x61\x22\xc2\
-\x8c\xc9\x63\xf0\xd6\x9f\xff\x95\x27\x7f\xc6\x58\x58\x8a\x8f\xd2\
-\x3b\x02\xdf\xb4\xdb\xc5\x2d\x0b\x8b\x28\xa0\x47\xa3\x06\x74\x05\
-\xa0\x32\x56\xfd\xb3\xd5\x2a\x05\x74\x0c\x49\x00\x25\xdb\x3f\x0f\
-\xe4\x10\x11\x41\x92\x04\x9e\xbe\xff\x56\x3c\x32\xff\x3a\x08\x11\
-\xdc\x45\x32\x8c\x31\xd6\x5f\xc1\xba\x03\xe0\x42\x2e\x05\x86\x3c\
-\x8f\xfa\x27\x00\xdf\x0f\xd4\x18\x01\x5b\x01\x58\x41\x64\x68\xb6\
-\x8a\x27\x02\x75\x7d\x00\x88\x35\x02\x71\xe6\x32\x9c\x2c\x3f\x15\
-\xc8\x61\xc2\x5e\x42\x7c\x1c\xfe\xf7\x17\x4f\xe3\xd1\x7b\xae\xe7\
-\xc9\x9f\x31\x16\xb6\x5c\x32\x70\x52\xa7\xa3\x7f\xfb\xa3\xd3\x2d\
-\x3d\x0e\xa2\x80\x05\x1c\xb0\x04\x20\xe1\x24\x9e\x4b\x8c\x41\xec\
-\x88\x74\xc2\xb0\x34\x42\x4e\x32\x21\x31\x1a\x7e\xdb\x0a\x90\x18\
-\x0d\x5c\x35\x5c\xc5\xc7\x9f\x6e\xf0\xd3\x15\x23\x53\x41\x6e\x16\
-\x16\xfd\xe1\xc7\x98\x5d\x38\x41\xef\x50\x18\x63\x2c\xa0\xca\x1a\
-\x05\x3c\x41\xd6\x01\xb0\x27\x36\x17\xe2\x16\xed\xc3\xbf\x06\xea\
-\xfa\x01\x5b\x9e\xcf\x49\x54\x1e\xca\x49\xbc\x70\xba\x27\xb8\x15\
-\xa0\xcd\x2e\xd0\x6a\x03\x5a\x6d\x02\x66\x27\xd0\xd7\xd2\xbd\xb4\
-\x38\x60\x56\x81\x8a\xb2\xe3\xe5\x28\x2b\x3f\xed\xa7\x88\x23\xcf\
-\x84\xd1\x43\xf1\xc7\x5f\x3e\xc3\x7b\xfb\x19\x63\x61\xaf\xde\x2c\
-\x70\xc6\x14\x3a\x77\xff\x67\x99\xdd\xf8\x37\x00\x7f\x0c\xc4\xb5\
-\x03\x92\x00\x14\xd7\xd3\x74\x90\x7a\x79\x57\x9f\x8b\x36\x00\x39\
-\x49\x84\x9c\x24\x00\x20\x38\x65\xa0\xc1\x22\x50\xdd\x2e\xd0\xe9\
-\xec\xfd\xda\xc3\xd3\x08\x97\xe4\x10\x0c\x02\x78\x67\xd5\x26\xff\
-\x06\x1e\x41\xae\xbc\xf4\x12\x2c\x7c\xe1\x51\xc4\x44\x87\x48\x45\
-\x0c\x63\x8c\xf5\x93\xc5\x05\x94\xd6\x87\xde\xe4\x0f\x00\x26\x07\
-\x32\x97\xee\xa3\xf9\x4f\x5c\x26\x56\xf9\xfb\xda\x01\x49\x00\x04\
-\xa9\x3f\xf5\xf5\xb5\xb1\x46\xef\xa4\x3e\x3c\x8d\xd0\x62\x13\xa8\
-\x6a\x07\x1a\xcd\x02\xe7\xae\xd2\x18\x04\x30\x28\x81\x30\x36\x93\
-\x30\xe8\xeb\x9a\xc8\xa3\x15\xd5\x28\x3a\x74\xd2\xcf\x91\x47\x86\
-\xbb\x6e\x98\x8d\x7f\x79\xe6\x1e\x48\x12\xb7\xf3\x65\x8c\x85\x37\
-\x97\x0c\xec\xa9\x96\xe0\x56\xf4\x8e\xa4\xff\xcc\x1e\xfa\x5f\x00\
-\xc1\x9f\x00\x94\x9c\xa1\x5c\x40\xbd\xb7\x3f\x5f\x9b\x99\x40\xc8\
-\x4c\x00\x14\x22\x98\x9c\x80\xa2\x0a\x44\x49\x84\xa4\x18\xe0\xc2\
-\xd6\xf3\x1f\x7c\xba\xd5\x1f\xe1\x46\x9c\xfb\x6e\xbd\x0a\xcf\x3f\
-\x31\x8f\x8b\xfd\x18\x63\x61\xcf\x25\x03\x3b\xab\x25\xd8\x42\xa4\
-\xf2\xbf\x3b\x1d\x36\x31\xfa\x1f\xfb\x68\xea\x77\x2f\x13\xa5\xfe\
-\xbc\xae\xdf\x13\x00\x61\x54\xbf\x4f\x84\xe8\x81\x5c\xc3\x20\x80\
-\xf4\x38\xa0\xbb\xea\x80\x96\x76\x13\xb6\xed\x3b\x32\x90\x21\x22\
-\xd2\x5d\x37\xcc\xe6\xc9\x9f\x31\x16\x11\xac\x6e\xef\x9d\xbf\xd5\
-\xad\x77\x24\x03\xa7\x02\x20\xc2\x5f\xe0\xe7\xa3\x82\xfd\xba\x06\
-\x5c\x5e\x4e\x31\x44\xf8\xae\x3f\xaf\xd9\x95\x95\x5f\x6c\x87\xac\
-\x84\xf0\x7a\x8e\x0e\xee\xbd\xf5\x4a\xfc\xeb\x77\xef\xe5\xc9\x9f\
-\x31\x16\xf6\x1a\x2d\x02\x5f\x55\x86\xc7\xe4\x7f\x56\xab\x15\x57\
-\xbe\x73\x90\xfc\x5a\xb1\xed\xd7\x04\xc0\x9c\xa0\xdc\x07\x20\xd3\
-\x9f\xd7\xbc\x90\xcb\xed\xc1\x67\x9b\xf6\x04\x72\x88\xb0\x73\xe3\
-\x55\xd3\xf1\xe3\x27\xee\xe6\xc9\x9f\x31\x16\xd6\x64\xc5\x5b\xec\
-\xb7\xf7\x4c\x68\x6d\xf7\xf3\x85\x4b\x81\x24\x7b\xf0\x7b\x7f\x5e\
-\xd3\xaf\x09\x80\x20\x3c\xe7\xcf\xeb\x75\x65\xd3\x8e\x12\x98\x2d\
-\xb6\x40\x0f\x13\x36\x66\x4e\x1b\x87\x5f\xfd\xe0\x01\x9e\xfc\x19\
-\x63\x61\x8b\x00\x9c\x31\x09\x6c\xae\x90\x70\xba\x43\xf4\x79\x6b\
-\x79\xa8\x30\x39\xe8\x31\x7f\x5e\xcf\x6f\x09\x40\x49\x03\x4d\x00\
-\xc4\x1c\x7f\x5d\xaf\x3b\x9f\x6d\xe4\xbb\x7f\x5f\x8d\x1b\x59\x80\
-\xdf\xfd\xcb\xe3\x30\x1a\x42\xe0\xf4\x0b\xc6\x18\xeb\x23\xc2\xd7\
-\xcb\xfd\xa7\x24\x94\xd4\x0a\x38\x82\xfc\xa8\xdf\x81\xea\x74\x88\
-\xd4\xe5\x07\xc9\x6f\x07\x04\xf9\xaf\x08\x90\xd4\x1f\xfa\xed\x5a\
-\xdd\xa8\xaa\x69\xe4\xc6\x3f\x3e\x1a\x9c\x91\x86\x3f\xfe\xea\x69\
-\xc4\xc7\xc6\xe8\x1d\x0a\x63\x8c\xf9\x95\xc3\xf3\x75\x63\x9f\x4e\
-\x01\x93\x0f\xfd\x63\xc2\x05\x01\xe8\xb4\xd1\x6f\x01\xf8\xe5\x00\
-\x1c\xbf\x24\x00\x65\xcd\x94\xe8\xf2\xa8\x0f\xf9\xe3\x5a\x3d\xf9\
-\x7c\x33\xdf\xfd\xfb\x22\x3a\x2a\x0a\xbf\xff\xb7\xc7\x91\x96\x92\
-\xa4\x77\x28\x8c\x31\xe6\x17\x6e\x05\xa8\x35\x09\xd4\x9a\x04\x3a\
-\xec\x7d\xef\x20\x1b\x2e\xda\x1d\x62\xca\x8a\x12\xca\x5c\x50\x28\
-\x5a\x06\x7a\x2d\xbf\x24\x00\x6e\x59\x79\x00\x10\xc9\xfe\xb8\x56\
-\xf7\x63\xc8\x58\xf7\x55\x51\x20\x87\x08\x1b\x3f\x7b\x6e\x01\xc6\
-\x8d\x2c\xd0\x3b\x0c\xc6\x18\xf3\x8b\x8a\x36\x81\xe3\x2d\x02\x32\
-\x6f\xfe\x82\x5b\x81\x30\x7b\xd4\xff\x05\xf0\xd4\x40\xaf\xe5\x97\
-\x1a\x00\x22\x3c\xe9\x8f\xeb\xf4\x64\x4f\xc9\x71\x2e\xfe\xf3\xc1\
-\x7d\xb7\x5e\x85\x9b\xaf\x9e\xa1\x77\x18\x8c\x31\xe6\x17\xe5\xad\
-\x02\x47\x1a\x79\xf2\x3f\x57\xa7\x4b\xea\x57\xb3\xbd\x0b\x0d\x38\
-\x01\x28\xad\xa5\x31\x80\x98\xe9\x8f\x60\x7a\xb2\x69\x3b\xdf\xfd\
-\xf7\x66\xcc\x88\x21\xf8\xfe\xa3\x77\xea\x1d\x06\x63\x8c\xf9\x85\
-\xc5\x05\x1c\x6f\xe6\x1d\x4c\x17\x32\x3b\x91\xfc\xee\x7e\xba\x61\
-\xa0\xd7\x19\x70\x02\x40\x42\x7d\x0a\xfe\x3b\xe5\xb7\x4b\x76\x87\
-\x0b\x3b\x8b\x8f\x06\x72\x88\x90\x17\x17\x1b\x8d\x85\x3f\x79\x04\
-\x51\x46\xae\xf8\x67\xcc\x9f\xa6\x4f\x1a\x8d\x91\x05\xd9\x7a\x87\
-\x11\x91\x0e\x35\x08\x28\x91\xfa\xb0\xbf\x17\x36\x05\xbf\x19\xe8\
-\x35\x06\x54\x03\xb0\x95\xc8\x48\x0d\xea\x23\x81\xae\xc6\xd8\xbe\
-\xef\x30\x5c\xee\x10\x6f\xe6\x1c\x60\x3f\x7a\xfc\x6e\x14\xe4\x66\
-\xe9\x1d\x06\x63\x61\x23\x2b\x23\x15\x3f\x7c\x6c\x1e\xae\xbd\x7c\
-\x8a\xde\xa1\x68\x4a\x55\x09\x36\x87\x03\x09\x71\x71\x90\x24\xfd\
-\xee\xbe\x6b\x4d\x02\x2d\x36\xbe\xfb\xef\x4e\x87\x9d\x2e\x7f\xa3\
-\x88\xa2\x9e\x9d\x21\xfa\x3d\x39\x0e\x28\x01\x48\xae\x53\x6e\x86\
-\x10\x39\x03\xb9\x86\x2f\x36\xef\x3c\x10\xe8\x21\x42\xda\x55\x33\
-\x27\xe1\xce\xeb\x67\xe9\x1d\x06\x63\x61\x21\xda\x68\xc4\xfd\x77\
-\xce\xc5\xa3\xf3\x6f\x40\x5c\xec\x80\x8e\x35\xd1\x9d\x5b\x96\xd1\
-\xd0\xd8\x86\xa6\xb6\x4e\x74\x74\x5a\xd0\xd6\x69\x46\x6b\x87\x19\
-\x1d\x26\x0b\x5a\xdb\x4d\xb0\x3b\xdd\xb0\x5a\x1d\xf0\xc8\x32\x1c\
-\x4e\x37\x1c\x4e\x57\xb7\x6d\xd6\xa3\x8c\x06\xc4\xc6\xc4\x20\x3e\
-\x2e\x06\xa9\x29\x89\x48\x4d\x4e\x40\x4a\x52\x02\x52\x93\x12\x90\
-\x92\x9c\x80\xec\x8c\x74\xe4\xe7\x66\x62\x48\x4e\x26\x92\x13\xe3\
-\x07\x14\xb7\xdd\x0d\x1c\x0c\xd1\xe3\x7b\xb5\xe2\x90\x85\x21\xd1\
-\x80\x1f\x03\xf8\x53\x7f\xaf\x31\xa0\x04\x40\x12\xe2\x91\x40\xaf\
-\xce\xd8\x1d\x2e\x3e\xf6\xb7\x07\x89\xf1\x71\xf8\x97\x67\xfc\x52\
-\x0f\xc2\x58\xc4\x9b\x39\x6d\x1c\x7e\xfc\x44\xe8\xad\xa6\xb5\x77\
-\x98\x51\x7e\xba\x1e\xa7\xeb\x9a\x50\xdb\xd0\x82\xda\xc6\x16\xd4\
-\x36\xb4\xa2\xa9\xb5\x13\xaa\xea\x9f\x9e\xb8\x1e\x59\x81\x47\xb6\
-\xc3\x62\xb3\xa3\xa9\xb5\xa3\xc7\xd7\x26\x27\x25\xa0\x20\x27\x13\
-\xf9\xb9\x99\x18\x9e\x9f\x8d\x89\x63\x86\x62\xdc\xc8\x02\xc4\x44\
-\x47\xf5\x3a\x8e\x53\x06\xf6\xd4\x48\x61\xd7\xca\x37\x10\x3a\x1c\
-\x78\x0e\x7a\x24\x00\x3b\x5a\x28\x89\xdc\xea\xed\xfd\xfd\x7a\x5f\
-\xed\x29\x3d\x0e\xb7\x1c\xe6\xed\x9d\x06\xe0\xb9\x87\x6f\xc7\xa0\
-\xd4\x80\xee\xc0\x64\x2c\xec\xe5\x64\xa5\xe3\x47\x8f\xdf\x8d\xab\
-\x2e\xbb\x44\xef\x50\x7a\xd5\xdc\xda\x89\x23\xe5\xd5\xa8\x38\x5d\
-\x8b\x93\x55\x75\x28\x3f\x55\x87\xb6\x4e\xb3\xde\x61\x9d\xc7\x6c\
-\xb1\xe1\x88\xc5\x86\x23\x27\x4f\x7f\xf3\x31\xa3\xc1\x80\x31\xc3\
-\x87\xe0\x92\xb1\x43\x31\x71\xcc\x70\x4c\x1e\x3f\x1c\x99\xe9\x29\
-\xe7\x7d\x9d\x43\x06\x76\x9e\x96\x60\x75\x69\x1c\x70\x88\xea\x70\
-\x60\xc4\x40\x7a\x02\xf4\x3b\x01\x48\x70\x29\xf3\x49\x88\x81\xad\
-\xf3\xf8\x60\xfb\xbe\x43\x81\x1e\x22\x64\x4d\x18\x3d\x14\x77\x5e\
-\x7f\xb9\xde\x61\x30\x16\xb2\xa2\x8c\x06\xcc\xbb\x71\x0e\x9e\x79\
-\xf0\xd6\xa0\xec\x9a\xa9\xaa\x2a\xaa\xeb\x9a\x71\xf8\x78\x15\x0e\
-\x1e\x3f\x85\xc3\xc7\xab\x50\xdf\xd4\xa6\x77\x58\xfd\x22\x2b\x0a\
-\x8e\x56\x54\xe3\x68\x45\x35\xf0\xcf\x6d\x00\x80\x61\xf9\xd9\x98\
-\x33\x7d\x22\xe6\x4c\x9f\x80\x21\x43\x87\x63\x7f\x8d\x21\xec\xdb\
-\xf9\xfa\x93\x47\x81\xb0\xc8\xea\x7f\x02\xe8\x57\x27\xde\x7e\x27\
-\x00\x24\xa4\x07\x02\xdd\x8b\x49\x56\x14\xec\x2e\x3e\x16\xd0\x31\
-\x42\x95\xd1\x60\xc0\x2f\xbe\x77\xbf\xae\x45\x3a\x8c\x85\xb2\xd9\
-\xd3\x27\xe0\xc7\x4f\xde\x8d\xbc\xc1\x19\x7a\x87\x72\x9e\x86\xe6\
-\x76\xec\x2b\x3d\x8e\x7d\x87\x4e\xa0\xe8\x50\x39\x6c\x76\x87\xde\
-\x21\x05\xcc\xe9\x9a\x46\x9c\xae\x69\xc4\x7b\xab\x37\x23\x39\x25\
-\x0d\xa3\xc6\x4f\xc2\xa8\xb1\x93\x90\x3f\x7c\x34\x1f\x60\xe6\x23\
-\xb3\x4b\x2c\x80\x96\x09\xc0\xc1\x46\xca\x52\x14\xf5\xba\xfe\x7c\
-\x6d\x5f\x1c\x3e\x5e\x05\x6b\x18\xff\xf2\x0f\xc4\x5d\x37\x5e\x8e\
-\x11\x05\x01\xaf\xbf\x64\x2c\xec\xe4\x0d\xce\xc0\x8f\x9f\xbc\x1b\
-\xb3\xa7\x4f\xd0\x3b\x14\x00\xde\x1b\x9d\x03\x47\x2a\xb1\xb3\xf8\
-\x08\xf6\x95\x9e\xc0\x99\xfa\x66\xbd\x43\xd2\x85\xd9\xd4\x81\x92\
-\x3d\xdb\x50\xb2\x67\x1b\x92\x53\xd3\x31\x65\xfa\x6c\x4c\x2a\x9c\
-\x85\xf8\x44\x6e\x69\xde\x93\x4e\xa7\xc8\x7c\xbb\x94\xc6\x3c\x36\
-\x55\xf4\xb9\x58\xae\x5f\x09\x80\xa2\xa8\xf7\xf7\xf7\x6b\xfb\x62\
-\xdf\xc1\x13\x81\x1e\x22\x24\xc5\xc5\xc5\xe0\xf1\x7b\x6f\xd4\x3b\
-\x0c\xc6\x42\x4a\x4c\x74\x14\x1e\x9a\x77\x2d\x1e\xbe\xfb\x3a\x44\
-\x47\xf5\x5e\x8c\x16\x48\x2e\xb7\x07\x45\x87\x4e\x62\x67\x71\x19\
-\xb6\xef\x3b\x82\x0e\x93\x45\xd7\x78\x82\x8d\xb9\xb3\x1d\xdb\x37\
-\xaf\xc1\xae\x2f\xbf\xc0\xa8\x71\x93\x30\x79\xfa\x6c\x0c\x1d\x31\
-\x06\xe0\x55\x81\x8b\x10\x01\x1e\x0f\xfe\x03\xc0\x23\x7d\xfd\xda\
-\x7e\x4e\xe2\x74\x7f\x80\x7b\xff\x00\xe0\x04\xa0\x3b\x8f\xdf\x73\
-\x03\x1f\xf4\xc3\x58\x1f\x5c\x35\x73\x12\x7e\xf4\xd8\x3c\xe4\x64\
-\xa5\xeb\x16\x83\xac\x28\xd8\x57\x7a\x02\x1b\xb6\x15\x61\xfb\xfe\
-\x23\xdc\xdb\xc4\x07\x8a\xa2\xe0\x44\x59\x29\x4e\x94\x95\x22\x3d\
-\x73\x30\x2e\x9d\x7d\x2d\x26\x4e\xb9\x14\x12\x1f\x71\x7e\x1e\xab\
-\x93\x6e\xed\xcf\xd7\xf5\x39\x01\x38\xdc\x44\x83\x3d\xb2\x1a\xf0\
-\xca\x33\x93\xd9\x86\xf2\xaa\xda\x40\x0f\x13\x72\x06\x67\xa4\xe1\
-\xde\x5b\xaf\xd2\x3b\x0c\xc6\x42\x42\x41\x6e\x16\x7e\xf2\xe4\x7c\
-\x5c\x36\x75\xac\x6e\x31\x1c\xad\xa8\xc6\xfa\xaf\x8a\xb0\x79\x67\
-\x29\x3a\xcd\x56\xdd\xe2\x08\x75\xed\x2d\x4d\x58\xff\xe9\xfb\xd8\
-\xfd\xd5\x7a\xcc\xbc\xe2\x7a\x5c\x52\x38\x0b\x06\x4e\x04\x00\x00\
-\x9d\x0e\x91\xfe\x41\x29\x8d\xf9\x4e\x1f\x1f\x03\xf4\x39\x01\xf0\
-\x78\xd4\xf9\x10\xfe\x39\x44\xa8\x27\xfb\x0f\x9f\x84\xaa\x72\x0f\
-\xc8\x0b\x3d\x7e\xef\x8d\x3e\xed\xa5\x65\x2c\x92\xc5\xc5\x46\xe3\
-\xb1\x7b\x6e\xc4\xfd\x77\xcc\xd5\xa5\x3d\xb6\xcd\xee\xc0\xfa\xed\
-\x25\xf8\x6c\xc3\x6e\x54\x54\xd7\x69\x3e\x7e\x38\x33\x77\xb6\x63\
-\xe3\x9a\x15\xd8\xbf\x6b\x0b\xae\xbc\xfe\x0e\x8c\x9d\x30\x25\xe2\
-\x1f\x0d\xa8\x00\x6c\x6e\xf5\x17\x40\xdf\x0e\xe6\xeb\xfb\x23\x00\
-\x21\xee\xd1\xe2\x24\xe6\x92\x23\xe5\x01\x1f\x23\xd4\x64\x0d\x4a\
-\xc5\xcd\x73\x2f\xd5\x3b\x0c\xc6\x82\xda\xb5\x97\x4f\xc1\x0f\x1f\
-\x9f\x87\xac\x41\xa9\x9a\x8f\x7d\xea\x4c\x3d\x3e\x5c\xb3\x0d\x5b\
-\x76\x1d\x80\xc3\xe9\xd6\x7c\xfc\x48\xd2\xd9\xde\x8a\xcf\x57\x2c\
-\x45\xf1\x90\x61\xb8\xf6\xb6\x7b\x90\x9d\x1b\xd9\x47\xa0\x5b\x5c\
-\xa2\xcf\x7d\x79\xfa\x94\x00\xec\xad\xa5\x41\x80\x7a\x75\x5f\x07\
-\xe9\x8f\xd2\xa3\xa7\xb4\x18\x26\xa4\x3c\x70\xd7\x35\x7c\xd8\x0f\
-\x63\xdd\x28\xc8\xcd\xc2\x8f\x9f\xb8\x1b\x33\xa7\x8d\xd3\x7c\xec\
-\x43\xc7\x4e\x61\xd9\xea\xcd\xd8\x5d\x72\x0c\x44\xbc\x72\xa9\xa5\
-\xfa\xda\xd3\x78\xef\x1f\x2f\x62\xfc\xe4\x19\xb8\xf6\xe6\xbb\x11\
-\x1b\x9f\xa0\x77\x48\xba\x30\x39\x45\xe6\x7b\x25\x34\xf4\xa1\x42\
-\x51\xed\xeb\xd7\xf4\x29\x01\x30\x0a\xe5\x4e\x40\x04\xbc\xfa\xbf\
-\xd3\x6c\x45\x4d\x84\x6e\x85\xe9\x4e\x5a\x4a\x12\xee\xb8\x8e\xfb\
-\xfd\x33\x76\xa1\xb8\xb8\x18\x3c\x79\xdf\x4d\xb8\xef\xb6\xab\x60\
-\xd4\xf0\x99\xb0\xaa\x12\xbe\xdc\x7b\x08\x6f\x7d\xb4\x01\xa7\xce\
-\xd4\x6b\x36\x2e\xbb\x18\x11\xe1\xe8\xc1\xfd\xa8\x3e\x75\x02\x37\
-\xde\x71\x3f\x46\x8e\x0d\xfe\x8e\x8e\xfe\xa6\x10\xe0\x54\xf1\x0b\
-\x00\xdf\xf3\xf5\x6b\xfa\x34\x99\x0b\x48\x77\x69\xb1\xfc\x7f\xe8\
-\x58\x15\x67\xd1\x17\xb8\xef\xd6\xab\x10\x1b\x13\xda\x07\x93\x30\
-\xe6\x4f\x42\x08\x5c\x7f\xc5\x34\xfc\xe0\xd1\x3b\x91\x91\x96\xd2\
-\xfb\x17\xf8\x09\x11\x61\xcb\xae\x83\x78\xeb\xe3\xf5\xa8\xaa\x69\
-\xd4\x6c\x5c\xd6\x3b\x9b\xc5\x8c\x4f\x96\x2f\xc2\x25\xd3\x66\xe2\
-\xda\x5b\xe6\x23\x3a\x26\x56\xef\x90\x34\x65\x71\xd2\x9d\x08\x44\
-\x02\x50\x5e\x4e\x31\x16\x04\xbe\xf9\x0f\x00\x1c\x3c\x56\xa9\xc5\
-\x30\x21\x23\x3a\x2a\x0a\x77\xdd\xc0\x2d\x7f\x19\x3b\x6b\x44\x41\
-\x2e\x5e\x78\xea\x6e\x4c\x9b\x38\x4a\xd3\x71\x0f\x1e\xab\xc4\xcb\
-\x6f\x7f\x86\x63\x15\x67\x34\x1d\x97\xf5\xcd\x91\x03\x7b\x51\x53\
-\x55\x8e\x3b\x16\x3c\x81\xec\xbc\xc8\xa9\x0d\x30\x3b\x45\xce\xb2\
-\x3d\x94\xfc\xf0\x2c\xe1\xd3\xe1\x10\x3e\x27\x00\xd6\x58\xe5\x3a\
-\x40\x24\xf6\x3f\x34\xdf\x95\x9d\xf4\xf9\x11\x46\x44\xb8\x66\xf6\
-\x14\xa4\x24\x47\xe6\x73\x2d\xc6\xce\x95\x10\x1f\x87\xa7\xee\xbf\
-\x19\xf7\xdc\x7c\x05\x0c\x86\x80\x6f\x46\xfa\x46\x63\x4b\x3b\x5e\
-\x7e\xfb\x53\x7c\xb9\x87\xcf\x26\x09\x15\xa6\xce\x76\xbc\xbf\xe4\
-\x2f\xb8\xfa\xc6\x79\x28\x9c\x15\x19\x5b\xa7\x65\x15\xc2\x23\xd4\
-\xef\x01\xf8\x7f\xbe\xbc\xde\xe7\x04\x40\x95\xc4\x6d\x5a\x6c\xb4\
-\x50\x14\x15\xe5\xa7\x79\xdb\xcc\xb9\xee\xbe\x69\x8e\xde\x21\x30\
-\xa6\x2b\x21\x04\x6e\xbe\x7a\x06\xbe\xf7\xf0\x1d\x48\x4f\xd5\xae\
-\x09\x96\xdb\xe3\xc1\x7b\xab\xb7\x60\xd9\x27\x9b\xb9\x71\x4f\x08\
-\x52\x14\x05\x5b\xbe\x58\x89\x86\xda\xd3\xb8\xe9\xae\x07\x60\xd4\
-\xb9\x03\xa4\x16\x9c\x24\x2d\x80\xbf\x13\x00\x01\xf4\xab\xd3\x50\
-\x5f\x9d\xaa\x69\xe0\x3f\xb4\x73\x8c\x1e\x96\x87\x4b\xc6\x0c\xd3\
-\x3b\x0c\xc6\x74\x33\x7a\x58\x1e\x5e\x78\x6a\x3e\x26\x8f\x1f\xa1\
-\xe9\xb8\x3b\x8b\xca\xf0\xd7\xa5\x9f\x84\xec\xe9\x7b\xec\x5b\xc7\
-\x0e\x17\xa3\xb3\xa3\x15\xf3\xbe\xf3\x34\x12\x92\xc2\xfb\xf8\x74\
-\xb3\x93\x26\xf9\xfa\x5a\x9f\x12\x80\xe2\x1a\x9a\x0c\xa8\xc3\xfa\
-\x1d\x51\x1f\x9c\xa8\xe4\xee\x7f\xe7\xe2\xbb\x7f\x16\xa9\x92\x12\
-\xe2\xf1\xcc\x03\xb7\x60\xde\x8d\xb3\x21\x49\xda\x2d\xf7\xd7\x34\
-\xb4\xe0\xaf\x4b\x3f\xc1\x9e\x12\x3e\x89\x34\x9c\x34\xd4\x56\xe3\
-\xbd\x45\x2f\x62\xfe\xc3\xcf\x22\x23\x2b\x7c\x0f\x52\xb3\xb9\x45\
-\xd4\x7b\x25\x74\xfd\x43\x85\x62\x53\x6f\xaf\xf5\x6d\x05\x40\x52\
-\x6f\x1e\x70\x54\x3e\x3a\x5e\xc9\xc5\x35\x67\x45\x19\x0d\x98\x7b\
-\xf9\x14\xbd\xc3\x60\x4c\x53\x42\x08\xdc\x78\xd5\x74\xfc\xf0\xd1\
-\x3b\x35\x3d\xf3\xc2\xe9\x72\x63\xf9\xa7\x5b\xb0\xec\x93\x2d\x70\
-\x7b\x78\x15\x32\x1c\x99\x4d\x1d\x78\x7f\xc9\xdf\x30\xff\xe1\xef\
-\x22\x2f\x7f\xb8\xde\xe1\x04\x8c\xd3\x43\xcf\x02\xf0\x4f\x02\x20\
-\x20\x6e\xd0\x62\xfb\x1f\x00\x9c\xac\xe2\xe7\xff\x67\xcd\x9c\x36\
-\x0e\xc9\x89\xf1\x7a\x87\xc1\x98\x66\xc6\x8d\x2c\xc0\x4f\x9f\x99\
-\x8f\x09\xa3\x86\x6a\x3a\xee\x96\x5d\xa5\x78\xf9\x9d\x4f\xd1\xdc\
-\xda\xa9\xe9\xb8\x4c\x7b\x2e\xa7\x1d\x2b\xdf\x79\x0d\x77\x7d\xe7\
-\x29\x0c\x1d\xa9\xdf\x19\x11\x81\x64\xf6\xc0\xa7\xaa\xc7\x5e\x13\
-\x80\xad\x55\x14\x0b\xa8\x9a\xac\x43\x13\x11\xaa\x6a\x79\x5f\xed\
-\x59\xd7\xcd\x29\xd4\x3b\x04\xc6\x34\x91\x9c\x94\x80\xe7\x1e\xbc\
-\x0d\xb7\x5f\x37\x0b\x92\xa4\x5d\x5f\xf7\xaa\x9a\x46\xbc\xb4\x64\
-\x15\xb7\x1e\x8f\x30\x6e\xb7\x0b\xab\xde\xfb\x07\xee\x7e\xf0\x19\
-\x0c\x1b\xa5\x7d\xe7\xc8\x40\x33\x3b\x91\xb5\xa2\x88\x52\x16\xcc\
-\x10\xa6\x9e\x5e\xd7\x6b\x02\x90\x1a\x2d\x5f\x49\x90\xe2\xfc\x17\
-\x5a\xf7\x1a\x5b\x3a\xe0\x70\xb8\xb4\x18\x2a\xe8\xc5\xc5\x46\xe3\
-\x8a\x19\x91\xd7\xcd\x8a\x45\x16\x49\x12\xb8\xe1\xca\xe9\xf8\xd1\
-\x63\x77\x21\x35\x59\x93\x5d\xc6\x00\x00\x87\xd3\x8d\xf7\x3f\xdb\
-\x82\x77\x57\x6d\x82\x47\x56\x34\x1b\x97\x05\x0f\x45\x91\xb1\xfa\
-\x83\xc5\x98\xff\xd0\x77\x51\x30\x7c\x8c\xde\xe1\xf8\x95\xa2\x0a\
-\xd8\x55\x3c\x03\xe0\x4f\x3d\xbd\xae\xd7\x04\x40\x85\x74\x83\x56\
-\xf9\x38\xdf\xfd\x7f\x6b\xce\xf4\x89\x88\x8b\xe5\xce\x7f\x2c\x7c\
-\x4d\x1c\x3d\x0c\x3f\x7d\x7a\x3e\xc6\x8e\xcc\xd7\x6c\x4c\x22\xc2\
-\xfa\x6d\xc5\x78\x75\xd9\xe7\x68\xef\xf0\xa9\x57\x0a\x0b\x63\xb2\
-\xc7\x83\x4f\x96\x2f\xc2\xbd\x8f\x7c\x0f\x79\x05\xda\xee\x32\x09\
-\x34\x87\x4c\xf3\x31\xd0\x04\x40\x00\x37\xf8\x2d\xa2\x5e\x54\x9d\
-\x69\xd0\x6a\xa8\xa0\x37\x87\xef\xfe\x59\x98\x4a\x4e\x8c\xc7\xe3\
-\xf7\xdd\x84\x7b\x6f\xb9\x52\xd3\xe5\xfe\xf2\xd3\x75\x78\x69\xf1\
-\x2a\x1c\x3a\xce\x07\x8d\xb1\x6f\x79\xdc\x6e\x7c\xb2\x7c\x11\x1e\
-\x7c\xfa\x27\x48\xcf\x18\xac\x77\x38\x7e\x63\x97\x45\xaf\xdb\x01\
-\x7b\x4c\x00\xca\x6a\x28\xdd\x05\x75\xb2\xff\x42\xea\x19\xf7\xd5\
-\xf6\x92\x24\x09\x97\x4d\x09\xcf\xe2\x14\x16\xb9\x24\x49\xc2\xdd\
-\x37\xcd\xc6\x33\x0f\xdc\x8a\xc4\x78\x4d\x9e\x2a\x02\x00\xcc\x56\
-\x3b\x16\xbd\xbf\x16\x9f\x6e\xdc\x0d\x55\x55\x35\x1b\x97\x85\x0e\
-\xa7\xc3\x8e\x95\xcb\xde\xc0\x83\x4f\xfd\x24\x6c\xfa\x04\x58\x9c\
-\x48\x5c\x51\x4a\x79\x0b\xa6\x8a\x6e\x2b\xeb\x7b\x4c\x00\xdc\x06\
-\xe5\x2a\x90\xd0\x6c\x03\x6e\x5d\x23\x37\xdc\x00\x80\xf1\x23\x0b\
-\xb8\xf5\x2f\x0b\x2b\x93\xc7\x8f\xc0\x4f\x9f\x9e\x8f\x51\x43\xf3\
-\x34\x1b\x53\x55\x09\x6b\x36\xef\xc1\xeb\xcb\xff\x09\xb3\xc5\xa6\
-\xd9\xb8\x2c\x34\x99\x3a\xda\xf0\xc9\xf2\x45\xb8\xff\xc9\x1f\x21\
-\x2a\x2a\xf4\x1f\xbf\xaa\x00\xec\xb2\xfa\x04\x80\xff\xee\xee\x35\
-\xbd\x3c\x02\x10\x9a\x36\x50\xae\x6b\x6a\xd5\x72\xb8\xa0\x75\xf9\
-\xf4\xf1\x7a\x87\xc0\x98\x5f\xa4\xa7\x25\xe3\xfb\x0f\xdf\x81\x9b\
-\xae\x9a\x0e\x21\xb4\x5b\xee\x3f\x51\x59\x83\x17\x17\xaf\x42\x59\
-\xf9\x69\xcd\xc6\x64\xa1\xaf\xb1\xfe\x0c\xd6\x7f\xf6\x01\x6e\xbf\
-\xe7\x51\xbd\x43\xf1\x0b\x97\x2c\xdd\x8a\xfe\x26\x00\x44\xb8\xda\
-\xef\x11\x75\xc3\xee\x74\xa1\xc3\x64\xd5\x6a\xb8\xa0\x36\x6b\x5a\
-\xf8\x6d\x4b\x61\x91\xc5\x60\x90\x30\xff\xa6\x2b\xf0\xf4\x77\x6e\
-\x46\x82\x96\xcb\xfd\x16\x1b\x96\x7e\xb4\x01\x2b\xd7\x6d\x87\xaa\
-\xf2\x91\xe2\xac\xef\x8e\x1f\x2a\x46\x4e\x6e\x01\xa6\x5f\x3e\x57\
-\xef\x50\x06\xcc\xea\x41\x8f\xc5\x64\xdd\x26\x00\x7b\xda\x28\x19\
-\x4e\x55\xb3\x36\x74\x0d\x4d\x6d\x20\xe2\x3f\xd8\xc4\xf8\x38\x8c\
-\x19\xae\x5d\x55\x34\x63\xfe\x36\x6d\xe2\x48\xbc\xf0\xd4\x3d\x18\
-\x51\xa0\x5d\xbb\x55\x45\x51\xb1\x6a\xfd\x0e\x2c\xf9\x60\x1d\xac\
-\x76\x87\x66\xe3\xb2\xf0\xf4\xd5\x86\x4f\x91\x95\x9b\x8f\xfc\xa1\
-\x23\xf5\x0e\x65\x40\x2c\x4e\x24\xad\xaa\xa0\xac\xf9\xa3\x44\x73\
-\x57\x9f\xef\x36\x01\x88\xb1\x2b\x57\x90\x24\x0c\x81\x0b\xed\x7c\
-\xfc\xfc\xdf\x6b\xd2\xd8\x61\x9a\x56\x46\x33\xe6\x2f\x83\x52\x93\
-\xf1\xbd\x47\xb4\x5f\xee\x2f\x3d\x5a\x89\x97\x96\xac\x42\x65\x75\
-\xbd\x66\x63\xb2\xf0\xa6\xaa\x2a\xfe\xf9\xf1\xdb\x78\xfc\x7b\x3f\
-\x47\x6c\x7c\xe8\xd6\x63\xa9\x04\x98\x4d\xea\xe3\x00\xfe\xaf\xab\
-\xcf\x77\x9b\x00\xa8\x42\xcc\xd1\x72\x1a\x6a\x68\x69\xd7\x70\xb4\
-\xe0\x35\x69\x5c\xf8\xf6\xa7\x66\xe1\xc9\x68\x30\xe0\xee\x9b\xe6\
-\xe0\x99\xef\xdc\x82\xf8\xf8\x58\xcd\xc6\x6d\xed\x30\xe1\xb5\x65\
-\x6b\xb0\x61\x5b\x31\xaf\x1e\x32\xbf\xb3\x9a\x4d\x58\xff\xd9\x07\
-\xb8\xeb\x3b\x4f\xe9\x1d\xca\x80\x38\x3d\xe2\x36\xf4\x35\x01\x80\
-\x24\x2e\x87\x86\x7f\x54\xad\xed\x3d\x76\x2c\x8c\x18\x97\x70\x02\
-\xc0\x42\xc8\x8c\xc9\x63\xf0\xd3\xa7\xe6\xa3\x20\x4f\xbb\xfd\xd3\
-\x1e\x59\xc1\x47\x6b\xbe\xc2\x5b\x1f\x6f\x80\xdd\xc9\x9d\x43\x59\
-\xe0\x94\x1f\x3b\x84\xc3\xc5\xbb\x31\x69\xfa\xe5\x7a\x87\xd2\x6f\
-\x0e\x59\x4c\xec\xee\x73\x5d\x26\x00\x44\x24\x1d\xa8\x57\xa7\x07\
-\x2e\xa4\x8b\x35\xb7\x75\x68\x39\x5c\x50\x32\x18\x24\x8c\xd3\xb0\
-\x2b\x1a\x63\xfd\x95\x99\x9e\x82\x67\x1f\xba\x1d\x37\x5f\x3d\x43\
-\xd3\x71\x4b\x8e\x94\xe3\xa5\x25\xab\xb8\x67\x08\xd3\xcc\x96\x75\
-\xab\x50\x30\x62\x0c\x52\xd2\x06\xe9\x1d\x4a\xbf\xd8\x5c\x48\x5f\
-\x41\x64\x58\x20\xc4\x45\x3d\xaf\xbb\x4c\x00\x4a\x6a\x71\x89\x90\
-\xa0\x69\x37\x84\x96\x36\x8b\x96\xc3\x05\xa5\x51\xc3\xf2\x10\x1f\
-\x1b\xa3\x77\x18\x8c\x75\x2b\xda\x68\xc4\x77\xee\x9a\x8b\x47\xe7\
-\xdf\x80\xd8\x18\xed\xf6\x4a\x37\x34\xb7\xe3\xef\x6f\xaf\xc6\xb6\
-\xbd\x87\x35\x1b\x93\x31\xc0\xdb\x29\x70\xd3\x3f\x3f\xc6\x3d\x0f\
-\x3f\xab\x77\x28\xfd\xe2\x56\x20\xdc\x07\x70\x23\x80\x2f\x2e\xfc\
-\x5c\x97\x09\x80\x64\x50\x66\x12\x69\x53\x01\xd0\xee\x00\xca\x5b\
-\x05\xaa\x1b\x79\x05\x60\xf4\xb0\x5c\xbd\x43\x60\xac\x5b\xd3\x27\
-\x8d\xc6\x4f\x9f\xba\x07\x43\x87\x68\xbb\xdc\xff\xc9\xfa\x9d\x58\
-\xfc\xfe\x5a\x5e\xee\x67\xba\xa9\x2a\x3f\x8a\x63\x87\x8a\x30\x7e\
-\xb2\xb6\x2b\x5e\xfe\xe2\x26\xcc\x83\xaf\x09\x00\x91\x98\x19\xe8\
-\x80\x14\x02\x0e\x35\x08\x54\x77\x08\x80\x08\x56\x0b\x1f\xcc\x31\
-\x22\x5f\xbb\x6d\x53\x8c\xf9\x2a\x77\xf0\x20\xfc\xe8\xf1\x79\xb8\
-\xf2\x52\x6d\xcf\xa7\xd8\xbe\xff\x08\xfe\xfe\xd6\x6a\xd4\x37\xf1\
-\x0e\x21\xa6\xbf\x2d\xeb\x56\x61\xf8\xe8\x09\x88\x8d\x8b\xd7\x3b\
-\x94\x3e\x73\xbb\xd1\x65\x11\x43\xd7\x45\x80\x02\x97\x21\x80\xf5\
-\x7f\xb2\x0a\xec\x3e\x23\xa1\xed\xeb\xee\x9c\x6e\x97\x13\x8a\x22\
-\x07\x6e\xc0\x10\x31\x8a\x57\x00\x58\x10\x89\x32\x1a\x30\xef\xc6\
-\x39\x78\xe6\xc1\x5b\x35\x7d\x34\x55\xdb\xd8\x8a\xbf\xbd\xf9\x09\
-\x76\x95\x1c\xd5\x6c\x4c\xc6\x7a\xe3\xb0\xd9\xb0\x73\xeb\x5a\x5c\
-\x77\xeb\xbd\x7a\x87\xd2\x67\x76\x0f\x75\x79\xd4\xe1\x45\x09\xc0\
-\xd6\x2a\x8a\x05\xd4\x80\xb6\xa2\x3b\x50\x27\xbe\x99\xfc\x01\xc0\
-\xe1\xb0\x07\x72\xb8\x90\x31\x3c\x3f\x5b\xef\x10\x18\x03\xe0\xbd\
-\xeb\x7f\xef\xaf\xbf\x44\xee\x60\xed\x0a\x9f\x1c\x4e\x37\xde\x5e\
-\xb9\x01\x1f\x7e\xfe\x25\x3c\xf2\x45\xf5\x4a\x8c\xe9\xee\xe0\xfe\
-\x9d\x98\x32\x7d\x36\x32\x06\x87\xd6\xcd\x9a\xc5\x2d\x12\x56\x94\
-\x51\xe2\x82\x89\xe2\xbc\x76\xbb\x17\x1d\xf4\x93\x66\xc4\x64\x10\
-\xa2\x02\x15\x48\x9d\x59\xa0\xce\x7c\x7e\x7d\x81\xc3\xc1\x07\x75\
-\xa4\xa5\x24\x21\x2d\x25\x49\xef\x30\x18\x03\x00\x0c\xce\x48\xd3\
-\x74\xf2\xdf\xb2\xab\x14\x0f\xfd\xf8\x0f\x58\xf6\xc9\x66\x9e\xfc\
-\x59\xd0\x52\x55\x15\x9b\xd7\xae\xd2\x3b\x8c\x3e\x53\x54\xc0\xe9\
-\xc2\xed\x17\x7e\xfc\xa2\x15\x00\x45\xa8\xd3\x02\x55\xfe\x27\xab\
-\xc0\x91\xc6\x8b\xaf\xee\xb4\xf3\x0a\x00\xdf\xfd\xb3\x48\x54\x5d\
-\xdb\x84\x17\x97\xac\x44\xf1\xe1\x72\xbd\x43\x61\xcc\x27\x35\xa7\
-\xcb\x51\x79\xb2\x0c\x23\xc7\x74\xbb\xbd\x3e\x28\xb9\x65\xdc\x0c\
-\xe0\x83\x73\x3f\x76\x51\x02\x20\x80\x69\x81\x0a\xe0\x54\x9b\x80\
-\xc3\x73\xf1\xc7\x1d\x76\x3e\x04\x28\x2f\x3b\x34\xf7\x98\x32\xd6\
-\x1f\x0e\xa7\x1b\xef\x7f\xb6\x05\xcb\x56\x6d\x86\x5b\xe6\xfa\x1f\
-\x16\x5a\xb6\x6f\x5a\x83\x11\xa3\x27\x68\xda\xf2\x7a\xa0\x5c\x32\
-\x2e\xea\xed\x73\x71\x11\xa0\x44\xd3\x10\x80\x2d\x80\x8a\x0a\x54\
-\xb6\x77\x7d\x5d\xa7\x83\x0f\xef\xc8\xce\x4c\xd3\x3b\x04\xc6\x34\
-\xb1\x71\x47\x09\x5e\x79\xfb\x33\xb4\x76\x70\xf7\x4f\x16\x9a\x5a\
-\x9b\xea\x71\xa2\xec\x00\xc6\x5d\x52\xa8\x77\x28\x3e\x73\x29\x54\
-\x70\xe1\xc7\xce\x4b\x00\x56\x10\x19\x50\xaf\x4e\x0a\xc4\xe0\xa7\
-\x3b\x04\x5c\xdd\x24\xfa\x6e\xb7\x33\x10\x43\x86\x94\xec\x8c\x74\
-\xbd\x43\x60\x2c\xa0\x4e\x9d\x69\xc0\x4b\x4b\x56\xe2\x40\x59\xa5\
-\xde\xa1\x30\x36\x60\x3b\xb7\xac\xc5\x98\x09\x53\x21\x49\x17\x95\
-\xd2\x05\x25\x87\x5b\x24\x11\x91\x24\x84\x50\xcf\x7e\xec\xbc\x04\
-\x60\x4c\x1d\x46\xaa\x02\x7e\x3f\xbc\x5b\x21\xa0\xa2\xb5\xfb\x55\
-\x05\x8f\xc7\xed\xef\x21\x43\x4e\x76\x16\xaf\x00\xb0\xf0\x74\x76\
-\xb9\xff\xdd\x55\x9b\xb8\xc0\x8f\x85\x8d\x8e\xb6\x16\x9c\x38\x52\
-\x12\x32\xcd\x81\x5c\x0a\xc4\x07\x87\x70\x29\x80\xbd\x67\x3f\x76\
-\x5e\x02\xa0\x48\xca\x44\x11\x80\xe5\xff\x33\x1d\x02\x8e\x1e\x1e\
-\xf3\x79\x5c\xdc\xe1\x2b\x3b\x93\x57\x00\x58\xf8\xd9\x59\x54\x86\
-\x3f\x2f\xfe\x18\xcd\xad\x9d\x7a\x87\xc2\x98\xdf\xed\xdd\xb1\x19\
-\xe3\x27\x4d\x07\x42\xa4\x16\xc0\xa3\xa8\x37\xa0\xbb\x04\x00\xaa\
-\x98\x00\x3f\xff\x77\x38\x65\xe0\x58\x73\xcf\x17\x75\xbb\x23\x3b\
-\x01\x30\x18\x24\x64\xa4\xa5\xe8\x1d\x06\x63\x7e\x53\x59\x5d\x8f\
-\x17\x17\xaf\xc2\xc1\x63\xbc\xdc\xcf\xc2\x57\x6b\x53\x3d\x2a\xcb\
-\x8f\x86\xcc\x8e\x00\x97\x2c\x5d\x76\xee\xbf\x9f\x97\x00\x08\x81\
-\x09\xfe\x1e\xf0\x70\x83\x80\xbb\x97\x55\x3f\x8f\x3b\xb2\x1f\x01\
-\xa4\x24\x25\xc0\x60\x08\x8d\xe7\x48\x8c\xf5\xc4\x6a\x77\x60\xc9\
-\x07\xeb\xb0\x6a\xfd\x0e\x28\x8a\xda\xfb\x17\x30\x16\xe2\xf6\xef\
-\xd8\x14\x32\x09\x80\x47\xa6\xf3\x9a\xfc\x5d\xb8\x0b\xc0\xaf\x09\
-\xc0\xb1\xe6\x8b\x9b\xfe\x74\x19\x54\x84\xd7\x00\x24\x25\x86\x5e\
-\x6f\x69\xc6\xce\x45\x44\xf8\xe2\xcb\xfd\x78\xfd\xbd\x35\x68\xef\
-\xe4\x93\x3d\x59\xe4\xa8\xad\x3e\x85\xc6\xfa\x33\xc8\xce\xbd\xa8\
-\xc8\x3e\xe8\x38\x3c\xe2\xbc\x03\x67\xbe\xb9\xed\x24\x22\x09\xc0\
-\x58\x7f\x0c\xe2\x56\x80\x92\x3a\x81\x13\x2d\xbe\x3d\x4f\x50\xd5\
-\xc8\x2e\x0c\x4a\x4d\x4a\xd4\x3b\x04\xc6\xfa\xad\xfc\x74\x1d\x7e\
-\xf0\x1f\x2f\xe3\x7f\x5e\x79\x9f\x27\x7f\x16\x91\x0e\xec\xdb\xa1\
-\x77\x08\x3e\xb1\xcb\x48\x58\xe8\x9d\xeb\x01\x9c\xb3\x02\x70\xa8\
-\x09\x43\x81\xfe\xef\x00\x90\x55\xa0\xd3\x21\xd0\x68\x01\xce\x74\
-\xf6\xbe\xec\x7f\x2e\x55\x8d\xec\xa5\xc2\xa4\xc4\x58\xbd\x43\x60\
-\x61\x28\xca\x68\xc0\x82\xdb\xe7\x62\xfc\xa8\xc0\xdc\x99\x58\x6c\
-\x76\x2c\x5a\xbe\x16\xab\x37\xee\x8e\xf8\xbf\x61\x16\xd9\x4e\x1c\
-\x2e\xc1\xdc\x1b\xee\x42\x5c\x42\x82\xde\xa1\xf4\xc8\xa3\x40\x8c\
-\x2c\xc2\x0c\x00\xfb\x80\x73\x12\x00\x55\x91\x47\x75\x71\x34\xc0\
-\xf9\x5f\xac\x02\x36\x17\x60\xf3\x08\xd8\xdd\x80\xdd\x0d\xd8\x3c\
-\x80\xcd\x25\x60\x97\x01\xea\xe7\x09\x82\xaa\x12\xd9\x2b\x00\xc9\
-\x89\xc1\xfd\x4b\xc3\x42\xcf\xf4\x49\xa3\xf1\xc2\x93\xf3\x31\x2c\
-\x00\x2d\xa6\x89\x08\xeb\xb7\x15\xe3\x95\x77\x3e\x43\x87\x2f\xeb\
-\x2f\xd6\x00\x00\x20\x00\x49\x44\x41\x54\x89\xef\xf8\x19\x93\x65\
-\x0f\x8e\x1c\xd8\x83\x4b\xaf\xb8\x4e\xef\x50\x7a\x27\xd4\xcb\x71\
-\x61\x02\x40\x24\x8d\xba\x70\x07\x80\x4a\x40\xbb\x5d\xa0\xc5\x06\
-\x34\x5b\x05\x3a\x9d\xfd\x9f\xe4\x7b\x12\xe9\x77\x0f\xc9\x5c\x03\
-\xc0\xfc\x24\x6f\x70\x06\x9e\x7f\x62\x1e\xe6\xcc\x08\x4c\x51\xd2\
-\xf1\xca\x33\x78\x71\xf1\x2a\x1c\x2d\xaf\x0e\xc8\xf5\x19\x0b\x55\
-\x07\x8b\x77\xe1\xd2\x39\xd7\x06\xfd\x96\x40\x19\x98\x7a\xf6\x9f\
-\xbf\x49\x00\x4a\x1b\xc5\x7d\x46\x01\x18\x0d\x80\x5b\x06\x3a\x9d\
-\x02\x26\x87\xb7\x89\x4f\xa0\x45\x7a\x0d\x40\x62\x82\xdf\x7b\x2f\
-\xb1\x08\x13\x17\x1b\x8d\x47\xee\xbe\x1e\xdf\xb9\x73\x2e\xa2\xa3\
-\xfc\x7f\x98\xa7\xd9\x62\xc3\x1b\xcb\xd7\xe2\xf3\xcd\xbb\xa1\xaa\
-\x1a\xbc\x29\x30\x16\x62\x3a\xdb\x5b\x51\x5b\x73\x0a\x43\x0a\x46\
-\xea\x1d\x4a\x8f\x64\x45\x8c\x39\xfb\xcf\xdf\x24\x00\x16\x27\xc6\
-\xb5\xd9\xf5\xc9\x5c\x22\x7d\x05\x20\xda\x78\xf1\x91\x0c\x8c\xf9\
-\xea\xda\xd9\x53\xf1\xc3\x47\xef\x42\x56\x46\xaa\xdf\xaf\xad\xaa\
-\x84\xcf\x37\xed\xc6\x1b\xef\xaf\x85\xd9\xc2\xc7\x76\x33\xd6\x93\
-\xb2\x03\xfb\x82\x3e\x01\x70\x2b\x62\xc8\xd9\x7f\xfe\x66\xe6\x71\
-\xc9\x82\x7b\xd1\xea\xc4\x68\x34\xe8\x1d\x02\x0b\x41\x05\xb9\x59\
-\xf8\xc9\x93\xf3\x71\xd9\x54\xbf\x6c\xde\xb9\xc8\x89\xca\x1a\xbc\
-\xb8\x78\x15\xca\xca\x4f\x07\xe4\xfa\x8c\x85\x9b\x13\x65\xa5\xb8\
-\xf6\xd6\x7b\x10\x15\x15\xad\x77\x28\xdd\x72\xcb\xf8\xe6\xe8\x59\
-\x23\x00\x2c\x24\x92\x9c\x9b\x28\x16\xfe\x6e\x03\xe8\xa3\x50\x3a\
-\x52\x31\x10\x38\x01\x60\x7d\x91\x18\x1f\x87\x27\xef\xbf\x19\xf7\
-\xdc\x7c\x45\x40\x1a\x48\x75\x9a\xad\x78\xfd\xbd\x35\x58\xbb\x75\
-\x1f\x2f\xf7\x33\xd6\x07\x6e\x97\x13\x15\xc7\x0f\x7b\xdb\x03\x07\
-\x29\xa7\x07\xdf\x14\x9d\x19\x01\x20\x7f\x1f\x26\x55\x07\xe0\x0c\
-\x00\x5f\x09\x9d\x12\x8f\x60\x61\xe4\x47\x00\xcc\x07\x42\x08\xdc\
-\x78\xd5\x74\xfc\xe0\x91\x3b\x91\x9e\x9a\xe4\xf7\xeb\xab\x2a\x61\
-\xc3\xf6\x62\xfc\xfd\xed\xd5\x30\x99\x79\xb9\x9f\xb1\xfe\x38\x71\
-\xe4\x40\x50\x27\x00\x6e\x05\x62\x65\x11\xe5\xdc\x33\x43\x34\x78\
-\x67\x1e\x23\x2e\xeb\xe5\x6b\x02\x4a\x48\x91\x9d\x00\x44\xf1\x0a\
-\x00\xeb\xc5\x84\x51\x43\xf1\x93\xa7\xef\xc6\x84\x51\x43\x03\x72\
-\xfd\xc3\x27\xaa\xf0\xd2\xe2\x55\x38\x59\x55\x1b\x90\xeb\x33\xff\
-\xca\x48\x4b\x41\x6e\xf6\x20\x0c\x1e\x94\x8a\x41\x69\xc9\x48\x4b\
-\x49\x44\x52\x42\x3c\x62\xa2\xa3\x10\x15\xfd\x6d\x11\xa8\xd3\xe9\
-\x82\xc5\xe6\x40\x87\xc9\x82\xf6\x4e\x0b\x1a\x9a\xdb\x71\xaa\xa6\
-\x81\x13\xbc\x00\x3a\x5d\x71\x1c\x1e\xb7\x0b\x51\xd1\x31\x7a\x87\
-\xd2\x25\x02\xe0\x10\x98\x05\xe0\x13\x23\x00\x78\x3c\xea\xd4\xde\
-\x7a\x00\x04\x52\xc4\xaf\x00\x18\x38\x01\x60\x5d\x4b\x4b\x49\xc2\
-\x73\x0f\xdd\x86\x5b\xe6\x5e\x06\x29\x00\x89\x72\x7b\xa7\x05\xaf\
-\xbf\xb7\x06\x5f\x7c\xb9\x1f\x14\x88\x3d\xbe\x6c\x40\x24\x49\xc2\
-\xb0\x21\xd9\x98\x30\x3a\x1f\x13\x46\x0d\xc5\xc8\x61\x39\x18\x96\
-\x37\x18\x09\xf1\x03\xdb\x39\x64\x32\xdb\x50\x56\x5e\x8d\x92\x23\
-\xe5\x28\x3d\x7a\x0a\x27\xab\x6a\xf8\x71\x8f\x9f\xc8\xb2\x07\x95\
-\x27\xcb\x30\xee\x92\x42\xbd\x43\xe9\x96\x4c\x98\x8a\xb3\x09\x80\
-\x42\x62\x84\x9e\xc1\x44\xfa\x0a\x80\x24\xf1\x41\x40\xec\x7c\x06\
-\x83\x84\x7b\x6e\xbe\x02\x4f\xde\x7f\x33\x12\x07\xf8\x66\xdf\x15\
-\x45\x51\xb1\x6a\xfd\x0e\x2c\xf9\x60\x1d\xac\x76\x87\xdf\xaf\xcf\
-\xfa\x2f\x6b\x50\x2a\x66\x4e\x1b\x87\x99\x53\xc7\xe3\xd2\xc9\xa3\
-\x07\x3c\xd9\x77\x25\x25\x39\x01\xb3\xa7\x4f\xc0\xec\xe9\xde\xe3\
-\x5f\x5a\xda\x4d\xd8\xba\xab\x14\x9b\x76\x1e\xe0\x1e\x0f\x7e\x70\
-\xb2\xac\x34\xb8\x13\x00\x55\x1d\x03\x7c\x5d\x03\xa0\xa8\x22\x4f\
-\xcf\x60\x22\x7d\x05\x40\x96\x65\xbd\x43\x60\x41\xa4\x70\xe2\x28\
-\xfc\xe4\xa9\xf9\x18\x51\x90\xd3\xfb\x8b\xfb\xe1\xe0\xb1\x4a\xbc\
-\xb4\xf8\x13\x54\x54\xd7\x05\xe4\xfa\xac\x6f\x24\x49\xc2\xd4\xf1\
-\x23\x30\xab\x70\x3c\x66\x4d\x1b\x87\x11\x05\xb9\x9a\xc7\x90\x99\
-\x9e\x82\x05\xb7\x5f\x8d\x05\xb7\x5f\x8d\xf2\xd3\x75\x78\xff\xb3\
-\xad\xd8\xb2\xb3\x14\x72\x84\x77\x69\xed\xaf\xaa\xf2\x63\xf0\x78\
-\xdc\x41\xbb\x1b\x40\x51\xa5\x21\xc0\xd7\x09\x80\xac\x22\x43\xcf\
-\x60\x44\x84\x2f\x81\xf3\x1f\x19\x03\x80\xac\x8c\x54\xfc\xf0\xd1\
-\xbb\x70\xed\xec\xa9\xbd\xbf\xb8\x1f\xda\x3a\xcd\x78\xf5\xdd\xcf\
-\xb1\x61\x5b\x31\x2f\xf7\x07\x81\x61\xf9\xd9\xb8\xf5\xea\x4b\x71\
-\xe3\xd5\xd3\x91\x91\x96\xa2\x77\x38\xdf\x18\x3d\x2c\x0f\xbf\x79\
-\xfe\x61\x3c\xfb\xe0\x6d\x58\xf4\xfe\x5a\xac\xe7\xdf\x97\x3e\xf3\
-\x78\xdc\xa8\x39\x5d\x81\x11\xa3\xfd\x7a\xc0\xae\xdf\xa8\x84\xc1\
-\xc0\xd7\x09\x80\x47\x81\xff\x4b\x8a\xfb\xc0\x68\xf4\x7f\xe7\xb2\
-\x50\x22\xcb\x9c\x00\x44\xb2\xe8\xa8\x28\x7c\xe7\xce\xb9\x78\x74\
-\xfe\xf5\x88\x8d\xf1\xff\x1d\x83\xac\x28\x58\xf9\xc5\x0e\x2c\x59\
-\xb1\x0e\x76\xbb\xd3\xef\xd7\x67\xbe\x4b\x4e\x8c\xc7\xf5\x73\xa6\
-\xe1\xe6\x6b\x2e\x0d\x58\x41\xa7\xbf\x0c\xce\x48\xc3\xaf\x7f\xf4\
-\x10\xe6\xdf\x72\x05\xfe\xb2\xf8\x13\x1c\xad\xe0\x47\x03\x7d\x51\
-\x55\x7e\x34\x68\x13\x00\x97\x42\x69\xc0\xd7\x09\x80\x4a\xd0\xb5\
-\x5c\x31\xd2\xb7\xc1\x79\x38\x01\x88\x58\x73\x66\x4c\xc4\xf3\x4f\
-\xcc\x43\xde\xe0\xc0\x2c\xc2\x95\x94\x55\xe0\xa5\xc5\x2b\x51\x55\
-\xd3\x18\x90\xeb\x33\xdf\x0c\xcb\xcf\xc6\x83\x77\x5e\x83\xeb\xaf\
-\x2c\x0c\xb9\xce\x9f\x13\x46\x0d\xc5\xeb\xff\xf3\x3c\xde\x5e\xb9\
-\x11\x6f\x7d\xbc\x01\x8a\x12\xd9\x9d\x5b\x7d\x55\x55\x7e\x4c\xef\
-\x10\xba\x25\xab\x48\x00\xbe\x5d\x01\xd0\xf5\x37\xd2\x10\xe9\x2b\
-\x00\xfc\x08\x20\xe2\x14\xe4\x66\xe1\xf9\xc7\xe7\x61\x56\xe1\xf8\
-\x80\x5c\xbf\xa5\xdd\x84\x57\xde\xfe\x14\x9b\x76\x1e\x08\xc8\xf5\
-\x99\x6f\xa6\x4d\x1c\x85\x07\xee\xba\x06\x97\x4f\x1b\x1f\xd2\x0d\
-\xcf\x24\x49\xc2\x13\xf7\xdd\x84\x19\x93\xc7\x60\xe1\x4b\xef\xa2\
-\xa9\xb5\x43\xef\x90\x82\x5e\x67\x7b\x2b\xda\x5b\x9b\x91\x9e\x91\
-\xa5\x77\x28\x17\xf1\x28\x22\x1a\x00\x8c\x2b\xca\x28\xba\xa6\x8d\
-\x84\xd9\x25\x02\x72\xd2\x9f\x2f\x8c\x01\x38\xbc\x24\x94\x28\x9c\
-\x00\x44\x8c\xf8\xd8\x18\x3c\x76\xef\x0d\x58\x70\xfb\xdc\x80\xf4\
-\x7f\xf0\xc8\x0a\x3e\x5a\xf3\x15\xde\xfa\x78\x03\xec\xff\x9f\xbd\
-\xfb\x8e\x8e\xb3\x3a\x13\x3f\xfe\xbd\xef\x14\xf5\x51\xb1\x7a\x97\
-\x6d\xc9\xb6\x64\xcb\x96\x2c\xdb\xe0\x8a\x0b\x60\x1b\x07\x5c\x08\
-\x09\x25\x05\x30\x21\x85\x24\x40\x36\xbb\xd9\x24\xfb\x4b\x76\xb3\
-\x9b\xb6\xd9\x40\x08\x29\x24\xb4\x84\x90\x50\x8d\xe9\x60\x70\x01\
-\x8c\x71\x95\x4d\x71\xc5\x4d\xdd\x92\x65\xf5\xae\x99\xf7\xfe\xfe\
-\x10\x26\x36\xc8\xb6\xca\xbc\x65\x34\xf7\x73\x4e\xce\xf1\x91\x67\
-\xee\x7d\x82\x47\x73\x9f\xf7\x96\xe7\x76\x75\xfb\xbd\x7d\xe5\xc2\
-\x84\x10\xcc\x9b\x31\x89\xeb\x97\x2f\x64\xc2\xd8\x4c\xab\xc3\xf1\
-\xab\x49\xe3\x72\xb8\xef\x67\xdf\xe6\xbb\xff\xf3\x67\x3e\x3c\xae\
-\x36\x91\x5e\xc8\xf1\x23\xfb\x6d\x99\x00\xf4\x78\x71\xdc\x27\xa5\
-\xcb\x99\xe7\x21\x61\x6c\x8c\xa4\xa3\x47\x72\xe8\x94\xa0\xac\x41\
-\x60\x76\x1e\x10\xec\x4b\x00\xed\x9d\xea\x8b\x7a\xa4\x13\x42\xb0\
-\x68\x76\x11\x5f\xff\xc2\x95\x24\xc4\x19\xb3\xe1\x6b\xc7\xbb\x87\
-\xb8\xeb\xc1\x35\x94\x57\xd5\x1a\xd2\xbe\x72\x61\x53\x27\xe5\xf2\
-\xf5\x1b\x3e\xc3\xb8\x31\x19\x56\x87\x62\x98\xf8\xd8\x68\x7e\xf7\
-\x93\x6f\xf2\x83\x5f\x3d\xc8\x8e\x77\x0f\x59\x1d\x8e\xad\x55\x1c\
-\xfd\x90\xe2\x19\xf3\xac\x0e\xe3\x53\x7c\x12\x92\x0f\x32\xda\x89\
-\x46\x22\x40\xb8\x1b\xa6\xa4\x48\xd2\x3d\x92\xd2\x6a\x8d\x8e\x1e\
-\xf3\x82\x09\xf6\x25\x80\xb6\xb6\x0e\xab\x43\x50\x0c\x34\x36\x2b\
-\x8d\x3b\x56\xaf\x60\xf2\x04\x63\x6e\x09\x3b\xd9\xd0\xcc\x1f\x1f\
-\x7d\x81\x57\xdf\xd8\x69\x48\xfb\xca\x85\xe5\x64\x24\x73\xe3\x35\
-\x8b\x59\x70\xf1\x64\xab\x43\x31\x45\x78\x58\x08\xbf\xfc\xf7\xaf\
-\xf0\x6f\x3f\xff\x33\xdb\xf7\x1c\xb4\x3a\x1c\xdb\xaa\x28\x3b\x82\
-\x94\xd2\x96\xcb\x3f\xed\x9d\x4c\x70\x22\xbc\x09\xc8\x7f\x16\xa2\
-\x89\x8f\x80\x79\x39\x3a\x5b\xca\x34\x9a\x4d\xda\x30\xec\x76\xdb\
-\xf3\xac\xa4\x59\x5a\x54\x02\x30\x22\x79\x22\xc3\x59\x7d\xed\x52\
-\x96\x5f\x7a\xb1\x21\xc5\x9e\x7a\xbc\x5e\x1e\x7b\x76\x23\x7f\x5d\
-\xf3\x3a\x5d\xdd\x26\x66\xec\xca\xc7\x12\xe3\x63\xf8\xea\x75\xcb\
-\x58\x34\xbb\xd8\x90\x4a\x8d\x76\xe6\x72\x3a\xf8\xaf\xef\xdc\xc8\
-\xea\xef\xff\x81\xca\x0a\x75\x42\xa0\x3f\x5d\x9d\x1d\xd4\xd7\x55\
-\x93\x90\x64\x69\xa9\x9d\x7e\xf5\xf8\xf4\x31\x4e\x9f\x14\xb1\x9f\
-\xfc\xd8\x86\x38\x61\x76\x8e\xce\xdb\xc7\x35\x9a\x4c\x28\x12\xe6\
-\x0e\xf1\x7f\xa5\xab\x40\xd2\xd2\xae\x12\x80\x91\x44\xd3\x04\xcb\
-\x16\x5e\xc4\xad\xd7\x5e\x41\xb4\x27\xc2\x90\x3e\xde\xd9\xbd\x9f\
-\xdf\x3c\xb0\x86\xca\x13\xf5\x86\xb4\xaf\x9c\xdf\xe9\xa3\x9b\x5f\
-\x58\xb1\x88\xb0\xd0\xe0\x7d\x80\x39\xd2\x18\xca\xf2\xeb\x6e\xe5\
-\xd1\x3f\xff\x9a\xc6\x06\xf5\x59\xec\x4f\xc5\xf1\xc3\xb6\x4c\x00\
-\xa4\x24\xc9\x89\x14\x31\xfd\x15\xe2\x73\x69\x70\x71\xa6\xce\x1b\
-\xc7\x8c\x5f\x0e\x08\x0d\x0d\x35\xb6\x03\x9b\x6b\x6d\x53\xa5\x58\
-\x47\x8a\x49\xe3\x72\xb8\xe3\xe6\x95\xe4\x8d\x4e\x37\xa4\xfd\xea\
-\xda\x53\xdc\xf3\xf0\x33\x6c\xde\xb1\xd7\x90\xf6\x95\x0b\x33\xfa\
-\xe8\x66\xa0\xa8\x6a\x16\x1c\x6b\x14\x84\x86\x47\xf0\x99\xcf\xdd\
-\xc4\xdf\xff\x7c\x17\x5e\x6f\xaf\xd5\x61\xd9\x4e\xe5\xf1\x23\xb6\
-\xdc\x07\x20\x25\xf1\x4e\xa1\x11\x7b\xae\x5d\x7f\x21\xce\xbe\x24\
-\xe0\xad\x63\x1a\x3d\x06\x6e\x54\x0f\xf6\x19\x80\x56\xb5\x04\x10\
-\xf0\xe2\x62\x3d\xac\xbe\x66\x31\xcb\x16\x5e\x64\xc8\x54\x70\x77\
-\x4f\x2f\x4f\xbd\xf4\x26\x0f\x3f\xb5\x8e\xce\x2e\x35\xdd\x6f\x85\
-\xf4\xe4\x78\xbe\x75\xe3\x8a\x8f\xeb\xe7\x07\xb3\x2e\x2f\xbc\x5b\
-\xf3\xcf\xcf\x79\x62\x72\x1a\x0b\x96\xae\x64\xdd\x73\x8f\x5b\x18\
-\x95\x3d\x55\x55\x1c\xb3\x3a\x84\x7e\x49\x4d\x8b\x75\x22\x39\xef\
-\x96\xe4\xa8\x10\x98\x91\xd1\xb7\x27\xc0\x67\xd0\xf1\x80\x90\x90\
-\xe0\x9e\x01\x38\xd5\xdc\x6a\x75\x08\xca\x10\x39\x1d\x0e\xae\x5e\
-\x3a\x87\x9b\x3e\x7b\x39\xe1\xe1\xc6\x7c\x8e\x37\xef\xd8\xcb\x3d\
-\x0f\x3f\x43\x75\xed\x29\x43\xda\x57\xce\x2f\x3c\x34\x84\x2f\xae\
-\xba\x94\xcf\x7d\xc6\x98\xa3\x9b\x81\xe8\xc3\x7a\xf1\xa9\x87\xc2\
-\xc2\xa9\x33\x29\x3b\x7a\x88\x83\x1f\xa8\xda\x13\x67\x6a\x6f\x6d\
-\xa1\xb5\xb9\x89\xa8\xe8\x18\xab\x43\x39\x8b\xae\xcb\x18\x27\x82\
-\x98\x0b\x9d\xfb\x1b\x15\x01\x85\x29\x92\xdd\xd5\xc6\x6c\x72\x71\
-\x07\xf9\x12\x40\x67\x67\x37\x6d\x1d\x9d\x86\xdc\xfa\xa6\x18\xc7\
-\xe8\x4b\x7b\xaa\x6a\xeb\xf9\xcd\x03\xcf\xb0\xa5\x74\x9f\x21\xed\
-\x2b\x17\x36\x73\x6a\x3e\x77\xae\x5e\x45\x72\x42\x9c\xd5\xa1\xd8\
-\x4a\x6d\x5b\xff\x63\xc1\xc2\x25\xab\x38\x7e\xf8\x20\xdd\x5d\x6a\
-\x56\xf3\x4c\x35\x55\x65\xb6\x4b\x00\x7c\x92\xe8\x0b\xce\x00\x9c\
-\x96\x15\x2b\x69\xeb\xe9\xcb\xfc\xfc\x2d\x34\x54\x0d\x7c\x35\x75\
-\x0d\xe4\x66\xdb\x6f\xa3\x88\xf2\x69\x29\x89\x71\x7c\xf3\x4b\xcb\
-\x99\x3b\x63\x92\x21\xed\x77\x75\xf7\xf0\xd7\x35\xaf\xf3\xd8\xb3\
-\x1b\xe9\x51\x37\x45\x5a\x62\x4c\x56\x2a\x77\xdc\xbc\x92\x29\xf9\
-\xc6\x1c\xdd\x0c\x74\xfa\x39\x1e\x1a\xc3\x23\xa3\x98\xb3\xf0\x0a\
-\x5e\x7f\xf1\x49\x73\x03\xb2\xb9\x9a\xaa\x32\xf2\xf2\xed\x75\x44\
-\x54\xd7\x45\xa4\x13\x44\x24\x03\x2c\xfd\x93\x9f\x28\xe9\xea\x85\
-\x8a\x66\xff\x26\x01\xa1\xa1\xc6\xec\x94\x0e\x24\x27\x4e\x36\xaa\
-\x04\xc0\xe6\x42\xdc\x2e\xae\x5f\xbe\x80\xeb\x97\x2f\x24\xc4\x6d\
-\x4c\xed\x8a\x4d\x5b\xdf\xe3\xb7\x0f\xaf\x55\xa5\x56\x2d\x12\x15\
-\x11\xce\x2d\xd7\x2e\x61\xf9\x65\x33\x0d\x39\xba\x39\x52\x24\x45\
-\x4a\x8e\x35\xf4\x3f\x0e\x4c\x9e\x36\x8b\xbd\x7b\xb6\x53\x53\xa5\
-\x8e\x06\x9e\x76\xa2\xb2\xdc\xea\x10\x3e\xc5\x27\x09\x73\x02\xe1\
-\x03\x7d\x83\x10\x50\x9c\x26\x71\x68\x70\xbc\xd1\x7f\x49\x40\x68\
-\x78\x38\x9a\xa6\xa1\xeb\xc1\x7b\xc9\xc4\x89\x93\x0d\x56\x87\xa0\
-\x9c\xc7\xbc\x8b\x0a\xb9\xed\x8b\x57\x91\x92\x68\xcc\x54\x70\x79\
-\x55\x2d\x77\x3d\xb8\x46\x55\x56\xb3\x88\x19\x47\x37\x47\x92\xdc\
-\x51\x92\xb2\x06\x41\x7f\xdf\xd8\x42\x08\xe6\x2c\x5a\xc6\x13\x7f\
-\xf9\x9d\xe9\x71\xd9\x55\x6d\x4d\x05\x48\xd9\x37\x88\xda\x84\xd7\
-\x27\x43\x9d\xa0\x87\xd1\xdf\x39\xc0\x73\x10\x02\x26\xa7\x4a\xa2\
-\x42\x60\x7f\x9d\xc0\xeb\x87\x31\x5b\x08\x41\x58\x44\x24\xed\xad\
-\x2d\xc3\x6f\x2c\x40\xd5\x9e\x54\x4f\x7c\x76\x94\x95\x9e\xc4\xed\
-\x37\xae\x64\xda\xe4\x3c\x43\xda\xef\xe8\xea\xe6\xe1\xa7\xd6\xf1\
-\xe4\x0b\x6f\xa8\x5b\x21\x2d\x32\x31\x2f\x9b\x3b\x6e\x5e\x39\xa2\
-\xcb\xf7\xfa\x5b\xb8\x1b\xd2\x63\x24\xe5\x4d\xfd\x8f\x1d\x99\xa3\
-\xf3\x48\xcf\x1c\x43\x65\xf9\x11\x93\x23\xb3\xa7\x9e\xee\x2e\x9a\
-\x9b\x1a\x88\x8e\x1d\x65\x75\x28\x1f\xd3\x25\x6e\x27\x88\x01\xcf\
-\x00\x9c\x26\x80\x31\xa3\x24\xc9\x51\x92\x7d\xb5\x82\x13\xad\x62\
-\xd8\x27\x04\xc2\x83\x3c\x01\x28\xaf\xae\xb3\x3a\x04\xe5\x0c\x61\
-\xa1\x6e\xae\xbd\x72\x3e\x37\xac\x5c\x64\xd8\xf5\xad\x6f\xef\xdc\
-\xcb\xaf\xef\x7f\x5a\x4d\xf7\x5b\xc4\x13\x15\xc1\x97\xaf\xbe\x8c\
-\xab\x97\xcc\x09\xba\x2a\x7e\xfe\x90\x17\x7f\xee\x04\x00\x60\xe6\
-\x82\xc5\x3c\xf1\xb0\x9a\x05\x38\xed\x64\x6d\x8d\xcd\x12\x00\xe1\
-\x1c\xd4\x12\xc0\x27\x45\xb8\x61\x5a\x86\xc4\xeb\x93\x34\x76\x09\
-\x9a\x3a\xa1\xa5\x1b\x5a\xbb\x04\x2d\xdd\xe7\xde\x28\xd2\x9f\xf0\
-\x88\xa8\xa1\x86\x31\x22\x1c\x29\xab\xb1\x3a\x04\x85\xbe\xd9\xa8\
-\xcb\xe7\x4e\xe5\xeb\x37\x7c\x86\xb8\x58\x8f\x21\x7d\x1c\xab\x38\
-\xc1\xaf\xef\x7f\x8a\xdd\x7b\xd5\xd3\x91\x15\xcc\x38\xba\x19\x0c\
-\x22\x43\xc0\x13\x0a\x2d\xe7\x28\x19\x9f\x99\x93\x47\x5a\x46\x8e\
-\x6d\xcf\xc1\x9b\xad\xbe\xb6\x9a\xb1\xe3\x27\x5a\x1d\xc6\xc7\xa4\
-\xc4\xe1\x04\x86\xbd\x05\xdf\xe9\x80\x84\x08\x49\xc2\xc7\x4b\x67\
-\x12\xaf\x0f\x6a\xda\x04\xd5\x2d\x50\xdb\x2a\x2e\x98\x0c\x04\x7b\
-\x02\x50\x5b\xdf\x48\x6b\x7b\x07\x51\x11\x43\xce\xc7\x94\x61\xca\
-\xcb\x49\xe7\xf6\x9b\x57\x50\x38\x7e\xb4\x21\xed\x77\x76\xf5\xf0\
-\x8f\xe7\x36\xf0\xc8\x9a\xd7\xd5\x74\xbf\x45\x8c\x3e\xba\x19\x6c\
-\x22\xdc\x92\x96\xae\x73\xcf\x02\x4c\x9e\x36\x4b\x25\x00\x1f\xa9\
-\xaf\xab\xb6\x3a\x84\xb3\x09\x34\x27\x60\x48\x0a\xec\x74\x40\x46\
-\xb4\x24\x23\x1a\xba\xbd\x92\x8a\x66\x41\x6d\x2b\x34\x74\xf4\xbf\
-\x5c\x10\x11\x11\x69\x44\x18\x01\xe5\x48\x59\x8d\x3a\x76\x64\x81\
-\x68\x4f\x04\xb7\x5e\x7b\x85\x61\x55\xfc\xa4\x94\xac\x7b\x73\x17\
-\xbf\xfb\xdb\xf3\x34\x34\x06\xef\x32\x97\x95\x92\x13\xe2\xb8\xed\
-\x4b\x57\x71\xc9\x45\x85\x56\x87\x32\xa2\xf4\x5e\x20\x8f\xcd\x2b\
-\x98\xc2\x86\x57\x9e\xa1\xab\xa3\xdd\x9c\x80\x6c\xec\x64\x9d\xbd\
-\x66\x79\x75\xbd\x2f\x01\x30\x66\x81\xf3\x0c\x21\x4e\x18\x3b\x4a\
-\x32\x76\x14\xf8\xa4\xa4\xa9\xb3\x6f\x99\xa0\xad\xa7\x6f\xc9\xa0\
-\xad\x5b\x10\xe9\xb1\x57\x91\x04\x2b\xa8\x04\xc0\x5c\x9a\xa6\xb1\
-\xfc\xb2\x99\xac\xfe\xfc\x12\x3c\x91\xc6\xcc\xbc\x1c\x2e\xab\xe2\
-\xd7\xf7\xaf\xe1\xbd\xfd\x47\x0d\x69\x5f\x39\xbf\x10\xb7\x8b\xeb\
-\xae\x5a\xc0\xf5\xcb\x17\x10\x1a\x12\xbc\x97\xf6\x18\xa5\xa3\xe7\
-\xfc\x09\xb3\xd3\xe9\x62\xe2\x94\xe9\xec\xdc\xb2\xd1\xa4\x88\xec\
-\xab\xf1\xd4\x49\x74\x9f\x0f\xcd\x61\x8f\x6a\x92\x52\x22\x9c\x12\
-\x9c\x66\x6e\x7f\x71\x08\x18\x15\x0e\xa3\xc2\xcf\x9c\x06\x90\x68\
-\x27\x63\xd8\xf4\xaa\x89\x81\xd8\xd0\xc1\xa3\x15\x56\x87\x10\x34\
-\x26\x4f\x18\xc3\x1d\xab\x57\x30\x36\xcb\x98\xda\x0b\x6d\x1d\x9d\
-\xfc\xf9\x1f\x2f\xf1\xcc\xab\x5b\x82\xfa\x78\xab\x95\xe6\x4e\x9f\
-\xc8\x6d\x5f\x5a\x4e\x6a\x92\x7d\x36\x5e\x8d\x24\x3d\x3e\xe8\x18\
-\xc0\xdd\x3f\x93\x8a\x2e\x52\x09\x00\xa0\xfb\x7c\x34\x37\x35\x10\
-\x3b\x2a\xc1\xea\x50\x80\xbe\x3d\x7a\x4e\x01\xb6\x48\x47\x52\x12\
-\x07\x54\x90\x70\x44\x7b\x77\x9f\x7a\x4a\x34\x5a\x42\x5c\x34\x5f\
-\xff\xc2\x95\x2c\x9a\x5d\x84\x30\xe0\x4c\xae\x94\x92\x97\x37\xed\
-\xe0\x0f\x7f\x7b\x81\x46\x75\xc7\x83\x25\x32\x53\x13\xb9\xfd\xe6\
-\x95\x4c\x9f\x3c\xce\xea\x50\x46\xb4\xd6\xee\x81\xbd\x6e\x54\x62\
-\x32\x71\x09\x49\x34\x9c\xac\x35\x36\xa0\x00\xd0\xd8\x70\xd2\x36\
-\x09\x00\x20\x9c\xd8\x24\x01\x48\x1c\x15\x6b\x75\x08\x96\xab\xaa\
-\xad\xa7\xae\xbe\x89\xc4\x78\xb5\x1c\xe2\x6f\x4e\x87\x83\x15\x97\
-\xcf\xe2\x96\xcf\x2f\x31\x6c\xe7\xf7\xa1\xa3\x95\xdc\xf5\xc0\x1a\
-\xde\x3f\xa8\x36\x3d\x59\xc1\x8c\xa3\x9b\xca\x3f\xf5\xf8\x06\x9e\
-\x40\xe7\xe5\x4f\x66\xeb\x1b\xeb\x0c\x8c\x26\x30\x34\x35\x9c\xb4\
-\x3a\x84\x8f\xf9\xa4\xb4\x4f\x02\x30\x2a\x26\x0a\xa7\xc3\x81\xd7\
-\x17\xdc\xbb\xa3\xdf\xdd\x7f\x94\x4b\xe7\x14\x5b\x1d\xc6\x88\x32\
-\x75\x52\x2e\x77\xdc\xb4\x92\xec\x8c\x64\x43\xda\x6f\x69\xeb\xe0\
-\xa1\x27\x5e\xe5\xe9\x57\x36\xab\xe9\x7e\x0b\x08\x21\x98\x7f\x51\
-\x21\xb7\x7d\x69\xb9\x4a\x9e\x4d\x14\xe2\x90\x0c\xb4\x88\x5c\xee\
-\xf8\x42\x95\x00\xd0\xb7\x0f\xc0\x2e\xa4\x14\xd8\x26\x01\xd0\x34\
-\x8d\xf8\xb8\xe8\xa0\x2f\x89\xbb\x7b\xdf\x61\x95\x00\xf8\x49\x5a\
-\x52\x3c\xdf\xba\x71\x39\xb3\x4a\x0a\x0c\x69\xdf\xe7\xd3\x59\xbb\
-\x6e\x0b\xf7\x3f\xf6\x32\xad\xed\xea\xf6\x33\x2b\x18\x7d\x74\xb3\
-\xa7\xb7\x97\xc7\x9e\xdb\xc4\x94\x82\x31\x86\xf5\x11\xa8\x62\xc3\
-\x20\xcc\x09\x9d\x03\xb8\xaf\x2a\x29\x25\x1d\x4f\x4c\x1c\x2d\x4d\
-\xc1\xfd\xfd\x6e\xa7\x04\x40\xc7\x84\x13\x00\x83\x91\x9a\x18\x17\
-\xf4\x09\xc0\xce\xf7\xac\xab\x05\xaf\x03\x75\xad\x82\x50\x97\x24\
-\x26\x80\xeb\xa3\x84\x84\x38\xf9\xca\xb5\x4b\xf9\xfc\x95\x97\xe0\
-\x76\x19\x73\x69\xcf\xee\xbd\x87\xb9\xfb\xc1\x67\x38\x52\x66\xb3\
-\xb3\xbd\x41\xc2\x13\x15\xc1\x57\xae\x5d\xca\x95\x8b\x2e\x32\xec\
-\xd2\x9e\xb7\x77\xee\xe5\x9e\x87\xd6\x52\x55\x5b\xcf\x5d\xff\xf1\
-\x35\x43\xfa\x08\x64\x42\x40\x7e\xb2\x64\x57\xe5\x00\x66\x01\x84\
-\x20\x6b\xcc\x38\xde\xdf\xf5\x8e\xf1\x81\xd9\x58\x73\x43\xbd\xd5\
-\x21\x9c\xc5\x09\xf8\xb0\x49\x22\x90\x9e\x9a\x40\xe9\xde\xc3\x56\
-\x87\x61\xa9\xea\xda\x53\x1c\x3a\x56\x49\x5e\x4e\xba\xa9\xfd\x76\
-\xf6\xc2\xd6\x72\x8d\xe6\xae\xbe\x29\xd5\x29\xa9\x92\xac\x98\x61\
-\xd6\x77\xb6\x48\xe1\xf8\xd1\x86\x3d\xad\xd5\x37\x36\xf3\x87\xbf\
-\xbd\xc0\xba\x37\x77\x21\x65\x60\xfe\xf7\x09\x64\x9a\x26\xb8\x74\
-\xce\x54\xbe\xf9\xa5\xab\x88\xf1\x18\x53\x3b\xa4\xf2\x44\x3d\xf7\
-\x3c\xf8\x0c\x5b\x4a\xf7\x19\xd2\xfe\x48\x92\x11\x2d\xa9\x6f\x83\
-\xb2\xf3\x94\x04\x3e\x2d\x33\x7b\x6c\xd0\x27\x00\x2d\xcd\x4d\xb6\
-\xb9\x14\x48\xc3\x66\x09\x40\x46\x8a\x6d\x76\x47\x5a\xea\xcd\x6d\
-\xef\x9b\x9a\x00\x34\x77\xc1\xd6\x32\xed\xe3\xa9\x3c\x29\xe1\x60\
-\x9d\x08\xd8\x04\xc0\x08\x5e\x9f\x8f\x35\xaf\xbc\xcd\x9f\x1f\x7b\
-\x89\xce\xce\x01\x6e\x7f\x56\xfc\x6a\xc2\xd8\x4c\xee\x58\xbd\x92\
-\xfc\xb1\x59\x86\xb4\xdf\xd5\xdd\xc3\xdf\x9f\xdd\xc0\xdf\xd6\xac\
-\xa7\xc7\x3b\x80\x79\x6d\x05\x80\xc9\x69\x92\x2e\x5f\x5f\xc5\xd7\
-\xf3\x49\xcf\x1e\x6b\x52\x44\xf6\xe5\xf3\x79\xe9\xe8\x68\xb3\x45\
-\xe5\x5b\x21\xe4\xc7\x09\x80\x2d\x64\xa4\x24\x5a\x1d\x82\x2d\xbc\
-\xb1\xed\x3d\x56\x7f\x7e\x89\x29\x7d\x55\x34\x09\xde\x3d\x21\xf8\
-\x64\x65\xda\xa8\xd0\xf3\x0f\xfe\x9d\x5e\x68\xec\x10\x44\x87\x4a\
-\x22\x46\x78\x7d\x95\x77\x76\xef\xe7\x9e\x07\x9f\xa1\xa2\xc6\x3e\
-\xeb\x77\xc1\x24\x2e\xd6\xc3\x37\x6e\xf8\x0c\x97\xcd\x9d\x6a\xd8\
-\xd1\xcd\xd7\x37\xef\xe6\xf7\x8f\x3c\xc7\xc9\x86\x66\xbf\xb7\x3f\
-\xd2\x69\xc0\xf4\x0c\xc9\xce\x0a\xa8\x39\x4f\x12\x10\xe5\x89\x21\
-\x3a\x76\x14\xcd\x8d\xa7\xcc\x0b\xce\x86\x5a\x9a\x1b\x6d\x91\x00\
-\x38\x84\x90\x4e\x09\x83\x38\xcc\x61\xac\xf4\x64\x55\xb0\x03\xfa\
-\x2e\x8b\x29\xab\xac\x25\x2b\x3d\xc9\xb0\x3e\xba\xbd\xb0\xa7\x5a\
-\xf4\xfb\x0b\xeb\x76\x40\x61\x72\xff\x09\x80\x94\x70\xb0\x5e\x70\
-\xa8\xae\xef\x2e\x70\x81\x20\x29\x4a\x32\x2e\x41\x12\x3b\xec\x5b\
-\x25\xec\xa5\xaa\xb6\x9e\x7b\x1e\x5a\xcb\xdb\x3b\xf7\x5a\x1d\x4a\
-\x50\x72\x38\x34\xae\x5e\x32\x87\x9b\xaf\x59\x6c\xd8\xd1\xcd\xc3\
-\x65\x55\xdc\x75\xff\x33\xbc\xbb\x5f\x5d\xcc\x34\x1c\x0e\xd1\x97\
-\x04\xbc\x5b\x03\xc7\x1b\xcf\x3d\xa2\xa4\x67\x8d\x09\xfa\x04\xa0\
-\xb5\xb9\x89\xe4\xd4\x4c\xab\xc3\x00\x90\x4e\x01\xb6\x99\xeb\x4a\
-\x4d\x89\x47\xd3\x34\x75\x94\x0a\x78\x7e\xfd\x56\x6e\xfb\xd2\x55\
-\x7e\x6f\x57\x07\x8e\x9d\x12\x1c\x3c\x29\xe8\x39\xc7\xdc\xcf\x94\
-\xd4\xfe\x9f\xea\xa5\x84\xd2\x6a\x41\xc5\x19\xeb\x7d\x12\x38\xd1\
-\x2a\xa8\x6d\x15\x64\x44\x4b\x0a\x92\x25\x21\xb6\x58\x50\x1a\xba\
-\xce\xae\x1e\x1e\x79\xe6\x75\x1e\x7b\x6e\x13\x3d\xbd\x03\x28\x75\
-\xa6\xf8\x5d\x51\xc1\x18\xee\x5c\x7d\x35\x39\x06\x1e\xdd\xbc\xff\
-\xb1\x97\x59\xbb\x4e\x55\x6a\xf4\x17\x21\xfa\xbe\x3b\x22\x43\x60\
-\x6f\xad\xa0\xbf\x2d\x32\x49\xa9\xe9\xec\xdd\xb3\xdd\xfc\xe0\x6c\
-\xa4\xa5\xd9\x1e\xd7\x7f\x6b\xa2\x6f\xed\xdf\x36\x09\x80\xdb\xe9\
-\x24\x3d\x39\x9e\xf2\xea\x3a\xab\x43\xb1\xdc\x4b\x9b\x76\xf0\x95\
-\xeb\x96\xfa\x6d\x17\x7b\x5b\x0f\x54\x35\x0b\x8e\x37\x88\xf3\x1e\
-\xdb\x09\x77\x43\xaa\xe7\xd3\xbf\xb9\xba\x84\xed\x15\x82\x13\xe7\
-\x98\xe2\x93\x40\x79\xb3\xa0\xba\x4d\x30\x3e\x41\x32\x7a\x94\xc4\
-\x98\xbd\xd9\xc6\xda\xf0\xce\xbb\xdc\xfb\x97\xb5\xd4\xd5\x37\x59\
-\x1d\x4a\x50\x4a\x88\x8b\xe6\x1b\x5f\xba\x8a\x45\xb3\x8a\x0c\x69\
-\x5f\xd7\x25\x2f\xac\xdf\xca\x7d\xff\x78\x91\xe6\x16\x75\x41\x8d\
-\x11\xc6\x8e\x92\x44\xb8\x60\x67\x95\xc0\xf7\x89\xdc\x2a\x21\xd1\
-\x98\xd2\xdb\x81\xa4\xb5\xc5\x1e\xdf\x2d\x42\x20\x9d\xc0\x39\x6e\
-\x73\xb6\xc6\xe8\xcc\x64\x95\x00\x00\x2d\xad\xed\x6c\x7a\xe7\x3d\
-\x2e\x9b\x3b\x75\x50\xef\xd3\x81\xf6\x8f\x2e\x58\x6a\xeb\xe9\x2b\
-\xd7\xd9\xd0\xd1\xf7\xe7\x81\xf0\xe9\xe0\x93\x7d\x53\x7a\xa7\x75\
-\xf4\xc2\xf6\x0a\x8d\xa6\xce\x0b\xbf\xdf\xeb\x83\x0f\x4e\xf4\x25\
-\x0a\xd3\xd2\xf5\x80\x99\x0d\x28\xaf\xae\xe3\xee\x07\xd7\xb0\x7d\
-\xcf\x41\xab\x43\x09\x4a\x66\x54\x6a\x3c\x70\xa4\x9c\xbb\xee\x7f\
-\x86\xbd\x1f\x1e\x37\xa4\x7d\xe5\x9f\x52\x3c\x92\xd9\x2e\xc9\xd6\
-\x72\x8d\xee\x33\x1e\x38\x12\x92\x53\xad\x0b\xca\x26\x3a\xda\xed\
-\x51\x22\x5c\xd3\xd0\x9d\xc0\x00\xbe\xd6\xcd\x33\x3a\x33\x85\x4d\
-\x5b\xdf\xb3\x3a\x0c\x5b\x58\xbb\x6e\xcb\x80\x12\x80\x86\x0e\xa8\
-\x6c\x16\xd4\x77\x08\xda\xba\xfa\x92\x80\xa1\xea\xf6\xc2\xd6\x32\
-\x41\x7a\x34\x38\xb4\xbe\xb6\x8f\x37\x0a\xf4\x41\x1e\x08\xa8\x6f\
-\x87\x37\x8e\x69\xcc\xce\xd2\x09\xb7\xf1\x26\xc1\xf6\x8e\x4e\x1e\
-\x78\xfc\x15\x9e\x7e\x65\x33\xbe\x4f\x3e\xae\x28\xa6\x28\x29\xcc\
-\xe3\xce\x9b\x57\x92\x99\x66\xcc\x9e\x97\x86\xc6\x16\x7e\xff\xb7\
-\xe7\x79\x55\x1d\xdd\x34\x55\x6c\x18\xcc\xcd\xd1\xd9\x52\xa6\xd1\
-\xfe\xd1\x03\x48\x68\x58\x38\x91\x9e\x68\xda\x5a\x82\x77\xb3\x65\
-\x47\x9b\x3d\x12\x00\x64\x5f\x02\x60\xab\x12\x66\x63\x32\x55\x86\
-\x78\xda\x7b\x07\x8e\xb2\x7b\xef\x61\x8a\x0a\xfa\x3f\x3e\xd3\xda\
-\x0d\x7b\x6a\x34\x4e\xf9\x79\x26\xf3\x64\xbb\xe0\xa4\x1f\xda\xec\
-\xe8\x81\xb7\x8e\x6b\xcc\xca\xd6\x89\xb4\x59\x12\x20\xa5\xe4\xd5\
-\x37\x77\xf1\xfb\x47\x9e\xa3\xa1\xc9\x26\xbf\x90\x41\x26\x21\x2e\
-\x9a\x5b\xaf\x5f\xc6\xe2\x79\x25\x86\xb4\x7f\xfa\xe8\xe6\x03\x8f\
-\xbf\x42\x7b\x87\xad\x9e\x73\x82\x46\x84\xbb\x2f\x09\x78\xa7\xfc\
-\x9f\x33\x88\x09\x49\x69\x41\x9d\x00\x74\xb6\xb7\x59\x1d\x02\x00\
-\x42\xe0\x73\x82\xec\x18\x68\x3d\x67\x33\x8c\xce\x4a\xb1\x3a\x04\
-\x5b\xb9\xff\xb1\x57\xf8\xdd\x4f\x6e\xfb\xd4\xcf\x4f\xb5\xc3\x3b\
-\x15\xda\xa7\x8e\xef\xd9\x4d\x67\x2f\xbc\x71\x44\x63\x6a\xba\x24\
-\x39\xca\x98\xa7\xaf\x2e\x6f\xdf\xfe\x86\x96\xee\xbe\xe5\x0f\xaf\
-\x14\x68\x02\x22\x5d\x92\x84\x48\x48\x8d\x96\x67\x2d\x69\xec\x3f\
-\x5c\xce\x5d\x0f\xac\x61\xdf\x87\x65\x86\xc4\xa3\x9c\x9f\xdb\xe9\
-\xe4\xf3\x57\x5d\xc2\x17\x56\x5c\x4a\x58\xa8\x31\x99\xe1\x8e\x77\
-\x0f\x71\xf7\x43\x6b\x28\xab\x54\x37\xd0\x59\x2d\xc4\x09\xb3\xb3\
-\x75\xb6\x57\x08\xea\xda\x04\x71\xf1\x89\x1c\xfb\x30\x78\x8b\x2c\
-\x75\xd8\x24\x01\x70\x38\xe8\x75\x82\xd6\xd9\xb7\x85\xcb\x1e\xd2\
-\x92\xe2\x09\x0b\x75\xd3\xd9\x35\xc0\x45\xeb\x11\xee\xdd\xfd\x47\
-\xd8\xf5\xfe\x87\x4c\x9d\x94\xfb\xf1\xcf\xba\xbd\xb0\xbd\xd2\xfe\
-\x83\xff\x69\xbd\x3a\x6c\x2d\x17\xc4\x86\x09\xe2\xc3\x25\x0e\xad\
-\xef\x67\x1d\x3d\xd0\xe9\x15\x74\xf6\xf6\xa5\xa0\x2e\x07\xc4\x84\
-\x49\x32\xa2\x21\x31\x72\x60\x9f\xc9\x63\x8d\x82\xbd\x27\x04\xde\
-\x7e\x66\xef\x1b\x10\x94\x37\xc3\xfe\x3a\x41\x51\x9a\xc4\xe5\x6b\
-\xe5\xbe\xbf\xbf\xc8\x8b\x1b\xb6\xa1\x0f\x76\x4d\x43\xf1\x8b\x8b\
-\x8a\x27\xf0\xed\x1b\x57\x18\x56\xf4\xab\xa6\xae\x81\x7b\xff\xfa\
-\x2c\x6f\xa8\x65\x44\x5b\x71\x6a\x70\x51\xa6\x64\x77\x35\x78\xa2\
-\x83\xfb\xe6\xd7\x8e\xf6\x56\x5b\x54\x03\xd4\xa0\xc7\x76\x4b\x00\
-\x9a\x26\xc8\xcb\xc9\x50\xe7\x72\xcf\x70\xdf\xa3\x2f\xf2\xc7\x9f\
-\x7e\x1b\x4d\xeb\xfb\xc0\x1c\x38\x29\xce\xda\x58\x13\x28\x1a\x3b\
-\xa1\xb1\xf3\xdc\x1f\xfa\x2e\x2f\xb4\x76\x0b\x2a\x9a\x20\x21\x02\
-\x0a\x53\x24\x51\x21\xfd\xbf\xd6\x27\xe1\xbd\x1a\x41\xd9\x79\xce\
-\x1c\x9f\xd6\xd6\xad\x73\xef\xe3\x9b\xd9\xba\xe9\x65\xda\x3b\x6c\
-\xf5\x71\x0f\x1a\xa9\x49\xa3\xf8\xe6\x97\x97\x33\x67\xda\x44\x43\
-\xda\xef\xee\xe9\xe5\xd1\xb5\xeb\x79\x74\xed\x06\xba\x7b\xd4\xd1\
-\x4d\x3b\xd2\x04\x14\xa7\x49\x0e\x66\xc6\xb2\xd1\xea\x60\x2c\xe4\
-\xf3\xf9\xe8\xee\xee\x22\x24\xd4\xda\xc2\x29\x4e\x21\xbb\x9d\x20\
-\xed\x31\x1f\x71\x86\x09\x63\x55\x02\x70\xa6\x7d\x87\xcb\x78\xf6\
-\xb5\x2d\xac\xb8\x7c\x16\x3d\x3e\x28\x1f\x40\xdd\xed\x40\x77\xb2\
-\x5d\xb0\xf1\xb0\x20\x67\x54\x5f\x91\x21\xf7\x19\x77\x56\x36\x75\
-\xc2\xae\x2a\x8d\xd6\x01\x54\xe4\xad\x38\x7e\x98\xf5\x2f\x3d\x4d\
-\x7d\xad\xba\xb4\xc7\x0a\x6e\x97\x8b\x1b\x56\x2c\xe0\xfa\xe5\x0b\
-\x09\x71\x1b\x73\x31\xd3\x9b\xdb\xde\xe7\xb7\x7f\x59\x4b\x4d\x5d\
-\x70\x5f\x24\x16\x08\x04\x50\x32\x36\xb8\x67\x00\x00\x3a\x3b\xda\
-\x2d\x4f\x00\x1c\x1a\x9d\x4e\x04\x4d\x36\x5a\x01\x00\x20\x3f\xd7\
-\x98\x5a\xdf\x81\xec\x0f\x8f\xbe\xc0\x9c\xe9\x13\x69\xf0\xc5\x7c\
-\xea\x6c\xad\xbf\x64\xc4\x48\x0a\x92\x24\x5e\x1d\xb6\x95\x0f\x6c\
-\x80\x35\x92\x0e\x1c\x39\x25\x28\x6b\x12\x24\x46\x48\x5c\x0e\x68\
-\xed\x12\x34\x0e\x60\xd1\xaa\xad\xb5\x99\x37\x5f\x7b\x9e\x7d\xef\
-\xed\xa4\xdf\x8a\x24\x8a\xe1\x66\x4e\xcd\xe7\xf6\x9b\x56\x92\x9a\
-\x64\x4c\x85\xcf\xf2\xea\x3a\xee\x79\x78\x2d\x5b\x4b\xf7\x1b\xd2\
-\xbe\x62\x8c\xe4\x04\x95\x00\x74\x77\x5b\x7f\xfa\x5e\x13\xa2\xc5\
-\x89\x4e\x93\x8d\xf6\x00\x02\x7d\x97\x7e\x28\x67\xeb\xe8\xe8\xe2\
-\x37\x0f\xad\x65\xe6\x15\x37\x1a\xd2\x7e\xa8\x13\x8a\x52\x25\x1f\
-\xad\x32\x50\x90\x24\xd9\x5a\x6e\x8f\x0f\x86\xd7\x07\xd5\x2d\x03\
-\x8b\xc5\xe7\xf3\xb2\x73\xcb\x46\xb6\xbe\xb9\x8e\xde\x1e\xb5\x8f\
-\xc4\x0a\xe9\xc9\xf1\x7c\xeb\xa6\x15\xcc\x2c\xce\x37\xa4\xfd\x8e\
-\x8e\x2e\x1e\x7c\xf2\x55\x9e\x7a\xe9\x2d\xbc\xbe\x00\xd9\x08\xa3\
-\x7c\xcc\x13\x15\x41\x78\x68\x08\x1d\x5d\xc1\x7b\xa9\x56\x4f\xb7\
-\xf5\xa7\x52\x84\x90\x2d\x4e\x29\xb0\xdd\x84\x72\x4a\x62\x1c\x71\
-\x31\x51\xea\x78\xd6\x27\x6c\xdc\xb2\x07\x57\xfc\x3b\x14\x4e\xbd\
-\xd8\xef\x6d\x87\xb9\xf8\x78\xf0\x07\x88\x0e\x95\xd8\xe9\x74\xc8\
-\x40\x94\x1d\x39\xc8\x86\x57\xd6\x70\xaa\xee\x84\xd5\xa1\x04\xa5\
-\x10\xb7\x8b\xeb\x97\x2f\xe0\x86\x15\x0b\xfd\x56\xc1\xf2\x4c\x52\
-\x4a\x36\x6e\x7d\x4f\x55\x6a\x1c\x01\x62\xa2\x23\x83\x3a\x01\xe8\
-\xea\xb2\x7e\x06\xc0\x21\x44\x93\x2d\x97\x00\x00\x26\xe6\x65\xf1\
-\xe6\xf6\x0f\xac\x0e\xc3\x76\x36\xbc\xf4\x34\xc9\x69\x99\x24\x26\
-\xfb\xb7\xa4\x66\x6b\x0f\x78\xf5\xbe\xdd\xba\xc0\x39\xef\x09\xb0\
-\xa3\xa6\x86\x7a\x36\xbe\xbc\x86\x23\x87\xd4\xa5\x3d\x56\x99\x77\
-\x51\x21\xdf\xfc\xd2\x55\x24\x27\xc4\x19\xd2\xfe\xa1\xa3\x95\xdc\
-\xf5\xc0\x1a\xde\x3f\x78\xcc\x90\xf6\x15\x73\x45\x45\x86\x43\x6d\
-\xf0\x5e\x0a\xd4\xd3\x65\xfd\x0c\x00\x9a\xde\xe0\x44\x97\x4d\x56\
-\x1f\x47\xe8\xcf\x94\xfc\xb1\x2a\x01\xe8\x87\xd7\xdb\xcb\xf3\x8f\
-\x3f\xc8\x75\xab\xef\x24\x2c\x22\xc2\x7f\xed\x7e\x54\xc2\xb7\x30\
-\x45\x22\x80\xc3\xa7\xec\xf7\x99\xf8\xa4\xde\x9e\x1e\xb6\x6d\x7e\
-\x8d\x1d\x9b\x37\xe0\xf3\x05\xe0\xb1\x88\x11\x20\x33\x35\x91\xdb\
-\x6f\x5e\xc9\xf4\xc9\xe3\x0c\x69\xbf\xb9\xa5\x9d\xfb\xfe\xf1\x22\
-\x2f\xac\xdf\xaa\x8e\x6e\x8e\x20\x9e\xc8\x11\x76\x75\xe8\x20\x75\
-\xdb\x60\x09\x40\x83\x7a\xa7\x26\x64\xa3\xb4\xe1\x54\xef\x94\x82\
-\x31\x56\x87\x60\x5b\x8d\x0d\xf5\x3c\xfd\xe8\x1f\xf9\xdc\x97\x6f\
-\xc3\xe5\x3e\xc7\x39\xb9\x21\x38\xde\x28\xa8\x6e\xe9\x2b\xa2\xd3\
-\x65\xe3\xf1\x54\x4a\xc9\x81\xf7\x77\xf1\xe6\xeb\xcf\xd3\xda\xac\
-\xa6\x82\xad\x10\x16\xea\xe6\xcb\x57\x5f\xc6\x35\xcb\x2e\xc1\xe5\
-\x74\x5c\xf8\x0d\x83\xa4\xeb\x3a\x6b\x5f\x7b\x87\xfb\xff\xf1\x12\
-\x2d\x6d\xea\xe8\xe6\x48\xe3\x89\x0c\xb7\x3a\x04\x4b\x75\xdb\x62\
-\x09\x40\xab\x75\x22\x9d\x27\x11\xf6\xab\x81\x3e\x36\x2b\x95\xc8\
-\xf0\x30\xda\x54\x09\xcf\x7e\x9d\xa8\x2a\x67\xcd\xa3\x7f\x66\xd5\
-\x17\x6e\xc5\xe9\xf4\xdf\x7a\xab\xdd\xa7\xfe\xcb\x8e\x1c\xe4\x8d\
-\xd7\x9e\xa3\xae\xa6\xd2\xea\x50\x82\xd6\xcc\xa9\xf9\xdc\xb9\x7a\
-\x95\x61\xd3\xfd\xfb\x0e\x97\x71\xf7\xfd\xcf\xb0\xef\xb0\xaa\xd4\
-\x38\x52\x45\x45\x04\x77\x02\xd0\xdb\x6b\xfd\x06\x65\xcd\xc1\x87\
-\x4e\x9f\x97\x5a\xcd\x98\xe3\xb9\xc3\xa2\x69\x1a\x85\x13\x72\xd8\
-\xb2\x2b\x78\x4b\x46\x5e\x48\xc5\xf1\x0f\x59\xf3\xb7\x3f\xb1\xfc\
-\xf3\x37\xe1\xb6\xf8\x4c\xa9\xd1\x4e\x54\x95\xf3\xe6\x6b\xcf\x53\
-\x7e\xec\x90\xd5\xa1\x04\xad\xcc\xd4\x44\x6e\xbf\x69\x25\xd3\xa7\
-\x18\x33\xdd\x7f\xaa\xa9\x85\xdf\x3f\xf2\x3c\xeb\xd4\xa5\x3d\x23\
-\x9e\x27\x2a\xb8\x13\x00\x9f\xd7\xfa\x29\x56\xb7\x8b\x03\xce\xd6\
-\x2c\x4e\x46\x57\xa3\x83\xfd\xae\x6f\x2f\x9e\x98\xab\x12\x80\x0b\
-\x28\x3f\x76\x88\x7f\x3c\xf8\x5b\x56\xdd\x70\x2b\x91\x9e\x68\xab\
-\xc3\xf1\xbb\xea\xca\xe3\xec\xd8\xbc\x9e\x0f\x0f\xbc\xaf\xce\xf3\
-\x5b\x24\x2c\x2c\x84\x9b\xaf\x59\xcc\xd5\x4b\xe7\xe0\x74\xf8\x7f\
-\xba\xbf\xd7\xeb\xe3\x89\x17\x36\xf1\x97\xa7\x5e\x0b\xea\x9d\xe1\
-\xc1\x24\x2c\xc4\x7f\x4b\x97\x81\xc8\xdb\x6b\x6d\xb5\x4a\x87\x00\
-\xf2\x39\xe6\x9c\x2f\x84\xb7\xb4\xca\xd7\x00\xc4\x5b\x1a\x51\x3f\
-\x2e\x9a\x32\x9e\x7b\xff\xf2\xac\xd5\x61\xd8\xde\xc9\xda\x2a\x1e\
-\xbd\xff\xd7\x5c\xb1\xea\x4b\xa4\x67\x8d\xb6\x3a\x9c\x61\x93\x52\
-\x72\xe4\xe0\x5e\x76\x6e\x59\x4f\x65\xd9\x51\xab\xc3\x09\x5a\x42\
-\x08\x2e\x9d\x53\xcc\xd7\xbf\xf0\x19\xe2\x63\x8d\x49\x2e\xb7\x96\
-\xee\xe7\x9e\x87\xd7\x52\x5e\x5d\x67\x48\xfb\x8a\x3d\xb9\x5c\x4e\
-\xab\x43\xb0\x94\xd7\x6b\x6d\x02\x10\xe2\xc4\x77\x8d\x10\xbe\xd3\
-\xff\x0a\x75\xd8\x30\x01\xc8\xce\x48\x26\x39\x21\x8e\x13\x27\x55\
-\x89\xcf\x0b\x69\x6d\x6e\xe2\xf1\x87\x7e\xcb\x45\xf3\x2e\xe3\xe2\
-\x79\x97\xa3\x69\xb6\x9b\xd0\xb9\x20\xaf\xb7\x97\xfd\xef\xee\x64\
-\xc7\x96\x8d\x34\xd4\xab\x5b\xdc\xac\x34\x3a\x33\x95\x3b\x57\xaf\
-\x64\x4a\xbe\x31\x9b\x71\xab\x6a\xeb\xb9\xe7\xa1\xb5\xbc\xbd\x53\
-\x1d\xdd\x0c\x46\x0e\x47\xe0\x7d\x3f\xf9\x93\xd5\x09\x80\xdb\x21\
-\xbb\x01\x9c\x00\x52\x88\x5a\x21\xa5\x31\x65\xbb\x86\xe9\xa2\xa2\
-\xf1\xac\x5d\xb7\xc5\xea\x30\x02\x82\x94\x3a\xef\x6c\x7a\x85\x63\
-\x87\xf6\xb1\x60\xe9\x2a\x52\x33\xb2\xad\x0e\x69\x40\xaa\x2a\x8e\
-\xb1\x6f\xcf\x0e\x0e\x7c\x50\x4a\xb7\x1d\xce\xc7\x06\xb1\x88\xf0\
-\x30\x6e\xfe\xdc\x62\x56\x2d\x9e\x6d\xc8\x97\x74\x57\x77\x0f\x7f\
-\x5d\xf3\x3a\x8f\x3d\xb7\x89\x1e\x8b\xa7\x41\x15\xeb\xb8\x9c\xc1\
-\x3d\x03\xe0\xb3\x38\x01\x70\x39\x44\x1b\x7c\x94\x00\x08\x29\x6b\
-\x2c\x8d\xe6\x3c\x2e\x2a\xca\x57\x09\xc0\x20\x9d\xa8\x2e\xe7\xef\
-\x0f\xdc\xcd\xf8\x49\xc5\xcc\x59\xb8\x8c\xe8\x18\x63\x76\x6b\x0f\
-\x47\x4b\x73\x23\xfb\xf6\xec\x60\xdf\x7b\x3b\x68\xa8\x57\xd3\xbf\
-\x56\x13\x42\xb0\x78\x5e\x09\x5f\xbb\xe1\x33\xc4\xc5\x44\x19\xd2\
-\xc7\x86\x2d\x7b\xb8\xf7\xaf\xcf\xaa\x2a\x7e\x8a\x21\x7b\x49\x02\
-\x89\xb7\xd7\xda\x4d\x80\x0e\x4d\x36\xc1\x47\x09\x00\x50\x61\x61\
-\x2c\xe7\x55\x3c\x69\x2c\x6e\x97\x4b\x3d\x2d\x0c\x96\x94\x1c\x78\
-\x6f\x17\x87\x3e\xd8\x4d\x5e\xfe\x14\x4a\x66\xcd\x27\x39\xd5\xc2\
-\x3b\x16\xa4\xa4\xee\x44\x15\xc7\x0e\xef\xe7\xd8\x87\xfb\xa8\x2a\
-\x3f\xa6\x76\x7a\xdb\x44\x5e\x4e\x3a\x77\xac\x5e\xc9\xa4\x71\x39\
-\x86\xb4\x7f\xb4\xbc\x86\xbb\x1f\x58\x43\xe9\xde\xc3\x86\xb4\xaf\
-\x04\x1e\x23\x6a\x47\x04\x12\x9f\xc5\x77\x58\x38\x34\x4e\xc0\xe9\
-\x25\x00\xa8\xb0\x5f\x29\xa0\x3e\xe1\xa1\x21\xcc\x98\x32\x8e\xb7\
-\x76\xa8\xaa\x80\x43\xa1\xeb\x3a\x07\x3e\x28\xe5\xc0\x07\xa5\x24\
-\xa7\x66\x32\x7e\x52\x31\xe3\x26\x16\x11\xe5\x89\x31\xbc\xef\xee\
-\xae\x0e\x8e\x1f\x3e\xd8\x37\xe8\x1f\xde\x4f\x7b\x6b\x8b\xe1\x7d\
-\x2a\x03\x17\x15\x11\xce\x2d\xd7\x2e\x61\xf9\x65\x33\x0d\xd9\x33\
-\xd2\xd6\xd1\xc9\x83\x8f\xbf\xc2\xd3\xaf\x6c\xc6\x67\xd4\x15\x96\
-\x4a\x40\x72\x06\xf9\x12\x00\xd2\xda\xdf\x07\x0d\x59\x01\xa7\x97\
-\x00\x90\x15\x76\xbe\xf8\x65\xfe\xc5\x53\x54\x02\xe0\x07\x27\xaa\
-\xcb\x39\x51\x5d\xce\x1b\xeb\x9e\x25\x21\x39\x8d\x8c\xec\xb1\x64\
-\x64\x8f\x25\x25\x3d\x8b\x88\x48\xcf\xb0\xda\xee\x6c\x6f\xa7\xbe\
-\xfe\x04\xf5\xb5\xd5\x9c\xa8\x2a\xa7\xa6\xaa\x8c\xc6\xfa\x3a\xf5\
-\x94\x6f\x43\x9a\x26\x58\x3a\x7f\x3a\x5f\xbd\x7e\x19\x31\x9e\x48\
-\xbf\xb7\xaf\xeb\x92\x97\x37\x6d\xe7\x8f\x8f\xbe\x48\x63\xb3\xba\
-\xd0\x4b\xf9\x34\x5d\x0f\xee\x84\x50\x5a\x7c\x01\x8f\xdb\xc1\x41\
-\xf8\x28\x01\xd0\x70\x54\xe8\xd8\xf7\x1f\x64\x56\x49\x3e\x6e\xa7\
-\x93\x1e\x1b\x14\x4f\x18\x09\xa4\x94\xd4\xd5\x54\x52\x57\x53\xc9\
-\xae\x77\x36\x01\x10\x12\x1a\xc6\xa8\x84\x24\x62\xe2\x12\x08\x8f\
-\x88\x24\x22\x32\x8a\x90\xd0\x30\x34\xcd\xf1\x71\xb9\xe1\x9e\xee\
-\x2e\x7a\x7b\xbb\xe9\xed\xee\xa1\xbd\xad\x95\xd6\x96\x46\xda\x5b\
-\x5b\x68\x6c\x3c\x49\x67\x7b\xbb\x85\xff\x8f\x94\x81\x1a\x37\x26\
-\x83\xef\xac\x5e\x45\x7e\x6e\x96\x21\xed\xab\x2a\x7e\xca\x40\xf4\
-\x06\xf9\x77\xb9\xb4\xf8\x5e\x0b\x87\x53\xdb\x0d\x1f\x25\x00\xdd\
-\x92\x72\x97\x7d\x27\x00\x88\x08\x0f\xa3\xa4\x30\x8f\x2d\xa5\xaa\
-\x28\x90\x51\xba\xbb\x3a\xa9\xae\x38\x4e\x75\xc5\x71\xab\x43\x51\
-\x0c\x10\x15\x11\xce\x8d\xd7\x5c\xce\xd5\x4b\x66\x1b\x32\xdd\xdf\
-\xd2\xd6\xc1\x43\x4f\xbc\xca\xd3\xaf\x6c\x0e\xfa\xa7\x3b\xe5\xc2\
-\x7a\x2d\xde\x04\x67\x35\x2b\x67\x00\x84\x80\x70\xc1\x76\xf8\x28\
-\x01\x98\x91\x2e\x4e\x95\x56\xf9\x3a\x00\xdb\xd6\x67\x5c\x38\xab\
-\x48\x25\x00\x8a\x32\x48\x9a\x26\x58\xb6\xf0\x22\xbe\x7a\xdd\x15\
-\x78\xa2\xfc\x77\x7b\xe4\x69\x3e\x9f\xce\xd3\xaf\x6c\xe6\xc1\xc7\
-\x5f\x51\xf7\x76\x28\x03\xe6\x0d\xf2\x3d\x21\x56\xce\x00\x84\xb8\
-\xd0\x57\x4e\x16\x75\xf0\xcf\x53\x00\x48\xc9\x51\x21\x98\x68\x59\
-\x54\x17\x30\x77\x46\x21\x61\xf7\x3f\x45\x67\xa7\x2a\x15\xaa\x28\
-\x03\x91\x37\x3a\x9d\xef\xdc\xb2\x8a\x82\xdc\x6c\x43\xda\xdf\xbd\
-\xf7\x30\x77\x3d\xf0\x0c\x47\xcb\xab\x0d\x69\x5f\x19\xb9\x82\x7e\
-\x09\xc0\xc2\xbd\x51\x61\x0e\x3e\xbe\x5e\xf3\x9f\x5b\x31\x85\x38\
-\x0c\xd2\xb6\x09\x40\x58\xa8\x9b\xf9\x17\x4d\xe6\xa5\x8d\xdb\xad\
-\x0e\x45\x51\x6c\x2d\xc6\x13\xc9\xad\xd7\x5d\xc1\x15\x0b\x66\xa0\
-\x69\xfe\x5f\xdb\xab\xab\x6f\xe2\xde\xbf\x3e\xcb\x86\x2d\x7b\xfc\
-\xde\xb6\x12\x1c\xbc\x5e\x9b\x5f\x3b\x6a\x30\x2b\x97\x00\x5c\x9a\
-\x3c\x75\xfa\xcf\x67\x9c\xc5\x90\xb6\x3f\xa4\xbb\xe4\x92\x69\x2a\
-\x01\x50\x94\x73\xd0\x34\x8d\xe5\x97\x5e\xcc\x2d\xd7\x2d\x35\xe4\
-\xba\xd5\x9e\xde\x5e\x1e\x7b\x6e\x13\x8f\x3c\xf3\x3a\x9d\x5d\xd6\
-\x5f\x67\xaa\x04\xae\x9e\x20\xdf\x03\x60\x65\xa9\x76\x97\xe3\x9f\
-\x75\x7f\x3e\x4e\x00\x84\xe4\x88\x8d\x4f\x02\x02\x30\x25\x7f\x0c\
-\xa9\x49\xa3\xa8\xae\x3d\x75\xe1\x17\x2b\x4a\x10\x99\x34\x2e\x87\
-\x3b\x56\xaf\x24\x2f\x27\xdd\x90\xf6\xdf\xde\xb9\x97\x7b\x1e\x5a\
-\x4b\x55\x6d\xbd\x21\xed\x2b\xc1\x25\xd8\xf7\x8b\x38\x2c\xac\x84\
-\xe8\x72\xc8\x8f\xef\x54\xff\x67\x02\x20\xf4\x23\xd2\x7e\x37\x02\
-\x9f\x45\x08\xc1\x95\x8b\x2e\xe6\x8f\x8f\xbe\x60\x75\x28\x8a\x62\
-\x0b\x9e\xa8\x08\xbe\x7c\xf5\x65\x5c\xbd\x64\x8e\x21\xd3\xfd\x95\
-\x27\xea\xb9\xe7\xc1\x67\xd4\x06\x5c\xc5\xaf\x9a\x5b\x83\xfb\xd8\
-\xb0\x10\xd6\x25\x00\x4e\xc9\xbb\x1f\xff\xf9\xf4\x1f\x34\x87\xf3\
-\x70\x20\x54\xeb\x5a\xb6\x70\x06\x0f\x3e\xf1\xaa\x2a\x0d\xac\x04\
-\x35\x87\x43\x63\xe5\xe5\xb3\xb9\xf9\xf3\x8b\x89\x0c\x0f\xf3\x7b\
-\xfb\x5d\xdd\x3d\x3c\xf4\xe4\x3a\x9e\x78\x61\x13\xbd\x41\xbe\x5e\
-\xab\xf8\x5f\x6b\x5b\xc7\x85\x5f\x34\x82\x99\x3d\x03\x20\x04\xc4\
-\x86\x41\x72\x94\x24\x42\x68\xeb\x4e\xff\xfc\xe3\x04\xa0\x30\x89\
-\xb2\xdd\xd5\x74\x02\xfe\xff\x36\xf1\xa3\x18\x4f\x24\x0b\x66\x4e\
-\xe6\x95\x37\x76\x5a\x1d\x8a\xa2\x58\x62\xf2\x84\x31\xdc\xb9\x7a\
-\x25\x63\xb2\x52\x0d\xeb\x63\xff\xe1\x72\x1e\x5d\xbb\xde\xb0\xf6\
-\x95\xe0\xd6\xdc\x12\xdc\x09\x80\x66\x42\x02\x20\x04\x24\x44\x48\
-\xd2\x3c\x90\xe2\x91\xb8\xfb\xba\x6c\x2b\x4a\x15\x07\x4f\xbf\xe6\
-\x8c\x25\x00\xa1\xef\xaa\xf2\x1d\x10\x50\x89\xce\xf1\xc3\x00\x00\
-\x20\x00\x49\x44\x41\x54\x64\x78\x64\xc3\xb4\x72\xf1\x6c\x95\x00\
-\x28\x41\x27\x32\xd2\xc3\x67\xae\xba\x92\x2b\x2e\x99\x4a\x46\xb4\
-\xd5\xd1\x28\xca\xd0\xb5\xb6\x07\x77\x02\xe0\x30\x70\x13\x60\x84\
-\x0b\xb2\xe3\x24\x19\x31\x92\xd0\x4f\x5e\xb9\x20\xe4\x3e\x21\xc4\
-\xc7\x47\x10\x3e\xf9\xd7\xfb\x08\x80\x04\x20\x3f\x37\x8b\x49\xe3\
-\x72\x78\xff\xe0\x31\xab\x43\x51\x14\xc3\x69\x9a\x46\xf1\x8c\xb9\
-\xcc\x9c\xbf\x04\x77\x48\x28\x7b\xaa\x61\xef\x09\x18\x3d\x4a\x32\
-\x36\x5e\xe2\xb2\xf7\xd6\x1d\x45\xf9\x94\x96\x20\x5f\x02\x70\x7e\
-\x54\x5e\xdd\x5f\x34\x20\xd9\x23\xc9\x8e\xed\x7b\xea\x17\xe7\xd8\
-\x0e\x24\xa5\x38\x6b\x33\xcf\x59\x09\x80\x26\xd8\x17\x28\x77\xb7\
-\x5c\xbf\x7c\x21\xdf\xfb\xc5\xfd\x56\x87\xa1\x28\x86\xca\xc8\x19\
-\xcb\xc2\xa5\x57\x13\x9f\x98\x72\xd6\xcf\x7b\x75\x38\x78\x52\x70\
-\xac\x41\x30\x21\x49\x92\x1d\x2b\xed\x7e\x88\x47\x51\x00\xf0\xfa\
-\x7c\x41\x9f\x00\xb8\x5c\x6e\xbf\xb4\xe3\x74\x40\x4e\x8c\x64\x4c\
-\x7c\x3f\x4f\xfb\xfd\xd0\xfa\x1e\xf2\xff\xf9\xfe\xb3\xff\x5a\xee\
-\xb3\xf3\xad\x80\x67\x9a\x55\x92\xcf\xe8\xcc\x14\x8e\x96\xd7\x58\
-\x1d\x8a\xa2\xf8\x5d\x44\xa4\x87\x79\x97\x5e\x49\xfe\xe4\x12\xce\
-\x99\xce\x03\x3d\x3e\x78\xb7\x5a\x50\xd1\x24\x28\x4a\xd5\x89\xf2\
-\xef\x83\x85\xa2\xf8\x5d\x5d\x7d\x53\xd0\xdf\x17\xe1\x0e\x19\xde\
-\x2f\xaa\xd3\x01\x39\xb1\x92\xdc\xf8\x8f\xd7\xf6\x07\x46\xca\xb3\
-\x12\x80\xb3\x26\x0f\x85\xee\x08\x98\xb3\x3e\x42\x08\xae\xbb\x6a\
-\x81\xd5\x61\x28\x8a\x5f\x69\x0e\x07\xc5\x33\xe6\x71\xf3\xb7\x7e\
-\x40\xfe\x94\x69\xe7\x1d\xfc\xcf\xd4\xd0\x01\x9b\x8e\x6a\x1c\x39\
-\x25\x2c\xbe\x68\x54\x51\xce\xef\x44\x7d\xa3\xd5\x21\x58\x2e\x35\
-\xd6\x4d\x8a\x47\x12\xe6\x1a\xdc\xfb\xc2\x9c\x50\x90\x2c\x59\x9c\
-\xa7\x53\x90\x34\xc8\xc1\x1f\xd0\x9c\x8e\x73\xcf\x00\x1c\x4a\xe3\
-\xc8\xd8\x6a\x6c\x7d\x29\xd0\x99\x16\xcd\x2e\xe2\xe1\x27\x5f\xa5\
-\xf2\x84\x2a\x4e\xa2\x04\xbe\xcc\xd1\x79\x2c\x5a\x7a\x35\x71\x09\
-\x49\x43\x7a\xbf\x4f\x87\xf7\x4f\x08\xea\xdb\xa1\x28\x6d\xf0\x5f\
-\x0e\x8a\x62\x86\x13\x75\x0d\x56\x87\x60\xb9\xf4\x38\x37\x33\x32\
-\x24\x20\x69\xe9\x82\x13\x6d\x82\xba\x56\x68\xec\x14\xf8\x3e\x91\
-\xc1\x6b\x40\x42\xa4\x24\x3b\x0e\x92\x23\xcf\xbd\xbe\x3f\x00\x6d\
-\x85\x49\x9c\x75\x4f\xf7\x59\x09\xc0\x35\x42\xf8\x76\x57\x7b\xdf\
-\x97\x52\xcc\x18\x72\x17\x26\x72\x3a\x1c\xdc\xfc\xb9\x25\xfc\xe7\
-\x6f\x1e\xb1\x3a\x14\x45\x19\xb2\xa8\xe8\x18\x2e\xb9\x7c\x05\xe3\
-\x0a\xa6\xf8\xa5\xbd\x9a\x56\x41\xcb\x51\xc1\xb4\x74\x9d\x18\x5b\
-\x1f\xea\x55\x82\xd1\x89\x93\x6a\x06\x20\x2c\xf4\x9f\x4b\x00\x9e\
-\x50\xf0\x84\x4a\xf2\xe2\xc1\xa7\x4b\x9a\xbb\xa0\xb5\x47\x80\x84\
-\x70\x37\xc4\x86\x49\x9c\x7e\xd8\xe8\x2b\x90\x7b\x84\x10\x67\xad\
-\xbd\x7c\xaa\x59\x89\xd8\x3d\xfc\xae\xcc\xb3\x70\x56\x11\x63\xb3\
-\xd2\xac\x0e\x43\x51\x06\xed\xf4\x74\xff\x4d\xb7\x7d\xdf\x6f\x83\
-\xff\x69\xed\x3d\xf0\xd6\x31\x8d\xa3\x0d\x81\xb1\xa7\x47\x09\x1e\
-\x27\x4e\xaa\x19\x80\xe8\x73\x5c\xcd\xed\xd0\x20\x2e\x1c\xb2\x62\
-\x24\x59\xb1\x92\x84\x08\xff\x0c\xfe\xd0\xff\xd8\xde\x4f\x02\x40\
-\x40\x25\x00\x9a\x26\x58\xfd\xf9\x25\x56\x87\xa1\x28\x83\x92\x3d\
-\x76\x3c\x5f\xfe\xfa\xf7\x58\xb0\x74\x25\x2e\x3f\x1f\x09\x3a\xcd\
-\x27\xe1\xbd\x1a\xc1\xae\x2a\x81\x37\xb8\xf7\x5c\x29\x36\x52\xa3\
-\x96\x00\xce\x99\x00\x18\x4b\x7e\xea\xfa\xce\x4f\x1d\x1c\x10\x68\
-\xa5\x10\x58\xdf\x16\xb3\xa7\x15\x50\x54\x30\x86\xdd\x7b\x8f\x58\
-\x1d\x8a\xa2\x9c\x57\xa4\x27\x9a\xb9\x0b\x3f\xd3\xb7\xc1\xcf\x24\
-\x15\x4d\x82\xa6\xce\xbe\x25\x01\x4f\xa8\x69\xdd\x2a\x4a\xbf\x8e\
-\xa8\x93\x5b\x44\x7b\xcc\xdf\x66\xa7\xe1\xb8\xf0\x0c\x40\x54\x3b\
-\xef\x23\x08\xb8\x42\xfb\xb7\xdf\xb4\xd2\xd2\x2b\x16\x15\xe5\x7c\
-\x9c\x4e\x17\x17\xcd\xbd\x9c\x9b\xbf\xf5\x43\x53\x07\xff\xa6\xc6\
-\x53\xb4\x34\x37\xd2\xda\xdd\xb7\x24\xd0\x14\xdc\x97\xb0\x29\x16\
-\xab\x6f\x6c\xa6\x25\xc8\x2f\x02\x02\xf0\x44\x9a\x3e\x03\xd0\x13\
-\xd1\xc1\xa7\x4e\xf9\x7d\x6a\xc4\xcc\xcd\x15\xdd\x48\xf6\x9b\x13\
-\x93\xff\x8c\xc9\x4a\x65\xf9\x65\x33\xad\x0e\x43\x51\x3e\x25\x6b\
-\xcc\x38\xbe\xf8\xb5\xef\x32\x7b\xe1\x52\xbf\x15\x00\xb9\x10\x6f\
-\x6f\x2f\x5b\x36\xbd\xcc\xc3\xf7\xfe\x8c\xe6\x86\xbe\xeb\xb3\x7b\
-\x75\xd8\x52\xa6\xd1\xd1\x63\x4a\x08\x8a\xf2\x29\x47\xcb\x4f\x58\
-\x1d\x82\x2d\x58\xb0\x04\xb0\x2f\x37\x57\x74\x7f\xf2\x87\xfd\xd6\
-\x0e\x92\xb0\x4d\x40\xa1\xf1\x31\xf9\xd7\xea\xcf\x2f\xe1\xf5\xb7\
-\x77\xab\x0c\x53\xb1\x85\xd8\xb8\x78\xe6\x2f\x59\xc9\xe8\xbc\x02\
-\x53\xfb\x3d\x7c\xe0\x7d\x36\xbe\xf2\x0c\xcd\x8d\xa7\x3e\xf5\x77\
-\x3d\x3e\x78\xef\x84\xe0\xa2\x4c\x55\x2d\x40\x31\x9f\x2a\xdc\x06\
-\xe1\xa1\x21\x84\xb8\x07\x59\x00\x60\xb8\x04\x5b\xfb\xfb\x71\xbf\
-\x09\x80\x40\x6e\x03\x71\x8b\xb1\x11\xf9\x9f\x27\x32\x9c\x6f\x7f\
-\x79\x39\x3f\xf9\xed\xa3\x56\x87\xa2\x04\x31\x97\xcb\xcd\xb4\xd9\
-\x0b\x98\x3e\x7b\x11\x4e\xa7\x79\xbf\xe8\x8d\x0d\xf5\x6c\x7c\x79\
-\x0d\x47\x0f\xed\x3d\xef\xeb\x6a\x5b\x05\x3d\x3e\x55\x27\x40\x31\
-\x9f\x4a\x00\x20\x3e\xce\xfc\x9b\xbc\x84\x2e\xb7\xf5\xf7\xf3\x7e\
-\x13\x00\x87\xc3\xb1\xcd\xe7\x0b\xac\x8d\x80\xa7\x5d\x3e\xaf\x84\
-\x75\x9b\x77\xb1\x6d\xf7\x01\xab\x43\x51\x82\xd0\xb8\xfc\x29\xcc\
-\x5b\xbc\x1c\x4f\x74\xac\x69\x7d\xf6\xf6\xf6\xb0\xf5\xcd\x75\xec\
-\x7c\x7b\x23\x3e\x9f\xf7\x82\xaf\x97\x40\x53\xa7\x20\x31\x52\xcd\
-\x02\x28\xe6\xfa\xf0\x58\x95\xd5\x21\x58\xce\x8a\x04\xc0\xa7\x39\
-\x06\x3e\x03\x50\x98\xc4\xbe\xdd\x35\x34\x23\x09\xc8\x4b\x47\xff\
-\xf5\xd6\x6b\xb8\xe1\x8e\x5f\xd0\xd9\xf9\xa9\x25\x0f\x45\x31\x44\
-\x5c\x7c\x22\xf3\x17\xaf\x24\x27\x77\x82\xa9\xfd\x1e\x39\xf8\x01\
-\x1b\x5e\x7a\x9a\xe6\xa6\xc1\x1d\xad\xea\x08\xb8\x6d\xbe\x4a\xa0\
-\xeb\xe8\xe8\x52\x27\x00\x80\x84\x51\xa6\x0f\xab\x4d\x53\x53\x38\
-\xd4\xdf\x5f\xf4\xbf\x04\x20\x84\x5e\x5a\xa5\xef\x04\xb9\xd0\xd8\
-\xb8\x8c\x91\x14\x1f\xcb\x37\xbf\x78\x15\xbf\xbc\xef\x09\xab\x43\
-\x51\x46\x38\x97\x3b\x84\x59\xf3\x97\x50\x3c\x63\x2e\x9a\xc3\xbc\
-\x39\xf5\x86\xfa\x5a\xd6\xbf\xf4\x34\x65\x47\x0e\x0e\xe9\xfd\x5d\
-\x17\x9e\x28\x50\x14\xbf\xda\xfb\x61\x79\xd0\x5f\x02\x04\x90\x60\
-\xfa\x0c\x80\xd8\xfa\xc9\x0a\x80\xa7\x9d\xf3\x02\x41\x21\xe4\x56\
-\x29\x09\xc8\x04\x00\xe0\xca\x4b\x2f\x66\xe7\xfb\x87\xd8\xb0\xe5\
-\x53\xb5\x0f\x14\xc5\x2f\xc6\xe4\x15\xb0\x68\xd9\x35\x44\x45\xc7\
-\x98\xd6\x67\x6f\x6f\x0f\x3b\xde\x5e\xcf\xb6\x37\x5f\x1f\xd0\x74\
-\xff\xb9\x74\xaa\x19\x00\xc5\x64\xef\x1d\x38\x6a\x75\x08\xb6\x90\
-\x38\xca\xbc\xef\x0b\x00\x49\xff\xeb\xff\x70\x9e\x04\x40\x0a\xf9\
-\x36\x32\xb0\xcb\x88\x7e\xf7\xd6\xcf\xb2\xf7\x50\x19\xb5\xea\xf6\
-\x29\xc5\x8f\x12\x92\xd2\x58\xb8\x6c\x15\xe9\x99\x63\x4c\xed\xf7\
-\xc8\xc1\x0f\x58\xff\xe2\x53\xb4\x34\x0f\xff\xf3\xdc\xd0\x21\x40\
-\xdd\x1b\xa8\x98\x48\x25\x00\x7d\x92\xe2\xcd\xdb\x1f\x04\x20\xd0\
-\x37\x9f\xeb\xef\xce\x99\x00\x74\x38\x1d\x9b\xc3\x7b\x74\xef\xf9\
-\x5e\x63\x77\x51\x11\xe1\xfc\xf0\x9b\xd7\x71\xfb\x7f\xfd\x81\x40\
-\xdd\xd4\xa8\xd8\x47\x48\x68\x38\xb3\x17\x2c\x65\xca\xf4\x59\x08\
-\x61\x5e\xd1\xa9\xfa\xba\x1a\xd6\xbf\xf4\x14\x15\xc7\x0e\xfb\xad\
-\xcd\xb6\x6e\x68\xee\x84\x68\x75\x59\x90\x62\x02\x5d\xd7\xd9\x77\
-\xb8\xdc\xea\x30\x6c\x21\x2d\x69\x94\x79\x9d\x09\x7a\x1d\x9a\xf3\
-\x9d\x73\xfd\xf5\x39\xbf\xc5\x66\x27\x88\xd6\xfe\x6a\x07\x07\x9a\
-\xa2\x82\xb1\x7c\xfd\x0b\x57\x5a\x1d\x86\x12\xc8\x84\x20\x7f\xf2\
-\x34\x6e\xfe\xd6\xf7\x29\x9a\x31\xc7\xb4\xc1\xbf\xb7\xa7\x87\x2d\
-\x9b\x5e\xe6\x91\x3f\xfe\xaf\x5f\x07\x7f\xe8\x7b\xf6\xdf\x5b\x27\
-\xd4\x1c\x80\x62\x8a\xbd\x1f\x96\xa9\x4d\xd9\x80\x10\x82\x14\x33\
-\x13\x00\x29\x77\x4e\x4e\x16\xe7\x2c\x8c\x73\xfe\xa7\x7b\x21\xde\
-\x44\x52\xe2\xf7\xa0\x4c\xf6\xb9\x65\xf3\x38\x52\x56\xcd\x4b\x1b\
-\xb7\x5b\x1d\x8a\x12\x60\x92\x52\xd2\x59\x78\xc5\x67\x49\xcd\xc8\
-\x36\xb5\xdf\x23\x07\x3f\xe0\xf5\x17\x9f\xa4\xb5\xb9\xc9\xb0\x3e\
-\xea\xda\x04\x47\x4e\xc1\xd8\x51\x2a\x0d\x50\x8c\xb5\xb5\x54\x1d\
-\xcb\x86\xbe\x13\x00\xe6\x16\x01\x12\x6f\x9e\xef\x6f\xcf\x9b\x00\
-\x08\x29\xdf\x94\x88\x3b\xfd\x1b\x90\x35\xee\x5c\xbd\x8a\x23\xe5\
-\x35\x1c\x3c\x52\x61\x75\x28\x4a\x00\x08\x0b\x8f\x64\xee\xa2\x65\
-\x4c\x2c\xbe\x08\x21\xcc\xdb\x0b\x73\xb2\xb6\x8a\xf5\x2f\x3e\x45\
-\x65\x99\x39\xeb\xa5\x7b\x6b\x05\x9e\x10\x54\x4d\x00\xc5\x50\x5b\
-\xf7\x04\x5c\x75\x79\x43\x98\x3a\xfd\x0f\x08\x5d\x0e\x3d\x01\x70\
-\xeb\x8e\xb7\xba\x35\x5d\xe7\x3c\x4b\x05\x81\x22\x34\xc4\xcd\xff\
-\x7e\x6f\x35\xb7\xfe\xe0\x37\xea\x3a\x4a\xe5\x9c\x84\xd0\x98\x32\
-\x6d\x16\xb3\x17\x2e\x25\x24\xd4\xbc\x1b\xbb\xba\xbb\x3a\xd9\xbc\
-\xe1\x45\xf6\x6c\x7f\x1b\x29\xcd\xdb\xaf\x22\x25\xec\xa8\x10\xcc\
-\x1d\x2d\x89\x32\xe6\x56\x62\x25\xc8\x35\x34\xb5\x72\xe8\x68\xa5\
-\xd5\x61\xd8\x42\x7a\x4a\x82\x99\xdd\xf9\xf0\x3a\xb6\x9c\xef\x05\
-\xe7\x1d\xd8\x0b\x32\x44\x83\x84\x77\xfd\x1b\x93\x75\xe2\x62\x3d\
-\xfc\xea\xfb\x5f\xc1\x13\x69\xfe\x55\x8c\x8a\xfd\x25\xa7\x65\x72\
-\xdd\x2d\xb7\xb3\xf0\x8a\xab\xcd\x1b\xfc\xa5\x64\xdf\x9e\x1d\x3c\
-\xf0\xdb\xff\x61\xf7\xb6\xb7\x4c\x1d\xfc\x4f\xeb\xd5\xe1\x9d\x72\
-\x0d\xaf\xcf\xf4\xae\x95\x20\xb0\x75\xf7\x7e\xa4\x54\x33\x4c\x00\
-\x39\xe9\xc9\xe6\x75\x26\xe4\xae\xa2\x1c\x71\xde\x35\xc4\x0b\xee\
-\xf0\xd7\xe0\x35\x09\x45\xfe\x8b\xca\x5a\x59\xe9\x49\xfc\xfc\x7b\
-\x37\xf3\x9d\xff\xbe\x8f\xce\x2e\x75\x2d\x9a\x02\x11\x91\x1e\xe6\
-\x5d\x76\x25\xf9\x85\x25\x60\xe2\x74\x7f\x6d\x75\x05\xeb\x5f\x7c\
-\x8a\xea\xca\xe3\xa6\xf5\x79\x2e\x1d\x3d\x7d\x9b\x02\x27\xa7\xa8\
-\x2f\x6a\xc5\xbf\xb6\xec\x3a\xff\xdd\x14\xc1\x24\x27\x23\xc5\xb4\
-\xbe\x04\xe2\xb5\x0b\xbd\xe6\x82\x53\xfb\x12\xfd\x82\x8d\x04\x9a\
-\xc2\xf1\xa3\xf9\xbf\x1f\xdc\x4a\x58\xa8\x39\x57\xb3\x2a\xf6\xa4\
-\x69\x1a\xc5\x33\xe6\x71\xf3\xb7\x7e\x40\xfe\xe4\x69\xa6\x0d\xfe\
-\xdd\x5d\x1d\x6c\x78\x69\x0d\x7f\xfb\xd3\xaf\x6d\x31\xf8\x9f\x76\
-\xbc\x51\xd0\xad\x2a\x04\x2a\x7e\xd4\xd1\xd1\xc5\x3b\xa5\x6a\xfd\
-\xff\xb4\xd1\x59\xe6\xcd\x00\x48\xa1\x5d\x70\xec\xbe\xe0\x0c\x40\
-\x73\x8f\x73\x73\xb4\x5b\xef\x04\x46\xd4\x89\xe1\xc2\x09\xa3\xf9\
-\x9f\x7f\xb9\x89\xef\xfd\xfc\x7e\x7a\xbc\xea\x5b\x2f\xd8\x64\x8e\
-\xce\x63\xe1\x92\x55\x8c\x4a\x34\xf1\x17\x52\x4a\xde\x2f\x7d\x87\
-\x37\x5f\x7f\x81\xae\x0e\xfb\x5d\x59\x2d\x25\x54\x36\x0b\xc6\xa8\
-\x53\x01\x8a\x9f\xbc\xb9\xe3\x03\xba\x7b\x54\xd9\x49\xe8\xab\x4b\
-\x33\x2a\xc6\x63\x56\x77\xed\x51\x6d\xfd\x5f\x01\x7c\xa6\x0b\x26\
-\x00\xf3\x73\x44\x57\x69\x95\xbe\x19\xe4\xa5\xfe\x89\xcb\x3e\xa6\
-\x4f\x19\xc7\x2f\xbf\x7f\x0b\xdf\xff\xe5\x83\x74\x74\xa9\x33\xaa\
-\xc1\x20\x2a\x3a\x86\x4b\x2e\x5f\xc1\xb8\x82\x29\xa6\xf6\x5b\x53\
-\x55\xc6\xfa\x17\x9e\xe2\x44\xb5\xbd\x8b\xa1\x34\x75\x59\x1d\x81\
-\x32\x92\xbc\xb6\x79\x97\xd5\x21\xd8\x46\x4e\xa6\x79\x0f\x1b\x02\
-\xb1\x31\x37\x57\x5c\x70\x50\x1b\x50\x95\x3f\x81\x7c\x4d\xc2\x88\
-\x4b\x00\x00\x4a\x0a\xf3\xf8\xcd\x8f\xbf\xce\xbf\xfc\xf4\x4f\x34\
-\xb7\xd8\xef\xa9\x4c\xf1\x0f\xcd\xe1\x60\x4a\xc9\x6c\xe6\x2c\xba\
-\x02\x97\xdb\xbc\xed\xee\x5d\x9d\x1d\x6c\xd9\xf8\x0a\xbb\xb7\x5b\
-\xb3\xc1\x6f\xb0\xbc\xf6\x0f\x51\x09\x10\x4d\x2d\x6d\xec\x7c\xaf\
-\xdf\x4b\xe8\x82\xd2\xf8\xd1\xe9\xa6\xf5\x25\x85\x1c\xd0\xd2\xfd\
-\x80\x12\x00\xcd\xa1\xbd\xec\xf3\xe9\xbf\x1c\x5e\x48\xf6\x35\x61\
-\x6c\x26\xbf\xfd\xf1\x37\xf8\xee\x4f\xff\xac\xee\x0d\x18\x81\x46\
-\xe7\xe6\x33\x7f\xc9\x4a\x62\x47\x99\x77\x04\x47\x4a\x9d\x3d\x3b\
-\xde\x66\xf3\xfa\x97\xe8\xee\xea\x30\xad\xdf\xe1\x0a\x31\xef\x42\
-\x43\x65\x84\x5b\xff\xf6\x1e\x55\x82\xfd\x0c\xe3\x46\x67\x98\xd6\
-\x97\x43\x6a\xaf\x0e\xe4\x75\x03\x4a\x00\x26\x27\x8b\x0f\x4a\xab\
-\x7c\xc7\x80\x9c\x61\x45\x65\x63\xa3\x33\x53\x78\xf0\x97\xdf\xe1\
-\xfb\xff\xfb\x10\xef\xee\x3f\x62\x75\x38\x8a\x1f\x44\x45\xc7\x30\
-\x67\xc1\x32\xf2\xa7\x4c\x33\xb5\xdf\x13\xd5\xe5\xac\x7f\xe1\x29\
-\x6a\xaa\xca\x4c\xed\xd7\x1f\xd4\xdd\x00\x8a\xbf\x3c\xfb\xda\x79\
-\x8f\xa0\x07\x9d\x09\x63\x4d\x4a\x00\x04\x47\x26\xa7\x8a\x01\xdd\
-\x13\x3e\x98\x8b\x7e\x5e\x04\x6e\x1b\x5a\x44\x81\x21\xda\x13\xc1\
-\xaf\xff\xe3\x56\x7e\xfe\xfb\xc7\x78\x6d\x73\xa9\xd5\xe1\x28\x43\
-\xe4\x72\xb9\x99\x3e\x67\x11\xd3\x66\x2d\xc0\xe9\x34\xaf\xec\x66\
-\x47\x5b\x2b\x6f\xbc\xf6\x1c\x7b\xdf\xdd\xd1\xb7\xa3\x2e\x00\xa9\
-\xeb\xda\x15\x7f\xd8\xb3\xef\x08\x47\xcb\x6b\xac\x0e\xc3\x36\xc2\
-\xc2\x42\x48\x4f\x49\x34\xab\xbb\x67\x07\xfa\xc2\x01\x27\x00\x52\
-\xe8\x2f\x0a\xa9\x8d\xe8\x04\x00\x20\xc4\xed\xe2\x47\xb7\x7f\x81\
-\x89\xe3\x72\xf8\xdd\x5f\x9e\x55\x27\x04\x02\x4c\x5e\xfe\x64\x2e\
-\xb9\x7c\x39\x9e\x98\x38\xd3\xfa\xd4\x75\x9d\xdd\xdb\xdf\xe2\xed\
-\x8d\x2f\xd3\xd3\xd5\x69\x5a\xbf\x46\xd8\x57\x27\xf0\x84\x5a\x1d\
-\x85\x12\xe8\x9e\x5d\xa7\x9e\xfe\xcf\x34\x2e\x27\x1d\x4d\x33\xe7\
-\x98\xb1\xd4\xf5\x17\x07\xfa\xda\x01\x27\x00\x9e\x76\xe7\xc6\x96\
-\x70\xbd\x55\x40\xd4\xd0\xc2\x0a\x2c\xab\x96\xcc\x26\x3f\x37\x93\
-\x1f\xdd\xf5\x57\xaa\x6b\x4f\x59\x1d\x8e\x72\x01\x29\xe9\x59\x5c\
-\x72\xf9\x72\xd2\x32\x47\x9b\xda\x6f\xc5\xf1\xc3\xac\x7f\xe9\x69\
-\xea\x6b\xab\x4d\xed\xd7\x28\x3e\x1d\xb6\x94\x09\xb4\x66\xf3\x0a\
-\x22\x29\x23\x4b\x63\x73\x2b\x9b\xb6\x8e\x98\x02\xb2\x7e\x51\x90\
-\x97\x6d\x56\x57\x2d\xa1\xcd\xce\xcd\x03\x7d\xf1\x80\x13\x80\xdc\
-\x5c\xd1\xbd\xbb\x4a\x7f\x5d\x22\x57\x0c\x2d\xae\xc0\x33\x61\x6c\
-\x26\x0f\xff\xea\xbb\xdc\xf3\xf0\x33\xbc\xb0\x7e\x9b\xd5\xe1\x28\
-\xfd\x48\x48\x4a\xe3\xe2\x4b\x2e\x27\x6f\x42\xa1\xa9\x55\xfc\xda\
-\x5a\x9b\xd9\xb4\xee\x59\x0e\xbc\x5f\x1a\xb0\xd3\xfd\xe7\x22\x25\
-\x1c\x6f\x50\x09\x80\x32\x34\x6b\xd7\x6d\xa1\x57\xd5\x95\x3e\xcb\
-\xe4\x09\xe6\x3c\x98\x08\x78\xa5\xa0\x40\x0c\xb8\xc4\xed\x60\xf6\
-\x00\x80\xd4\x9f\x43\x88\xa0\x49\x00\x00\xc2\xc3\x42\xf8\xde\xd7\
-\x3e\xcf\xdc\x19\x85\xfc\xe2\xf7\x8f\x73\xaa\xa9\xc5\xea\x90\x14\
-\x20\x31\x25\x9d\x8b\xe7\x5d\x4e\xee\xf8\x49\xa6\x0e\xfc\xba\xcf\
-\x47\xe9\xd6\x37\x78\x7b\xd3\x2b\xf4\xf6\xa8\xda\x11\x8a\x72\xa6\
-\x8e\xae\x6e\x9e\x7c\xe9\x2d\xab\xc3\xb0\x15\x4d\x13\x4c\x1a\x9f\
-\x6d\x4a\x5f\xba\x90\xcf\x0f\xe6\xf5\x83\x4b\x00\x7a\x1d\x6b\x71\
-\xeb\xf7\x01\x41\x57\x43\x77\x66\x71\x3e\x8f\xdd\xfb\x03\x1e\x7e\
-\x6a\x1d\xff\x78\x6e\x23\xba\xda\x2d\x65\x3e\x21\xc8\x1a\x9d\x47\
-\xf1\x8c\xb9\x8c\xc9\x2b\x30\x75\xe0\x07\xa8\x38\x76\x98\xf5\x2f\
-\x3d\x45\x7d\x9d\xda\xdc\xa4\x28\xfd\x59\xfb\xea\xdb\xb4\xb4\xaa\
-\x7a\x2a\x67\xca\xc9\x48\x21\x2a\xc2\x94\xcb\xc5\x7a\x5c\xbd\x8e\
-\x01\xaf\xff\xc3\x20\x13\x80\xa2\x1c\xd1\x54\x5a\xa5\xbf\x31\x12\
-\xab\x02\x0e\x44\x58\xa8\x9b\xaf\xdd\xb0\x8c\x84\xd1\x45\x3c\xf9\
-\xe4\xd3\x54\x55\x1c\xb3\x3a\xa4\xa0\xe0\x0e\x09\x25\x7f\x72\x09\
-\xc5\x33\xe6\x12\x17\x9f\x64\x7a\xff\xad\x2d\x4d\x6c\x7a\xe5\x19\
-\x0e\xee\xdd\x63\x7a\xdf\x8a\x12\x28\xba\x7b\x7a\x79\xec\x85\x37\
-\xac\x0e\xc3\x76\x26\x8f\x37\xe7\xf4\xbc\x14\xe2\xb5\xc2\x2c\x31\
-\xa8\x42\x36\x83\x9b\x01\x00\xa4\x94\x4f\x0b\x31\x32\xab\x02\x0e\
-\x44\x63\x27\x38\xa3\x33\xb8\xf6\xe6\x6f\x73\x68\xff\x7b\xbc\xf5\
-\xfa\xf3\x34\x9e\x3a\x69\x75\x58\x23\x52\x72\x6a\x26\x93\x4b\x66\
-\x32\x6e\x52\x31\x6e\x13\xab\xf7\x9d\xe6\xf3\xf9\xd8\xb5\x65\x23\
-\xef\xbc\xf9\x2a\xbd\x3d\xea\xe6\x48\x45\x39\x9f\x17\x37\x6c\xa3\
-\xa1\x51\x2d\x91\x7e\x52\xd1\xa4\x5c\x73\x3a\x92\xfa\xd3\x83\x7d\
-\xcb\xa0\x13\x00\xb7\x4b\x5b\xdb\xeb\xd5\x7f\x07\x04\x65\xcd\xb0\
-\x43\xf5\x1f\x4d\x3b\x0b\x41\x5e\xfe\x64\xc6\x8e\x9b\xc8\x07\x7b\
-\xb6\xb3\xed\xad\xd7\x68\x6e\x54\xa7\x05\x86\x2b\x22\xd2\xc3\xf8\
-\x49\xc5\x14\x4c\x9e\x46\x62\x8a\x79\xa5\x33\x3f\xe9\xf8\xe1\x03\
-\x6c\x78\xf9\x69\x1a\xea\xeb\x2c\x8b\x41\x51\x02\x45\x67\x57\x0f\
-\x7f\x79\x6a\xc4\x5d\x1c\x3b\x6c\x9a\xa6\x51\x62\x4e\x02\xe0\x95\
-\xc2\x31\xa8\xf5\x7f\x18\x42\x02\x30\x29\x49\xd4\x96\x56\x79\xdf\
-\x06\x31\x77\xb0\xef\x0d\x74\xad\xdd\x70\xa2\xf5\xec\x75\x67\xcd\
-\xe1\xa0\x70\xea\xc5\x4c\x9c\x32\x9d\xfd\xef\xed\x64\xdb\xdb\xeb\
-\x69\x38\x59\x6b\x51\x84\x81\xc9\x1d\x12\xca\xd8\x09\x93\xc8\x2f\
-\x2c\x21\x6b\x74\x1e\x42\x5c\xf0\x96\x6a\xc3\x34\x37\x35\xb0\xe9\
-\x95\x67\xf8\x70\xff\x7b\x96\xc5\xa0\x28\x81\xe6\x1f\xcf\x6d\x54\
-\x1b\xa4\xfb\x91\x3f\x36\xd3\xa4\xf5\x7f\xb1\xb1\x24\x55\xd4\x0f\
-\xf6\x5d\x83\x4e\x00\x00\x24\xe2\x49\x01\x41\x97\x00\x94\x35\x8a\
-\x73\x9e\xf8\xd2\x1c\x0e\x0a\x8a\x66\x50\x30\x65\x3a\xc7\x8f\x1e\
-\xa4\xf4\x9d\x37\x38\x76\x78\x3f\x72\x84\x1d\x11\xf3\x97\xb0\xf0\
-\x48\xc6\x8e\x9f\x48\xee\x84\x42\xb2\xc6\x8c\xc3\xe1\x18\xd2\x47\
-\xd1\x6f\xbc\xde\x5e\x76\xbc\xbd\x81\xed\x6f\xbd\x4e\x6f\xaf\x9a\
-\xee\x57\x94\x81\x3a\xd5\xd4\xc2\x63\xcf\x6d\xb4\x3a\x0c\x5b\x9a\
-\x3e\x65\x9c\x29\xfd\x48\x21\x9f\x1a\xca\xfb\x86\xf4\xad\x2b\x34\
-\xed\x71\x74\xfd\xae\xa1\xbe\x3f\x50\xd5\xb5\x0d\x60\xd7\xb9\x10\
-\x64\x8f\x19\x4f\xf6\x98\xf1\x34\x37\x9e\xe2\x83\x3d\xdb\xd9\xbb\
-\x7b\x1b\x2d\xcd\xea\x92\xa1\x51\x09\xc9\xe4\xe4\x4e\x60\x4c\xde\
-\x44\xd2\xb3\x47\x5b\xfa\xa4\x7f\xa6\x23\x87\xf6\xb2\xf1\xe5\x35\
-\x34\x35\x0c\x3a\x81\x56\x94\xa0\xf7\xc0\x63\x2f\xab\xeb\xd4\xcf\
-\x61\xda\x64\x53\x12\x80\x1e\xaf\xae\x0d\x7a\xfd\x1f\x86\x38\x80\
-\x17\xa7\x88\x93\xa5\x55\xfa\xeb\x20\x17\x0f\xe5\xfd\x81\x48\x02\
-\xed\x83\x7c\x30\x8c\x8e\x1d\xc5\xac\xf9\x4b\x98\x79\xc9\x62\x2a\
-\xcb\x8e\x70\xf8\xc0\xfb\x1c\x3e\xf0\x7e\xd0\xec\x15\x08\x8b\x88\
-\x20\x2b\x27\x8f\xec\xb1\x13\xc8\x1e\x33\x9e\x48\x4f\xb4\xd5\x21\
-\x9d\xa5\xa9\xa1\x9e\x8d\x2f\xaf\xe1\xc8\xa1\xbd\x56\x87\xa2\x28\
-\x01\xe9\xd0\xb1\x4a\x5e\xd8\xb0\xdd\xea\x30\x6c\x29\xc6\x13\x49\
-\x41\x6e\x96\x09\x3d\x89\x97\x67\xa4\x8b\x21\x0d\x2a\x43\x7f\x82\
-\x17\xfa\xdf\x91\x22\x68\x12\x00\x24\xe8\x43\x9c\xcd\x17\x42\x90\
-\x91\x3d\x96\x8c\xec\xb1\xcc\x5f\xbc\x82\x53\x27\x4f\x70\xe4\xe0\
-\x07\x1c\x39\xb8\xb7\xef\x28\xe1\x08\x59\x26\x88\x88\xf4\x90\x9e\
-\x35\x9a\xd4\xcc\xd1\xa4\x67\x8e\x26\x31\x25\x1d\x61\xf2\x59\xfd\
-\x81\xf0\xf6\xf6\xb2\xfd\xed\xd7\xd9\xfe\xd6\x7a\xbc\xde\x5e\xab\
-\xc3\x51\x94\x80\xa4\xeb\x92\xff\xfb\xd3\xd3\xaa\x26\xca\x39\xcc\
-\x9e\x56\x80\xa6\x19\x3f\xcb\x29\xd1\xff\x31\xd4\xf7\x0e\x39\x01\
-\x70\x68\x8e\x35\x3e\x9f\xfe\x07\x20\x62\xa8\x6d\x04\x12\x21\xc0\
-\xa9\x41\xaf\x1f\x3e\xeb\xa3\x12\x92\x19\x95\x90\xcc\xf4\xd9\x8b\
-\x68\x6b\x6d\xa6\xaa\xe2\x18\x35\xe5\xc7\xa8\xae\x3c\x4e\x6d\x75\
-\x05\x3e\x9f\xfd\xcb\x68\x46\x45\xc7\x90\x98\x9c\x4e\x62\x72\x1a\
-\x09\xc9\x69\xa4\xa6\x67\xdb\xee\x09\xbf\x3f\x1f\x1e\x78\x9f\x4d\
-\x2f\xaf\xa1\xb9\xa9\xc1\xea\x50\x14\x25\xa0\x3d\xf5\xd2\x9b\xec\
-\xfd\xf0\xb8\xd5\x61\xd8\xd6\xec\x92\x49\x66\x74\xd3\xee\x74\x38\
-\x5e\x18\xea\x9b\x87\x9c\x00\x4c\x4e\x16\xed\xa5\x55\xbe\x17\x80\
-\xcf\x0d\xb5\x8d\x40\x13\x13\x26\x39\xd9\xee\xdf\x27\xda\xc8\xa8\
-\x68\xc6\xe5\x4f\x61\x5c\xfe\x14\x00\x7c\x3e\x2f\x27\xaa\x2b\xa8\
-\xa9\x38\xce\xa9\x93\x27\x68\xac\x3f\x49\xc3\xa9\x3a\x3a\xda\x5b\
-\xfd\xda\xef\x40\x68\x0e\x07\xd1\x31\x71\xc4\xc4\x25\x10\x1b\x97\
-\x40\xec\xa8\x04\xe2\xe2\x13\x49\x4a\x49\x27\x34\x3c\xb0\xf2\xbe\
-\x86\xfa\x3a\x36\xbe\xb2\x86\x63\x1f\xee\xb7\x3a\x14\x45\x09\x78\
-\xb5\xf5\x8d\xdc\xff\xd8\xcb\x56\x87\x61\x5b\x61\xa1\x6e\xa6\x4d\
-\xce\x33\xbe\x23\xc9\x9a\xc9\xc9\x62\xc8\xa5\x17\x87\xb5\x89\x4f\
-\x48\xf9\x88\x14\x22\x68\x12\x80\xc4\x28\x38\x69\x70\x95\x4b\x87\
-\xc3\x49\x5a\x46\x0e\x69\x19\x67\x57\x8f\xea\xe9\xea\xa4\xa1\xe1\
-\x24\x8d\xa7\x4e\xd2\xda\xd4\x48\x47\x47\x1b\x9d\x1d\xed\x74\x75\
-\xb4\xd3\xd9\xd1\x4e\x67\x67\x3b\x1d\xed\x7d\xc1\xf9\xbc\xbd\xfd\
-\x4e\x6d\xbb\x5c\x6e\x1c\x4e\x27\xa1\xa1\x61\x38\x5c\x2e\x9c\x4e\
-\x17\x61\xe1\x11\x84\x47\x46\x11\x11\xe9\x21\x32\xd2\x43\x44\x44\
-\x14\x11\x9e\x68\x22\x3d\x31\x44\xc7\xc4\x99\x32\x85\x65\xa4\xde\
-\x9e\x1e\xb6\xbe\xb9\x8e\x9d\x5b\x36\xe2\xf3\xa9\xab\x9d\x15\x65\
-\xb8\xa4\x94\xfc\xf2\xbe\x27\xd4\xc6\xbf\xf3\x98\x3e\x65\x3c\x21\
-\x6e\x97\xe1\xfd\x08\xa1\x3f\x32\x9c\xf7\x0f\x2b\x01\xf8\x30\xcd\
-\xf1\xca\xd8\x6a\xbd\x12\xb0\xae\x62\x8b\x89\x72\x62\x25\x65\x8d\
-\x82\x36\x0b\x3e\xf7\xee\xd0\x30\x92\x53\x33\x49\x4e\xcd\x1c\xf4\
-\x7b\xa5\xd4\x6d\xb3\xe3\xde\x4c\x07\xf7\xee\x61\xd3\xab\xcf\xd0\
-\xda\xdc\x64\x75\x28\x8a\x32\x62\x3c\xf5\xf2\x5b\x6c\xdb\x7d\xc0\
-\xea\x30\x6c\x6d\xe1\xcc\x22\x33\xba\xa9\xfc\x30\xd5\xb9\x61\x38\
-\x0d\x0c\x6b\x54\xb8\x46\x08\x1f\xf0\xe8\x70\xda\x08\x24\x4e\x0d\
-\x66\x66\xe9\x84\x1b\x9f\xd8\xf9\x55\xb0\x0d\xfe\xed\xad\x2d\x3c\
-\xf1\x97\xdf\xf1\xfc\x13\x0f\xa9\xc1\x5f\x51\xfc\xe8\x68\x79\x0d\
-\x7f\x78\x64\xc8\x4b\xce\x41\x21\x2c\x2c\x84\x59\x25\x05\xc6\x77\
-\x24\x78\xe8\xa3\x31\x78\xc8\x86\x3d\x32\x68\x52\x7b\x90\xbe\x53\
-\x72\x41\x21\xdc\x05\xb3\xb2\x75\xdc\x41\x59\x08\x39\x30\xd4\xd7\
-\xd5\x50\x7e\xf4\x90\xd5\x61\x28\xca\x88\xd2\xe3\xf5\xf2\x5f\xbf\
-\x79\x94\x9e\x5e\x75\x72\xe6\x7c\x2e\x99\x51\x68\xc6\xf4\xbf\xd4\
-\xbd\xda\x5f\x86\xdb\xc8\xb0\x13\x80\x29\xe9\xe2\x10\xc8\x77\x86\
-\xdb\x4e\x20\x89\x70\xc3\xf4\x4c\x1d\xfb\x1d\x70\x53\x14\x45\x31\
-\xc6\x6f\x1e\x78\x86\xc3\x65\x55\x56\x87\x61\x7b\x8b\x66\x15\x1b\
-\xdf\x89\x90\x6f\x94\x64\x8a\x23\xc3\x6d\xc6\x5f\x73\xc3\x0f\xfa\
-\xa9\x9d\x80\x11\x1f\x0e\xf1\x11\x41\x33\xf1\xa1\x28\x4a\x10\x7b\
-\xee\xf5\xad\x3c\xfb\xda\x16\xab\xc3\xb0\xbd\x51\x31\x1e\x4a\x0a\
-\x4d\xd8\xfd\xaf\xf3\x90\x3f\x9a\xf1\x4b\x02\x10\xe2\x72\x3c\x0e\
-\x04\xdd\x4d\x10\x21\x41\x55\x08\x59\x51\x94\x60\xb4\xf7\xc3\xe3\
-\xdc\x7d\xff\x90\x2a\xcd\x06\x9d\xa5\x0b\xa6\xe3\x70\x18\xbe\xe7\
-\xaa\x49\xd7\x1c\x43\xaa\xfd\xff\x49\x7e\x89\xb4\x20\x51\xb4\x49\
-\xf8\x9b\x3f\xda\x0a\x14\xbd\x3a\x7e\xaf\x09\xa0\x28\x8a\x62\x27\
-\xf5\x8d\xcd\x7c\xff\x17\x0f\xd1\xe3\x55\x47\x68\x2f\x44\x08\xc1\
-\xd2\xf9\xd3\x0d\xef\x47\x4a\xfe\x52\x92\x2a\x3a\xfc\xd1\x96\xdf\
-\x52\x15\xa1\x69\xbf\x23\x48\x36\x03\x4a\x09\x7b\xaa\x04\xdd\xea\
-\x77\x42\x51\x94\x11\xaa\xa3\xa3\x8b\xef\xfe\xcf\x9f\xd5\x35\xbf\
-\x03\x34\x25\x7f\x34\x19\x29\x09\x86\xf7\xe3\x70\x68\x7f\xf6\x57\
-\x5b\x7e\x4b\x00\x8a\x53\xc4\x3e\x81\x7c\xdb\x5f\xed\xd9\x95\x94\
-\x50\x5a\x2d\xa8\x6a\x51\x4f\xff\x8a\xa2\x8c\x4c\x3d\x5e\x2f\xdf\
-\xfb\xe5\x83\x7c\x78\x5c\x6d\xfa\x1b\xa8\xcf\x2c\xbc\xd8\xf8\x4e\
-\x84\xdc\x34\x25\x45\xf8\xed\xf6\x32\xff\x2e\x56\x08\xfe\xe8\xd7\
-\xf6\x6c\xc6\x27\x61\x5b\x85\xa0\xa2\x49\x0d\xfe\x8a\xa2\x8c\x4c\
-\x52\x4a\x7e\xf1\x87\xc7\x29\xfd\xe0\x43\xab\x43\x09\x18\xb1\xd1\
-\x51\x5c\x72\x71\xa1\xe1\xfd\x48\xe9\xdf\x31\xd6\xaf\x09\x40\x64\
-\xbb\xe3\x29\xe0\xa4\x3f\xdb\xb4\x0b\x9f\x0e\xdb\xca\x05\x27\x5a\
-\xd5\xe0\xaf\x28\xca\xc8\x24\xa5\xe4\x57\x7f\x7a\x8a\x57\xdf\xd8\
-\x69\x75\x28\x01\x65\xc5\xe5\x33\x71\xbb\x0c\x3f\xfb\x5f\x1b\xda\
-\xe4\x78\xc6\x9f\x0d\xfa\x35\x01\xc8\xcd\x15\xdd\x23\x71\x16\xc0\
-\x27\x61\x4b\x99\x46\x5d\x9b\x1a\xfc\x15\x25\x18\x95\x35\x8e\xfc\
-\x0d\x4e\x52\x4a\xee\xba\xff\x69\x75\xdc\x6f\x90\x5c\x4e\x07\xcb\
-\x2f\x9d\x69\x78\x3f\x02\xfe\x50\x50\x20\x7a\xfc\xd9\xa6\xdf\xcf\
-\x2b\xe8\x3d\xda\x1f\x00\xbf\x06\x69\xb5\xbd\x27\x04\xa7\xfc\xb2\
-\xe7\x52\x51\x94\x40\x54\xd5\x22\xd8\x59\x29\xd0\x47\x68\x16\x70\
-\xfa\xc9\x7f\xcd\xab\x23\x7e\x1b\x97\xdf\x2d\x98\x55\x44\x5c\xac\
-\xc7\xe8\x6e\xba\x7b\x5d\xda\x7d\xfe\x6e\xd4\xef\x09\x40\x49\xb6\
-\xa8\x11\x92\x27\xfc\xdd\xae\x55\x7a\x7d\x50\xd6\xa8\x9e\xfc\x15\
-\x25\xd8\x55\x35\x0b\x36\x1f\xd7\x46\xdc\xe9\x1f\x9f\x4f\xe7\x67\
-\xbf\x7f\x4c\x3d\xf9\x0f\x81\x10\x82\xeb\xae\x9c\x6f\x7c\x3f\xf0\
-\xf7\xe9\x89\xe2\x84\xbf\xdb\x35\xa4\x62\x81\x4f\xd3\xee\x32\xa2\
-\x5d\x2b\x74\x7b\xfb\x96\x00\x14\x45\x51\x1a\x3a\x60\xe3\x11\x8d\
-\xda\x11\xb2\x17\xa8\xab\xbb\x87\x7f\xff\xe5\x03\xbc\xb4\x71\xbb\
-\xd5\xa1\x04\xa4\x99\x53\xf3\x19\x93\x95\x6a\x7c\x47\x52\xbb\xc7\
-\x88\x66\x0d\x49\x00\x4a\x52\x45\xa9\x40\x6e\x36\xa2\x6d\xb3\x85\
-\x3a\x51\x35\xff\x15\x45\xf9\x58\x97\x17\xb6\x96\x0b\xde\xad\x11\
-\xf4\xea\x56\x47\x33\x74\xad\xed\x1d\xdc\xf9\x93\xfb\xd8\xb2\x6b\
-\x9f\xd5\xa1\x04\xac\xeb\x97\x2f\x30\xa1\x17\xb1\xa1\x28\x5d\xec\
-\x31\xa2\x65\xc3\x6a\x16\xea\x82\x11\x31\x0b\xe0\x74\x40\x74\x98\
-\xd5\x51\x28\x8a\x62\x27\x12\x38\xd6\x20\x78\xed\x90\xc6\xa1\xfa\
-\xc0\x2b\x0a\x76\xbc\xe2\x04\x5f\xf9\xde\xdd\xbc\x77\xe0\xa8\xd5\
-\xa1\x04\xac\xa2\x82\xb1\x14\x8e\x1f\x6d\x78\x3f\x12\xfd\x6e\xa3\
-\xda\x36\xac\x9a\x7d\x71\x8a\x63\xed\xee\x6a\x7d\x1f\x90\x6f\x54\
-\x1f\x66\x49\x8e\x92\x34\x75\xaa\x79\x00\x45\x51\xce\xd6\xe3\x83\
-\x7d\xb5\x82\xfd\x75\x82\xa8\x10\x88\x09\x91\xb8\x9d\x7d\x0f\x0e\
-\x0e\x01\x2e\x0d\x22\x42\x20\x36\x4c\xe2\x34\xbc\x44\xfc\xc0\xbc\
-\xb9\xfd\x03\x7e\xf2\xdb\xbf\xd1\xd9\xd9\x6d\x75\x28\x01\xed\xc6\
-\xcf\x5e\x66\x46\x37\xfb\x8b\x53\x1d\x2f\x1a\xd5\xb8\x61\x09\x80\
-\x10\x42\x2f\xad\xf2\xfe\x1a\xc4\xfd\x46\xf5\x61\x96\x54\x8f\xe4\
-\x40\x9d\x4a\x00\x14\x45\xe9\x9f\x94\xd0\xd2\x05\x2d\x5d\xfd\x7f\
-\x4f\x84\x3a\x05\x33\x32\x75\x62\x2d\x9c\x4d\xd4\x75\xc9\xc3\x4f\
-\xbd\xca\x43\x4f\xae\x43\x4a\xb5\xb1\x69\x38\xa6\x4e\xca\xa5\x78\
-\x62\xae\xf1\x1d\x49\xf9\x73\x21\x84\x61\x0b\x4d\x86\xe6\xa4\x7a\
-\x8d\xe3\xaf\x40\x85\x91\x7d\x98\xc1\x13\x02\xd1\xa1\x56\x47\xa1\
-\x28\x4a\xa0\xea\xdb\x37\xa0\xd1\x65\xd1\x52\x41\x43\x53\x2b\xff\
-\xf2\xd3\xfb\x78\xf0\x89\x57\xd5\xe0\xef\x07\xb7\x5c\xbb\xd4\x8c\
-\x6e\x2a\x43\x9a\x1d\x8f\x19\xd9\x81\xa1\x09\x40\x49\x89\xe8\x15\
-\x60\xc8\xee\x45\xb3\x65\xc4\xa8\x5f\x1a\x45\x51\x86\xae\xdb\x0b\
-\x1f\x9c\x30\x7f\x26\x71\xfb\xbb\x07\xf9\xf2\x77\x7f\xc5\xf6\x3d\
-\x07\x4d\xef\x7b\x24\x9a\x55\x52\xc0\xc4\xbc\x6c\xc3\xfb\x11\x82\
-\x5f\xf9\xbb\xf0\xcf\x27\x19\xbe\x2a\xd5\xee\xd6\xee\x03\x1a\x8d\
-\xee\xc7\x68\x69\x1e\xa9\x4e\x03\x28\x8a\x32\x2c\x55\x2d\x82\x96\
-\x2e\x73\xfa\xea\xe8\xe8\xe2\x57\x7f\x7a\x92\xef\xfc\xf7\x7d\x34\
-\x34\xaa\x1b\xfd\xfc\x41\xd3\x34\x6e\xbd\xee\x0a\x33\xba\x6a\x70\
-\x3b\xb5\x07\x8c\xee\xc4\xf0\x04\x60\x76\x82\x68\x05\x7e\x6b\x74\
-\x3f\x46\x0b\x73\x41\x8c\x3a\x0d\xa0\x28\xca\x30\x48\x09\x47\x1b\
-\x8c\x7f\x94\xd8\xb6\xfb\x00\x5f\xb8\xf3\x97\xac\x5d\xb7\x45\x4d\
-\xf9\xfb\xd1\x55\x97\x5e\xcc\xe8\xcc\x14\xe3\x3b\x92\xdc\x5d\x90\
-\x28\xda\x8c\xee\xc6\xb0\x4d\x80\x67\xd2\xbb\xb4\x5f\x6b\xa1\xfa\
-\xb7\x80\x18\x33\xfa\x33\x4a\x5a\xb4\xa4\x51\x9d\x06\x50\x14\x65\
-\x18\x2a\x9b\x05\x05\x49\x12\x97\xc3\xff\x6d\x9f\x6a\x6a\xe1\x77\
-\x7f\x7d\x8e\x75\x6f\xee\xf2\x7f\xe3\x41\x2e\x22\x3c\x8c\x9b\xae\
-\xb9\xdc\xf8\x8e\x04\xcd\xa2\x47\x33\xe5\xa1\xd9\x94\x83\x29\x25\
-\x63\x44\x33\x82\x7b\xcd\xe8\xcb\x48\xd9\xb1\x12\x8f\xda\x0c\xa8\
-\x28\xca\x30\x78\x75\xfc\x7e\xb1\x58\xaf\xd7\xc7\xdf\x9f\xdd\xc0\
-\xb5\xdf\xfa\x99\x1a\xfc\x0d\xf2\xa5\x55\x8b\x88\x8d\x8e\x32\xbe\
-\x23\xc9\xaf\x8b\x72\x44\x93\xf1\x1d\x99\x34\x03\x00\x20\xba\xb5\
-\xff\x93\x6e\xfd\x36\x02\x78\x16\xc0\xa9\xc1\x9c\x1c\x9d\x3d\x55\
-\x82\xaa\x16\x35\x13\xa0\x28\xca\xd0\xd4\xb6\x41\x5a\xf4\xf0\xdb\
-\x91\x52\xb2\x79\xe7\x5e\xfe\xf0\xc8\xf3\x94\x57\xd7\x0d\xbf\x41\
-\xa5\x5f\x59\xe9\x49\x7c\xf6\x8a\x79\xc6\x77\x24\x68\x16\xdd\xc6\
-\x94\xfd\xed\x8f\x69\x09\x40\x51\x8e\x68\xda\x55\xe9\xfb\xbd\x10\
-\x7c\xdf\xac\x3e\x8d\xe0\xd2\xa0\x24\x43\x92\xd0\x00\xfb\xea\x04\
-\x3d\x3e\xab\x23\x52\x14\x25\xd0\xd4\xb5\x09\x24\xc3\xdb\x58\xfc\
-\xde\xfe\xa3\xdc\xf7\xf7\x97\x78\x77\xff\x11\xbf\xc5\xa5\x7c\x9a\
-\x10\x82\xef\xac\x5e\x85\xcb\x69\xc0\x9a\xcd\x27\xfb\x92\xdc\x65\
-\xd6\xd3\x3f\x98\x98\x00\x00\x78\xd1\x7e\xed\x42\xbf\x0d\x30\xfc\
-\xee\x44\x23\x09\x20\x3b\x4e\x92\xe2\x91\xec\xa9\x11\xd4\xa8\xd9\
-\x00\x45\x51\x06\xa1\xcb\xdb\x57\x38\x68\x28\xf5\x45\xb6\xbf\x7b\
-\x90\x47\xd6\xbc\xce\xee\xbd\x87\xfd\x1f\x98\xf2\x29\x8b\xe7\x95\
-\x98\x53\xf4\x07\x1a\xe9\xd1\x7e\x63\x46\x47\xa7\x99\x9a\x00\xcc\
-\x48\x17\xa7\x76\x57\xf9\x7e\x2d\xe1\xc7\x66\xf6\x6b\x94\x10\x27\
-\x4c\xcf\x90\x7c\x58\xdf\x57\x0e\x54\x51\x14\x65\xa0\xea\xdb\x05\
-\xd1\xa1\x03\xdb\xa1\xaf\xeb\x3a\x1b\xde\x79\x97\xbf\xaf\xdd\xc0\
-\xa1\x63\x95\x06\x47\xa6\x9c\xe6\x89\x8a\xe0\x1b\x5f\xbc\xd2\x94\
-\xbe\x84\xe0\x7f\xcd\x7c\xfa\x07\x93\x13\x00\x00\xb7\x4b\xfb\xbf\
-\xee\x5e\xfd\x6b\x40\x92\xd9\x7d\x1b\x41\x00\x79\xf1\x92\xce\x1e\
-\x38\xd6\xa8\x92\x00\x45\x51\x06\xa6\x75\x00\x25\x5e\x1a\x1a\x5b\
-\x78\xf9\x8d\x1d\xac\x5d\xb7\x85\x9a\xba\x06\xe3\x83\x52\xce\x72\
-\xe7\xea\x55\xc4\x78\x22\xcd\xe8\xaa\xae\xdd\xa5\x99\xbe\x51\xde\
-\xf4\x04\xa0\x20\x51\xb4\xed\xaa\xf6\xfd\x5c\xc8\x91\x71\x5b\xe0\
-\x69\x13\x53\x24\x4d\x5d\x82\xc6\x4e\xab\x23\x51\x14\x25\x10\xb4\
-\x75\x0b\xfa\xee\x15\x3c\x9b\xd7\xe7\x63\xdb\xee\x03\xbc\xbc\x69\
-\x3b\x9b\x77\xec\xc5\xeb\x53\x1b\x8d\xac\x30\x77\xc6\x24\x16\xcd\
-\x2a\x32\xa5\x2f\x09\xff\xf5\x51\xcd\x1c\x53\x99\x9e\x00\x00\x84\
-\x36\x6a\xbf\xef\x8e\xd1\xbf\x05\xe4\x58\xd1\xbf\x11\x1c\x02\x8a\
-\xd3\x74\x36\x1e\xd1\xd0\x55\xdd\x0d\x45\x51\x2e\xa0\xf5\x8c\xcb\
-\xf8\x74\x5d\xe7\xbd\x03\xc7\xd8\xb0\x65\x0f\xeb\xb7\xec\xa6\xb9\
-\xa5\xdd\xba\xc0\x14\xa2\x3d\x11\xfc\xcb\x2d\x9f\x35\xab\xbb\xe3\
-\x9e\x0e\xcd\x92\x4b\xf3\x2c\x49\x00\x0a\x0a\x44\x4f\x69\x95\xf7\
-\xbf\x41\x18\x5e\xea\xd0\x4c\x51\x21\x30\x3e\x51\xaa\xfd\x00\x8a\
-\xa2\x5c\x50\x6b\x7b\x27\x6f\x6c\x3d\xc8\xe6\x9d\x1f\xb0\xa5\x74\
-\x9f\x1a\xf4\x6d\xe4\x3b\xb7\x7c\x96\xb8\x18\x13\xce\xfc\x03\x48\
-\xf9\xa3\xdc\x5c\x61\xc9\xdd\xcc\x96\x24\x00\x00\x87\x53\x1d\x7f\
-\x19\x5b\xad\x7f\x1b\x28\xb4\x2a\x06\x23\x8c\x8d\x97\x54\x36\x9b\
-\x57\xef\x5b\x51\x94\xc0\xd0\xd9\xd1\x46\x4d\x65\x19\x95\x65\x47\
-\x29\x3f\x76\x88\xda\xea\x4a\xa4\x34\xec\xa6\x57\x65\x88\x96\x2d\
-\x9c\xc1\x82\x8b\x27\x9b\xd5\xdd\x9e\xc3\x69\x8e\x47\xcd\xea\xec\
-\x93\x2c\x4b\x00\xae\x11\xc2\xb7\xa7\xb2\xf7\x76\x5d\x68\x1b\xac\
-\x8a\xc1\x08\x1a\x30\x25\x45\xe7\xad\x63\x5a\x3f\xab\x7b\x8a\xa2\
-\x8c\x74\x5e\x6f\x2f\x2d\x4d\x0d\xd4\xd7\x9d\xe0\x54\x5d\x0d\xf5\
-\x75\x35\x9c\xa8\xae\xa0\xb9\xf1\x94\xd5\xa1\x29\x17\x90\x99\x9a\
-\xc8\xb7\x6f\x5c\x69\x5e\x87\x9a\x76\xfb\x35\x42\x58\xb6\xc9\xc3\
-\xb2\x04\x00\x60\x4a\xba\x6b\xe3\xae\x2a\x7d\xad\x40\x2e\xb7\x32\
-\x0e\x7f\x8b\x0b\x87\xcc\x18\x49\x59\x93\x5a\x0a\x50\x94\x91\xe0\
-\x83\x3d\xdb\xa9\xad\xa9\x38\xeb\x67\x5e\x6f\x2f\x3d\xdd\xdd\x74\
-\x76\x76\xd0\xdd\xd9\x4e\x7b\x5b\x2b\x2d\x4d\x8d\xb4\xb7\xa9\x9b\
-\xf7\x02\x91\xdb\xe9\xe4\xc7\xb7\x7f\x81\xb0\x50\xb7\x29\xfd\x09\
-\x78\xa2\x28\x45\xbc\x61\x4a\x67\xe7\x60\x69\x02\x00\xa0\x3b\xc5\
-\x77\x1c\x5e\xb9\x04\x08\xb1\x3a\x16\x7f\xca\x4f\x92\xd4\xb4\xaa\
-\x4a\x81\x8a\x32\x12\xec\x7f\x6f\x27\xfb\xad\x0e\x42\x31\xd4\xb7\
-\x6e\x5a\x41\xde\xe8\x74\xb3\xba\xeb\x42\xd3\xfe\xcd\xac\xce\xce\
-\xc5\x94\xcb\x80\xce\x67\x5a\x92\x38\x2a\xc0\xd4\xea\x47\x66\x08\
-\x71\x42\x7e\xa2\x5a\x04\x50\x14\x45\xb1\xbb\x4b\xe7\x14\xb3\xfc\
-\xb2\x99\xe6\x75\x28\xf8\x55\x51\x8a\x38\x6e\x5e\x87\xfd\xb3\x3c\
-\x01\x00\x68\x77\x6b\xff\x8d\xa0\xc6\xea\x38\xfc\x2d\x2b\x56\x12\
-\x1b\x66\x75\x14\x8a\xa2\x28\xca\xb9\x8c\xce\x4c\xe5\xdf\xbe\xfa\
-\x39\x33\xbb\xac\x0a\x71\x6a\xbf\x30\xb3\xc3\x73\xb1\x45\x02\x30\
-\x3b\x41\xb4\x22\xe5\x7f\x58\x1d\x87\xbf\x09\x01\x93\x53\x74\x84\
-\xda\x0a\xa0\x28\x8a\x62\x3b\x31\x9e\x48\x7e\xf6\x6f\x37\x12\x1a\
-\x62\xce\xba\x3f\x80\x14\xf2\x7b\x05\x89\xa2\xcd\xb4\x0e\xcf\xc3\
-\x16\x09\x00\x40\x51\xaa\xe3\x21\xa4\xdc\x61\x75\x1c\xfe\x16\x13\
-\xd6\x37\x13\xa0\x28\x8a\xa2\xd8\x87\xdb\xe5\xe2\x67\xff\x76\x13\
-\x69\x49\xf1\xa6\xf5\x29\x84\xdc\x56\x9c\x62\xdd\xb1\xbf\x4f\xb2\
-\x4d\x02\x20\x84\xd0\x85\x70\xdc\x4e\x7f\xb5\x31\x03\x5c\x7e\xa2\
-\xc4\x6d\xfc\x4d\x92\x8a\xa2\x28\xca\x00\x08\x21\xf8\xf7\xaf\x7f\
-\x9e\x49\xe3\x4c\x2d\x46\x2b\x7d\x38\xfe\x45\x08\x61\x9b\x31\xce\
-\x36\x09\x00\x40\x51\x9a\xd8\x22\xe0\x49\xab\xe3\xf0\x37\xb7\x03\
-\x26\x24\xd9\xe6\xdf\x5c\x51\x14\x25\xa8\x7d\xed\xfa\x65\x5c\x3a\
-\xa7\xd8\xdc\x4e\x25\x7f\x2b\x49\x15\x9b\xcd\xed\xf4\xfc\x6c\x95\
-\x00\x00\x48\xa1\xfd\x2b\x30\xe2\xae\xd4\xc9\x8e\x91\x78\x46\xd4\
-\x41\x47\x45\x51\x94\xc0\xb3\x6c\xe1\x0c\xae\x5b\xbe\xc0\xec\x6e\
-\xdb\x34\xa9\xfd\xbb\xd9\x9d\x5e\x88\xed\x12\x80\xe2\x54\x51\x06\
-\xfc\xa7\xd5\x71\xf8\x9b\x10\x50\xa0\x66\x01\x14\x45\x51\x2c\x33\
-\xa3\x68\x3c\xdf\xfd\xca\x35\xa6\xf7\x2b\x05\xff\x31\x25\x43\x54\
-\x99\xde\xf1\x05\xd8\x2e\x01\x00\x68\x4e\xd5\xfe\x0f\xd8\x65\x75\
-\x1c\xfe\x96\x14\x25\x89\x0b\xb7\x3a\x0a\x45\x51\x94\xe0\x33\x6e\
-\x4c\x06\x3f\xb9\xf3\xcb\x38\x1c\x26\x0f\x7b\x52\xee\x38\x92\xa2\
-\xfd\xd6\xdc\x4e\x07\xc6\x96\x09\xc0\x7c\x21\xbc\x48\xed\x66\x04\
-\xbd\x56\xc7\xe2\x6f\x93\x92\x75\xd4\xa9\x40\x45\x51\x14\xf3\xe4\
-\x66\xa7\x71\xd7\x0f\x6f\x25\x3c\xcc\xf4\x75\x58\xaf\x26\x1c\xb7\
-\x5a\x59\xef\xff\x7c\x6c\x99\x00\x00\x14\xa7\x8b\x77\x85\xe4\x2e\
-\xab\xe3\xf0\xb7\xd8\xb0\xbe\x99\x00\x45\x51\x14\xc5\x78\x63\xb3\
-\xd2\xf8\xcd\x8f\xbe\x86\x27\x2a\xc2\xfc\xce\x05\x3f\x9f\x92\x26\
-\x76\x9b\xdf\xf1\xc0\xd8\x36\x01\x00\xe8\xd4\xb5\x1f\x03\x87\xad\
-\x8e\xc3\xdf\xf2\x13\xa5\x2a\x0e\xa4\x28\x8a\x62\xb0\x31\x59\xa9\
-\xd6\x0d\xfe\x70\xa8\xb9\x5b\xfb\x1f\x2b\x3a\x1e\x28\x5b\x27\x00\
-\x33\x33\x44\xa7\xd0\xf5\x5b\x18\x61\xb5\x01\x3c\xa1\x90\xe1\x19\
-\x51\xff\x97\x14\x45\x51\x6c\x25\x33\x2d\x89\xbb\x7e\x78\x2b\xd1\
-\x1e\x4b\x06\x7f\x29\xa5\xfe\xb5\xf9\x39\xa2\xcb\x8a\xce\x07\xca\
-\xd6\x09\x00\x40\x51\x86\x6b\x13\xf0\x90\xd5\x71\xf8\xdb\xf8\x44\
-\x89\x43\xcd\x02\x28\x8a\xa2\xf8\x5d\x66\x5a\x12\xf7\xfe\xf8\xeb\
-\xc4\xc5\x7a\xac\x09\x40\x70\xdf\xd4\x74\xd7\x06\x6b\x3a\x1f\x38\
-\xdb\x27\x00\x00\x7a\x97\x76\x27\x60\xbb\x23\x14\xc3\x11\xee\x86\
-\xec\x38\x35\x0b\xa0\x28\x8a\xe2\x4f\x79\x39\xe9\xdc\xfb\x9f\xdf\
-\xb0\x72\xf0\xaf\x11\xdd\xf6\x3b\xf3\xdf\x9f\x80\x48\x00\x4a\xc6\
-\x88\x66\x21\xe5\x1d\x56\xc7\xe1\x6f\x79\xf1\x12\xa7\x2a\x11\xac\
-\x28\x8a\xe2\x17\x25\x85\x79\x7d\x83\x7f\x4c\x94\x65\x31\x48\xe4\
-\x37\x8a\x72\x44\x93\x65\x01\x0c\x42\x40\x24\x00\x00\x45\xe9\xce\
-\x27\x25\x62\xad\xd5\x71\xf8\x53\x88\x13\xc6\x8e\x52\xb3\x00\x8a\
-\xa2\x28\xc3\xb5\xe4\x92\x69\xfc\xea\x07\x5f\x21\x3c\x3c\xd4\xba\
-\x20\x84\x78\x6a\x6a\xaa\xf3\x19\xeb\x02\x18\x9c\x80\x49\x00\x00\
-\x64\xaf\xf8\x3a\x50\x6f\x75\x1c\xfe\x94\x1b\x2f\x89\x30\xef\x26\
-\x4a\x45\x51\x94\x11\xe7\xfa\xe5\x0b\xf9\xfe\x37\xae\xc5\xe9\xb0\
-\x74\x4a\xb5\xce\xe5\x10\xb7\x59\x19\xc0\x60\x05\x54\x02\x50\x92\
-\x2d\x6a\x04\x72\xb5\xd5\x71\xf8\x93\x43\xc0\xc4\x64\x35\x0b\xa0\
-\x28\x8a\x32\x58\x9a\xa6\xf1\xdd\xaf\x7c\x96\xaf\xdd\xb0\x0c\x61\
-\xed\xd9\x6a\x29\x91\xab\x27\x25\x89\x5a\x2b\x83\x18\xac\x80\x4a\
-\x00\x00\x8a\xd2\x9c\xcf\x0a\xc9\x9f\xac\x8e\xc3\x9f\x52\xa2\x24\
-\x49\x91\x2a\x09\x50\x14\x45\x19\xa8\xb0\xb0\x10\xfe\xf7\xdf\x6f\
-\xe1\xaa\xcb\x66\x5a\x1d\x0a\x02\x7e\x3f\x35\xcd\xf9\xbc\xd5\x71\
-\x0c\x56\xc0\x25\x00\x00\x3e\x4d\xbb\x03\x38\x60\x75\x1c\xfe\x34\
-\x29\x45\x1d\x0b\x54\x14\x45\x19\x88\xcc\xd4\x44\xfe\xf4\xd3\xdb\
-\x99\x51\x34\xde\xea\x50\x00\xf6\xfb\xfa\x6e\xb1\x0d\x38\x01\x99\
-\x00\x94\xa4\x8a\x0e\x5d\x68\xd7\x03\x3d\x56\xc7\xe2\x2f\x91\xee\
-\xbe\xfd\x00\x8a\xa2\x28\xca\xb9\xcd\x9d\x3e\x91\x3f\xff\xfc\x76\
-\x72\x32\x92\xad\x0e\x05\xa0\x5b\x48\xed\xba\x92\x54\xd1\x61\x75\
-\x20\x43\x11\x90\x09\x00\x40\x49\xaa\x28\x45\xf0\x23\xab\xe3\xf0\
-\xa7\x71\x09\xea\xb6\x40\x45\x51\x94\xfe\x38\x1c\x1a\x5f\xbb\x61\
-\x19\x3f\xfd\xd7\x9b\x89\x08\x0f\xb3\x3a\x9c\x3e\x92\xef\x17\xa5\
-\x8b\x3d\x56\x87\x31\x54\x01\x9b\x00\x00\x14\xa5\x68\xbf\x04\x61\
-\xfb\x6a\x4b\x03\x25\x04\x94\xa4\xe9\xb8\x02\xfa\x5f\x45\x51\x14\
-\xc5\xbf\xe2\x63\xa3\xf9\xed\x7f\x7e\x83\xeb\x97\x2f\xb4\x3a\x94\
-\x33\x88\xd7\x8a\xd2\xb4\xbb\xad\x8e\x62\x38\x02\x7a\xa8\x11\x42\
-\xe8\x9a\x2e\xbe\x08\x34\x58\x1d\x8b\xbf\x84\xbb\x61\x6a\xba\xba\
-\x2c\x48\x51\x14\x05\xa0\x78\x62\x2e\x0f\xfe\xef\x77\x28\x1c\x3f\
-\xda\xea\x50\xce\xd4\xe8\x45\xdc\x2c\x84\xd0\xad\x0e\x64\x38\x02\
-\x3a\x01\x00\x98\x92\x21\xaa\xa4\x90\xb7\x58\x1d\x87\x3f\x25\x47\
-\x49\x72\x55\x81\x20\x45\x51\x82\x98\xdb\xe5\xe2\x6b\x37\x2c\xe3\
-\xee\xff\xf7\x55\x4b\x2b\xaa\xe2\xe9\xdb\x00\x00\x1a\x00\x49\x44\
-\x41\x54\xfb\xf5\x47\x48\x79\xeb\xf4\x34\x51\x61\x75\x1c\xc3\x15\
-\xf0\x09\x00\xc0\xd4\x54\xe7\x1a\x09\x0f\x5b\x1d\x87\x3f\x4d\x48\
-\x94\x24\x44\xa8\x24\x40\x51\x94\xe0\x33\x26\x2b\x95\x3f\xfd\xfc\
-\xdb\x5c\xbf\x7c\x21\x9a\x66\xaf\x61\x4a\x48\xfe\x54\x94\xee\x7c\
-\xd2\xea\x38\xfc\xc1\x69\x75\x00\xfe\x12\xea\xd2\xbe\xd9\xdd\xab\
-\x4f\x07\xf2\xad\x8e\xc5\x1f\x84\x80\xe2\x74\xc9\xc6\xc3\x82\x1e\
-\x9f\xd5\xd1\x28\x81\x2a\x26\x2e\x1e\x4f\x4c\xac\xd5\x61\x28\xca\
-\x80\x38\x1d\x0e\xbe\xb8\xea\x52\xbe\xb4\xea\x52\x1c\x0e\x7b\x0d\
-\xfc\x00\x08\xde\xd7\x1c\xda\x9d\x56\x87\xe1\x2f\x23\x26\x01\x28\
-\x48\x14\x6d\x7b\x2a\xe5\x0a\x5d\xd3\xb7\x23\x89\xb6\x3a\x1e\x7f\
-\x08\x73\xc2\xe4\x54\xc9\x8e\x0a\xb5\x21\x40\x19\x1c\xa7\xcb\xc5\
-\xf4\xd9\x0b\x99\x3e\x7b\x11\x4e\xa7\xcb\xea\x70\x14\xe5\x82\xb2\
-\x33\x92\xf9\xe1\x6d\xd7\x32\x7e\x4c\xa6\xd5\xa1\x9c\x4b\x93\xf4\
-\x69\x2b\x27\xa7\x8a\x76\xab\x03\xf1\x97\x11\x93\x00\x00\x4c\x49\
-\x17\x87\x4a\xab\xbc\x5f\xa4\xef\xd2\xa0\x11\x31\x6a\xa6\x79\x24\
-\x47\x23\x04\xa7\x46\xcc\x47\x4e\x31\x5a\xee\x84\x42\xe6\x2f\x5e\
-\x81\x27\x26\xce\xea\x50\x14\xe5\x82\xdc\x4e\x27\x9f\xbf\x6a\x3e\
-\x5f\xbe\xfa\x52\xdc\x2e\xdb\x26\xab\x52\x0a\x79\xd3\xd4\x0c\x71\
-\xd8\xea\x40\xfc\x69\x44\x25\x00\x00\xc5\x69\xce\xe7\x4a\x2b\x7d\
-\x3f\x47\x10\x10\xf7\x31\x0f\xc4\xc4\x24\x9d\x37\x8f\x6a\xa8\x1d\
-\x01\xca\xf9\xc4\xc6\xc5\x33\x7f\xe9\x2a\x46\xe7\x8e\x88\x55\x30\
-\x25\x08\x14\x17\x8c\xe5\xce\xd5\xab\xc8\xb6\x47\x51\x9f\xf3\xf9\
-\x49\x20\xdd\xf2\x37\x50\x23\x2e\x01\x00\x28\x4a\xd3\x7e\xb8\xbb\
-\x5a\x16\x81\x5c\x6c\x75\x2c\xfe\x10\x1b\x06\x31\x61\xd0\xd8\x69\
-\x75\x24\x8a\x1d\xb9\x5c\x6e\xa6\xcd\x5e\xc0\x8c\x39\x97\xe2\x70\
-\x8c\xc8\x5f\x69\x65\x84\x89\x8f\x8d\xe6\xab\x37\x2c\x63\xf1\xbc\
-\x12\xab\x43\x19\x00\xf1\xda\xe1\x54\xf1\x5f\x56\x47\x61\x84\x11\
-\xf9\x6d\x21\x84\xd0\xf7\x56\xc8\xeb\xbb\x35\xb9\x13\xc8\xb1\x3a\
-\x1e\x7f\x48\x8a\x92\x34\x76\x8e\x88\x55\x0d\xc5\x8f\xc6\xe4\x15\
-\xb0\xf0\x8a\xab\xd5\x74\xbf\x12\x10\x1c\x0e\x8d\x95\x97\xcf\xe6\
-\x96\x6b\x97\x12\x1e\x16\x62\x75\x38\x03\x51\xa6\x0b\x71\xdd\x35\
-\x42\x8c\xc8\xad\xd8\x23\x32\x01\x00\x28\xc8\x10\x0d\xbb\x2b\xe5\
-\x4a\x29\xf4\x2d\x80\x4d\xea\x46\x0e\x5d\x6c\xa8\xd5\x11\x28\x76\
-\x32\x2a\x31\x99\x85\x4b\x56\x91\x39\x3a\xcf\xea\x50\x14\x65\x40\
-\x2e\x2a\x9e\xc0\x6d\x5f\xb8\x32\x10\xa6\xfb\x4f\xeb\x92\x42\x5b\
-\x55\x92\x2a\xea\xad\x0e\xc4\x28\x23\x36\x01\x00\x28\x4a\x17\x7b\
-\x4a\xab\xbd\xb7\x22\xc5\x5f\xad\x8e\x65\xb8\x3c\xa1\x92\x11\xb2\
-\xaf\x51\x19\x06\x97\xdb\xcd\xb4\x59\x6a\xba\x5f\x09\x1c\xf9\xb9\
-\x59\x7c\xf5\x86\x65\x14\x17\x8c\xb5\x3a\x94\x41\x91\xc8\xaf\x4f\
-\x4d\x15\xbb\xac\x8e\xc3\x48\x23\xfe\x1b\xa4\x38\xd5\xf9\x48\x69\
-\xa5\x6f\x16\x82\x5b\xad\x8e\x65\x38\xc2\x5c\x10\xe1\x86\xf6\x11\
-\x73\xff\xa1\x32\x58\x63\xf2\x0a\x58\xb8\xec\xb3\x78\xa2\xd5\xb9\
-\x7e\xc5\xfe\xb2\xd2\x93\xb8\xf9\x73\x4b\x98\x7f\x51\x21\x22\xf0\
-\x6a\x9b\xdf\x3b\x35\xcd\xf9\x90\xd5\x41\x18\x6d\xc4\x27\x00\x00\
-\x51\x9d\xda\xb7\xdb\x22\x7c\x53\xa4\x14\x33\xac\x8e\x65\x38\xb2\
-\x62\x24\xfb\xea\x02\xee\x17\x49\x19\xa6\xd8\x51\x89\x2c\xba\xe2\
-\x6a\xb2\xc6\x8c\xb3\x3a\x14\x45\xb9\xa0\xa4\xf8\x58\xbe\xb4\xea\
-\x52\x96\x2d\x9c\x61\xbb\x2a\x7e\x03\x23\xb7\x84\x34\x39\xbe\x63\
-\x75\x14\x66\x08\x8a\x04\x20\x37\x57\x74\x6f\xaf\x93\xcb\x9d\xbd\
-\xfa\x56\x20\xcb\xea\x78\x86\x6a\x6c\xbc\xa4\xa2\x59\xd0\xda\x6d\
-\x75\x24\x8a\x19\xdc\xa1\x61\xcc\x9a\xbf\x84\xa2\xe9\x73\x02\xf4\
-\x8b\x54\x09\x26\x69\x49\xf1\x5c\x77\xd5\x7c\x96\x2e\x98\x81\xcb\
-\xe9\xb0\x3a\x9c\xa1\x3a\xe6\x72\x3a\x56\x16\x14\x88\xa0\x98\x6b\
-\x0d\x8a\x04\x00\x60\x7a\xa2\x38\xb1\xab\x5a\x2e\x11\x52\x7f\x1b\
-\x08\xc8\x39\x54\x4d\x40\x71\x9a\xce\xdb\xc7\x35\xbc\x01\x7d\x07\
-\x95\x72\x5e\x42\x90\x5f\x58\xc2\x25\x97\x5d\x45\x78\xa4\xbd\x2e\
-\x41\x51\x94\x4f\x1a\x93\x95\xca\xb5\x57\xce\xe7\xd2\xd9\xc5\xf6\
-\x2c\xdf\x3b\x50\x82\x66\x87\xa6\x5d\x39\x29\x49\xd4\x5a\x1d\x8a\
-\x59\x82\x26\x01\x00\x98\x9a\x2a\xf6\x97\xd6\xc8\x15\xe8\xfa\xab\
-\x40\x40\x9c\x41\xf9\xa4\xd8\x30\x98\x9e\x21\xd9\x56\x2e\xf0\xa9\
-\xca\x40\x23\x4e\x62\x72\x1a\x0b\x96\x5e\x4d\x7a\x96\xad\xae\x3e\
-\x55\x94\x4f\x29\x1c\x3f\x9a\xeb\x97\x2f\x60\xe6\xd4\xfc\x40\x5c\
-\xe3\x3f\x9b\xa0\x17\x5d\x5f\x35\x39\xd5\xf1\x81\xd5\xa1\x98\x29\
-\xa8\x12\x00\x80\xe2\x14\xf1\x46\x69\x95\xf7\x46\x10\x8f\x12\xa0\
-\xdb\xea\x13\x23\x25\xd3\x33\x61\x47\xa5\xc0\x3b\x22\x4f\xa7\x06\
-\x9f\x90\xd0\x70\xe6\x2c\xba\x82\xc9\x25\x33\x11\x22\x80\x9f\xa2\
-\x94\x11\xcd\xe1\xd0\x98\x3d\x6d\x22\xd7\x5e\x39\x9f\x89\x79\xd9\
-\x56\x87\xe3\x2f\x12\x5d\xae\x2e\x4e\x77\xad\xb7\x3a\x10\xb3\x05\
-\x5d\x02\x00\x50\x9c\xe6\xfc\x47\x69\x95\x6f\x3c\xf0\xff\xac\x8e\
-\x65\xa8\x92\x22\x25\x73\x73\x24\xef\x94\x69\x74\xf6\x5a\x1d\x8d\
-\x32\x64\xa7\xa7\xfb\x2f\xbf\x8a\xf0\x08\x35\xdd\xaf\xd8\x53\x5c\
-\xac\x87\x25\xf3\xa6\xb1\xe2\xf2\x99\x24\x27\x8c\xb0\xa2\x53\x92\
-\x1f\x15\xa7\x3b\x03\xfe\xa8\xf8\x50\x04\x65\x02\x00\x50\x94\xaa\
-\xfd\x78\x77\x8d\x9e\x8d\xe4\x8b\x56\xc7\x32\x54\x9e\x10\x98\x37\
-\x5a\xa7\xb4\x4a\x50\xd7\x16\x90\x93\x19\x41\x2d\x29\x25\x9d\x85\
-\x57\x7c\x96\xd4\x8c\x6c\xab\x43\x51\x94\x7e\x8d\x1b\x93\xc1\x67\
-\x97\xce\x65\xd1\xec\x22\x9c\x8e\x80\xdd\xd8\x77\x3e\xff\x28\x4a\
-\xd3\xfe\xdb\xea\x20\xac\x12\xb4\x09\x80\x10\x42\xee\xdc\x29\x57\
-\x6b\x29\x32\x0d\xe4\x42\xab\xe3\x19\xaa\x50\x27\x5c\x9c\x25\x29\
-\x6b\x80\xf7\x6b\x05\x3e\xb5\x39\xd0\xf6\x42\xc3\x23\x98\xbb\x68\
-\x19\x93\x8a\x2f\x0e\xfc\xb5\x53\x65\xc4\x89\xf1\x44\x72\xd9\x9c\
-\x62\xae\xba\x74\x26\x59\xe9\x49\x56\x87\x63\x1c\x29\xdf\x88\xea\
-\x74\xdc\x28\x84\x08\xda\xdd\x54\x41\x9b\x00\x00\x94\x94\x88\xde\
-\x9d\x47\xe4\x2a\x11\x22\x37\x0b\xc1\x44\xab\xe3\x19\x2a\x01\x64\
-\xc7\x49\xe2\x23\x24\xbb\x6b\x34\x75\x75\xb0\x4d\x09\x21\x98\x50\
-\x58\xc2\xfc\xc5\xcb\x09\x0b\x8f\xb4\x3a\x1c\x45\xf9\x98\xdb\xe9\
-\xa4\x64\x72\x1e\x8b\x2f\x99\xce\x9c\x69\x13\x03\xf9\x18\xdf\x40\
-\xed\x73\xfa\x1c\x2b\x72\x73\x45\x50\x1f\xaa\x0e\xea\x04\x00\xa0\
-\x64\x8c\x68\xde\x5d\x23\x3f\x23\x75\x7d\x2b\x10\xd0\xe9\x6e\x64\
-\x08\xcc\xce\xd6\x29\x6b\x14\xec\xab\x15\xf4\xa8\x0d\x82\xb6\x91\
-\x94\x9a\xc1\xa2\x2b\x3e\x4b\x4a\x7a\xc0\x96\xa1\x50\x46\xa0\x71\
-\x63\x32\xb8\x7c\x6e\x09\x97\xcf\x99\x4a\xb4\x27\xc2\xea\x70\xcc\
-\x72\x02\xa1\x2d\x2d\xcc\x12\x8d\x56\x07\x62\xb5\xa0\x4f\x00\x00\
-\x8a\x52\xc4\xf1\x5d\xd5\xf2\x0a\x81\xbe\x1e\x49\xb4\xd5\xf1\x0c\
-\x87\x00\xb2\x63\x25\xa9\x1e\xc9\xc1\x93\x82\x63\xa7\x04\x6a\x55\
-\xc0\x3a\xe1\x11\x51\xcc\xbd\xf4\x33\x4c\x9c\x32\x1d\xd4\x74\xbf\
-\x62\x31\x4d\x13\x4c\x18\x93\xc5\xbc\x19\x93\x98\x3f\x73\x0a\x29\
-\x89\x23\x6c\x43\xdf\x85\x35\x09\xa9\x2d\x29\x4a\x13\x65\x56\x07\
-\x62\x07\x2a\x01\xf8\xc8\xd4\x54\xb1\xab\xb4\x52\x2e\x41\xe8\xeb\
-\x80\x80\x9f\x9f\x75\x3b\x60\x52\xb2\x24\x27\xb6\xaf\x7c\x70\x4d\
-\x8b\x20\x68\x17\xba\x2c\xf0\xf1\x74\xff\xe5\x2b\x08\x8b\x08\x9a\
-\x27\x2b\xc5\x86\x34\x4d\x63\x62\x5e\x36\x97\x5c\x3c\x99\xf9\x17\
-\x4f\x26\x21\x2e\xa0\x9f\x71\x86\xa3\x43\x0a\xed\xca\xe2\x34\xb1\
-\xc7\xea\x40\xec\x42\x25\x00\x67\x28\x4e\x17\xef\x94\x56\xf6\x2e\
-\x47\x68\x2f\x00\x23\xe2\x02\xde\xc8\x90\xbe\xc2\x41\x2d\x5d\x7d\
-\x33\x02\xd5\x2a\x11\x30\x5c\x7a\xd6\x68\x16\x5e\x71\x35\x09\x49\
-\x69\x56\x87\xa2\x04\xa9\x88\xf0\x30\x4a\x0a\x73\x99\x33\x6d\x12\
-\xb3\x4a\xf2\x89\x8a\x08\xb7\x3a\x24\xab\x75\x6a\x52\x5f\x36\x25\
-\xcd\xf1\x96\xd5\x81\xd8\x89\x4a\x00\x3e\xa1\x38\xdd\xb5\x7e\x57\
-\x75\xef\x72\x21\xb5\x67\x09\xd0\x6a\x81\xfd\xf1\x84\xc2\xb4\x0c\
-\x49\x43\x87\xe4\xc0\x49\xc1\xc9\x36\x95\x08\xf8\x5b\x44\x94\x87\
-\x79\x8b\xae\x24\x7f\x72\x89\x9a\xee\x57\x4c\xa5\x69\x82\xdc\x9c\
-\x74\x4a\x26\xe5\x31\xad\x30\x8f\xc9\xf9\x63\x82\x61\x23\xdf\xc0\
-\x08\x7a\xa5\x2e\xaf\x99\x92\xee\xda\x68\x75\x28\x76\xa3\x12\x80\
-\x7e\x4c\x4d\x75\xbd\xba\xab\xda\x7b\x9d\x90\xe2\x71\x46\xd8\x7f\
-\xa3\xb8\x70\x98\x99\x25\x69\xed\x96\x94\x37\x0a\xaa\x5a\x04\x1d\
-\xaa\x90\xd0\xb0\x68\x0e\x07\xc5\xd3\xe7\x30\x73\xfe\x12\xdc\x21\
-\x23\x62\xe2\x48\x09\x00\x29\x89\x71\x14\x15\x8c\x65\xc6\x94\x09\
-\x4c\x2b\xcc\xc5\x13\xa5\x96\x9a\xfa\xe1\x43\xca\x2f\x4e\x4d\x77\
-\xbe\x60\x75\x20\x76\x34\xa2\x06\x37\x7f\x9a\x9a\xea\x5c\xb3\xbb\
-\xd2\xbb\x5a\x0a\xf1\x20\x30\xe2\x6a\xb3\x46\x85\x40\x41\xb2\x24\
-\x3f\x59\xd2\xd4\x09\x95\xcd\x82\xca\x66\x41\xb7\xd7\xea\xc8\x02\
-\x4b\x46\xf6\x58\x16\x2e\x5d\x45\x7c\x52\xaa\xd5\xa1\x28\x23\x5c\
-\x6a\xd2\x28\x26\x8d\xcf\x61\xf2\xf8\xd1\x94\x14\xe6\x91\x9a\x34\
-\xca\xea\x90\xec\x4e\x0a\x21\xbf\x5a\x94\xea\x7c\xcc\xea\x40\xec\
-\x4a\x25\x00\xe7\x51\x94\xee\xfc\xcb\xae\x2a\x5f\xa4\x80\x7b\xad\
-\x8e\xc5\x28\x82\xbe\x0b\x86\x62\xc3\x24\x05\x49\x7d\xb3\x02\x07\
-\xeb\x85\x2a\x2f\x7c\x01\x91\x9e\x68\x2e\xb9\x7c\x39\xe3\x27\x16\
-\x5b\x1d\x8a\x32\x02\x85\x87\x87\x32\x3e\x27\x9d\xf1\xb9\x99\x14\
-\x8e\x1b\x4d\xe1\x84\x1c\x3c\x91\x41\xbf\x8e\x3f\x18\x52\xc0\x6d\
-\x45\xa9\xce\xfb\xad\x0e\xc4\xce\x54\x02\x70\x01\x53\xd3\x1c\xbf\
-\xdb\x55\xed\x73\x09\xc9\x5d\x56\xc7\x62\x34\x4d\xf4\x15\x14\xca\
-\x88\xe9\xdb\x27\x70\xf8\x94\x40\xaa\x8d\x02\x67\xd1\x1c\x0e\xa6\
-\x94\xcc\x66\xf6\xc2\xa5\x6a\xba\x5f\xf1\x0b\xa7\xc3\xc1\x98\xec\
-\x54\x0a\xc7\xe5\x30\x6e\x4c\x06\xe3\x46\xa7\x93\x95\x96\x84\xa6\
-\xa9\x7d\x24\xc3\xf0\xef\x45\x69\x8e\xdf\x5b\x1d\x84\xdd\xa9\x04\
-\x60\x00\xa6\xa6\x3a\xee\x2e\xad\xf6\x8d\x42\xf2\x43\xab\x63\x31\
-\x83\x43\x83\x82\xa4\xbe\x5a\x02\xa5\x55\x1a\xad\x41\x5d\x2b\xeb\
-\x9f\x32\xb2\x73\x59\xb4\xec\x6a\x46\x25\x24\x5b\x1d\x8a\x12\x80\
-\x1c\x0e\x8d\xa4\xf8\x58\xb2\xd3\x93\xc8\xc9\x48\x21\x27\x23\x99\
-\x9c\xf4\x24\x72\xb2\x52\x70\x3b\xd5\x57\xb1\xbf\x48\xf8\xcf\xa9\
-\x69\x8e\x5f\x58\x1d\x47\x20\x50\x9f\xba\x01\x2a\x4e\x75\xfc\x47\
-\x69\x95\xaf\x0d\xf8\xb9\xd5\xb1\x98\x25\x36\x0c\x2e\x19\xa3\xf3\
-\x7e\x8d\xe0\x78\x63\xf0\x3e\x8d\x78\xa2\x63\x99\xb7\x78\x39\xe3\
-\xf2\xa7\x58\x1d\x8a\x12\x00\xa2\x3d\x11\xa4\x27\x25\x90\x9e\x1a\
-\x4f\x46\x4a\x02\x69\xc9\xf1\x64\xa5\x25\x92\x9d\x91\xac\x06\x7a\
-\xe3\xfd\x62\x6a\x9a\xe3\xc7\x56\x07\x11\x28\x82\xf7\x5b\x7d\x88\
-\x76\x55\xf9\xfe\x55\x40\xd0\x65\x97\xe5\x4d\x82\x77\x6b\x02\xe3\
-\xb2\xa1\xb2\x23\x07\x79\xf2\xaf\xc3\x9f\xfd\x73\x38\x9c\x94\xcc\
-\x9a\xcf\x45\x73\x2f\xc3\xe5\x72\xfb\x21\xb2\xc0\xa1\xeb\x3a\x4d\
-\xa7\x4e\x72\xaa\xbe\x96\xc6\xfa\x3a\x4e\xd5\xd7\xd2\x74\xea\x24\
-\x2d\xcd\x8d\xb4\xb7\xb5\xa0\xeb\x01\xf0\x41\x30\x48\xb4\x27\x82\
-\xa4\x51\xb1\x24\xc6\x47\x93\x9c\x10\x47\xe2\xa8\x18\x12\xe3\x63\
-\x49\x4b\x1a\x45\x46\x6a\x02\x91\xe1\x61\x56\x87\x18\x9c\x24\xff\
-\xaf\x38\xdd\xf1\x13\xab\xc3\x08\x24\x2a\x1d\x1d\xa4\xa9\x69\x8e\
-\x5f\x96\x56\xfa\x5a\x10\xfc\x8e\x11\x78\x3a\xe0\x5c\x32\x63\x24\
-\x31\x61\x92\x77\xca\xb4\xa0\xd8\x20\x98\x93\x9b\xcf\x82\x25\x2b\
-\x89\x1d\x95\x60\x75\x28\x96\xd0\x34\x8d\xb8\x84\x24\xe2\x12\x3e\
-\x7d\x3d\x86\x94\x92\xf6\xb6\x16\x5a\x9b\x9b\x68\x6b\x6d\xa6\xb5\
-\xa5\x91\xd6\xe6\x66\xda\xdb\x5a\xe8\xee\xec\xa0\xab\xb3\x83\xce\
-\xae\x0e\xba\xbb\x3a\xe9\xea\xe8\xc0\xe7\xb3\xf7\xd1\x12\x21\x34\
-\x42\xc3\xc3\x08\x0d\x0d\x27\x34\x2c\x9c\xf0\xf0\x48\xc2\x23\xa3\
-\x88\x88\x8c\x26\x35\x3e\x82\x29\xd9\x51\xc4\xc5\xf4\xfd\x2f\x29\
-\x3e\x96\x10\xb7\xcb\xea\x90\x95\xb3\x49\x21\xb8\xa3\x28\xcd\xf1\
-\x1b\xab\x03\x09\x34\x2a\x01\x18\x82\xe2\x74\xc7\x1f\x77\x57\x7b\
-\x5b\xa5\x14\x0f\x13\x44\xff\x0d\x3d\x21\x30\x6f\xb4\xce\x3b\x65\
-\x1a\xcd\x5d\x56\x47\x63\x8c\xe8\xd8\x51\xcc\x5f\xbc\x82\xb1\xe3\
-\x27\x59\x1d\x8a\x6d\x09\x21\x88\x8c\x8a\x26\x32\x6a\x60\x25\x65\
-\x7b\x7b\x7a\xe8\xea\xea\xa0\xbb\xb3\x83\xce\xce\x0e\xba\xba\x3a\
-\xe9\xed\xee\xc2\xeb\xed\xa5\xa7\xbb\x1b\x5d\xf7\xd1\xd5\xd9\x89\
-\xae\xfb\xe8\xe9\x3e\xbd\xe1\x44\xd2\xdd\xd5\xf9\x71\x1b\xba\xcf\
-\x87\x4f\xd7\x71\xb9\xce\x1e\x7c\xdd\xa1\x61\x88\x33\x26\x32\x1d\
-\x4e\x07\x2e\xd7\x47\xf5\xbb\x04\x84\x86\x86\xe1\x74\xba\x71\xb9\
-\x5c\xb8\x43\xc3\x70\xb9\xfb\xfe\x1c\x12\x1a\x4e\x68\x68\x18\xa1\
-\x61\xe1\xe7\xdc\xcc\x29\x80\x39\xa3\x75\xe2\xd4\x03\xbd\x9d\xf9\
-\x24\xf2\x96\xe2\x54\xe7\x43\x56\x07\x12\x88\x82\x66\xf0\xf2\xb7\
-\xa2\x54\xe7\xa3\xbb\x2a\xbd\xbd\x42\x13\x7f\x43\x12\x34\x8f\x04\
-\xa1\x4e\x98\x93\xad\xb3\xad\x42\x70\xb2\x7d\xe4\xac\x20\x39\x1c\
-\x0e\x26\x97\xcc\x66\xf6\xa2\x2b\x70\xbb\x47\x4c\x01\x48\x5b\x70\
-\xb9\xdd\xb8\xdc\x6e\xa2\x3c\x31\x56\x87\x32\x28\x29\x1e\xa9\x06\
-\x7f\x7b\xeb\x41\xca\xeb\xa7\xa6\x3b\x9f\xb2\x3a\x90\x40\xa5\x12\
-\x80\x61\x98\x9a\xee\x7c\x62\x77\xa5\xb7\x5d\x0a\xf1\x24\x10\x34\
-\x5f\x15\x4e\x07\x5c\x9c\x25\xd9\x5e\x01\x27\x5a\x03\x3f\x09\x18\
-\x93\x57\xc0\xfc\xa5\xab\x88\x89\x55\x85\x55\x94\x3e\x1a\x90\x9f\
-\xa4\xce\xc0\xda\x58\xb7\xf8\xff\xed\xdd\x6d\x70\x5c\xd5\x79\x07\
-\xf0\xff\x73\xee\xae\x24\xdb\xb2\x5e\x2d\x59\xda\xd5\xe2\x37\x6c\
-\x6c\x64\x90\x57\xf2\x2b\xd8\x99\x18\x28\xae\x71\x9a\x74\x02\x69\
-\x9b\x69\x93\x69\x87\xbc\x4d\x69\x53\x32\xd3\x4e\x68\x3f\x04\xdb\
-\x9d\x29\xc3\x4c\x98\x14\xe7\xa5\x81\xa4\x69\x52\xd3\x21\x75\x20\
-\x0d\x31\x34\x38\x09\x38\x30\x40\xec\xc1\x92\x4d\x8c\x0c\x06\x1b\
-\xc7\xb5\x91\x8d\x85\x25\x59\x96\xb4\xd2\xee\xde\xf3\xf4\x83\x70\
-\x6b\xc2\x9b\x25\xed\xee\xd9\x7b\xf7\xff\x9b\xf1\x17\x8d\xb4\xfa\
-\x5b\x7b\xf6\x9c\xe7\x9e\x7b\xee\x39\xd0\x3f\x4e\xb6\x44\x1e\x71\
-\x1d\x24\xc8\x58\x00\x4c\x51\xb2\x25\xf2\x58\xd7\x29\xdd\xa8\xd6\
-\xee\x14\x60\xa6\xeb\x3c\x85\x62\x64\xfc\x6c\x81\x67\x8f\x09\xfa\
-\x52\x1f\xfc\xfd\xc5\xa8\xa6\x6e\x16\xd6\x6f\xfc\x38\x16\x2c\x6a\
-\x75\x1d\x85\x8a\x4c\x73\x95\xa2\xb2\xb4\xd6\x7d\x06\xc9\x30\x60\
-\xff\x30\x19\x8f\xfe\xd2\x75\x90\xa0\x63\x01\x90\x03\xed\xcd\xf2\
-\xd4\xfe\x53\x7a\xbd\x5a\xfb\x38\x80\x92\x39\x60\xdb\x13\x60\x45\
-\xc2\xe2\xc9\x23\x06\x99\x00\x2d\x0a\x8f\x44\xa3\x58\xb9\xf6\x7a\
-\xac\x5c\x7b\x03\x22\x91\x92\xb9\x7b\x43\x13\x30\xbb\x64\x4a\xf9\
-\xc0\x19\x10\x98\x4d\xc9\xb8\xf7\x9c\xeb\x20\x61\xc0\x02\x20\x47\
-\x92\xcd\xf2\x7c\x67\x8f\xde\x28\x6a\x1f\x03\xf0\xce\xa5\xd3\x21\
-\x35\x2d\x0a\x2c\xa8\x1f\xdf\x39\x30\x08\x16\x2c\x6a\xc5\x75\x9b\
-\x6e\x41\x75\x4d\xc9\xd4\x69\x34\x09\x65\xec\x19\x8b\xd1\x69\x03\
-\x73\xd3\xb2\xb8\xec\x77\x1d\x24\x2c\xd8\xcc\x73\xa8\x23\x26\x9d\
-\x2f\x9c\xd6\x35\xbe\x6f\x1f\x03\xb0\xc4\x75\x9e\x42\x99\x5b\xa7\
-\x38\xfc\x66\x71\x6f\x1b\x5c\x5b\xdf\x80\xeb\x36\x7e\x1c\xf3\x16\
-\x5e\xe9\x3a\x0a\x05\x40\xd6\x77\x9d\x80\x7e\x47\x37\xc4\x6c\x5a\
-\x16\x93\xe3\xae\x83\x84\x09\x0b\x80\x1c\x6b\x6b\x92\x63\xbf\x39\
-\xae\xd7\x66\xa2\xfa\xb0\xa8\xae\x77\x9d\xa7\x10\x2a\x22\xe3\xc7\
-\x0c\x9f\x1d\x76\x9d\xe4\x9d\xa2\xd1\x32\xac\x58\x7b\x1d\x56\xad\
-\xfb\x3d\x78\x1e\x9b\x3b\x5d\x9a\xa1\xb4\xeb\x04\xf4\xff\xe4\x49\
-\x49\xcb\xcd\xc9\x79\x32\xe0\x3a\x49\xd8\xb0\x47\xcc\x83\xab\xe7\
-\x48\x7f\x77\xb7\xfe\xfe\x58\xad\x7e\x07\x8a\x4f\xbb\xce\x53\x08\
-\x0d\xd3\x15\x67\x8b\xec\xb1\xc0\x05\x8b\x5a\x71\xfd\xa6\x5b\x50\
-\xc5\xe9\x7e\x9a\xa0\x61\x16\x00\x45\x41\x81\xef\x57\x0c\xc8\xe7\
-\x5b\x5b\x85\xef\x48\x1e\x14\x57\x8f\x1d\x32\xaa\x2a\x5d\x3d\xf6\
-\x4e\x01\xbe\x82\x90\xff\xad\x7b\x87\x05\xcf\xfe\xb6\x38\xfe\x8b\
-\x6f\x9e\x39\x85\x91\xe1\xf3\xb8\x6c\xde\x22\xd7\x51\x28\xa0\xe2\
-\x55\x8a\x15\x89\x22\xbe\xa7\x15\x7e\xaa\xc0\xd6\xf6\x98\xd9\x22\
-\x22\x7c\x23\xf2\x84\x33\x00\x79\xf4\x56\xc3\xdd\xbc\xff\x64\xf6\
-\x98\x8a\xdc\x0f\x20\xb4\x0f\x16\xd5\x4f\x57\x18\x11\xd8\x22\xf8\
-\xa8\xce\x6a\x6c\x06\xd0\xec\x3a\x06\x05\x58\x75\xc9\xec\xea\x51\
-\x94\xd2\x10\xfd\x4c\x47\x2c\xb2\xdd\x75\x90\xb0\x2b\x99\xbd\xec\
-\x5d\x4a\xb6\x44\x7e\xa0\x6a\x37\x02\x08\xed\x3d\x2c\x23\x40\xf5\
-\xbb\xef\xa8\x4a\x14\x28\x22\x40\xac\xaa\x08\x2a\xd9\xd2\xd4\x2f\
-\xd6\x6e\x68\xe7\xe0\x5f\x10\x2c\x00\x0a\xa4\xa3\x25\xfa\x24\x8c\
-\xb9\x16\xc0\x6f\x5d\x67\xc9\x97\xea\x0a\x76\x9a\x14\x7c\x31\x6e\
-\x02\xe4\xca\x31\x15\x73\x6d\x32\x11\xfd\x95\xeb\x20\xa5\x82\x05\
-\x40\x01\xb5\x37\xcb\xa1\x6c\xd4\xac\x01\x74\x8f\xeb\x2c\xf9\x50\
-\xc5\x19\x00\x0a\x38\x03\x60\x49\x03\x0b\xd9\xc2\xd3\xe7\x3c\xcf\
-\xac\xee\x88\xc9\x4b\xae\x93\x94\x12\x16\x00\x05\xb6\xb2\x51\x4e\
-\x9f\x8b\x79\xeb\x00\xdc\xed\x3a\x4b\xae\x55\x97\xb3\xe3\xa4\x60\
-\x9b\x57\xaf\xa8\xe4\x59\x50\x05\x25\x8a\xfb\xcb\x07\xbc\xf5\x6d\
-\x4d\x72\xc6\x75\x96\x52\xc3\x45\x80\x0e\xac\x17\xc9\x02\xb8\xa3\
-\xb3\x27\xfb\xa2\xa8\xdc\x07\x60\xba\xeb\x4c\xb9\x50\x35\x6d\xfc\
-\x51\x07\x96\x01\x14\x44\x65\x1e\x70\x05\xaf\xfe\x0b\x69\x14\xd0\
-\xdb\x92\x2d\x91\xef\xb9\x0e\x52\xaa\x38\x03\xe0\x50\x47\x2c\xf2\
-\x80\xa8\xb9\x16\xc0\x31\xd7\x59\x72\x21\x6a\xc6\xb7\x06\x26\x0a\
-\xa2\x25\xb3\x15\x65\x9e\xeb\x14\x25\xe3\x7f\xc4\x98\x0f\xb5\xc7\
-\x39\xf8\xbb\xc4\x02\xc0\xb1\x64\x8b\x1c\xc8\xa8\x59\x01\xc8\x2e\
-\xd7\x59\x72\xa1\x8a\x0b\x01\x29\x80\x66\x94\x01\x73\x6b\xd8\x76\
-\x0b\x42\xe5\x67\xe5\xd6\x24\x93\xcd\xf2\xbc\xeb\x28\xa5\x8e\x05\
-\x40\x11\x58\xd5\x22\x67\x93\x31\xd9\x08\xe0\x0e\x00\x01\x3a\x57\
-\xef\x9d\xf8\x28\x20\x05\xd1\xa2\x59\x0a\x29\x8e\x7d\xac\xc2\x4c\
-\x01\xdc\x9d\x8c\xcb\x47\x5a\x13\xd2\xe7\x3a\x0c\xb1\x00\x28\x1a\
-\x22\xa2\xed\x71\xef\x6e\x55\xfd\x18\x02\xbc\x5f\xc0\x74\x3e\x3e\
-\x45\x01\x33\x2d\x02\x24\x6a\x79\xf5\x9f\x67\x83\x2a\x7a\x73\x7b\
-\xdc\xbb\x43\x44\x02\x7d\x91\x13\x26\x2c\x00\x8a\x4c\x47\x4b\xe4\
-\x51\xb1\x66\xa5\x2a\x5e\x74\x9d\x65\x32\x2a\xa3\xec\x48\x29\x58\
-\xe6\xd6\x29\x3b\xc2\xfc\x7a\x59\xc5\xac\xee\x88\x45\xfe\xcb\x75\
-\x10\x7a\x3b\xb6\xfb\x22\x94\x4c\xc8\xab\x15\x65\x66\x0d\x80\xc0\
-\x2d\x90\xe1\x39\xea\x14\x24\x46\x80\xb9\xbc\xfa\xcf\x1b\x05\xbe\
-\xe3\x79\x66\x39\x9f\xef\x2f\x4e\x2c\x00\x8a\x54\x6b\xa3\x0c\xb5\
-\xc7\xbd\x5b\x55\xf4\x66\x00\x67\x5d\xe7\xb9\x54\x5c\x45\x4d\x41\
-\x12\xaf\x56\x94\xb3\x68\xcd\x87\x01\x40\x3f\xd9\x11\xf7\x3e\xd7\
-\xd6\x24\x45\x78\x50\x38\x01\x2c\x00\x8a\x5e\x47\x2c\xf2\xe3\x6c\
-\xd4\x2c\x05\xe4\x71\xd7\x59\x2e\x05\x17\x52\x51\x90\xcc\xaf\xe3\
-\xd5\x7f\xee\xc9\x13\xc6\x9a\xa5\xed\xf1\xc8\x0f\x5d\x27\xa1\xf7\
-\xc7\x02\x20\x00\x56\x36\xca\xe9\x64\x4c\x6e\x12\xc1\xed\x00\xc6\
-\x5c\xe7\x21\x0a\x83\xba\xe9\x40\x2d\x4f\xfd\xcb\x1d\x41\x46\x81\
-\x2d\xc9\x98\xdc\xb8\x2c\x21\xaf\xbb\x8e\x43\x1f\x8c\x05\x40\x40\
-\x88\x88\x26\x63\xde\xbd\x9e\x67\x96\x43\x70\xd0\x75\x1e\xa2\xa0\
-\x5b\x50\xcf\xab\xff\x1c\x7a\xc9\xa8\x59\xd5\x11\xf7\x36\x73\x95\
-\x7f\x70\xb0\x00\x08\x98\xb6\x26\x79\x71\xd4\x37\xab\x00\x6c\x03\
-\x77\xdd\x25\x9a\x94\xda\x69\x3c\xf2\x37\x67\x14\xdb\x3d\xcf\xac\
-\x58\x16\x97\xfd\xae\xa3\xd0\xc4\xf0\x8e\x6d\x80\x75\xf6\x64\x36\
-\x88\x9a\xef\x03\x68\x72\x9d\xe5\x82\xd1\x2c\xf0\xf8\x61\xd6\x95\
-\x54\xbc\x0c\x80\x75\xf3\x2d\xa7\xff\xa7\xae\x57\xa1\xb7\x76\xc4\
-\x23\x3b\x5d\x07\xa1\xc9\x61\x4f\x1d\x60\x1d\xb1\xe8\x2e\x9b\x31\
-\xed\x80\xfc\xd4\x75\x96\x0b\x7c\x5e\x54\x51\x91\x5b\xda\xac\x1c\
-\xfc\xa7\x4a\xe5\xe1\x68\xc4\x5c\xc5\xc1\x3f\xd8\x38\x03\x10\x12\
-\xfb\x4f\x66\x3f\xa1\x22\xdf\x02\x30\xcb\x65\x8e\xc1\x51\xe0\xc9\
-\xa3\xac\x2b\xa9\xf8\x08\xc6\x4f\xfb\x5b\xdc\xc8\x2a\x75\x0a\x4e\
-\x43\xf5\xaf\xdb\x5b\x22\x0f\xb9\x0e\x42\x53\xc7\x9e\x3a\x24\x92\
-\x2d\x91\x1f\x79\x9e\x69\x85\x62\xbb\xcb\x1c\x9c\x01\xa0\x62\x34\
-\x2d\x02\x2c\x4f\x70\xf0\x9f\x02\x85\x62\x7b\xb9\x35\xad\x1c\xfc\
-\xc3\x83\x33\x00\x21\xd4\x75\x32\x7b\x0b\x44\xbe\x0e\x07\x6b\x03\
-\xfa\x53\xc0\x53\xaf\xb1\xae\x24\xf7\x44\x80\xda\x0a\xa0\xa5\x46\
-\x31\xb7\x56\x61\xd8\xdb\x4d\x8a\x02\x87\x45\xcc\xe7\xda\x63\xf2\
-\xb4\xeb\x2c\x94\x5b\xfc\x48\x84\xd4\xbe\xa3\x5a\x6d\x2a\xec\x56\
-\x00\xb7\x01\x28\xd8\xfe\x7c\xe7\x52\xc0\x6e\x16\x00\x94\x67\x82\
-\xf1\x83\xa7\x2a\xa2\x40\x85\x37\xbe\x9b\xdf\x85\x7f\x15\x11\xa0\
-\x22\xa2\x98\x59\x01\x78\xec\xe1\xa6\x22\x0b\xe0\x9e\x73\x69\xb3\
-\x79\xfd\x3c\x19\x75\x1d\x86\x72\x8f\x1f\x8f\x90\xdb\xd7\xa3\xed\
-\x46\xed\xfd\x00\x3a\x0a\xf1\xfb\xce\x8d\x02\xbb\xb9\x06\x80\xf2\
-\xc0\x08\x90\xa8\x51\x34\x57\x01\xf5\xd3\x14\x51\x6e\x3b\x9d\x37\
-\x0a\xec\x87\x98\xcf\x76\xc4\xa4\xd3\x75\x16\xca\x1f\xf6\xd4\x21\
-\xb7\x3c\x26\x5d\xf6\x94\x59\x03\xe0\x0e\x00\x23\xf9\xfe\x7d\xdc\
-\x0a\x98\x72\x4d\x64\xfc\xc4\xbe\x0d\x8b\x2c\x92\x31\x45\x53\x25\
-\x07\xff\x3c\x1a\x56\xc1\x97\x8e\xc6\xcc\x0a\x0e\xfe\xe1\xc7\xee\
-\xba\x84\x1c\x38\xa1\x71\x2b\xf6\x2e\x08\x3e\x95\xaf\xdf\x31\x92\
-\x01\x7e\xfe\x0a\xeb\x4a\xca\x8d\xa6\x4a\xc5\x95\xb3\x15\x55\x15\
-\xae\x93\x84\x9e\x42\xe4\x21\x40\xfe\xae\x3d\x26\xc7\x5d\x87\xa1\
-\xc2\x60\x01\x50\x82\x0e\x9c\xcc\xac\xf7\x61\xb6\x89\x60\x69\xae\
-\x5f\xdb\x02\xd8\x79\xc8\x40\xb9\xd8\x9a\xa6\xa0\x7e\x06\xb0\xb8\
-\x41\xd1\x30\x83\x0d\x29\xff\x74\x9f\xc0\xbb\x3d\x19\x97\x67\x5d\
-\x27\xa1\xc2\x62\x01\x50\xa2\x76\xab\x46\x6a\x4e\xd9\xdb\x14\xd8\
-\x02\x45\x75\x2e\x5f\x7b\xd7\x2b\x06\xa9\x4c\x2e\x5f\x91\x4a\x81\
-\x00\x98\x5d\xa9\x58\xd4\xa0\xa8\x9b\xee\x3a\x4d\x49\xe8\x51\xc1\
-\x96\xf6\x66\xf3\x5d\xee\xdf\x5f\x9a\x58\x00\x94\xb8\x83\x6f\xe8\
-\xec\x6c\xc6\x6e\x55\xc1\xad\xc8\xd1\xd3\x02\xcf\x1d\x17\x9c\x19\
-\x62\xd3\xa2\x4b\x23\x18\xdf\x97\x7f\x51\x83\xa2\x9a\x53\xfd\x85\
-\x30\x2a\x82\x7b\x8c\x31\x77\xb5\x35\xc9\xb0\xeb\x30\xe4\x0e\x7b\
-\x69\x02\x00\xec\xeb\xd1\xc5\x06\xba\x15\xaa\x9f\x98\xea\x6b\x1d\
-\x3c\x2d\x38\x7a\x96\x4d\x8b\x3e\x58\xe3\x5b\xf7\xf8\x6b\x38\xf0\
-\x17\x84\x40\x1e\x35\x9e\x7c\xb1\xad\x49\x8e\xb9\xce\x42\xee\xb1\
-\x97\xa6\xb7\xe9\x7a\x3d\x73\x03\x60\xbe\x0a\xa0\x6d\xb2\xaf\x71\
-\xbc\x5f\xb0\xbf\x87\x4d\x8b\xde\xdb\xf4\x32\xa0\xad\x49\x31\x7b\
-\x26\xef\xf1\x17\x82\x02\xfb\x45\xcc\xed\xdc\xcc\x87\x2e\xc6\x5e\
-\x9a\xde\x61\x87\xaa\xb7\xa0\xc7\xff\xb4\x40\xee\x04\x30\x67\xa2\
-\x3f\xdf\x37\x02\x3c\x7d\x8c\x4f\x02\xd0\x3b\x09\x80\xf9\xf5\x8a\
-\x2b\x1b\x15\x1e\x9b\x48\x21\x1c\x83\xea\xe6\x64\xdc\x7b\x80\xf7\
-\xf9\xe9\x77\xb1\x00\xa0\xf7\xd4\xdd\xad\x65\xa3\xb5\xf6\xcf\x45\
-\xb1\x05\x13\xd8\x56\x38\xed\x03\xff\xfd\x32\x7b\x77\x7a\xbb\x32\
-\x0f\x68\x8f\x2b\x9a\x78\xd5\x5f\x08\x6f\x02\xf8\xea\xb9\xb4\xb9\
-\x97\xbb\xf8\xd1\x7b\x61\x01\x40\x1f\xe8\x85\xd3\x3a\xc3\xf7\xed\
-\x5f\x61\x7c\x33\xa1\x9a\x4b\xf9\x99\x9d\x87\x0c\x0f\x06\xa2\xff\
-\x53\x11\x01\xae\x99\x6b\x51\x55\xee\x3a\x49\xb8\x29\x70\x5e\x80\
-\x6f\xa5\x2b\xcc\x3f\xad\xae\x97\x41\xd7\x79\xa8\xb8\xb1\x00\xa0\
-\x4b\xf6\x4c\xaf\xce\x9c\x9e\xb6\x7f\x89\x4b\x28\x04\x76\x1d\x36\
-\x48\x65\x0b\x93\x8b\x8a\x5b\x45\x04\x58\x3b\xcf\xa2\xb2\xcc\x75\
-\x92\xf0\xba\x30\xf0\x47\xb2\xe6\xee\xab\xe7\x48\xbf\xeb\x3c\x14\
-\x0c\x2c\x00\x68\xc2\x2e\x2a\x04\xbe\x0c\xa0\xf6\xdd\xbe\xe7\xf1\
-\xc3\x06\xa3\x2c\x00\x4a\x9e\x08\xb0\xe6\x32\x45\x63\x25\xa7\x83\
-\xf2\x81\x03\x3f\x4d\x05\x0b\x00\x9a\xb4\xfd\xc7\xb4\x06\x65\xf6\
-\x6f\x14\xf8\x22\x80\xba\x0b\x5f\x3f\x33\x24\x78\xee\x38\x9b\x16\
-\x01\x0b\xea\x15\x57\x35\x71\xf0\xcf\x83\xb3\x50\xdc\x6b\xc7\xcc\
-\xb6\xe5\x0b\xe4\x9c\xeb\x30\x14\x4c\xec\xa5\x69\xca\x76\x1f\xd3\
-\x8a\xea\xa8\xff\x47\x2a\xf2\x0f\x02\x5c\xf1\x9b\x53\x82\xd7\xfa\
-\xd8\xb4\x4a\x9d\x27\xc0\x8d\x8b\x2c\xca\x23\xae\x93\x84\xca\x69\
-\x05\xee\xd3\x51\xf3\x35\x0e\xfc\x34\x55\xec\xa5\x29\x67\x54\xd5\
-\x74\xf5\xf8\x9b\x0e\x9d\x31\x5f\x7f\xb5\x57\x26\xfc\xf8\x20\x85\
-\x4b\x53\xa5\x62\xf5\x1c\x5e\xfd\xe7\xc8\x11\x11\x7c\xa3\x72\xd8\
-\x7c\x7b\xe1\x42\x19\x73\x1d\x86\xc2\x81\x05\x00\xe5\xc5\xbf\x77\
-\xe9\xad\x83\x29\xbd\xb3\x77\x44\x12\x3c\x18\xa8\x34\x2d\x6e\x54\
-\x2c\x6e\xe0\x9b\x3f\x15\x2a\xb2\x1b\x6a\xbf\xd6\x1e\xf3\x1e\x15\
-\x11\xfe\x31\x29\xa7\x58\x00\x50\x5e\xfd\xdb\x41\xfd\x70\x6a\x08\
-\xff\xdc\x3b\xa4\x6d\xbe\xb2\xb9\x95\x92\xe5\x2d\x8a\x96\x6a\x8e\
-\x59\x93\x90\x86\xc8\x23\xd6\x97\x7b\x96\x27\x64\xaf\xeb\x30\x14\
-\x5e\xec\x91\xa9\x20\x76\x74\xeb\xe5\x83\x43\xf8\xe6\x9b\x23\xb8\
-\x7e\x24\x93\x9b\x43\x87\xa8\xb8\xad\x4c\x28\x62\x55\x2c\x00\x26\
-\xe0\x0d\x05\xbe\x5d\x16\x31\xff\x72\xd5\x6c\x79\xc3\x75\x18\x0a\
-\x3f\x16\x00\x54\x50\xbb\x77\x6b\xc5\x89\x99\xf6\x2b\x7d\xa3\xf2\
-\xd9\xfe\x94\xcc\xe2\xed\x81\xf0\xea\x88\x2b\x12\x35\x7c\x83\x2f\
-\x41\xa7\x0a\xee\x1f\xf3\xcd\xf6\x6b\x12\x92\x72\x1d\x86\x4a\x07\
-\x0b\x00\x72\xe6\x27\x2f\xe8\xba\x33\x29\xbd\xeb\xcc\xb0\xac\x49\
-\xfb\xe0\xde\xc1\x21\xb3\xa4\x51\x71\x05\xd7\x00\xbc\x3b\xc1\x39\
-\xb1\xf8\x4f\x5f\xcd\x37\x96\x27\xe4\xa0\xeb\x38\x54\x9a\x58\x00\
-\x90\x73\x3b\xba\xb5\x2e\x95\xb2\xff\x38\x30\x2a\x9f\xec\x4b\x49\
-\x2d\x67\x05\xc2\x21\x51\xad\xe8\x68\xe1\x9b\x79\x11\x85\xea\xd3\
-\x00\xbe\x67\x8d\xf7\xd0\xf2\x98\x8c\xb8\x0e\x44\xa5\x8d\x05\x00\
-\x15\x95\x07\xbb\x74\xf9\xf9\x2c\xb6\xf6\x8f\xe0\x86\xe1\x34\xa2\
-\xae\xf3\xd0\xe4\x55\x96\x01\x37\x2c\xe4\x01\x74\x00\x5e\x07\xf0\
-\x80\x5a\xf3\xdd\x8e\x84\x1c\x71\x1d\x86\xe8\x02\x16\x00\x54\x94\
-\x76\xa8\x7a\x63\x9d\xf6\x4b\xe7\xd2\xf2\x85\xfe\x94\xcc\xcf\xf8\
-\x6c\xab\x41\x23\x00\x6e\x5a\x62\x11\x2d\xc1\x9b\x3b\x0a\x9c\x17\
-\xc5\x4f\x44\xec\xf6\x65\xb1\xc8\x13\x3c\x8a\x97\x8a\x11\x3b\x55\
-\x2a\x7a\xf7\xed\xd3\xea\xa8\xb5\x7f\x3b\x92\x35\x9f\xea\x1b\xc1\
-\x9c\x2c\xbb\xd2\xc0\x58\x37\xd7\xa2\x7e\x86\xeb\x14\x05\xe3\x03\
-\xb2\x1b\x6a\xb7\x97\x97\x79\x3f\x6e\x6d\x94\x21\xd7\x81\x88\xde\
-\x0f\x0b\x00\x0a\x94\x07\xf7\xeb\xdc\x54\xc6\x6e\x1e\x4c\x9b\x8f\
-\x0c\x8c\xa0\x9e\xb5\x40\x71\xbb\xba\x59\x31\xbf\x2e\xd4\xeb\x00\
-\xb2\x80\xfc\x4a\xc4\xee\x28\xf3\xbd\x87\x5b\x13\xd2\xe7\x3a\x10\
-\xd1\xa5\x62\x01\x40\x81\xf5\x1f\xcf\xeb\xfc\x51\xb5\x5f\x1e\x4e\
-\x9b\x8f\xf6\xa7\xd0\xe4\x87\x7a\x9c\x09\xa6\x39\xb5\x8a\x64\x2c\
-\x74\x6f\x8c\x0f\xe8\x1e\x11\xf9\x51\xc4\x33\x3f\xe4\x33\xfb\x14\
-\x54\x2c\x00\x28\x14\x1e\xec\xd2\xd8\x98\xda\xbf\x1f\x1a\x35\x1f\
-\xeb\x1f\x45\x0b\xd7\x0c\x14\x87\xc6\x4a\xc5\x35\xe1\x38\x0f\x60\
-\x10\x22\xbb\x14\xf6\xa7\x9a\xf2\x76\xf2\x20\x1e\x0a\x03\x76\x92\
-\x14\x3a\xdb\x5e\xd5\xf2\xca\x7e\x7c\x7e\x2c\xab\x7f\x7a\x2e\x8d\
-\x65\x23\x69\x29\x73\x9d\xa9\x54\xd5\x4d\x03\x3e\x34\x3f\x98\x37\
-\x6a\x44\xf1\x1a\x80\x5f\x5a\xd1\x47\x2b\x06\xbc\x5d\xad\xad\x92\
-\x76\x9d\x89\x28\x97\x58\x00\x50\xe8\x3d\x70\x40\x6f\x1c\x19\xd3\
-\x2f\x0c\xa7\x65\xed\xe0\x98\x36\xf8\x96\xcd\xbe\x50\x66\xcd\x00\
-\xd6\xce\x0d\x4c\x01\x30\xa4\x22\x4f\x19\xe8\x2f\xc4\x9a\x9f\x2d\
-\x6b\x91\x57\x5c\x07\x22\xca\x27\xf6\x84\x54\x52\x76\xee\xd3\xe9\
-\xc3\x11\xfb\x67\xc3\x19\xf3\x27\xe7\xc7\xd0\x31\x90\x42\x95\x0d\
-\xc5\x0c\x75\x71\x8a\x57\x29\x56\x24\x8a\xf6\x0f\xec\x43\xb5\x4b\
-\x8c\xfc\x5c\xc5\xfc\xa2\xbc\x0f\xbf\xe6\x55\x3e\x95\x12\x16\x00\
-\x54\xd2\xfe\xb5\x4b\x63\x9a\xb5\x9f\x19\xf3\x65\xe3\x48\x5a\xae\
-\x1c\x4a\xa3\x8a\x3b\x11\xe6\xce\x8a\x84\x22\x5e\x3c\x07\x02\xf9\
-\x00\x0e\x00\x78\x56\x54\x9f\x29\x53\xef\x09\xae\xda\xa7\x52\xc6\
-\x02\x80\xe8\x22\x8f\x1c\xd4\xd9\xe7\xc6\xec\x5f\xa4\x7c\xb3\x71\
-\x38\x8d\xa5\x43\x63\xa8\xcd\x5a\x7e\x4e\x26\x23\x56\xa5\x58\xe9\
-\xf6\xea\x7f\x00\x90\xbd\x0a\xdd\x23\xb0\xcf\x78\x5e\xe4\xd7\x6d\
-\x4d\x32\xec\x32\x10\x51\x31\x61\xc7\x46\xf4\x3e\x76\x74\x6b\x59\
-\x6a\x14\x9b\xc6\x7c\xfb\xd1\x74\x46\x56\xa7\xb2\x32\x67\x28\x8d\
-\x69\x9c\x25\x78\x7f\x65\x1e\x70\xdd\xe5\x16\x15\x91\x02\xfd\x42\
-\x41\x06\x8a\x6e\x28\xf6\x0a\x74\x8f\x35\xde\xde\xf6\x66\xbc\x2c\
-\x22\x7c\xa7\x88\xde\x03\x0b\x00\xa2\x09\xda\xb6\x47\xab\x66\x18\
-\xfc\x41\xd6\xda\x0d\x59\x6b\x92\xa9\x2c\xe6\x8c\xa4\x51\xc9\x99\
-\x82\x71\x02\x60\xd5\x1c\x45\x53\x65\xde\xc6\xde\x21\x00\x87\xa1\
-\x38\x24\x06\x9d\x3e\x4c\x67\xda\x47\x27\x8f\xd2\x25\x9a\x18\x76\
-\x58\x44\x39\xb0\x79\xb3\x9a\xcb\x6e\xc2\xea\x2c\xec\x86\x6c\x56\
-\x56\xa5\xad\x2c\x4c\x67\x31\x3b\x95\xd1\x19\xbe\x96\xd6\xc7\x6c\
-\xe1\x2c\x45\xeb\xec\x9c\x0c\xfe\x63\x00\x8e\x42\xa4\x5b\x55\x0f\
-\x19\xd5\x6e\xf1\xbc\x43\x6d\x4d\x78\x89\x7b\xeb\x13\x4d\x5d\x69\
-\xf5\x4c\x44\x05\xb6\x63\x87\x7a\xa9\xcb\xb1\xd2\xfa\x76\x6d\x06\
-\x26\x99\xce\xe0\x8a\x6c\x56\x63\x63\x56\x6a\xc6\x7c\x94\x67\x43\
-\xb6\x61\x51\xed\x34\x60\xdd\x7c\x8b\x09\x9c\xff\x33\x08\xe0\x35\
-\x88\x1c\x81\xea\x11\x11\x3d\x0a\x5f\x8f\x18\x89\x1c\xb9\x3a\x8e\
-\xd7\x39\x85\x4f\x94\x3f\xa1\xea\x7c\x88\x82\xe6\x07\x7b\x75\x91\
-\x6f\xd0\x61\xad\x6d\xf3\x55\x16\x66\x14\x2d\xea\x4b\x43\x56\x51\
-\x93\xce\x62\xfa\x98\x8f\xb2\xa0\xdc\x5a\x10\x01\x3e\x3c\xcf\xa2\
-\x7a\x1a\x00\xc0\x02\xe8\x55\x45\xaf\x88\xbc\x01\xd5\x1e\x05\x4e\
-\x08\x70\x42\xa1\x27\x55\xbd\xe3\x5e\x16\x27\x92\xf3\x64\xc0\x6d\
-\x6a\xa2\xd2\x15\x88\x8e\x85\xa8\x94\x6d\xee\xd6\xca\xc4\x18\x16\
-\x4b\x16\x0b\x44\xec\x65\x6a\x4d\xb3\x2f\x68\xb0\x16\xf5\x59\xd5\
-\x3a\xa8\x54\x66\xad\xce\x54\x48\xd4\x2a\xca\xad\x22\x6a\x15\x9e\
-\x6f\x35\xaa\x16\xc6\x42\xde\x76\x41\x9e\xb1\x90\x8b\x17\x31\x46\
-\x0c\x60\x0c\x14\x6f\x7d\x2d\xe2\xa9\x42\x45\x01\x58\x23\xb0\x62\
-\xd4\x37\x40\xd6\x33\x92\x8e\x18\x8c\x1a\xd1\x51\x4f\x64\xd0\x18\
-\x1d\x14\xc1\x80\x81\xf6\x89\x67\x7a\x23\x6a\x4f\x2e\x69\x34\x4f\
-\xd9\x72\xf4\xae\x68\xc0\x19\x4e\xd3\x13\x15\xb7\xff\x05\x82\x22\
-\xaa\x2f\x56\xbc\x39\x1a\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\
-\x60\x82\
-\x00\x00\x07\xda\
-\x89\
-\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
-\x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\
-\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
-\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\
-\x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
-\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
-\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x07\x57\x49\x44\
-\x41\x54\x58\x85\x9d\x97\x6b\x50\x54\xe7\x19\xc7\x7f\xef\xd9\x0b\
-\xec\x72\x0f\x77\x64\xb9\x09\x2a\xa0\x46\x44\xb4\x01\x43\x84\x18\
-\x31\xd5\x54\x21\xd3\x66\xd2\xcc\x34\x56\x33\xd3\x69\xcd\x4c\x6a\
-\x9c\xd6\x74\x3a\xcd\x74\xfc\xd0\xd4\xde\x92\x69\xda\xa9\xe9\x34\
-\x37\xe3\x2d\x35\xa3\x24\xc6\x4b\xea\x2d\x4a\x0c\x04\x04\x2d\xa0\
-\x48\x50\x50\x58\x40\x60\x57\xb9\x2e\xcb\x5e\xce\xdb\x0f\xe8\xba\
-\xcb\x6e\x84\xf4\xf9\xf4\x9e\xe7\xfd\x3f\xcf\xff\x7f\xce\x79\xce\
-\xf3\x3e\x47\x30\x43\xfb\xb8\x49\xc6\x0f\x3b\xd4\xad\x23\x0e\x65\
-\x8d\xdd\x29\x53\x9c\x6e\x61\xb0\xbb\xd1\x48\x09\xb3\x22\xa4\x3d\
-\x3f\x49\x6d\x92\x42\x5c\x12\xc8\xd3\x36\xbd\xe6\xc8\xf2\x58\x31\
-\x32\x93\xbc\x62\x3a\xc0\xbe\x26\xb9\xc2\x3a\xca\x5b\xd6\x51\xe6\
-\xb8\xe5\xa4\x4f\xaf\x01\x83\x0e\x0c\x3a\x89\x51\x37\xb9\xce\x8a\
-\x91\xde\x61\x36\x60\x9f\x50\x95\x1d\x79\x26\xd1\xf6\x7f\x09\xf8\
-\xb2\x4b\x1a\x5a\x7a\xe5\x71\xf3\x90\x28\x76\x4b\xdf\x3d\x8d\x80\
-\xd2\x4c\x15\xa3\x0e\xae\xf4\x0b\x86\xec\x50\x98\x2a\xfd\x93\x08\
-\x9c\x52\xf2\xc6\xb0\x43\x79\xb5\x24\x5d\xd8\x67\x2c\xe0\x40\x93\
-\x2c\x1c\x9b\x90\x95\x42\x10\xdb\x3f\x2a\x18\x99\xb8\xbf\x17\x16\
-\x04\x2e\x15\xe6\xc4\x4a\x12\xc3\x24\xc1\x5a\xff\x78\x55\x95\x28\
-\x8a\x77\x6a\x59\xa3\x3a\x35\x15\x4b\xd2\x44\xef\xb4\x02\x76\xd7\
-\xcb\x67\x1c\x2a\xef\x76\x0d\x61\x88\x36\x4a\xcc\x7d\x23\xdc\xea\
-\xed\x62\xc0\x62\x21\x42\x19\x21\x44\xa7\x62\x34\x04\x93\x9e\x1c\
-\x4f\xf1\xb2\x05\x3e\xb1\x36\x9b\x9d\x77\x0e\x7c\xc6\xac\x84\x18\
-\xca\xcb\x8a\xa6\xa6\x36\x4b\x55\x59\x93\x6f\x12\x8d\xde\x4e\x1f\
-\xfd\x7b\xea\xe4\xca\x1b\x83\xec\x1d\x19\x9f\x50\x9a\x2f\xd6\xd0\
-\x78\xa1\x1a\x4b\xbf\x9f\x68\x00\x72\x32\x53\x3d\x02\xa4\x94\x9c\
-\xa8\x6a\xe0\xef\xbb\x3e\xc1\x3a\x38\xcc\x4b\x1b\xcb\x03\x85\x24\
-\x0b\x45\x3d\x72\xa4\x5d\x2e\x5f\x93\x21\x6e\xfa\x09\x78\xeb\x82\
-\x8c\x30\x8f\xca\xc3\xad\x5f\xb7\x2a\x47\x0f\xee\xc6\x36\x3a\xa3\
-\x22\x06\x60\xfb\x5f\x77\x73\xa2\xaa\x61\x26\xd0\xe4\x20\x97\x7a\
-\xec\xcb\x2e\x99\x5f\x68\x12\xe3\x3e\x02\x54\x87\x3c\x59\x75\xae\
-\x2a\xf8\xf4\xf1\x83\x20\x03\x14\xd4\x03\xac\xbb\xd7\x3a\x63\x6c\
-\x98\x81\xec\xd6\x01\xb9\x0f\x58\x0f\xa0\x00\x7c\x78\x49\x96\xd4\
-\x5d\xbe\xbe\xe4\xcc\x14\x72\x45\x11\xac\x2c\xca\x63\xfb\xd6\x0d\
-\x3c\xb3\xf6\xb1\x6f\x25\x2a\x90\xb9\x25\x7c\x7e\x5d\xa1\xdd\x2a\
-\xd6\xfd\xab\x4e\x2e\x85\xbb\x4f\x20\x54\xe7\xde\x72\xfe\x3f\x87\
-\x90\x5e\xe4\xc6\xe0\x20\x5e\xdb\xb6\x89\xfc\x05\x59\x00\x58\xef\
-\x0c\xcd\x88\x24\x3b\x33\x85\xa5\x0b\xe7\x06\xdc\x33\x0f\x0a\xc6\
-\x1c\x93\x6b\xbb\x4b\xbe\x0b\xe4\x6a\x6b\xac\x32\xfc\x7a\x73\xc7\
-\x13\x5d\x66\xb3\x0f\xf8\x95\xcd\xcf\x7a\xc8\x67\x62\x11\xe1\x21\
-\xfc\xe4\x87\x6b\x59\x5b\xba\x0c\x97\xea\xa6\xa1\xb9\x8d\xb6\x1b\
-\x3d\x58\x07\x87\x09\x35\x18\x50\x43\xe2\x08\x49\xc8\x41\xa7\xd3\
-\x03\x70\xc7\x26\xb2\xf7\x36\xc9\x78\xad\xde\xee\x5e\xd3\xd0\x7c\
-\x2d\xd8\x3b\xd9\xdc\xd9\x26\x4a\x1f\x79\x78\xc6\xe4\xab\x4b\x0a\
-\x58\x59\x94\x47\x70\xb0\x9e\xbd\x1f\x9f\x66\xdf\xe1\x33\x0c\x0d\
-\x8f\xf9\xe1\x8c\xa1\x61\xac\x5e\xf7\x2c\x19\x73\x72\x71\xba\x11\
-\xb6\x09\xf5\x57\x0a\x88\xd2\x76\x73\x9f\x0f\xf0\xd1\x82\xf9\xd3\
-\x92\xce\x9f\x9b\xea\x59\x57\x94\x15\x71\xe5\xeb\x9b\x3c\xbf\xe5\
-\x0f\xec\xdc\xf3\x69\x40\x72\x00\xdb\xe8\x08\x95\xfb\xdf\xc6\xdc\
-\x79\x1d\x00\xa7\x53\x79\x5c\xdb\x37\xc2\xf2\x6e\xcb\xb8\x0f\x30\
-\x29\x2e\xfa\x1b\x89\x33\x52\x92\xd8\xb2\xa9\x9c\xbc\xdc\x4c\x00\
-\x7a\xfa\xac\xbc\xf9\x5e\x25\x55\x75\xcd\xd3\x8a\x06\x50\xdd\x6e\
-\x4e\x1e\x3e\xc0\x86\x9f\x6d\xc3\xe6\x24\x4d\x7b\xb9\x5f\xc9\x9c\
-\x70\xf9\x36\x44\xad\xce\xbf\xbf\x86\x87\x18\xf9\xf9\xc6\x0a\xca\
-\xcb\x8a\xd0\x68\x14\x26\x1c\x4e\xf6\x54\x9e\x62\x4f\xe5\x69\x26\
-\x1c\x4e\x0f\x4e\xa7\xd5\xf0\xc4\xa3\xf9\xac\x5e\x51\x80\x29\x31\
-\x96\x7e\xeb\x20\x7f\xd9\x75\x92\xd6\x96\xfb\x02\x2d\xfd\xbd\xf4\
-\x76\x77\x62\x48\x4f\x35\x68\x8b\xd2\xa4\x7a\x2c\xd2\x80\xf7\x91\
-\x75\xab\xdf\xff\xbb\x2e\x7b\x6c\x89\x67\x7d\xae\xb6\x99\xbf\xbd\
-\x5f\x49\x4f\x9f\x2f\x6e\x59\xde\x3c\x5e\xfa\x71\x39\x29\x49\x71\
-\x1e\x5f\x74\x54\x04\x6b\x7f\xb0\x09\xeb\xce\x3f\x62\xe9\xeb\xf1\
-\xf8\xcd\x37\xae\x91\x9c\x92\xaa\xd1\x06\x69\x24\x0b\x33\x62\xa9\
-\xae\xb9\x9f\xe8\xf8\xd9\x7a\x2a\xbe\xbb\x82\x6b\x56\x0d\x63\x4e\
-\x88\x0c\x86\xb8\x50\xc9\xcd\x9e\x01\x3e\xd8\x77\x90\xaf\x2e\x5d\
-\xf5\x21\x4e\x8a\x8f\xe6\xc5\xe7\xd7\x53\xbc\xd4\xbf\x76\x14\x01\
-\x39\x09\x82\xdc\x85\x4b\x38\x7b\xe2\x13\x8f\xdf\x6a\xe9\x07\x26\
-\xfb\xc0\x70\xde\xfc\xcc\x18\xef\xa0\xf6\xce\x1e\x5e\xdc\xfe\x0e\
-\x8b\x8b\x56\x11\x16\x1e\x8e\xd5\xd2\x4f\x4b\x63\x3d\x57\x9b\xea\
-\x71\xbb\xdd\x1e\x5c\x90\x5e\xc7\x73\xeb\x4b\x79\x6e\xfd\xe3\x04\
-\xe9\x75\xb8\xdd\x2a\x87\x3e\x3b\x4f\x58\x88\xc1\xe7\x89\xcd\x8d\
-\x95\xa4\x25\x45\x71\xd6\x8b\xc3\x31\x31\x8e\x5e\x83\x5b\x8b\x94\
-\x1d\xb9\x59\xa9\x31\xe9\xa6\x04\x3a\xba\x6e\x79\x00\x57\x5b\x2e\
-\x73\xb5\xe5\xb2\xdf\x1d\xdd\xb3\x45\x0b\x17\xb0\x75\xd3\x3a\xd2\
-\x67\x4d\x16\xec\xc5\xcb\xd7\x78\xfd\xed\x43\xb4\x77\xf6\x04\x3c\
-\x8c\x92\xc3\x5d\x3e\xd7\x1a\x54\x52\x22\xa5\x59\x3b\x39\x46\x51\
-\xf0\xf2\x0b\x4f\xb3\x65\xfb\x4e\x5c\x5e\x77\x18\xc8\xa2\xa2\x63\
-\x29\x7d\xb2\x82\xf4\xac\x1c\xae\xd9\x20\x4d\xaa\xbc\xf9\xde\x21\
-\xfe\x7d\xe4\xdc\x03\xe3\x7a\xfb\x2c\x3e\xd7\x19\x09\x46\xb2\x63\
-\xe5\x71\x45\x20\x4f\x03\xe4\xe5\x66\xf2\xbb\x6d\x1b\x79\x28\x32\
-\x3c\x60\x02\x83\x31\x94\xe2\x95\x4f\xb1\x61\xf3\x2b\x24\x26\xa7\
-\x71\xa9\xee\x0b\xc6\x1c\xd0\x71\x47\xd0\xdc\x7a\x33\x60\x8c\xb7\
-\x55\xd5\xfa\x7e\xa6\x69\xc9\xf1\x08\xe4\x29\x6d\x90\x4e\xf3\xe9\
-\x84\x53\x1d\x03\x42\x0a\x17\xe7\xf0\xcf\x3f\xfd\x9a\x5d\x27\x5a\
-\x19\xb4\x0e\x60\x9f\x18\x27\x24\x34\x9c\xb8\x84\x59\xcc\x32\xa5\
-\x23\x14\x85\xa6\x86\x1a\xaa\x4e\x1d\x26\x32\x32\x86\x45\x05\xcb\
-\x69\xec\x15\x0c\x06\x1c\xb6\xee\xdb\xc9\xf3\x17\x69\xbb\xd1\xed\
-\xe3\xcb\x5f\x90\x35\xae\xd7\x6b\x8e\x69\x73\xe3\xc4\x68\x43\xb7\
-\x7b\x3f\xb0\x09\x40\x55\x82\x98\x93\xe3\xdf\x86\x6f\x0f\xf4\x71\
-\xf4\xd0\x6e\x6e\x75\x77\x02\x10\x19\x79\xbf\x6e\xa7\x9e\xde\xa3\
-\x5e\x23\x5c\xed\x7f\x5b\xd9\xf1\x8f\xfd\x3e\xfb\x39\x99\xa9\x64\
-\xa5\x27\xef\xce\x8d\x13\xa3\x5a\x00\xa1\x2a\x3b\xa4\x46\xfd\x11\
-\x12\x5d\x7c\xa8\x24\xca\x20\xb8\xe3\xdb\x1c\xb9\xd1\x7e\xd5\x43\
-\x3e\x9d\x35\xb6\xdf\xe6\xe4\xf9\x8b\x9c\xa9\xbe\xc4\xb9\xaf\x9a\
-\x7c\x4e\x59\x45\x51\xd8\xbc\xe1\x7b\x4e\x55\xab\xfc\x1e\xee\x1e\
-\xc7\x79\x26\xd1\x56\xdf\xed\x7e\x43\xc0\x2f\x34\x0a\x2c\x4a\x54\
-\xf9\xbc\x43\xf9\xb6\x73\x89\xc7\xea\xaa\xcf\x52\x57\xed\xef\x57\
-\x14\xc1\xcb\x2f\x3c\xcd\xc3\xf3\x32\xfe\xbc\x38\x5e\xb4\xc3\xdd\
-\x81\x04\x60\xd8\xa1\xbc\x0a\xb2\x06\x20\xc2\x00\x69\x91\x0f\x66\
-\x8f\x88\x8e\x79\xe0\xfe\x54\x8b\x8e\x0c\xe7\xf5\xdf\xfc\x94\xf5\
-\xab\x1e\xa9\x0e\xb3\x29\xbf\xbd\xe7\xf7\x34\xfd\x92\x74\x61\xaf\
-\xed\x97\xe5\x5a\xa7\x5a\x0b\x98\xe6\xc5\x49\x86\xec\x82\xdb\x53\
-\x5e\x45\x48\x68\x38\x2b\xca\xd6\x91\xbd\x20\x7f\x5a\x52\x21\x04\
-\xb3\x53\x92\x78\x6a\xe5\x77\x78\xb2\xa4\x00\x63\x70\x50\x8f\xa2\
-\x2a\xdf\xcf\xca\x12\x9e\x2a\xf1\x1b\xcb\xeb\xbb\xe4\x42\xa1\xa8\
-\x47\x80\x64\x09\xf4\x0c\x09\xea\xcd\x82\x86\xda\x2f\x18\xbc\x63\
-\xa1\x70\xc5\x6a\xf4\x41\x3e\xe3\x03\x6d\x2d\x8d\xdc\xb6\xf4\x31\
-\x61\xb7\x13\x1f\xae\x65\x76\x82\x91\xa4\x84\x68\xb2\x67\x9b\x88\
-\x8a\x08\xbb\x07\xeb\x52\x55\x65\xcd\x12\x93\x68\xf2\x11\x19\x48\
-\x79\x43\xaf\x8c\x45\x75\x7f\x04\xa2\x18\xe0\x5c\x87\x82\x75\x4c\
-\x22\xc4\xb4\x7f\x72\x14\xa7\xab\x3c\x64\x9c\xea\x95\xd5\x2e\x9d\
-\xa6\x62\x69\x9c\xb8\x35\x75\x47\x99\xea\x00\x58\x9c\x28\x06\xc2\
-\x6c\x9a\x55\xc0\x76\x60\x4c\xaf\x99\x9e\x3c\x3a\x04\x16\x25\xca\
-\xa9\xe4\x0e\x24\xaf\x85\xd9\x34\x25\x81\xc8\x61\x06\x3f\xa7\xb5\
-\xfd\x32\xe1\x4a\xa7\xfc\xa8\x73\x50\x14\xba\xd4\xc0\xf8\xb4\x28\
-\xc9\xa2\x24\x9f\xa2\x1d\x13\x92\x3d\x2e\x9d\xb2\xa3\xe0\x6e\xb5\
-\x7f\x93\x4d\xff\x4c\xef\xda\x07\x17\x64\xa2\x4b\xaa\xbf\xb4\x39\
-\x45\x99\xdd\x25\x4c\xe3\x4e\x8c\x6e\x15\x45\x05\xe6\x27\xc8\x91\
-\xcc\x68\xf5\x8a\x44\x5c\x94\xaa\x3c\x13\xac\xd7\x1c\xcd\x8d\x13\
-\xa3\x33\xc9\xfb\x3f\xd5\x8a\xed\x13\x27\x02\xa0\xfb\x00\x00\x00\
-\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
-\x00\x00\x10\xb6\
-\x89\
-\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
-\x00\x00\x40\x00\x00\x00\x40\x08\x06\x00\x00\x00\xaa\x69\x71\xde\
-\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
-\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\
-\x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
-\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
-\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x10\x33\x49\x44\
-\x41\x54\x78\x9c\xcd\x9b\x79\x7c\x55\x45\x96\xc7\xbf\x75\xef\xcd\
-\xcb\xbe\xef\x8f\x40\xd8\x95\x1d\x42\x02\x88\x0e\x04\x11\x05\x45\
-\x11\x5a\x47\xe4\x23\xbb\x1f\x46\xdb\x71\x9b\xd6\x19\x9d\xed\xe3\
-\xa7\xfb\xd3\xfd\x69\x5b\xc7\xb5\x6d\x07\x51\x36\x31\x2a\x22\x88\
-\x8d\xf4\x20\x9b\x18\x10\x24\x31\x21\x40\x80\x84\x26\x10\x42\xf6\
-\x7d\x7b\xc9\x5b\xee\xad\xf9\x23\x24\x64\x79\xb9\x79\x59\xb0\xfb\
-\xf7\xd7\xab\x3a\x55\xa7\x4e\x9d\x5b\x75\xea\xd4\x39\xf5\x04\x37\
-\x19\x9b\x4f\xc9\x71\x86\xcb\x58\xed\x70\x29\x49\x0e\x9d\xa1\x76\
-\x9d\x08\xa7\x0b\x8b\x21\x51\x9d\x06\x42\x00\x9a\x82\xf4\x52\x71\
-\xcd\x1c\x22\x73\xfc\xbd\xe5\x19\xe0\x02\x42\x66\xab\x8a\x9a\x3a\
-\x29\x46\x94\xdd\x4c\xf9\xc4\x80\x73\x94\x52\x6c\xce\x60\x55\x93\
-\x93\xa7\x6a\x9b\x18\x6f\x73\xe2\xed\x69\xd7\xd1\x91\x92\xb1\x51\
-\xb2\x23\x37\x49\xb6\x50\xf8\x16\x94\x4f\x13\xac\x22\x7d\xa0\xc5\
-\x1d\x30\x05\x6c\xc8\x92\xc3\xa4\x9d\x77\x6a\x6c\xcc\x6b\x70\x78\
-\x3e\x69\x21\x20\x36\x50\x52\xd1\x28\x98\x33\xd2\xc0\x57\x33\x6d\
-\x7e\x4e\xc0\xa6\x46\x8b\xb2\xfe\x8e\x48\x51\xdf\x6f\xa1\x19\x00\
-\x05\x6c\x3d\x2e\xc7\xd4\x49\x36\x55\xd9\x98\xe6\x32\xcc\xf9\xc5\
-\x04\x4a\xa2\x03\xa1\xb0\x16\x62\x03\x41\x15\x10\xe6\x27\x29\xaa\
-\x13\x54\x35\x41\x62\x9c\xc4\xa2\x7a\x34\x6c\xb5\x84\x77\xbc\x5c\
-\xca\xdb\x13\xe3\x45\x75\x7f\xe4\xef\xb3\x02\x5e\xc9\x96\x96\xe8\
-\x3a\x63\x4b\xb9\x4d\x79\xc4\xa5\x7b\xc6\xc7\xd7\x0b\x46\x86\x4b\
-\xe2\x82\x25\x42\x80\x45\x85\x9c\x72\xc1\xf9\xb2\x96\xee\xe1\xfe\
-\xf0\x0f\x43\x8d\xde\x88\x51\x25\x04\xbf\x9e\x1c\xab\xbc\x2b\x84\
-\xe8\x55\xc7\x56\xf4\x49\x01\x3f\x15\xc8\x91\xb9\x55\xc6\xbe\xf3\
-\xa5\xca\xf0\xde\xf4\xb3\xa8\x70\xf7\x68\x03\x4d\x69\x29\x4b\xa0\
-\xbc\x41\xd0\xec\x82\xba\x66\xa8\x77\xc0\x6d\x43\xa4\x29\x0f\xf7\
-\x90\xc7\x0c\x5d\x5d\x99\x38\x44\x5c\xea\x6d\xcf\x5e\x2b\x60\xc7\
-\x59\xfd\xf9\x9a\x26\xf1\xfb\xaa\x46\x61\x89\x0d\x92\x54\x37\x41\
-\x45\xa3\x67\x6c\x84\x80\xd1\x11\x12\x45\x80\x97\x0a\xc3\xc3\xfa\
-\x32\x59\xf7\x90\x50\x2f\x90\xeb\x12\x06\x69\x9f\xf5\xa6\x9f\xb9\
-\xc9\x69\x87\xed\x52\xaa\xae\x2c\xf6\xf8\x6a\x72\x7e\x4e\xbd\x20\
-\xd2\x5f\x32\x2c\x54\xe2\xd4\x05\x15\x8d\xdd\xf7\xf3\xb7\xc0\x90\
-\x10\x49\x49\xbd\x40\x08\xd0\xd4\x16\xa3\x17\x60\xf1\x5c\x48\xbb\
-\xc3\x89\xb7\xc5\xcb\xb4\x8d\x80\x40\x10\x9f\x66\x14\xe9\x49\x53\
-\x62\x95\x17\x3d\xdd\x12\x1e\x7d\xba\x6d\x27\x64\x50\xb5\x2e\x4f\
-\x4f\x88\x95\xf1\x27\xae\x2a\xd8\x1c\x10\xe0\x0d\x0a\x50\x67\x87\
-\x48\x7f\x89\xa6\x42\x71\xdd\x0d\x76\x01\x16\x48\x18\x64\x10\xe6\
-\x07\xf5\x8d\x36\x6a\x9a\x20\xdc\x4f\xc1\xcf\xcf\xc7\x93\x21\x01\
-\xb8\x5c\x50\xc2\x9b\x1f\x7e\xc9\xb2\x45\x77\x32\x23\x61\x8c\xc7\
-\xfd\x80\x14\xa3\x58\x59\x95\x98\x28\x9c\x3d\x35\xec\x71\x05\xfc\
-\x39\x5d\xfa\xe5\xd9\xc9\x6d\xb4\x8b\xe8\x8b\x15\x60\x73\x80\x97\
-\x02\xf1\x21\x92\x06\x07\xd4\x3b\x04\xe5\x8d\x82\xaa\xf2\x52\xf2\
-\x2f\xe7\x52\x51\x56\x4c\x6d\x55\x25\xcd\x0d\x95\x54\x56\x56\xe1\
-\x74\xe9\x5d\x78\xfa\xf9\x78\x13\x15\x19\xca\xff\xfc\xc7\x3a\xa2\
-\x23\x42\xbb\xd0\x6d\xb6\x66\x36\x7e\xb1\x8f\x1d\x7b\x53\x71\xe9\
-\x3a\xcb\x16\xdd\xe9\xe9\xc4\x5b\xb1\x4c\x58\x65\x70\x7a\xba\x5c\
-\xdc\x93\x12\x4c\x15\xf0\x8a\x94\x4a\xfe\x0f\x9c\xae\x6c\x24\x1a\
-\xa0\xa0\xa6\xe5\x0b\x3b\x0d\x38\x53\x2c\xc9\xcb\x3d\x47\x4e\x76\
-\x26\x57\xf3\x72\x69\x6c\xa8\xf3\x58\x3a\x5b\xb3\x9d\x2b\x05\x25\
-\xd8\xed\x8e\x0e\xf5\x52\x4a\xf6\xa7\x66\xf0\xde\xd6\xaf\xa9\xac\
-\xf1\x9c\x9f\x3b\x08\x29\xef\x13\xb1\x72\x8b\x94\xf2\x31\xb3\xed\
-\x60\xaa\x80\xe8\x1f\x49\x2b\x6e\x64\x44\xfb\x3a\x97\xd3\x49\x56\
-\xfa\x31\xd2\x8e\x1d\xa2\xa1\xbe\xb6\x5f\x42\xb6\xc7\xa5\xfc\x22\
-\xde\xf8\x70\x27\x59\xe7\x7b\x6d\xc8\xcd\xf0\xe8\xe1\x4b\x46\x3d\
-\xf0\x4f\xdd\x35\xe8\x56\x01\x1b\xd3\xe4\x1b\x57\xaa\x49\x68\x5f\
-\x97\x97\x9b\xcd\x81\x3d\x5f\x50\x57\xdb\x2f\xdf\xa3\x03\xec\x0e\
-\x27\xef\x6f\xfb\x33\xbb\xf6\x1d\x43\xd7\xfb\x74\x94\x9b\xc2\x69\
-\x88\x75\x1f\xa5\x49\xfb\xda\x24\xf1\x8c\x3b\xba\x5b\x05\xa4\xa4\
-\xcb\x3b\xf2\x6a\x79\xae\xb5\x6c\xe8\x3a\x07\xf7\x7e\x49\x56\xfa\
-\xb1\x01\x17\xb0\xb4\xbc\x8a\x1d\x7b\x53\x07\x9c\x6f\x2b\xc2\xfc\
-\x24\x5e\x8a\x5c\x9d\x56\x2a\xdf\x4a\x8a\x16\x79\x9d\xe9\x4a\xe7\
-\x8a\xed\xdb\xa5\x5a\x62\x63\x8f\xe3\xba\x77\xe7\x74\x3a\xf8\x62\
-\xeb\xfb\x37\x65\xf2\x3f\x07\x54\x01\x21\xbe\x04\x94\x57\xcb\x5d\
-\xdb\xb7\xcb\x2e\x8e\x76\x17\x05\xd4\x0e\x35\x36\xd7\x34\x13\x0c\
-\x2d\x5f\xfe\xeb\xcf\x37\x51\x70\xe5\xe2\xcf\x21\xeb\x4d\x43\x95\
-\x0d\x32\x8b\xc4\xc4\xda\x78\xe3\xa3\xce\xb4\x0e\x0a\xd8\x7d\x41\
-\x5a\xcb\x1a\x94\x65\xad\xe5\xd4\x83\xdf\x70\xf9\xe2\x39\x8f\x07\
-\x0a\x09\x0a\x20\x79\xc6\x44\x1e\x5f\xba\x80\x7b\xe7\x4c\xeb\x97\
-\xd0\x03\x85\xf3\x65\x82\xef\x2f\x2b\xd8\x5d\x50\xd2\xa0\x2c\xff\
-\x24\x43\xc6\xb7\xa7\x77\xb0\x01\xe5\x75\x6c\xb5\xbb\x5a\x94\x72\
-\x2d\x3f\x8f\xf4\x1f\x0e\x79\x34\xc8\xd0\xc1\x31\xac\x5c\x32\x8f\
-\x39\x33\x27\xa1\xa9\x2d\xab\xec\x44\xc6\x79\xf6\x1e\x3e\x39\x30\
-\xb3\xe8\x23\x1a\x9d\x90\x5b\x71\xc3\x39\x73\xea\x28\x0d\x4e\xb6\
-\x00\xc9\xad\x75\x6d\x0a\xd8\x7e\x51\x46\xe6\x17\xb5\x10\x34\x05\
-\xd2\xbf\xdb\x8d\x94\xe6\xbe\xba\x10\x82\x47\x16\xce\x66\xdd\xb2\
-\x7b\xb1\x78\x99\xbb\xaa\x7f\x0b\x54\xda\x04\x9d\xa7\x50\xd6\xc0\
-\xac\xf7\x4f\xc9\x41\x4f\x4e\x16\x85\xd0\x4e\x01\xa3\xfc\x8c\x95\
-\x23\x47\xa2\xd6\xdb\x21\x2b\x3b\x87\xbf\xe6\x5d\xe9\x71\x80\xa7\
-\x56\x3c\xc0\xd2\xfb\x93\x07\x54\xe8\x81\x42\x79\xa3\xe0\x6c\x71\
-\x57\x4f\xdf\xa9\x23\x34\x3b\xef\x01\x0f\x42\x3b\x05\x18\x92\x95\
-\x42\x40\xa0\x37\xec\x3f\x72\xa2\xc7\x01\x96\xde\x9f\xfc\xb3\x4c\
-\xde\x4b\x53\x09\x0a\xf2\x73\x4b\xb3\x35\xd9\xa9\x6f\xb4\xa1\xaa\
-\x0a\x81\xfe\x7e\x78\x5b\xbc\x30\x24\xfc\x78\x55\x50\xd6\x20\xe8\
-\x6e\xfd\x56\x36\xb1\xa0\xf5\xb7\x06\x90\x59\x2c\x93\xa4\x61\x8c\
-\x87\x16\xc7\xe4\xf8\x4f\xe7\x4d\x85\x8a\x8a\x08\xe1\xf1\xa5\x0b\
-\x4c\xdb\x0c\x04\xa6\x4d\xba\x85\xe7\xd6\x2e\x61\x88\x35\x0a\x80\
-\xe2\xb2\x2a\x8e\x67\x9e\xe7\x44\xc6\x39\x2e\x5c\x2a\xa0\xaa\xe6\
-\x46\x54\x4c\x51\x04\x71\x31\x91\x0c\x1d\x39\x86\x61\xe3\xa7\x13\
-\x19\x3d\xa8\x5b\xbe\x36\x07\x96\xad\x69\x72\xf9\x8a\x24\xf1\x71\
-\xcb\x0a\x90\xc6\xa3\xad\xc4\xb3\x39\x57\xb0\x35\xdb\x4d\x05\x7b\
-\x62\xd9\x42\x7c\xbc\x7b\x71\x9f\xed\x25\x62\x22\xc3\x78\x7a\xe5\
-\x22\x66\xcf\x98\x08\xc0\x99\x9c\xcb\x6c\xdd\xb1\x9f\x13\xa7\x2e\
-\x74\x6b\x97\x0c\x43\x72\xb5\xa8\x8c\xab\x45\x65\xa4\xa6\x7e\xcf\
-\xb8\xc9\xd3\xb8\x73\xc1\x12\x2c\xde\xee\x6f\x9f\x8d\x3a\xcf\x00\
-\x2d\x0a\x90\x92\xbb\x5b\x09\x17\xaf\x14\x9a\x0a\x17\x1c\xe4\xcf\
-\x9c\x99\x93\xfb\x34\xb1\x9e\x60\xd1\x34\x1e\x5d\x74\x27\xcb\x97\
-\xcc\xc5\xc7\xdb\x42\x79\x55\x2d\x7f\xda\xfa\x35\xfb\x8f\x66\xf4\
-\x8a\x8f\x94\x92\xb3\x99\x3f\x52\x5a\x74\x8d\x87\x56\x3c\x81\x7f\
-\x40\x50\x97\x36\x75\xcd\x8c\x07\xd0\xb2\x4a\x64\x94\xae\x1b\x63\
-\x5b\x09\x97\xf2\x8b\x4c\x99\x4f\x9f\x7c\x2b\x5e\x9a\x67\x91\xcb\
-\xde\x60\x66\xc2\x58\x9e\x59\xb3\x98\xb8\x98\x08\x9c\x2e\x9d\x94\
-\xaf\x0e\xb1\x79\xc7\xb7\x3d\xae\x46\x33\x94\x97\x16\xf2\xf5\x67\
-\x1b\x79\x64\xf5\xd3\x28\x6a\x47\x99\x1b\x1d\xf8\x7c\x98\x26\xc7\
-\x6b\xba\x4b\x9f\x85\x10\x02\x5a\x82\x1b\x7f\x2d\x32\x8f\x36\x8f\
-\x1b\x35\xb4\xcf\x02\xb9\x83\x9f\x9f\x0f\xaf\xbe\xf4\x38\xb7\x27\
-\x8e\x03\xe0\x64\x56\x0e\x6f\x7d\xb4\x93\xab\x45\x03\x93\x0f\x29\
-\x2c\xb8\xcc\x89\xd4\x6f\x99\x99\xdc\xd5\x66\x29\x18\xcb\x35\x29\
-\xc4\x78\x01\x48\x09\x99\x85\x0a\x8d\xb6\x66\x53\x86\x83\x63\x23\
-\x07\x44\xb0\x56\x44\x84\x06\x13\x91\x18\x4c\x49\x79\x15\xef\x6e\
-\xd9\xcd\x91\x13\xa7\x07\x94\x3f\x40\xda\xb1\xc3\x24\x4c\x9f\x8d\
-\x8f\x6f\xc7\xd3\xc4\xe1\x12\xd3\xb4\xbc\x4a\x31\xaf\xa6\x19\xaa\
-\x6d\x82\x06\x07\xd8\x1d\xe6\x0a\x08\x0c\xf0\xed\xb7\x40\x16\x4d\
-\xe3\x91\x07\x92\x89\x89\x0e\xc7\xe1\x74\x92\xb2\xfb\x30\xdb\x76\
-\x1d\xa4\xb9\x53\x80\x64\xa0\xe0\x74\xd8\x39\x75\xf2\x28\x33\x66\
-\xdf\xdd\xa1\xbe\x59\x17\xc3\xb5\x2b\xd5\x4c\xae\xb7\xdf\x70\x18\
-\xbc\x54\xf3\xfd\x6d\xf1\xf2\x38\x8e\xea\x16\xed\xf7\xfa\xb1\xf4\
-\x6c\xde\xde\xb4\x8b\xa2\xd2\x4a\x8f\xfa\x46\x85\x87\x90\x7c\xdb\
-\x24\x26\x8f\x19\x41\x54\x44\x30\x0d\x8d\xcd\x5c\xb8\x54\xc0\xe7\
-\x7b\x8e\x50\x5d\x6b\xbe\x75\x2f\x9e\xcf\xea\xa2\x00\x97\x2e\xc3\
-\xb5\x61\x61\x34\x9d\x29\xc1\xaf\xf5\x74\xb1\x58\xcc\x8f\x37\xbb\
-\xb3\xc7\x38\xa3\x5b\xc4\xc5\x44\xf0\xcc\x9a\xc5\xcc\x4c\x18\xcb\
-\xb5\x92\x0a\x5e\xfc\xed\x07\x1c\xcf\x34\xf7\x37\x5a\x11\x1e\x12\
-\xc4\xe3\x4b\x17\xb0\x60\x4e\x52\xdb\x5d\xa3\x15\x89\x13\x47\x33\
-\x3f\x39\x89\xb5\x2f\xff\x91\xca\x8a\xf2\x6e\x79\x94\x96\x14\x52\
-\x5f\x5b\x43\x60\x70\x48\x5b\x9d\xc3\x10\xde\xda\xf0\x30\x29\xbc\
-\x14\x38\x53\x22\x70\xe8\x20\x34\x73\x05\x54\x55\x37\x78\x24\x74\
-\x2b\x7c\x7d\x2c\xac\x58\x32\x8f\x47\x1e\x48\xc6\xd0\x0d\x3e\xf8\
-\x74\x2f\x9f\xed\x3e\x8c\xc3\xe5\xea\xb1\xaf\xa6\xaa\x3c\x7c\xdf\
-\x2c\x56\x3f\x7c\x0f\x7e\xbe\xdd\xa7\x1b\x7d\xfd\x83\xb8\x67\xf1\
-\x63\xa4\x6c\x78\xb3\x7b\x66\x52\x52\x5c\x98\xdf\x41\x01\xba\x8e\
-\xaa\x01\x01\x83\x43\x5a\xd2\x55\x35\xcd\x90\x1a\x16\x40\xbe\x49\
-\x58\x2e\xef\x6a\x31\x77\x24\x8d\xeb\x51\x78\x21\x04\x77\xdd\x3e\
-\x85\x5f\xae\x78\x80\xa8\xf0\x10\x0e\x9f\xc8\xe2\x8f\x9b\x77\x53\
-\x5a\xe1\x59\x38\x2d\x71\xe2\x68\x9e\x5f\xb3\x84\xf8\xb8\x68\x0c\
-\xc3\x3c\x54\xe6\xe7\x05\x71\x71\x43\x19\x34\x78\x18\x85\x05\x97\
-\xbb\x6d\x57\x55\x5e\xda\xa1\xac\x1b\x88\xb6\x78\x80\x10\x10\xea\
-\x0b\x13\x46\x44\x99\x0e\x76\xe2\x54\xcb\xb2\x35\x68\xb9\x70\x9c\
-\x29\x69\xf1\xbb\x3b\x67\x87\x92\x26\x8d\xe6\x95\xe7\x57\xd0\xd4\
-\x64\xe7\xb9\x5f\xbf\xcf\x7f\xbd\xbe\xd9\xa3\xc9\x47\x47\x84\xf2\
-\x9b\x17\x56\xf1\xd6\x7f\x3f\x89\xaf\xaf\x37\xaf\xbc\xb9\x95\xb4\
-\xac\x5c\xd3\x3e\x42\xc0\xad\xd1\x92\x61\xa3\xcd\x73\x07\x95\x15\
-\xa5\x5d\xea\x34\xa0\x01\x08\x6b\xad\x88\x8f\x8b\x36\x65\x72\xfa\
-\x7c\x1e\xc7\x4f\xe7\x53\x69\x19\x86\x4b\x07\x1f\x0d\xf2\xae\xdb\
-\xb0\xd8\xa0\x96\x4c\x50\x98\x1f\x5c\x2e\x77\x71\xe4\xe0\x3e\xf6\
-\x1d\x38\x82\x4b\xef\x9a\x1b\xe8\x0c\x8b\xa6\xb1\x74\xd1\x1c\x56\
-\x2c\xb9\x0b\x45\x55\xf8\x78\xe7\x01\xb6\xee\xdc\x4f\x53\xb3\x83\
-\xf9\xb3\x93\x7a\xec\x3f\x3a\x42\x92\x78\x4b\x2c\x47\x0f\x76\xdf\
-\xa6\xa9\xb1\xe3\xf6\x55\x15\xa4\x06\xd4\xd3\x4e\x01\x63\x47\x0e\
-\x41\x08\x61\x1a\x0b\x78\xf5\xfd\x14\x1e\x5e\xf5\x34\xfe\x01\x41\
-\x68\x2a\x34\x5f\xdf\xce\x45\x75\x02\x5d\xd7\xc9\x3e\x75\x92\x63\
-\x87\xf6\x7a\x9c\x2b\xb8\x6d\xca\x18\x9e\x5d\xbb\x84\xb8\x98\x08\
-\x7e\xc8\x38\xc7\x3b\x1b\x77\x71\xad\xa4\xc2\xa3\xbe\xed\x31\x66\
-\x70\x80\x29\xdd\xe9\xec\x78\xcc\xaa\x2a\xba\x06\x54\x00\x6d\x61\
-\xa2\xe8\x88\x50\x46\xc6\x5b\x4d\xef\x04\x15\xe5\x65\x6c\x7e\xef\
-\x55\x12\x67\x26\x13\x6d\x1d\x8c\xb7\xb7\x2f\x75\x75\xd5\x14\xe6\
-\x5f\x22\xe7\xec\x29\x8f\x27\x6e\x8d\x0e\xe7\xd9\xd5\x8b\xb9\x3d\
-\x71\x1c\xd7\x4a\x2a\xf8\xd7\xdf\x6d\xe0\x87\x0c\xcf\x43\x70\x9d\
-\xe1\xd3\xc3\x09\xe6\xb0\x77\x74\xab\x2d\x8a\xb4\x6b\x40\x2e\x30\
-\xb5\x3d\x61\xf6\x8c\x89\x3d\x5e\x8a\x9a\x6c\x0d\xa4\x1e\xd8\xd3\
-\x37\x41\xbd\x2d\x3c\xb6\x78\x2e\xcb\x16\xcd\x01\x21\x58\x9f\xf2\
-\x0d\x9f\x7f\xfd\x9d\x47\x27\x83\x19\x7a\x3a\xa2\x0d\xd9\x71\x2b\
-\xfa\x5a\x44\xb1\x26\x20\xa7\xf3\x62\x5f\x72\xcf\xed\x7c\xb2\xfb\
-\x10\x4d\x4d\x7d\xbf\x88\x74\x87\xd1\x63\x27\x71\xf7\x7d\x0f\x92\
-\x38\x32\x14\x2f\x2f\x49\x41\x61\x29\x1f\xef\x3c\x30\x20\xbc\x6b\
-\xeb\x6d\xa6\xf4\x60\x7f\x1f\x62\x02\x24\xde\x5e\x30\x2c\x54\x12\
-\xe2\xc3\x16\x4d\x0a\x79\x16\xd9\xd1\x82\x07\x05\xfa\xb3\x74\x61\
-\x32\x9b\xbe\xd8\x37\x20\x82\x01\x84\x86\x47\x31\xf7\xbe\x5f\x30\
-\x74\xc4\xad\x00\xec\x3e\x72\x96\xa4\x31\x71\x0c\xed\x7a\x53\xed\
-\x33\x0a\x8a\xba\x77\x84\x00\xa2\x42\x7d\x99\x11\x7f\xe3\x73\x4b\
-\x21\xb3\x35\x55\x51\x53\x75\xdd\x90\x74\x4a\x95\xaf\x7c\x68\x1e\
-\x69\x59\x39\x9c\xcd\xbd\xd2\x2f\xa1\x2c\x16\x6f\x6e\x9b\x7d\x0f\
-\x09\xb7\x25\xa3\xaa\x2a\xd5\x55\x15\x1c\xfe\xcb\x4e\xf2\x72\xb3\
-\x89\x7a\xf6\xdf\x19\x64\x6e\xb7\x7a\x85\xec\xdc\xee\x7d\x00\x68\
-\xf1\x28\xdb\xc1\x90\xa8\xa9\xda\xa4\x18\x51\xf6\xd3\x35\x3d\x5b\
-\x88\x96\x00\x41\x2b\x34\x55\xe5\x77\x2f\xae\xe6\x57\xbf\xfd\xa0\
-\x47\x7b\xd0\x1d\xc6\x4c\x98\xca\xec\xbb\x17\x11\x10\x14\x8c\xd3\
-\xe9\xe0\xf8\x77\xfb\x48\x3b\x76\x10\x5d\x6f\xd9\xeb\x4e\xe3\x46\
-\xc6\xb9\xbf\x70\xb8\x5c\x9c\x3c\x65\xee\x2f\x8c\x88\xb7\xb6\xfd\
-\x96\x90\x95\x68\x15\x15\x1a\x80\xa2\xb0\x5f\xca\x8e\x0a\x00\x08\
-\x0b\x0d\xe2\xbd\x5f\xff\x33\xaf\x6f\xfc\x8a\xfd\x47\x4e\xf6\x18\
-\x26\x07\x40\x08\x46\x8c\x1a\xcb\xb4\x59\xf3\x18\x34\x78\x18\x00\
-\xb9\xe7\xb2\xf8\x6e\xdf\x57\xd4\xd5\x54\x75\x69\x5e\x54\x37\x30\
-\x0a\x38\x78\x34\x93\xfa\x46\x73\x1b\x30\x6a\xa8\xb5\x7d\xf1\x00\
-\x5c\x0f\x8a\x4a\x94\x14\x30\x9e\x77\xd7\xc9\xcf\xcf\x87\x07\x7f\
-\xf1\x28\xd6\xb1\xc9\x9c\xc9\xfc\x91\xcb\x17\xcf\x53\x5d\x59\x8e\
-\x94\x37\xdc\xd3\x80\xa0\x60\xa2\x63\x07\x33\x7c\xd4\x58\x86\x8f\
-\x1e\xd7\xe6\x6f\x57\x57\x96\x71\xf0\x9b\x2f\xb9\x72\xe9\x42\xb7\
-\x42\x35\xf7\xcf\xf0\x03\x2d\xd1\xe1\xf5\x9f\x7c\x63\xda\xc6\xdf\
-\xcf\x97\x71\xb7\x0c\x6d\x2b\x2b\x52\x49\x81\xeb\x0a\x48\xb0\x8a\
-\xf4\x9f\xae\xe9\x67\x3b\x6f\x83\x56\xc4\x04\x42\x44\xb4\x95\x39\
-\xf3\x17\x33\x67\xfe\x62\x5c\x2e\x27\x0e\x7b\x33\x86\x6e\xe0\xe3\
-\xeb\x87\xe6\x26\x29\x52\x5c\x98\xcf\x67\x1f\xbd\x8d\xee\x81\x17\
-\xd8\x5f\xfc\xef\x27\x7b\xa8\xa8\x36\x7f\xab\x70\x7b\xe2\x58\x2c\
-\x5a\xdb\x55\xfe\xdc\x94\x38\x71\x0a\xda\xe5\x06\x15\xc1\x96\xee\
-\x3a\x17\x75\xf2\x6b\x34\xcd\x0b\x3f\xff\x40\x02\x82\x82\xdd\x4e\
-\x1e\xa0\xd9\xd6\x38\x60\x93\xaf\x6e\xea\x9e\xb6\x6d\xd7\x41\x76\
-\xfe\xdf\xd1\x1e\x79\x2c\x9c\x3b\xe3\x46\x41\xd2\x96\x24\x6d\x53\
-\x40\xa3\x45\x59\x0f\xb8\xbd\xad\x34\xde\x9c\x40\x8d\xc7\xc8\x29\
-\x17\xa4\x5e\x51\x28\xa8\xbd\x61\x2f\xec\x0e\x27\x6f\x6f\xdc\xc5\
-\xfa\x14\xf3\xa5\x0f\x30\x69\xcc\x08\x12\xc6\x8d\x6c\x2d\x56\xd9\
-\xbc\x95\x0d\xad\x85\x36\x05\x5c\x7f\x7b\xfb\xae\x3b\x06\x13\x62\
-\x25\xbe\x7f\xc3\xd4\x9f\x04\x2a\x1b\x21\xa7\x4c\xe0\x70\xb9\xf8\
-\xcb\x77\x69\xac\x7e\xe1\x75\xbe\xd8\xfb\x7d\x8f\x86\x59\x51\x14\
-\x7e\xb9\x62\x61\x7b\x66\x6f\xb5\x7f\x67\xdc\x21\xbe\xa5\xb9\x94\
-\xb7\x5c\x9a\xf1\x24\xd0\x21\xf2\x19\xe4\x0d\x09\x83\x24\x3f\x5c\
-\xe9\x3e\xdd\x74\x33\x91\x93\x7d\x8a\xa2\x6b\xf9\x54\x96\x14\xf2\
-\xc6\xb5\x4b\xd4\x35\x98\x5b\xfb\xf6\x78\x6c\xf1\xdc\xf6\x91\xec\
-\x52\xc3\xae\xbc\xd3\x9e\xde\x41\x01\x13\xe3\x45\x75\x46\xa1\xeb\
-\x25\x10\x5d\x1e\x12\x44\xfa\x4b\xac\xc1\x50\x58\x3b\x30\xc7\x56\
-\x6f\x70\x36\xf3\xc7\x3e\xf5\xbb\x3d\x71\x1c\x6b\xff\x71\xfe\x8d\
-\x0a\x21\x5f\x4c\x1c\x21\x3a\x58\xcb\x2e\x2f\x44\xa6\x58\xd5\x4d\
-\x20\xdd\xbe\x87\x19\x1f\x2d\x51\xbb\xf4\xf8\xfb\xc4\xb4\xc9\xb7\
-\xf0\x9b\x5f\xad\x44\x6d\x13\x58\x7e\x3f\x25\x56\xdd\xd6\xb9\x5d\
-\x97\xe9\x08\x21\xa4\xa1\xab\x2b\x11\x74\x39\x57\x5a\x5f\x7b\xff\
-\xbd\xe3\xe1\x7b\x67\xf1\xda\xcb\xeb\xda\xbf\x59\xa8\xd1\x35\x75\
-\xb5\x10\xa2\x8b\xf0\x6e\xbf\x67\xe2\x10\x71\x09\x43\x3e\xee\x8e\
-\x76\x4b\xa4\x24\xd0\xe3\xbf\x43\xb8\x87\xa2\x28\x24\xcc\x98\x45\
-\x50\x70\x58\xcf\x8d\x7b\x81\xd8\xa8\x30\xde\xf8\xcf\x27\x78\x76\
-\xcd\xe2\x76\x5f\x1e\xa4\x90\x6b\xdd\xbd\x10\x03\x93\x77\x82\x09\
-\x71\xda\x8e\x8c\x22\xfd\x0d\x24\xff\xd2\xbe\x5e\x11\x30\x2a\x42\
-\x92\x59\xd8\x37\x83\x18\x37\x64\x04\x73\xef\x7b\x88\xc8\x18\x6b\
-\xcf\x8d\x3d\x44\x74\x44\x28\xcb\x16\xdd\xc9\xfd\x77\x4d\xef\xf2\
-\x52\x45\xc0\x1f\x12\xac\xda\xce\xee\xfa\x9a\x66\x39\xa6\xc4\x2a\
-\x2f\x64\x14\x19\x61\x02\x56\xb5\xaf\x1f\x12\x22\xf1\xd1\xe0\x74\
-\x89\xa0\xc1\xc3\x90\x81\x7f\x40\x10\xc9\xf7\x2c\x62\xcc\x84\xa9\
-\x2d\x51\xcc\x7e\x22\x2a\x22\x84\xc4\x09\xa3\x99\x3b\x73\x0a\x49\
-\x93\x46\xa3\x28\x6e\x17\x73\xca\x64\xab\xf2\xb2\x19\x1f\x53\x05\
-\x08\x21\x64\x7a\xba\x5c\x27\xac\x32\x0a\x29\xef\xed\x20\x40\x80\
-\x64\xee\x08\x49\x69\x83\xe0\x74\xb1\xc0\xd6\x4d\x30\x46\x51\x55\
-\x12\xa6\xcf\x62\x66\xf2\x7c\xb7\xb9\x7a\xcd\xcb\x42\x78\x54\x0c\
-\x75\x35\xd5\x38\x1d\x5d\xb5\xa9\xaa\x1a\xb1\x51\x61\xc4\xc5\x86\
-\x63\x8d\x0c\x63\x78\xbc\x95\xa9\x13\x46\xf5\x98\xa3\x14\x88\x3d\
-\x7a\xb1\x58\x25\x06\x99\x3f\x9b\xf7\xe8\x53\x1c\x96\x52\x0b\x2e\
-\x32\xd6\x03\x6b\xdc\xd1\x9b\x5d\x70\xf8\x52\xcb\x53\xb4\x56\x5c\
-\xbe\x78\x8e\x93\x47\x0f\x71\xd7\xc2\x87\x08\x8f\x8c\xf1\x64\x18\
-\x00\x1c\xf6\x66\xa4\x34\x10\x08\x2c\x3e\xbe\xf8\x59\x60\xde\xa8\
-\x1e\xfe\x8c\xd4\x09\x42\xb2\x4d\x2f\x51\xd6\x78\xf2\x5c\xde\x63\
-\xbe\x52\x4a\x91\x51\x64\xfc\x41\xc0\x0b\xee\xe8\xa7\x8a\x05\x57\
-\xaa\x6e\xb0\x73\x39\x9d\xdd\xde\x13\x7a\x83\xd8\x20\xc9\xf4\xc1\
-\x1e\x5b\x1b\x29\xe0\xb5\xc9\x56\xe5\x25\x77\x16\xdf\x1d\x3c\x3e\
-\xd5\x85\x10\x72\xea\x20\xf5\x45\x21\xe4\x62\xdc\xdc\x19\x02\x3b\
-\x05\x64\x07\x62\xf2\xd0\x62\x70\x3d\x44\x9d\x94\x72\xe9\x94\x41\
-\xea\xbf\x79\x3a\x79\xe8\x85\x02\x5a\x31\xc5\xaa\x7d\xa5\x6b\x4a\
-\x22\xc8\x0e\x2f\x9c\x6f\x86\x83\x34\x38\x44\x12\xe6\x51\x36\x5e\
-\x7e\xaf\xaa\xca\xe4\xa9\x71\xda\xf6\xde\x8e\xd1\x67\x73\x2c\xa5\
-\x14\x99\x85\xfa\x72\x84\x78\x0d\x88\xba\x5a\x2b\xc8\xb8\xd6\x7f\
-\xeb\x1e\xee\x0f\xb7\x46\x4a\x42\x7c\x24\x5e\x3d\xbf\xc4\xa9\x92\
-\x82\x97\x13\x62\x95\x0d\xbd\xf9\xea\xed\xd1\x6f\x89\x33\x2f\xcb\
-\x10\x69\x31\x9e\x29\x69\x10\x2f\x9c\xc8\x17\x81\xfd\xe1\x35\xc5\
-\x2a\x89\x0f\xf5\x68\x1e\x95\x48\xde\x36\xec\xca\x3b\x9d\x7d\xfb\
-\xde\x62\xc0\x6e\x36\x9b\x4e\xca\x18\x17\xf2\x4f\xd5\x36\xb1\xa0\
-\xc1\x81\xe7\xff\x8c\xba\x0e\x4d\x85\x85\xb7\x9a\x67\x81\xa5\xe4\
-\x2c\xb0\xd1\xc7\xa2\x6c\x18\x17\x25\x7a\x97\xa7\xef\x06\x37\xe5\
-\x6a\xb7\x35\x4d\x2e\x6f\xd2\x79\xaa\xce\xce\xc4\x06\x3b\xbe\x9e\
-\xae\xcd\xfb\xc7\x1a\xa8\x1d\x25\x32\x80\xd3\x12\xf6\x2b\x52\x49\
-\x69\x0d\x63\x0d\x24\x6e\xfa\xdd\x76\xeb\x4f\x72\x8c\xcb\x30\x56\
-\x39\x75\x91\x60\xd7\x19\xe5\x70\x89\x08\x97\x81\xc5\x65\xa0\xb9\
-\x0c\x84\x04\xbc\x14\x64\xb0\xaf\x6c\x9a\x19\x6f\x9c\x03\x91\x23\
-\x24\x17\xa4\x22\xb3\x25\x6a\x6a\xa2\x55\xf4\x3e\x4b\xda\x0b\xfc\
-\x3f\xcf\xc2\x42\x82\x41\x31\x16\x33\x00\x00\x00\x00\x49\x45\x4e\
-\x44\xae\x42\x60\x82\
-\x00\x00\x03\xb1\
-\x89\
-\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
-\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
-\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
-\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\
-\x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
-\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
-\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x03\x2e\x49\x44\
-\x41\x54\x38\x8d\x5d\x93\x5d\x4c\x9b\x75\x14\xc6\x7f\xff\xb7\x6f\
-\x8b\x6d\x0a\x2b\x8c\x41\xd9\x04\x5c\x1d\x96\x16\x27\xac\x2c\xab\
-\xba\x10\x03\xce\x69\x9c\x2e\x12\x8c\x2e\x3a\x13\x89\x0e\x83\x9a\
-\x5d\xe0\xc7\x05\xd1\x99\x18\xbd\x51\xb6\xcc\x45\x13\xa2\x75\xd3\
-\x69\x1c\x31\xce\x68\xc0\x1b\x98\xd3\x18\x97\xe9\x36\x98\xb8\x15\
-\x5a\xfa\xa1\x80\xb4\x0c\x3b\x69\xf9\x18\xb4\xd0\xf7\xef\xc5\xe4\
-\x0d\xf3\x5c\x9d\x9c\xe7\x39\x1f\x4f\xce\x39\x82\xff\xd9\xd1\xf3\
-\xf2\xd1\xf4\x32\xaf\xcd\x66\x70\x0a\x30\x59\x4d\x52\x7a\xcb\x64\
-\x40\x4a\x4e\xa9\xaa\xe2\xab\xb6\x8b\xcb\xab\xf9\x62\xc5\xe9\xbe\
-\x20\x2d\x33\xaa\xec\x09\x27\x44\x7d\x56\x03\xa3\x02\xf7\xdc\xaa\
-\x31\xbb\x28\x28\xc9\x93\x48\x29\x11\x42\x64\x05\x74\x9a\x92\x4a\
-\x5b\x55\x95\xc8\xe8\x05\x8e\x5d\x94\xb6\xf9\x05\x6d\xf8\xdc\x80\
-\xdf\x1e\x1d\x19\x42\xa6\x53\x90\x4d\xf3\x54\xe3\x0e\xb6\xd5\x38\
-\xf9\xe1\x97\x41\xce\x0f\x06\x79\xf5\xb9\xc7\x56\xfa\x9e\x4e\x8d\
-\x88\x5d\xf5\xf5\x62\x51\x05\x98\x9c\x9c\x3e\xf3\x91\xef\xa8\x7d\
-\x32\x36\x76\x83\x9c\xfb\xea\x6a\xe9\xfd\xa9\x9f\x37\x8f\x7c\xce\
-\x9d\x1e\xd7\x2a\x44\x36\x2c\x6f\x90\x3e\x60\xaf\xf2\x8d\x5f\xd6\
-\x75\x75\x9d\x70\x4f\xc6\xc6\x70\x94\xad\xe7\xf0\x81\x56\xb6\x54\
-\x6d\xd2\xa9\x8b\x99\x25\x00\x8a\xd7\xe6\xeb\xb1\x85\x25\x38\x3b\
-\xa6\x3c\xf9\xd9\x45\xf9\x88\x9a\x8c\x47\x9e\x0d\x8d\x04\x31\xdf\
-\x94\x43\x47\xfb\x3e\x8a\x0a\x6d\x74\xf5\xfc\xa8\x93\xd7\xda\x72\
-\x39\xb0\x7f\x2f\xaa\x6a\xe0\xf5\x8e\x4f\xb8\x9a\x9c\xc3\x5c\x58\
-\x86\xb7\xee\x7e\x66\x2d\xe6\x37\xd4\x68\x7c\xe6\x01\x80\x6a\xb7\
-\x83\xa2\x42\x9b\x9e\x78\x47\xa5\x03\xcf\xed\x9b\x98\x9b\xbf\xc6\
-\xa7\x27\xfb\x18\x0a\x8d\xae\x92\x10\x61\x32\x1e\xe3\xe9\x67\x5a\
-\x5d\xea\x5c\xd6\x58\x00\x60\x50\x14\x1d\x6e\xd9\xf3\x20\x45\x85\
-\x36\x3e\xfc\xe2\x3b\x7a\x4e\xff\x8a\xd5\x62\x66\x7f\x73\x23\x3b\
-\xee\xae\xe1\xec\xc8\x55\x3a\xde\xeb\x64\x34\x12\x24\x36\xf5\xb7\
-\x49\xdd\xee\x2a\x48\x77\x82\x3a\x70\x29\xcc\xd7\x3f\xff\x81\x31\
-\x77\x1d\x61\x7f\x84\xde\xde\x5e\xe6\xaf\x2d\xf0\x50\x83\x97\x96\
-\x27\x76\x31\x95\x48\x52\x90\x9f\x47\x83\x27\x8f\xaf\x1c\x15\x84\
-\x02\x7e\x92\xff\x24\x50\x37\xde\x6c\x1f\xad\xdd\x5c\xe1\xee\xbf\
-\x14\xe2\xd0\xe1\x23\xfa\x14\xeb\x4b\xcb\x69\x6d\x6d\x62\x4b\xb9\
-\x99\xf6\x77\x8e\x61\xb1\xe4\xd0\xd1\xde\x82\xd9\x08\x6b\x72\xb2\
-\x00\x6c\xb0\xa4\x13\xaa\x94\xf4\xbd\xfd\x4a\xb3\xfb\xd0\xf1\x53\
-\x0c\x87\xc7\xb1\x58\xad\xdc\xe6\xaa\x46\xd3\xb2\x84\x26\x66\x99\
-\xf8\x6b\x82\xdf\x03\x51\x7d\x8d\x57\x12\xd3\xf8\x83\x51\x00\x9c\
-\xe5\x6b\xba\x55\x55\x55\x7c\x56\x8b\xf9\xc5\xa6\xc6\xdd\x86\x50\
-\xe2\xfa\x61\xf6\x75\x7f\xc9\xe0\x85\x33\xec\xdc\xbd\x87\xf9\xff\
-\x8e\x75\x2a\xb5\xc4\xb9\xdf\x82\xbc\x7f\xfc\x5b\x16\x16\x33\x6c\
-\xab\xae\x94\x9b\x9d\x8e\x83\x4a\xb5\x5d\x5c\x16\xd0\xe9\x5c\x27\
-\x31\xab\xd7\xc7\x9f\x49\x4d\x03\x60\x34\x1a\x75\x49\xd1\x48\x98\
-\xb6\xb7\x3a\x89\x8e\xc5\x71\x57\x94\xf3\xd2\xbe\x26\x9f\xa7\x44\
-\x0c\xa9\x00\xa6\xa4\xd2\x26\x6d\xb2\xd2\x5b\xa6\xdd\x3b\x10\x53\
-\xc8\xcd\xb3\xf1\xf0\xe3\xcd\x38\xdd\x35\xfc\x19\x0e\x50\x7a\x4b\
-\x05\xc5\x36\x13\xa5\xc5\x36\xbc\x35\x2e\xee\xaa\x75\x7f\x2f\xae\
-\x18\x5e\xd0\x7f\x01\xc0\xef\x97\xa6\xb4\x4d\x3b\x38\x9e\x12\xcf\
-\xf7\x8f\xa3\x20\x74\x88\x7c\x33\x6c\x2f\xd7\x50\x0d\x64\x81\x0f\
-\xb4\xb8\xf2\xf2\xd6\xad\x62\xe9\x86\x02\x2b\x76\x32\x20\xeb\xa7\
-\x92\xbc\x3b\x93\xa6\x2a\xb3\x4c\x4e\xae\x09\xad\x6e\xa3\x36\x2c\
-\x14\x7a\x11\xca\xc7\x9e\x12\x31\xb4\x9a\xff\x2f\xc7\xd3\x35\x8d\
-\xdf\xe8\x52\x39\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
-\
-\x00\x00\x0c\x01\
-\x89\
-\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
-\x00\x00\x30\x00\x00\x00\x30\x08\x06\x00\x00\x00\x57\x02\xf9\x87\
-\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
-\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\
-\x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
-\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
-\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x0b\x7e\x49\x44\
-\x41\x54\x68\x81\xb5\x9a\x79\x70\xd5\x55\x96\xc7\x3f\xe7\xf7\x7e\
-\x2f\xfb\x1e\x08\x21\x0b\x90\x8d\x80\x49\x34\x31\x09\x8b\x10\xe8\
-\xb8\x01\x5a\xb6\xb4\xd6\xa8\x88\x38\x8e\xd8\x3d\x88\xe5\x14\xf6\
-\xbe\x59\xd3\xce\x54\x69\x6b\xd3\x5a\x25\x53\xad\xe8\xb4\x82\x2c\
-\xda\x2d\x22\x4d\xf7\x40\x43\x77\xc9\xe2\x02\x91\x10\x76\x12\x48\
-\x08\x31\xfb\x9e\x97\x3d\x6f\xfb\xdd\xf9\xe3\x25\x21\x21\xbf\x97\
-\x0d\xfc\xfe\x45\xce\x3d\xf7\x9c\xef\xb9\xef\xfe\xce\xbd\xe7\x5c\
-\x84\x9b\x80\xf7\x2f\xa8\x4c\x7a\x8c\x67\x7a\x5d\xb2\xd8\xee\x96\
-\x38\xbb\x93\x10\x97\x81\xee\x34\x10\x01\x74\x0d\x35\x27\x4a\x35\
-\x25\x44\x70\x5a\x50\x25\xa0\x8e\xbb\xac\x96\x43\xf3\xa2\xa4\xfe\
-\x46\x7d\xcb\x64\x27\x6e\x3d\xad\x66\xf7\xd9\xd5\xab\x9d\x76\xb9\
-\xa7\xb3\x8f\x40\x35\x8a\xae\x06\xa4\x46\x29\x52\xa7\x5e\xa7\xa5\
-\xd4\x09\x44\xb6\x39\x95\xb6\x73\x7e\x9c\xb4\x4c\x86\xc7\x84\x03\
-\xd8\x5a\xa4\x16\x76\xf4\xa9\x77\x5a\xba\x25\xdd\x30\x61\x1d\xe2\
-\x07\x53\x03\x15\xb6\x5e\x21\x22\x40\x11\xee\x0f\x15\x6d\x70\xdb\
-\x74\x45\xa0\x8f\x57\xb3\xdd\xa2\x78\x57\x94\xb6\x31\x33\x5e\x6a\
-\xbe\x95\x00\xfe\xfc\x95\xf2\x6f\xd6\xd4\x9e\xc6\x4e\xb9\xd7\x8c\
-\xf8\x00\x74\x0d\xe6\xcf\x50\x04\xfb\x2a\x7c\x75\x38\x51\x25\xd4\
-\x76\x08\xa1\x7e\x90\x9f\x64\x8c\xe5\xa6\x47\xc1\xef\x42\x7a\xb4\
-\x57\x52\x52\xc4\x7e\xd3\x02\xd8\x71\x5a\x2d\x33\x0c\xb5\xa5\xd2\
-\x26\xd1\x76\xd7\xe8\xba\xba\x06\x77\x26\x1b\xf4\x38\x60\x4a\xa0\
-\x47\x66\x77\x41\x6b\x8f\x30\x3d\x64\xb4\x8d\x36\x0c\xa7\x35\xa5\
-\x3d\x9a\x19\x27\x97\xc7\x52\xd4\xc6\x52\xf8\xf8\xbc\xfb\xa7\xfe\
-\x16\xe3\x2f\x55\x36\x89\xbe\x33\xc9\x20\x3e\xd4\x9c\x44\x90\x0f\
-\x4c\x0f\xf1\xac\x7a\x79\x8b\x60\x1d\x62\xd9\x57\x67\x04\x79\x97\
-\xdb\x3d\x9a\xdb\x4c\xb7\x18\x85\x27\x6b\x5d\xdf\xbb\xa1\x00\xde\
-\x39\xae\x3e\xf0\xd5\xe4\xe5\x8b\x8d\x9a\xaf\xdd\x05\xa7\x6a\x85\
-\xea\x0e\x61\x5a\x90\xf2\xa4\x17\xc0\xde\xdd\xc1\x14\xe7\x15\xc2\
-\x7a\x4b\xe8\xad\x3d\x4f\x40\xe7\x05\x1c\xcd\x97\x69\x69\xaa\xc3\
-\xe1\x74\x9a\xda\x3d\x76\xaa\x98\xb5\x3f\x7d\x7d\x54\x62\x02\xc1\
-\xa2\xe4\xe3\xa2\x6a\xf7\xba\xd1\xf4\x74\x6f\x03\x7f\x3c\xa1\xb6\
-\x56\xb6\xb1\xa6\xa9\x47\x70\xb8\x21\x22\x00\x3a\xfa\x14\x15\x65\
-\x97\x39\x7a\xb5\x94\xaa\xab\xa5\x34\x35\xd4\xe0\xf4\x42\x12\xe0\
-\xd7\xcf\xaf\x66\xf9\xd2\x9c\xc1\xbf\x6b\x1b\x5a\x78\x73\xcb\xa7\
-\x7c\x71\xe2\x02\x7e\xbe\xde\xbf\xe8\x21\xb0\x20\xbc\x55\x54\xe3\
-\xb6\xde\x1e\x6b\xd9\x34\xee\x00\xde\x3f\xe1\x7e\xad\xc2\xc6\x93\
-\x0a\xcf\xfe\x75\x3a\xec\xfc\xe3\xf8\x71\x4e\x1e\x3b\x4c\x87\xad\
-\x75\x3c\x8e\x87\xc1\xee\x70\xb2\x6b\xdf\x51\xb6\xec\x3a\x48\x6f\
-\x9f\x63\xc2\xf3\x81\x37\x0e\x95\xba\xda\xf2\x53\xf4\xed\xd7\x0f\
-\x8c\x08\x60\xfb\xd7\xea\xae\xf2\x76\x7e\xac\xfa\xb7\xec\xb9\x93\
-\xc7\x38\x7c\xf0\x2f\xd8\xfb\x7a\x27\xe3\x98\x2b\xdf\xd4\xf2\x8b\
-\xd7\xde\xa3\xb6\x61\x52\x69\x7e\x00\x96\x0e\xa7\xf6\xee\xce\x0b\
-\xaa\xf6\xf1\x34\xf9\x6c\xe8\xc0\xb0\x6f\xe0\xaf\x85\x2a\xa0\xa1\
-\x4f\xed\x75\x19\x88\xdb\xed\xe2\xff\x3e\xd9\xc6\x81\xbd\x1f\x4d\
-\x9a\x3c\x40\x69\x45\xed\x8d\x92\x07\x20\x3e\x54\xf9\xa5\x84\x1a\
-\xbf\xff\xe0\x80\x0a\x1c\x2a\x1f\x16\x40\x9d\x9b\xdd\xed\x7d\x12\
-\xa0\x94\xc1\xbe\x4f\xb6\x51\x7c\xb6\xf0\x86\x1d\xdf\x2c\x28\x05\
-\x65\xcd\x92\xe9\x08\x57\x3b\x87\xca\x07\x03\xf8\xe8\x8c\x4a\x6f\
-\xec\xe2\x5e\x80\x63\x87\x0f\x72\xe9\xc2\xe9\x51\x0d\x66\xa5\x25\
-\xf3\x83\x55\xf7\xf3\x9b\x17\x9e\xc4\x47\xf7\x9a\x0b\x6e\x1a\x8e\
-\x55\x0a\xc5\x8d\x42\x7d\xa7\x3c\xb0\xfb\xa4\x9a\x3b\x20\x1f\xf4\
-\x6c\xeb\x51\x5b\x1d\x6e\x91\x8e\xd6\x06\x0a\x3e\x3f\xe8\xd5\xd0\
-\xec\xc4\x38\x7e\xbe\xee\x51\x66\x27\xc6\x0d\xca\x7e\xfb\x87\x8f\
-\x60\x8c\x03\xee\x46\xd0\xde\x07\x8d\x5d\x9e\xbc\xed\x70\x23\x0d\
-\x0e\xb5\x1d\xc8\x86\xfe\x5f\xa0\xa0\x5a\x45\xde\x12\xcd\xdc\xac\
-\x18\x45\x59\xe1\xdf\x71\x7b\x39\x64\xf2\x72\xd3\xd9\xfc\xf2\x86\
-\x61\xe4\xbf\x6d\x38\x0d\x28\x6e\x18\x7e\x61\x68\xee\x91\xac\x3f\
-\x5f\x50\x33\xa0\xff\x17\xb0\x6a\xc6\x6a\xab\x0f\xfe\xe2\xea\xe5\
-\xf3\x82\x33\xa6\x86\x66\x27\xc6\xf1\xd2\x8f\xfe\x15\xab\x6e\xb9\
-\x29\xc4\xe6\x26\xc7\x0f\x27\xd5\xd6\x4e\x6d\x43\x0b\xad\xb6\x2e\
-\x34\x11\x66\xc6\x46\x11\x18\x1e\xc5\xd7\x55\x16\x1c\xd7\xad\xa7\
-\xd3\x8d\x74\x76\x1b\x2f\x03\x4f\x78\xb6\x90\x52\x6b\x40\xf8\xaa\
-\xa8\x18\xa7\x6b\xe4\xea\x8b\x08\x2f\xac\x7d\xe8\xa6\xec\xf5\x88\
-\xf0\x10\xd6\x3f\xf1\x00\xcb\x96\x64\x53\xd7\xd8\xca\xa7\x07\xbe\
-\xe4\xf3\xaf\xcf\x51\x55\xd7\x34\x42\x37\x34\x2c\x82\xec\x3b\xbe\
-\x43\x66\x6e\x1e\x9a\x36\xfc\xd2\x60\xeb\x93\x07\x01\xf4\xc2\x0a\
-\x35\x1d\x8c\x6c\x80\xe2\xb2\x4a\x53\xa7\x73\x92\xe2\xc9\x48\x4d\
-\xb8\x21\xe2\x16\x8b\xc6\xc3\x2b\xf2\x58\xfb\xc8\x32\x00\x36\x6d\
-\xd9\xc3\xee\xbf\x7f\x39\xea\x9d\xa8\xdd\xd6\xca\x67\xfb\x76\x53\
-\x56\x72\x9e\x95\x8f\xad\xc5\xc7\xd7\x6f\x70\xac\xa3\x4f\x82\x76\
-\x9d\x55\x73\x74\x8b\x8f\x3b\x5f\x29\xcf\xcd\xa6\xb4\xa2\xd6\xd4\
-\x50\x5e\x6e\xc6\x0d\x91\xcf\x4a\x4b\xe2\x85\xb5\x0f\x93\x10\x1f\
-\xcd\xfe\xc3\x27\x78\x7b\xc7\xdf\x68\xb5\x75\x8e\x7b\x7e\x65\xf9\
-\x65\xf6\xef\xde\xce\x83\x8f\xad\x65\xe0\x12\xa6\x80\x1e\xa7\xf1\
-\x6f\xba\x52\x32\x1f\xa0\xca\x26\x54\x37\xf5\x98\x1a\x48\x4e\x88\
-\x99\x34\xf9\xbc\xdc\x34\x96\x2f\xcd\xe1\x72\x79\x35\xcf\xfe\xea\
-\x4d\xce\x5f\xae\x98\x94\x9d\xd2\x92\x73\x5c\x2a\x3e\x43\xea\x2d\
-\x99\x83\xb2\x5e\x97\xe4\xeb\x55\x36\xc9\xeb\xe8\x83\x8a\x36\xc1\
-\xe9\xe8\x33\x9d\x1c\x19\x1a\x3c\x61\x87\x33\x62\xa7\x91\x9a\x18\
-\x87\xdb\x50\x6c\x7c\xe7\x63\xf6\xfe\xf3\x38\x86\x31\x66\x41\x33\
-\x2a\x0a\x8e\xfe\x63\x58\x00\xdd\x0e\x49\xd0\x4b\x9a\x24\xbd\xbb\
-\xff\x7e\xe5\x6b\x35\xbf\x5d\xfb\xfb\xfb\x8e\xdb\x89\xbf\xbf\x2f\
-\x4f\xff\xcb\x32\x1e\x5e\xb1\x98\xfd\x87\x4e\xb0\xf9\xc3\x7d\x74\
-\x74\x76\x7b\xd5\xcf\xce\x48\x61\xd9\x92\x1c\x66\x27\xc6\x22\x08\
-\x27\xcf\x95\xf2\xfe\xae\x83\x74\x76\x8d\xdc\x0d\x8d\x75\xd5\xb4\
-\xb5\x36\x13\x1e\x31\x05\x00\xb7\x41\xb0\x9e\x13\xaf\x7a\x8e\x55\
-\x48\xa8\xc3\x0d\x4a\x33\xbf\xe2\xda\xda\xbb\x98\x11\x13\x35\x2a\
-\x71\x11\xe1\xde\x25\xd9\xac\x5f\xf3\x00\xf5\x4d\xad\x3c\xfb\xeb\
-\x4d\x5c\xba\x52\xe5\x55\x3f\x69\x66\x0c\x1b\x9e\xfe\x1e\x59\x69\
-\xc9\x23\xe4\xfe\x53\x66\xb1\xf1\x8d\x4d\xa6\xe7\xd1\x37\x57\x4a\
-\x08\x8f\x58\x0c\x80\xcb\xad\xac\x7a\xb8\x9f\xf2\xbf\x2b\x59\x51\
-\xdf\x25\x7c\x1a\x18\x40\xa3\x89\xb3\x86\x16\xdb\xa8\xe4\x67\x27\
-\xc4\xb1\xee\x89\xfb\x89\x9f\x3e\x95\xb7\xb6\xff\x8d\xfd\x87\x4f\
-\xa0\x94\x97\xca\x2d\xc0\x9f\x67\x1e\x5b\xc1\xca\x65\x77\xa0\x5b\
-\xcc\xcf\x94\x94\xc4\x59\x24\xa5\xa6\x73\xf9\xe2\xc8\x33\xa9\xb9\
-\xb1\x6e\xf0\xdf\x0e\x43\x3c\xc9\xd5\x57\x87\x99\x61\x8a\xf4\xc4\
-\xa9\xa6\x06\x8f\x1e\x3f\x8b\xdd\x05\x15\xad\xc2\x85\x7a\xe1\x62\
-\x83\xd0\x37\xe4\xea\xf0\xe6\x4b\xeb\xb9\x5c\x5e\xc3\xaa\xe7\x5f\
-\x61\xdf\xa1\xaf\x4d\xc9\x8b\x08\xf7\xe5\xcf\xe3\xc3\x4d\xbf\x60\
-\x76\x62\x2c\xff\xf1\x9f\x7f\xf0\xba\x20\x33\xc3\x15\xe9\xe9\xb7\
-\x9a\x8e\xb5\x36\x0f\x5f\x62\x1d\xe8\x02\x22\x00\x12\x67\x4c\x37\
-\x9d\xf4\x45\xe1\x79\x76\x1c\xa9\x24\x22\x7a\x16\xfe\x3a\xf4\xba\
-\xe0\x4a\x8b\xe0\x6f\x85\x8a\xab\x65\xec\xff\xeb\x27\x34\xd4\x99\
-\xa7\x60\x80\xd4\xa4\x78\x7e\xf8\xcc\x43\x44\x4f\x8d\xe0\x7f\x3e\
-\xd8\xcb\xc1\xa3\x27\xf1\xf5\xb1\x7a\xd5\xf7\xb7\xc2\xd2\xb4\x30\
-\x76\x9b\x8c\xd9\x7b\xaf\x7d\x1b\x3e\x9a\x32\x74\x84\x36\x94\x27\
-\x80\x79\x99\xa9\xa6\x06\x9d\x2e\x37\x3b\xb7\x6c\x66\xc1\x92\x7b\
-\x99\x91\x90\x82\x52\x8a\xc6\xba\x6a\x2e\x9e\x2d\xa4\xaa\xa2\xcc\
-\x2b\x91\x90\xe0\x40\xfe\x7d\xd5\x7d\xac\xc8\xcf\xe5\x93\xfd\x5f\
-\xb0\xe1\xbf\xde\xa6\xb7\x77\x5c\xdd\x12\xa6\x86\x07\x99\x73\x71\
-\x5e\xab\xe8\x74\x8b\x38\x75\xa5\xe4\x8a\xa0\x92\x00\xe2\xa7\x4f\
-\x25\x65\x56\x2c\xa5\x15\x23\x7b\x4b\x7d\xbd\x3d\x1c\x3e\xb0\x67\
-\x5c\xce\x35\x4d\xe3\xfe\xbb\x16\xf0\xec\xea\xfb\x69\x69\xeb\xe0\
-\xa9\x1f\xff\x9e\xca\x9a\x86\x71\xcd\x1d\x40\x4f\xaf\x79\x4a\x37\
-\xfa\x3f\x6c\x4d\x20\xc2\x9f\x1a\xcd\xd3\xab\xbc\x86\x35\x0f\xdf\
-\x33\x21\x47\xd7\x23\x26\x7e\x16\xab\x7f\xf0\x23\xd2\x96\x3c\x4a\
-\x7d\xa7\xc6\xa5\xf2\xea\x09\x93\x07\x68\xb5\x75\x99\xca\x23\x43\
-\xfc\xb9\x63\xa6\x62\x79\xaa\xc1\xed\x31\xc6\x5e\x0d\xd4\xf1\xa1\
-\x0a\xf9\x0b\x6e\x25\x2f\x37\x7d\xc2\x0e\x03\x82\x82\x59\xb1\xf2\
-\x71\x1e\x5f\xbb\x01\x8b\xc5\xc2\x8e\xf7\x36\xb1\xe7\xc8\x05\x26\
-\x7b\x76\x95\x56\x54\x9b\x07\x10\x16\x48\x54\x90\xc2\xc7\x02\x06\
-\xea\x98\x6e\xd5\x2d\x9f\x39\x5d\x86\xa2\xbf\x4b\x27\x22\xbc\xf8\
-\xfc\x6a\x7e\xfe\xda\x7b\x14\x9d\x2f\x1d\xd3\x91\x66\xb1\x90\x95\
-\xbb\x98\x45\xf9\x2b\x00\x38\x74\x60\x0f\xa7\x0a\x8e\x62\x18\x06\
-\x19\x99\x0b\x68\x99\x64\x39\x7d\xa4\xe0\x9c\xa9\x7c\x48\xa2\x51\
-\x3e\x56\xcb\x11\x3d\x63\x9a\x34\x14\x55\xbb\x0a\x11\xc9\x1d\x18\
-\x09\x08\xf0\xe3\x8d\x17\xd7\xf1\xda\xce\xaf\xf8\xe2\xe8\x61\xda\
-\xdb\x46\x16\xe5\x11\x53\xa6\x91\x3c\x27\x83\xcc\xdc\x45\x84\x84\
-\x86\x73\xf1\x4c\x21\x47\xfe\xb9\x97\xee\xce\x8e\x61\x7a\xd5\xed\
-\x13\x6f\x80\x9f\xbe\x78\x85\xd2\xab\xe6\x3d\xde\x5b\x52\x66\x0e\
-\xf0\x2f\xc8\x98\x26\x0d\x9e\x0b\xbe\xc8\x36\x20\x77\xa8\xa2\xc5\
-\xa2\xb1\x68\xf1\x12\xe6\x66\xe5\xd1\xd6\xda\x44\x6b\x4b\x13\x86\
-\xdb\x4d\x60\x70\x08\x61\x61\x91\x04\x04\x5d\xbb\x1f\xfd\x69\xcb\
-\x26\xaa\xae\x9a\x67\x23\x35\xc1\x2d\x64\x77\x38\x79\xfd\x7f\x77\
-\x9b\x9e\x25\x3e\x56\x2b\x0b\xb2\x3c\xe5\xb0\x20\xdb\xa0\xbf\x22\
-\x73\x2a\x6d\xa7\x55\x8c\x57\x80\xc1\x96\x85\xd3\x80\x2e\x3b\x20\
-\x42\x78\x64\x14\xe1\x91\xde\xaf\x12\xf5\xd5\xe6\x75\xc4\x68\x70\
-\x1b\xd0\xda\xe3\xe9\xf8\x0d\xc0\xe1\x72\xf1\xe2\xc6\x2d\x94\x57\
-\x9a\x9f\x29\x77\x2d\xca\x24\xc0\x73\x2f\xeb\x76\x28\xed\x4f\xd0\
-\x5f\x13\xcf\x8f\x93\x16\x51\xbc\x3b\x54\x59\x17\xcf\x09\xfd\x6d\
-\xe2\xe8\x55\x8d\x2f\x2b\x3c\x5b\xec\x62\xd9\x37\x7c\xff\x67\x6f\
-\xf0\x55\xd1\x45\x53\x5d\xab\x6e\x61\xcd\x43\x77\x7b\xfe\x10\xde\
-\x1e\x78\x10\x19\xa4\x28\x4a\xdb\xa8\xc4\xf8\x3e\xfd\xbf\x82\x08\
-\x2c\x9a\x65\xf0\x59\x99\x36\xa2\x26\xbd\x19\x30\x0c\x83\xe2\xb3\
-\x85\x14\xd8\x5a\xd8\x51\x53\xc2\xd9\x92\xf2\x51\xf5\x9f\x7e\x64\
-\x39\x33\x62\xa2\x50\xd0\xe9\xd6\xb5\x8d\x03\xf2\xc1\x00\x32\xe3\
-\xa5\xa6\xa8\xc6\xfd\xdf\xc0\x6f\x07\x64\x7e\x3a\xcc\x8d\x52\x9c\
-\xa9\x9b\xf4\x4b\x94\x57\x0c\x74\xfe\xc6\x83\xa5\x0b\x6e\x65\xf5\
-\xca\x3b\x01\x10\xc5\x4b\x43\xdf\xd6\x86\x15\x00\x46\x9d\xf6\xba\
-\x82\x53\x43\x65\xb3\xc2\x15\xc1\xe3\x2f\x07\x6e\x3a\xee\xcb\x9f\
-\xc7\x6f\x36\x3c\x39\x50\xd4\x17\x19\xf5\xda\x9b\x43\xc7\x87\x05\
-\x90\x93\x23\x4e\x0c\xed\x11\x60\x30\x17\x8a\x40\x56\xcc\xc4\x4f\
-\xa3\xd0\xf0\x48\xa6\x46\xc7\x4e\x8e\x35\x10\x12\x14\xc0\x2f\x9f\
-\x5b\xc5\x2f\x9f\x5b\x35\xd0\xca\xe9\xb2\xa0\x3d\x9e\x93\x23\xc3\
-\xfa\xf9\x23\x3e\xd3\xec\x78\x29\x3b\x59\xeb\x7a\x4a\x94\x7c\x0c\
-\x58\xc0\x93\x29\xe2\x42\xd5\xb8\x72\xba\x6e\xb5\x72\xfb\xfc\xa5\
-\x2c\x5c\xba\x0c\xab\x8f\x0f\x4d\xf5\x13\x7a\xb3\x23\x66\x5a\x24\
-\x0f\xde\xb3\x90\xef\xde\xb3\x90\xe0\xc0\xc1\x14\xe5\x06\xb5\xfa\
-\xb6\x58\xb9\x34\xc2\x9f\x99\x91\xec\x18\xfd\xd3\x93\xb5\xee\xf5\
-\xa2\xd8\x3c\x20\xcb\x89\x53\xc4\x86\xc0\xa5\x66\xc1\xe6\xe5\x74\
-\x4d\x9e\x93\x4e\xfe\xf2\x87\x08\x0d\x8f\x1c\x37\x61\x7f\x3f\x1f\
-\x92\x67\xc6\x92\x9d\x91\x42\x76\x46\x0a\xb7\xcd\x4d\xbc\xbe\x07\
-\xa4\x44\xd4\xba\xac\x18\x7d\xaf\xd9\xfc\x51\x97\xf4\x54\xb5\xeb\
-\x29\x25\xf2\x2e\x43\x02\x55\xca\xf3\xd4\x54\x69\xbb\x36\x75\xeb\
-\x5b\xaf\x92\x77\xf7\x03\x24\xa6\xdc\x32\xc2\x86\xcb\xe5\xc4\xd6\
-\xda\x4c\x6f\x4f\x37\x2e\xa7\x03\x43\x29\x74\x8b\x4e\x4c\xa4\x1f\
-\x77\xa7\x87\x11\x3e\x7a\xc3\xc0\xad\x14\xcf\x65\xc7\x59\x36\x7b\
-\x53\x18\x73\x4f\x9c\xaa\x75\xad\x34\x94\x7c\x20\x30\xe8\xa9\xbd\
-\x17\x0e\x95\x5f\x5b\x25\xc3\xed\x46\xf3\x52\x1e\x7a\x43\x5a\xb4\
-\x22\x25\x72\xd4\x57\xcb\x0e\x50\x6b\x6e\x8f\x35\x5f\xf9\x01\x8c\
-\xf9\x4a\x99\x15\xa3\xef\xd1\x0c\x2d\x7b\x68\x76\xf2\xbf\xae\xf6\
-\x9f\x28\x79\x1f\x0b\x24\x84\x8d\x4a\xbe\x48\x19\x5a\xf6\x58\xe4\
-\x61\x1c\x01\x00\x64\xc5\x4b\x69\x47\x8c\x36\x4f\x84\x0d\x0a\x3a\
-\x2d\xe3\x9a\x35\x12\x91\x81\x90\x1d\xa7\xf8\x4e\x92\x81\x97\x1e\
-\x71\x8f\x82\x97\x7c\x6d\xda\xc2\xec\x78\xf1\x5e\xea\x0d\xc1\x84\
-\x4f\xa8\xc2\x0a\x35\xdd\xe2\x63\xfc\x64\x5f\xb1\xb6\xc1\xee\x1e\
-\xff\xfc\xb4\x69\x8a\x94\x29\x5e\x57\xbd\x1b\x61\xb3\x4b\xd7\x7e\
-\x37\xd1\xff\x00\x32\xe9\x23\xf6\xc3\x53\x6a\x56\x97\xd3\x78\xb5\
-\xa3\x4f\xee\xeb\xb0\x4b\x90\x97\x2e\xca\x20\x16\x27\x18\x4c\x09\
-\x18\x26\x52\xa0\x0a\x14\xb2\xdd\xcf\xd0\x3e\x4c\x8b\x97\x89\x3f\
-\x7f\x72\x03\x01\x0c\xc5\xae\xb3\x6a\x4e\xbb\xdd\x58\xef\x72\x69\
-\x77\xf4\x38\xd5\x4c\xbb\x21\x21\x2e\xb7\xb2\xba\x0c\x11\x05\x58\
-\x35\x8c\xc5\x09\xc6\x37\x21\xbe\x72\x49\x94\x2a\x31\x44\x1d\xd7\
-\x2d\x96\x43\xb7\x45\x8b\x59\x1b\x6a\x42\xf8\x7f\xd2\xb5\x8d\x07\
-\x16\x9e\x74\x46\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
-\
-\x00\x00\x19\xd5\
-\x89\
-\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
-\x00\x00\x60\x00\x00\x00\x60\x08\x06\x00\x00\x00\xe2\x98\x77\x38\
-\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
-\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x02\x0b\x00\x00\x02\x0b\
-\x01\xf7\x4d\xfa\xeb\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
-\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
-\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x19\x52\x49\x44\
-\x41\x54\x78\x9c\xe5\x9d\x79\x60\x95\xd5\x9d\xf7\x3f\xe7\x79\xee\
-\xbd\xd9\x57\x12\x02\xd9\x48\x08\x10\x20\xec\x84\x4d\x50\x36\x17\
-\x16\x99\x22\x52\xec\x54\x5b\x44\x5c\xea\x54\xa7\xd5\xa9\xd3\xce\
-\x74\x3a\xb5\x9d\xb7\x7d\x6b\x3b\x6f\xa5\x76\x46\xe5\xd5\xb8\x80\
-\x5a\x14\x45\x05\x51\x51\x16\x95\x45\xd6\x40\xd8\x24\x21\x10\x42\
-\xc8\x4a\xf6\xe5\xee\xf7\x39\xf3\xc7\x85\x90\xe5\xde\x9b\xfb\xe4\
-\xde\x04\x3a\xfd\xfe\x95\x9c\xe7\x9c\xdf\x39\xf7\xfc\xce\xf2\x3b\
-\xbf\xe5\x1c\xc1\x0d\x8a\x37\xf6\xcb\x54\x87\x91\x85\x4e\x8d\x5c\
-\x97\x53\xe6\x38\x34\x91\x6e\x77\xc9\x38\xa7\x26\x8c\x2e\x0d\x55\
-\x93\xa8\x0e\x0d\xa1\x00\xaa\x82\x34\x28\x38\x85\x82\x2b\x33\x4e\
-\x56\x0c\x4b\xe0\x88\x40\x16\x69\xc8\x33\x8a\x50\x0b\x5d\x66\x8a\
-\x72\xb3\x44\xd3\xf5\xfe\x4d\x9e\x20\xae\x77\x03\xae\xe2\x85\xaf\
-\x65\x86\x41\xe5\x01\xbb\x4b\x2e\x32\xdb\xc5\xa8\x56\x3b\xe1\x52\
-\xea\xa7\xa3\x0a\x98\x95\xa9\x11\x17\xd6\x39\x5d\x48\xce\x03\xdb\
-\x41\x6e\x57\x5d\xea\xf6\x71\x43\x44\x43\x50\x1a\x1e\x20\xae\x2b\
-\x03\x5e\xde\x27\x87\x49\x83\xf6\x74\xab\x4d\x2c\x6e\xb4\x8a\xd8\
-\xde\x74\xb8\x27\xcc\xca\x90\x24\x44\xf8\x24\xe6\x02\xb9\x57\x08\
-\xde\xc0\xa6\x6e\x9c\x98\x29\x1a\x83\x53\xb3\x7e\xf4\x3b\x03\x5e\
-\xdd\x25\x43\x0d\xd1\x3c\xd9\x68\x95\x8f\xd4\x9a\x45\x7a\xb0\x3a\
-\x3d\xcc\x00\x36\x27\x24\x44\x4a\x6e\x1a\xa2\x8b\xa8\x55\xc0\x66\
-\x34\xf9\xfa\x84\x54\xf5\x53\x21\x84\x16\x9c\x16\xf9\x87\x7e\x63\
-\xc0\x33\x7b\x64\x54\x84\xca\x1f\x9b\x2c\x72\xa5\xcd\x29\x8c\xc1\
-\xa2\x1b\x61\x82\x11\x89\x92\xc4\x70\xc9\xbe\x52\x85\xf1\xc9\x92\
-\x44\xdf\xa3\xdf\x17\x4e\x21\xe5\xef\x9b\x52\xd4\xb7\xe6\x0a\xe1\
-\x0c\x56\x1b\x7d\xa1\xcf\x19\xf0\xcc\x19\x19\x15\xd5\x28\xd7\xd5\
-\xb7\x89\x25\x76\x17\xaa\xde\xf2\x06\x15\x26\x25\x4b\xe2\xc3\x25\
-\x56\x27\x14\x54\x28\x4c\x4c\xd1\x08\x35\xb8\xbf\x1b\x55\x68\xb1\
-\xc2\xbe\x52\x05\x4d\xc2\x94\x34\x49\x5c\x98\xc4\xa0\x04\xd4\xec\
-\x62\x90\xbf\x6b\x4a\x56\x5f\xef\x6b\x46\xf4\x29\x03\x9e\x3f\x20\
-\x9f\x6a\x32\xf3\x1f\x16\x07\x21\xbd\xa5\x61\x50\x20\x3a\x14\x4c\
-\xaa\x64\x5a\x9a\xa4\xc5\x0e\x8a\x80\x48\x93\xfb\x7b\x8b\x0d\x76\
-\x97\x28\xd8\x5d\xd7\xca\xa4\x44\x4b\xa6\xa4\x05\x65\x6d\x2b\x10\
-\x28\x3f\x9c\x98\x22\xf6\x06\x83\x98\x27\xf4\x09\x03\xde\x29\x90\
-\x99\xa5\x8d\xf2\xcb\x16\x9b\x48\x0b\x16\xcd\x09\x83\x25\x19\xf1\
-\x9d\x3b\x55\x4a\xf8\xaa\x44\xa1\xc1\xd2\x39\x6f\x46\x9c\x64\x42\
-\x72\x90\x36\x17\x90\x48\xde\x50\x0d\xca\x4f\xc6\x0f\x12\x35\xc1\
-\x22\x7a\x15\x41\x67\xc0\xd1\x0a\xe7\xd2\x36\xbb\x78\xf5\xb3\x22\
-\x25\x36\x58\x34\x23\x4d\x70\xeb\x70\xdf\x7b\xa3\xc5\x09\x6d\x36\
-\x28\xa9\x77\xff\xa4\x20\xcd\x80\x8e\xa8\x13\xc8\xd5\x13\x53\x0c\
-\x1f\x06\x93\x68\xd0\x18\xf0\xcc\x19\x19\x15\xd9\x20\x77\x59\xec\
-\x4c\x6e\xb5\x0b\xa2\x42\x40\x93\xd0\x6a\x77\x8f\xd4\x40\xa0\x08\
-\x98\x31\x44\x62\x50\x24\x2e\x0d\x8e\x57\x2a\xcc\x1b\xd6\xaf\xc2\
-\xca\x55\x48\xe0\xcf\x21\x8d\xca\x53\x39\x39\xc2\x1e\x0c\x82\x41\
-\x61\xc0\xba\x7c\x39\x3b\xd2\x24\xf3\x92\x22\x65\xd6\xc9\x2a\x41\
-\x45\x8b\x60\xd1\x48\x0d\xa3\xe2\xee\xfc\x03\x17\x05\x55\xad\xfa\
-\xaa\x12\x40\xa8\x11\x2c\x8e\x6b\x69\x06\x15\x52\xa2\x24\x59\x03\
-\x24\xd1\xa1\xc1\x68\x79\x2f\x21\xe4\x41\x21\xd4\x7b\x26\x0e\x16\
-\x17\x02\x26\x15\x28\x81\xbc\x83\xf2\x21\x4d\xe3\xb9\x61\x89\x5a\
-\xe8\xbe\x52\x05\x9b\x13\x42\x0d\x70\xc7\x08\x8d\xea\x56\xc1\xe9\
-\x1a\x81\xd9\x0e\x4e\x3f\x07\x6c\x98\x11\x52\x63\x24\x99\x71\x92\
-\xb3\xb5\x82\xb2\x66\x41\x66\x9c\x24\x29\x4a\x12\x1f\x0e\x81\x09\
-\x37\x6e\x34\x34\xb5\x10\x17\x13\x15\x28\x99\x7a\xa4\x72\xe7\xa4\
-\x54\xf1\x75\x20\x44\x02\x62\xc0\x4b\x07\xe4\xd3\x8d\x56\xf9\x8b\
-\xb9\x59\x52\xd9\x7f\x51\x50\x73\x65\x94\x2b\x02\x26\xa7\x4a\x0a\
-\x2a\x04\x76\x97\x5b\x92\x19\x9d\x24\x31\xaa\x70\xb2\x4a\x60\xf3\
-\x20\xd8\x0d\x8c\x94\x64\x27\xba\x3b\xf9\x6a\xa3\x2e\xd4\x0b\x92\
-\xa2\x24\x61\x41\x3a\x35\x38\x5d\x2e\x36\x7d\xba\x97\x8d\x5b\xbf\
-\x64\xe3\xf3\xbf\x08\x06\xc9\x36\x29\xb4\xe5\x93\x93\x8d\x9f\xf6\
-\x96\x80\xa1\xb7\x05\x5f\x38\xe0\x7a\xfe\x52\x33\x8f\x4e\x4e\x81\
-\x26\x2b\x5c\x6e\xbb\xc6\x4b\x4d\x42\xe1\x65\xc1\xc0\x48\x49\x65\
-\xb3\xc0\xa9\xc1\xf1\xca\xee\xbc\x76\xd8\xed\x34\xd4\xd5\x80\xb5\
-\x11\x11\xd2\x44\xc9\x51\x33\xad\x66\x2b\x9a\x26\x69\x6d\xb3\x20\
-\x71\x6f\x1e\x61\x21\x26\x42\x42\x8c\x44\x84\x85\x12\x16\x16\xca\
-\xe0\x81\xf1\x0c\x49\x1e\x48\x72\xd2\x00\xbf\xdb\x7b\xf0\x58\x21\
-\x6b\x5e\xd9\xc4\xc5\x8a\x1a\xa2\xa3\x22\x7a\xfb\xb3\xbb\x22\x42\
-\x48\xe5\xc3\xfc\x72\xe7\xfd\x93\x52\x0c\x7f\xe9\x0d\x81\x5e\x31\
-\x60\xed\x7e\xd7\xda\x8a\x66\xe5\x61\x29\xa1\xbc\x19\xea\xda\x94\
-\xf6\x8d\x36\xdc\x04\xd3\xd2\x34\xea\xcc\x82\x0b\x0d\x02\xd7\x95\
-\x74\x87\xc3\x4e\x65\xd9\x05\x2a\x2e\x95\x50\x59\x56\xca\xe5\x9a\
-\x4a\x9a\x9b\x1a\x7a\xbd\x43\xe7\x8e\x1b\xc1\x9a\x7f\x7f\xb4\xc7\
-\x7c\x55\x97\xeb\x79\xee\xb5\x0f\xf8\xea\xc0\x89\x5e\xd5\xe3\x07\
-\x4c\x20\xde\xc8\x2f\x77\xca\x49\x29\x86\x0d\x7a\x0b\xeb\x66\xc0\
-\x0b\x07\xe4\x6f\x2a\x9a\x79\xf8\x6a\xbf\x55\x36\x77\x1e\xd9\x66\
-\x3b\xec\x3a\xe7\x5e\xa9\x2d\x6d\x6d\x14\x9e\x3a\xca\xf9\xa2\x53\
-\x5c\x2c\x39\x8b\xd3\xe9\xe8\x4a\xae\xcf\x60\x77\x38\xf8\xcb\xe6\
-\x5d\xac\xdf\xb4\x03\xab\x2d\x28\x02\x8b\x2f\x28\x08\xb1\xee\x48\
-\x85\xa3\x61\x72\xb2\x71\x9b\x9e\x82\xba\x18\xf0\xda\x21\xf9\xf0\
-\x85\x46\xfe\xb5\xa7\x41\x5b\x7a\xae\x90\xe3\x47\xf6\x51\x7c\xe6\
-\x04\x2e\x97\xcb\x77\xe6\x3e\xc0\xbe\xfc\xd3\xfc\x29\xef\x7d\xca\
-\xab\x6b\xfb\xaf\x52\x89\x51\xa0\x6c\xca\xbf\x24\x6f\xd5\xb3\x31\
-\xfb\xcd\x80\x75\x05\x72\x5e\x69\xad\x7c\x51\x93\xde\xf7\xed\x4b\
-\xa5\xe7\xd9\xbb\x73\x2b\x65\x17\x8a\xfd\x25\x1b\x54\x54\x54\xd7\
-\xf1\xe2\x1b\x5b\xd8\xf9\x75\xc1\x75\xa9\x1f\x08\x6f\x75\x68\x1f\
-\xad\x3f\x22\x97\x7e\x6f\xb2\xd8\xed\x4f\x01\xbf\x18\xb0\xf6\x73\
-\x19\x53\xd9\xc0\x16\x87\x4b\x78\xec\xfd\xc6\xfa\x5a\x3e\xdb\xf2\
-\x36\x17\xcf\x17\xe9\x69\x6c\xd0\x60\xb3\x3b\x78\xe3\xfd\x1d\xbc\
-\xf9\xc1\x4e\xec\x8e\xfe\x5b\xe6\x3c\x21\xdc\x48\x7c\xbd\x55\x6e\
-\xc9\xdb\x23\xd3\x56\xcf\x12\x2d\x3d\xe5\xf7\x8b\x01\xf6\x08\xf6\
-\xb5\xb5\x12\xde\xed\x83\x94\x1c\xda\xb7\x93\xbd\xbb\x3e\xc1\x79\
-\x1d\x7f\xf8\xd3\xcf\xae\x63\xf7\xa1\x93\xd7\xad\xfe\x8e\x50\x04\
-\x84\xa8\xc4\x28\x06\x36\x03\x73\x7b\xca\xdf\x23\x03\x5e\x3a\x20\
-\x7f\x5f\xd6\xc4\xe8\xae\xe9\x0e\xbb\x9d\x4f\xde\x7f\x83\xa2\xd3\
-\xd7\x6d\xba\xb7\xc3\xd2\xf7\x9b\xac\x2e\x4c\x4d\x93\x80\x9c\x7d\
-\xb4\xc2\xb9\x74\x62\xb2\xe1\x03\x5f\x79\x7d\x1e\x2c\x5f\xc9\x97\
-\xa3\xab\x5b\xf9\x49\xd7\xf4\xd6\x96\x26\xfe\x92\xb7\xe6\x86\xe8\
-\xfc\x1b\x18\xc2\xe1\x14\x79\xaf\xe4\xcb\x6e\x83\xb7\x23\x7c\x32\
-\xa0\xd9\xc2\x56\xbb\xab\xf3\x69\xd9\x6a\x6e\x63\xe3\xba\xe7\xa9\
-\xa9\x2a\x0f\x46\x23\xff\x57\xa3\xa4\x41\xc4\xb7\x9a\xe5\x66\x5f\
-\x79\xbc\x32\xe0\xd5\xc3\xf2\x47\xf5\x6d\x64\x74\x4c\xb3\xdb\x6d\
-\xbc\xf7\xe6\x5a\xea\x6a\xaa\x82\xd4\xc4\xff\x9d\x70\x6a\x50\x5c\
-\x27\x38\x53\x23\xa8\x35\x8b\xac\xbc\xc3\xf2\x71\x6f\x79\x3d\x32\
-\xe0\xd5\x12\x19\x5a\xd3\xca\xef\xba\xa6\x6f\xff\x68\x23\x95\x97\
-\x4a\x83\xd2\x48\x21\x04\x43\x52\x93\xc8\x1d\x37\x82\x5b\xa6\x8d\
-\xc5\x68\xd0\x6d\xad\xbc\x21\xd1\x62\x83\xed\x67\x15\x4e\x56\x5d\
-\xd3\x02\xd4\xb6\xc8\x3f\xbc\x5a\x22\x3d\xea\x6f\x3d\x6e\xc2\xce\
-\xcb\xda\x4b\x6d\x76\xa5\x53\x81\xa2\xd3\x05\x9c\x2e\x38\x14\x50\
-\xe3\x84\x10\x4c\x9f\x30\x92\x3b\xe7\x4f\x67\xfc\xe8\xa1\xc4\x46\
-\x47\xb6\x7f\x5b\xb4\xea\xdf\x70\xb4\xb4\x05\x44\xff\x7a\x43\x4a\
-\x38\x5c\xae\x60\xed\xa2\x6c\x6c\x73\x88\x10\xad\x56\x7b\x1e\x78\
-\xa0\x6b\x99\x6e\x0c\x78\xba\x44\x86\xd6\x96\x72\x4f\xc7\x34\x8b\
-\xb9\x95\xcf\x3f\x7a\xbb\xd7\x0d\x13\x42\xb0\x70\xce\x14\xee\xbb\
-\x6b\x3e\xe9\xc9\x03\x7b\x4d\xe7\x46\x47\x75\xab\xa0\xc9\xe2\xf9\
-\x5b\x4d\x8b\xf8\xde\xda\xc3\xf2\xb1\x47\x72\x85\xb9\x63\x7a\x37\
-\x06\xa4\xd5\xf2\xa7\x52\x3b\x9d\x14\xc0\x27\x0e\xec\xc4\xd2\xd6\
-\xbb\xd1\x39\x70\x40\x2c\xff\xfe\xa3\xfb\x98\x30\x3a\xab\x57\xe5\
-\xff\x9a\xd0\xe6\x43\x1a\xb6\x38\x85\x01\xa7\xf6\x67\x60\x75\xc7\
-\xf4\x4e\x7b\x80\x94\x52\x64\xc4\x69\xf3\x26\xa5\x4a\x92\xa3\x25\
-\xe1\x46\x48\x0b\x6f\xe1\xd0\x7e\xbf\x4e\xd5\xdd\x30\x32\x2b\x9d\
-\xbc\xdf\x3f\xf9\x37\xd1\xf9\x0e\xed\x9a\x3d\xda\x1b\x9a\xac\xca\
-\xbd\xef\x48\xd9\x69\xb3\xeb\xc4\x80\x63\xe5\xae\x45\x71\x61\x0c\
-\x4b\x8f\x91\x4c\x4d\x93\xdc\x3e\x42\xe3\xd0\xbe\x5d\x58\xac\xfa\
-\x0f\x3a\xa3\x86\xa5\xf3\xdc\x2f\x1f\x0d\x86\xe5\xe9\x86\x47\x9d\
-\x19\xbe\x3a\xaf\xd0\xda\x43\x37\xb5\xda\x09\xb1\xe6\x6b\x3f\xeb\
-\x98\xd6\x79\x06\x28\xca\xca\x8e\xff\x6b\x9a\x64\xdb\x97\x87\x75\
-\x37\x68\xe0\x80\x58\x7e\xf7\xb3\xd5\x84\x87\x5f\x4f\xc3\x6d\xdf\
-\xa3\xcd\x0e\xfb\x4a\x05\x7b\x4a\x14\x5a\x6c\xfe\x95\xa9\xb7\x28\
-\x9d\x8c\x18\xed\x7b\xc0\xe1\x73\x32\x06\xa9\xdd\xd9\xf1\xe3\xa9\
-\xb3\x17\xb8\x5c\xaf\xcf\xab\x5b\x08\xc1\xcf\xfe\xe1\x3b\x0c\x88\
-\x8d\xd6\x55\xae\xbf\x30\x34\x3d\x99\x27\x1f\x5c\xe6\xf5\x7b\x45\
-\x75\x1d\x45\x25\xe5\x94\x96\x57\x51\x53\xdb\x44\x4b\x9b\x19\xa7\
-\xcb\x45\x88\xc9\x48\x54\x44\x38\x99\x69\x83\x18\x96\x91\xcc\xf0\
-\xa1\xe9\x1c\xb9\x64\xa4\xde\xcb\xa6\xeb\x0d\x8d\x66\x52\x5e\x3b\
-\x2c\xb3\xee\xcf\x15\xe7\xa0\x03\x03\x44\x88\xf6\x1d\xa0\x93\x53\
-\xf7\x17\xbd\x50\xeb\xde\x3a\x73\x22\x53\xc7\x67\xeb\x2e\xd7\xd7\
-\x88\x08\x0f\x63\xf5\x3d\x0b\xb8\x7b\xc1\x2c\x54\xf5\xda\xc4\x77\
-\xba\x5c\x1c\x3a\x5e\xc4\xae\x7d\xc7\x38\x7c\xbc\x88\x9a\x3a\xff\
-\x1c\xa5\xc3\x23\x22\xc8\xce\x99\xc4\xf8\x29\x33\x49\x18\x38\xd8\
-\xef\x76\x68\x80\x4d\xd3\x9e\x06\xbe\x07\x1d\x19\x20\xe4\xbd\x5d\
-\x6d\xf4\xfb\x8f\x9d\xf1\x9b\x30\x80\xa2\x28\xac\x5a\x71\x87\xae\
-\x32\x7d\x0d\x21\x04\x0b\x66\xe7\xf2\xe8\x7d\x4b\x88\x8f\xbd\xb6\
-\x1f\x99\xad\x36\xb6\x6c\xdf\xcf\xdb\x5b\xbe\xf0\xbb\xd3\x3b\xc2\
-\xdc\xd6\xc6\xd1\x83\xbb\x29\x38\xbc\x97\xf1\xb9\x33\x99\x39\x6f\
-\x11\xa1\x61\xdd\x15\xc6\x9e\xd0\x6a\x53\xda\x57\x1a\x03\x5c\x59\
-\x7e\xd0\x6e\xea\x98\xc9\x6a\xb3\x53\x56\x71\x59\x57\xa3\xa6\x4f\
-\x18\x79\x43\xc9\xf9\x23\x32\x53\x79\xe2\xc1\x65\x8c\xcd\xce\x6c\
-\x4f\x73\xb9\x34\xde\xdf\xb6\x97\x57\x36\x6e\xa3\x39\x08\x07\x3f\
-\x4d\xd3\x38\x7a\x70\x37\xe7\x8a\x4e\xb1\xfc\xbe\x1f\x10\x9f\x98\
-\xd4\x63\x99\x66\x2b\xb1\x6b\x0f\xcb\xf4\x47\x72\xc5\x45\x03\x80\
-\x12\xea\x9a\x0d\xa2\x93\x78\x74\xbe\xac\x12\x4d\xd3\xe7\x7d\xb6\
-\x68\xde\x34\x5d\xf9\xfb\x0a\x51\x11\xe1\x3c\xf4\xdd\x45\x2c\xbd\
-\x6d\x06\x8a\x72\x6d\xb9\x29\xf8\xe6\x1c\x7f\x7c\x79\x13\xe7\x4a\
-\x2b\x82\x5e\x67\x73\x63\x3d\x6f\xe5\xad\x61\xd9\x7d\x8f\x90\x9c\
-\x9a\xe1\x33\xaf\x94\x60\x70\x69\x8f\x00\x3f\x37\x00\x08\x29\xe6\
-\x76\xb5\x34\x9e\x3d\xaf\x4f\xdb\xa9\xaa\x0a\x53\xc7\x8d\xd0\x55\
-\x26\xd8\x10\x42\x70\xfb\x2d\x93\x79\xec\xfb\x7f\xd7\x49\xfc\xad\
-\x6b\x6c\xe6\xf9\xf5\x5b\xf8\xec\xab\x23\xc8\x60\x45\x84\x78\x80\
-\xd5\x62\xe6\x83\xb7\x5e\xe6\xfb\x8f\x3e\x45\x64\x54\x8c\xcf\xbc\
-\x16\xa7\x58\xcc\x55\x06\x48\xc1\xbc\xae\x19\x4a\xab\xea\x74\x55\
-\x3e\x72\x68\xfa\x75\x15\x3b\x47\x66\xa5\xf3\xe4\x43\xcb\x18\x3d\
-\x6c\x48\x7b\x9a\xd3\xe5\x62\xe3\xd6\xaf\x78\x65\xe3\x36\x2c\x16\
-\x3f\xe5\xc4\x00\x61\x6e\x6b\x61\xeb\x7b\xeb\x58\xb1\xf2\x87\x08\
-\xe1\x5d\xdb\xdf\x6a\x17\xd9\x00\x86\x2b\xeb\xff\xd8\x8e\x1f\xcf\
-\xd7\x0b\x0a\x2b\xf5\x35\x38\x2b\xc3\x7f\x49\x20\xd8\x78\xe0\xdb\
-\x77\x30\x26\x3b\x13\x45\xb9\x36\x8d\x8f\x9c\x38\xcb\x1f\xf3\xde\
-\xa3\xf4\x52\x75\xbf\xb7\xa7\xac\xa4\x98\xfc\x03\xbb\x99\x3c\x7d\
-\xb6\xd7\x3c\x16\x27\xa1\x9b\x8a\xe5\x40\x83\xc1\x44\xb6\xd6\x41\
-\xfc\xb1\x3a\xe1\x74\x8d\xc0\x66\xd1\x27\xe0\x5e\xcf\xcd\x77\xdc\
-\xa8\xa1\xed\x7f\x57\xd7\x36\xf0\x5f\xaf\x7d\xc8\xae\xfd\xd7\xd7\
-\x5a\xf7\xf5\x97\xdb\x18\x37\x69\x3a\x46\x93\xe7\xd8\x14\x29\xa1\
-\xb1\x91\x85\x06\x4d\x75\x65\x23\x05\x1a\xd0\x6a\x85\x53\x35\x02\
-\xa7\x0b\x6c\x36\xab\xae\x0a\x13\xe2\x7d\xaf\x79\xc1\x46\x4c\x54\
-\x67\x91\xcf\xee\x74\xb2\xe1\xc3\x2f\x58\xb7\xe9\xf3\xfe\x70\xc4\
-\xea\x11\x56\x73\x1b\xc7\xf3\xf7\xfb\x9c\x05\x4e\xa7\x76\x8b\xe1\
-\x64\xa5\x72\x4f\x9d\x59\xd0\x68\xed\xec\x25\xe8\xb0\xeb\x63\x40\
-\x78\x68\xaf\xa3\x90\x74\x21\x32\x3c\x8c\xd5\xdf\x59\xc0\xb2\x3b\
-\x66\xb5\xa7\x7d\x7d\xf4\x1b\xfe\x94\xb7\x89\x4b\x55\xfd\xe8\x88\
-\xe5\x07\x4e\xf4\xc4\x00\x29\x46\x1a\x1a\xad\x62\x7c\xd7\x10\x1f\
-\x00\x45\xd5\xe7\xb5\x18\x62\x0a\x5a\xe0\xa3\x47\x28\x8a\x60\xd1\
-\xdc\xa9\x3c\xf2\xdd\xc5\xed\x12\x4e\x45\x75\x1d\x7f\x7a\xf5\x7d\
-\xf6\x1e\x3e\x15\x30\xfd\xb8\x98\x28\x66\xe6\x8e\x26\x33\x6d\x30\
-\xc9\x49\x03\x08\x0b\x31\x51\xdb\xd0\xcc\xb1\xd3\xc5\xec\xd8\x77\
-\xac\x57\x9b\x78\x6d\x75\x05\x8d\x0d\x75\xc4\xc6\x79\x76\x22\x76\
-\x6a\xa4\x1a\x9c\x12\x8f\xea\xca\xb0\x10\x7d\x23\xda\xe1\xe8\xbb\
-\x60\xc2\xd1\xc3\x86\xf0\xc4\x83\xcb\x18\x35\x2c\x1d\x08\xae\x23\
-\xd6\x8c\x89\xa3\xf8\xfb\x6f\xcd\x65\xc2\xe8\xac\x4e\x67\x86\xab\
-\x58\x30\x3b\x97\x1f\xdc\x7b\x27\xbf\x7f\xf1\x6d\xbe\x3a\xa8\xdf\
-\xf7\xe8\x5c\xe1\x49\xaf\xb3\xc0\xe1\x12\xb1\x86\x99\xe9\x5a\xc5\
-\x91\x72\x11\x53\xd5\xd2\xf9\x20\x20\x8c\x26\x5d\x15\x59\xec\xc1\
-\x5f\x77\xe3\x62\xa2\xf8\xc1\xbd\x8b\x59\x38\x67\x6a\xbb\x84\xf3\
-\xd5\x81\x13\xfc\xf9\xf5\x0f\xa8\xac\xa9\x0f\x88\xf6\xd8\xec\x4c\
-\x1e\xbf\xff\x5b\x8c\x1e\x3e\xa4\xc7\xbc\xb1\xd1\x91\x3c\xfd\xe4\
-\x2a\x1e\xff\xcd\x7a\x4e\x9d\x38\xa6\xab\x1e\x5f\x0c\xb0\x3b\x65\
-\xa8\xc1\xa8\x12\x3e\x25\x4d\xb2\xaf\x54\x50\xd7\xe1\x64\x6e\xd2\
-\xc9\x80\xfa\x86\x1e\xbd\xf0\xfc\x86\xaa\x2a\xdc\xbd\x60\x16\x0f\
-\xdc\xb3\x80\xc8\x70\xb7\x7e\xf0\x62\x45\x0d\x6b\xf2\x36\x71\xb0\
-\xa0\x30\x20\xda\xf1\xb1\x51\x3c\x7a\xdf\x12\x16\xcc\xce\x45\x78\
-\xf6\xb4\xf4\x88\x46\xab\xca\xbc\x3b\xbf\x43\xe9\x85\x12\x5a\x5b\
-\xfc\xd7\x10\x57\x55\x94\x79\xfd\x26\x11\xaa\x01\x88\x52\x05\xcc\
-\x1c\xa2\x51\xd9\x22\xb8\xdc\xea\xb6\x6d\x9a\xbc\x88\x4f\xde\x50\
-\x56\x19\x9c\x08\xce\x49\x63\x86\xf3\xc4\xea\x65\x64\xa6\x0d\x02\
-\xdc\x4a\xb3\xd7\xdf\xfd\x9c\x77\x3e\xfa\x02\x87\xb3\xf7\x9e\xd6\
-\xaa\xaa\x70\xf7\xc2\x9b\x59\xbd\xe2\x0e\x22\xc2\xc3\x7a\x2e\xd0\
-\x05\x03\x22\x24\x61\xa1\x61\xe4\xde\x34\x97\x2f\xb6\xf9\x74\x76\
-\xeb\x04\xbb\xd5\x42\x6b\x4b\x93\xc7\x93\xb1\x4b\x43\x31\x00\x91\
-\xe0\xf6\x69\x4c\x89\x96\xa4\x44\x03\x48\xea\xb3\xe3\x38\xac\x23\
-\xfa\xe9\x4c\xb1\x77\x4e\xfb\x83\xa4\x01\xb1\xfc\xe4\xe1\x6f\x33\
-\x6f\xc6\xf8\xf6\xb4\xed\x7b\x8f\xf2\xdf\xaf\x7f\xa8\xdb\x26\xd1\
-\x15\x13\x73\xb2\x78\x62\xf5\xdd\x0c\x4d\x77\x1f\x16\xed\x4e\x27\
-\x26\x83\x3e\x21\x43\x00\x52\xc0\x98\x09\x53\xf9\xf2\xb3\xcd\x48\
-\xe9\xbf\x9e\xac\xfe\x72\xb5\x37\x06\x08\xaf\xad\x18\x76\x65\x04\
-\xfa\x8b\xd3\xc5\x17\x69\x69\x33\x13\x15\x71\x4d\x3e\x77\x68\x70\
-\xa2\x52\x30\x3c\x41\xa2\x0a\x77\x94\xa3\xc9\x8b\xfb\xcf\xda\xdf\
-\xfd\xb8\xbd\x53\xce\x5f\xac\xe4\xd9\xbc\xf7\x38\x7a\xea\x9c\xae\
-\x36\x74\x45\x62\x7c\x0c\x3f\x5c\xf9\x2d\x6e\x9d\x39\xb1\x3d\x6d\
-\xe7\xd7\x05\xac\x7f\xef\x73\x5e\xfd\xcf\x6e\x1e\x97\x3e\xa1\x08\
-\x18\x91\x20\x29\x94\x11\x0c\x4e\x49\xa7\xe2\xd2\x05\xbf\xcb\xd6\
-\x5d\xae\x26\x7d\xa8\x67\x3d\x99\x01\x68\x05\xe2\xbb\x7e\x18\x92\
-\xa2\xef\x64\xab\x69\x1a\x1f\xef\x3a\xc4\xf2\xc5\xb3\xb9\xd4\x28\
-\xa8\x35\x43\x4d\xab\xc0\xe1\x82\xc8\x10\x28\xaa\x15\x20\x61\x4c\
-\x52\xf7\x88\x77\x4d\x82\xaa\x1a\x68\x33\x5b\xc8\x7b\x67\x1b\xef\
-\x7d\xb2\x1b\x97\xab\xf7\x71\xc0\x46\x83\xca\x8a\x3b\xe7\xb0\x72\
-\xf9\x6d\xed\xe7\x93\x92\xb2\x2a\x9e\xcd\xdb\x44\xfe\xc9\xb3\xbd\
-\x8e\x11\x1b\x35\x50\x62\x52\x21\x29\x39\x4d\x17\x03\x9a\x9a\x3c\
-\x0b\x0c\xaa\x82\x34\x00\x2d\x78\x60\x40\xe2\x80\x18\xc2\xc2\x42\
-\x74\xc9\xbf\x1b\x36\xef\x62\x68\xce\x34\xca\xcd\xd7\x66\x41\xb8\
-\x09\x8a\x6b\xdd\xa7\x6b\x80\x63\x95\x82\xc2\x5a\x81\xc0\x1d\x8e\
-\x6a\x34\x40\x61\x35\x9c\x3c\x76\x88\xdd\xdb\x37\xd3\xd4\x1c\xd8\
-\x66\x3e\x75\x7c\x36\x3f\x5e\xbd\xac\x5d\x35\xd2\x6a\xb6\xf0\xca\
-\xdb\x9f\xf2\xde\xa7\x7b\x02\x62\xea\x55\x64\x0d\x90\x4c\x1c\x96\
-\xc8\xd1\x83\xfe\x97\x71\xd8\x3c\xf7\xa1\xaa\xa0\x5d\x9d\x01\xdd\
-\x20\x84\x60\xe4\xd0\x54\x5d\xcb\xc0\xe5\xfa\x26\xd6\xbe\xf6\x36\
-\x8b\xbf\xbd\xb2\x5d\x13\x18\x6a\x00\x2b\x74\xba\x4c\xe3\x6a\xf0\
-\x75\x51\xad\xa0\xe2\xd2\x05\xbe\xfc\xf4\x03\xca\xcb\x4a\xfc\xae\
-\xc7\x13\x06\x0f\x8c\xe7\xf1\x95\x4b\xb9\x65\x9a\x5b\xaf\xa8\x69\
-\x92\x4f\xbe\x38\xc8\x8b\x6f\x6e\xa5\xa1\x29\x78\x12\x1a\x40\xc6\
-\x60\x7d\xf6\x6e\xbb\xdd\x33\x03\x84\x22\x5d\x06\x90\xcd\xde\xc2\
-\x85\x67\xe6\x8e\xd1\xbd\x0e\x9f\x39\x75\x0c\x97\xcb\xc5\x6d\x7f\
-\x77\x0f\xe1\x11\x51\xd4\x9b\xbb\xe7\xd1\x34\x8d\x8b\x25\x45\x1c\
-\xde\xb7\x8b\x0b\xc5\xfa\xcc\x9e\x5d\x61\x32\x1a\xb9\x77\xe9\x3c\
-\xee\xbb\x6b\x7e\xfb\x69\xfc\x9b\xe2\x8b\x3c\xfb\xf2\x26\x4e\x17\
-\x07\xc7\x8f\xb5\x2b\xc2\x42\xf5\x89\xe8\x4e\x2f\x0c\x08\x51\xb1\
-\x1a\x10\x4a\xb9\xb7\x50\xd1\x59\x53\xc6\xf0\x5f\xaf\xeb\xbf\x9b\
-\xe2\xec\x99\x13\x94\x9e\x2f\x62\x44\xce\x44\x52\x87\x64\x12\x19\
-\x15\x8b\xdd\x6e\xa3\xa9\xa1\x96\xda\xcb\xd5\x9c\x2f\x3a\xd9\x6b\
-\x4f\xbb\x8e\x98\x99\x9b\xc3\x8f\x56\xdd\xd5\x1e\x2f\xdc\xd0\xd4\
-\xc2\xda\xb7\xb6\xf2\xf1\xae\x83\x68\x5a\xdf\x19\x5e\xf4\x4a\x50\
-\x76\x87\xe7\x43\xaa\x41\xa1\xd1\x20\x90\x85\xde\x9a\x9a\x3a\x28\
-\x81\x8c\xb4\x41\x5c\x28\xd3\xef\x8e\x6e\xb7\xdb\x38\x79\x74\x3f\
-\x27\x8f\xee\xd7\x5d\xb6\x27\xa4\x0e\x4a\xe0\x47\xab\x97\x31\x63\
-\xe2\x28\xc0\x6d\xe7\xdd\xb4\x6d\x0f\x79\x1b\x3e\xa5\xd5\xac\xd3\
-\x4f\xa4\x17\xd0\xab\x6d\x75\x79\x09\xcf\x35\x28\xe2\x92\x41\x22\
-\x0b\x7d\xdd\x58\xb0\x64\xfe\x74\xfe\xfc\x9a\xff\x07\x8f\xbe\x84\
-\xc9\x64\xe2\xae\xc5\xb7\xf1\xe0\x8a\x39\x84\x19\xaf\x8d\xc2\x5f\
-\x3f\xb7\x9e\x1d\x7b\xf5\xa9\x08\x02\x81\x59\xa7\x62\xce\x68\xea\
-\x6c\x29\x0c\x37\x42\x5a\xac\x64\x40\x98\x7c\xc7\xa0\xb8\xd4\x42\
-\x4d\xf1\x2e\x1d\x2c\x99\x37\x8d\xd7\xdf\xfd\x8c\xe6\x56\x0f\x8b\
-\x79\x3f\x22\x7b\xf4\x04\x66\x2f\x58\x4a\x74\x4c\x1c\x5f\x97\x42\
-\x6e\x9a\x46\xf4\x95\xc3\x7a\x53\x4b\xff\xb6\xed\x72\xbd\x3e\x37\
-\x96\xe4\xf8\x30\x46\x24\xb8\xaf\x5c\x4b\x8a\x82\xe4\x28\x89\x10\
-\x20\x85\xb2\x47\xb1\x86\x73\x06\xf0\xba\x60\x86\x87\x87\x72\xff\
-\xf2\xdb\x03\x6d\x73\xaf\x11\x9f\x98\xc4\x8a\x95\x3f\x64\xc9\x3d\
-\xab\x88\x8e\x89\x03\xa0\xc9\x2a\x79\x73\xe7\x85\xf6\xcb\x41\xfa\
-\x1b\x65\x95\xfa\xdc\x75\x06\xc6\x86\x32\x3a\x49\x32\x29\x45\x92\
-\x12\xed\xee\x7c\x40\xb3\x18\x29\x32\x4c\x1f\x20\x9a\xf3\xcb\x5d\
-\x27\x80\x71\xde\x08\x2c\x5f\x74\x33\xbb\x0f\x9d\x08\xf8\x64\xaa\
-\x07\x46\x53\x08\x33\xe7\x2c\x60\xd2\xf4\xd9\x28\xea\xb5\xe3\x73\
-\x65\x79\x29\x3b\x3e\x7a\x97\x90\xb0\x30\x26\xe4\x3c\xca\xc0\x48\
-\x1f\x44\xfa\x08\xc5\x17\xf4\xb9\xb5\x44\x46\x74\xd7\x3d\x49\x28\
-\x98\x95\x28\x5a\x14\x00\x21\xd9\xe9\x8b\x80\xa2\x28\xfc\xea\x89\
-\x95\xfd\xe6\xef\x99\x35\x22\x87\x07\x1e\xfb\x57\x72\x67\xce\x6b\
-\xef\x7c\xab\xc5\xcc\xce\x8f\x37\xf1\xd6\x4b\x6b\xa8\xaa\xb8\x08\
-\x40\x5d\x9b\xa0\xad\x9f\xc3\x93\xdb\xcc\x16\x0a\xcf\x5f\xd2\x55\
-\x26\x75\x50\x82\xa7\xe4\x1d\x70\xc5\x33\x4e\x0a\xb9\x0b\xc4\x8f\
-\x7d\x11\x89\x8f\x8d\xe2\xb7\x3f\x5d\xc5\x53\xbf\x7d\x39\x28\x1e\
-\x65\x9e\x90\x38\x28\x99\xf9\x8b\x97\x93\x9a\x7e\x2d\x9e\x40\x4a\
-\x8d\x82\xc3\xfb\xd8\xbd\x7d\x2b\x36\x6b\xe7\xb5\xde\xa9\x41\x41\
-\x45\xff\x2e\x43\xfb\x8f\x16\xea\x76\x58\x1b\x36\x24\xb9\x5b\x9a\
-\xa2\xc9\x5d\x70\x85\x01\x9a\x55\xfd\x52\x09\xd5\x5c\xe0\xfb\x5e\
-\xcf\x9c\xe1\x19\xbc\xf8\x7f\x1e\xe7\x9f\x7e\xf3\xff\x03\x36\x88\
-\x74\x44\x68\x78\x04\x37\xcd\x5e\xc0\x84\xa9\xb3\x3a\x59\xa5\xca\
-\xcb\x4a\xd8\xb1\xf5\x5d\x6a\x2a\xbd\x8f\xb8\x56\x9b\xf0\xbe\x81\
-\xf5\x01\x3e\xfe\xe2\x80\xae\xfc\xaa\xaa\x90\x99\xde\x4d\xb1\xe9\
-\xb4\x85\xab\x7b\xe0\x0a\x03\x72\xb3\x44\x53\x7e\xb9\x73\x2f\x88\
-\x5b\x7a\x22\x98\x9e\x92\xc4\x2b\x7f\xf8\x27\x9e\x5d\xb7\x8d\xed\
-\xbb\xf6\xe8\x1e\x0d\x1d\x11\x1d\x13\xc7\xe4\x9b\xe6\x32\x76\xd2\
-\xf4\x4e\xf6\x07\x73\x6b\x0b\x5f\x7e\xbe\x99\x53\x05\x87\x7a\xbc\
-\x4f\xc8\xec\x80\x06\x73\xff\xcc\x82\xd2\x4b\xd5\x1c\x2a\xd0\x77\
-\x1f\xc6\x90\x94\x41\x98\x8c\x9d\xed\xe5\x52\x88\xdd\xd3\x07\x88\
-\x66\xe8\xe0\x1d\x2d\x85\x78\x53\x48\x7a\x64\x00\xb8\x7d\x2f\x67\
-\xdd\x7e\x37\x29\xa3\x66\x52\x70\x78\x2f\xdf\x1c\xcf\xc7\x62\xf6\
-\xa8\x52\xea\x86\x98\xd8\x78\x86\x64\x65\x33\x22\x67\x02\xe9\x99\
-\x23\x3a\x8d\xf8\xab\x8e\xae\x7b\x77\x7d\x82\xdd\xea\xff\x81\xca\
-\xd5\x4f\x53\xe0\xa5\x0d\x1f\xeb\x1e\x70\xd3\x27\x8e\xec\x96\x26\
-\xa4\xf6\xc6\xd5\xbf\xaf\x31\xc0\xa2\xbc\x2d\x42\xb5\x35\x74\x89\
-\x11\xf0\x06\x45\xc0\x80\xc4\x41\xcc\x5b\x78\x37\x73\x6e\x5f\x4a\
-\x55\x65\x19\x97\xab\xca\xa9\xbb\x5c\x85\xcd\x6a\xc5\xe9\xb0\xa3\
-\xa8\x06\x42\xc3\xc2\x88\x8a\x8a\x25\x3e\x31\x89\xc4\x41\x29\xc4\
-\xc4\x76\x53\xbc\x02\x50\x56\x7a\x8e\x1d\x5b\xdf\xa5\xb6\x3a\xf8\
-\x8e\xb3\xc1\xc0\xbe\x23\xa7\xf9\x62\xff\x71\xdd\xe5\xe6\xcc\xe8\
-\x26\x5c\x5a\x85\x5d\xdd\x74\xf5\x9f\x76\x06\xe4\x66\x89\xa6\xa3\
-\xe5\xae\x2d\x12\x56\xf8\x43\x78\x6c\x92\xc6\xee\x0b\xee\xd1\xab\
-\xa8\x2a\xc9\xa9\x19\x3d\x7a\x05\xfb\xc2\x87\x1b\xf2\xb0\x9a\x6f\
-\xcc\x38\xe1\xcb\xf5\x4d\x3c\xf3\x82\xfe\x30\xdd\x41\x89\xf1\x8c\
-\xca\x4a\xef\x9a\xfc\x41\xc7\xeb\xf2\x3b\xfb\x61\x68\xf2\x75\x7f\
-\x08\xbb\x24\x9c\xad\xbd\x61\xde\x7e\xd0\x0d\x97\xe6\xff\x55\x75\
-\xad\x66\x0b\x3f\xfd\xbf\x2f\x53\xd7\xd8\xac\xbb\x1e\x8f\x86\x7f\
-\x45\xae\xeb\xf4\x6f\xc7\x7f\x26\xa4\xaa\x9f\x48\x49\x8f\xce\x2f\
-\xf5\x66\xfd\x17\xb1\xde\x48\x70\x49\x77\x70\x5d\xb3\xcd\x37\x23\
-\xea\x1b\x5b\xf8\xc7\xa7\x9f\xa7\xa8\x44\x9f\xdc\x0f\x10\x16\x16\
-\xc2\x8a\xc5\xdd\xb6\xd4\xd3\x13\x07\xa9\x9d\xee\x94\xeb\xc4\x00\
-\x21\x84\x54\x14\xd9\xed\x8e\x88\xae\x88\x09\x95\x28\x7f\xbd\xfd\
-\x0f\xb8\xaf\xd9\xdc\x59\xac\xb0\xf9\x1b\x85\xae\x3e\x51\xe0\xf6\
-\xae\x5e\xf5\x93\xff\xa4\x48\xe7\xa1\xeb\x2a\x96\x2f\xbc\xd9\x83\
-\xe9\x53\xfe\x47\xd7\x07\x22\xba\x29\xb6\xcf\x0e\x56\x37\x0c\xab\
-\xd0\x7e\x09\x0c\xf7\x46\xdc\xa4\x42\x4e\x92\xe4\x54\x95\xdb\xa9\
-\xf7\xaf\x19\x52\xc2\xf9\x7a\x18\x74\xc5\x3f\xb0\xa4\xac\x8a\x75\
-\x9b\x3e\x67\xfb\x9e\xa3\xbd\x0e\xe6\x88\x8b\x89\xe2\xef\x97\xcc\
-\xe9\x9a\x5c\x5c\x9c\xac\x6e\xec\x9a\xd8\x8d\x01\x2b\x84\x70\xe5\
-\x97\x3b\x9f\x01\xf1\xb2\xaf\x4a\xb2\x06\xb8\x1f\x49\x38\xda\xcf\
-\x27\xd1\xbe\xc0\xa5\xea\x16\x76\x96\x17\xf3\xd1\x8e\xfd\x1c\x3a\
-\x5e\x14\x70\x14\xcd\x3f\xff\x60\x45\xb7\xd1\x2f\xa4\xfc\xcd\x0a\
-\x21\xba\x39\x36\x79\x34\xed\x14\x27\xab\xaf\x0d\xab\xd0\x1e\x03\
-\x26\xf8\xaa\x28\x3d\x4e\x52\xde\xcc\x75\xd3\x4a\xf6\x16\x0e\x9b\
-\x95\x0f\xff\xf2\x32\x76\x87\x9d\xe6\x86\x3a\x1a\xea\x83\xe7\x55\
-\xbd\x78\xde\x34\x6e\x9e\x32\xa6\x6b\x72\xfe\xd9\x14\x75\xbd\xa7\
-\xfc\x1e\x63\x68\x56\x08\xe1\x52\x34\xe5\x11\xf0\xbd\xc2\x08\x60\
-\xdc\x60\x19\x94\x0b\xb5\xfb\x13\x2e\x97\xcb\x6d\x36\x3d\x57\x18\
-\xd4\xce\xcf\x19\x9e\xc1\x8f\x1f\xe8\x16\x04\xae\x21\x95\xc7\x3c\
-\x8d\x7e\xf0\x71\x63\xd6\x84\x34\x71\x10\x78\xad\xa7\x4a\x23\x4d\
-\x90\x39\xa0\x3f\xb5\x31\x37\x26\x86\xa6\x27\xf3\x87\x9f\x3f\xd4\
-\xcd\x60\x2f\x21\xcf\xd7\x45\xae\x3e\x07\xaf\x26\x94\x9f\x02\x3d\
-\x46\xeb\x65\x27\x4a\xaf\x1e\x6f\x7f\x0b\xc8\xce\x4a\x63\xcd\x2f\
-\x1e\x21\x3a\xb2\x5b\xa0\x76\xad\x53\x2a\xff\xe2\xab\xac\x4f\x06\
-\xe4\x26\x8b\x5a\x21\xe5\x4a\x7c\x58\xcc\xc0\x2d\x15\x8d\x1c\xf8\
-\xb7\x39\x0b\xe6\xcd\x18\xcf\x7f\xff\xfa\x31\xe2\xe3\xba\xd9\x4a\
-\xa4\x40\x3e\x38\x2d\x55\xf8\x1c\xc0\x3d\x2e\xdf\x13\x53\x0d\x5b\
-\x85\x60\x4d\x4f\xf9\x32\xe3\x25\x03\x23\xfb\x8f\x09\x46\x9d\xee\
-\xf3\xc1\x46\x58\xa8\x89\x7f\x5c\x75\x17\xbf\x7a\x72\x25\xa1\x21\
-\xdd\xdb\x22\xe1\xff\xf9\xf3\xde\x8c\x5f\x0e\x2e\xae\x0a\xe5\xa7\
-\xca\x60\xd7\x74\x10\x33\xbc\xe5\x11\xb8\x5f\x3a\xfa\xec\x6c\xdf\
-\x4a\x44\x06\x83\x91\x29\xb3\xe6\x33\x6d\xd6\xad\x7d\x5a\x8f\x2f\
-\x4c\x19\x3f\x82\xa7\x1e\x5e\xe1\xfd\xfd\x02\x21\x0f\x86\x36\xa8\
-\x3f\xf7\x87\x96\x5f\x0c\xc8\xcd\x15\x8e\xa3\x95\xf2\xbb\x52\xd3\
-\x0e\x02\x89\xde\xf2\x85\x9b\x20\x3e\x1c\x8f\xde\x70\xc1\xc0\x90\
-\xac\x6c\xe6\x2f\xba\x9b\xf8\x84\x9e\xef\x63\xe8\x0b\x8c\xcd\xce\
-\xe4\xbe\xbb\xe6\x33\x33\x37\xc7\x57\xb6\x1a\x0d\xf5\xdb\xfe\x3e\
-\xf2\xe3\xb7\x8b\xd7\xc4\xc1\xe2\x42\x7e\x85\x5c\x24\xa5\xb6\x53\
-\xe0\x39\xae\x0c\xdc\x8f\x37\x1c\x28\x53\x82\xca\x84\xd8\xf8\x04\
-\xe6\x2d\x5c\xc6\xd0\x11\x3e\x7f\x78\x9f\xc0\x64\x34\x32\x67\xc6\
-\x38\x96\xcc\x9f\xce\xc4\x9c\x61\x3e\xf3\x4a\x68\x91\x42\x59\x98\
-\x9b\x2c\x2e\xfa\x4b\x5f\x97\x8f\xdd\xa4\x64\x71\xf8\xc8\x25\xc7\
-\x52\x84\xf2\x31\x78\x7e\x1d\x2f\xc4\x00\x37\x67\x68\x94\x35\x09\
-\xbe\xa9\x16\x58\x02\x88\xdd\x33\x1a\x4d\x4c\xbb\xe5\x56\x72\x6f\
-\x9a\x87\xc1\xd0\xb7\x51\x98\x1d\x11\x16\x6a\x62\xca\xb8\x11\xcc\
-\x98\x94\xc3\x9c\x19\xe3\x3a\xc5\x3c\xf8\x80\x5d\x15\xda\xf2\x09\
-\xc9\x6a\xbe\x9e\xba\x74\xbf\xa0\x31\x39\xd5\xb8\x33\xbf\xdc\x79\
-\x3f\x88\x37\xf1\xb2\x89\x0b\x01\xe9\xb1\x92\xd4\x18\x49\x55\x8b\
-\xfb\x25\xa5\x56\x9d\x51\x9e\xc3\x47\x8d\x63\xee\x82\xbb\x88\xf6\
-\x62\xc0\x09\x16\x14\x45\x30\x28\x31\x9e\xac\xf4\xc1\xe4\x8c\xc8\
-\x60\x4c\x76\x06\xa3\x87\xa7\x77\x33\x23\xf6\x00\x4d\x22\xbf\x3f\
-\x21\xd9\xf8\x99\xde\xfa\x7b\xf5\x86\xcc\xa4\x14\xc3\x86\x23\x15\
-\x4e\xbb\x90\xe2\x4d\xc0\xeb\x0d\x1d\x8a\x80\xe4\x68\xf7\x13\x54\
-\xf9\x97\x04\xe5\xcd\x3d\x6f\xd0\xf1\x09\x03\x99\xb7\xf0\x6e\x32\
-\x86\x75\x37\xe5\x79\xc3\xed\x4b\xee\xa1\xae\xb6\x9a\x96\xc6\x06\
-\x6c\x36\x0b\x36\x9b\x15\x97\xc3\x81\xfd\x4a\xe4\xa6\x50\x04\x21\
-\x21\x61\x28\x8a\x8a\x29\xc4\x44\x7a\x62\x04\xe3\x33\xa3\x49\x1a\
-\x10\x4b\xea\xe0\x04\xbd\x9d\xdd\x15\x76\x90\x2b\x27\xa7\x18\x7a\
-\x75\xb1\x6a\x40\x22\xcb\x91\x4b\x8e\x79\x42\x28\xef\x03\x3d\x3a\
-\x0c\x79\x7b\xf7\xf1\x2a\xd6\xfe\xf1\x97\x8c\x9d\x34\x9d\x69\x37\
-\xdf\x86\xaa\x33\x48\x5c\x2f\x6e\xc9\xd4\x88\xf7\xef\x72\xab\x9e\
-\xd0\x7a\xe5\x19\x2b\x5d\xef\xc6\x74\x44\xc0\x32\xe3\x91\x0a\x39\
-\x59\x48\xed\x63\xa0\xc7\x98\xa6\xe2\x3a\xc1\xc9\x2a\xcf\x55\x9a\
-\xdb\x5a\x08\x8f\xe8\x9f\x2b\x2e\xe7\x0f\xd3\x88\x0a\xfc\x66\x85\
-\x6a\x29\x94\xc5\x93\x93\xc5\x91\x40\x88\x04\xac\x47\x9b\x9c\x2c\
-\x8e\x38\x51\x72\x41\xee\xeb\x29\x6f\x5c\xa8\xf7\x83\x5a\x7f\x75\
-\x7e\x52\x94\x0c\xbc\xf3\xa5\x3c\xa4\xaa\xca\x8c\x40\x3b\x1f\x82\
-\xf3\x32\x20\x53\x53\x44\x59\x53\xb2\x3a\x5b\xc2\xaf\xf0\xa1\x41\
-\x0d\xed\x3f\x41\xc6\x2b\x46\x26\x06\x74\x5a\x97\xc0\x73\x21\x4d\
-\xea\xac\xf1\x83\x44\x60\x31\x55\x57\x10\xb4\xc5\xf6\xca\xcb\xd3\
-\x4f\xe7\x97\x3b\xf3\x41\xbc\x02\x74\x3b\x26\x06\xf8\xca\x75\xc0\
-\x18\x18\x29\x89\xd3\x1f\xa3\x7d\x15\xb5\x12\xf9\xc0\xe4\x14\xc3\
-\x96\x20\x36\x29\xf8\xaa\xfc\x49\x29\x86\xcd\x06\xa7\x32\x1c\x78\
-\x8e\x2e\xb3\xe1\x7a\xa8\xeb\xe2\xc2\xdc\x0f\x3c\x8f\x1d\x24\xaf\
-\xbc\xed\xa2\x1b\x12\xc9\x7a\x14\x65\x74\xb0\x3b\x1f\xfa\xf8\x49\
-\xf3\x23\x15\x72\xb2\xd0\x5c\x2f\x20\xc4\x14\x70\xbb\x83\x7c\xf4\
-\x8d\xd2\x2f\x8c\x88\x0f\x87\xd1\x49\x1a\x09\x81\x49\x3b\xc7\xae\
-\x3c\x69\xde\xe3\xfe\xd6\x5b\xf4\xe9\xa2\x30\x39\x59\x1c\x29\x4e\
-\x51\x67\x80\x5c\x0d\x9c\x55\x15\x30\xf5\xad\x84\x89\x2a\x20\x37\
-\x55\x72\x4b\x66\x40\x9d\x5f\x24\xa4\x5c\x55\x9c\xac\xe4\xf6\x65\
-\xe7\x43\x1f\xcf\x80\x8e\x90\x52\x2a\xf9\x15\xae\xc5\x3b\xcf\x29\
-\x1b\x5a\xac\x22\x38\x52\xb8\x07\x0c\x4f\x90\xe4\x24\xf5\x6e\x8e\
-\x49\xc9\x49\x81\xfc\x43\x53\x8a\xfa\xd6\x95\x3d\xad\xcf\xd1\xc7\
-\xe3\xf1\x1a\xae\xf8\xc3\x6c\x91\x52\x46\xbe\x59\xa0\x3d\xd9\x64\
-\x16\x8f\xd5\xb6\x89\x8c\x60\x3b\xd6\xf6\xe2\xed\x61\x0b\xf0\x21\
-\x8a\x5c\x37\x69\x90\xfa\xa9\x10\xa2\x5f\xb7\xaa\xeb\xea\xce\xb0\
-\xee\xb0\x4c\xb7\xba\xb4\xdf\xb6\x39\x94\x85\x4d\x16\xe2\x83\x11\
-\xda\x3b\x34\x5e\x32\x6e\x70\x8f\x84\x9c\x08\xb9\x07\xc9\x7a\xcd\
-\xaa\xbe\x97\x9b\x25\x02\xbb\x8e\x25\x00\xdc\x30\xfe\x24\x6f\x15\
-\xca\x04\x6b\x8b\xf6\x90\xc5\xae\x2c\x6b\xb1\xc9\x31\x16\xa7\x08\
-\xed\x8d\x7b\x8e\xb7\x25\x48\x48\xce\x03\xdb\x41\x6e\xc7\xa1\x7e\
-\xde\xd1\x41\xf6\x7a\xe2\x86\x61\x40\x57\xbc\x55\x28\x13\x34\x33\
-\xb7\x3b\x9c\xda\x4d\x36\x97\x32\xde\xe6\x22\xc3\xa5\xc9\x68\x97\
-\x26\x8c\x2e\x0d\x83\x53\xc3\xe0\x74\x21\xa4\x00\xa3\x82\x54\x90\
-\x5a\x88\x51\xd8\x66\x66\xb8\x4e\x98\x54\x71\x01\x41\x91\x40\x9e\
-\xd1\x50\x0b\x2d\x46\x8a\x66\x25\xf6\xfc\xb0\xe6\xf5\xc0\xff\x00\
-\x7f\xe1\xcb\x24\x2c\x98\x3d\x94\x00\x00\x00\x00\x49\x45\x4e\x44\
-\xae\x42\x60\x82\
-\x00\x00\x49\xbb\
-\x89\
-\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
-\x00\x01\x00\x00\x00\x01\x00\x08\x06\x00\x00\x00\x5c\x72\xa8\x66\
-\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
-\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\
-\x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
-\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
-\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x20\x00\x49\x44\
-\x41\x54\x78\x9c\xec\xbd\x67\x7c\x1c\xd5\xbd\xff\xff\x3e\xb3\xab\
-\xde\x25\xab\x17\xf7\x82\xbb\x64\x19\x1b\x63\x5c\x31\x06\x4c\xb3\
-\x29\x09\x84\x16\x48\xc8\x4d\x0f\xe1\xde\x4b\x92\x9b\xe4\x47\x92\
-\xff\x4d\x42\x72\x13\xd2\x81\x04\xd3\x0c\x04\x0c\x36\x1d\x4c\x71\
-\xc3\x05\x5c\x24\xb9\xf7\xaa\x66\x49\xb6\xba\xb4\x7d\xe7\xfc\x1f\
-\xac\x7b\x91\x76\x56\xb3\x3b\xbb\xd2\xbc\x1f\xf0\xc2\xab\x99\x73\
-\xbe\x5a\xcd\xf9\xcc\x29\xdf\x22\x30\x89\x68\x9e\x5c\x27\xb3\xe2\
-\xe2\x98\x2c\x24\xc5\x4e\x8f\x3a\x58\x95\x4a\xb6\x2a\xc9\x50\x55\
-\x52\x3c\x52\x26\x79\x54\xe2\x3d\x2a\x31\x48\x21\x54\x55\x2a\x2a\
-\x42\x01\xf0\xa8\x08\x55\xfa\xda\x88\xb6\x20\x4f\xb5\x67\x55\xf0\
-\x08\xc0\x6a\xc1\x63\x55\x70\x58\x14\x3a\x15\x21\xdb\x2d\x8a\x68\
-\xe9\x17\xaf\x6e\x1b\x98\x2e\x37\x0a\x95\x3a\x69\xb5\x1c\x71\x78\
-\x38\x3c\xa5\x50\xd8\x0d\xf9\xc5\x4d\x74\x41\x18\x6d\x80\x49\xf7\
-\xfc\x7d\xa5\x4c\x4c\x48\x61\xae\xea\x55\x67\x38\x3d\xa2\xc4\xa5\
-\x8a\x22\x87\x9b\x74\x97\x97\x58\xb7\x17\x25\x54\x76\xc4\x5a\xe1\
-\xea\xa1\x2a\xd6\x73\x7b\xac\x07\x79\x18\xc4\x11\x24\x87\x11\x72\
-\xbb\xc5\x62\xd9\xee\xae\x66\x6f\x69\xa9\x70\x87\xca\x36\x93\xc0\
-\x30\x05\x20\xcc\x78\x66\xa7\x4c\x8f\xb2\xf3\x65\xb7\x2a\xe7\xda\
-\xdc\x8c\xb5\xb9\x44\xae\xdd\x43\x8c\x94\xdd\xdf\x1b\x0a\x46\x64\
-\x49\x46\x64\xfa\x65\x8c\x0b\xd8\x85\x64\xbb\x10\x6c\x53\x85\xb2\
-\xc1\xe9\x65\xb3\x39\x63\x08\x2f\x4c\x01\x30\x98\x97\xca\xe5\x48\
-\x9b\x57\xbd\xcf\xe1\x14\xb3\x1c\x1e\x31\xbc\xd3\x45\x52\x98\x8c\
-\xf5\x8b\x22\x04\x8c\xcc\x92\x0c\xed\x17\x90\x95\x1e\x60\x2b\xb0\
-\x0e\x29\xcb\x54\xc5\xb2\xaa\x34\x4f\x54\xea\x6b\xa1\x89\x16\x4c\
-\x01\x08\x31\x4f\x54\xc8\xd4\x44\x0f\x5f\x77\x7b\xb8\xbd\xc3\x25\
-\x47\x77\x3a\x45\x5c\x38\x0f\xf8\x4b\x31\xa9\x50\x92\x9b\xdc\x73\
-\xcb\x85\xe4\x10\xf0\xa9\x2a\xe4\x7b\xc9\x36\xcb\xc7\x43\x87\x0a\
-\x67\xcf\xad\x33\xf1\x17\x53\x00\x42\xc0\xe2\x32\x39\xa1\xdd\xa3\
-\x3e\xd2\xe6\x56\x66\xb7\xda\xc9\x52\x23\x71\xc4\x9f\xc7\xa8\xec\
-\x80\x67\x01\x5d\xd1\x01\x62\x39\x52\x2e\x53\xa4\xf2\xee\xf8\x42\
-\x51\xa3\x77\x07\x26\xe7\x62\x0a\x40\x90\x78\xaa\x5c\x96\x7a\xec\
-\xf2\x47\x76\x8f\x98\xd3\xe1\x44\x87\x77\x65\x78\x31\x6d\xa0\x4a\
-\x7a\x7c\x50\xbb\x50\x41\x7e\x2e\x84\x78\x5d\x7a\x94\xd7\x4b\x8a\
-\x44\x6d\x50\x7b\xeb\xa3\x98\x02\xa0\x23\x8b\xb6\xca\xe1\x0e\x87\
-\xfc\x55\x9b\x53\xcc\x6d\x75\x90\x6c\xb4\x3d\xc1\xa2\x28\x45\x52\
-\x52\x10\x52\x49\x53\x11\xf2\x33\x54\xb1\xd8\x8d\xb2\x78\x52\x81\
-\x68\x0c\x65\xe7\xbd\x19\x53\x00\x74\x60\x51\xb9\xbc\xf3\x78\x07\
-\xbf\x6d\x73\x50\xa4\x1a\x6d\x4c\x90\x89\xb5\xc2\xec\xa1\x2a\x51\
-\x21\x3b\x7c\xbc\x00\x27\x42\xbc\x83\xf4\xfe\xb3\x38\xcf\xba\x5c\
-\x08\xd1\xdb\x26\x57\x21\xc5\x14\x80\x40\x90\x52\xac\xac\x21\x5f\
-\xb8\xd4\x87\xaa\x5b\xc5\xb7\x0f\x9e\x10\xe9\xde\x3e\xf2\x18\x8e\
-\xcb\x93\x0c\x4c\x0b\x9b\x5f\xf6\x80\x84\x97\xbd\x28\x0b\x2f\xcf\
-\x17\x55\x46\x1b\x13\x89\x98\x02\xa0\x81\xef\x7e\x20\x63\x06\x27\
-\xf3\x13\x87\x9b\xef\x39\xbd\xa4\x7a\x7b\xe9\xeb\x3e\x35\x0e\x06\
-\x65\x48\x92\xa2\x25\x55\x2d\x82\x43\x4d\x67\x1e\x93\xe9\x83\x54\
-\xd2\xe2\x0c\x34\xee\x62\x08\xdc\x42\xe5\x35\x50\xfe\x50\x5c\x20\
-\xb6\x18\x6d\x4e\x24\x61\x0a\x80\x1f\xfc\xf8\x03\x99\xd9\x2f\x89\
-\xdf\xda\x5d\x7c\xc5\xe5\x25\xc6\x68\x7b\x82\x45\xac\x15\x46\x66\
-\x4b\x0a\x53\xe5\xe9\x07\xe3\x68\x8b\xa0\xa2\xc6\xf7\xaf\xc4\x18\
-\x98\x3e\x50\x25\xca\x62\x9c\x8d\xdd\x23\xd7\x49\x78\xbc\x24\xcf\
-\xf2\x9e\xb9\x3c\xe8\x1e\x53\x00\xba\xe0\x37\x2b\xe5\x80\xd8\x28\
-\x16\xb6\xda\x99\xa9\xca\xde\xfb\x5d\x59\x2d\x30\x24\x5d\x32\xa4\
-\x9f\x3c\xc7\xcd\xf7\x48\xb3\x60\x47\x9d\xc0\xa3\x42\x5a\x1c\x5c\
-\xd1\x5f\x25\x3a\xac\x07\xff\x19\xa4\x64\x87\x82\xfc\xbf\x96\x7c\
-\xcb\xcb\x33\x85\xf0\x18\x6d\x4f\xb8\xd2\x6b\x1f\xea\x9e\xf0\xf8\
-\x4a\x59\x10\x65\xe1\x5f\xed\x4e\xe6\x7a\xc3\x74\xe0\x0b\x01\xa9\
-\xb1\x90\x16\x27\xb1\x9c\x1c\xb4\x76\x37\xb4\x38\x04\x1d\x27\x5d\
-\x69\x14\x01\xfd\x12\x24\xf1\xd1\xf8\x36\xed\x24\xb8\x25\xb8\x3d\
-\xa0\x28\x60\x15\xbe\xe9\x7e\x7e\xb2\xc4\x7a\xd6\xc0\x96\x12\xb6\
-\xd4\x0a\x8e\xb6\x9c\xf9\xd5\xaf\x1c\x20\xc9\x4c\x88\xc8\x17\xea\
-\x11\x29\xf8\xcd\xc1\x5c\x65\xe1\x1d\x42\x78\x8d\x36\x26\xdc\x08\
-\xcb\x87\xdb\x28\xfe\xf7\x33\x99\x1b\x27\x78\xba\xcd\x21\x6f\x50\
-\xa5\x08\xdb\xef\x26\x2d\x0e\x4a\xf2\x55\x92\x2e\xb1\x18\xd9\xdf\
-\x28\xd8\x59\x27\xb8\xa2\x48\x92\xad\xd1\xb1\x58\x95\xb0\xb9\x5a\
-\x50\xdb\x76\xee\xaf\x3f\x26\x47\x32\x38\x23\x22\x05\xe0\x14\xbb\
-\x91\xf2\xb7\xc5\xf9\x96\x97\x84\x10\xbd\x74\xf7\x46\x3b\x61\xfb\
-\x90\x87\x92\xc7\xde\x95\xf1\xa9\xa9\xea\x93\xad\x0e\xe5\x6e\xaf\
-\x0c\x5d\x74\x5d\x20\x44\x5b\x60\xce\xb0\xae\x8f\xe1\x3a\x5c\xf0\
-\xe9\x7e\x05\xab\x02\x43\xfb\x49\x86\xf5\x93\xf8\x23\x67\x5e\x09\
-\x9b\x2a\x05\x75\x1d\x17\x5e\x2c\x04\x64\x25\x48\x92\x62\x20\x3e\
-\x1a\x72\x92\x24\xf1\x51\x3d\xf8\x45\x8c\x63\x8b\x22\xd4\x47\xc7\
-\xe7\x45\x7d\x6c\xb4\x21\xe1\x40\x9f\x16\x80\xc7\x1e\x93\x4a\xf2\
-\x2c\x1e\xb1\xbb\x79\xcc\xe9\x25\xb8\x7e\x6d\x3a\x91\x18\x0d\xc3\
-\x32\x7d\xd3\x7e\x8f\xd7\x37\x5d\x8f\xb2\x42\x6e\x92\x44\x39\xeb\
-\xaf\xb9\xff\x84\x60\xdf\x71\x81\x14\x70\x45\xa1\x4a\x46\x42\xd7\
-\xed\x7a\x54\xf8\xe2\xa8\xe0\x84\xcd\xbf\x47\x22\x4a\xf1\xf9\x03\
-\xc4\x5a\x7b\xf0\xcb\x18\x8a\xf8\x14\x45\x7c\xbf\x24\x57\xec\x32\
-\xda\x12\x23\xe9\xb3\x02\xf0\xc7\xd5\xf2\x1a\x97\xca\x0b\x36\x37\
-\x39\x46\xdb\xa2\x07\x85\x29\x92\x09\x01\x7a\xe7\xa9\x12\xbe\xa8\
-\x14\x34\x5c\xe4\xcd\xdf\x15\x7a\x05\x04\x19\x86\xc0\x8d\xe4\x49\
-\xd5\xa1\xfc\xbc\x74\xb0\x68\x35\xda\x1c\x23\xe8\x73\x02\xf0\xeb\
-\x4f\x65\x46\x6c\x0c\x6f\xb6\xda\xb9\x2a\x82\x1f\xdd\x0b\x28\xce\
-\x93\xf4\x0f\xc0\x41\x47\x02\x65\xd5\x82\xea\x56\xed\x8f\xc2\x35\
-\x43\x55\xe2\xa3\x35\xdf\x16\x8e\x34\x48\xe4\x8f\x4a\xf2\x2c\xcf\
-\xf7\xb5\xa3\xc3\xb0\x5e\xef\xea\x8a\x94\xe2\x0f\x6b\xe5\x7f\x7a\
-\x05\xb5\x2d\xbd\x6c\xf0\x0f\x4a\x0f\x6c\xf0\x83\x6f\xa9\x10\xc8\
-\xe0\x4f\x3e\xb9\x17\xd0\x4b\xc8\x12\x88\x67\x2b\x6a\xbd\xab\xb7\
-\xd6\xc8\xe1\x46\x1b\x13\x4a\xfa\xc4\x0c\x60\xa5\x94\xd6\xf2\xb5\
-\xf2\x8b\x56\xbb\x98\x60\xb4\x2d\x7a\x53\x98\x22\x29\xc9\xf7\x6f\
-\x93\xef\x7c\x9c\x1e\x58\x7e\x40\xc1\x15\xc0\xe1\x58\xdc\xc9\xf4\
-\x60\x96\xde\xf7\x0a\xb1\x4b\xf8\x5d\x6c\x8b\xf2\xeb\x51\xa3\x84\
-\xcb\x68\x63\x82\x4d\xaf\x17\x80\xb2\x5a\x39\x41\x48\x75\xe1\xfe\
-\x13\x62\xdc\xce\xfa\xde\xf5\xeb\xc6\x47\xf9\x36\xe2\x2c\x3d\xf8\
-\xb5\x54\xe9\x3b\x35\x70\x7a\x7c\x8d\xb8\xbc\xe0\xf6\x42\xab\x03\
-\xda\x9c\x02\x9b\xd3\x27\x14\x17\x3b\x37\x1b\x9f\x27\x19\x10\x3e\
-\x71\x01\xba\x22\xa1\x42\x0a\xe5\x6b\xa5\x79\xa2\xdc\x68\x5b\x82\
-\x49\xc4\xee\xe1\x76\x87\x94\x52\xd9\x72\x4c\xfd\xae\x94\xea\xef\
-\x80\xe8\xb8\xc8\x3c\xb2\xea\x92\xa2\x54\xd9\xa3\xc1\x0f\x3e\x67\
-\xa1\xe4\x18\x20\xe6\x62\x03\xd9\xf7\x99\x94\xd0\xee\x84\xbd\xc7\
-\x05\x35\x67\xf9\x07\xf4\xd6\x58\x08\x00\x01\xc5\x42\xaa\x1b\xca\
-\x6a\xbc\xff\x7b\x30\x4f\xf9\x55\x6f\x75\x22\xea\x5d\xaf\xc4\x93\
-\x6c\xae\x95\x45\x8a\xf4\x2e\x02\x31\x0d\x7c\x6f\xb5\x0d\x55\x0a\
-\x8d\x9d\x46\x5b\xa6\x2f\xe3\x72\x25\x03\xd3\xbb\x7f\x03\xcb\x93\
-\xff\xe9\xa9\x6b\x93\xc4\xb7\x67\x70\xb8\x49\x90\x18\x2d\x29\x2d\
-\x90\xc4\xf4\xda\x57\xc8\xd9\xc8\xcf\xbd\x56\xcb\xdd\x13\xb3\xc5\
-\x21\xa3\x2d\xd1\x9b\x5e\x27\x00\x2f\x6c\xf2\xfe\xd6\x6a\x15\xdf\
-\x73\x7b\x89\x73\x7a\xa0\xc3\x25\x68\x77\xfa\xa6\xba\xbd\x8d\xdc\
-\x24\xc9\xa4\xa2\xae\x7f\xb1\x83\x8d\x82\xdd\x0d\x82\x91\xd9\x92\
-\x41\x7e\x88\x85\xc9\x25\x69\x93\x42\x7e\x7b\x42\x9e\xf5\x25\xa3\
-\x0d\xd1\x93\x5e\x23\x00\xef\x6d\x93\x69\xd5\x36\xbe\xa8\x6d\x63\
-\x58\x57\xd7\xc5\x58\xc1\x22\xc0\xd6\x4b\x32\xd6\x9f\xbd\x0e\x6f\
-\x77\xfa\x3c\x05\x63\xac\xbe\xb7\xf5\x8e\x3a\xc1\xc1\x46\xdf\x9f\
-\xb8\x30\x55\x32\x21\xdf\x14\x80\x1e\x23\x78\x41\x45\xf9\x56\x69\
-\x9e\xb0\x19\x6d\x8a\x1e\xf4\x0a\x01\xf8\x77\x99\xbc\xea\x58\xa7\
-\x5c\xa6\x28\x22\xbe\x5f\x82\x24\x21\xca\xb7\x6e\xdd\x5e\x27\x38\
-\x95\xa8\x23\x31\x06\x8a\x73\xcf\x78\xc4\x35\xdb\xe1\x70\x93\xa0\
-\xd3\x2d\x48\x39\xb9\xfe\x3d\xdc\x2c\x08\x97\xfc\xfb\xfe\x22\x80\
-\xac\x44\x89\x57\xc2\x89\x4e\x41\x9c\x15\xc6\xe6\x4a\x8e\x36\x73\
-\xda\xa5\xd7\x6a\x81\x2b\xfb\x87\x61\x1c\x7f\xe4\xb2\x4d\xf5\x2a\
-\x0b\x4a\x8b\xc4\x41\xa3\x0d\xe9\x29\x11\x2f\x00\x2f\x6e\xf2\xfe\
-\x8f\x43\x15\x3f\x1f\x99\x25\xa3\xcf\x3e\x97\x6e\xb1\xc3\xaa\x43\
-\x67\xce\xa8\x86\x64\x48\x46\xe7\x74\x3d\xba\xb7\x9f\xf5\xc6\xec\
-\x0d\x08\x01\xfd\x53\x25\xc3\x33\x25\xbd\x71\x13\xd4\x60\xda\x84\
-\x90\xf7\x15\xe7\x59\xdf\x32\xda\x90\x9e\x10\xb1\x4f\xfb\x63\x52\
-\x2a\x39\x1b\xf8\xb0\xdd\x29\xaf\x99\x31\xf8\xcc\x6e\xb8\xcb\xeb\
-\x7b\xb3\x1f\x38\x21\x70\x9f\xb5\x4b\x6d\xb5\xc0\x75\xc3\xbb\x3e\
-\x32\xab\x6a\x15\x94\x55\x87\xdf\x57\x62\x51\xb4\xed\xb8\xa7\xc4\
-\x42\x41\x8a\x2f\xb1\x47\xe4\xfa\xea\x47\x04\x12\xf8\x5d\x71\x9e\
-\xf2\x93\x48\x8d\x30\x0c\xbf\xa7\xdd\x0f\x9e\xde\x2c\x73\xed\x0e\
-\x2a\x9a\xed\x64\x4f\x1f\xa8\x92\x7a\x72\x6a\x5b\xd5\x2a\xd8\x7a\
-\x4c\xe0\xb9\xc8\x81\x4d\x46\x02\x5c\x39\x40\x3d\xc7\xf5\x51\x4a\
-\xa8\xef\x14\x20\x7d\x71\xf5\xdb\xeb\x02\xf3\x8a\xd3\x1b\x21\x7c\
-\xd3\xfa\x82\x64\xc8\x4c\xf4\x0d\xe2\xf2\x1a\x41\x65\xcb\xc5\x6d\
-\x8b\xb1\x42\x46\xbc\xa4\x5f\x02\xe4\x24\xca\xde\xe4\xa1\x17\x21\
-\x88\xb7\x2d\x16\xf1\x95\x71\x39\x22\xe2\xce\x99\x8c\x7f\xda\x35\
-\xf2\xc2\x16\x59\x5c\xdf\x22\xd7\x75\xba\x44\xdc\xd8\xb3\x12\x54\
-\xee\x6e\x10\xec\x3d\x7e\xe9\x5f\xa7\xb4\x40\x52\x90\x72\x66\x09\
-\x20\x25\x6c\xac\x12\x1c\x6b\x0f\x9f\xaf\x20\x4a\x81\xa2\x34\xc9\
-\xa0\x0c\xdf\x3e\x06\x80\xdd\x03\x15\x35\xe7\x06\xea\x08\x7c\x39\
-\x01\xf2\x52\x24\x59\x89\xbe\x10\xdd\xf0\xf9\x2d\xfa\x2c\x5b\x2d\
-\x52\xb9\x61\x5c\x81\xa8\x36\xda\x10\x2d\x44\xd4\x04\xf1\x85\x4d\
-\xf2\xe6\xda\x66\xde\xb0\xbb\x85\x75\x54\xf6\x99\xc1\x5f\xd5\xd2\
-\xf5\xe0\x07\x2e\x48\x65\x75\xa0\x31\x3c\x06\x7f\xb4\x05\xb2\x93\
-\x24\x79\x49\x90\x95\x74\xa1\x63\xcf\xa6\x4a\x85\xa6\xb3\xca\x69\
-\x26\xc7\x40\x71\x7e\xef\xdc\xd0\x73\x79\x3c\xbc\xfa\xf6\x4a\xae\
-\x9d\x31\x91\xac\x8c\x54\xa3\xcd\xd1\xca\x38\xaf\x50\xbf\x28\xaf\
-\x96\xf3\x4a\x0a\xc4\x56\xa3\x8d\xf1\x97\x88\x11\x80\x17\xcb\xe4\
-\xc3\x47\x5b\xf8\x83\xdb\x8b\x18\xda\xef\x4c\x59\x2a\xb7\xea\xdb\
-\xbc\xeb\x8e\x56\x07\x64\x25\xfa\xfe\xbf\xc5\xe1\x9b\x31\x5c\x8a\
-\x7e\x09\x92\x7e\xf1\x10\x1f\x03\xd1\x8a\x6f\x0d\xee\xf2\xc2\x81\
-\x13\x82\x66\x1d\x6a\xdb\x5a\x14\x5f\x42\x8d\xc2\x14\xdf\x54\x5f\
-\xe9\xc2\x7c\xab\x45\x02\x02\x45\xf8\x2a\xf3\x0e\xe9\x17\xe6\x19\
-\x4b\x02\xa4\x6c\xfb\x7e\xfe\xb8\x70\x09\x47\xab\xeb\xb9\x7a\x6a\
-\x89\xd1\xe6\x04\x4a\xbe\x14\xea\x9a\xb2\x5a\xf7\x1d\x13\xf2\xa2\
-\x96\x19\x6d\x8c\x3f\x44\x84\x00\x2c\xdc\xec\xfd\xcb\xe1\x26\xbe\
-\xeb\x95\xbe\xa9\xef\xc8\xac\x33\x53\xf9\x83\x8d\xc2\xaf\x60\x96\
-\x43\x4d\xe2\x74\xc4\x5c\x79\x8d\x72\x49\xc7\xa0\xf8\x68\x5f\xdd\
-\xbb\xd4\xd8\x73\x3d\xe7\xda\x9c\xe0\xf4\xf6\x6c\xc6\xa0\x08\x18\
-\xd2\x4f\x32\x24\x43\xfa\x9d\x5c\xb3\x38\x4f\x72\xa2\xf3\xcc\x5e\
-\x40\x6f\xa3\xa1\xb1\x85\xa7\x5f\x79\x9f\x8f\x56\x6f\x36\xda\x14\
-\x5d\x10\x90\x84\x54\xde\x2e\xab\xf1\xdc\x3b\x21\xdf\xfa\x9a\xd1\
-\xf6\x74\x47\xd8\x3f\x52\x4f\x7d\xa1\x2e\xae\x6c\x16\xb7\x9f\x3a\
-\x9f\x1f\x93\xa3\x9e\x33\x30\xab\x2f\xb1\x31\x76\x3e\x76\xb7\x2f\
-\x4d\x16\xd0\xa5\x60\xd8\x5c\xb0\xfa\x90\x2f\x9d\x56\xac\xd5\x77\
-\x7a\xe0\xf6\x42\x67\x0f\xe3\xc2\xa2\x2d\x30\xa5\xff\x99\x0d\x4b\
-\x7f\x89\x8b\xf2\x39\xf1\xf4\x36\x9c\x2e\x37\x6f\x7c\xf0\x19\xcf\
-\xbf\xf1\x31\x76\x47\xaf\x0b\xba\x8b\x16\x88\x97\xcb\x6b\x3c\x89\
-\x25\xf9\xd6\x85\x46\x1b\xd3\x15\x61\x2d\x00\x7f\x5f\xaf\x7e\x58\
-\xdb\x26\xae\x3d\xf5\xef\x9c\x24\x79\x4e\x41\xca\x56\x87\x2f\x92\
-\xcd\x5f\xb4\x84\xbd\x7a\x54\x6d\x6d\x77\x47\x69\x81\xbc\xe8\xe0\
-\xef\xb4\xd9\xe9\xb4\x39\xe9\xb4\x3b\xb0\x39\x1c\xd8\xed\xbe\x4e\
-\x5d\x1e\x0f\x0e\xe7\x85\xee\x8a\xb1\x31\x51\x44\x5b\x7d\x7f\xb6\
-\xa4\x04\x5f\x83\x71\x71\x31\xa4\x24\x26\x90\x94\x18\x87\xa2\x84\
-\xff\x02\x61\xed\xa6\x9d\xfc\xe5\xf9\x37\xa9\xad\xef\xd5\x25\xfe\
-\x2c\x20\xfe\x55\x56\xeb\x4d\x9a\x90\x67\xf9\x93\xd1\xc6\x5c\x8a\
-\xb0\x15\x80\x27\x3f\x57\x3f\x3d\xd6\x2e\x66\x9f\xfd\x59\xd1\x59\
-\xfb\x42\x52\xfa\x5c\x5d\xb5\xa0\x08\xdf\x71\x59\x66\x02\x24\xc7\
-\xfa\xda\xd8\xa7\xd3\xba\xfe\x6c\xa4\x54\x69\x69\x6e\xa2\xa3\xad\
-\x99\x8e\xb6\x56\xdc\xb6\x56\x8e\x6e\x68\xe1\x44\x53\x2b\x27\x9a\
-\xdb\x38\xde\xd4\x4a\x47\xa7\x9d\x0e\x9b\xce\x1d\x03\xf1\xf1\xb1\
-\xa4\x24\xc6\x93\x92\x94\x48\x46\x5a\x12\xd9\x19\xa9\xf4\x4b\x4f\
-\x21\x27\x33\x9d\xa2\xfc\x4c\x8a\xf2\xb3\x89\x8f\x35\xa6\xb6\x49\
-\x75\xdd\x09\xfe\xbc\x70\x29\x9f\x57\xec\x36\xa4\x7f\x03\x10\x42\
-\xf2\x44\x45\x8d\x37\xa5\x38\xdf\xf2\x0b\xa3\x8d\xb9\x18\x61\x29\
-\x00\x4f\x6f\x50\x57\xd7\xb4\xfa\x22\xf9\xce\x26\x2e\xca\x37\x15\
-\x56\xa5\xef\x68\xec\x78\x67\xd7\x02\x90\x18\xe3\xf3\x84\x8b\xb1\
-\xfa\x62\xe7\xd3\xe2\xcf\xec\xb2\xdb\xdd\xb0\xa3\xbe\x67\x83\xdf\
-\xeb\xf5\xd2\xd2\x74\x9c\x13\x0d\x75\xb4\x36\x9f\xe0\x44\x43\x1d\
-\x8d\xc7\xeb\x68\x3a\x5e\x8f\xdb\x6d\xcc\xb4\xd6\x66\x73\x60\xb3\
-\x39\x38\xd6\xd0\x74\xd1\x9f\x5f\x3f\xf3\x72\x7e\xf2\xed\x3b\x43\
-\x6a\x93\xd3\xe5\xe6\xe5\xb7\x96\xf3\xd2\x9b\x2b\x70\xb9\x7b\x49\
-\x10\x86\x06\x24\x3c\x56\x5e\xe3\x55\x4a\xf2\x2d\xff\xcf\x68\x5b\
-\xce\x27\xec\x04\xe0\xa9\xcf\xd5\x4f\x2f\x36\xf8\x01\x76\xd4\x2b\
-\x14\x24\x4b\x0e\x37\x0b\xda\x1c\xdd\xb7\x35\x22\xf3\xc2\xb3\xff\
-\xfa\x76\x41\x65\x0b\xd4\xb5\x9f\x89\x13\xf0\x97\x96\xe6\x46\x6a\
-\x8f\x1e\xa2\xae\xb6\x8a\x9a\xaa\x43\x34\x1c\xab\x46\x46\x5a\xf0\
-\x40\x88\x59\xb7\x79\x27\x7f\x7a\x76\xe9\x25\x05\xa9\x0f\xf1\xf3\
-\x8a\x5a\xaf\xa3\x38\xcf\xf2\x1b\xa3\x0d\x39\x9b\xb0\x12\x80\xa7\
-\x36\xc8\x77\x6b\x5b\x99\x7d\xa9\x9f\x37\x76\x42\x63\x37\x6f\xfd\
-\xb3\x39\xdc\x24\x90\xf8\x66\x0c\xad\x76\xa8\x6d\x13\x38\xfc\x2c\
-\x12\xe5\xf5\x7a\xa9\xad\x3a\x44\xd5\xe1\x83\x1c\xab\x39\x42\x6d\
-\xd5\x11\x9c\x0e\xfd\xa7\xec\xbd\x95\xca\xda\x06\x9e\x58\xb8\x84\
-\x4d\x5b\xf7\x19\x6d\x4a\xd8\x20\x25\xbf\x2e\xaf\xf6\xba\x4a\x0a\
-\x2c\x7f\x30\xda\x96\x53\x84\x8d\x00\x3c\xb9\x5e\xbe\x5e\xdb\xca\
-\x0d\x7a\xb6\xd9\x68\x83\x46\x3f\xf3\xdc\x03\xb4\xb5\x34\x71\x70\
-\xef\x4e\x8e\x1c\xd8\x4d\xd5\x91\x03\xb8\x5c\x4e\x3d\xcd\xe9\x13\
-\xd8\x1c\x4e\x5e\x78\xe3\x13\x16\xbf\xb7\x0a\xf7\xc5\x7c\xb2\xfb\
-\x3a\x82\xdf\x57\xd4\x7a\xda\x8a\xf3\xac\xff\x32\xda\x14\x08\x13\
-\x01\xf8\xe7\x06\xef\x33\xd5\xad\xdc\x66\x44\xdf\x4d\xc7\xeb\xd9\
-\xb7\x6b\x2b\xfb\xf7\x6c\xa3\xbe\xd6\x2c\x31\xdf\x13\x96\xaf\xdb\
-\xc2\xdf\x5f\x7c\x9b\x86\xc6\x16\xa3\x4d\x09\x67\x84\x94\xe2\xc9\
-\xb2\x1a\x4f\x5b\x38\xf8\x09\x18\x2e\x00\xcf\x6e\xf2\xfe\xf7\xd1\
-\x16\xe5\xc1\x50\xf6\xe9\xb0\x75\xb2\x6f\xf7\x56\x76\x6e\xd9\x44\
-\x4d\x65\xaf\xcb\xf2\x14\x72\x2a\x6b\x1b\xf8\xd3\xb3\x4b\xd9\xb8\
-\x65\xaf\xd1\xa6\x44\x0a\x16\x21\xc4\xa2\xf2\x1a\x77\x63\x49\x7e\
-\xd4\xa7\x46\x1a\x62\xa8\x00\xbc\x50\x26\xe7\x57\xb6\xf0\xdb\x50\
-\xec\xa3\x49\x29\x39\x72\x60\x37\x5b\x36\xae\xe5\xd0\xfe\xdd\x48\
-\x19\x91\xd1\x9b\x61\x85\xcd\xe6\xe0\xd9\xd7\x3f\xe2\x8d\x0f\xd6\
-\xe0\xf1\x9a\xd3\x7d\x4d\x48\xa2\x40\x59\xb2\xb9\x4a\x4e\x2d\x2d\
-\x14\xdb\x8d\x32\xc3\x30\x01\x78\xb9\x42\x8e\xaf\x6c\x61\xb1\xdb\
-\x1b\xdc\x40\x36\xa7\xc3\xce\xb6\xb2\xf5\x6c\xdd\xb4\x8e\x96\xe6\
-\x5e\xed\x78\x12\x32\xa4\x94\x7c\xb2\xa6\x9c\xbf\xbf\xf8\x0e\x8d\
-\x2d\x6d\x46\x9b\x13\xc9\x24\x2b\x8a\xfa\xce\xf6\x7a\x39\x79\x4c\
-\xb6\xa8\x37\xc2\x00\x43\x04\xe0\xc5\xad\x32\xab\xa6\x45\xae\xb3\
-\xbb\x45\xd0\xfa\xb7\x75\xb6\xb3\x65\xd3\x5a\xca\x3e\x5f\x6d\xee\
-\xde\xeb\x48\x65\x4d\x3d\x4f\x3c\xbb\xd4\xdc\xdd\xd7\x8f\x01\x6e\
-\x8f\xfa\xfe\xd6\x3a\x39\xdd\x88\x7c\x02\x21\x17\x80\xc5\x3b\x65\
-\x74\x4d\x13\xdb\x3b\x9c\x22\x28\xd5\x78\x3b\xda\x5a\xf9\xfc\xb3\
-\x8f\xd8\x51\xbe\x01\xaf\xd7\xcf\x33\x3f\x93\x6e\xe9\xb0\xd9\x59\
-\xf8\xea\x32\x96\x7e\xb4\x16\x6f\x6f\x2e\x08\x60\x0c\x13\x3e\x3f\
-\xc2\xf6\x95\x2b\xe5\xb0\x99\x33\x45\x48\x1f\xda\x90\x0b\x40\x43\
-\xab\x5c\xd3\x6c\x17\x59\x7a\xb7\xeb\x74\xd8\xd8\xb0\x66\x39\xe5\
-\x1b\x56\xe3\xe9\x83\xde\x66\xc1\x42\x4a\xc9\x47\x9f\x95\xf1\x8f\
-\x45\xef\xd0\xd4\xd2\x6e\xb4\x39\xbd\x96\x13\x9d\x0c\x94\xf1\x7c\
-\x0a\xcc\x08\x65\xbf\x21\x15\x80\x85\x1b\xbd\xbf\x39\xda\x22\x2e\
-\xd7\xb3\x4d\x55\x55\xd9\xb2\x69\x0d\xeb\x56\x2c\xc3\xe9\xe8\x15\
-\x99\x9a\xc3\x86\x7d\x87\xaa\xf9\xe3\x33\x4b\xd8\xb1\xef\x88\xd1\
-\xa6\xf4\x7a\x06\x67\x48\xf6\x34\x88\xe9\x0b\x37\x7b\x7f\xf9\x60\
-\xa9\xe5\xe7\xa1\xea\x37\x64\x02\xb0\x68\x93\x9c\x73\xa4\x8d\x47\
-\xf5\x6c\xb3\xae\xa6\x92\x4f\xdf\x7f\x9d\xba\x9a\x4a\x3d\x9b\xed\
-\xf3\x9c\x9a\xee\x2f\x59\xb6\x16\x55\x35\xa7\xfb\xa1\x60\x70\x86\
-\x64\xff\x09\x41\x6d\xab\xf2\xd3\x17\xcb\xe4\xaa\x7b\x27\x88\x15\
-\xa1\xe8\x37\x24\x02\xf0\xca\x5e\xd9\xaf\xba\x8e\x77\xf4\xda\xf1\
-\x77\x39\x1d\xac\x5c\xf6\x26\xdb\x2b\x36\x10\x71\x89\xfc\xc3\x18\
-\x55\x95\xbc\xbf\x62\x03\x4f\xbd\xf2\x1e\xad\x6d\x11\x97\xdf\x32\
-\xa2\xb1\x2a\xbe\xe4\xae\x9d\x2e\x44\x7d\x87\x7c\xef\xcd\x0a\x99\
-\x37\xbf\x58\x04\xdd\xa3\x2a\x24\x02\xd0\xd2\xc4\xe7\x1d\x4e\x62\
-\xf5\x68\xab\xf2\xf0\x3e\x96\xbd\xf9\x0a\x6d\xad\xcd\x7a\x34\x67\
-\x72\x16\x7f\x7b\xf1\x6d\x16\xbf\xb7\xda\x68\x33\xfa\x2c\xee\x93\
-\xae\x14\xed\x4e\x11\xd7\xe0\x94\x6b\x81\xd1\xc1\xee\x33\xe8\xd9\
-\x23\x9e\xd9\xe4\x7d\xaa\xae\x9d\x21\x3d\x6d\xc7\xeb\xf5\xb0\xe2\
-\x83\xa5\x2c\x7e\xe1\x1f\xe6\xe0\x0f\x12\x1d\x9d\xe6\x71\xa9\x91\
-\xe4\x26\xfb\x66\xb3\x42\x40\x8b\x43\x8c\x5a\xba\x4b\xfe\x57\xb0\
-\xfb\x0c\xea\x0c\xe0\xa9\x8d\x72\x76\x6d\x2b\x0f\xf5\xb4\x9d\xf6\
-\xd6\x16\xde\x5d\xfc\x1c\xb5\xd5\x47\x74\xb0\xca\xc4\x24\x3c\x29\
-\xce\x93\x8c\xcb\xf5\x89\xc0\xc9\x44\xb1\x3f\x29\xaf\x95\x8b\x4b\
-\xf2\xc4\xd1\x60\xf5\x19\xb4\x19\xc0\xe2\x9d\x32\xba\xcd\xce\x9b\
-\x1e\xb5\x67\xeb\xfe\xaa\x23\xfb\x59\xf4\xf4\xff\x99\x83\xdf\xa4\
-\x4f\xa0\x08\xce\xce\x12\x9d\x8a\xf4\x2e\x5a\x2c\xa5\x9f\x29\x64\
-\x03\xe8\x2f\x58\x0d\x37\xb7\xcb\xf7\x3b\x9c\x24\xf5\xa4\x8d\xb2\
-\xcf\x57\xb1\xf8\xf9\xbf\x63\xeb\x34\xcf\x9f\x4d\xfa\x2a\xe2\xaa\
-\x21\xc7\xd4\xa0\x2d\x05\x82\x22\x00\x2f\x6d\x95\xb7\x1d\x6b\x17\
-\x57\x07\xdc\x80\x94\xac\x5f\xf5\x21\x2b\x97\xbd\x69\x66\xdc\x31\
-\x31\x81\x5f\x6e\xa9\x91\xc5\xc1\x68\x58\x77\x01\xf8\xcb\x7e\x19\
-\x53\xd7\xca\xf3\x97\xca\xbb\xdf\x1d\x5e\xaf\x87\x77\xdf\x78\x81\
-\xf5\x2b\x23\xa2\xae\x82\x89\x49\xf0\x91\x44\xd5\xb6\xab\xaf\xff\
-\x7d\xa5\x4c\xd4\xbb\x69\xdd\x05\x20\xae\x99\xb7\xdb\x1c\x24\x04\
-\x72\xaf\xd7\xeb\xe5\xdd\xc5\xcf\xb1\x77\x47\x85\xde\x66\x99\x98\
-\x44\x34\x07\x1a\x95\xc1\x4a\x2c\x4b\xf5\x6e\x57\x57\x01\x58\x54\
-\x26\xaf\xad\x69\x65\x6e\x20\xf7\x9e\x1a\xfc\x07\xf6\xec\xd0\xd3\
-\x24\x13\x93\x88\xa7\xc5\xee\xcb\x87\x59\xdf\xc1\x9c\x85\x9b\xe4\
-\x75\x7a\xb6\xad\x9b\x00\x2c\x5e\x2c\x2d\xf5\x1d\xbc\x1a\xc8\xd4\
-\x5f\x3d\x3d\xf8\x0d\xcb\x8b\x60\x62\x12\x96\x48\x60\x7b\xbd\x72\
-\x3a\xb9\x6d\xb3\x9d\x7f\x2f\x5e\xac\xdf\xa9\x80\x6e\x02\xd0\x31\
-\x90\x7f\xb5\x39\x49\x09\xe4\xde\x15\xcb\x96\x9a\x83\xdf\xc4\xe4\
-\x22\xec\xa8\x13\x34\x9e\xe5\x95\xdd\xe6\x20\xa5\xb5\x3f\xba\x95\
-\x1b\xd3\x45\x00\x16\x6f\x91\xf9\xc7\xda\xb9\x2f\x90\x7b\x37\xac\
-\xf9\x84\x2d\x1b\xd7\xea\x61\x86\x89\x49\xaf\xc1\xe1\x81\x4d\xd5\
-\x82\x83\x8d\x17\xba\xd1\xd4\xb7\x73\xcf\x33\xeb\x64\x7f\x3d\xfa\
-\xd1\xc5\x13\xb0\xc5\x21\x97\x3a\x3d\x42\xb3\x98\xec\xd9\x51\xc1\
-\x9a\xe5\xef\xeb\x61\x82\x89\x49\xaf\xa0\xcd\xc9\xc9\xa8\xc0\x4b\
-\x17\xae\x71\xa9\x28\x4e\x85\xa5\xc0\x84\x9e\xf6\xd7\xe3\x19\xc0\
-\xbf\xcb\xe5\xec\xda\x76\xed\x31\xfe\x8d\xc7\xeb\xf8\xe8\xad\x57\
-\xcc\x68\x3e\x13\x13\xc0\xe3\x85\xb2\x1a\xc1\xca\x03\x0a\x55\x2d\
-\xdd\x57\xad\x6a\xe8\xa0\xe4\xb9\x32\x79\x63\x4f\xfb\xed\xf1\x0c\
-\xa0\xc9\xce\x22\xad\x1b\x7f\x6e\x97\x8b\xb7\x5f\x7b\xd6\xb0\xfa\
-\x79\xc1\xc6\x6a\xb1\x90\x96\x92\x48\xbf\xf4\x14\x12\xe2\x7d\x41\
-\x90\x2e\x97\x87\x6d\x7b\xcc\x14\xe4\x26\x17\xe2\x51\x61\xed\x51\
-\x85\x16\x0d\xb1\x58\x52\x42\x73\x27\x0b\x81\x1e\x65\xd7\xea\x91\
-\x00\xbc\xb8\xc9\xfb\xf0\xc1\x66\x72\xb5\xde\xf7\xc9\xfb\x8b\x69\
-\x3a\x6e\x48\x12\x54\xdd\x89\x8b\x8b\xa1\x64\xd4\x10\x86\x0f\x2a\
-\x64\xd8\xc0\x02\x86\x0c\xc8\x25\xbb\x5f\x1a\x42\x9c\xbb\x76\xab\
-\xad\x6f\xe4\x8e\x6f\xff\x7f\x06\x59\x69\x12\xce\xec\xa8\x13\x9a\
-\x06\xff\x29\x5a\x1c\x64\x3e\xb7\xc9\xfb\x93\xaf\x4e\xb4\xfc\x3a\
-\xd0\xbe\x03\x16\x80\xc5\x8b\xa5\xe5\xa8\x5d\xfe\xaf\xd6\x09\xfc\
-\xee\x6d\x9b\xd9\xb5\x65\x53\xa0\xdd\x86\x05\x39\x99\xe9\xcc\xbc\
-\x62\x1c\x57\x94\x5c\xc6\x98\x11\x83\x88\xb2\x06\x2d\x56\xc3\xa4\
-\x97\xd3\x6c\x87\xa3\x2d\x81\xc7\xcb\x1d\xb7\x89\x9f\x2e\x5e\x2c\
-\x1f\xbf\xe3\x0e\x11\x50\x61\x86\x80\x05\xa0\x63\xa0\xfa\xfb\xf6\
-\x66\x25\x4e\xcb\x3d\x0e\x5b\x27\x2b\x97\xbd\x15\x68\x97\x86\x12\
-\x6d\xb5\x32\x75\xe2\x28\xe6\x4e\x9f\xc8\x15\x25\x97\xa1\x28\x41\
-\x4f\xa5\x60\xd2\xcb\x91\x12\xb6\x1e\x53\x7a\xb4\x0d\xd6\xe1\x14\
-\x71\x2d\x03\xd4\x3f\x00\x3f\x08\xe4\xfe\x80\x04\xe0\x31\x29\xad\
-\x8d\x9f\xf1\x4d\xad\xf7\x2d\xff\x70\x69\xc4\x45\xf6\xc5\xc5\x46\
-\x73\xc3\xac\xc9\xdc\xbd\x60\x36\x19\xa9\xc9\x46\x9b\x63\xd2\x8b\
-\xa8\x6c\x09\x6c\xea\x7f\x3e\x8d\x1d\xca\x37\x17\xef\x94\xff\x7d\
-\xc7\x28\xa1\x79\x53\x2d\x20\x01\x18\x54\xae\xfe\xfe\x80\x53\xd1\
-\x94\xe2\xeb\xf0\xfe\xdd\xec\xde\xb6\x39\x90\xee\x0c\x21\x36\x26\
-\x9a\x3b\x6e\x98\xc6\x97\xe6\xcd\x20\x25\x39\xa0\xd0\x06\x13\x93\
-\x4b\x22\x81\x03\x17\x39\xe3\x0f\x04\xbb\x87\xe8\xd6\x4e\xfe\x04\
-\x7c\x4b\xeb\xbd\x9a\xe7\xb1\x8b\xa5\xb4\x34\x74\x88\xff\xd0\x72\
-\x8f\xea\xf5\xb2\xe2\xc3\x25\x5a\xbb\x32\x8c\x29\x13\x46\xb2\xe8\
-\x89\x47\x79\xe8\xce\x79\xe6\xe0\x37\x09\x0a\x76\x17\xb4\xeb\x58\
-\x7d\xbe\xd1\x26\xbf\xf6\xdc\x61\xa9\x39\xef\xa6\xe6\x19\x80\x7b\
-\x8b\xfa\xfb\x36\x8d\x6f\xff\xad\x65\xeb\x69\x6e\x3c\xae\xb5\xab\
-\x90\x93\x95\x91\xca\x0f\x1e\x58\xc0\xb4\x49\x63\x8c\x36\xc5\xa4\
-\x97\x63\xd7\xb9\x76\x8d\xcd\x25\xa2\x3c\xc7\xd5\x3f\x03\xdf\xd0\
-\x72\x9f\xa6\x19\x80\x94\x52\x34\x74\x28\x9a\x3a\x70\xb9\x9c\x6c\
-\xf8\xec\x23\x2d\xb7\x18\x42\xc9\xe8\xa1\x2c\xfc\xdd\x0f\xcd\xc1\
-\x6f\x12\x12\xa2\x82\x90\x8d\xb3\xc9\xa6\xdc\xa7\x35\x50\x48\x93\
-\x19\x2f\x95\xab\x3f\x6e\xb6\x2b\x7e\xd7\xf4\x53\x04\x1c\xa9\x58\
-\x4e\x47\x7b\xf8\x6e\xfc\x29\x8a\xc2\x83\x5f\x9a\xcb\x3d\xf3\xe7\
-\xa0\x28\x41\x2d\x54\x6c\x62\x72\x9a\xa6\x4e\xfd\x9f\xb5\x4e\x17\
-\x31\x9d\x03\xd5\x9f\x02\xbf\xf0\xf7\x1e\x4d\x02\x90\x9d\x28\xe6\
-\x67\x24\x48\x6c\x6e\x68\x73\xc0\x09\x9b\xa0\xe3\x12\xeb\x18\xab\
-\x05\xc6\x64\xd8\xf8\xeb\xca\x55\x5a\xba\x08\x29\xf1\xf1\xb1\xfc\
-\xef\x23\x5f\x65\xe2\xb8\x61\x46\x9b\x62\xd2\x87\x70\x7a\x60\x67\
-\x7d\x70\x5e\x36\x8d\x76\xe5\xfb\x04\x43\x00\xca\xab\xe5\x38\x84\
-\x7a\x5e\xf0\x81\xc4\xe6\x82\x23\xcd\x82\xa3\x2d\x02\xa7\xc7\x97\
-\xd3\x3c\x33\x41\x32\x26\x47\xf2\xce\x87\xeb\xb0\xdb\x75\xdc\xe9\
-\xd0\x91\xa4\x84\x78\xfe\xef\xa7\x5f\x67\xd4\xd0\x01\x46\x9b\x62\
-\xd2\x87\xf0\xaa\xb0\xb1\x5a\xc1\x1d\xa4\x8a\x6b\x6d\x0e\xd2\x9e\
-\x2f\x93\x5f\xb9\x7f\x82\x78\xd9\x9f\xeb\xfd\x9f\x01\x08\xf5\xbf\
-\xe1\xc2\x14\xdf\xf1\xd1\x30\x32\x5b\x72\x59\x96\xc4\xee\xf1\x95\
-\x38\x8a\xb6\x80\xc7\xeb\x65\xc9\xb2\xf0\x0c\xf3\x4d\x4f\x4d\xe2\
-\x89\x9f\xfd\x07\x83\xfb\xe7\x19\x6d\x8a\x49\x1f\xc2\xa3\xc2\xc6\
-\xaa\x73\xe3\xfb\x83\x41\x9b\x93\x5f\x01\x7e\x09\x80\x5f\x9b\x80\
-\x1b\x1b\x64\x0e\x82\xdb\xbb\xba\x46\x08\x88\x8f\xf2\x0d\x7e\x80\
-\xe5\x6b\x2b\x68\x38\x11\xf4\xd2\x66\x9a\x49\x4f\x4d\xe2\xef\xbf\
-\xfa\xae\x39\xf8\x4d\x42\x4a\xab\x03\x56\x1d\x52\x68\xe8\x08\xfe\
-\x3e\x53\x53\x27\x03\x9f\xdf\x22\x47\xf9\x73\xad\x5f\x33\x80\x28\
-\x8f\xfa\x55\x29\x89\xd2\x62\xc4\x1b\x1f\xae\xd1\x72\x79\x48\x88\
-\x89\x8e\xe2\x37\x8f\x3e\x40\x61\x6e\xa6\xd1\xa6\x98\xf4\x11\x1c\
-\x1e\xd8\x77\x5c\x70\xa4\x59\x10\x68\xa6\x6c\xad\xa8\x12\x9c\x4e\
-\xf9\x7f\x40\xb7\xf9\x03\xbb\x9d\x01\x48\x29\x85\x94\x3c\xa0\xc5\
-\x80\xa3\xd5\xf5\xec\x3e\x10\x5e\x25\xbb\x85\x10\xfc\xe8\x9b\x5f\
-\x36\xd7\xfc\x26\x41\x47\x05\xea\xda\x05\x65\x35\x82\x4f\xf7\x2b\
-\x1c\x6a\x0a\xdd\xe0\x3f\x45\x93\x4d\xcc\x7e\x6c\xa5\xec\xf6\x05\
-\xdf\xed\x05\x15\x35\x9e\x59\x08\x45\x53\x71\xcf\x0f\x56\x85\x5f\
-\xb4\xdf\x03\x77\xcc\x65\xce\x55\x25\x46\x9b\x61\xd2\xcb\xb1\xbb\
-\x61\xfd\x51\x45\x57\x2f\xbf\x00\xed\x88\x2a\x48\xe7\x11\xe0\xf1\
-\xae\xae\xeb\x7e\x0f\x40\x28\x5f\xd7\xd2\xb1\xaa\xaa\x7c\xfc\x59\
-\x78\xf9\xfc\x97\x8c\x1e\xca\xfd\xb7\x5d\x63\xb4\x19\x26\x7e\x90\
-\x94\x10\x4f\x6c\x4c\xb4\xd1\x66\x04\x84\x04\x2a\x6a\x85\xe1\x83\
-\xff\x14\x36\x87\xec\x36\x36\xa0\xcb\x19\xc0\x86\x6a\x99\x01\xea\
-\x2d\x5a\x3a\x2d\xdb\x7e\x80\xe3\x4d\xad\x5a\x6e\x09\x2a\x71\x71\
-\x31\xfc\xf8\x5b\x5f\xbe\x20\x41\x87\x49\x78\xa1\x28\x82\xeb\x66\
-\x5c\xce\x37\xef\xbe\x81\xd4\x64\xdd\x0b\xe0\x9c\xc6\xe6\x70\xd2\
-\x70\xbc\x99\xa6\xd6\x76\x4e\x34\xb5\xd1\x61\x77\x60\xb7\x3b\x70\
-\x38\xdd\xb8\xdc\x1e\x84\x80\xa4\x84\x38\x14\x45\x21\x25\x29\x81\
-\x94\xa4\x04\xd2\x52\x12\x29\xc8\xcd\x24\x39\xb1\x6b\x1f\xb8\xa3\
-\xcd\x22\x24\x9b\x7c\xfe\xd2\x62\x17\x45\x2f\x6e\x90\x03\xef\x9d\
-\x24\x0e\x5f\xea\x9a\x2e\x05\xc0\x2a\xd4\x3b\x81\x18\x2d\x9d\xae\
-\xdd\x1c\x5e\xe9\xbd\xbf\x7d\xcf\x4d\xe4\x66\xa5\x1b\x6d\x86\x49\
-\x17\x0c\x1f\x5c\xc8\x23\x5f\xbb\x95\x91\x43\x75\x49\x74\x0b\x80\
-\xcb\xe3\xe1\xd0\xd1\x5a\x0e\x57\xd5\x73\xb8\xba\x8e\x23\x95\x75\
-\x1c\xa9\xa9\xe7\x58\x43\x53\xc0\xf5\x26\x93\x93\x12\xe8\x9f\x97\
-\xc5\x65\x43\x0a\x19\x3d\x7c\x20\xa3\x87\x0f\x20\x2b\x23\x15\x80\
-\xfa\x0e\xc1\x8e\xba\xf0\x19\xfc\x70\x72\x33\x50\xa8\xbf\x02\xee\
-\xbe\xd4\x35\x5d\x0a\x80\x40\xde\x7e\x91\xa3\xff\x2e\xf9\xbc\x7c\
-\xb7\xa6\xeb\x83\xc9\x84\x31\x43\xb9\x79\xce\x15\x46\x9b\x61\x72\
-\x09\x92\x93\x12\xf8\xc6\x9d\xd7\x73\xe3\xd5\x57\xf4\xd8\x0d\xdb\
-\xe3\xf5\xb2\x6b\xff\x51\x2a\x76\x1e\xa0\x7c\xc7\x01\x76\xec\x3d\
-\x82\xd3\xa5\x6f\xc4\x4d\x5b\x7b\x27\xdb\xf7\x1e\x66\xfb\xde\xc3\
-\x2c\x7e\xff\x33\x00\x06\x15\xe5\x32\x66\xcc\x18\x92\x0b\xc6\x90\
-\x9d\x57\xa4\x6b\x7f\x7a\xd0\x6a\x57\x6e\xe8\xea\xe7\x97\x14\x80\
-\xcd\x47\x64\x2e\xa8\x57\x6a\xe9\xec\x68\x75\x3d\xb5\xf5\x8d\x5a\
-\x6e\x09\x1a\x8a\x22\xf8\xfe\x57\xe7\x9b\x53\xff\x30\x44\x51\x04\
-\x37\xce\xbe\x82\x6f\xdc\x75\x3d\xc9\x49\x81\x87\x5b\x77\xd8\xec\
-\x7c\xb6\x61\x3b\xab\x3e\xdf\x4a\xc5\xae\x03\xd8\x1d\xa1\x4f\x32\
-\x7b\xa8\xf2\x18\x87\x2a\x8f\x01\x1f\x93\x9e\x99\xcd\xf8\x89\x57\
-\x32\x6a\xdc\xe5\xc4\xc4\x6a\x4a\x96\x15\x34\xda\x9d\xa4\x3c\x5f\
-\x26\x27\xdc\x3f\x41\x94\x5d\xec\xe7\x97\x14\x00\x4b\xb4\x7a\x87\
-\x94\x68\x8a\x2c\x5a\x5f\xbe\x4b\xab\x7d\x41\x63\xf6\x94\x62\x06\
-\x15\x69\xce\x57\x6a\x12\x64\x46\x0e\xed\xcf\x23\x5f\xbb\x95\xe1\
-\x83\x0b\x03\xba\xdf\x66\x77\xb2\x66\xe3\x76\x96\xaf\xaf\x60\xd3\
-\xd6\xbd\xb8\x3d\x01\xa5\xc2\x0b\x0a\x4d\xc7\xeb\x59\xf1\xc1\x52\
-\xd6\x7c\xf2\x1e\xe3\x4a\xa7\x30\x69\xda\x1c\xe2\xe2\x83\xb7\x9f\
-\xe1\x0f\x12\x70\xb9\xe5\x8f\x81\xdb\x2e\xf6\xf3\x4b\x0a\x80\x94\
-\xf2\x0e\xad\xd3\xff\x0d\x5b\xf6\x68\xba\x3e\x58\x58\x2c\x0a\x5f\
-\xbd\x23\xa0\x1a\xa5\x26\x41\x22\x35\x39\x91\x6f\xde\x7d\x03\xd7\
-\xcd\xb8\x3c\xa0\xe9\xfe\xd1\xea\x7a\x96\x2c\x5b\xcb\x87\xab\x37\
-\x85\x6d\x7c\xc9\x29\xdc\x6e\x17\x9b\x3f\x5f\xc5\xb6\xb2\xcf\x29\
-\x99\x3c\x83\xcb\xaf\x9a\x4d\x74\xb4\xa6\xad\x34\x5d\x69\x77\x89\
-\xab\x2f\xf5\xb3\x8b\x0a\xc0\xd6\x6a\x59\xe0\x45\x9d\xac\xa5\x13\
-\x55\x55\xd9\xb5\xef\xa8\x56\xdb\x82\xc2\xdc\x69\xa5\x14\xe5\xf5\
-\x28\x5d\xba\x89\x4e\x28\x8a\xc2\xfc\xb9\x53\xf8\xda\x97\xaf\x23\
-\x29\xc1\xef\x48\xf2\xd3\x6c\xa8\xd8\xc3\x2b\xef\xac\xa0\x7c\xc7\
-\x81\x80\x37\xef\x8c\xc2\xe5\x72\xf2\xc5\x67\x1f\xb1\x7b\xdb\x26\
-\xe6\xdc\xf8\x25\x06\x0c\x19\x61\x88\x1d\x6d\x0e\x52\x5e\xdc\x20\
-\x4b\xef\x9d\x24\x2e\x38\x9f\xbf\xa8\x00\xa8\x42\xbd\x09\x8d\xc9\
-\x42\x0e\x55\x1d\xc3\xe6\x30\x5e\x99\x15\x45\x70\xdf\x6d\x73\x8c\
-\x36\xc3\x04\x18\x3b\x62\x10\x0f\x7f\x6d\x01\x43\x07\xe4\x6b\xbe\
-\x77\xdb\x9e\x43\x3c\xfd\xf2\x07\x6c\xdd\x7d\x30\x08\x96\x85\x96\
-\xd6\x96\x26\xde\x58\xf4\x24\xa3\x8a\x27\x31\xfb\xfa\x5b\x43\x3e\
-\x1b\x90\x80\x03\xf5\x87\xc0\x5d\xe7\xff\xec\xa2\x02\x20\x11\xd7\
-\xfa\x6e\xf3\x9f\x1d\x7b\xc3\xe3\xed\x3f\x69\xdc\x08\xf2\xb3\xfb\
-\x19\x6d\x46\x9f\x26\x3d\x2d\x99\x6f\xdd\x7d\x23\x73\xa7\x4d\xd0\
-\xbc\x09\x5b\x5b\xdf\xc8\x5f\x9f\x7f\x8b\x35\x9b\x76\x04\xc9\x3a\
-\xe3\xd8\x59\xb1\x81\xfa\x9a\x4a\x6e\xbe\xf3\x6b\xa4\xa5\x87\xf6\
-\x19\xb5\xb9\x94\x99\x17\xfb\xfc\x02\x01\xd8\xb9\x53\x46\x3b\x51\
-\x2f\x7a\x71\x57\xec\xdc\x77\x24\x00\xb3\xf4\x67\xfe\x75\x53\x8d\
-\x36\xa1\xcf\x62\xb1\x28\xdc\x7a\xdd\x55\x3c\x78\xc7\x5c\x12\xe2\
-\xb5\xed\x82\x3b\x5d\x6e\x5e\x7e\x6b\x39\x2f\xbf\xb5\x42\xf7\xe3\
-\xbb\x70\xe2\x44\xc3\x31\x5e\x7a\xfa\x0f\xdc\x78\xfb\x7d\x21\x5d\
-\x12\xb4\xb9\xc8\x7e\xe9\x0b\x99\x7c\xf7\x64\xd1\x76\xf6\xe7\x17\
-\x08\x80\x33\xd5\x33\x0d\x14\xcd\x5b\x97\x3b\xf7\x1b\x1f\xfc\x93\
-\x96\x92\xc4\xa4\x71\xc6\xac\xb3\xfa\x3a\xc5\xa3\x86\xf0\xf0\x83\
-\x0b\x02\x3a\x79\x59\xb3\x69\x07\x7f\x7d\xfe\xad\xb0\x39\x42\x0e\
-\x36\x4e\x87\x8d\xa5\xaf\xfc\x93\x1b\x6e\xbb\x8f\x61\x23\xc7\x85\
-\xa4\x4f\x8f\x17\xe1\xb6\xa8\xdf\x00\x7e\x7f\xf6\xe7\x17\x2e\x01\
-\x84\x72\x9d\xc6\xd9\x3f\x6e\x8f\x97\x9a\x3a\xe3\xb3\xfe\xce\x9a\
-\x32\x0e\x8b\xc5\xac\xd8\x13\x4a\x32\x52\x93\xf9\xe6\x3d\x81\x4d\
-\xf7\xab\xeb\x4e\xf0\x97\x67\xdf\x0c\xab\xe3\xe3\x50\xa1\x7a\xbd\
-\xbc\xbb\xf8\x79\xae\x5f\xf0\x15\x2e\x1b\x5b\x1a\x92\x3e\x6d\x1e\
-\xf1\x25\xba\x15\x00\xc9\xb5\x5a\x1b\xae\xad\x3b\x81\xd7\x1b\xa4\
-\x1c\x47\x1a\x98\x31\x39\x34\x6a\x6a\xe2\xab\x80\x7c\xc7\x0d\xd3\
-\xb9\xff\xb6\x6b\x88\x8f\xd3\xb6\xa9\x65\x77\xb8\x78\x71\xe9\x27\
-\xbc\xf6\xce\x2a\x5c\x1e\x4f\x90\x2c\x0c\x7f\xa4\x54\xf9\x60\xe9\
-\xcb\x58\xac\x51\x21\x99\x09\x74\xb8\xc4\x05\x49\x42\xce\x11\x80\
-\xed\xf5\x32\xdb\xed\x51\x47\x6a\x6d\xf8\x68\xad\xf1\x6f\xff\x84\
-\xf8\x38\xc6\x8c\x18\x68\xb4\x19\x7d\x82\xe2\x51\x83\xf9\xe1\xd7\
-\x6e\x63\x60\x61\x8e\xe6\x7b\xd7\x6d\xde\xc9\x13\x0b\x97\x52\x77\
-\xbc\x29\x08\x96\x45\x1e\x3e\x11\x58\x44\x72\x4a\x1a\x39\xf9\xc1\
-\x75\x25\xb6\x39\x89\x7d\x63\x9b\x1c\x71\xdb\x58\x71\xda\x61\xe7\
-\x1c\x01\x70\x7b\xbd\x53\xb5\x3a\xff\x00\x54\xd5\x1a\x5f\xea\x7b\
-\xd2\xb8\x61\x58\x2d\x66\x95\xde\x60\x92\x99\x9e\xc2\x37\xbe\x72\
-\x03\xd7\x4e\xd7\x3e\x65\xad\xac\x6d\xe0\xcf\xcf\xbd\xc9\x86\x8a\
-\xf0\x70\x16\x0b\x27\x3c\x6e\x37\x6f\xfd\xfb\x19\xee\x7e\xe8\x11\
-\x12\x93\x53\x82\xd6\x8f\x04\xda\x9c\xea\xfd\xc0\x8f\x4e\x7d\x76\
-\x8e\x00\x08\xc4\x95\x81\xb8\x5a\x1c\xad\x69\xe8\x99\x65\x3a\x50\
-\x32\xc6\x4c\xed\x1d\x2c\xa2\xad\x56\xbe\x74\xd3\x0c\xee\x5d\x30\
-\x87\xb8\x58\x6d\xb1\xfa\x36\xbb\x93\xe7\x5e\xff\x88\xd7\xdf\xff\
-\x0c\x8f\x37\x7c\xdc\x76\xc3\x8d\x8e\xf6\x56\xde\x5d\xfc\x1c\x5f\
-\x7e\xf0\x7b\x08\x11\xbc\x7d\x2c\xbb\x5b\xcc\xe1\x52\x02\x20\x91\
-\x57\x06\x32\x03\xa8\x6b\x30\x7e\x3a\x37\x76\xc4\x00\xa3\x4d\xe8\
-\x95\x94\x8c\x1e\xca\x0f\x1f\x5c\xc0\x80\x00\xa7\xfb\x7f\x7c\x66\
-\x09\xf5\x27\x9a\x83\x60\x59\xef\xa3\xa6\xea\x30\x1b\xd7\x2e\x67\
-\xd2\x55\xc1\x73\x64\xb3\xb9\x18\x7e\xf6\xbf\x4f\x0b\xc0\xfa\x2a\
-\x19\x87\x54\xc7\x07\xd2\x68\x63\x6b\x47\x4f\xed\xea\x11\x09\xf1\
-\x71\x0c\x28\x30\x03\x7f\xf4\x24\x37\x2b\x9d\xef\xde\x3f\x9f\x69\
-\x97\x8f\xd6\x7c\xef\xc1\xa3\xb5\x3c\xb1\x70\x29\x5b\x76\x45\xbe\
-\x17\x5f\xa8\x59\xbf\x72\x19\x83\x86\x8e\x22\x33\x27\x38\x59\xab\
-\x3b\x5c\x22\xe1\x1f\xdb\x64\xda\xb7\xc6\x8a\x66\x38\x4b\x00\x62\
-\x2d\x4c\x44\xa2\x69\x7e\xd7\xe9\x82\x6d\x75\x82\x86\x66\x63\x4b\
-\x7f\x0d\x1d\x90\x67\x96\xf5\xd2\x89\xe8\xa8\x28\xbe\x72\xcb\x2c\
-\xee\x9e\x3f\x9b\x98\x68\x4d\x89\xa0\xe9\xb0\xd9\x59\xf8\xea\x32\
-\x96\x7e\xb4\x36\x2c\x4e\x85\x22\x11\xaf\xd7\xc3\x87\x6f\xbe\xc4\
-\xdd\xdf\xf8\x4f\x14\x45\xff\xa5\x80\x04\x12\x5d\x7c\x09\x78\x0a\
-\xce\x12\x00\x21\xd5\xcb\xb5\xac\xff\xdb\x9d\xb0\xe6\xb0\x82\xc3\
-\xe5\xc5\x61\xb3\xe9\x6c\xa6\x36\xcc\xb0\x5f\x7d\x98\x70\x32\x77\
-\x62\x5e\x76\x86\xa6\xfb\xa4\x94\x7c\xb8\x6a\x13\x4f\xbe\xf4\x1e\
-\xcd\xad\xe1\x5b\x07\x32\x52\x68\xa8\xab\x61\x7b\xd9\xe7\x8c\x9b\
-\xa8\x29\x1d\x87\xdf\xb8\x24\x73\x39\x5f\x00\x90\x8c\xf3\x77\xf9\
-\x2f\x25\x94\x55\x2b\xb8\xbc\x60\xb3\x75\x18\x1e\xa5\x65\x0a\x80\
-\x3e\xcc\x0d\x60\x77\x7f\xdf\xa1\x6a\xfe\xf8\xcc\x12\x76\x84\x89\
-\x2b\x78\x6f\x61\xdd\xaa\x0f\x19\x39\xae\x94\xa8\x20\x04\x0e\xd9\
-\x5d\x8c\x3d\xf5\xff\xa7\x05\x40\x8a\x33\x1f\x76\x47\x75\xab\xa0\
-\xc5\xe1\xfb\x7f\x5b\x87\xf1\x8a\x6f\x06\xff\x84\x9e\xb6\x0e\x1b\
-\xff\x7c\xe5\x7d\xde\xf9\xf4\x0b\x54\xd5\x9c\xee\xeb\x8d\xad\xa3\
-\x9d\x0d\x6b\x96\x33\x75\xf6\xf5\xba\xb7\xdd\xe9\x92\xa7\x37\x18\
-\x14\x80\xcd\x9b\x65\x14\xe0\x97\x13\xbd\x94\xb0\xef\xc4\x99\xa9\
-\x82\xcb\x6d\x7c\x08\x70\x76\x66\xaa\xd1\x26\xf4\x19\x54\x55\xf2\
-\xce\x27\x9f\xf3\xe5\xef\xfe\x9a\xb7\x3e\x5e\x6f\x0e\xfe\x20\x52\
-\xf6\xf9\x4a\x6c\x9d\xfa\xbf\x60\x1d\x6e\x11\xfb\xf4\x66\x19\x0f\
-\xa7\x66\x00\xd9\x8c\x00\xff\x36\x00\xab\x5a\xcf\xcd\x7b\xee\x75\
-\x1b\xeb\xca\x29\x84\x20\xbb\x5f\x9a\xa1\x36\xf4\x15\x76\xed\x3f\
-\xca\x1f\x9f\x59\xca\x9e\x83\xc6\x07\x7e\xf5\x05\xdc\x6e\x17\x5b\
-\x36\xad\x65\xca\x8c\x6e\x2b\x7c\x69\x42\x05\xa2\xe1\x1a\xe0\x2d\
-\x05\xc0\x62\xf1\xfa\x35\xfd\x6f\x77\x72\x41\xea\x63\x8f\xc7\xd8\
-\xd0\xcd\xc4\xf8\x38\xcd\xbb\xd5\x26\xda\x68\xeb\xb0\xf1\xe7\x67\
-\xdf\xe4\x3f\xfe\xe7\xcf\xe6\xe0\x0f\x31\x15\x1b\xd6\xe2\x71\xeb\
-\x3f\xc6\x54\xd5\x17\xf2\x6f\x05\x90\xaa\x18\xd5\xd5\x06\xa0\xc3\
-\x03\xc7\xda\x04\x7b\x8e\x0b\x5c\xe7\x39\x73\x79\xbc\xc6\xce\x00\
-\xba\x2b\xd6\x60\x72\x06\x21\x04\x03\x0a\xb2\xfd\xbe\x5e\x55\x55\
-\xde\xfc\x68\x3d\xcf\xbc\xfa\x21\xed\x9d\xc6\x9e\xf4\xf4\x55\xec\
-\xb6\x0e\x76\x6f\x2f\x63\x4c\x89\xa6\x0c\x7d\xdd\xe2\x51\x45\x09\
-\x9c\x5a\x02\x08\x06\x81\x6f\x7d\xdf\xe6\x80\x86\x4e\x41\x9b\x13\
-\x3a\x5d\x82\x0e\x27\x17\x0c\xfa\xb3\x51\x0d\x8e\xe6\x4a\x4e\x32\
-\x05\xc0\x1f\x2e\x1b\x52\xc4\xc3\x0f\x2e\xf0\xbb\xf8\xc6\xb6\xdd\
-\x87\xf8\xe3\x33\x4b\x39\x70\xb4\x26\xc8\x96\x99\x74\x47\xd9\xe7\
-\xab\x74\x17\x00\x97\x57\x14\xc2\x49\x01\x58\x77\x44\x5c\xa3\x4a\
-\x41\x9b\x03\xdc\x1a\xf7\x74\x82\x31\x3d\xd1\x82\xd6\x50\xd4\xbe\
-\x46\x6a\x72\x22\xdf\xb8\x6b\x1e\xf3\x66\x4d\xf2\xcb\x59\xaa\xb1\
-\xa5\x8d\x7f\x2c\x7a\x97\x8f\x3f\x2b\x33\xfc\x78\xd7\xc4\xc7\x89\
-\x86\x63\x34\x1c\xab\x26\x2b\xb7\x40\xb7\x36\x9d\x1e\x99\x01\x27\
-\x05\xa0\xd9\x2e\x52\x3d\x11\xba\x99\x1b\x6d\xed\xb6\xc0\x71\x9f\
-\xc4\x62\x51\x98\x37\x6b\x12\xdf\xb8\x73\x1e\x29\xc9\xdd\x17\xdf\
-\xf0\x7a\x55\x96\x2c\x5b\xcb\xc2\xd7\x96\xd1\x69\xb3\x87\xc0\x42\
-\x13\x2d\xec\xde\x5e\xae\xab\x00\x38\x3c\xc2\x77\x0a\xf0\xd7\x0d\
-\x72\xe0\xf1\xd6\x00\x22\x80\x4e\x62\xb1\x18\x3b\x00\x2d\x56\x33\
-\x03\xd0\xf9\x14\x8f\x1a\x7c\x32\x3d\x97\x7f\xfe\xe4\xe5\x3b\x0f\
-\xf0\xc4\x33\x4b\x38\x5c\x55\x17\x64\xcb\x42\x4b\x5a\x4a\x12\x69\
-\x29\x89\xa4\x24\x25\x90\x9c\x14\x8f\xd7\xeb\xc5\xee\x70\xd1\x61\
-\x73\x50\x73\xec\x04\x1d\x11\x24\x74\x7b\x77\x94\x33\x7d\xce\x8d\
-\xa0\x53\xa5\x2b\x97\x17\xe5\x2f\x9f\xc9\x4c\x6b\x9c\x85\x1e\x15\
-\xcf\x0b\x86\xbf\xb2\x16\xcc\x1c\x00\x67\xc8\xca\x48\xe5\xdb\xf7\
-\xde\xcc\xec\x2b\xfd\x8b\xe9\x6a\x68\x6c\xe1\xef\x2f\xbe\xcd\xf2\
-\x75\x5b\x82\x6c\x59\xf0\x89\xb6\x5a\x19\x37\x6a\x10\x25\xa3\x86\
-\x32\x7c\x50\x21\x43\x06\xe4\x91\x9e\x9a\x74\xc9\xeb\xa5\x94\x54\
-\x1d\x3b\xce\x9e\x03\x55\x6c\xd8\xba\x87\x35\x9b\x76\x60\xb3\x39\
-\x42\x68\xb1\x36\xda\x5a\x9b\xa9\xae\x3c\x4c\x41\xff\x41\xba\xb5\
-\x99\x94\x24\x63\xa2\xc1\x00\x00\x20\x00\x49\x44\x41\x54\xc0\x64\
-\xab\xe2\x51\x2f\xd3\x58\x02\xe0\x1c\x84\xc1\x03\xd0\x74\x44\xf1\
-\x3d\xfc\x5f\xbe\x79\x06\xf7\xcc\xf7\x2f\x5e\xdf\xed\xf1\xb2\xf8\
-\xbd\x55\x3c\xff\xc6\xc7\x86\xd4\xd3\xd3\x8b\xb8\xd8\x68\xa6\x4f\
-\x1a\xcb\xac\x2b\x8b\x29\x19\x35\x84\xd8\x18\xff\x63\xd9\x84\x10\
-\x14\xe5\x65\x51\x94\x97\xc5\x35\xd3\x26\x60\x73\x38\x79\x7f\xc5\
-\x06\x16\xbd\xb9\x9c\xa6\xe6\xb6\xee\x1b\x30\x80\xfd\xbb\xb6\xe8\
-\x2a\x00\x0a\x8c\xb1\xaa\x82\x1e\xd5\x64\x56\x0c\x16\x00\x4f\xa4\
-\x6e\x5e\xe8\xc4\x94\x09\x23\xf9\xde\x57\xe7\x53\x90\xe3\x9f\x3b\
-\xf4\xc6\x2d\x7b\xf9\xd3\xb3\x4b\xa9\xac\x35\x3e\x89\x4b\xa0\x0c\
-\x1f\x5c\xc8\xad\xd7\x4e\x65\xc6\xe4\x71\xba\x6d\x02\xc7\xc7\xc6\
-\x70\xfb\xf5\xd3\x98\x37\x73\x12\xff\xfa\xf7\x07\xbc\xf1\xe1\x9a\
-\xb0\xdb\x04\x3d\x72\x68\xaf\xae\xed\xb9\x25\x03\xad\x28\x4a\x7a\
-\x4f\x1a\xb1\x18\xbc\x04\x70\x1b\xec\x87\x60\x14\x85\xb9\x99\x7c\
-\xf7\xab\xb7\x30\xa5\xc4\xbf\x14\x8e\xb5\xf5\x8d\xfc\xed\x85\xb7\
-\xf8\x6c\x63\xe4\x16\xdc\x98\x5c\x72\x19\x77\xdd\x3c\x8b\x92\x51\
-\x43\x82\xd6\x47\x7c\x5c\x0c\xdf\x7f\x60\x3e\xa5\x63\x87\xf1\x8b\
-\x3f\x2d\x0a\x8b\x6a\x57\xa7\x68\x6c\xa8\xa3\xa3\xbd\x95\xc4\x24\
-\x7d\xd2\x86\xa9\x1e\x99\x67\x2d\xce\x53\x77\x0f\x4a\x63\xde\xd6\
-\x63\x0a\xcd\x01\xec\x89\x18\x3d\x03\xb0\xdb\x23\x77\x0a\x1b\x08\
-\x71\xb1\xd1\xdc\x77\xeb\x35\xdc\x71\xe3\x74\xbf\x4e\x40\x7c\x05\
-\x37\x56\xf0\xf2\x5b\xcb\x23\xb6\xe0\xc6\xd8\x11\x83\xf8\xe6\x3d\
-\x37\x30\x66\x78\xe8\x92\xbe\x5e\x59\x3a\x8a\xbf\xfe\xe2\xdb\x7c\
-\xff\x97\x4f\xd1\x11\x46\x4e\x50\x95\x07\xf7\x31\x72\xfc\x44\x5d\
-\xda\xf2\x48\x91\x69\x55\x20\x23\x35\x0e\xae\x1a\xa8\x52\x56\x2d\
-\xa8\x69\xd3\xb6\xcb\x68\xb5\x6a\xcb\x11\xa7\x37\x6d\x9d\x91\xb3\
-\x93\xdb\x13\x84\x10\xcc\x9e\x32\x9e\x6f\xdd\x7b\x13\x59\x19\xfe\
-\x05\x3f\x7d\xb6\x71\x07\x7f\x7b\x21\x72\x0b\x6e\xe4\x66\xa5\xf3\
-\x9d\x7b\x6f\x66\xfa\x64\xbf\x03\x55\x75\x65\xf8\xe0\x42\x1e\x7a\
-\xe8\xeb\xfc\xe5\x6f\xff\x30\xdc\xdf\xe5\x14\x47\x0e\xed\xd5\x4d\
-\x00\xbc\x2a\x69\x56\x89\xc8\x00\x89\x22\x60\x42\x81\xc4\x79\x14\
-\x4e\x74\xfa\x2f\x02\xf1\xf1\xdd\x9f\x31\x07\x93\xf6\x8e\xf0\x51\
-\xe7\x60\x31\xb8\x7f\x1e\x3f\x78\x60\x3e\xc5\x7e\x4e\x7d\x23\x3d\
-\x03\x6f\x4c\xb4\x2f\x2b\xd1\x57\x6e\xd1\x9e\x95\x48\x4f\xaa\x5b\
-\x05\x4a\xea\x20\x66\x5f\x7f\x1b\x1f\xbd\xfd\x6f\xc3\xec\x38\x9b\
-\xaa\xc3\xfb\x75\x6b\x4b\x55\x65\xa2\x15\xd4\x7e\xa7\x12\x81\x2a\
-\x02\x26\x15\x49\x3e\x3b\x74\x6e\xc4\x5f\x57\xc4\xc6\x19\xeb\x8a\
-\xdb\xda\xde\x89\xd7\xab\xf6\xca\x8a\x40\x49\x09\xf1\x7c\xed\xcb\
-\xd7\x71\xcb\x35\x53\xfc\xfa\xfd\x6c\x0e\x27\x2f\x2e\xf9\x84\xd7\
-\xde\x5d\x85\xdb\x13\x99\x19\x78\xaf\x9a\x38\x9a\xef\xde\x7f\x8b\
-\xe6\xac\x44\xc1\x60\xdf\x71\xdf\xb8\x18\x53\x32\x99\xc3\xfb\x77\
-\xb1\x6f\xd7\x56\x83\x2d\x82\xf6\xb6\x16\x6c\x1d\xed\xc4\x27\x5e\
-\xfa\x88\xd3\x5f\x3c\x52\xc4\x59\x41\x9c\x33\x9f\x8c\x52\x60\x72\
-\x7f\x95\xcf\x0e\x29\x38\xfd\xd8\x5f\x8b\x8d\x8b\xf7\x39\x27\x18\
-\xb4\x63\xaa\xaa\x2a\xc7\x9b\x5a\xc8\xc9\xec\xd1\x5e\x66\x58\xa1\
-\x28\x82\x1b\x66\x4d\xe2\xa1\xbb\xe6\x91\x9a\xec\x5f\x99\xc6\x4f\
-\xd7\x55\xf0\x8f\x17\xdf\xa1\xa1\xb1\x25\xc8\xd6\x05\x87\xa2\xbc\
-\x2c\xbe\xff\xd5\xf9\x4c\x2a\x0e\x9f\xda\x8e\x67\x3f\xd1\xb3\xae\
-\xbb\x95\xc3\x07\xf6\xe0\x76\x19\xbf\x29\x58\x7f\xac\x9a\x81\x43\
-\x2f\xeb\x71\x3b\x5e\x29\x63\xac\x42\x12\x2b\xcf\x9b\xf1\x27\x44\
-\xc1\xd4\x01\x2a\x1b\x2a\x15\x3a\xba\xd9\x63\x53\x2c\x16\xa2\xa2\
-\xa2\x0d\xfd\x62\xea\x8e\x37\xf7\x1a\x01\x18\x35\x74\x00\x0f\x7f\
-\x6d\x3e\x23\x06\xfb\x57\x25\xe6\x50\xe5\x31\x9e\x58\xb8\x84\x8a\
-\x9d\x91\x99\x81\x37\x3e\x36\x86\x7b\x6f\x9d\xc3\x97\x6e\x9c\x41\
-\x94\x35\xbc\x9c\xba\x06\xa4\x49\xb6\x9f\x0c\x7f\x4f\x4c\x4e\xa1\
-\xf4\x8a\x19\x7c\xbe\xfa\x23\x83\xad\x82\x06\xbd\x04\x40\x15\x16\
-\xab\x14\x17\x4f\x04\x92\x14\x03\xb3\x86\xa8\x54\xb6\x08\x1a\x3a\
-\xa0\xd5\x21\xb0\xb9\x2f\xfe\xa2\x8f\x8f\x4f\xa0\xd5\x40\x01\xa8\
-\xaa\x3d\xce\xf8\x91\x83\x0d\xeb\x5f\x0f\xd2\xd3\x92\xf9\xe6\xc9\
-\xaa\x3b\xfe\x14\xd9\xec\xb4\xd9\x59\xf8\xda\x32\x96\x2c\x8b\xdc\
-\x0c\xbc\x57\x5f\x59\xac\x69\x53\x33\xd4\xf4\x4f\x93\xec\xaa\x17\
-\x78\x4f\x3e\xf3\xa5\x53\x66\x50\xf6\xc5\x6a\x5c\x4e\x63\x3d\x06\
-\x1b\x8e\x55\xeb\xd2\x8e\xaa\xa2\x58\xe9\x22\x13\x90\x22\x7c\x2a\
-\x38\x20\x0d\x40\xe2\xf0\x40\x4d\xab\xa0\xaa\x55\xd0\xea\x38\x23\
-\x06\xb1\x71\xf1\xb4\xb6\x18\x57\x1c\xe4\x60\x65\xad\x61\x7d\xf7\
-\x14\xab\xc5\xc2\x6d\xd7\x5f\xc5\x03\xb7\xcf\x25\x3e\x3e\xb6\xdb\
-\xeb\x4f\x65\xe0\x7d\xea\xe5\xf7\x68\x6a\x31\x3e\x1f\x63\x20\x0c\
-\x2a\xca\xe5\xe1\x07\x17\xf8\xbd\xa9\x69\x14\x56\x05\x62\xa3\x7c\
-\xe9\xef\x01\x62\x62\xe3\x19\x39\xb6\x94\x2d\x9b\xd6\x1a\x6a\x57\
-\x7d\x9d\x4e\x02\x00\xc2\x8a\x20\xc6\xdf\x72\xe0\xb1\x56\x18\x9c\
-\x21\x19\x9c\x21\x71\xab\xd0\x66\x87\x76\xa7\xe0\x93\x7e\x69\xd4\
-\xeb\xa4\x4a\x81\xb0\xff\x70\x64\x0a\x40\xe9\xd8\x61\xfc\xe0\xab\
-\xf3\xfd\xae\xba\x13\xe9\x19\x78\x13\xe2\xe3\x78\xf0\x4b\xd7\x72\
-\xeb\xb5\x53\x23\x62\xd3\x56\x05\x6c\xe7\x9d\xfe\x8d\x2b\xbd\xd2\
-\x70\x01\x68\x69\x6e\xc4\xe3\x76\x63\x8d\xea\xf1\x09\x89\xb0\x6a\
-\x2d\x06\x72\x8a\x28\x05\x32\x12\x20\x23\x41\x32\xbc\x28\x8d\xad\
-\xdb\x7b\x6a\x4b\xe0\xec\x39\x58\x89\xcb\xe3\x89\x98\xd0\x60\xad\
-\x45\x36\xdb\x3b\x6d\x3c\xfb\xda\x47\x2c\x59\xb6\x36\x22\x63\x1f\
-\x84\x10\x5c\x33\x6d\x02\xdf\xbe\xe7\xa6\x2e\x03\x74\xc2\x0d\xbb\
-\xeb\xc2\x25\x6f\x66\x4e\x1e\xe9\x99\xd9\x34\x1d\x37\xb0\x20\xae\
-\x94\xb4\xb5\x36\x91\xde\xcf\xff\xec\x4e\x17\xc3\xab\x22\xba\x5c\
-\x02\xf8\x4b\xae\xc1\x47\x36\x4e\x97\x9b\x3d\xfb\x2b\x19\x7b\x99\
-\x7e\x81\x12\xc1\x20\x26\x3a\x8a\xbb\x6e\x9e\xc5\x57\x6e\x99\xe5\
-\x57\xe0\x8a\xaa\xaa\xbc\xf3\xe9\x17\xfc\xf3\xdf\x1f\xd0\xd6\xde\
-\x19\x02\x0b\xf5\x67\xf8\xe0\x42\x1e\x7e\x70\x01\xa3\x87\x0d\xf0\
-\xeb\xfa\xda\xfa\x46\x52\x92\xe2\x49\x88\x8f\x0b\xae\x61\x7e\x10\
-\x6d\x05\x8b\xe0\xf4\x1e\xc0\x29\x86\x5e\x36\x96\x0d\xc7\x3f\x31\
-\xc6\xa8\x93\xb4\x34\x37\xf6\x58\x00\x24\xe7\x15\x07\x0d\x94\xbc\
-\x2c\xe3\xcf\x6c\x37\x6e\xdd\x1b\xb0\x00\x34\xda\xa0\xaa\x45\x70\
-\x59\x96\x24\x26\x48\x93\x88\x94\xa4\x78\x16\x3d\xf1\xa8\xdf\xe7\
-\xdb\x3b\xf6\x1d\xe1\x8f\xcf\x2c\x61\xdf\x21\xe3\x96\x56\x3d\x21\
-\x39\x29\x81\x6f\xdc\x79\x3d\x37\x5e\x7d\x85\x5f\x99\x88\x1c\x4e\
-\x17\x2f\xbf\xb5\x82\x57\xde\x5e\xc1\xa2\x27\x1e\x0d\x0b\x01\x88\
-\x52\x60\x58\xa6\x64\x77\xc3\xb9\xf6\x0f\x18\x72\x19\x1b\x3e\x33\
-\x56\x00\x5a\x9b\xf5\xf1\xee\xb4\x02\x2e\xa0\x47\xdf\x76\x6e\x96\
-\xf1\x69\xb9\x57\x6f\xd8\xc6\xd7\xbe\xac\x3d\x7d\x72\x65\xab\x60\
-\x6b\x8d\x6f\xa7\x37\x3d\x1e\x8a\x52\xcf\xc8\xbd\xdd\x0d\x31\x56\
-\xdf\x66\x68\x4f\x49\x88\x8f\xf3\xeb\xa1\x6e\x6b\xef\xe4\xb9\xd7\
-\x3f\x66\xc9\xb2\x35\xa8\x6a\x78\x45\xa3\xf9\x83\xa2\x08\xe6\x5c\
-\x35\x81\xef\xde\x77\xb3\xdf\x3e\x0c\xeb\x36\xef\xe4\xcf\xcf\xbd\
-\x19\x96\x2e\xcb\xc3\x32\x25\x4e\x0f\x1c\x6a\x3a\xf3\x10\xe4\x15\
-\xf4\xc7\x6a\x8d\x32\x34\x23\xb6\x1e\x9b\xee\x02\xb0\x22\x70\x21\
-\x7b\x26\x00\x39\x61\x30\x03\x38\x5c\x55\xc7\xa1\xca\x63\x7e\x97\
-\x09\xb3\xb9\x60\x7b\xbd\xe0\xd8\xc9\xd8\x87\xe4\x58\x28\x48\xf1\
-\x0d\x38\x09\xec\xaa\x17\x1c\x38\x21\x88\xb2\xc0\xd0\x7e\xbe\x8d\
-\xcf\x60\xd6\x1f\xf5\x7a\x55\x96\x7c\xb8\x86\x85\x8b\x3f\x8a\xd8\
-\x94\x5c\x63\x86\x0f\xe4\xe1\x07\x17\x30\x6c\x90\x7f\xa9\xab\x8e\
-\x54\xd5\xf1\xa7\xe7\xde\x64\xf3\xb6\x7d\x41\xb6\x2c\x70\x04\x30\
-\x26\x57\x62\x55\xce\x14\xc4\xb1\x58\xac\xe4\xe4\x17\x52\x7d\xf4\
-\x90\x61\x76\xb5\xe9\x30\x03\xb0\x28\x48\x2b\x92\x1e\x1f\xe0\xc7\
-\xc7\xc6\x90\x93\x99\x4e\xdd\x71\xe3\x8e\x02\x01\xde\xfe\x64\x3d\
-\x0f\x3f\x78\xeb\x45\x7f\xe6\x51\xa1\xd3\x09\x2d\x0e\x41\x7d\x07\
-\xd4\xb5\x09\xce\xde\x4e\x2b\x4c\xf5\x0d\x70\x55\x42\x45\xad\xa0\
-\xaa\xc5\xf7\xc7\x76\x79\x61\x67\xbd\xe0\x68\xb3\x60\x42\x81\x4a\
-\x5a\x10\x66\xa6\xe5\x3b\x0f\xf0\xa7\x85\x4b\x39\x54\x79\x4c\xff\
-\xc6\x43\x40\x7a\x5a\x32\xdf\xba\xfb\x46\xe6\x4e\x9b\xe0\x97\x0f\
-\x83\xcd\xe6\xe0\xd9\xd7\x3f\xe2\x8d\x0f\xd6\xe0\xf1\x86\xbf\xcb\
-\xb2\x00\x46\x66\x4b\x12\xa2\x61\x6b\xad\xef\xb9\xe9\x97\x95\x67\
-\xac\x00\xb4\x36\xeb\xd1\x8c\x3c\xb5\x04\xe8\x31\xc3\x06\xe6\x19\
-\x2e\x00\xcb\x56\x97\xf1\xf5\x3b\xaf\x27\xf1\xe4\x54\x5b\x95\x70\
-\xe0\x84\xa0\xb2\x55\xd0\xe9\xa4\xcb\xd3\xce\x7d\x0d\x82\x13\x9d\
-\xd0\xe6\x10\xd8\x2f\x32\xb3\xeb\x70\xf9\xaa\x21\x4f\x2a\x92\x64\
-\x27\xea\x33\x35\x8f\xf4\x94\x5c\x56\x8b\x85\x5b\xaf\x9b\xca\x03\
-\x77\xcc\xf5\x6b\x79\x23\xa5\x64\xd9\xea\xcd\x3c\xf9\xf2\x7b\x61\
-\x9b\x75\xa7\x2b\xfa\xa7\x49\xe2\xa2\x60\x63\xb5\x20\x33\xdb\xbf\
-\x7c\x8b\xc1\xc2\x61\xef\x79\x10\x9c\x02\xd2\x2a\x24\xae\xf3\x5d\
-\x81\x03\x61\xd8\xa0\x42\xc3\x93\x4d\x74\xda\xec\x2c\x7e\x6f\x35\
-\x0f\xdc\x71\x2d\x52\xc2\xfa\xa3\x0a\x27\xfc\xdc\x3c\x77\xab\x50\
-\xdf\xde\xf5\x17\xa1\x4a\xf8\xa2\x52\x30\x24\x1d\x32\x13\x7d\xbb\
-\xc3\x36\x97\x4f\x58\xfa\xc5\x4b\x52\x2f\x32\x06\xaa\x5a\x05\x95\
-\xcd\xe0\xf0\x08\x14\x01\x59\x89\x92\xc1\xe9\x1e\xde\xf9\x78\x1d\
-\xff\x7a\xf5\x03\xec\x76\xe3\x7d\xcb\x03\xa1\x78\xd4\x10\x1e\x7e\
-\x70\xbe\xdf\x89\x47\xf7\x1d\xaa\xe6\x4f\xcf\xbe\xc9\xb6\x3d\xc6\
-\xbd\x35\xf5\x20\x2b\x51\x72\xd5\x00\x49\xed\x91\x2c\x43\xed\xb0\
-\xdb\x7a\x7e\x2a\xa4\x28\xa8\x56\x29\xd0\xc5\xaf\x71\xd8\x40\xfd\
-\x52\x16\xf7\x84\x57\xdf\x5b\xcd\x4d\x73\xae\xa0\x43\xa6\xfa\x3d\
-\xf8\xb5\x20\x25\xec\x6f\x14\xec\xbf\x60\x09\x26\xc8\x4a\x94\x8c\
-\xc9\x91\x24\xc5\x80\xc7\x0b\xdb\xea\x04\x95\x2d\xe7\x8a\xca\x96\
-\xed\x7b\x58\xfd\xd1\x52\x4e\x1c\x8f\xcc\x94\x5c\x59\x19\xa9\x7c\
-\xe7\xbe\x9b\x99\x35\xc5\xbf\xc4\xa3\x2d\x6d\x1d\xfc\xf3\x95\xf7\
-\x79\x6f\xc5\x86\x88\xdc\xd4\xbc\x18\x29\xb1\x70\xf5\x98\x54\x5e\
-\x34\xd0\x06\xa7\xd3\x81\x94\x2a\x42\x04\xee\x50\x65\x15\xd2\x6b\
-\x05\xd9\x4c\xe0\x59\xc1\x4f\x33\x6c\x50\x7e\x8f\xdb\xd0\x03\x9b\
-\xcd\xc1\x9f\x9f\x7d\x93\x69\x37\x3d\xe0\xf7\x3d\x85\x29\x92\x31\
-\xb9\x92\xf5\x47\x94\xd3\x65\xcf\x03\xa1\xa1\x43\xb0\xe2\xa0\x20\
-\x21\xca\xe7\x41\x76\xf6\xf3\xde\xda\xdc\xc8\xca\x65\x6f\x72\x60\
-\x8f\x81\x1e\x53\x3d\x20\xda\x6a\xe5\x4b\x37\xcd\xe0\xde\x05\xfe\
-\x25\x1e\xf5\x7a\x55\xde\xfa\xb8\xf7\x96\x15\x2b\xca\x49\x45\x51\
-\x14\xe3\x1c\xb3\xa4\xc4\x61\xb3\x13\x97\x10\x78\x3e\x0e\x8b\x82\
-\xd3\x0a\xca\x89\xae\x57\xc7\xfe\xd1\x2f\x2d\x85\x8c\xd4\x64\x1a\
-\x5b\x8c\x5f\xdb\xad\xfc\x7c\x2b\x31\xd9\x5f\x30\xba\xd8\xbf\x72\
-\x4a\x49\xb1\x10\x6d\x81\xd4\x38\x49\x8b\xa3\x67\x62\x28\x25\xe7\
-\x44\x50\x7a\xdc\x6e\x36\xae\xfb\x94\x8d\x6b\x97\x87\x4d\x56\x19\
-\xad\x5c\x51\x7c\x19\xdf\x7b\x60\x3e\x85\xb9\x99\x7e\x5d\x5f\xb1\
-\xf3\x00\x7f\x7a\xf6\x4d\x0e\x1e\x8d\x4c\x17\x6d\x7f\xb0\x5a\x2c\
-\x24\x25\xc6\xd1\xda\x66\x9c\x83\x96\xc3\xd1\xd9\x23\x01\x50\x84\
-\xb0\x59\x41\xea\x76\xf8\x5a\x32\x66\x08\x9f\xac\x29\xd7\xab\xb9\
-\x1e\xf1\xe9\xfb\x6f\x90\xd6\x2f\x9b\xfc\xc2\xee\xf3\xc8\x1d\x6c\
-\x14\x08\xe0\x58\x37\x7b\x00\x5a\xd9\xb7\x6b\x2b\xab\x3f\x7a\xcb\
-\xd0\x40\xa9\x9e\x90\x97\x9d\xc1\xf7\xee\x9f\xcf\xd4\x89\xa3\xfc\
-\xba\xbe\xa1\xb1\x85\x7f\xbc\xf8\x0e\xcb\xd7\x6f\x09\xbb\x8c\xba\
-\xc1\x20\x25\x31\xc1\x50\x01\xb0\xdb\x6c\xa4\xf5\xe0\x04\x5e\x51\
-\xe8\xb0\x0a\x38\xa1\xd7\x9f\x6a\xe2\xd8\xe1\x61\x23\x00\x1e\xb7\
-\x9b\xa5\x2f\xfd\x93\xdb\xef\xfb\x26\x39\x79\x5d\xc7\xd6\x3b\x3d\
-\xbe\xa3\x3e\xbd\xa8\xae\x3c\xc8\x67\x1f\xbf\x43\x6d\xd5\x11\xdd\
-\xda\x0c\x25\xb1\x31\xd1\xdc\x3d\x7f\x36\x77\xdd\x3c\x93\x68\x3f\
-\x02\x4e\x5c\x1e\x0f\x8b\xdf\x5d\xcd\x0b\x4b\x22\xbb\xce\x80\x56\
-\x92\x12\x8c\xcd\x86\xd5\xd3\x1c\x1c\x56\x85\x26\xab\x0a\x8d\x7a\
-\x3d\xfa\xa5\x63\x86\xe9\xd4\x92\x3e\x38\x1d\x36\x16\x3f\xf7\x37\
-\xae\xbf\xf5\x5e\x86\x8c\x18\x1d\xf4\xfe\x8e\x1e\xdc\xcb\xc6\x75\
-\xcb\x39\x7a\x50\xdf\xfc\xed\xa1\x64\xfa\xe4\xb1\x7c\xf7\xbe\x9b\
-\xfd\x4e\xb0\xb2\xbe\x7c\x17\x7f\x7d\xee\x2d\xaa\x8e\x1d\x0f\xb2\
-\x65\xe1\x47\x5c\x9c\xb1\x09\x71\x55\xb5\x67\x3e\x14\x56\x45\x1e\
-\xb7\x0a\x64\xa3\x1e\x9b\x80\x00\x59\xfd\x52\x29\xca\xcb\x0a\xab\
-\xa2\x13\x2e\x97\x93\xb7\x5e\x7d\x86\xe2\x89\x53\xb9\x6a\xf6\x3c\
-\xa2\x63\xf5\xf5\xe4\x71\x39\xec\xec\xd9\xb9\x85\x2d\x1b\xd7\xd0\
-\x50\x17\xb9\xa5\xb4\x8b\xf2\xb3\x79\xf8\x81\x05\x4c\x1c\xe7\x9f\
-\x88\x57\xd7\x9d\xe0\x2f\xcf\xbd\xc9\xfa\xb2\x5d\x41\xb6\x2c\x7c\
-\x89\x32\x38\x25\x7e\x4f\x13\xc1\x58\x15\x51\x6b\x15\x2a\xc7\xa4\
-\x8e\xa1\xd9\xa5\x63\x87\x85\x95\x00\x00\x20\x25\x15\x1b\xd7\xb0\
-\x77\xe7\x16\x2e\xbf\xea\x6a\xc6\x4d\xb8\x82\xa8\xe8\xc0\x2b\xca\
-\xb8\x5d\x4e\x8e\x1c\xdc\xcb\xde\x1d\x15\x1c\xd8\xb3\xdd\x50\x9f\
-\xf0\x9e\x12\x1f\x1b\xc3\xfd\xb7\x5f\xc3\xed\xf3\xa6\xfb\x95\x92\
-\xcb\xee\x70\xb1\xe8\xcd\x4f\x78\xf5\xed\x55\xb8\x3c\x7d\xb3\x28\
-\xcb\x29\xac\x06\xa7\x30\xeb\xf1\x0c\xc0\xaa\x1e\xb1\x4a\xab\xe5\
-\x08\x3a\x1e\x65\xcc\xbc\x62\x1c\x4b\x97\x19\x9b\x30\xe1\x52\xd8\
-\x3a\xdb\x59\xb5\xec\x4d\xd6\xad\xf8\x80\x21\x23\xc6\x30\x70\xc8\
-\x08\x72\xf2\x8a\x48\xeb\x97\xd5\xa5\x0b\x6b\x7b\x5b\x0b\x0d\x75\
-\x35\x1c\xaf\xab\xe6\xe8\xc1\xfd\xd4\x54\x1d\x42\x8d\x00\x17\xd6\
-\xae\x10\x42\x9c\x4e\xc9\x95\x99\xee\x5f\xa5\x99\xe5\xeb\xb6\xf0\
-\xf7\x17\xdf\x8e\xd8\xc4\xa3\x7a\x63\x74\x52\x13\x6f\x0f\xab\x62\
-\x09\xaf\xb2\xcd\xea\xf0\x70\x38\x56\x41\xa2\xd3\x3a\x60\xdc\x65\
-\x83\xc9\x4c\x4f\xe1\x78\x53\xab\x1e\xcd\x05\x05\xb7\xcb\xc9\xee\
-\x6d\x9b\xd9\xbd\x6d\x33\x00\x51\xd1\x31\xa4\x65\x64\x12\x1d\x15\
-\x43\x54\x4c\x0c\x16\x8b\x05\xbb\xad\x13\xbb\xad\x93\xce\x8e\x36\
-\x9c\x8e\xc8\x0c\xce\xb9\x14\x5a\x53\x72\x1d\xaa\xac\xe5\x89\x85\
-\x4b\x23\x36\xf1\x68\xb0\x30\x3a\x8e\x41\xf6\x70\x09\xd0\xea\xe5\
-\x0b\xeb\x94\x42\x61\x2f\xaf\xf1\x1e\x07\x74\xf1\x6d\x54\x14\xc1\
-\xec\x2b\x8b\x79\xf5\xdd\x55\x7a\x34\x17\x12\xdc\x2e\xa7\x6e\x89\
-\x16\xc3\x99\xb8\xd8\x68\xee\xbc\x69\x26\xf7\x2c\x98\xe3\xd7\x74\
-\xbf\xc3\x66\x67\xd1\xd2\xe5\x2c\x7e\x2f\x72\xeb\x0c\x04\x13\x97\
-\xdb\xd8\x25\x90\x2a\x03\xff\x9b\x44\x5b\x50\xbf\x33\x49\x34\x5a\
-\x01\x84\x90\x87\xa5\x14\xba\x39\x37\x5f\x3d\x35\xb2\x04\xa0\xb7\
-\x23\x84\x60\xee\xb4\x09\x7c\xeb\xee\x1b\x49\x4f\x4b\xee\xf6\x7a\
-\x55\x95\xbc\xbf\x62\x03\x4f\xbf\xf2\x3e\x2d\x6d\x1d\x21\xb0\x30\
-\x32\xf1\xb8\x0d\x9e\x01\xf4\xc0\xb5\x3a\x36\x8a\x4e\x38\x99\x11\
-\x48\x4a\x71\x04\x98\xa4\x8b\x55\xc0\x88\xc1\x45\x0c\x28\xcc\xe1\
-\x48\x55\x9d\x5e\x4d\x9a\x04\xc8\xb0\x81\x05\xfc\xe0\xc1\xf9\x8c\
-\x1d\xe1\x5f\xb6\xa4\x5d\xfb\x8f\xf2\xc4\xc2\xa5\xec\x3e\x50\x19\
-\x64\xcb\x22\x9f\x4e\x83\x2b\x07\xf7\x24\x29\x68\x8c\x45\x36\xc1\
-\x69\x01\xe0\xa0\x1f\x61\xdc\x9a\xb8\xe3\xfa\x69\xfc\xee\xe9\xc5\
-\xfa\x36\x6a\xe2\x37\x49\x09\xf1\x7c\xfd\xae\xeb\xb9\x65\xce\x15\
-\x28\x7e\x96\x70\x7f\xe5\xed\x15\x3c\xf9\xd2\x7b\x7d\xc2\x8b\x4f\
-\x0f\x8c\xce\xd3\x68\xb1\x6a\x13\x00\x01\xa4\xc4\x41\x6e\x92\x24\
-\x23\x4e\xbe\x07\x27\x05\x40\x08\xb9\x43\x2f\x5f\x80\x53\x5c\x3b\
-\x73\x22\xcf\xbc\xf6\x61\xc4\xe6\xae\x8f\x58\x84\x60\xe4\xd8\x52\
-\xe6\xdf\x72\x33\x63\xfb\x27\x22\x84\xff\x83\xf9\x48\x75\xbd\x39\
-\xf8\x35\xd0\x62\xb0\x00\xf8\x3b\x03\x88\xb1\xfa\x52\xdd\x0d\x48\
-\x97\x24\x9c\xbc\x45\x48\xb9\x1a\x4e\x0a\x80\xc5\x62\xd9\xae\x77\
-\x75\x99\x68\xab\x95\x1b\x67\x5f\xc1\x0b\x4b\x3e\xd6\xb5\x5d\x93\
-\x4b\x93\x93\x5f\xc4\xec\x79\xb7\x91\x9b\xdf\x1f\x27\xb0\xa9\x0a\
-\x92\x62\x04\xc5\xf9\x2a\xe9\xc6\xe7\xd8\xec\x55\x38\x9c\x2e\xc3\
-\x73\x39\x58\x2d\x5d\x67\xb0\x4d\x8c\xf6\xa5\xb3\x3b\x95\xed\xea\
-\x6c\xbc\x8a\x65\x3b\x9c\x14\x80\xa6\x6c\xf6\xa4\xd4\xe2\x04\x02\
-\xf7\x8e\xb9\x08\xb7\x5e\x7b\x25\xaf\xbe\xbb\x12\xa7\x2b\x72\x1d\
-\x65\x22\x81\xb8\x84\x04\xae\x9a\x7d\x23\x63\x4a\x26\x5f\xe0\xcf\
-\xd0\xee\xf4\x65\x32\x1a\x92\x21\x19\x99\x25\xd1\x7b\xa9\xd7\x57\
-\xa9\x3f\xae\x4b\x4a\xae\x1e\x31\xae\xd0\x4a\x56\xa6\xa4\xae\x43\
-\xd0\x76\xb2\x52\x97\x10\x90\x11\x0f\x03\xd3\x25\x79\x49\x97\xfc\
-\x7b\x3b\x0e\xe5\xb2\x1f\x4e\x0a\xc0\x4c\x21\x3c\xe5\x35\xde\xdd\
-\x80\x7f\x59\x1e\xfc\x24\x3d\x2d\x99\x3b\x6e\x98\xce\xa2\xa5\x9f\
-\xea\xd9\xac\xc9\x49\x84\x50\x18\x37\x71\x0a\x53\x67\xcd\xeb\xb2\
-\x4c\xbb\x94\xb0\xff\x84\xa0\xc9\x2e\x98\x58\xa0\x12\x1b\x19\xf5\
-\x53\xc2\x9a\xba\x30\x10\x80\x82\xb4\x68\x06\x67\x49\x2e\xcb\xf2\
-\x55\xea\xb2\xbb\x20\x3e\x0a\xfc\x38\xe1\xdd\x79\x87\x10\x5e\x80\
-\x33\xbb\x43\x82\x6d\xc1\x30\xf2\x9e\x5b\x66\xfb\x9d\x1e\xda\xc4\
-\x7f\xf2\x0b\x07\x72\xcf\x37\x1e\xe1\xea\x79\xb7\x77\x39\xf8\xcf\
-\xa6\xb1\x13\x56\x1e\x54\x38\xd1\x69\x4e\x03\x7a\x4a\x75\xdd\x09\
-\xa3\x4d\x20\x2d\xe5\x4c\x95\xa5\x28\xc5\x97\xd9\xda\x1f\xef\x64\
-\xc1\x99\xb1\x7e\x5a\x00\x84\x24\x28\xa9\x6a\xe2\xe3\x63\x79\xe0\
-\x8e\xb9\xc1\x68\xba\x4f\x12\x9f\x98\xc4\x75\xf3\xbf\xc2\x9d\x0f\
-\x7e\x9f\xac\x5c\x6d\x69\xd8\x5a\x9a\x1b\xa9\x28\xdb\xc4\xba\xa3\
-\x67\xd2\xa1\x9b\x04\x86\xd1\x19\x9c\x15\x45\x21\x35\x39\xb0\x64\
-\x20\x52\x72\x3a\x79\xe7\xe9\xc9\xa0\x2a\x94\x0d\x42\x06\x27\xbd\
-\xd1\x4d\x73\xae\xe0\xed\x4f\x3e\xef\xd5\x19\x62\x82\x8d\xa2\x28\
-\x14\x5f\x7e\x15\x57\xce\xbc\x4e\x73\x44\xa3\xc7\xed\x66\xc3\xda\
-\x4f\xd9\xb4\x76\x39\xc3\xc7\x94\x30\x72\xdc\x44\xb6\x1e\x13\xe4\
-\x5c\x7a\x8d\x68\xd2\x0d\x87\x0d\xf6\x71\x49\x4d\x4e\xf0\xfb\x78\
-\xf7\x7c\x84\x50\xbe\x38\xf5\xff\xa7\x05\x20\xb9\x93\x8d\xed\xf1\
-\xfa\x6f\x04\x82\x2f\x7d\xd2\x4f\xbf\x7b\x17\x0f\xfd\xe8\x09\xd3\
-\xa5\x34\x00\x0a\x07\x0c\x61\xf6\xf5\xb7\xd2\x2f\x80\x54\xd4\xfb\
-\xf7\x6c\x67\xd5\x87\x4b\x2f\xc8\x4a\xe4\xf0\xf8\xf2\x16\x26\x18\
-\x1b\xd2\x1e\x91\xa8\xaa\xca\xfe\xa3\xc6\x86\x7e\x9f\x3d\xfd\xd7\
-\x88\xb3\xc5\xc5\xe9\xac\x3d\xa7\x05\x60\xe8\x50\xe1\x2c\xaf\xf1\
-\x54\x80\xf0\x2f\x91\x9e\x46\x86\x0e\xc8\xe7\xfe\xdb\xaf\xe1\x5f\
-\xff\xfe\x30\x18\xcd\xf7\x4a\x12\x12\x93\x99\x3e\xe7\x26\x46\x8e\
-\x2b\x45\xeb\xab\xba\xb9\xe9\x04\x2b\x3f\x58\xc2\xa1\xfd\x97\x8e\
-\xd7\x6f\x77\x0a\x12\xa2\xcd\x73\x7f\xad\xec\x3f\x5c\x63\xf8\x11\
-\x60\x46\x80\x55\x96\x05\x72\xd3\xcc\x81\xe2\x74\xea\xdb\x73\xf7\
-\x83\x85\x58\x87\x24\x28\x02\x00\x70\xf7\x2d\x57\xb3\x7e\xf3\x6e\
-\x76\xee\x3f\x12\xac\x2e\x7a\x05\x8a\xc5\x42\xc9\xe4\xe9\x5c\x39\
-\xe3\x5a\xcd\x79\x0b\xdc\x2e\x17\x5f\x7c\xf6\x31\x9b\xd7\xaf\xec\
-\x36\x5c\xb4\xd9\x0e\x39\x91\x53\xad\x3b\x6c\xd8\xbe\xf7\x88\xd1\
-\x26\x90\x93\x19\x58\x3d\x4e\x89\x58\x77\xf6\xbf\xad\xe7\xfe\x50\
-\xae\x13\x88\x47\x7a\x60\x57\x97\x58\x2c\x0a\xbf\x7e\xf4\xab\x7c\
-\xfd\xd1\x27\xcc\x98\xf2\x4b\x50\x38\x70\x08\x57\xcf\xbb\x9d\x8c\
-\xcc\x1c\xcd\xf7\x1e\xdc\xbb\x83\x15\x1f\x2c\xf1\x3b\x09\xe9\xe1\
-\x26\xc1\x80\x93\xd5\x6e\x4c\xfc\xa7\x6c\xbb\xf1\xb5\x0c\x8b\xf2\
-\x03\x2b\x0d\x2e\x91\x97\x16\x00\xab\x62\x59\xa7\xb7\x47\xe0\xf9\
-\x64\xa4\x26\xf3\xdb\x1f\x3d\xc8\xb7\x7f\xf6\xd7\x3e\x95\x40\xb2\
-\x3b\x12\x93\x53\x98\x36\xfb\x46\x46\x8e\x9f\xa8\xf9\xde\xe6\xc6\
-\xe3\xac\xf8\x70\x09\x87\xf7\xef\xd6\x74\x9f\xcb\x0b\x1b\xaa\x14\
-\xae\x1a\xa0\x62\x70\x6e\x8b\x88\xc1\xed\xf1\xb2\x79\xc7\x7e\xa3\
-\xcd\xa0\x28\x3f\xa0\xe0\x5d\x29\x14\xcb\x17\x67\x7f\x70\x8e\x00\
-\x8c\xcb\x11\x0d\xe5\x35\xde\x9d\x80\x7f\x79\xa0\x03\x64\xd8\xc0\
-\x02\xfe\xe7\x3b\x5f\xe1\xe7\x7f\x7c\xc1\xb8\xc2\x0a\x61\x82\xc5\
-\x62\xa5\xf4\xca\x99\x4c\xbe\xea\x1a\xa2\xa2\xb5\xed\xc8\xb9\x5d\
-\x4e\xd6\xaf\xfa\x88\xb2\x2f\x56\x05\x9c\xa1\xa8\xc5\x0e\xdb\x8e\
-\x09\x8a\xf3\xcd\xbd\x00\x7f\xd8\xba\xeb\xa0\xe1\xeb\x7f\x80\xa2\
-\xbc\x80\x04\x60\x5b\x49\xae\x38\x27\x7b\xeb\x05\x3e\x61\x12\x3e\
-\x14\x41\x16\x00\x80\x19\x93\xc7\xf2\xd8\x0f\xee\xe1\x97\x7f\x7e\
-\xc9\xf0\xcc\x2a\x46\x51\x34\x68\x18\x57\x5f\x7f\x1b\xe9\x99\xda\
-\xa7\x73\x07\xf7\xee\x60\xf9\xfb\x6f\xe8\x52\x25\xb6\xb2\x45\x30\
-\x2c\xd3\x14\x00\x7f\x58\xbe\xbe\xc2\x68\x13\x88\xb2\x5a\xc8\xf5\
-\x33\x6b\xf3\x79\x5c\xb0\x03\x7f\x81\x00\x08\xa9\x2e\x43\x28\xff\
-\x19\x48\xeb\x5a\x99\x35\x65\x3c\x42\x11\xfc\xe2\x89\x45\x7d\x4a\
-\x04\x52\xd2\x32\x98\x79\xed\x7c\x86\x8c\x18\xa3\xf9\xde\x13\x0d\
-\xc7\x58\xfe\xfe\x1b\x54\x1d\x39\xa0\x9b\x3d\x12\x68\x73\x9a\x0e\
-\x01\xdd\xe1\xf6\x78\x59\xfd\x45\x50\x1c\x66\x35\x51\x98\x97\x1d\
-\x58\x3e\x42\x45\x59\x76\xfe\x47\x17\x08\x40\x4c\xab\x75\x8d\x23\
-\x55\x6d\x17\x10\x92\xfd\xe1\x99\x93\xc7\x11\xff\xa3\x18\xfe\xdf\
-\x13\x2f\xd2\x61\xeb\x5d\xb9\xf7\xce\xc7\x1a\x15\xc5\xe5\x53\x67\
-\x73\xf9\x95\x57\x6b\x4e\xe6\xe0\x72\xd8\x59\xb7\x6a\x19\x15\x1b\
-\x3e\x0b\xca\xb2\x29\xc6\x62\xce\x00\xba\xe3\xf3\xb2\x5d\xb4\x75\
-\x18\x5f\xe7\x70\xd4\xb0\xae\x0b\xdd\x5c\x82\x36\xb5\x86\xf5\xe7\
-\x7f\x78\x81\x00\x8c\x1a\x25\x5c\xe5\x35\xea\x4a\x90\x37\x05\xd2\
-\x4b\x20\x4c\x2a\x1e\xc1\x33\xbf\xfb\x21\x3f\x7e\x7c\xa1\xe1\x1e\
-\x56\xc1\x62\xe8\x88\x31\xcc\xb8\x76\x3e\x29\x5a\x6b\x39\x49\xc9\
-\xae\xad\x9b\x59\xfd\xe9\x3b\x74\xb6\x07\xaf\xee\x62\x4f\x6b\x22\
-\xf6\x05\xde\xfa\x78\x5d\xf7\x17\x85\x80\x51\x43\x07\x68\xbf\x49\
-\x8a\x4f\x4b\x4b\xc5\x05\x61\xb9\x17\x8f\x0b\x93\x72\x19\x82\x90\
-\x09\x00\x40\x41\x4e\x3f\x9e\xfe\xcd\x0f\x78\xe2\x99\x25\x7c\xb8\
-\x6a\x53\x28\xbb\x0e\x2a\x85\x03\x86\x30\xf5\xea\x1b\xfc\xaa\x51\
-\x78\x3e\x0d\x75\x35\x2c\x7f\xff\x0d\x6a\x2a\x0f\x05\xc1\xb2\x73\
-\xd9\x7e\x4c\xd0\x68\x33\x45\xe0\x52\x54\xd6\x36\xb0\x69\x9b\xf1\
-\xc7\x7f\x00\xa3\x86\xf5\xd7\x7c\x8f\x50\xd4\x0b\xa6\xff\x70\x09\
-\x01\x50\xa4\xf2\x8e\x2a\xd4\xbf\x71\x76\xb4\x60\x08\x88\x8f\x8d\
-\xe1\x7f\xbe\x73\x17\x73\xa6\x4e\xe0\xf1\xa7\x16\x53\x7f\x22\x32\
-\x8b\x6a\x22\x04\x03\x06\x0d\x67\xe2\x95\xb3\xe8\x3f\x78\xb8\xe6\
-\xdb\x9d\x0e\x1b\x6b\x57\x7c\xc0\x96\x8d\xeb\x90\x41\x8a\xcf\x38\
-\x1f\xc9\xb9\x55\x8d\x4d\xce\xe5\xf9\x37\x3e\x0e\x8b\x6c\x49\x09\
-\xf1\x71\xf4\xd7\xee\x03\xa0\x7a\x5d\x96\xf7\x2e\xf6\x83\x8b\x0a\
-\xc0\xf8\x42\x51\x53\x51\xe3\x59\x2f\x11\x53\xb5\xf6\xa4\x07\x97\
-\x8f\x1f\xce\xcf\x7f\xfa\x28\x8b\x96\xae\xa0\xfc\x8b\x55\xb8\x7a\
-\x58\x04\x31\x54\x44\xc7\xc6\x31\x7a\xdc\xe5\x8c\xbf\x7c\x2a\xe9\
-\xfd\xb4\x1f\xd3\x48\x29\xd9\xb9\x65\x03\xab\x3f\x7e\x17\xbb\xcd\
-\xcc\xc6\x1b\x2e\x1c\xa9\xaa\xe3\xd3\xb5\xc6\xef\xfe\x03\x8c\x1e\
-\xde\x1f\xe5\xfc\xf4\x3e\xdd\x20\x85\x58\x5d\x3a\x40\x5c\x34\x7c\
-\xf1\x92\xa9\x21\x24\x62\x31\x60\x88\x00\x78\x55\xa8\xe9\x88\x65\
-\xea\xec\xeb\x99\x70\xc5\x34\x36\x7c\xf6\x09\x5b\x37\xaf\xc7\xed\
-\x0e\xbf\x57\x94\xd5\x1a\xc5\x80\x21\x23\x18\x31\xaa\x98\x41\x23\
-\x46\x13\x1d\x60\xc9\xb1\xfa\xda\x2a\x3e\x7d\xff\x75\x8e\x55\x1f\
-\xd5\xd9\x42\x93\x9e\xb2\xf0\xb5\x65\x61\xe3\xaf\x32\x69\xfc\x65\
-\x9a\xef\x11\xaa\xbc\x64\x76\xde\x4b\x0a\x80\x27\x4a\x79\xdd\xea\
-\x56\x9f\x00\x42\x5e\x00\xed\x78\xa7\xc0\x75\xf2\x54\x30\x2e\x3e\
-\x91\x19\xd7\xce\xe7\x8a\x19\xd7\xb2\x73\xcb\x26\xb6\x96\xad\xa3\
-\xb1\xc1\xd8\x8d\xc2\xa8\xe8\x18\xfa\x0f\x1a\xc6\xf0\x51\xe3\x19\
-\x3c\x7c\x34\xd1\x31\xb1\x01\xb7\xe5\xb0\x75\xb2\x66\xf9\xfb\x6c\
-\x2b\x5b\x1f\x16\x53\x4c\x93\x73\xd9\x77\xb8\x9a\x55\x1b\x8c\x3f\
-\xfa\x3b\xc5\x94\x09\x23\xb5\xde\xe2\xb5\x58\x95\xa5\x97\xfa\xe1\
-\x25\x05\xe0\xf2\x2c\x51\x57\x5e\xeb\x59\x83\x14\x33\xb4\xf6\xd8\
-\x53\x5a\x1d\x17\x7e\x16\x13\x1b\x47\xc9\xe4\x69\x94\x4c\x9e\x46\
-\x5d\x6d\x25\x07\x76\xef\xe0\xc0\xde\xed\x9c\xa8\x0f\x7e\x8e\x81\
-\xa8\xe8\x68\xf2\x0b\x07\x51\x34\x70\x28\x85\x03\x87\x92\x9d\x57\
-\x18\x70\x2c\xf6\x29\xa4\x94\x6c\x2b\x5b\xcf\x9a\xe5\xef\xe3\xb0\
-\x19\x9b\x5d\xd6\xe4\xe2\xa8\xaa\xe4\x2f\xcf\xbd\x15\x36\xc2\x5c\
-\x94\x97\x45\x41\x4e\x3f\x8d\x77\x89\x15\xe3\x72\xc4\x25\xab\xf5\
-\x76\x9d\x1d\x4e\x15\x8b\x11\xcc\xd0\xd8\x63\x8f\x89\xea\x66\xce\
-\x91\x93\x57\x44\x4e\x5e\x11\x53\x67\x5f\x4f\x7b\x5b\x0b\xb5\x55\
-\x47\x38\x56\x75\x98\xba\x9a\x2a\x1a\x4f\xd4\x07\xbc\x7e\x16\x42\
-\x21\x35\x2d\x9d\x8c\xac\x5c\xd2\x33\xb3\xc9\xcc\xca\x25\x3d\x33\
-\x87\x7e\x59\xb9\x58\x74\x2c\x05\x7d\xac\xfa\x28\x9f\xbe\xff\x3a\
-\xf5\xb5\x55\xba\xb5\x69\xa2\x3f\x6f\x7c\xb8\x86\x2d\xbb\xc2\xa7\
-\x1e\x62\x00\x6f\x7f\x40\x7d\xad\xab\x9f\x76\x29\x00\x6e\x94\xc5\
-\x51\xa8\x7f\x04\x02\x9f\xe3\x06\x40\x6e\x92\x64\x57\xbd\xc0\xe3\
-\xc7\xb2\x2b\x29\x39\x95\xe1\xa3\xc6\x33\x7c\xd4\x99\x7c\xa6\x4e\
-\x87\x9d\x96\xa6\x13\x74\x76\xb6\xe3\xe8\xec\xc4\x66\xef\x44\x3d\
-\x19\x1a\xeb\xb0\xdb\x89\x8e\x8e\xc1\x6a\xb5\x12\x1d\x1b\x4b\x4c\
-\x4c\x1c\x09\xc9\xc9\x24\x24\x24\x91\x94\x92\x86\x55\x63\xb1\x05\
-\x2d\xd8\x6d\x1d\xac\xfe\xf8\x5d\x76\x6e\xd9\x10\x36\x6f\x15\x93\
-\x8b\x53\x75\xec\x38\xff\x7c\xe5\x7d\xa3\xcd\x38\x87\xa9\x13\x47\
-\x6b\xbd\xc5\x6e\xf5\x58\x2e\x39\xfd\x87\x6e\x04\x60\x52\x81\x68\
-\x2c\xaf\xf1\xbe\x05\x7c\x59\x6b\xcf\x3d\x21\x2e\x0a\x2e\x2f\x94\
-\x7c\x71\x54\x10\xc8\xd6\x4b\x4c\x6c\x1c\xd9\x79\x85\xba\xdb\xd5\
-\x13\x6c\x1d\xed\x2c\xfc\xeb\xaf\x71\x3a\x8c\xf7\x24\x33\xe9\x1a\
-\x55\x95\xfc\xe6\x1f\xaf\xe2\x70\x86\xcf\xa6\x73\x5e\x76\x06\xe3\
-\x2e\xf3\xaf\xbc\xdb\x69\x04\xaf\x8f\xed\x2f\xba\x0c\x16\xe9\x76\
-\x21\x2b\xa5\xfa\x2f\x6d\xbd\xea\x43\x56\xa2\x64\x68\x2f\x0a\x50\
-\x71\xb9\x5d\xe6\xe0\x8f\x10\x9e\x79\xed\x03\xb6\xed\x0e\xbe\xf3\
-\x95\x16\xe6\x4e\x2b\xbd\xa0\xe6\x43\x77\x48\x94\x67\xba\xbb\xa6\
-\x5b\x01\x28\xc9\xb7\xae\x04\x0c\x09\x80\x8e\x35\x13\x55\x98\x84\
-\x98\x65\xab\x37\xf3\xe2\x92\xf0\xab\x63\x31\xe7\xaa\x12\xad\xb7\
-\xec\x2b\xc9\x65\x6d\x77\x17\x75\x2b\x00\x42\x08\x89\xe0\x59\xad\
-\xbd\xf7\x94\x36\x27\xec\x69\x30\x5d\x53\x4d\x42\xc7\xd6\xdd\x07\
-\xf9\xdd\x93\x5d\xee\x99\x19\xc2\xd8\xcb\x06\x69\x8e\xff\x17\xf0\
-\x2f\xe1\x47\x61\x48\xbf\xce\xb2\xa2\x2c\xca\x73\x08\x42\x56\xdf\
-\xab\xc5\x0e\x6b\x0f\x2b\x38\xbb\x4e\x69\x67\x62\xa2\x1b\x47\xab\
-\xeb\xf9\xc9\xef\x9f\xc3\xe5\x09\xbf\x87\xee\xd6\x6b\xaf\xd2\x7a\
-\x8b\x4b\xb1\x28\x2f\xfa\x73\xa1\x5f\x02\x30\x26\x5b\xd4\x0b\x95\
-\x90\x48\x63\xbb\x13\xd6\x1d\x51\x4e\x3b\x02\x99\xf4\x3d\xf6\x1c\
-\x17\x84\xf2\x90\x64\xdf\xe1\x6a\xbe\xfd\xf3\xbf\xd1\xda\x16\x7e\
-\xfe\x18\x79\xd9\x19\xcc\x98\x3c\x56\xd3\x3d\x12\x5e\xe9\xea\xec\
-\xff\x6c\xfc\xf6\x66\x51\xa5\xf2\x7b\x5f\xdb\xc1\xa5\xaa\x55\xe0\
-\x0e\x0f\xaf\x4b\x13\x83\xa8\x6c\x11\x6c\xa8\x12\x21\x79\x09\xec\
-\x3e\x50\xc9\x0f\x7e\xf9\x14\x2d\x6d\xe1\x19\x7b\x71\xc7\xbc\xe9\
-\x9a\x93\x7f\x08\xa9\xfc\xc9\xdf\x6b\xfd\x6e\x79\x42\xa1\xd8\x06\
-\x62\xb9\x26\x4b\x02\xa0\x3b\x27\x20\x93\xbe\x41\x5d\xbb\x60\xe5\
-\x01\x85\x23\xcd\x81\x1d\x05\xfb\xc3\x86\x8a\x3d\x7c\xff\xb1\x7f\
-\xd0\xd6\x1e\x7e\x6f\x7e\x80\xe4\xc4\x78\xae\x9f\x75\xb9\xc6\xbb\
-\xc4\xb2\x92\x02\xb1\xd5\xdf\xab\x35\xd5\x89\x95\xc2\xfb\x07\x21\
-\x95\xab\x35\x5a\xa4\x89\xec\x04\xc9\x4e\xcc\xcd\x3f\x13\xb0\x7b\
-\x60\x4b\xad\x60\x47\x9d\x20\x21\x06\xa2\x15\x49\x94\x05\xfa\xa7\
-\x41\x76\x62\xe0\x93\x51\x29\x25\xaf\xbc\xbd\x82\xa7\x5f\x79\x1f\
-\x55\x0d\xdf\xa3\xe6\x2f\xdf\x38\x93\xf8\x58\x8d\xc1\x65\xd2\xfb\
-\x7f\x5a\x2e\xd7\x24\x00\x13\xf2\xa2\x96\x95\xd7\x78\xb7\x02\xe3\
-\x34\x19\xa5\x81\xe4\x58\x48\x8a\xf1\xed\x05\x98\x98\x00\x78\x54\
-\x68\xb5\x03\x27\x5f\x0c\x75\xed\x30\x63\xb0\x24\x39\x80\xc0\xcb\
-\xd6\xb6\x4e\x7e\xf1\x97\x45\x6c\xdc\xb2\x57\x57\x1b\xf5\xa6\x5f\
-\x5a\x0a\xb7\xcf\x9b\xa6\xf5\xb6\x6d\xc5\xf9\xd6\x15\x5a\x6e\xd0\
-\x1c\xd1\x22\xa4\x7c\x42\xeb\x3d\x5a\xc9\x4f\x09\x5f\x55\x36\x31\
-\x1e\x55\xc2\xfe\xe3\xda\x67\x89\x9f\xae\xab\xe0\x9e\x47\x7e\x17\
-\xf6\x83\x1f\xe0\x81\x2f\x5d\x4b\x5c\xac\xb6\x34\xf1\x42\xc8\xc7\
-\xfd\x39\xfa\x3b\x1b\xcd\x02\xb0\x3f\xdf\xf2\x92\x84\xa0\x7e\x83\
-\xfd\xd3\xa4\x5f\x75\xce\x4d\xfa\x2e\xd5\x6d\xc2\xef\x63\xe2\xda\
-\xfa\x46\x1e\xf9\xdf\xa7\x79\xec\x89\x17\x69\x6a\x0e\x5e\x5e\x45\
-\xbd\x28\xca\xcb\x62\xde\x4c\xad\x6b\x7f\xf6\xed\xcf\xb5\x68\x3e\
-\xa9\xd3\x2c\x00\x77\x08\xe1\x55\x84\xfc\x95\xd6\xfb\xb4\x10\x67\
-\x85\xab\x06\xa8\x24\x87\x34\x04\xc9\x24\x92\x90\x12\xea\x3b\xba\
-\x9e\x05\x34\xb7\xb6\xf3\xf7\x17\xdf\xe1\x9e\x87\x1f\x67\x43\xc5\
-\x9e\x10\x59\xd6\x33\x84\x10\xfc\xe7\x43\xb7\x05\xb0\xf3\x2f\x7f\
-\x7a\x87\x10\x9a\xcf\x4d\x34\xed\x01\x9c\x62\x7c\xae\xe5\xdf\x15\
-\xb5\xea\x7f\x03\xda\x0e\x28\x35\x90\x12\x0b\x33\x06\xab\xec\xae\
-\x17\xec\x3f\x61\x6e\x0a\x9a\x5c\x48\x43\x07\x14\xa5\x5e\xf8\x79\
-\x73\x6b\x3b\xaf\xbe\xbb\x9a\x25\x1f\xae\x09\xab\x80\x1e\x7f\x98\
-\x37\xeb\x72\x4a\x46\x0f\xd5\x74\x8f\x94\xec\x28\xce\xb7\x2c\x09\
-\xa4\xbf\x80\x04\x40\x08\xa1\x96\x57\x7b\x7e\x85\x10\xaf\x07\x72\
-\xbf\xbf\x28\xc0\xa8\x6c\x89\xc3\xed\xf3\x0f\x30\x31\x39\x9b\x56\
-\x87\xe0\x94\x6b\x8a\xaa\x4a\xca\x77\xec\xe7\xc3\xd5\x9b\x58\xf5\
-\xf9\x56\x9c\xae\x90\x39\xae\xea\x46\x7a\x5a\x32\xdf\xbe\x57\x7b\
-\x32\x6e\x45\xc8\x9f\x0a\x21\x02\x3a\x2d\x0d\x48\x00\x00\x8a\xf3\
-\x2d\x4b\x2a\x6a\xd5\x2d\xc0\xf8\x6e\x2f\xee\x21\x63\xf3\x24\x8d\
-\x36\x81\x2d\xf2\xfe\xa6\x26\x41\xa4\xcd\xe6\x66\xe3\xd6\x03\x6c\
-\xa8\xd8\xcd\xca\xcf\xb7\x46\x7c\xc5\xe9\x47\x1e\xbc\x95\xa4\x84\
-\x78\xad\xb7\x95\x8d\xcf\xb3\xbc\x13\x68\x9f\x01\x0b\x80\x10\x42\
-\x96\x57\xbb\xff\x13\xa1\x04\x3d\x74\x2a\x4a\x81\xf1\x79\x92\xf5\
-\x47\xcd\x59\x40\x5f\x45\x4a\x95\xb6\x96\x66\xea\x8f\x55\xd3\x70\
-\xac\x86\xfa\x63\x95\x54\x1f\x39\x18\x96\x89\x62\x03\xe1\xe6\x39\
-\x53\x98\xae\xd1\xe5\x17\x40\xa0\xfe\x58\x88\xc0\xcb\x3a\x05\x2c\
-\x00\x00\x25\x05\x51\xcb\xcb\x6b\xd4\x77\x41\xde\xd8\x93\x76\xfc\
-\x21\x2b\x51\x92\x9f\x02\x35\xe6\x52\xa0\xd7\xf3\xe1\xd2\x97\x4e\
-\x97\x4e\xb3\x77\x76\xd0\xd9\xd1\x8e\xad\xb3\xbd\xd7\x66\x51\x1a\
-\xd2\x3f\x9f\xef\x3f\x70\x8b\xf6\x1b\xa5\x58\x5a\x5c\x10\xf5\x49\
-\x4f\xfa\xee\x91\x00\x00\xa8\x5e\xf1\xb0\x62\x91\xd7\x00\x81\xe5\
-\xc3\xd6\xc0\x98\x1c\x49\x7d\x87\xc0\x63\x06\x0a\xf5\x6a\x42\x51\
-\x09\x29\x5c\x88\x8f\x8d\xe1\x97\x3f\xbc\x87\x68\x8d\xb5\x22\x01\
-\x97\x90\xe2\x47\x3d\xed\xbf\xc7\x95\x7f\x4a\x8b\xc4\x41\x09\x7f\
-\xed\x69\x3b\xfe\x10\x6b\x85\x11\xbd\x28\x4b\x90\x49\xdf\x46\x51\
-\x14\x7e\xf6\xfd\xbb\x29\xd2\x5e\xe9\x07\x24\x7f\x28\x2e\x14\x3d\
-\x4e\xd4\xa3\x4b\xe9\x2f\x7b\xb4\xf2\x4b\x20\x24\xc9\xfa\x07\x65\
-\x48\x92\x82\x3e\xd7\x30\x31\x09\x3e\xdf\xbd\xff\x66\xae\xd2\x9e\
-\xe8\x13\xa0\xde\x15\xa7\xfc\x56\x0f\x1b\x74\x11\x80\xa9\x99\xa2\
-\x5d\x22\x7f\xa2\x47\x5b\xdd\xa1\x00\xa3\xb3\xcd\x59\x80\x49\x64\
-\x73\xf3\x9c\x29\xdc\x7e\xbd\x66\x5f\x7f\x1f\x42\xfe\xd7\xe4\x0c\
-\xa1\x8b\x4b\xa3\x6e\xc5\x3f\x4b\xf2\x2c\xcf\x83\x08\x49\x32\xb5\
-\xec\x24\x49\xbf\x78\x53\x04\x4c\x22\x93\xab\xaf\x2c\xe6\x91\xaf\
-\xdf\x1a\xd0\xbd\x52\x88\x95\xc5\xb9\x96\x97\xf4\xb2\x45\x37\x01\
-\x10\x42\x48\x8b\x45\x3c\x04\x84\x24\xb8\x7a\x54\x8e\x34\x83\x86\
-\x4d\x22\x8e\x19\x93\xc7\xf2\xb3\xef\xdd\x1d\x68\x65\x29\x9b\xf4\
-\x88\xaf\x6b\x0d\xf8\xe9\x0a\x5d\xcb\x7f\x8f\xcb\x11\x87\x81\xa0\
-\xc6\x09\x9c\x22\x2d\x0e\x72\x92\xcd\x59\x80\x49\xe4\x30\x7d\xf2\
-\x58\x1e\x7b\xf8\x5e\xcd\x7e\xfe\xa7\x91\xfc\xbc\xb4\x48\xe8\x5a\
-\xaa\x48\x57\x01\x00\x68\xcd\x53\xfe\x00\x94\xe9\xdd\xee\xc5\x18\
-\x99\x25\xd1\x98\x2a\xdd\xc4\xc4\x10\xe6\x4e\x2f\xe5\x17\x0f\xdf\
-\x8b\x35\xf0\x12\x73\x5b\x5a\xf3\x95\x3f\xeb\x69\x13\x04\x41\x00\
-\x66\x0a\xe1\x51\x85\xf2\x50\x28\xb2\x08\x27\xc5\xf8\x42\x87\x4d\
-\x4c\xc2\x99\xdb\xaf\x9f\xc6\x4f\xbf\x73\x57\x4f\x06\xbf\x0b\xa9\
-\xdc\x3f\x53\x08\xdd\x53\x16\xeb\x2e\x00\x00\xa5\x79\xa2\x5c\xc0\
-\x63\xc1\x68\xfb\x7c\x46\x66\x49\xa2\xcd\xdc\x01\x26\x61\x88\xc5\
-\xa2\xf0\x5f\x0f\xdd\xce\xf7\x1f\x98\xaf\xb9\xaa\xcf\xd9\x48\xf8\
-\x99\x96\x3c\x7f\x5a\x08\x8a\x00\x00\x8c\xcf\x55\x7e\x2b\x85\x58\
-\x19\xac\xf6\x4f\x11\x6d\xf1\x89\x80\x89\x49\x38\x91\x9a\x9c\xc8\
-\x1f\xfe\xe7\x1b\xdc\x7c\xcd\x94\x1e\xb6\x24\x3f\x3b\xe8\x5b\x56\
-\x07\x85\xa0\x09\x80\x10\x42\xb5\xaa\xe2\x5e\xa0\x29\x58\x7d\x9c\
-\xa2\x7f\xba\x24\xd7\xdc\x10\x34\x09\x13\x2e\x1b\x52\xc4\x33\x8f\
-\x3f\x4c\xe9\xd8\x61\x3d\x6d\xaa\x45\x15\x96\x7b\x02\x49\xf4\xe1\
-\x2f\x41\x13\x00\x80\x71\x05\xa2\x1a\x29\xbf\x11\xcc\x3e\xc0\x97\
-\x2a\xb2\x38\x4f\x92\xa0\x2d\x85\x9a\x89\x89\xae\x28\x8a\xe0\xae\
-\x9b\x67\xf1\xe4\xff\x7e\x8f\x9c\xcc\xf4\x1e\xb7\x27\x91\xff\x51\
-\x9a\x27\x2a\x75\x30\xed\x92\x04\x55\x00\x00\x4a\x0a\xac\x6f\x20\
-\x78\x21\xd8\xfd\x44\x5b\xa0\xb4\x40\x0d\xfe\x2f\x14\xe1\xc4\xc6\
-\xc6\x19\x6d\x42\xaf\x24\x2f\x3b\x83\xbf\xfd\xe2\x3b\x7c\xeb\x9e\
-\x1b\x7b\xb2\xd9\x77\x1a\x09\xff\x9a\x90\x6f\x0d\x7a\x35\xae\x1e\
-\x47\x03\xfa\x43\xab\x53\xf9\x8f\x94\x68\x75\x0c\xa0\xb9\xc4\xa9\
-\x16\xd2\xe2\x60\x68\xa6\x64\x6f\x00\x19\x63\x7b\x3b\x71\x09\x09\
-\x4c\x9b\x7d\x23\xa3\x4b\x26\x1b\x6d\x4a\xaf\x42\x51\x04\x37\xcf\
-\x99\xc2\x37\xef\xb9\x51\x7b\x0e\xff\x4b\xb3\xc5\xa9\x2a\xdf\xd7\
-\xab\xb1\xae\x08\x89\x00\xcc\x1c\x28\x1c\xe5\xb5\x72\x01\x52\x2d\
-\x03\x32\x82\xd9\xd7\xd0\x0c\xc9\xc1\x46\x81\xc7\x2c\x2f\x06\xf8\
-\x92\x4c\x5e\x36\xb6\x94\x99\x73\xe7\x13\x97\x90\x60\xb4\x39\xbd\
-\x8a\xa1\x03\xf2\x79\xe4\xa1\xdb\x18\x3d\x6c\x80\x9e\xcd\x36\x59\
-\x2c\xca\x82\x29\xf9\xc2\xae\x67\xa3\x97\x22\x24\x02\x00\x50\x92\
-\x27\x8e\x56\xd4\xb8\xef\x94\x28\x1f\x02\x41\x3b\xb8\xb3\x5a\x20\
-\x3d\x5e\xd2\xd0\x4d\xc6\xd8\xbe\x40\x5e\xe1\x00\x66\xcf\xbb\x9d\
-\xec\xdc\x02\xa3\x4d\xe9\x55\x24\x27\x25\xf0\xb5\x2f\x5f\xc7\x2d\
-\x73\xae\x08\xd4\xa5\xf7\x52\xa8\x52\xa8\x5f\x19\x97\x63\x39\xac\
-\x67\xa3\x5d\x11\x32\x01\x00\x28\xce\x8f\xfa\xa4\xa2\xc6\xfb\x2b\
-\x19\x64\x1f\x81\x7e\x09\xbe\x8c\xb1\x7d\x95\xd8\xf8\x04\xae\x98\
-\x3e\x97\x92\x49\xd3\x7a\x74\xfe\x6c\x72\x2e\x71\xb1\xd1\x2c\xb8\
-\xf6\x2a\xee\x59\x30\x9b\xc4\x78\xfd\xf7\x52\x84\xe0\xa7\x25\x79\
-\x51\xcb\x74\x6f\xb8\x0b\x42\x2a\x00\x00\xe3\xf3\x94\x5f\x55\xd4\
-\xca\x62\x90\x37\x07\xab\x8f\x82\x64\xdf\x3e\x80\xb7\x8f\x2d\x03\
-\x14\x45\x61\xfc\xc4\xab\xb8\x72\xd6\x75\xc4\x98\x9b\x7d\xba\x11\
-\x1b\x13\xcd\x8d\xb3\x27\x73\xcf\x82\xab\x49\x4f\x4d\x0a\x4e\x27\
-\x52\x2c\x19\x9f\x27\x74\x89\xf1\xd7\x42\xc8\x05\x40\x08\xa1\xae\
-\xaf\x92\x77\xc6\x2a\xea\x0a\x10\x41\xd9\x91\x8a\x8f\x86\xf1\xb9\
-\x92\xf2\xda\xd0\xd6\x99\x37\x92\x82\xa2\xc1\xcc\x9e\x77\x1b\x99\
-\x39\x79\x46\x9b\xd2\x6b\x88\x8f\x8f\x65\xde\x8c\xcb\xb9\x7b\xc1\
-\x6c\x32\x52\x93\x83\xd8\x93\xdc\x6c\xb1\x2a\xf7\xe9\x19\xe5\xe7\
-\x2f\x21\x17\x00\x80\x29\x85\xc2\x5e\x7e\x4c\xde\x84\x54\x3f\x47\
-\x32\x38\x18\x7d\x14\xa6\x4a\x14\x05\x2a\x6a\x7a\xf7\x86\x60\x42\
-\x62\x32\x33\xe6\xde\xcc\x65\x63\x26\x60\x46\x46\xe9\x43\x51\x7e\
-\x36\xf3\xe7\x4e\x61\xde\xcc\x49\xc4\xc7\x05\x3d\xfd\xd4\xe1\x28\
-\xab\xe5\x86\x31\xd9\xc2\x90\x1a\xe5\x86\x08\x00\x40\x49\xae\x38\
-\x5e\x56\x2b\x6f\x14\xa8\xeb\x80\xb4\x60\xf4\x91\x9f\x2c\x49\x8e\
-\x91\x6c\xac\x52\x7a\x5d\xb5\x61\xc5\x62\xa1\x64\xd2\x34\xa6\xcc\
-\xb8\x96\xe8\x18\xb3\x86\x5a\x4f\x89\xb2\x5a\x98\x5a\x3a\x9a\xf9\
-\xd7\x5e\x49\xf1\xa8\x21\xa1\xda\x3b\x69\x52\x85\x72\xfd\x98\x6c\
-\x51\x1f\x8a\xce\x2e\x86\x61\x02\x00\x30\x21\x4f\xec\x2e\x3f\x26\
-\xe7\xa3\xaa\x1f\x11\xa4\xac\xc2\x49\x31\xbe\x12\x63\xfb\x8e\x0b\
-\x0e\x9c\x10\x78\x7b\xc1\x92\xa0\x70\xc0\x10\x66\xcf\xbb\x8d\x7e\
-\x59\xb9\x46\x9b\x12\xf1\x0c\x1f\x5c\xc8\xdc\x69\xa5\xcc\x99\x5a\
-\x4c\x5a\x4a\x90\xd6\xf7\x17\x43\xe0\x46\x55\xef\x28\xcd\xb7\x18\
-\x5a\xb4\xd0\x50\x01\x00\x28\xc9\x15\xab\xcb\x6a\x3c\xf7\x09\xc4\
-\xcb\x04\xe9\x78\xd0\x22\xe0\xb2\x2c\x49\x61\xaa\x64\x47\x9d\xa0\
-\xae\x3d\x32\xa7\xca\x49\xc9\xa9\x4c\x9f\x7b\x0b\x23\x46\x17\x1b\
-\x6d\x4a\xc4\xa2\x28\x82\xcb\x06\xf7\x67\xfa\xa4\x31\x4c\x9b\x3c\
-\x96\x82\x9c\x7e\x46\x98\xe1\x45\x95\x77\x95\x14\x44\x2d\x37\xa2\
-\xf3\xb3\x31\x5c\x00\x00\x26\xe4\x5b\x5f\xab\xa8\xf6\xc4\x4a\x21\
-\x9e\x25\x88\xee\xc9\x89\xd1\x30\xb9\x48\xd2\x64\x97\xec\xae\x17\
-\x1c\xef\x8c\x0c\x21\xb0\x58\x2c\x4c\xb8\x62\x26\x93\xa7\x5f\x43\
-\x74\xb4\x99\x12\x59\x2b\x09\xf1\x71\x94\x8e\x1d\xca\xa4\x71\x23\
-\x98\x52\x3a\x92\x7e\x69\x29\x46\x9a\x23\x85\x90\xff\x51\x9c\x6f\
-\x7d\xc3\x48\x23\x4e\x11\x16\x02\x00\x50\x5c\x60\x7d\xa1\xdc\xec\
-\x1a\x97\x00\x00\x09\x20\x49\x44\x41\x54\xac\xc6\x9b\x28\xe0\x6f\
-\xc1\xee\x2b\x3d\x0e\xae\x1c\x20\xe9\x70\x49\x2a\x9b\x05\x95\x2d\
-\x02\x87\xee\xa9\x16\xf4\xa1\xff\xe0\xe1\x5c\x3d\xef\x36\xd2\x32\
-\xb2\x8c\x36\x25\x62\x48\x49\x4e\x60\xd4\xd0\xfe\x8c\x1e\x36\x80\
-\xf1\xa3\x06\x33\x6a\xe8\x80\xc0\xd3\x70\xe9\x8c\x94\x3c\x52\x92\
-\x6f\x7d\xc6\x68\x3b\x4e\x11\x36\x02\x00\x30\x21\xdf\xf2\xf7\xb2\
-\x6a\x6f\xb4\x10\xfc\x31\x14\xfd\x25\x46\xc3\xc8\x6c\xc9\x65\x59\
-\x92\xda\x36\xc1\x8e\x7a\x81\x3d\x4c\x0a\x90\x26\x26\xa7\x30\x6d\
-\xf6\x8d\x8c\x1c\x3f\xd1\x68\x53\xc2\x1a\x45\x51\x28\xca\xcf\x62\
-\xf8\xa0\x02\xc6\x8d\x18\xc4\x98\x11\x03\x19\x50\x90\x1d\x9e\x0e\
-\x50\x82\x1f\x4f\xc8\xb7\x3c\x61\xb4\x19\x67\x13\x56\x02\x00\x30\
-\xa1\xc0\xf2\x44\x79\x8d\x37\x15\xf8\x79\xa8\xfa\x14\x02\xf2\x53\
-\x24\xd9\x89\x92\x6d\x75\xbe\x19\x81\x51\x28\x16\x0b\xe3\x4b\xa7\
-\x32\xf5\xea\x79\xe6\x74\xff\x2c\xa2\xac\x16\x32\x33\x52\x19\x50\
-\x90\xcd\xc0\xc2\x5c\x06\x16\xe6\x30\xb0\x20\x9b\xc2\xfc\x2c\x3d\
-\x83\x70\x82\x86\x84\x5f\x4c\xc8\xb3\x84\xdc\xd1\xa7\x3b\xc2\x50\
-\x26\x7d\x94\xd7\x78\x1f\x05\x0c\xf9\xc2\x2a\x5b\x04\x5b\x6a\x05\
-\xaa\x8e\x27\x06\x2d\xcd\x8d\x3c\xf3\xa7\x5f\x76\x79\xcd\xa0\xa1\
-\x23\x99\x79\xfd\xad\xa4\xa5\x1b\xb2\x31\x85\xcb\xe5\xa4\xb9\xf1\
-\x38\x4d\x27\xea\x69\x6f\x6d\xa6\xa3\xbd\x95\xb6\x96\x66\x3a\x3b\
-\xda\xb0\x77\x76\x60\xb7\xdb\x70\x3a\xf4\x8d\x51\x11\x42\x10\x1d\
-\x13\x47\x5c\x7c\x3c\x89\x49\xc9\x14\x64\xa5\x50\x94\x9d\x4c\x56\
-\x46\x2a\x19\xa9\xc9\x64\xf5\x4b\x21\x33\x3d\x95\x9c\xcc\x74\x14\
-\x25\x6c\x1f\xd7\xee\xf8\x7f\x25\xf9\x96\xae\xff\xf8\x06\x11\x76\
-\x33\x80\x53\x94\xe4\x5b\x1e\x2f\xaf\xf6\xb6\x22\xf8\x3b\x21\xc8\
-\x5b\x70\x36\x45\xa9\x92\x84\x28\xc9\x86\x2a\x05\x57\x08\x0a\x91\
-\xa6\xa4\x65\x30\xf3\xda\x05\x0c\x19\x11\x50\x99\x28\xdd\x88\x8e\
-\x8e\x21\x3b\xb7\xa0\xeb\xe0\x21\x29\x71\x38\xec\xd8\xed\x36\x5c\
-\x27\xc5\xc0\xe5\x74\xa2\xaa\x5e\xbc\xaa\x17\xb7\xeb\xdc\x72\xdd\
-\x42\x08\x62\xce\xf2\x53\x88\x89\x8d\x23\x26\x36\x96\xa8\xe8\x18\
-\xa2\x63\x62\xcf\x99\xe5\xc4\x58\x61\xce\x10\x15\x6b\xef\xc9\xf1\
-\x28\x85\xe0\xe1\xe2\x3c\x8b\xee\xd9\x7c\xf5\x22\x6c\x05\x00\xa0\
-\xa4\xc0\xf2\x54\x45\xad\xa7\x5d\x4a\xf1\x3c\x21\xb6\x35\x23\x01\
-\xa6\xf4\x57\x59\x73\x58\x09\x9a\xef\x80\x35\x2a\x8a\x92\x49\xd3\
-\xb9\x62\xfa\x5c\xa2\xa2\x23\x24\x9d\x91\x10\xc4\xc6\xc5\x13\x1b\
-\x17\xaf\x7b\xd3\x43\xfb\xc9\xde\x34\xf8\xbd\x12\xf9\xf5\x92\x3c\
-\xeb\x73\x46\x1b\xd2\x15\x61\x2d\x00\x00\xc5\x79\xd6\x97\xcb\x6a\
-\x3c\x1e\x81\x78\x11\x08\xe9\x28\x49\x8d\x83\xd1\xb9\x92\xad\xb5\
-\xfa\x4f\x3d\x07\x0f\x1b\xc5\xcc\xeb\x6f\x25\x35\x2d\xa8\xe9\x11\
-\x22\x8a\xcc\x84\x5e\xe0\xa5\xe5\xc3\x89\x94\x77\x4f\x28\x08\x8f\
-\xa3\xbe\xae\x08\x7b\x01\x80\x93\x7e\x02\x35\xb2\x5a\xa2\xbe\x05\
-\x84\x74\x81\x5c\x94\x22\xd9\x55\x27\x70\xeb\x14\x4f\x90\x9a\xde\
-\x8f\x59\xd7\x2d\x60\xd0\xb0\x51\xfa\x34\xd8\x8b\xe8\x0d\x5e\x9a\
-\x40\xb3\x50\xd5\x05\xc5\x85\x51\xab\x8c\x36\xc4\x1f\x22\x6a\x57\
-\xa5\xac\x4a\x0e\x11\x8a\xfa\x01\x30\x34\x94\xfd\x6e\xa8\x14\x1c\
-\xeb\xa1\xf7\x60\x7b\x5b\x0b\x3b\x2b\x36\x32\x71\xea\x2c\x2c\x96\
-\x88\xd0\xdd\x90\x73\x45\x91\x24\x3b\x29\xa2\x55\xe0\xb0\x2a\x94\
-\xeb\x4b\xf3\x84\xa1\xee\xbd\x5a\x88\x28\x01\x00\xd8\x5c\x2b\xfb\
-\x29\xd2\xfb\x16\x88\x2b\x43\xd5\xe7\x81\x46\xc1\x8e\xba\x88\xfb\
-\xaa\x22\x8e\xab\x87\xa8\x24\x86\xff\x89\xde\xc5\x11\x72\x63\x94\
-\xc5\x72\x93\x91\x81\x3d\x81\x10\x1e\xee\x51\x1a\x28\xcd\x13\x27\
-\x54\x61\xb9\x46\x20\xde\x0c\x55\x9f\xa9\x66\xb0\x5d\xd0\xc9\x49\
-\x92\x91\x3b\xf8\xa5\x58\xe2\xf0\x5a\x66\x44\xda\xe0\x87\x08\x14\
-\x00\x80\xd2\x3c\x61\x1b\x9f\x27\x6e\x05\x7e\x04\x04\xfd\xa0\x2e\
-\x39\x36\xa2\xa7\xa5\x61\x8f\x10\x11\x5b\xdd\x49\x02\x8f\x17\xe7\
-\x8b\x3b\xa6\x14\x86\x26\x89\xa7\xde\x44\xa4\x00\x00\x08\x21\x64\
-\x49\xbe\xe5\x71\x45\xaa\x73\x80\x86\x60\xf6\x15\x6d\x81\x58\x73\
-\xd9\x1e\x34\x06\xa6\x49\x92\x23\x6f\x96\xd5\x28\x85\x7a\x5d\x49\
-\xbe\xe5\x47\x42\x88\x88\x4d\x39\x13\xb1\x02\x70\x8a\xf1\x05\x51\
-\x2b\x3d\x28\xa5\x48\xb9\x29\x98\xfd\x24\xc5\x44\xe4\x1b\x2a\xec\
-\x51\xf0\x9d\xff\x47\x12\x12\x2a\x2c\x16\x65\xe2\x84\xbc\xa8\x8f\
-\x8c\xb6\xa5\xa7\x44\xbc\x00\x00\x5c\x9e\x2f\xaa\x1c\xd2\x32\x5d\
-\x40\xd0\x9c\x2e\xe2\xa2\x82\xd5\x72\xdf\x26\x3f\x55\x46\xd4\x77\
-\x2b\xe1\x5f\x6d\x2e\x65\xca\xb8\x1c\x11\xb2\xd4\xdd\xc1\xa4\xd7\
-\x6d\x6d\x97\xd7\x7a\x6e\x45\x8a\x7f\x02\x3d\x2f\xce\x76\x16\x3b\
-\xeb\x05\xfb\x4f\xf4\xba\xaf\xcb\x70\xa6\x0f\x52\x49\x8b\x84\x04\
-\xc6\x82\x56\xa4\xfc\x66\x49\xbe\xf5\xdf\x46\x9b\xa2\x27\xbd\x62\
-\x06\x70\x36\x25\x79\xd6\x25\x1e\x94\xf1\x08\xb9\xca\x68\x5b\x4c\
-\xba\x26\x3d\x9e\x88\x18\xfc\x52\x88\x95\x16\x55\x19\xdd\xdb\x06\
-\x3f\xf4\x42\x01\x00\xdf\x92\xa0\x38\xd7\x32\x4b\x08\x7e\x00\xb8\
-\xba\xbd\xc1\x24\xe4\x08\x60\x54\x56\xd8\xef\x9d\x79\x24\xfc\xa2\
-\x24\x57\x5c\x3d\xae\x40\x54\x1b\x6d\x4c\x30\xe8\x95\x02\x00\xbe\
-\x53\x82\xe2\x3c\xcb\x9f\x15\x94\xc9\x40\x79\x4f\xdb\xeb\x2b\xf5\
-\x05\x42\xc5\x88\x2c\x49\x46\x58\x97\x2a\x94\x9b\x91\x4a\xe9\x84\
-\x7c\xcb\x63\x91\xbc\xcb\xdf\x1d\xbd\x56\x00\x4e\x31\x3e\x5f\x54\
-\xb4\xe6\x29\x93\x4e\xce\x06\x02\x2e\x18\x66\x8e\xff\x9e\x23\x84\
-\x6f\xca\x3f\xa9\x48\x32\x3c\x33\x6c\xbf\x51\x3b\xf0\xa3\x03\x79\
-\x96\xc9\x25\x05\x62\xab\xd1\xc6\x04\x9b\x3e\xb5\xab\xb5\xa9\x5e\
-\x0e\xb2\x78\xe4\x53\x20\xe7\x68\xbd\x77\xdb\x31\xc1\xa1\xa6\x3e\
-\xf5\x75\x05\x8c\x00\x32\x12\x24\xfd\x12\x20\x21\x0a\x62\xa3\x20\
-\xc6\x22\x89\x8f\x06\x6b\x38\xbf\x72\xa4\x5c\xad\x60\x79\x68\x7c\
-\x81\xd8\x67\xb4\x29\xa1\xa2\xcf\x3d\xd1\x52\x4a\x51\x5e\xeb\xbd\
-\x5f\x20\x7e\x03\x64\xfb\x7b\xdf\xf6\x3a\xc1\xc1\xc6\x3e\xf7\x75\
-\x69\x26\x3b\x49\x32\x32\x4b\x92\x12\x59\x8e\x3d\x75\x48\xf9\x68\
-\x49\x81\xf5\x45\xa3\x0d\x09\x35\x7d\xf6\x89\xde\x5a\x27\x13\x3c\
-\x5e\xf5\xbf\x04\x3c\x0a\x74\xfb\xb8\x9a\x01\x41\x5d\x93\x99\x20\
-\x19\x96\x19\x61\x31\xfd\x02\x37\x92\x27\x5d\xb1\xca\xcf\x26\x67\
-\x88\x36\xa3\xcd\x31\x82\x3e\xff\x44\x97\x55\xc9\x21\xc2\x22\x7f\
-\x8d\x94\xb7\x77\x75\x5d\x5d\xbb\xe0\x8b\xca\x3e\xff\x75\x5d\x40\
-\x46\x02\x8c\xce\x8e\x90\xb3\xfc\xb3\x10\x88\xf7\xbc\x5e\xf1\x83\
-\xd2\x22\x71\xd0\x68\x5b\x8c\xc4\x7c\xa2\x4f\x52\x5e\xe3\xbe\x1a\
-\x94\xc7\x81\x92\x8b\xfd\xbc\xcd\x01\x2b\x0e\x86\xf3\x02\x36\xb4\
-\xc4\x45\xc1\x98\x1c\x49\x5e\x72\x04\xbd\xf1\x01\x90\x9b\xa5\x94\
-\x8f\x4e\x28\x88\x5a\x61\xb4\x25\xe1\x80\x29\x00\xe7\x51\x5e\xe3\
-\xbe\x5a\xa2\xfc\x4e\xc0\x39\xf5\xb7\xdc\x5e\x78\x7f\x8f\x29\x00\
-\xe0\x9b\xe6\x4f\x2c\x94\x44\x47\x56\xfe\xbe\x9d\x42\xca\x5f\x8c\
-\xcf\xb7\xbc\x61\x44\x19\xee\x70\xc5\x7c\xa2\xcf\xa3\x24\x3f\xea\
-\xd3\x92\x3c\x65\x82\x44\xde\x04\x6c\x3b\xf5\xb9\xd5\x02\x61\x52\
-\x5c\xc6\x50\xd2\xe2\x60\x72\xff\x88\x1a\xfc\xbb\x91\xf2\xbe\x03\
-\x79\xca\xb8\xe2\x02\xeb\xeb\xe6\xe0\x3f\x17\x73\x06\xd0\x05\x8b\
-\xa5\xb4\x0c\x3e\xe6\xbd\x53\x48\xf1\x5f\x2d\x76\xc6\xae\x3a\xd4\
-\xb7\x15\x40\x00\x33\x07\xab\x91\x12\xba\xbb\x45\x08\xf9\xfb\xf1\
-\xb9\x96\x57\x7b\xb3\x23\x4f\x4f\x31\x05\xc0\x4f\xde\xde\x2d\x17\
-\x1c\x3a\xc1\x53\x2d\x76\xc2\xd7\x85\x25\xc8\xa4\xc4\xc1\xcc\x41\
-\xe1\x3e\x96\xe4\x3a\x09\x8f\x97\xe4\x59\xde\x33\xdf\xf6\xdd\x63\
-\x0a\x80\x46\x16\x95\xcb\x9b\x3a\x9d\x3c\xde\xd0\xc9\x08\x4f\xb8\
-\x8f\x05\x9d\xc9\x4f\xf6\xad\xfd\xc3\x10\x17\x82\x7f\xab\x5e\xe5\
-\x0f\xa5\x85\x62\xbb\xd1\xc6\x44\x12\xa6\x00\x04\xc8\xe2\xcd\xb2\
-\xa8\xd9\xa3\xfe\xae\xd9\xf6\xff\xb7\x77\x37\x3d\x4d\x04\x61\x1c\
-\xc0\xff\xcf\x4c\x09\x50\x8a\x21\x82\xd8\x42\x40\x88\xc1\x10\x09\
-\x28\x95\x78\x20\x26\x86\x53\x23\x07\x8d\x9c\xf5\x13\x78\xf4\xa4\
-\x37\x3f\x84\x31\x72\xd0\x18\xbd\x10\x3d\x18\xe2\x81\x83\x06\x0f\
-\x36\xbe\x04\x4d\x14\xc3\x41\x13\x4b\xe2\xa1\x18\x15\xb5\x94\x76\
-\xdf\xe6\x79\x3c\xd4\x78\x52\x23\xd2\x76\xd9\x3a\xbf\x0f\xb0\xf3\
-\xec\x66\xe7\x9f\xd9\xdd\xd9\x19\x3a\x5d\x0a\x28\x22\xbb\x7a\x6c\
-\x4f\x77\x42\x30\xb9\x6f\x47\x05\xc0\x1b\x00\xd7\xb4\x56\xd7\x0f\
-\x25\xa9\xa6\xab\x42\x35\x2a\x1b\x00\x55\x70\xe3\xb9\x9c\xd9\xf4\
-\x70\xf1\x73\x09\xc3\xbe\x69\xdc\x6b\xda\xda\x04\x64\x0e\x84\x3e\
-\xec\x71\x41\x34\x0f\x31\xb3\xe3\x3d\xb1\x07\x76\x98\xbf\x3d\x0d\
-\x7b\xb3\x86\x61\x31\x27\xc9\xf7\xeb\x7c\x69\xbd\xac\x66\xbe\x94\
-\xd1\xd5\x88\x7f\x10\x4e\x0f\x73\x18\x5f\x00\x0c\x40\x8b\x44\x3c\
-\x07\x57\xdf\x19\x1f\xa4\xaf\x75\xaf\xa0\x41\xd9\x00\xa8\x91\xd9\
-\x67\x32\xc2\x06\x17\x4a\x01\x32\xdf\x9c\xc6\x09\x83\x63\x03\x82\
-\xae\xfa\x4c\xf7\x65\x40\x1e\x13\xd1\x6d\x3f\xa6\xe6\x8e\x76\xd3\
-\x5a\x3d\x1a\xfd\xdf\xd8\x00\xa8\x83\xbb\x2f\xe4\xe0\xba\xe0\xfc\
-\x86\x83\x4c\xc1\x41\x4f\x94\x1f\x13\xd2\xbd\x82\xfe\x8e\x9a\x05\
-\x40\x01\x42\xf7\x49\xf1\x82\xf1\xf4\xbd\x89\x01\xca\xd7\xaa\x21\
-\xab\x22\xb2\x37\x62\x54\x5d\x5e\x94\x44\xbc\x9d\xcf\x39\xbe\x9a\
-\x29\x7a\x32\x5a\xf4\xa8\x35\x4a\xa3\x83\x23\xbd\x82\xbe\xea\x06\
-\xc0\x4b\x00\x0b\xc4\xbc\x60\x3e\xc4\xb2\x13\x13\xe4\x57\xf3\xe0\
-\xd6\x9f\xd9\x00\x08\xd9\xcd\x15\x49\x99\x32\x9f\x2d\x79\x74\xb2\
-\xec\xd3\xc8\x86\x83\x8e\xd0\x5f\xb3\xfd\x46\x73\xac\x32\x11\x68\
-\x1b\x7b\x24\x04\xa8\x74\xf8\x2c\x89\x3c\x12\xad\x1f\xa6\x53\xf4\
-\xb1\x6a\x05\x5a\x5b\x66\x03\x60\x87\xb9\xf5\x44\x76\xe9\x16\x9c\
-\x72\x03\x3e\xe1\x06\xea\xf0\xa6\x8f\xbe\x82\x83\x04\x87\x3c\x4a\
-\x20\x54\xa6\x00\xef\x4d\xfc\x75\x21\x0e\x80\x15\x01\x5e\x91\x60\
-\x99\x48\x3d\x4d\x94\xb0\x34\x34\x44\x6e\xed\xaa\xb4\xb6\xca\x06\
-\x40\x04\x5c\x5d\x92\xb8\x12\x4c\x13\xf3\x71\x03\x1a\xf3\x03\xf4\
-\xbb\x86\x3a\xcb\x3e\xda\x3c\x53\x9f\xff\x39\xf6\x77\x0a\x46\x93\
-\xbf\xe8\xfc\x84\x3c\x44\x72\x02\xca\x11\xe1\x9d\xb0\xbc\x86\xd2\
-\xcb\x85\x14\xde\x4e\x11\x05\xf5\xa8\xcd\xfa\x77\x36\x00\x22\xee\
-\x4a\x56\xba\x5b\x9a\x31\x49\xe0\xb1\x00\xaa\x4f\x8c\x24\x0d\x63\
-\x8f\x2f\xd8\x6d\x98\xda\x03\x96\x36\x16\x34\x31\x93\x06\x80\x80\
-\xa1\x01\x90\x61\x90\xf9\xd1\x9f\x35\x01\x5a\xfd\x5c\xf6\x90\x63\
-\x0a\xac\x35\x02\x05\xf1\x34\xa1\xac\x15\x8a\xe9\x5e\x99\x8f\x37\
-\x21\x0f\x91\x4f\xac\x64\x4d\x10\x5b\xdd\x70\xb1\x3a\x35\x48\x4e\
-\x38\x67\x6e\x55\xc3\x77\x15\x65\xab\x07\x70\xac\xd6\x54\x00\x00\
-\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
-\x00\x00\x05\xc2\
-\x89\
-\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
-\x00\x00\x18\x00\x00\x00\x18\x08\x06\x00\x00\x00\xe0\x77\x3d\xf8\
-\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
-\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\
-\x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
-\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
-\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x05\x3f\x49\x44\
-\x41\x54\x48\x89\x7d\x95\x69\x6c\x54\x55\x14\x80\xbf\xfb\xe6\x31\
-\xe5\xcd\x74\x1e\xb5\x53\x5a\x9c\x96\xca\x5a\x01\x31\x85\x52\x11\
-\xab\x60\x52\x40\xc3\x22\x1a\x14\x89\x10\x23\xb8\x41\x5c\xb0\x44\
-\x82\x06\x7f\x68\xa2\xd1\xa8\x08\x08\x46\x04\x11\x90\x62\xaa\x56\
-\x45\x5a\x4b\x54\x44\x04\x62\x44\x96\x41\x0d\x4b\x2d\x52\x5a\xa0\
-\x0b\xd0\x5a\x3a\x9d\x4e\x3b\xed\xbc\x77\xfc\x51\x66\xa4\x1b\xe7\
-\xd7\xbb\x27\xe7\x7e\xe7\x9d\xf5\x2a\xae\x23\xf9\xc7\x24\xa7\x35\
-\x2c\x2f\x36\xb7\x33\xa9\xd5\x52\x66\xb8\x5d\x9c\x22\x8a\x9c\xc1\
-\x52\x99\xe4\xa2\x54\x90\xef\x2c\xb4\x9d\xe3\x53\xd5\xf9\xde\x18\
-\xaa\x27\xe5\xe6\x23\x72\x7b\x30\x2c\xf9\xf5\x21\x35\x5c\xa4\x43\
-\xe7\x33\x85\x54\x13\xaa\x02\x30\x34\x51\xf0\xba\x63\xe6\x36\x4a\
-\x7d\xad\x94\x5a\x3e\xf6\x46\x55\xd1\x95\xa5\x75\x55\x7c\x7d\xdc\
-\xca\x13\x5b\x0e\x04\xc3\xff\xc3\x5d\x7d\xc0\xa1\x41\xa2\x4b\xc8\
-\x4e\x13\x3c\x7d\xff\xb7\x17\x11\x0d\x91\x39\xb6\x6d\xff\xe5\xaf\
-\x8a\xcc\xba\x6e\x04\x1b\x0e\xd9\x3b\x83\xad\xcc\x6a\x0c\x34\x71\
-\xba\xac\x8c\xfa\xfa\x3a\x0c\x09\xa2\x22\x21\x3c\xf1\x06\x8b\xe6\
-\xcd\xc4\x65\xc4\x01\x50\x71\xbe\x96\x55\x9f\x7c\xc3\x1b\xcb\x16\
-\x60\xc6\xbb\xae\x89\x86\xa5\x59\x3e\xc7\xda\xa8\x42\x8f\x7e\x6c\
-\xfa\x5d\x56\x9d\xa8\x6c\x98\xb5\xef\xc7\x9d\x94\x9d\xfc\x13\xdb\
-\xb6\xbb\xa5\xee\xb1\xd9\x53\x71\x3a\x75\x36\x7c\x56\x42\x61\xc9\
-\x7e\x22\x96\xd5\x3d\x23\xc2\xea\x92\x93\x91\xd0\x8c\x51\xfa\xa6\
-\x58\x8a\xb6\x1f\x95\x09\x87\x4f\x55\xe4\x6d\xfd\xf0\x1d\x4a\x8f\
-\x1f\xeb\x11\x1e\x95\x40\xa0\x99\x82\xa2\xbd\x3d\xc1\x63\x4e\x4c\
-\x43\xad\xd9\xf6\xbb\x64\xc7\x1c\x5c\xb8\xdc\x54\xb0\xe3\xb3\x4f\
-\x54\xb8\x35\x04\x40\x3f\xd3\xcd\x73\x8f\xdd\xcf\xca\x15\x4f\xf7\
-\xea\x28\x2a\xaa\x4b\x9b\xd8\x02\xfb\xca\x35\x77\x63\x84\x02\x00\
-\xfd\x48\xb5\xa4\x7f\x5a\xb8\x3b\x3d\x18\x0c\x00\x60\x9a\x1e\x3e\
-\x7e\xeb\x05\x7c\x29\x5e\x02\xc1\x50\xaf\xe0\x74\x5f\x32\x79\x4f\
-\xcc\xc6\xe3\x76\x11\x08\x86\xb8\x12\x08\xe2\xf1\xc4\x73\xfa\x8a\
-\x9b\x36\x0b\xea\x42\x0c\x2b\xf0\x4b\xb6\xae\x61\x3f\x70\xe0\xd0\
-\x1f\xb1\x6e\x7a\x6a\xee\xbd\xf8\x52\xbc\xbd\x82\x75\xdd\xc1\xe2\
-\xf9\x33\x79\x68\xfa\x44\x76\xfd\x72\x98\xf7\xb7\xec\xa0\xf2\xc2\
-\xc5\xab\xd1\x28\xd2\x87\x64\x90\x3b\x6d\x36\xde\xfe\x03\x08\xb4\
-\xc9\x2b\x3a\xa2\xa6\x55\xd5\xfe\x1b\x03\x4c\x1c\x3f\xba\x47\xf0\
-\x84\xac\x91\xb8\xdd\x06\x71\xce\x3e\x8c\xbe\x79\x10\x8b\x56\xbc\
-\xcf\x99\xca\xea\x4e\x36\x22\x42\xe5\x99\xbf\xf9\x62\xeb\x07\x2c\
-\x78\xe6\x25\x5a\x5c\x9e\x1c\xfd\xd7\x72\xfb\xae\xe6\x50\x47\x2a\
-\x34\x4d\x91\xd8\xcf\xec\x74\x29\x35\x25\x89\x25\x0b\x1f\xe0\xce\
-\xec\x5b\xa8\x6b\x68\xe4\xed\x8f\x8a\xd9\x7d\xc0\x8f\x5c\x1d\x92\
-\x21\xe9\x3e\x32\x47\x0e\xa6\xb6\xae\x81\x83\xfe\x53\x88\x08\xa1\
-\x60\x13\x47\x7f\xdb\x47\xe2\xb4\x99\x37\xe8\x83\x93\x94\xee\xd0\
-\x1d\x58\x11\x0b\xdb\x16\x82\xcd\x21\x4c\x4f\xc7\x98\xba\x8d\xbe\
-\xe4\xaf\x59\x8e\xa6\x69\x14\x14\xed\x65\x73\xe1\x0f\xb4\xb4\x84\
-\x01\xb8\x29\x2d\x85\xbc\x85\xb3\xb9\x2d\x33\x03\x80\xcb\xcd\x8a\
-\xd5\xf9\x7b\xd8\xff\x53\x31\x00\xe5\x65\x27\x69\xbf\x67\x46\x1f\
-\xdd\x67\x12\x49\xf7\x25\x73\xf6\x5c\x0d\x00\x3b\x76\x1f\x64\xec\
-\x1d\x53\x68\x0a\x43\xff\x78\x07\xa5\xa5\xff\xf0\x79\xe1\x37\x9c\
-\xab\xea\xc8\xb3\x61\xc4\xf1\xf8\x9c\x7b\x99\x33\x63\x12\xb6\x6d\
-\x63\x59\x36\x0e\x87\x86\xd7\x2d\xe4\x4e\x9a\x10\x73\xd0\x50\x7f\
-\x09\xa4\x63\xd0\xaa\x73\xb2\x46\x65\x44\x1d\x6c\x2a\x28\x61\xf0\
-\xe1\x72\xdc\x66\x3f\xaa\xcf\x9d\xa5\xfe\x72\x6d\xac\x80\x53\x26\
-\x66\xf1\xec\xa3\xf7\x91\x74\x43\x3f\x7e\x3d\x72\x82\xb5\x5b\xbe\
-\xe5\xe3\xb7\x97\x62\xc6\xbb\xd0\x80\x9c\x61\x06\x4a\x53\x88\x2d\
-\x44\x22\xed\xc4\xe9\x12\xd6\x41\x95\xcf\x9d\x79\x77\xc6\xae\xbd\
-\x87\x68\x68\x6c\x42\x44\x28\x3f\x7d\xb2\x53\x1d\xfa\x0f\xf0\x91\
-\x3b\xfd\x21\x46\x8f\x18\x4a\xbc\x2b\xcc\xb2\x37\x37\x72\xd0\x7f\
-\xaa\x5b\x23\x04\x9b\x1a\x11\xbb\xa3\x36\xa6\xc7\xcd\xd4\xe1\xea\
-\x67\x4d\x90\x92\xc4\x04\x0f\xeb\x5e\x7b\x86\x31\xa3\x47\x74\x9a\
-\x9c\xa4\x14\x1f\x53\x66\xcc\x61\xde\x93\x4b\xa9\x39\x77\x96\x8b\
-\xf5\xcd\xfc\x75\xbe\xb5\x47\x38\xc0\x81\x43\xc7\xaf\x69\x0e\x2f\
-\x0a\x29\xd6\x2d\xb4\x9d\x3a\xf6\x9a\x41\x03\x07\x38\x96\x3e\xbf\
-\x98\xa3\x15\xad\x84\x5a\x42\xb8\x0c\x17\xce\xbe\x06\x15\x67\x4a\
-\xd9\xb6\xfe\x5d\x1a\xea\x2f\x71\xcb\x98\xf1\x9c\xae\xeb\x71\xc3\
-\x53\x75\xb1\x8e\xcd\x85\xdf\xc7\xce\xb9\x77\x64\xda\x0e\xb4\x62\
-\x7d\x7c\xaa\x3a\xef\xaf\xb2\xb6\x02\x4f\x24\x18\x42\x9c\x61\xe0\
-\xec\x6b\x00\x10\x6e\x0d\xf1\xd5\xb6\xf5\x3d\x02\xa3\xb2\xc7\x5f\
-\x49\xcb\xbf\x35\x6c\xff\x76\x4f\x6c\xf2\x93\xbd\x09\x4c\xce\xc9\
-\xda\x96\x99\xa6\x2e\xe8\x00\x9a\xad\xbd\x6a\x69\xf6\xc3\x5e\x17\
-\x9e\x21\x89\xc2\x99\xfa\x8e\xbf\x8c\xbe\x07\xd7\x93\xf7\xd6\x6e\
-\xec\x74\x36\x3d\x6e\x5e\x7f\x71\x41\x73\x52\x42\xe2\x0a\xb8\xba\
-\xec\xc6\x0c\x54\x55\x4a\x93\xb9\x80\x35\xb2\xbf\x10\xa7\x77\x86\
-\x68\x0e\x07\xd9\x77\xe6\x62\xb8\xdc\xf4\x26\xba\xc3\xc1\xd4\x89\
-\x59\x6c\x5d\xb9\xcc\x1e\x95\x91\xfe\x48\xf6\x20\x55\x03\x5d\x1e\
-\x1c\x7f\xb5\xb5\x04\x61\x75\x9b\x85\x76\xbc\x56\x51\x56\xd3\x42\
-\xd1\x17\x5b\x98\x3c\xfd\x41\xbc\xc9\x03\x00\x68\x6f\x0b\xb3\xff\
-\xa7\x62\x5a\x5b\x5a\x18\xe8\x35\x18\x98\xec\x26\xdd\x97\xc2\xb8\
-\x5b\x87\x91\x60\xc6\xdb\x40\x5e\x56\xaa\x63\x5d\x94\xd9\xad\x62\
-\xfe\xaa\xc8\x2c\x41\x6d\x6f\xb7\xf0\xec\x3a\xa5\xba\xef\xe3\x68\
-\x54\x0a\x26\x0f\xb3\x71\x3b\x63\xaa\x80\x88\xcc\x1f\x97\xa6\x7f\
-\xd7\xc9\xae\xeb\xc5\xac\x54\xbd\x48\x69\xda\x50\x5d\xb1\x4e\xeb\
-\x05\x3e\xd4\x2b\x4c\x1d\x1e\x83\xdb\x08\xf9\x76\xbb\x36\xa2\x2b\
-\xbc\xc7\x08\xae\x95\x2f\x4f\xc8\x98\x40\xb3\xfd\x72\x30\xac\x72\
-\xdb\x2c\x65\x86\x2d\x9c\x11\x1b\x75\xcf\x70\xfb\x1f\xc3\xa9\xca\
-\x94\x48\x89\x26\x5a\x51\x66\x9a\xba\xd0\x1b\xe3\x3f\x12\xae\x2a\
-\x58\xfd\xf2\x8c\xf2\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\
-\x82\
-"
-
-qt_resource_name = b"\
-\x00\x05\
-\x00\x6f\xa6\x53\
-\x00\x69\
-\x00\x63\x00\x6f\x00\x6e\x00\x73\
-\x00\x17\
-\x0b\xce\x1a\xe7\
-\x00\x71\
-\x00\x75\x00\x74\x00\x65\x00\x62\x00\x72\x00\x6f\x00\x77\x00\x73\x00\x65\x00\x72\x00\x2d\x00\x31\x00\x32\x00\x38\x00\x78\x00\x31\
-\x00\x32\x00\x38\x00\x2e\x00\x70\x00\x6e\x00\x67\
-\x00\x17\
-\x07\xc8\x36\xe7\
-\x00\x71\
-\x00\x75\x00\x74\x00\x65\x00\x62\x00\x72\x00\x6f\x00\x77\x00\x73\x00\x65\x00\x72\x00\x2d\x00\x35\x00\x31\x00\x32\x00\x78\x00\x35\
-\x00\x31\x00\x32\x00\x2e\x00\x70\x00\x6e\x00\x67\
-\x00\x15\
-\x02\x0a\xc7\x87\
-\x00\x71\
-\x00\x75\x00\x74\x00\x65\x00\x62\x00\x72\x00\x6f\x00\x77\x00\x73\x00\x65\x00\x72\x00\x2d\x00\x33\x00\x32\x00\x78\x00\x33\x00\x32\
-\x00\x2e\x00\x70\x00\x6e\x00\x67\
-\x00\x15\
-\x02\xf8\xd9\xc7\
-\x00\x71\
-\x00\x75\x00\x74\x00\x65\x00\x62\x00\x72\x00\x6f\x00\x77\x00\x73\x00\x65\x00\x72\x00\x2d\x00\x36\x00\x34\x00\x78\x00\x36\x00\x34\
-\x00\x2e\x00\x70\x00\x6e\x00\x67\
-\x00\x15\
-\x02\x26\xc3\x07\
-\x00\x71\
-\x00\x75\x00\x74\x00\x65\x00\x62\x00\x72\x00\x6f\x00\x77\x00\x73\x00\x65\x00\x72\x00\x2d\x00\x31\x00\x36\x00\x78\x00\x31\x00\x36\
-\x00\x2e\x00\x70\x00\x6e\x00\x67\
-\x00\x15\
-\x02\x14\xc5\x47\
-\x00\x71\
-\x00\x75\x00\x74\x00\x65\x00\x62\x00\x72\x00\x6f\x00\x77\x00\x73\x00\x65\x00\x72\x00\x2d\x00\x34\x00\x38\x00\x78\x00\x34\x00\x38\
-\x00\x2e\x00\x70\x00\x6e\x00\x67\
-\x00\x15\
-\x02\xa6\xd3\x07\
-\x00\x71\
-\x00\x75\x00\x74\x00\x65\x00\x62\x00\x72\x00\x6f\x00\x77\x00\x73\x00\x65\x00\x72\x00\x2d\x00\x39\x00\x36\x00\x78\x00\x39\x00\x36\
-\x00\x2e\x00\x70\x00\x6e\x00\x67\
-\x00\x17\
-\x0a\x1f\xbe\xe7\
-\x00\x71\
-\x00\x75\x00\x74\x00\x65\x00\x62\x00\x72\x00\x6f\x00\x77\x00\x73\x00\x65\x00\x72\x00\x2d\x00\x32\x00\x35\x00\x36\x00\x78\x00\x32\
-\x00\x35\x00\x36\x00\x2e\x00\x70\x00\x6e\x00\x67\
-\x00\x15\
-\x02\x38\xc1\xc7\
-\x00\x71\
-\x00\x75\x00\x74\x00\x65\x00\x62\x00\x72\x00\x6f\x00\x77\x00\x73\x00\x65\x00\x72\x00\x2d\x00\x32\x00\x34\x00\x78\x00\x32\x00\x34\
-\x00\x2e\x00\x70\x00\x6e\x00\x67\
-"
-
-qt_resource_struct = b"\
-\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
-\x00\x00\x00\x00\x00\x02\x00\x00\x00\x09\x00\x00\x00\x02\
-\x00\x00\x00\x78\x00\x00\x00\x00\x00\x01\x00\x00\xbd\xb9\
-\x00\x00\x01\x08\x00\x00\x00\x00\x00\x01\x00\x00\xda\x06\
-\x00\x00\x00\xd8\x00\x00\x00\x00\x00\x01\x00\x00\xd6\x51\
-\x00\x00\x01\x9c\x00\x00\x00\x00\x00\x01\x00\x01\x49\xa3\
-\x00\x00\x01\x38\x00\x00\x00\x00\x00\x01\x00\x00\xe6\x0b\
-\x00\x00\x00\xa8\x00\x00\x00\x00\x00\x01\x00\x00\xc5\x97\
-\x00\x00\x00\x44\x00\x00\x00\x00\x00\x01\x00\x00\x23\x02\
-\x00\x00\x01\x68\x00\x00\x00\x00\x00\x01\x00\x00\xff\xe4\
-\x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
-"
-
-def qInitResources():
- QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
-
-def qCleanupResources():
- QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
-
-qInitResources()
diff --git a/qutebrowser/utils/debug.py b/qutebrowser/utils/debug.py
index 7d069909a..0fa74f4e7 100644
--- a/qutebrowser/utils/debug.py
+++ b/qutebrowser/utils/debug.py
@@ -81,7 +81,7 @@ def log_signals(obj: QObject) -> QObject:
pass
if inspect.isclass(obj):
- old_init = obj.__init__ # type: ignore[misc]
+ old_init = obj.__init__
@functools.wraps(old_init)
def new_init(self: Any, *args: Any, **kwargs: Any) -> None:
@@ -89,7 +89,7 @@ def log_signals(obj: QObject) -> QObject:
old_init(self, *args, **kwargs)
connect_log_slot(self)
- obj.__init__ = new_init # type: ignore[misc]
+ obj.__init__ = new_init
else:
connect_log_slot(obj)
diff --git a/qutebrowser/utils/log.py b/qutebrowser/utils/log.py
index 9cd07e2e3..22035e074 100644
--- a/qutebrowser/utils/log.py
+++ b/qutebrowser/utils/log.py
@@ -44,6 +44,7 @@ except ImportError:
if TYPE_CHECKING:
from qutebrowser.config import config as configmodule
+ from typing import TextIO
_log_inited = False
_args = None
@@ -245,7 +246,9 @@ def disable_qt_msghandler() -> Iterator[None]:
@contextlib.contextmanager
def py_warning_filter(action: str = 'ignore', **kwargs: Any) -> Iterator[None]:
"""Contextmanager to temporarily disable certain Python warnings."""
- warnings.filterwarnings(action, **kwargs)
+ # FIXME Use Literal['default', 'error', 'ignore', 'always', 'module', 'once']
+ # once we use Python 3.8 or typing_extensions
+ warnings.filterwarnings(action, **kwargs) # type: ignore[arg-type]
yield
if _log_inited:
_init_py_warnings()
@@ -257,7 +260,7 @@ def _init_handlers(
force_color: bool,
json_logging: bool,
ram_capacity: int
-) -> Tuple[logging.StreamHandler, Optional['RAMHandler']]:
+) -> Tuple["logging.StreamHandler[TextIO]", Optional['RAMHandler']]:
"""Init log handlers.
Args:
@@ -381,8 +384,8 @@ def qt_message_handler(msg_type: QtCore.QtMsgType,
"""Qt message handler to redirect qWarning etc. to the logging system.
Args:
- QtMsgType msg_type: The level of the message.
- QMessageLogContext context: The source code location of the message.
+ msg_type: The level of the message.
+ context: The source code location of the message.
msg: The message text.
"""
# Mapping from Qt logging levels to the matching logging module levels.
@@ -727,7 +730,8 @@ class ColoredFormatter(logging.Formatter):
datefmt: str,
style: str, *,
use_colors: bool) -> None:
- super().__init__(fmt, datefmt, style)
+ # FIXME Use Literal["%", "{", "$"] once we use Python 3.8 or typing_extensions
+ super().__init__(fmt, datefmt, style) # type: ignore[arg-type]
self.use_colors = use_colors
def format(self, record: logging.LogRecord) -> str:
diff --git a/qutebrowser/utils/message.py b/qutebrowser/utils/message.py
index 50a438637..c490aa4e8 100644
--- a/qutebrowser/utils/message.py
+++ b/qutebrowser/utils/message.py
@@ -18,7 +18,8 @@
# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>.
# Because every method needs to have a log_stack argument
-# pylint: disable=unused-argument
+# and because we use *args a lot
+# pylint: disable=unused-argument,differing-param-doc
"""Message singleton so we don't have to define unneeded signals."""
@@ -125,7 +126,7 @@ def ask(*args: Any, **kwargs: Any) -> Any:
Return:
The answer the user gave or None if the prompt was cancelled.
"""
- question = _build_question(*args, **kwargs) # pylint: disable=missing-kwoa
+ question = _build_question(*args, **kwargs)
global_bridge.ask(question, blocking=True)
answer = question.answer
question.deleteLater()
@@ -139,7 +140,7 @@ def ask_async(title: str,
"""Ask an async question in the statusbar.
Args:
- message: The message to display to the user.
+ title: The message to display to the user.
mode: A PromptMode.
handler: The function to get called with the answer as argument.
default: The default value to display.
@@ -174,7 +175,7 @@ def confirm_async(*, yes_action: _ActionType,
The question object.
"""
kwargs['mode'] = usertypes.PromptMode.yesno
- question = _build_question(**kwargs) # pylint: disable=missing-kwoa
+ question = _build_question(**kwargs)
question.answered_yes.connect(yes_action)
if no_action is not None:
question.answered_no.connect(no_action)
diff --git a/qutebrowser/utils/objreg.py b/qutebrowser/utils/objreg.py
index 0819a5d0a..c3cdb1071 100644
--- a/qutebrowser/utils/objreg.py
+++ b/qutebrowser/utils/objreg.py
@@ -170,7 +170,7 @@ def _get_tab_registry(win_id: _WindowTab,
window: Optional[QWidget] = QApplication.activeWindow()
if window is None or not hasattr(window, 'win_id'):
raise RegistryUnavailableError('tab')
- win_id = window.win_id
+ win_id = window.win_id # type: ignore[attr-defined]
elif win_id is None:
raise TypeError("window is None with scope tab!")
@@ -205,7 +205,7 @@ def _get_window_registry(window: _WindowTab) -> ObjectRegistry:
raise RegistryUnavailableError('window')
try:
- return win.registry
+ return win.registry # type: ignore[attr-defined]
except AttributeError:
raise RegistryUnavailableError('window')
diff --git a/qutebrowser/utils/resources.py b/qutebrowser/utils/resources.py
index ff5ec9d9a..b14b0faf4 100644
--- a/qutebrowser/utils/resources.py
+++ b/qutebrowser/utils/resources.py
@@ -24,19 +24,25 @@ import sys
import contextlib
import posixpath
import pathlib
-from typing import Iterator, Iterable
+from typing import Iterator, Iterable, Union
# We cannot use the stdlib version on 3.7-3.8 because we need the files() API.
if sys.version_info >= (3, 9):
import importlib.resources as importlib_resources
+ from importlib.abc import Traversable
else: # pragma: no cover
import importlib_resources
+ from importlib_resources.abc import Traversable
import qutebrowser
_cache = {}
-def _path(filename: str) -> pathlib.Path:
+
+_ResourceType = Union[Traversable, pathlib.Path]
+
+
+def _path(filename: str) -> _ResourceType:
"""Get a pathlib.Path object for a resource."""
assert not posixpath.isabs(filename), filename
assert os.path.pardir not in filename.split(posixpath.sep), filename
@@ -64,7 +70,7 @@ def _keyerror_workaround() -> Iterator[None]:
def _glob(
- resource_path: pathlib.Path,
+ resource_path: _ResourceType,
subdir: str,
ext: str,
) -> Iterable[str]:
@@ -77,14 +83,11 @@ def _glob(
glob_path = resource_path / subdir
if isinstance(resource_path, pathlib.Path):
+ assert isinstance(glob_path, pathlib.Path)
for full_path in glob_path.glob(f'*{ext}'): # . is contained in ext
yield full_path.relative_to(resource_path).as_posix()
- else: # zipfile.Path or importlib_resources compat object
- # Unfortunately, we can't tell mypy about resource_path being of type
- # Union[pathlib.Path, zipfile.Path] because we set "python_version = 3.6" in
- # .mypy.ini, but the zipfiel stubs (correctly) only declare zipfile.Path with
- # Python 3.8...
- assert glob_path.is_dir(), glob_path # type: ignore[unreachable]
+ else: # zipfile.Path or other importlib_resources.abc.Traversable
+ assert glob_path.is_dir(), glob_path
for subpath in glob_path.iterdir():
if subpath.name.endswith(ext):
yield posixpath.join(subdir, subpath.name)
diff --git a/qutebrowser/utils/standarddir.py b/qutebrowser/utils/standarddir.py
index f1bd01a5f..c753e9de6 100644
--- a/qutebrowser/utils/standarddir.py
+++ b/qutebrowser/utils/standarddir.py
@@ -147,7 +147,8 @@ def _init_data(args: Optional[argparse.Namespace]) -> None:
# system_data
_locations.pop(_Location.system_data, None) # Remove old state
if utils.is_linux:
- path = '/usr/share/' + APPNAME
+ prefix = '/app' if version.is_flatpak() else '/usr'
+ path = f'{prefix}/share/{APPNAME}'
if os.path.exists(path):
_locations[_Location.system_data] = path
diff --git a/qutebrowser/utils/urlutils.py b/qutebrowser/utils/urlutils.py
index 002f10411..cfba2c1d8 100644
--- a/qutebrowser/utils/urlutils.py
+++ b/qutebrowser/utils/urlutils.py
@@ -95,6 +95,7 @@ def _parse_search_term(s: str) -> Tuple[Optional[str], Optional[str]]:
engine = None
term = s
else:
+ # pylint: disable=else-if-used
if config.val.url.open_base_url and s in config.val.url.searchengines:
engine = s
term = None
@@ -328,6 +329,7 @@ def invalid_url_error(url: QUrl, action: str) -> None:
"""Display an error message for a URL.
Args:
+ url: The URL to display a message for.
action: The action which was interrupted by the error.
"""
if url.isValid():
diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py
index ee0f899cc..56c29899d 100644
--- a/qutebrowser/utils/usertypes.py
+++ b/qutebrowser/utils/usertypes.py
@@ -301,6 +301,7 @@ class Backend(enum.Enum):
"""The backend being used (usertypes.backend)."""
+ # pylint: disable=invalid-name
QtWebKit = enum.auto()
QtWebEngine = enum.auto()
diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py
index a56769255..77543f161 100644
--- a/qutebrowser/utils/utils.py
+++ b/qutebrowser/utils/utils.py
@@ -44,7 +44,7 @@ except ImportError: # pragma: no cover
"""Empty stub at runtime."""
-from PyQt5.QtCore import QUrl, QVersionNumber, QRect
+from PyQt5.QtCore import QUrl, QVersionNumber, QRect, QPoint
from PyQt5.QtGui import QClipboard, QDesktopServices
from PyQt5.QtWidgets import QApplication
@@ -382,7 +382,7 @@ def get_repr(obj: Any, constructor: bool = False, **attrs: Any) -> str:
obj: The object to get a repr for.
constructor: If True, show the Foo(one=1, two=2) form instead of
<Foo one=1 two=2>.
- attrs: The attributes to add.
+ **attrs: The attributes to add.
"""
cls = qualname(obj.__class__)
parts = []
@@ -391,11 +391,10 @@ def get_repr(obj: Any, constructor: bool = False, **attrs: Any) -> str:
parts.append('{}={!r}'.format(name, val))
if constructor:
return '{}({})'.format(cls, ', '.join(parts))
+ elif parts:
+ return '<{} {}>'.format(cls, ' '.join(parts))
else:
- if parts:
- return '<{} {}>'.format(cls, ' '.join(parts))
- else:
- return '<{}>'.format(cls)
+ return '<{}>'.format(cls)
def qualname(obj: Any) -> str:
@@ -669,11 +668,12 @@ def yaml_load(f: Union[str, IO[str]]) -> Any:
r"of from 'collections\.abc' is deprecated.*"):
try:
data = yaml.load(f, Loader=YamlLoader)
- except ValueError as e:
- if str(e).startswith('could not convert string to float'):
+ except ValueError as e: # pragma: no cover
+ pyyaml_error = 'could not convert string to float'
+ if str(e).startswith(pyyaml_error):
# WORKAROUND for https://github.com/yaml/pyyaml/issues/168
raise yaml.YAMLError(e)
- raise # pragma: no cover
+ raise
end = datetime.datetime.now()
@@ -784,30 +784,13 @@ def mimetype_extension(mimetype: str) -> Optional[str]:
This mostly delegates to Python's mimetypes.guess_extension(), but backports some
changes (via a simple override dict) which are missing from earlier Python versions.
- Most likely, this can be dropped once the minimum Python version is raised to 3.7.
+ Most likely, this can be dropped once the minimum Python version is raised to 3.10.
"""
overrides = {
+ # Added in 3.10
+ "application/x-hdf5": ".h5",
# Added around 3.8
"application/manifest+json": ".webmanifest",
- "application/x-hdf5": ".h5",
-
- # Added in Python 3.7
- "application/wasm": ".wasm",
-
- # Wrong values for Python 3.6
- # https://bugs.python.org/issue1043134
- # https://github.com/python/cpython/pull/14375
- "application/octet-stream": ".bin", # not .a
- "application/postscript": ".ps", # not .ai
- "application/vnd.ms-excel": ".xls", # not .xlb
- "application/vnd.ms-powerpoint": ".ppt", # not .pot
- "application/xml": ".xsl", # not .rdf
- "audio/mpeg": ".mp3", # not .mp2
- "image/jpeg": ".jpg", # not .jpe
- "image/tiff": ".tiff", # not .tif
- "text/html": ".html", # not .htm
- "text/plain": ".txt", # not .bat
- "video/mpeg": ".mpeg", # not .m1v
}
if mimetype in overrides:
return overrides[mimetype]
@@ -856,3 +839,16 @@ def parse_rect(s: str) -> QRect:
raise ValueError("Invalid rectangle")
return rect
+
+
+def parse_point(s: str) -> QPoint:
+ """Parse a point string like 13,-42."""
+ try:
+ x, y = map(int, s.split(',', maxsplit=1))
+ except ValueError:
+ raise ValueError(f"String {s} does not match X,Y")
+
+ try:
+ return QPoint(x, y)
+ except OverflowError as e:
+ raise ValueError(e)
diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py
index 8cd244fca..bf6b49fa6 100644
--- a/qutebrowser/utils/version.py
+++ b/qutebrowser/utils/version.py
@@ -511,10 +511,10 @@ def _get_pyqt_webengine_qt_version() -> Optional[str]:
give us an accurate answer.
"""
try:
- import importlib_metadata
+ import importlib.metadata as importlib_metadata # type: ignore[import]
except ImportError:
try:
- import importlib.metadata as importlib_metadata # type: ignore[no-redef]
+ import importlib_metadata # type: ignore[no-redef]
except ImportError:
log.misc.debug("Neither importlib.metadata nor backport available")
return None
@@ -723,7 +723,7 @@ class WebEngineVersions:
)
-def qtwebengine_versions(avoid_init: bool = False) -> WebEngineVersions:
+def qtwebengine_versions(*, avoid_init: bool = False) -> WebEngineVersions:
"""Get the QtWebEngine and Chromium version numbers.
If we have a parsed user agent, we use it here. If not, we avoid initializing
@@ -773,8 +773,6 @@ def _backend() -> str:
if objects.backend == usertypes.Backend.QtWebKit:
return 'new QtWebKit (WebKit {})'.format(qWebKitVersion())
elif objects.backend == usertypes.Backend.QtWebEngine:
- webengine = usertypes.Backend.QtWebEngine
- assert objects.backend == webengine, objects.backend
return str(qtwebengine_versions(
avoid_init='avoid-chromium-init' in objects.debug_flags))
raise utils.Unreachable(objects.backend)
diff --git a/requirements.txt b/requirements.txt
index e6b75b5ef..129fe402a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,13 +1,12 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-adblock==0.5.0
+adblock==0.5.2
colorama==0.4.4
-dataclasses==0.6 ; python_version<"3.7"
-importlib-metadata==4.6.1 ; python_version<"3.8"
-importlib-resources==5.2.0 ; python_version<"3.9"
-Jinja2==3.0.1
-MarkupSafe==2.0.1
-Pygments==2.9.0
-PyYAML==5.4.1
-typing-extensions==3.10.0.0
-zipp==3.5.0
+importlib-metadata==4.11.4 ; python_version=="3.7.*"
+importlib-resources==5.7.1 ; python_version=="3.7.*" or python_version=="3.8.*"
+Jinja2==3.1.2
+MarkupSafe==2.1.1
+Pygments==2.12.0
+PyYAML==6.0
+typing_extensions==4.2.0 ; python_version<"3.8"
+zipp==3.8.0
diff --git a/scripts/asciidoc2html.py b/scripts/asciidoc2html.py
index 0a6912b82..ba8493247 100755
--- a/scripts/asciidoc2html.py
+++ b/scripts/asciidoc2html.py
@@ -43,7 +43,10 @@ class AsciiDoc:
"""Abstraction of an asciidoc subprocess."""
- FILES = ['faq', 'changelog', 'contributing', 'quickstart', 'userscripts']
+ FILES = [
+ 'faq', 'changelog', 'contributing', 'quickstart', 'userscripts',
+ 'install', 'stacktrace'
+ ]
def __init__(self,
asciidoc: Optional[str],
@@ -196,7 +199,11 @@ class AsciiDoc:
continue
self._build_website_file(item_path.parent, item_path.name)
- copy = {'icons': 'icons', 'doc/img': 'doc/img', 'www/media': 'media/'}
+ copy = {
+ 'qutebrowser/icons': 'icons',
+ 'doc/img': 'doc/img',
+ 'www/media': 'media/',
+ }
for src, dest in copy.items():
full_src = REPO_ROOT / src
diff --git a/scripts/dev/build_release.py b/scripts/dev/build_release.py
index a1c6646eb..a4fbadf15 100755
--- a/scripts/dev/build_release.py
+++ b/scripts/dev/build_release.py
@@ -26,6 +26,7 @@ import os.path
import sys
import time
import shutil
+import pathlib
import plistlib
import subprocess
import argparse
@@ -227,7 +228,7 @@ def patch_mac_app():
# Replace some duplicate files by symlinks
framework_path = os.path.join(app_path, 'Contents', 'MacOS', 'PyQt5',
- 'Qt', 'lib', 'QtWebEngineCore.framework')
+ 'Qt5', 'lib', 'QtWebEngineCore.framework')
core_lib = os.path.join(framework_path, 'Versions', '5', 'QtWebEngineCore')
os.remove(core_lib)
@@ -301,7 +302,9 @@ def build_mac(*, gh_token, debug):
for d in ['dist', 'build']:
shutil.rmtree(d, ignore_errors=True)
utils.print_title("Updating 3rdparty content")
- update_3rdparty.run(ace=False, pdfjs=True, fancy_dmg=False, gh_token=gh_token)
+ # FIXME:qt6 Use modern PDF.js version here
+ update_3rdparty.run(ace=False, pdfjs=True, legacy_pdfjs=True, fancy_dmg=False,
+ gh_token=gh_token)
utils.print_title("Building .app via pyinstaller")
call_tox('pyinstaller-64', '-r', debug=debug)
utils.print_title("Patching .app")
@@ -394,8 +397,9 @@ def _build_windows_single(*, x64, skip_packaging, debug):
def build_windows(*, gh_token, skip_packaging, only_32bit, only_64bit, debug):
"""Build windows executables/setups."""
utils.print_title("Updating 3rdparty content")
- update_3rdparty.run(nsis=True, ace=False, pdfjs=True, fancy_dmg=False,
- gh_token=gh_token)
+ # FIXME:qt6 Use modern PDF.js version here
+ update_3rdparty.run(nsis=True, ace=False, pdfjs=True, legacy_pdfjs=True,
+ fancy_dmg=False, gh_token=gh_token)
utils.print_title("Building Windows binaries")
@@ -480,24 +484,27 @@ def build_sdist():
"""Build an sdist and list the contents."""
utils.print_title("Building sdist")
- _maybe_remove('dist')
+ dist_path = pathlib.Path('dist')
+ _maybe_remove(dist_path)
+
+ subprocess.run([sys.executable, '-m', 'build'], check=True)
- subprocess.run([sys.executable, 'setup.py', 'sdist'], check=True)
- dist_files = os.listdir(os.path.abspath('dist'))
- assert len(dist_files) == 1
+ dist_files = list(dist_path.glob('*.tar.gz'))
+ filename = 'qutebrowser-{}.tar.gz'.format(qutebrowser.__version__)
+ assert dist_files == [dist_path / filename], dist_files
+ dist_file = dist_files[0]
- dist_file = os.path.join('dist', dist_files[0])
- subprocess.run(['gpg', '--detach-sign', '-a', dist_file], check=True)
+ subprocess.run(['gpg', '--detach-sign', '-a', str(dist_file)], check=True)
- tar = tarfile.open(dist_file)
by_ext = collections.defaultdict(list)
- for tarinfo in tar.getmembers():
- if not tarinfo.isfile():
- continue
- name = os.sep.join(tarinfo.name.split(os.sep)[1:])
- _base, ext = os.path.splitext(name)
- by_ext[ext].append(name)
+ with tarfile.open(dist_file) as tar:
+ for tarinfo in tar.getmembers():
+ if not tarinfo.isfile():
+ continue
+ name = os.sep.join(tarinfo.name.split(os.sep)[1:])
+ _base, ext = os.path.splitext(name)
+ by_ext[ext].append(name)
assert '.pyc' not in by_ext
@@ -507,11 +514,13 @@ def build_sdist():
utils.print_subtitle(ext)
print('\n'.join(files))
- filename = 'qutebrowser-{}.tar.gz'.format(qutebrowser.__version__)
artifacts = [
- (os.path.join('dist', filename), 'application/gzip', 'Source release'),
- (os.path.join('dist', filename + '.asc'), 'application/pgp-signature',
- 'Source release - PGP signature'),
+ (str(dist_file), 'application/gzip', 'Source release'),
+ (
+ str(dist_file.with_suffix(dist_file.suffix + '.asc')),
+ 'application/pgp-signature',
+ 'Source release - PGP signature',
+ ),
]
return artifacts
@@ -550,6 +559,7 @@ def github_upload(artifacts, tag, gh_token):
Args:
artifacts: A list of (filename, mimetype, description) tuples
tag: The name of the release tag
+ gh_token: The GitHub token to use
"""
import github3
import github3.exceptions
@@ -589,9 +599,9 @@ def github_upload(artifacts, tag, gh_token):
assets = [asset for asset in release.assets()
if asset.name == basename]
if assets:
- asset = assets[0]
- print("Deleting stray asset {}".format(asset.name))
- asset.delete()
+ stray_asset = assets[0]
+ print("Deleting stray asset {}".format(stray_asset.name))
+ stray_asset.delete()
else:
break
@@ -599,15 +609,19 @@ def github_upload(artifacts, tag, gh_token):
def pypi_upload(artifacts):
"""Upload the given artifacts to PyPI using twine."""
utils.print_title("Uploading to PyPI...")
- filenames = [a[0] for a in artifacts]
- subprocess.run([sys.executable, '-m', 'twine', 'upload'] + filenames,
- check=True)
+ run_twine('upload', artifacts)
+
+
+def twine_check(artifacts):
+ """Check packages using 'twine check'."""
+ utils.print_title("Running twine check...")
+ run_twine('check', artifacts, '--strict')
-def upgrade_sdist_dependencies():
- """Make sure we have the latest tools for an sdist release."""
- subprocess.run([sys.executable, '-m', 'pip', 'install', '-U', 'twine',
- 'pip', 'wheel', 'setuptools'], check=True)
+def run_twine(command, artifacts, *args):
+ filenames = [a[0] for a in artifacts]
+ subprocess.run([sys.executable, '-m', 'twine', command] + list(args) + filenames,
+ check=True)
def main():
@@ -667,9 +681,9 @@ def main():
elif sys.platform == 'darwin':
artifacts = build_mac(gh_token=gh_token, debug=args.debug)
else:
- upgrade_sdist_dependencies()
test_makefile()
artifacts = build_sdist()
+ twine_check(artifacts)
upload_to_pypi = True
if args.upload:
diff --git a/scripts/dev/change_release.py b/scripts/dev/change_release.py
index 31398f9fa..b033b04ea 100644
--- a/scripts/dev/change_release.py
+++ b/scripts/dev/change_release.py
@@ -56,9 +56,9 @@ def change_release_description(release, filename, description):
"""Change a release description to the given new one."""
assets = [asset for asset in release.assets() if asset.name == filename]
if not assets:
- raise Error("No assets found for {filename}")
+ raise Error(f"No assets found for {filename}")
if len(assets) > 1:
- raise Error("Multiple assets found for {filename}: {assets}")
+ raise Error(f"Multiple assets found for {filename}: {assets}")
asset = assets[0]
asset.edit(filename, description)
diff --git a/scripts/dev/changelog_urls.json b/scripts/dev/changelog_urls.json
new file mode 100644
index 000000000..6b33f15ef
--- /dev/null
+++ b/scripts/dev/changelog_urls.json
@@ -0,0 +1,159 @@
+{
+ "pyparsing": "https://github.com/pyparsing/pyparsing/blob/master/CHANGES",
+ "pylint": "https://pylint.pycqa.org/en/latest/whatsnew/2/index.html",
+ "tomlkit": "https://github.com/sdispater/tomlkit/blob/master/CHANGELOG.md",
+ "dill": "https://github.com/uqfoundation/dill/commits/master",
+ "isort": "https://pycqa.github.io/isort/CHANGELOG/",
+ "lazy-object-proxy": "https://github.com/ionelmc/python-lazy-object-proxy/blob/master/CHANGELOG.rst",
+ "mccabe": "https://github.com/PyCQA/mccabe#changes",
+ "pytest-cov": "https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst",
+ "pytest-xdist": "https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst",
+ "pytest-forked": "https://github.com/pytest-dev/pytest-forked/blob/master/CHANGELOG.rst",
+ "pytest-xvfb": "https://github.com/The-Compiler/pytest-xvfb/blob/master/CHANGELOG.rst",
+ "PyVirtualDisplay": "https://github.com/ponty/PyVirtualDisplay/commits/master",
+ "execnet": "https://execnet.readthedocs.io/en/latest/changelog.html",
+ "pytest-rerunfailures": "https://github.com/pytest-dev/pytest-rerunfailures/blob/master/CHANGES.rst",
+ "pytest-repeat": "https://github.com/pytest-dev/pytest-repeat/blob/master/CHANGES.rst",
+ "requests": "https://github.com/psf/requests/blob/main/HISTORY.md",
+ "requests-file": "https://github.com/dashea/requests-file/blob/master/CHANGES.rst",
+ "Werkzeug": "https://werkzeug.palletsprojects.com/en/latest/changes/",
+ "click": "https://click.palletsprojects.com/en/latest/changes/",
+ "itsdangerous": "https://itsdangerous.palletsprojects.com/en/latest/changes/",
+ "parse-type": "https://github.com/jenisys/parse_type/blob/master/CHANGES.txt",
+ "sortedcontainers": "https://github.com/grantjenks/python-sortedcontainers/blob/master/HISTORY.rst",
+ "soupsieve": "https://facelessuser.github.io/soupsieve/about/changelog/",
+ "Flask": "https://flask.palletsprojects.com/en/latest/changes/",
+ "Mako": "https://docs.makotemplates.org/en/latest/changelog.html",
+ "glob2": "https://github.com/miracle2k/python-glob2/blob/master/CHANGES",
+ "hypothesis": "https://hypothesis.readthedocs.io/en/latest/changes.html",
+ "exceptiongroup": "https://github.com/agronholm/exceptiongroup/blob/main/CHANGES.rst",
+ "mypy": "https://mypy-lang.blogspot.com/",
+ "types-PyYAML": "https://github.com/python/typeshed/commits/master/stubs/PyYAML",
+ "pytest": "https://docs.pytest.org/en/latest/changelog.html",
+ "iniconfig": "https://github.com/RonnyPfannschmidt/iniconfig/blob/master/CHANGELOG",
+ "tox": "https://tox.readthedocs.io/en/latest/changelog.html",
+ "PyYAML": "https://github.com/yaml/pyyaml/blob/master/CHANGES",
+ "pytest-bdd": "https://github.com/pytest-dev/pytest-bdd/blob/master/CHANGES.rst",
+ "snowballstemmer": "https://github.com/snowballstem/snowball/blob/master/NEWS",
+ "virtualenv": "https://github.com/pypa/virtualenv/blob/main/docs/changelog.rst",
+ "packaging": "https://packaging.pypa.io/en/latest/changelog.html",
+ "build": "https://github.com/pypa/build/blob/main/CHANGELOG.rst",
+ "attrs": "https://www.attrs.org/en/stable/changelog.html",
+ "Jinja2": "https://jinja.palletsprojects.com/en/latest/changes/",
+ "MarkupSafe": "https://markupsafe.palletsprojects.com/en/latest/changes/",
+ "flake8": "https://github.com/PyCQA/flake8/tree/main/docs/source/release-notes",
+ "flake8-docstrings": "https://pypi.org/project/flake8-docstrings/",
+ "flake8-debugger": "https://github.com/JBKahn/flake8-debugger/",
+ "flake8-builtins": "https://github.com/gforcada/flake8-builtins/blob/master/CHANGES.rst",
+ "flake8-bugbear": "https://github.com/PyCQA/flake8-bugbear#change-log",
+ "flake8-tidy-imports": "https://github.com/adamchainz/flake8-tidy-imports/blob/master/HISTORY.rst",
+ "flake8-tuple": "https://github.com/ar4s/flake8_tuple/blob/master/HISTORY.rst",
+ "flake8-comprehensions": "https://github.com/adamchainz/flake8-comprehensions/blob/master/HISTORY.rst",
+ "flake8-copyright": "https://github.com/savoirfairelinux/flake8-copyright/blob/master/CHANGELOG.rst",
+ "flake8-deprecated": "https://github.com/gforcada/flake8-deprecated/blob/master/CHANGES.rst",
+ "flake8-future-import": "https://github.com/xZise/flake8-future-import#changes",
+ "flake8-mock": "https://github.com/aleGpereira/flake8-mock#changes",
+ "flake8-string-format": "https://github.com/xZise/flake8-string-format#changes",
+ "flake8-plugin-utils": "https://github.com/afonasev/flake8-plugin-utils#change-log",
+ "flake8-pytest-style": "https://github.com/m-burst/flake8-pytest-style#change-log",
+ "pep8-naming": "https://github.com/PyCQA/pep8-naming/blob/master/CHANGELOG.rst",
+ "pycodestyle": "https://github.com/PyCQA/pycodestyle/blob/master/CHANGES.txt",
+ "pyflakes": "https://github.com/PyCQA/pyflakes/blob/master/NEWS.rst",
+ "cffi": "https://github.com/python-cffi/release-doc/blob/master/doc/source/whatsnew.rst",
+ "astroid": "https://github.com/PyCQA/astroid/blob/main/ChangeLog",
+ "pytest-instafail": "https://github.com/pytest-dev/pytest-instafail/blob/master/CHANGES.rst",
+ "coverage": "https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst",
+ "colorama": "https://github.com/tartley/colorama/blob/master/CHANGELOG.rst",
+ "hunter": "https://github.com/ionelmc/python-hunter/blob/master/CHANGELOG.rst",
+ "uritemplate": "https://github.com/python-hyper/uritemplate/blob/master/HISTORY.rst",
+ "more-itertools": "https://github.com/erikrose/more-itertools/blob/master/docs/versions.rst",
+ "pydocstyle": "https://www.pydocstyle.org/en/latest/release_notes.html",
+ "Sphinx": "https://www.sphinx-doc.org/en/master/changes.html",
+ "Babel": "https://github.com/python-babel/babel/blob/master/CHANGES.rst",
+ "alabaster": "https://alabaster.readthedocs.io/en/latest/changelog.html",
+ "imagesize": "https://github.com/shibukawa/imagesize_py/commits/master",
+ "pytz": "https://mm.icann.org/pipermail/tz-announce/",
+ "sphinxcontrib-applehelp": "https://www.sphinx-doc.org/en/master/changes.html",
+ "sphinxcontrib-devhelp": "https://www.sphinx-doc.org/en/master/changes.html",
+ "sphinxcontrib-htmlhelp": "https://www.sphinx-doc.org/en/master/changes.html",
+ "sphinxcontrib-jsmath": "https://www.sphinx-doc.org/en/master/changes.html",
+ "sphinxcontrib-qthelp": "https://www.sphinx-doc.org/en/master/changes.html",
+ "sphinxcontrib-serializinghtml": "https://www.sphinx-doc.org/en/master/changes.html",
+ "jaraco.functools": "https://github.com/jaraco/jaraco.functools/blob/master/CHANGES.rst",
+ "parse": "https://github.com/r1chardj0n3s/parse#potential-gotchas",
+ "py": "https://py.readthedocs.io/en/latest/changelog.html#changelog",
+ "Pympler": "https://github.com/pympler/pympler/blob/master/CHANGELOG.md",
+ "pytest-mock": "https://github.com/pytest-dev/pytest-mock/blob/master/CHANGELOG.rst",
+ "pytest-qt": "https://github.com/pytest-dev/pytest-qt/blob/master/CHANGELOG.rst",
+ "pyinstaller": "https://pyinstaller.readthedocs.io/en/stable/CHANGES.html",
+ "pyinstaller-hooks-contrib": "https://github.com/pyinstaller/pyinstaller-hooks-contrib/blob/master/CHANGELOG.rst",
+ "pytest-benchmark": "https://pytest-benchmark.readthedocs.io/en/stable/changelog.html",
+ "typed-ast": "https://github.com/python/typed_ast/commits/master",
+ "docutils": "https://docutils.sourceforge.io/RELEASE-NOTES.html",
+ "bump2version": "https://github.com/c4urself/bump2version/blob/master/CHANGELOG.md",
+ "six": "https://github.com/benjaminp/six/blob/master/CHANGES",
+ "altgraph": "https://github.com/ronaldoussoren/altgraph/blob/master/doc/changelog.rst",
+ "urllib3": "https://github.com/urllib3/urllib3/blob/master/CHANGES.rst",
+ "lxml": "https://github.com/lxml/lxml/blob/master/CHANGES.txt",
+ "wrapt": "https://github.com/GrahamDumpleton/wrapt/blob/develop/docs/changes.rst",
+ "pep517": "https://github.com/pypa/pep517/blob/master/doc/changelog.rst",
+ "cryptography": "https://cryptography.io/en/latest/changelog.html",
+ "toml": "https://github.com/uiri/toml/releases",
+ "tomli": "https://github.com/hukkin/tomli/blob/master/CHANGELOG.md",
+ "PyQt5": "https://www.riverbankcomputing.com/news",
+ "PyQt5-Qt5": "https://www.riverbankcomputing.com/news",
+ "PyQtWebEngine": "https://www.riverbankcomputing.com/news",
+ "PyQtWebEngine-Qt5": "https://www.riverbankcomputing.com/news",
+ "PyQt-builder": "https://www.riverbankcomputing.com/news",
+ "PyQt5-sip": "https://www.riverbankcomputing.com/news",
+ "PyQt5-stubs": "https://github.com/stlehmann/PyQt5-stubs/blob/master/CHANGELOG.md",
+ "sip": "https://www.riverbankcomputing.com/news",
+ "Pygments": "https://pygments.org/docs/changelog/",
+ "vulture": "https://github.com/jendrikseipp/vulture/blob/master/CHANGELOG.md",
+ "distlib": "https://github.com/pypa/distlib/blob/master/CHANGES.rst",
+ "py-cpuinfo": "https://github.com/workhorsy/py-cpuinfo/blob/master/ChangeLog",
+ "cheroot": "https://cheroot.cherrypy.dev/en/latest/history.html",
+ "certifi": "https://ccadb-public.secure.force.com/mozilla/IncludedCACertificateReport",
+ "chardet": "https://github.com/chardet/chardet/releases",
+ "charset-normalizer": "https://github.com/Ousret/charset_normalizer/blob/master/CHANGELOG.md",
+ "idna": "https://github.com/kjd/idna/blob/master/HISTORY.rst",
+ "tldextract": "https://github.com/john-kurkowski/tldextract/blob/master/CHANGELOG.md",
+ "typing_extensions": "https://github.com/python/typing/blob/master/typing_extensions/CHANGELOG",
+ "diff-cover": "https://github.com/Bachmann1234/diff_cover/blob/master/CHANGELOG",
+ "beautifulsoup4": "https://bazaar.launchpad.net/~leonardr/beautifulsoup/bs4/view/head:/CHANGELOG",
+ "check-manifest": "https://github.com/mgedmin/check-manifest/blob/master/CHANGES.rst",
+ "yamllint": "https://github.com/adrienverge/yamllint/blob/master/CHANGELOG.rst",
+ "pathspec": "https://github.com/cpburnz/python-path-specification/blob/master/CHANGES.rst",
+ "filelock": "https://github.com/tox-dev/py-filelock/blob/main/docs/changelog.rst",
+ "github3.py": "https://github3.readthedocs.io/en/latest/release-notes/index.html",
+ "manhole": "https://github.com/ionelmc/python-manhole/blob/master/CHANGELOG.rst",
+ "pycparser": "https://github.com/eliben/pycparser/blob/master/CHANGES",
+ "python-dateutil": "https://dateutil.readthedocs.io/en/stable/changelog.html",
+ "platformdirs": "https://github.com/platformdirs/platformdirs/blob/main/CHANGES.rst",
+ "pluggy": "https://github.com/pytest-dev/pluggy/blob/master/CHANGELOG.rst",
+ "mypy-extensions": "https://github.com/python/mypy_extensions/commits/master",
+ "pyroma": "https://github.com/regebro/pyroma/blob/master/CHANGES.txt",
+ "adblock": "https://github.com/ArniDagur/python-adblock/blob/master/CHANGELOG.md",
+ "importlib-resources": "https://importlib-resources.readthedocs.io/en/latest/history.html",
+ "importlib-metadata": "https://github.com/python/importlib_metadata/blob/main/CHANGES.rst",
+ "zipp": "https://github.com/jaraco/zipp/blob/main/CHANGES.rst",
+ "pip": "https://pip.pypa.io/en/stable/news/",
+ "wheel": "https://wheel.readthedocs.io/en/stable/news.html",
+ "setuptools": "https://setuptools.readthedocs.io/en/latest/history.html",
+ "future": "https://python-future.org/whatsnew.html",
+ "pefile": "https://github.com/erocarrera/pefile/commits/master",
+ "SecretStorage": "https://github.com/mitya57/secretstorage/blob/master/changelog",
+ "bleach": "https://github.com/mozilla/bleach/blob/main/CHANGES",
+ "jeepney": "https://gitlab.com/takluyver/jeepney/-/blob/master/docs/release-notes.rst",
+ "keyring": "https://github.com/jaraco/keyring/blob/main/CHANGES.rst",
+ "pkginfo": "https://bazaar.launchpad.net/~tseaver/pkginfo/trunk/view/head:/CHANGES.txt",
+ "readme-renderer": "https://github.com/pypa/readme_renderer/blob/main/CHANGES.rst",
+ "requests-toolbelt": "https://github.com/requests/toolbelt/blob/master/HISTORY.rst",
+ "rfc3986": "https://rfc3986.readthedocs.io/en/latest/release-notes/index.html",
+ "twine": "https://twine.readthedocs.io/en/stable/changelog.html",
+ "webencodings": "https://github.com/gsnedders/python-webencodings/commits/master",
+ "PyJWT": "https://github.com/jpadilla/pyjwt/blob/master/CHANGELOG.rst",
+ "commonmark": "https://github.com/readthedocs/commonmark.py/blob/master/CHANGELOG.md",
+ "rich": "https://github.com/Textualize/rich/blob/master/CHANGELOG.md",
+ "ply": "https://github.com/dabeaz/ply/blob/master/CHANGES"
+}
diff --git a/scripts/dev/check_coverage.py b/scripts/dev/check_coverage.py
index c66cb3e8d..8f1d2df2b 100644
--- a/scripts/dev/check_coverage.py
+++ b/scripts/dev/check_coverage.py
@@ -333,7 +333,7 @@ def main_check():
subprocess.run([sys.executable, '-m', 'coverage', 'report',
'--show-missing', '--include', filters], check=True)
print()
- print("To debug this, run 'tox -e py36-pyqt515-cov' "
+ print("To debug this, run 'tox -e py39-pyqt515-cov' "
"(replace Python/Qt versions based on your system) locally and check "
"htmlcov/index.html")
print("or check https://codecov.io/github/qutebrowser/qutebrowser")
@@ -353,7 +353,7 @@ def main_check_all():
tests.
This runs pytest with the used executable, so check_coverage.py should be
- called with something like ./.tox/py36/bin/python.
+ called with something like ./.tox/py39/bin/python.
"""
for test_file, src_file in PERFECT_FILES:
if test_file is None:
diff --git a/scripts/dev/gen_resources.py b/scripts/dev/gen_resources.py
deleted file mode 100644
index dd76c123e..000000000
--- a/scripts/dev/gen_resources.py
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env python3
-# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
-
-# Copyright 2014-2021 Florian Bruhin (The-Compiler) <me@the-compiler.org>
-
-# this file is part of qutebrowser.
-#
-# qutebrowser is free software: you can redistribute it and/or modify
-# it under the terms of the gnu general public license as published by
-# the free software foundation, either version 3 of the license, or
-# (at your option) any later version.
-#
-# qutebrowser is distributed in the hope that it will be useful,
-# but without any warranty; without even the implied warranty of
-# merchantability or fitness for a particular purpose. see the
-# gnu general public license for more details.
-#
-# you should have received a copy of the gnu general public license
-# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>.
-
-"""Generate Qt resources based on source files."""
-
-import subprocess
-
-with open('qutebrowser/resources.py', 'w', encoding='utf-8') as f:
- subprocess.run(['pyrcc5', 'qutebrowser.rcc'], stdout=f, check=True)
diff --git a/scripts/dev/misc_checks.py b/scripts/dev/misc_checks.py
index bae51e372..908daad4d 100644
--- a/scripts/dev/misc_checks.py
+++ b/scripts/dev/misc_checks.py
@@ -50,7 +50,7 @@ def _get_files(
filenames = subprocess.run(
['git', 'ls-files', '--cached', '--others', '--exclude-standard', '-z'],
stdout=subprocess.PIPE,
- universal_newlines=True,
+ text=True,
check=True,
)
all_ignored = ignored or []
@@ -111,7 +111,7 @@ def check_changelog_urls(_args: argparse.Namespace = None) -> bool:
utils.print_col(f"Extra changelog URLs: {req_str}", 'red')
if not ok:
- print("Hint: Changelog URLs are in scripts/dev/recompile_requirements.py")
+ print("Hint: Changelog URLs are in scripts/dev/changelog_urls.json")
return ok
@@ -199,11 +199,6 @@ def check_spelling(args: argparse.Namespace) -> Optional[bool]:
"Don't use a blanket 'noqa', use something like 'noqa: X123' instead.",
),
(
- re.compile(r'# type: ignore[^\[]'),
- ("Don't use a blanket 'type: ignore', use something like "
- "'type: ignore[error-code]' instead."),
- ),
- (
re.compile(r'# type: (?!ignore(\[|$))'),
"Don't use type comments, use type annotations instead.",
),
@@ -342,7 +337,7 @@ def check_userscripts_descriptions(_args: argparse.Namespace = None) -> bool:
def check_userscript_shebangs(_args: argparse.Namespace) -> bool:
- """Check that we're using /usr/bin/env in shebangs."""
+ """Check that we're using /usr/bin/env in shebangs and scripts are executable."""
ok = True
folder = pathlib.Path('misc/userscripts')
@@ -350,6 +345,10 @@ def check_userscript_shebangs(_args: argparse.Namespace) -> bool:
if sub.is_dir() or sub.name == 'README.md':
continue
+ if not os.access(sub, os.X_OK):
+ print(f"{sub} is not marked as executable")
+ ok = False
+
with sub.open('r', encoding='utf-8') as f:
shebang = f.readline().rstrip('\n')
assert shebang.startswith('#!'), shebang
diff --git a/scripts/dev/pylint_checkers/qute_pylint/modeline.py b/scripts/dev/pylint_checkers/qute_pylint/modeline.py
index 114cfaf94..1df2c375e 100644
--- a/scripts/dev/pylint_checkers/qute_pylint/modeline.py
+++ b/scripts/dev/pylint_checkers/qute_pylint/modeline.py
@@ -31,9 +31,9 @@ class ModelineChecker(checkers.BaseChecker):
__implements__ = interfaces.IRawChecker
name = 'modeline'
- msgs = {'W9002': ('Does not have vim modeline', 'modeline-missing', None),
- 'W9003': ('Modeline is invalid', 'invalid-modeline', None),
- 'W9004': ('Modeline position is wrong', 'modeline-position', None)}
+ msgs = {'W9102': ('Does not have vim modeline', 'modeline-missing', None),
+ 'W9103': ('Modeline is invalid', 'invalid-modeline', None),
+ 'W9104': ('Modeline position is wrong', 'modeline-position', None)}
options = ()
priority = -1
diff --git a/scripts/dev/pylint_checkers/qute_pylint/openencoding.py b/scripts/dev/pylint_checkers/qute_pylint/openencoding.py
deleted file mode 100644
index 972a55db8..000000000
--- a/scripts/dev/pylint_checkers/qute_pylint/openencoding.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
-
-# Copyright 2014-2021 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
-
-# This file is part of qutebrowser.
-#
-# qutebrowser is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# qutebrowser is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>.
-
-"""Make sure open() has an encoding set."""
-
-import astroid
-from pylint import interfaces, checkers
-from pylint.checkers import utils
-
-
-class OpenEncodingChecker(checkers.BaseChecker):
-
- """Checker to check open() has an encoding set."""
-
- __implements__ = interfaces.IAstroidChecker
- name = 'open-encoding'
-
- msgs = {
- 'W9400': ('open() called without encoding', 'open-without-encoding',
- None),
- }
-
- @utils.check_messages('open-without-encoding')
- def visit_call(self, node):
- """Visit a Call node."""
- if hasattr(node, 'func'):
- infer = utils.safe_infer(node.func)
- if infer and infer.root().name == '_io':
- if getattr(node.func, 'name', None) in ['open', 'file']:
- self._check_open_encoding(node)
-
- def _check_open_encoding(self, node):
- """Check that an open() call always has an encoding set."""
- try:
- mode_arg = utils.get_argument_from_call(node, position=1,
- keyword='mode')
- except utils.NoSuchArgumentError:
- mode_arg = None
- _encoding = None
- try:
- _encoding = utils.get_argument_from_call(node, position=2)
- except utils.NoSuchArgumentError:
- try:
- _encoding = utils.get_argument_from_call(node,
- keyword='encoding')
- except utils.NoSuchArgumentError:
- pass
- if _encoding is None:
- if mode_arg is None:
- mode = None
- else:
- mode = utils.safe_infer(mode_arg)
- if mode is not None and not isinstance(mode, astroid.Const):
- # We can't say what mode is exactly.
- return
- if mode is None:
- self.add_message('open-without-encoding', node=node)
- elif 'b' in getattr(mode, 'value', ''):
- # Files opened as binary don't need an encoding.
- return
- else:
- self.add_message('open-without-encoding', node=node)
-
-
-def register(linter):
- """Register this checker."""
- linter.register_checker(OpenEncodingChecker(linter))
diff --git a/scripts/dev/recompile_requirements.py b/scripts/dev/recompile_requirements.py
index 158741e5c..365f9a51e 100644
--- a/scripts/dev/recompile_requirements.py
+++ b/scripts/dev/recompile_requirements.py
@@ -24,9 +24,12 @@ import re
import sys
import os.path
import glob
+import json
import subprocess
import tempfile
import argparse
+import shutil
+import pathlib
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir,
os.pardir))
@@ -37,157 +40,8 @@ REPO_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
'..', '..') # /scripts/dev -> /scripts -> /
REQ_DIR = os.path.join(REPO_DIR, 'misc', 'requirements')
-CHANGELOG_URLS = {
- 'pyparsing': 'https://github.com/pyparsing/pyparsing/blob/master/CHANGES',
- 'pylint': 'https://pylint.pycqa.org/en/latest/whatsnew/changelog.html',
- 'isort': 'https://pycqa.github.io/isort/CHANGELOG/',
- 'lazy-object-proxy': 'https://github.com/ionelmc/python-lazy-object-proxy/blob/master/CHANGELOG.rst',
- 'mccabe': 'https://github.com/PyCQA/mccabe#changes',
- 'pytest-cov': 'https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst',
- 'pytest-xdist': 'https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst',
- 'pytest-forked': 'https://github.com/pytest-dev/pytest-forked/blob/master/CHANGELOG',
- 'pytest-xvfb': 'https://github.com/The-Compiler/pytest-xvfb/blob/master/CHANGELOG.rst',
- 'EasyProcess': 'https://github.com/ponty/EasyProcess/commits/master',
- 'PyVirtualDisplay': 'https://github.com/ponty/PyVirtualDisplay/commits/master',
- 'execnet': 'https://execnet.readthedocs.io/en/latest/changelog.html',
- 'pytest-rerunfailures': 'https://github.com/pytest-dev/pytest-rerunfailures/blob/master/CHANGES.rst',
- 'pytest-repeat': 'https://github.com/pytest-dev/pytest-repeat/blob/master/CHANGES.rst',
- 'requests': 'https://github.com/psf/requests/blob/master/HISTORY.md',
- 'requests-file': 'https://github.com/dashea/requests-file/blob/master/CHANGES.rst',
- 'Werkzeug': 'https://werkzeug.palletsprojects.com/en/latest/changes/',
- 'click': 'https://click.palletsprojects.com/en/latest/changes/',
- 'itsdangerous': 'https://itsdangerous.palletsprojects.com/en/latest/changes/',
- 'parse-type': 'https://github.com/jenisys/parse_type/blob/master/CHANGES.txt',
- 'sortedcontainers': 'https://github.com/grantjenks/python-sortedcontainers/blob/master/HISTORY.rst',
- 'soupsieve': 'https://facelessuser.github.io/soupsieve/about/changelog/',
- 'Flask': 'https://flask.palletsprojects.com/en/latest/changes/',
- 'Mako': 'https://docs.makotemplates.org/en/latest/changelog.html',
- 'glob2': 'https://github.com/miracle2k/python-glob2/blob/master/CHANGES',
- 'hypothesis': 'https://hypothesis.readthedocs.io/en/latest/changes.html',
- 'mypy': 'https://mypy-lang.blogspot.com/',
- 'types-PyYAML': 'https://github.com/python/typeshed/commits/master/stubs/PyYAML',
- 'types-dataclasses': 'https://github.com/python/typeshed/commits/master/stubs/dataclasses',
- 'pytest': 'https://docs.pytest.org/en/latest/changelog.html',
- 'iniconfig': 'https://github.com/RonnyPfannschmidt/iniconfig/blob/master/CHANGELOG',
- 'tox': 'https://tox.readthedocs.io/en/latest/changelog.html',
- 'PyYAML': 'https://github.com/yaml/pyyaml/blob/master/CHANGES',
- 'pytest-bdd': 'https://github.com/pytest-dev/pytest-bdd/blob/master/CHANGES.rst',
- 'snowballstemmer': 'https://github.com/snowballstem/snowball/blob/master/NEWS',
- 'virtualenv': 'https://virtualenv.pypa.io/en/latest/changelog.html',
- 'packaging': 'https://packaging.pypa.io/en/latest/changelog.html',
- 'build': 'https://github.com/pypa/build/blob/main/CHANGELOG.rst',
- 'attrs': 'https://www.attrs.org/en/stable/changelog.html',
- 'Jinja2': 'https://jinja.palletsprojects.com/en/latest/changes/',
- 'MarkupSafe': 'https://markupsafe.palletsprojects.com/en/latest/changes/',
- 'flake8': 'https://gitlab.com/pycqa/flake8/tree/master/docs/source/release-notes',
- 'flake8-docstrings': 'https://pypi.org/project/flake8-docstrings/',
- 'flake8-debugger': 'https://github.com/JBKahn/flake8-debugger/',
- 'flake8-builtins': 'https://github.com/gforcada/flake8-builtins/blob/master/CHANGES.rst',
- 'flake8-bugbear': 'https://github.com/PyCQA/flake8-bugbear#change-log',
- 'flake8-tidy-imports': 'https://github.com/adamchainz/flake8-tidy-imports/blob/master/HISTORY.rst',
- 'flake8-tuple': 'https://github.com/ar4s/flake8_tuple/blob/master/HISTORY.rst',
- 'flake8-comprehensions': 'https://github.com/adamchainz/flake8-comprehensions/blob/master/HISTORY.rst',
- 'flake8-copyright': 'https://github.com/savoirfairelinux/flake8-copyright/blob/master/CHANGELOG.rst',
- 'flake8-deprecated': 'https://github.com/gforcada/flake8-deprecated/blob/master/CHANGES.rst',
- 'flake8-future-import': 'https://github.com/xZise/flake8-future-import#changes',
- 'flake8-mock': 'https://github.com/aleGpereira/flake8-mock#changes',
- 'flake8-polyfill': 'https://gitlab.com/pycqa/flake8-polyfill/-/blob/master/CHANGELOG.rst',
- 'flake8-string-format': 'https://github.com/xZise/flake8-string-format#changes',
- 'pep8-naming': 'https://github.com/PyCQA/pep8-naming/blob/master/CHANGELOG.rst',
- 'pycodestyle': 'https://github.com/PyCQA/pycodestyle/blob/master/CHANGES.txt',
- 'pyflakes': 'https://github.com/PyCQA/pyflakes/blob/master/NEWS.rst',
- 'cffi': 'https://github.com/python-cffi/release-doc/blob/master/doc/source/whatsnew.rst',
- 'astroid': 'https://github.com/PyCQA/astroid/blob/2.4/ChangeLog',
- 'pytest-instafail': 'https://github.com/pytest-dev/pytest-instafail/blob/master/CHANGES.rst',
- 'coverage': 'https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst',
- 'colorama': 'https://github.com/tartley/colorama/blob/master/CHANGELOG.rst',
- 'hunter': 'https://github.com/ionelmc/python-hunter/blob/master/CHANGELOG.rst',
- 'uritemplate': 'https://github.com/python-hyper/uritemplate/blob/master/HISTORY.rst',
- 'more-itertools': 'https://github.com/erikrose/more-itertools/blob/master/docs/versions.rst',
- 'pydocstyle': 'https://www.pydocstyle.org/en/latest/release_notes.html',
- 'Sphinx': 'https://www.sphinx-doc.org/en/master/changes.html',
- 'Babel': 'https://github.com/python-babel/babel/blob/master/CHANGES',
- 'alabaster': 'https://alabaster.readthedocs.io/en/latest/changelog.html',
- 'imagesize': 'https://github.com/shibukawa/imagesize_py/commits/master',
- 'pytz': 'https://mm.icann.org/pipermail/tz-announce/',
- 'sphinxcontrib-applehelp': 'https://www.sphinx-doc.org/en/master/changes.html',
- 'sphinxcontrib-devhelp': 'https://www.sphinx-doc.org/en/master/changes.html',
- 'sphinxcontrib-htmlhelp': 'https://www.sphinx-doc.org/en/master/changes.html',
- 'sphinxcontrib-jsmath': 'https://www.sphinx-doc.org/en/master/changes.html',
- 'sphinxcontrib-qthelp': 'https://www.sphinx-doc.org/en/master/changes.html',
- 'sphinxcontrib-serializinghtml': 'https://www.sphinx-doc.org/en/master/changes.html',
- 'jaraco.functools': 'https://github.com/jaraco/jaraco.functools/blob/master/CHANGES.rst',
- 'parse': 'https://github.com/r1chardj0n3s/parse#potential-gotchas',
- 'py': 'https://py.readthedocs.io/en/latest/changelog.html#changelog',
- 'Pympler': 'https://github.com/pympler/pympler/blob/master/CHANGELOG.md',
- 'pytest-mock': 'https://github.com/pytest-dev/pytest-mock/blob/master/CHANGELOG.rst',
- 'pytest-qt': 'https://github.com/pytest-dev/pytest-qt/blob/master/CHANGELOG.rst',
- 'pyinstaller': 'https://pyinstaller.readthedocs.io/en/stable/CHANGES.html',
- 'pyinstaller-hooks-contrib': 'https://github.com/pyinstaller/pyinstaller-hooks-contrib/blob/master/CHANGELOG.rst',
- 'pytest-benchmark': 'https://pytest-benchmark.readthedocs.io/en/stable/changelog.html',
- 'typed-ast': 'https://github.com/python/typed_ast/commits/master',
- 'docutils': 'https://docutils.sourceforge.io/RELEASE-NOTES.html',
- 'bump2version': 'https://github.com/c4urself/bump2version/blob/master/CHANGELOG.md',
- 'six': 'https://github.com/benjaminp/six/blob/master/CHANGES',
- 'altgraph': 'https://github.com/ronaldoussoren/altgraph/blob/master/doc/changelog.rst',
- 'urllib3': 'https://github.com/urllib3/urllib3/blob/master/CHANGES.rst',
- 'lxml': 'https://lxml.de/index.html#old-versions',
- 'jwcrypto': 'https://github.com/latchset/jwcrypto/commits/master',
- 'wrapt': 'https://github.com/GrahamDumpleton/wrapt/blob/develop/docs/changes.rst',
- 'pep517': 'https://github.com/pypa/pep517/blob/master/doc/changelog.rst',
- 'cryptography': 'https://cryptography.io/en/latest/changelog.html',
- 'toml': 'https://github.com/uiri/toml/releases',
- 'PyQt5': 'https://www.riverbankcomputing.com/news',
- 'PyQt5-Qt': 'https://www.riverbankcomputing.com/news',
- 'PyQt5-Qt5': 'https://www.riverbankcomputing.com/news',
- 'PyQtWebEngine': 'https://www.riverbankcomputing.com/news',
- 'PyQtWebEngine-Qt': 'https://www.riverbankcomputing.com/news',
- 'PyQtWebEngine-Qt5': 'https://www.riverbankcomputing.com/news',
- 'PyQt-builder': 'https://www.riverbankcomputing.com/news',
- 'PyQt5-sip': 'https://www.riverbankcomputing.com/news',
- 'PyQt5-stubs': 'https://github.com/stlehmann/PyQt5-stubs/blob/master/CHANGELOG.md',
- 'sip': 'https://www.riverbankcomputing.com/news',
- 'Pygments': 'https://pygments.org/docs/changelog/',
- 'vulture': 'https://github.com/jendrikseipp/vulture/blob/master/CHANGELOG.md',
- 'distlib': 'https://bitbucket.org/pypa/distlib/src/master/CHANGES.rst',
- 'py-cpuinfo': 'https://github.com/workhorsy/py-cpuinfo/blob/master/ChangeLog',
- 'cheroot': 'https://cheroot.cherrypy.org/en/latest/history.html',
- 'certifi': 'https://ccadb-public.secure.force.com/mozilla/IncludedCACertificateReport',
- 'chardet': 'https://github.com/chardet/chardet/releases',
- 'idna': 'https://github.com/kjd/idna/blob/master/HISTORY.rst',
- 'tldextract': 'https://github.com/john-kurkowski/tldextract/blob/master/CHANGELOG.md',
- 'typing-extensions': 'https://github.com/python/typing/commits/master/typing_extensions',
- 'diff-cover': 'https://github.com/Bachmann1234/diff_cover/blob/master/CHANGELOG',
- 'pytest-icdiff': 'https://github.com/hjwp/pytest-icdiff/blob/master/HISTORY.rst',
- 'icdiff': 'https://github.com/jeffkaufman/icdiff/blob/master/ChangeLog',
- 'pprintpp': 'https://github.com/wolever/pprintpp/blob/master/CHANGELOG.txt',
- 'beautifulsoup4': 'https://bazaar.launchpad.net/~leonardr/beautifulsoup/bs4/view/head:/CHANGELOG',
- 'check-manifest': 'https://github.com/mgedmin/check-manifest/blob/master/CHANGES.rst',
- 'yamllint': 'https://github.com/adrienverge/yamllint/blob/master/CHANGELOG.rst',
- 'pathspec': 'https://github.com/cpburnz/python-path-specification/blob/master/CHANGES.rst',
- 'filelock': 'https://github.com/benediktschmitt/py-filelock/commits/master',
- 'github3.py': 'https://github3py.readthedocs.io/en/master/release-notes/index.html',
- 'manhole': 'https://github.com/ionelmc/python-manhole/blob/master/CHANGELOG.rst',
- 'pycparser': 'https://github.com/eliben/pycparser/blob/master/CHANGES',
- 'python-dateutil': 'https://dateutil.readthedocs.io/en/stable/changelog.html',
- 'appdirs': 'https://github.com/ActiveState/appdirs/blob/master/CHANGES.rst',
- 'pluggy': 'https://github.com/pytest-dev/pluggy/blob/master/CHANGELOG.rst',
- 'inflect': 'https://github.com/jazzband/inflect/blob/master/CHANGES.rst',
- 'jinja2-pluralize': 'https://github.com/audreyfeldroy/jinja2_pluralize/blob/master/HISTORY.rst',
- 'mypy-extensions': 'https://github.com/python/mypy_extensions/commits/master',
- 'pyroma': 'https://github.com/regebro/pyroma/blob/master/HISTORY.txt',
- 'adblock': 'https://github.com/ArniDagur/python-adblock/blob/master/CHANGELOG.md',
- 'importlib-resources': 'https://importlib-resources.readthedocs.io/en/latest/history.html',
- 'importlib-metadata': 'https://github.com/python/importlib_metadata/blob/main/CHANGES.rst',
- 'zipp': 'https://github.com/jaraco/zipp/blob/main/CHANGES.rst',
- 'dataclasses': 'https://github.com/ericvsmith/dataclasses#release-history',
- 'pip': 'https://pip.pypa.io/en/stable/news/',
- 'wheel': 'https://wheel.readthedocs.io/en/stable/news.html',
- 'setuptools': 'https://setuptools.readthedocs.io/en/latest/history.html',
- 'future': 'https://python-future.org/whatsnew.html',
- 'pefile': 'https://github.com/erocarrera/pefile/commits/master',
- 'Deprecated': 'https://github.com/tantale/deprecated/blob/master/CHANGELOG.rst',
-}
+CHANGELOG_URLS_PATH = pathlib.Path(__file__).parent / "changelog_urls.json"
+CHANGELOG_URLS = json.loads(CHANGELOG_URLS_PATH.read_text())
def convert_line(line, comments):
@@ -235,6 +89,7 @@ def read_comments(fobj):
'add': [],
'replace': {},
'pre': False,
+ 'pip_args': [],
}
for line in fobj:
if line.startswith('#@'):
@@ -264,6 +119,8 @@ def read_comments(fobj):
comments['add'].append(args)
elif command == 'pre':
comments['pre'] = True
+ elif command == 'pip_args':
+ comments['pip_args'] += args.split()
return comments
@@ -287,7 +144,7 @@ def run_pip(venv_dir, *args, quiet=False, **kwargs):
return subprocess.run([venv_python, '-m', 'pip'] + args, check=True, **kwargs)
-def init_venv(host_python, venv_dir, requirements, pre=False):
+def init_venv(host_python, venv_dir, requirements, pre=False, pip_args=None):
"""Initialize a new virtualenv and install the given packages."""
with utils.gha_group('Creating virtualenv'):
utils.print_col('$ python3 -m venv {}'.format(venv_dir), 'blue')
@@ -299,6 +156,8 @@ def init_venv(host_python, venv_dir, requirements, pre=False):
install_command = ['install', '-r', requirements]
if pre:
install_command.append('--pre')
+ if pip_args:
+ install_command += pip_args
with utils.gha_group('Installing requirements'):
run_pip(venv_dir, *install_command)
@@ -329,10 +188,11 @@ class Change:
"""A single requirements change from a git diff output."""
- def __init__(self, name):
+ def __init__(self, name: str, base_path: pathlib.Path) -> None:
self.name = name
self.old = None
self.new = None
+ self.base = extract_requirement_name(base_path)
if CHANGELOG_URLS.get(name):
self.url = CHANGELOG_URLS[name]
self.link = '[{}]({})'.format(self.name, self.url)
@@ -340,24 +200,27 @@ class Change:
self.url = '(no changelog)'
self.link = self.name
+ def __lt__(self, other):
+ return (self.base, self.name.lower()) < (other.base, other.name.lower())
+
def __str__(self):
+ prefix = f"- [{self.base}] {self.name}"
+ suffix = f" {self.url}"
if self.old is None:
- return '- {} new: {} {}'.format(self.name, self.new, self.url)
+ return f"{prefix} new: {self.new} {suffix}"
elif self.new is None:
- return '- {} removed: {} {}'.format(self.name, self.old,
- self.url)
+ return f"{prefix} removed: {self.old} {suffix}"
else:
- return '- {} {} -> {} {}'.format(self.name, self.old, self.new,
- self.url)
+ return f"{prefix} {self.old} -> {self.new} {suffix}"
def table_str(self):
"""Generate a markdown table."""
if self.old is None:
- return '| {} | -- | {} |'.format(self.link, self.new)
+ return f'| {self.base} | {self.link} | -- | {self.new} |'
elif self.new is None:
- return '| {} | {} | -- |'.format(self.link, self.old)
+ return f'| {self.base} | {self.link} | {self.old} | -- |'
else:
- return '| {} | {} | {} |'.format(self.link, self.old, self.new)
+ return f'| {self.base} | {self.link} | {self.old} | {self.new} |'
def _get_changed_files():
@@ -365,25 +228,43 @@ def _get_changed_files():
changed_files = set()
filenames = git_diff('--name-only')
for filename in filenames:
- filename = filename.strip()
- filename = filename.replace('misc/requirements/requirements-', '')
- filename = filename.replace('.txt', '')
- changed_files.add(filename)
+ requirement_name = extract_requirement_name(pathlib.Path(filename))
+ changed_files.add(requirement_name)
return sorted(changed_files)
+def extract_requirement_name(path: pathlib.Path) -> str:
+ """Get a requirement name from a file path.
+
+ e.g. "pylint" from "misc/requirements/requirements-pylint.txt"
+ """
+ if path == pathlib.Path("requirements.txt"):
+ return "qutebrowser"
+
+ prefix = "requirements-"
+ assert path.suffix == ".txt", path
+ assert path.stem.startswith(prefix), path
+ return path.stem[len(prefix):]
+
+
def parse_versioned_line(line):
"""Parse a requirements.txt line into name/version."""
- if '==' in line:
- if line[0] == '#': # ignored dependency
- line = line[1:].strip()
+ if line[0] == '#': # ignored dependency
+ line = line[1:].strip()
+
+ # Strip comments and pip environment markers
+ line = line.rsplit('#', maxsplit=1)[0]
+ line = line.split(';')[0].strip()
- # Strip comments and pip environment markers
- line = line.rsplit('#', maxsplit=1)[0]
- line = line.split(';')[0].strip()
+ ops = ["==", "~=", "!=", ">", "<", ">=", "<="]
- name, version = line.split('==')
+ if any(op in line for op in ops):
+ # strictly speaking, this version isn't necessarily correct, but it's
+ # enough for the table.
+ for op in ops:
+ if op in line:
+ name, version = line.split(op)
elif line.startswith('-e'):
rest, name = line.split('#egg=')
version = rest.split('@')[1][:7]
@@ -400,10 +281,19 @@ def parse_versioned_line(line):
def _get_changes(diff):
"""Get a list of changed versions from git."""
changes_dict = {}
+ current_path = None
+
for line in diff:
if not line.startswith('-') and not line.startswith('+'):
continue
- elif line.startswith('+++ ') or line.startswith('--- '):
+ elif line.startswith('--- '):
+ prefix = '--- a/'
+ current_path = pathlib.Path(line[len(prefix):])
+ continue
+ elif line.startswith('+++ '):
+ prefix = '+++ b/'
+ new_path = pathlib.Path(line[len(prefix):])
+ assert current_path == new_path, (current_path, new_path)
continue
elif not line.strip():
# Could be newline changes on Windows
@@ -415,14 +305,14 @@ def _get_changes(diff):
name, version = parse_versioned_line(line[1:])
if name not in changes_dict:
- changes_dict[name] = Change(name)
+ changes_dict[name] = Change(name, base_path=current_path)
if line.startswith('-'):
changes_dict[name].old = version
elif line.startswith('+'):
changes_dict[name].new = version
- return [change for _name, change in sorted(changes_dict.items())]
+ return sorted(changes_dict.values())
def print_changed_files():
@@ -449,8 +339,8 @@ def print_changed_files():
print('::set-output name=changed::' +
files_text.replace('\n', '%0A'))
table_header = [
- '| Requirement | old | new |',
- '|-------------|-----|-----|',
+ '| File | Requirement | old | new |',
+ '|------|-------------|-----|-----|',
]
diff_table = '%0A'.join(table_header +
[change.table_str() for change in changes])
@@ -494,7 +384,8 @@ def build_requirements(name):
init_venv(host_python=host_python,
venv_dir=tmpdir,
requirements=filename,
- pre=comments['pre'])
+ pre=comments['pre'],
+ pip_args=comments['pip_args'])
with utils.gha_group('Freezing requirements'):
args = ['--all'] if name == 'tox' else []
proc = run_pip(tmpdir, 'freeze', *args, stdout=subprocess.PIPE)
@@ -531,7 +422,7 @@ def test_tox():
list_proc = subprocess.run([venv_python, '-m', 'tox', '--listenvs'],
check=True,
stdout=subprocess.PIPE,
- universal_newlines=True)
+ text=True)
environments = list_proc.stdout.strip().split('\n')
for env in environments:
with utils.gha_group('tox for {}'.format(env)):
@@ -553,9 +444,20 @@ def test_requirements(name, outfile, *, force=False):
print(f"Skipping test as there were no changes for {name}.")
return
+ in_file = os.path.join(REQ_DIR, 'requirements-{}.txt-raw'.format(name))
+ with open(in_file, 'r', encoding='utf-8') as f:
+ comments = read_comments(f)
+
host_python = get_host_python(name)
with tempfile.TemporaryDirectory() as tmpdir:
- init_venv(host_python, tmpdir, outfile)
+ init_venv(host_python, tmpdir, outfile, pip_args=comments['pip_args'])
+
+
+def cleanup_pylint_build():
+ """Clean up pylint_checkers build files."""
+ path = pathlib.Path(__file__).parent / 'pylint_checkers' / 'build'
+ utils.print_col(f'$ rm -r {path}', 'blue')
+ shutil.rmtree(path)
def main():
@@ -571,6 +473,8 @@ def main():
utils.print_title(name)
outfile = build_requirements(name)
test_requirements(name, outfile, force=args.force_test)
+ if name == 'pylint':
+ cleanup_pylint_build()
utils.print_title('Testing via tox')
if args.names and not args.force_test:
diff --git a/scripts/dev/run_pylint_on_tests.py b/scripts/dev/run_pylint_on_tests.py
index d0385bd17..e044de976 100644
--- a/scripts/dev/run_pylint_on_tests.py
+++ b/scripts/dev/run_pylint_on_tests.py
@@ -59,8 +59,13 @@ def main():
'len-as-condition',
'compare-to-empty-string',
'pointless-statement',
+ 'use-implicit-booleaness-not-comparison',
# directories without __init__.py...
'import-error',
+ # tests/helpers imports
+ 'wrong-import-order',
+ # __tracebackhide__
+ 'unnecessary-lambda-assignment',
]
toxinidir = sys.argv[1]
diff --git a/scripts/dev/src2asciidoc.py b/scripts/dev/src2asciidoc.py
index 82bbdb167..1267a278a 100755
--- a/scripts/dev/src2asciidoc.py
+++ b/scripts/dev/src2asciidoc.py
@@ -176,13 +176,15 @@ def _get_setting_quickref():
def _get_configtypes():
"""Get configtypes classes to document."""
- predicate = lambda e: (
- inspect.isclass(e) and
- # pylint: disable=protected-access
- e not in [configtypes.BaseType, configtypes.MappingType,
- configtypes._Numeric, configtypes.FontBase] and
- # pylint: enable=protected-access
- issubclass(e, configtypes.BaseType))
+ def predicate(e):
+ return (
+ inspect.isclass(e) and
+ # pylint: disable=protected-access
+ e not in [configtypes.BaseType, configtypes.MappingType,
+ configtypes._Numeric, configtypes.FontBase] and
+ # pylint: enable=protected-access
+ issubclass(e, configtypes.BaseType)
+ )
yield from inspect.getmembers(configtypes, predicate)
@@ -444,7 +446,7 @@ def _generate_setting_option(f, opt):
if opt.restart:
f.write("\nThis setting requires a restart.\n")
if opt.supports_pattern:
- f.write("\nThis setting supports URL patterns.\n")
+ f.write("\nThis setting supports link:configuring{outfilesuffix}#patterns[URL patterns].\n")
if opt.no_autoconfig:
f.write("\nThis setting can only be set in config.py.\n")
_generate_setting_backend_info(f, opt)
@@ -536,7 +538,9 @@ def regenerate_manpage(filename):
# pylint: disable=protected-access
for group in parser._action_groups:
groupdata = []
- groupdata.append('=== {}'.format(group.title))
+ # https://bugs.python.org/issue9694 backport
+ title = "options" if group.title == "optional arguments" else group.title
+ groupdata.append('=== {}'.format(title))
if group.description is not None:
groupdata.append(group.description)
for action in group._group_actions:
diff --git a/scripts/dev/update_3rdparty.py b/scripts/dev/update_3rdparty.py
index 88f56b7f3..b1991fa1f 100755
--- a/scripts/dev/update_3rdparty.py
+++ b/scripts/dev/update_3rdparty.py
@@ -64,7 +64,20 @@ def download_nsis_plugins():
urllib.request.urlcleanup()
-def get_latest_pdfjs_url(gh_token):
+def find_pdfjs_asset(assets, legacy):
+ """Find the PDF.js asset to use."""
+ for asset in assets:
+ name = asset["name"]
+ if (
+ name.startswith("pdfjs-") and
+ name.endswith("-dist.zip") and
+ name.endswith("-legacy-dist.zip") == legacy
+ ):
+ return asset
+ raise Exception(f"No pdfjs found in {assets}")
+
+
+def get_latest_pdfjs_url(gh_token, legacy):
"""Get the URL of the latest pdf.js prebuilt package.
Returns a (version, url)-tuple.
@@ -83,22 +96,25 @@ def get_latest_pdfjs_url(gh_token):
with urllib.request.urlopen(request) as fp:
data = json.loads(fp.read().decode('utf-8'))
- download_url = data['assets'][0]['browser_download_url']
+ asset = find_pdfjs_asset(data["assets"], legacy=legacy)
+
+ download_url = asset['browser_download_url']
version_name = data['name']
return (version_name, download_url)
-def update_pdfjs(target_version=None, gh_token=None):
+def update_pdfjs(target_version=None, legacy=False, gh_token=None):
"""Download and extract the latest pdf.js version.
If target_version is not None, download the given version instead.
Args:
target_version: None or version string ('x.y.z')
+ legacy: Whether to download the legacy build for 83-based.
gh_token: GitHub token to use for the API. Optional except on CI.
"""
if target_version is None:
- version, url = get_latest_pdfjs_url(gh_token)
+ version, url = get_latest_pdfjs_url(gh_token, legacy=legacy)
else:
# We need target_version as x.y.z, without the 'v' prefix, though the
# user might give it on the command line
@@ -107,13 +123,14 @@ def update_pdfjs(target_version=None, gh_token=None):
# version should have the prefix to be consistent with the return value
# of get_latest_pdfjs_url()
version = 'v' + target_version
+ suffix = "-legacy" if legacy else ""
url = ('https://github.com/mozilla/pdf.js/releases/download/'
- 'v{0}/pdfjs-{0}-dist.zip').format(target_version)
+ f'{version}/pdfjs-{target_version}{suffix}-dist.zip')
os.chdir(os.path.join(os.path.dirname(os.path.abspath(__file__)),
'..', '..'))
target_path = os.path.join('qutebrowser', '3rdparty', 'pdfjs')
- print("=> Downloading pdf.js {}".format(version))
+ print(f"=> Downloading pdf.js {version}{' (legacy)' if legacy else ''}")
try:
(archive_path, _headers) = urllib.request.urlretrieve(url)
except urllib.error.HTTPError as error:
@@ -160,20 +177,20 @@ def test_dicts():
print('Testing dictionary {}... '.format(lang.code), end='')
lang_url = urllib.parse.urljoin(dictcli.API_URL, lang.remote_filename)
request = urllib.request.Request(lang_url, method='HEAD')
- response = urllib.request.urlopen(request)
- if response.status == 200:
- print('OK')
- else:
- print('ERROR: {}'.format(response.status))
+ with urllib.request.urlopen(request) as response:
+ if response.status == 200:
+ print('OK')
+ else:
+ print('ERROR: {}'.format(response.status))
-def run(nsis=False, ace=False, pdfjs=True, fancy_dmg=False, pdfjs_version=None,
- dicts=False, gh_token=None):
+def run(nsis=False, ace=False, pdfjs=True, legacy_pdfjs=False, fancy_dmg=False,
+ pdfjs_version=None, dicts=False, gh_token=None):
"""Update components based on the given arguments."""
if nsis:
download_nsis_plugins()
if pdfjs:
- update_pdfjs(pdfjs_version, gh_token=gh_token)
+ update_pdfjs(pdfjs_version, legacy=legacy_pdfjs, gh_token=gh_token)
if ace:
update_ace()
if fancy_dmg:
@@ -191,6 +208,9 @@ def main():
help='Specify pdfjs version. If not given, '
'the latest version is used.',
required=False, metavar='VERSION')
+ parser.add_argument("--legacy-pdfjs",
+ help="Use legacy PDF.js build (for 83-based)",
+ action='store_true')
parser.add_argument('--fancy-dmg', help="Update fancy-dmg Makefile",
action='store_true')
parser.add_argument(
@@ -202,7 +222,8 @@ def main():
'--gh-token', help="GitHub token to use.", nargs='?')
args = parser.parse_args()
run(nsis=False, ace=True, pdfjs=True, fancy_dmg=args.fancy_dmg,
- pdfjs_version=args.pdfjs, dicts=args.dicts, gh_token=args.gh_token)
+ pdfjs_version=args.pdfjs, legacy_pdfjs=args.legacy_pdfjs,
+ dicts=args.dicts, gh_token=args.gh_token)
if __name__ == '__main__':
diff --git a/scripts/dictcli.py b/scripts/dictcli.py
index 8cb93fb8a..a937fd31d 100755
--- a/scripts/dictcli.py
+++ b/scripts/dictcli.py
@@ -142,11 +142,11 @@ def parse_entry(entry):
def language_list_from_api():
"""Return a JSON with a list of available languages from Google API."""
listurl = API_URL + '?format=JSON'
- response = urllib.request.urlopen(listurl)
- # A special 5-byte prefix must be stripped from the response content
- # See: https://github.com/google/gitiles/issues/22
- # https://github.com/google/gitiles/issues/82
- json_content = response.read()[5:]
+ with urllib.request.urlopen(listurl) as response:
+ # A special 5-byte prefix must be stripped from the response content
+ # See: https://github.com/google/gitiles/issues/22
+ # https://github.com/google/gitiles/issues/82
+ json_content = response.read()[5:]
entries = json.loads(json_content.decode('utf-8'))['entries']
parsed_entries = [parse_entry(entry) for entry in entries]
return [entry for entry in parsed_entries if entry is not None]
@@ -176,8 +176,8 @@ def available_languages():
def download_dictionary(url, dest):
"""Download a decoded dictionary file."""
- response = urllib.request.urlopen(url)
- decoded = base64.decodebytes(response.read())
+ with urllib.request.urlopen(url) as response:
+ decoded = base64.decodebytes(response.read())
with open(dest, 'bw') as dict_file:
dict_file.write(decoded)
diff --git a/scripts/hostblock_blame.py b/scripts/hostblock_blame.py
index 38acaa58d..b18c62925 100644
--- a/scripts/hostblock_blame.py
+++ b/scripts/hostblock_blame.py
@@ -41,8 +41,8 @@ def main():
for url in configdata.DATA['content.blocking.hosts.lists'].default:
print("checking {}...".format(url))
- raw_file = urllib.request.urlopen(url)
- byte_io = io.BytesIO(raw_file.read())
+ with urllib.request.urlopen(url) as raw_file:
+ byte_io = io.BytesIO(raw_file.read())
f = hostblock.get_fileobj(byte_io)
for line in f:
line = line.decode('utf-8')
diff --git a/scripts/link_pyqt.py b/scripts/link_pyqt.py
index 158cc145d..0ec7f2556 100644
--- a/scripts/link_pyqt.py
+++ b/scripts/link_pyqt.py
@@ -44,13 +44,13 @@ def run_py(executable, *code):
f.write('\n'.join(code))
cmd = [executable, filename]
try:
- ret = subprocess.run(cmd, universal_newlines=True, check=True,
+ ret = subprocess.run(cmd, text=True, check=True,
stdout=subprocess.PIPE).stdout
finally:
os.remove(filename)
else:
cmd = [executable, '-c', '\n'.join(code)]
- ret = subprocess.run(cmd, universal_newlines=True, check=True,
+ ret = subprocess.run(cmd, text=True, check=True,
stdout=subprocess.PIPE).stdout
return ret.rstrip()
diff --git a/scripts/mkvenv.py b/scripts/mkvenv.py
index 7f6920bb8..737ea145d 100755
--- a/scripts/mkvenv.py
+++ b/scripts/mkvenv.py
@@ -147,7 +147,7 @@ def run_venv(
return subprocess.run(
[str(venv_dir / subdir / executable)] + [str(arg) for arg in args],
check=True,
- universal_newlines=capture_output or capture_error,
+ text=capture_output or capture_error,
stdout=subprocess.PIPE if capture_output else None,
stderr=subprocess.PIPE if capture_error else None,
env=proc_env,
diff --git a/scripts/opengl_info.py b/scripts/opengl_info.py
new file mode 100644
index 000000000..1bfdf22aa
--- /dev/null
+++ b/scripts/opengl_info.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2020-2021 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
+
+# This file is part of qutebrowser.
+#
+# qutebrowser is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# qutebrowser is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>.
+
+"""Show information about the OpenGL setup."""
+
+from PyQt5.QtGui import (QOpenGLContext, QOpenGLVersionProfile,
+ QOffscreenSurface, QGuiApplication)
+
+app = QGuiApplication([])
+
+surface = QOffscreenSurface()
+surface.create()
+
+ctx = QOpenGLContext()
+ok = ctx.create()
+assert ok
+
+ok = ctx.makeCurrent(surface)
+assert ok
+
+print(f"GLES: {ctx.isOpenGLES()}")
+
+vp = QOpenGLVersionProfile()
+vp.setVersion(2, 0)
+
+vf = ctx.versionFunctions(vp)
+print(f"Vendor: {vf.glGetString(vf.GL_VENDOR)}")
+print(f"Renderer: {vf.glGetString(vf.GL_RENDERER)}")
+print(f"Version: {vf.glGetString(vf.GL_VERSION)}")
+print(f"Shading language version: {vf.glGetString(vf.GL_SHADING_LANGUAGE_VERSION)}")
+
+ctx.doneCurrent()
diff --git a/scripts/setupcommon.py b/scripts/setupcommon.py
index b1d246044..bd549d7cc 100644
--- a/scripts/setupcommon.py
+++ b/scripts/setupcommon.py
@@ -43,7 +43,7 @@ def _call_git(gitpath, *args):
return subprocess.run(
['git'] + list(args),
cwd=gitpath, check=True,
- stdout=subprocess.PIPE).stdout.decode('UTF-8').strip()
+ stdout=subprocess.PIPE, text=True).stdout.strip()
def _git_str():
diff --git a/setup.py b/setup.py
index 4c4bd3243..cb088e04c 100755
--- a/setup.py
+++ b/setup.py
@@ -72,9 +72,8 @@ try:
['qutebrowser = qutebrowser.qutebrowser:main']},
zip_safe=True,
install_requires=['jinja2', 'PyYAML',
- 'dataclasses; python_version < "3.7"',
'importlib_resources>=1.1.0; python_version < "3.9"'],
- python_requires='>=3.6',
+ python_requires='>=3.7',
name='qutebrowser',
version=_get_constant('version'),
description=_get_constant('description'),
@@ -96,7 +95,6 @@ try:
'Operating System :: MacOS',
'Operating System :: POSIX :: BSD',
'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
diff --git a/tests/conftest.py b/tests/conftest.py
index 40631af34..f7e685f4e 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -17,31 +17,29 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>.
-# pylint: disable=unused-import,wildcard-import,unused-wildcard-import
-
"""The qutebrowser test suite conftest file."""
import os
import pathlib
import sys
-import warnings
import pytest
import hypothesis
-from PyQt5.QtCore import PYQT_VERSION
pytest.register_assert_rewrite('helpers')
+# pylint: disable=wildcard-import,unused-import,unused-wildcard-import
from helpers import logfail
from helpers.logfail import fail_on_logging
from helpers.messagemock import message_mock
from helpers.fixtures import * # noqa: F403
+# pylint: enable=wildcard-import,unused-import,unused-wildcard-import
from helpers import testutils
-from qutebrowser.utils import qtutils, standarddir, usertypes, utils, version
+from qutebrowser.utils import usertypes, utils, version
from qutebrowser.misc import objects, earlyinit
-from qutebrowser.qt import sip
-import qutebrowser.app # To register commands
+# To register commands
+import qutebrowser.app # pylint: disable=unused-import
_qute_scheme_handler = None
@@ -196,9 +194,8 @@ def pytest_ignore_collect(path):
@pytest.fixture(scope='session')
def qapp_args():
"""Make QtWebEngine unit tests run on older Qt versions + newer kernels."""
- seccomp_args = testutils.seccomp_args(qt_flag=False)
- if seccomp_args:
- return [sys.argv[0]] + seccomp_args
+ if testutils.disable_seccomp_bpf_sandbox():
+ return [sys.argv[0], testutils.DISABLE_SECCOMP_BPF_FLAG]
return []
@@ -214,20 +211,76 @@ def pytest_addoption(parser):
help="Delay between qutebrowser commands.")
parser.addoption('--qute-profile-subprocs', action='store_true',
default=False, help="Run cProfile for subprocesses.")
- parser.addoption('--qute-bdd-webengine', action='store_true',
- help='Use QtWebEngine for BDD tests')
+ parser.addoption('--qute-backend', action='store',
+ choices=['webkit', 'webengine'], help='Set backend for BDD tests')
def pytest_configure(config):
- webengine_arg = config.getoption('--qute-bdd-webengine')
- webengine_env = os.environ.get('QUTE_BDD_WEBENGINE', 'false')
- config.webengine = webengine_arg or webengine_env == 'true'
- # Fail early if QtWebEngine is not available
- if config.webengine:
- import PyQt5.QtWebEngineWidgets
+ backend = _select_backend(config)
+ config.webengine = backend == 'webengine'
+
earlyinit.configure_pyqt()
+def _select_backend(config):
+ """Select the backend for running tests.
+
+ The backend is auto-selected in the following manner:
+ 1. Use QtWebKit if available
+ 2. Otherwise use QtWebEngine as a fallback
+
+ Auto-selection is overridden by either passing a backend via
+ `--qute-backend=<backend>` or setting the environment variable
+ `QUTE_TESTS_BACKEND=<backend>`.
+
+ Args:
+ config: pytest config
+
+ Raises:
+ ImportError if the selected backend is not available.
+
+ Returns:
+ The selected backend as a string (e.g. 'webkit').
+ """
+ backend_arg = config.getoption('--qute-backend')
+ backend_env = os.environ.get('QUTE_TESTS_BACKEND')
+
+ backend = backend_arg or backend_env or _auto_select_backend()
+
+ # Fail early if selected backend is not available
+ # pylint: disable=unused-import
+ if backend == 'webkit':
+ import PyQt5.QtWebKitWidgets
+ elif backend == 'webengine':
+ import PyQt5.QtWebEngineWidgets
+ else:
+ raise utils.Unreachable(backend)
+
+ return backend
+
+
+def _auto_select_backend():
+ # pylint: disable=unused-import
+ try:
+ # Try to use QtWebKit as the default backend
+ import PyQt5.QtWebKitWidgets
+ return 'webkit'
+ except ImportError:
+ # Try to use QtWebEngine as a fallback and fail early
+ # if that's also not available
+ import PyQt5.QtWebEngineWidgets
+ return 'webengine'
+
+
+def pytest_report_header(config):
+ if config.webengine:
+ backend_version = version.qtwebengine_versions(avoid_init=True)
+ else:
+ backend_version = version.qWebKitVersion()
+
+ return f'backend: {backend_version}'
+
+
@pytest.fixture(scope='session', autouse=True)
def check_display(request):
if utils.is_linux and not os.environ.get('DISPLAY', ''):
@@ -282,10 +335,10 @@ def check_yaml_c_exts():
"""Make sure PyYAML C extensions are available on CI.
Not available yet with a nightly Python, see:
- https://github.com/yaml/pyyaml/issues/416
+ https://github.com/yaml/pyyaml/issues/630
"""
- if testutils.ON_CI and sys.version_info[:2] != (3, 10):
- from yaml import CLoader
+ if testutils.ON_CI and sys.version_info[:2] != (3, 11):
+ from yaml import CLoader # pylint: disable=unused-import
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
diff --git a/tests/end2end/conftest.py b/tests/end2end/conftest.py
index a4a089cea..5241ab4c3 100644
--- a/tests/end2end/conftest.py
+++ b/tests/end2end/conftest.py
@@ -17,12 +17,9 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>.
-# pylint: disable=unused-import
-
"""Things needed for end2end testing."""
import re
-import os
import pathlib
import sys
import shutil
@@ -34,13 +31,14 @@ from PyQt5.QtCore import PYQT_VERSION, QCoreApplication
pytest.register_assert_rewrite('end2end.fixtures')
+# pylint: disable=unused-import
from end2end.fixtures.notificationserver import notification_server
from end2end.fixtures.webserver import server, server_per_test, server2, ssl_server
from end2end.fixtures.quteprocess import (quteproc_process, quteproc,
quteproc_new)
from end2end.fixtures.testprocess import pytest_runtest_makereport
+# pylint: enable=unused-import
from qutebrowser.utils import qtutils, utils
-from qutebrowser.browser.webengine import spell
def pytest_configure(config):
@@ -57,11 +55,11 @@ def pytest_unconfigure(config):
if config.getoption('--qute-profile-subprocs'):
stats = pstats.Stats()
for fn in pathlib.Path('prof').iterdir():
- stats.add((pathlib.Path('prof') / fn))
- stats.dump_stats((pathlib.Path('prof') / 'combined.pstats'))
+ stats.add(pathlib.Path('prof') / fn)
+ stats.dump_stats(pathlib.Path('prof') / 'combined.pstats')
-def _check_hex_version(op_str, running_version, version):
+def _check_version(op_str, running_version, version_str, as_hex=False):
operators = {
'==': operator.eq,
'!=': operator.ne,
@@ -71,9 +69,12 @@ def _check_hex_version(op_str, running_version, version):
'<': operator.lt,
}
op = operators[op_str]
- major, minor, patch = [int(e) for e in version.split('.')]
- hex_version = (major << 16) | (minor << 8) | patch
- return op(running_version, hex_version)
+ major, minor, patch = [int(e) for e in version_str.split('.')]
+ if as_hex:
+ version = (major << 16) | (minor << 8) | patch
+ else:
+ version = (major, minor, patch)
+ return op(running_version, version)
def _get_version_tag(tag):
@@ -84,7 +85,7 @@ def _get_version_tag(tag):
casesinto an appropriate @pytest.mark.skip marker, and falls back to
"""
version_re = re.compile(r"""
- (?P<package>qt|pyqt|pyqtwebengine)
+ (?P<package>qt|pyqt|pyqtwebengine|python)
(?P<operator>==|>=|!=|<)
(?P<version>\d+\.\d+(\.\d+)?)
""", re.VERBOSE)
@@ -108,10 +109,11 @@ def _get_version_tag(tag):
return pytest.mark.skipif(do_skip[op], reason='Needs ' + tag)
elif package == 'pyqt':
return pytest.mark.skipif(
- not _check_hex_version(
+ not _check_version(
op_str=match.group('operator'),
running_version=PYQT_VERSION,
- version=version
+ version_str=version,
+ as_hex=True,
),
reason='Needs ' + tag,
)
@@ -123,10 +125,21 @@ def _get_version_tag(tag):
else:
running_version = PYQT_WEBENGINE_VERSION
return pytest.mark.skipif(
- not _check_hex_version(
+ not _check_version(
+ op_str=match.group('operator'),
+ running_version=running_version,
+ version_str=version,
+ as_hex=True,
+ ),
+ reason='Needs ' + tag,
+ )
+ elif package == 'python':
+ running_version = sys.version_info
+ return pytest.mark.skipif(
+ not _check_version(
op_str=match.group('operator'),
running_version=running_version,
- version=version
+ version_str=version,
),
reason='Needs ' + tag,
)
@@ -165,7 +178,7 @@ if not getattr(sys, 'frozen', False):
def pytest_collection_modifyitems(config, items):
- """Apply @qtwebengine_* markers; skip unittests with QUTE_BDD_WEBENGINE."""
+ """Apply @qtwebengine_* markers."""
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-75884
# (note this isn't actually fixed properly before Qt 5.15)
header_bug_fixed = qtutils.version_check('5.15', compiled=False)
diff --git a/tests/end2end/data/brave-adblock/generate.py b/tests/end2end/data/brave-adblock/generate.py
index 7a23a21ab..393cda4e7 100644
--- a/tests/end2end/data/brave-adblock/generate.py
+++ b/tests/end2end/data/brave-adblock/generate.py
@@ -53,9 +53,9 @@ def main():
else:
request = urllib.request.Request(URL)
print(f"Downloading {URL} ...")
- response = urllib.request.urlopen(request)
- assert response.status == 200
- data_str = response.read().decode("utf-8")
+ with urllib.request.urlopen(request) as response:
+ assert response.status == 200
+ data_str = response.read().decode("utf-8")
print(f"Saving to cache file {CACHE_PATH} ...")
CACHE_PATH.write_text(data_str, encoding="utf-8")
data = io.StringIO(data_str)
diff --git a/tests/end2end/data/click_element.html b/tests/end2end/data/click_element.html
index acf0cf77c..b2a691e08 100644
--- a/tests/end2end/data/click_element.html
+++ b/tests/end2end/data/click_element.html
@@ -6,9 +6,11 @@
<span id='test' onclick='console.log("click_element clicked")'>Test Element</span>
<span onclick='console.log("click_element special chars")'>"Don't", he shouted</span>
<span>Duplicate</span>
- <span>Duplicate</span>
- <form><input id='qute-input'></input></form>
+ <span class='clickable' onclick='console.log("click_element CSS selector")'>Duplicate</span>
+ <form><input autofocus id='qute-input'></input></form>
<a href="/data/hello.txt" id='link'>link</a>
<span id='foo.bar' onclick='console.log("id with dot")'>ID with dot</span>
+ <span style='position: absolute; left: 20px;top: 42px; width:10px; height:10px;'
+ onclick='console.log("click_element position")'></span>
</body>
</html>
diff --git a/tests/end2end/data/darkmode/mathml-display.html b/tests/end2end/data/darkmode/mathml-display.html
new file mode 100644
index 000000000..f0c280cca
--- /dev/null
+++ b/tests/end2end/data/darkmode/mathml-display.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>MathML-like SVG</title>
+ </head>
+ <body style="margin: 0; background-color: #ffff99">
+ <!--
+ Image based on: https://en.wikipedia.org/wiki/Pythagorean_theorem
+ with a black square added for testing.
+
+ onload based on:
+ https://stackoverflow.com/questions/53423742/waiting-for-an-image-to-finish-rendering
+ -->
+ <img
+ class="mwe-math-fallback-image-display"
+ src="mathml.svg"
+ alt="Pythagorean theorem"
+ onload="requestAnimationFrame(() => requestAnimationFrame(() => console.log('Image loaded')));"
+ >
+ <!-- -->
+ </body>
+</html>
diff --git a/tests/end2end/data/darkmode/mathml.html b/tests/end2end/data/darkmode/mathml-inline.html
index fa2371638..fa2371638 100644
--- a/tests/end2end/data/darkmode/mathml.html
+++ b/tests/end2end/data/darkmode/mathml-inline.html
diff --git a/tests/end2end/data/hints/html/README.md b/tests/end2end/data/hints/html/README.md
index 2a6e97c24..5bbaecb4a 100644
--- a/tests/end2end/data/hints/html/README.md
+++ b/tests/end2end/data/hints/html/README.md
@@ -3,3 +3,5 @@ Tests in this directory are automatically picked up by `test_hints` in
They need to contain a special `<!-- target: foo.html -->` comment which
specifies where the hint in it will point to, and will then test that.
+
+With `<!-- target: null -->`, the page is expected to not generate any hints.
diff --git a/tests/end2end/data/hints/invisible.html b/tests/end2end/data/hints/html/invisible.html
index b0bfa9dd9..d382c80fa 100644
--- a/tests/end2end/data/hints/invisible.html
+++ b/tests/end2end/data/hints/html/invisible.html
@@ -1,3 +1,5 @@
+<!-- target: null -->
+
<!DOCTYPE html>
<html>
diff --git a/tests/end2end/data/hints/html/tabindex-negative.html b/tests/end2end/data/hints/html/tabindex-negative.html
new file mode 100644
index 000000000..03adb32bf
--- /dev/null
+++ b/tests/end2end/data/hints/html/tabindex-negative.html
@@ -0,0 +1,13 @@
+<!-- target: null -->
+
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Span with tabindex -1</title>
+ </head>
+ <body>
+ <p>This text should not get a hint:</p>
+ <span tabindex=-1>test</span>
+ </body>
+</html>
diff --git a/tests/end2end/features/hints.feature b/tests/end2end/features/hints.feature
index cf35c5356..47153b741 100644
--- a/tests/end2end/features/hints.feature
+++ b/tests/end2end/features/hints.feature
@@ -241,11 +241,6 @@ Feature: Using hints
# The actual check is already done above
Then "No elements found." should not be logged
- Scenario: Hinting invisible elements
- When I open data/hints/invisible.html
- And I run :hint
- Then the error "No elements found." should be shown
-
Scenario: Clicking input with existing text
When I open data/hints/input.html
And I run :click-element id qute-input-existing
diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature
index e6a02e038..26fe8f357 100644
--- a/tests/end2end/features/misc.feature
+++ b/tests/end2end/features/misc.feature
@@ -389,7 +389,7 @@ Feature: Various utility commands.
Scenario: Partial commandline matching with startup command
When I run :message-i "Hello World" (invalid command)
- Then the error "message-i: no such command" should be shown
+ Then the error "message-i: no such command (did you mean :message-info?)" should be shown
Scenario: Multiple leading : in command
When I run :::::set-cmd-text ::::message-i "Hello World"
@@ -436,7 +436,7 @@ Feature: Various utility commands.
Scenario: Clicking an element with unknown ID
When I open data/click_element.html
And I run :click-element id blah
- Then the error "No element found with id blah!" should be shown
+ Then the error "No element found with ID "blah"!" should be shown
Scenario: Clicking an element by ID
When I open data/click_element.html
@@ -457,6 +457,49 @@ Feature: Various utility commands.
- data/click_element.html
- data/hello.txt (active)
+ Scenario: Clicking an element by CSS selector
+ When I open data/click_element.html
+ And I run :click-element css .clickable
+ Then the javascript message "click_element CSS selector" should be logged
+
+ Scenario: Clicking an element with non-unique filter
+ When I open data/click_element.html
+ And I run :click-element css span
+ Then the error "Multiple elements found matching CSS selector "span"!" should be shown
+
+ Scenario: Clicking first element matching a selector
+ When I open data/click_element.html
+ And I run :click-element --select-first css span
+ Then the javascript message "click_element clicked" should be logged
+
+ Scenario: Clicking an element by position
+ When I open data/click_element.html
+ And I run :click-element position 20,42
+ Then the javascript message "click_element position" should be logged
+
+ Scenario: Clicking an element with invalid position
+ When I open data/click_element.html
+ And I run :click-element position 20.42
+ Then the error "String 20.42 does not match X,Y" should be shown
+
+ Scenario: Clicking an element with non-integer position
+ When I open data/click_element.html
+ And I run :click-element position 20,42.001
+ Then the error "String 20,42.001 does not match X,Y" should be shown
+
+ Scenario: Clicking on focused element when there is none
+ When I open data/click_element.html
+ # Need to loose focus on input element
+ And I run :click-element position 20,42
+ And I wait for the javascript message "click_element position"
+ And I run :click-element focused
+ Then the error "No element found with focus!" should be shown
+
+ Scenario: Clicking on focused element
+ When I open data/click_element.html
+ And I run :click-element focused
+ Then "Entering mode KeyMode.insert (reason: clicking input)" should be logged
+
## :command-history-{prev,next}
Scenario: Calling previous command
diff --git a/tests/end2end/features/notifications.feature b/tests/end2end/features/notifications.feature
index 08c5f725f..cd34793ef 100644
--- a/tests/end2end/features/notifications.feature
+++ b/tests/end2end/features/notifications.feature
@@ -4,7 +4,8 @@ Feature: Notifications
HTML5 notification API interaction
Background:
- Given I open data/javascript/notifications.html
+ Given I clean up open tabs
+ And I open data/javascript/notifications.html
And I set content.notifications.enabled to true
And I run :click-element id button
And I clean up the notification server
@@ -120,8 +121,13 @@ Feature: Notifications
Scenario: User clicks presented notification
When I run :click-element id show-button
And I wait for the javascript message "notification shown"
+ And I open about:blank in a new tab
And I click the notification
Then the javascript message "notification clicked" should be logged
+ And the following tabs should be open:
+ - about:blank
+ - data/javascript/notifications.html (active)
+ - about:blank
@pyqtwebengine<5.15.0
Scenario: User clicks presented notification (old Qt)
diff --git a/tests/end2end/features/search.feature b/tests/end2end/features/search.feature
index 5fafd19f0..305b45690 100644
--- a/tests/end2end/features/search.feature
+++ b/tests/end2end/features/search.feature
@@ -71,7 +71,25 @@ Feature: Searching on a page
When I run :search foo
And I wait for "search found foo" in the log
And I run :search foo
- Then "Ignoring duplicate search request for foo" should be logged
+ Then "Ignoring duplicate search request for foo, but resetting flags" should be logged
+
+ Scenario: Reset search direction on duplicate search, forward-to-back
+ When I run :search baz
+ And I wait for "search found baz" in the log
+ And I run :search -r baz
+ And I wait for "Ignoring duplicate search request for baz, but resetting flags" in the log
+ And I run :search-next
+ And I wait for "next_result found baz with flags FindBackward" in the log
+ Then "BAZ" should be found
+
+ Scenario: Reset search direction on duplicate search, back-to-forward
+ When I run :search -r baz
+ And I wait for "search found baz with flags FindBackward" in the log
+ And I run :search baz
+ And I wait for "Ignoring duplicate search request for baz, but resetting flags" in the log
+ And I run :search-next
+ And I wait for "next_result found baz" in the log
+ Then "baz" should be found
## search.ignore_case
diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature
index e8fc9e7bc..54e84a10e 100644
--- a/tests/end2end/features/tabs.feature
+++ b/tests/end2end/features/tabs.feature
@@ -633,6 +633,27 @@ Feature: Tab management
- data/numbers/1.txt (active)
- data/numbers/3.txt
+ Scenario: :tab-move with absolute position
+ When I open data/numbers/1.txt
+ And I open data/numbers/2.txt in a new tab
+ And I open data/numbers/3.txt in a new tab
+ And I run :tab-focus 1
+ And I run :tab-move end
+ Then the following tabs should be open:
+ - data/numbers/2.txt
+ - data/numbers/3.txt
+ - data/numbers/1.txt (active)
+
+ Scenario: :tab-move with absolute position
+ When I open data/numbers/1.txt
+ And I open data/numbers/2.txt in a new tab
+ And I open data/numbers/3.txt in a new tab
+ And I run :tab-move start
+ Then the following tabs should be open:
+ - data/numbers/3.txt (active)
+ - data/numbers/1.txt
+ - data/numbers/2.txt
+
Scenario: Make sure :tab-move retains metadata
When I open data/title.html
And I open data/hello.txt in a new tab
@@ -653,7 +674,7 @@ Feature: Tab management
Scenario: :tab-clone with -b and -w
When I run :tab-clone -b -w
- Then the error "Only one of -b/-w can be given!" should be shown.
+ Then the error "Only one of -b/-w/-p can be given!" should be shown.
Scenario: Cloning a tab with history and title
When I open data/title.html
@@ -737,6 +758,26 @@ Feature: Tab management
- url: http://localhost:*/data/title.html
title: Test title
+ Scenario: Cloning to private window
+ When I open data/title.html
+ And I run :tab-clone -p
+ And I wait until data/title.html is loaded
+ Then the session should look like:
+ windows:
+ - tabs:
+ - active: true
+ history:
+ - url: about:blank
+ - url: http://localhost:*/data/title.html
+ title: Test title
+ - private: true
+ tabs:
+ - active: true
+ history:
+ - url: about:blank
+ - url: http://localhost:*/data/title.html
+ title: Test title
+
# https://github.com/qutebrowser/qutebrowser/issues/2289
@qtwebkit_skip
@@ -1329,6 +1370,25 @@ Feature: Tab management
And I run :tab-take 0/1
Then the error "Can't take tabs when using windows as tabs" should be shown
+ @windows_skip
+ Scenario: Close the last tab of a window when taken by another window
+ Given I have a fresh instance
+ When I open data/numbers/1.txt
+ And I run :tab-only
+ And I open data/numbers/2.txt in a new window
+ And I set tabs.last_close to ignore
+ And I run :tab-take 1/1
+ And I wait until data/numbers/2.txt is loaded
+ Then the session should look like:
+ windows:
+ - tabs:
+ - history:
+ - url: about:blank
+ - url: http://localhost:*/data/numbers/1.txt
+ - active: true
+ history:
+ - url: http://localhost:*/data/numbers/2.txt
+
# :tab-give
@xfail_norun # Needs qutewm
@@ -1386,6 +1446,24 @@ Feature: Tab management
And I run :tab-give 0
Then the error "Can't give tabs when using windows as tabs" should be shown
+ @windows_skip
+ Scenario: Close the last tab of a window when given to another window
+ Given I have a fresh instance
+ When I open data/numbers/1.txt
+ And I run :tab-only
+ And I open data/numbers/2.txt in a new window
+ And I set tabs.last_close to ignore
+ And I run :tab-give 1
+ And I wait until data/numbers/1.txt is loaded
+ Then the session should look like:
+ windows:
+ - tabs:
+ - active: true
+ history:
+ - url: http://localhost:*/data/numbers/2.txt
+ - history:
+ - url: http://localhost:*/data/numbers/1.txt
+
# Other
Scenario: Using :tab-next after closing last tab (#1448)
@@ -1663,3 +1741,22 @@ Feature: Tab management
And I run :undo
And I run :message-info "Still alive!"
Then the message "Still alive!" should be shown
+
+ Scenario: Passthrough mode override
+ When I run :set -u localhost:*/data/numbers/1.txt input.mode_override 'passthrough'
+ And I open data/numbers/1.txt
+ Then "Entering mode KeyMode.passthrough (reason: mode_override)" should be logged
+
+ Scenario: Insert mode override
+ When I run :set -u localhost:*/data/numbers/1.txt input.mode_override 'insert'
+ And I open data/numbers/1.txt
+ Then "Entering mode KeyMode.insert (reason: mode_override)" should be logged
+
+ Scenario: Mode override on tab switch
+ When I run :set -u localhost:*/data/numbers/1.txt input.mode_override 'insert'
+ And I open data/numbers/1.txt
+ And I wait for "Entering mode KeyMode.insert (reason: mode_override)" in the log
+ And I run :fake-key -g <esc>
+ And I open data/numbers/2.txt in a new tab
+ And I run :tab-prev
+ Then "Entering mode KeyMode.insert (reason: mode_override)" should be logged
diff --git a/tests/end2end/features/utilcmds.feature b/tests/end2end/features/utilcmds.feature
index bb2b3e2fb..e8172ae20 100644
--- a/tests/end2end/features/utilcmds.feature
+++ b/tests/end2end/features/utilcmds.feature
@@ -93,6 +93,7 @@ Feature: Miscellaneous utility commands exposed to the user.
## :debug-cache-stats
+ @python>=3.9.0
Scenario: :debug-cache-stats
When I run :debug-cache-stats
Then "is_valid_prefix: CacheInfo(*)" should be logged
diff --git a/tests/end2end/fixtures/quteprocess.py b/tests/end2end/fixtures/quteprocess.py
index 3cbea01ad..ab8f28d26 100644
--- a/tests/end2end/fixtures/quteprocess.py
+++ b/tests/end2end/fixtures/quteprocess.py
@@ -48,10 +48,7 @@ instance_counter = itertools.count()
def is_ignored_qt_message(pytestconfig, message):
"""Check if the message is listed in qt_log_ignore."""
regexes = pytestconfig.getini('qt_log_ignore')
- for regex in regexes:
- if re.search(regex, message):
- return True
- return False
+ return any(re.search(regex, message) for regex in regexes)
def is_ignored_lowlevel_message(message):
@@ -552,8 +549,8 @@ class QuteProc(testprocess.Process):
'--debug-flag', 'werror', '--debug-flag',
'test-notification-service']
- if self.request.config.webengine:
- args += testutils.seccomp_args(qt_flag=True)
+ if self.request.config.webengine and testutils.disable_seccomp_bpf_sandbox():
+ args += testutils.DISABLE_SECCOMP_BPF_ARGS
args.append('about:blank')
return args
@@ -609,8 +606,7 @@ class QuteProc(testprocess.Process):
self.wait_for(category='webview',
message='Scroll position changed to ' + point)
- def wait_for(self, timeout=None, # pylint: disable=arguments-differ
- **kwargs):
+ def wait_for(self, timeout=None, **kwargs):
"""Extend wait_for to add divisor if a test is xfailing."""
__tracebackhide__ = (lambda e:
e.errisinstance(testprocess.WaitForTimeout))
@@ -716,7 +712,7 @@ class QuteProc(testprocess.Process):
target_arg)
self._wait_for_ipc()
- def start(self, *args, **kwargs): # pylint: disable=arguments-differ
+ def start(self, *args, **kwargs):
try:
super().start(*args, **kwargs)
except testprocess.ProcessExited:
@@ -913,8 +909,8 @@ class QuteProc(testprocess.Process):
"""Get a screenshot of the current page.
Arguments:
- probe: If given, only continue if the pixel at the given position isn't
- black (or whatever is specified by probe_color).
+ probe_pos: If given, only continue if the pixel at the given
+ position isn't black (or whatever is specified by probe_color).
"""
for _ in range(5):
tmp_path = self.request.getfixturevalue('tmp_path')
diff --git a/tests/end2end/fixtures/test_webserver.py b/tests/end2end/fixtures/test_webserver.py
index 3c825e5bc..ed0c32ae5 100644
--- a/tests/end2end/fixtures/test_webserver.py
+++ b/tests/end2end/fixtures/test_webserver.py
@@ -37,7 +37,8 @@ def test_server(server, qtbot, path, content, expected):
with qtbot.wait_signal(server.new_request, timeout=100):
url = 'http://localhost:{}{}'.format(server.port, path)
try:
- response = urllib.request.urlopen(url)
+ with urllib.request.urlopen(url) as response:
+ data = response.read().decode('utf-8')
except urllib.error.HTTPError as e:
# "Though being an exception (a subclass of URLError), an HTTPError
# can also function as a non-exceptional file-like return value
@@ -46,8 +47,6 @@ def test_server(server, qtbot, path, content, expected):
print(e.read().decode('utf-8'))
raise
- data = response.read().decode('utf-8')
-
assert server.get_requests() == [server.ExpectedRequest('GET', path)]
assert (content in data) == expected
diff --git a/tests/end2end/fixtures/webserver.py b/tests/end2end/fixtures/webserver.py
index 0fc32cd88..2c2eab930 100644
--- a/tests/end2end/fixtures/webserver.py
+++ b/tests/end2end/fixtures/webserver.py
@@ -149,11 +149,9 @@ class WebserverProcess(testprocess.Process):
def _random_port(self) -> int:
"""Get a random free port."""
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.bind(('localhost', 0))
- port = sock.getsockname()[1]
- sock.close()
- return port
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
+ sock.bind(('localhost', 0))
+ return sock.getsockname()[1]
def get_requests(self):
"""Get the requests to the server during this test."""
@@ -162,7 +160,7 @@ class WebserverProcess(testprocess.Process):
def _parse_line(self, line):
self._log(line)
- started_re = re.compile(r' \* Running on https?://127\.0\.0\.1:{}/ '
+ started_re = re.compile(r' \* Running on https?://127\.0\.0\.1:{}/? '
r'\(Press CTRL\+C to quit\)'.format(self.port))
if started_re.fullmatch(line):
self.ready.emit()
diff --git a/tests/end2end/fixtures/webserver_sub.py b/tests/end2end/fixtures/webserver_sub.py
index 92f3091d1..392fbe43f 100644
--- a/tests/end2end/fixtures/webserver_sub.py
+++ b/tests/end2end/fixtures/webserver_sub.py
@@ -231,7 +231,7 @@ def drip():
def generate_bytes():
for _ in range(numbytes):
- yield "*".encode('utf-8')
+ yield b"*"
time.sleep(pause)
response = flask.Response(generate_bytes(), headers={
@@ -292,7 +292,9 @@ def view_user_agent():
@app.route('/favicon.ico')
def favicon():
- icon_dir = END2END_DIR.parents[1] / 'icons'
+ # WORKAROUND for https://github.com/PyCQA/pylint/issues/5783
+ # pylint: disable-next=no-member,useless-suppression
+ icon_dir = END2END_DIR.parents[1] / 'qutebrowser' / 'icons'
return flask.send_from_directory(
icon_dir, 'qutebrowser.ico', mimetype='image/vnd.microsoft.icon')
diff --git a/tests/end2end/test_hints_html.py b/tests/end2end/test_hints_html.py
index ebb2a7e33..5af09649f 100644
--- a/tests/end2end/test_hints_html.py
+++ b/tests/end2end/test_hints_html.py
@@ -40,7 +40,7 @@ def collect_tests():
@dataclasses.dataclass
class ParsedFile:
- target: str
+ target: Optional[str]
qtwebengine_todo: Optional[str]
@@ -59,7 +59,7 @@ def _parse_file(test_name):
with file_path.open('r', encoding='utf-8') as html:
soup = bs4.BeautifulSoup(html, 'html.parser')
- comment = str(soup.find(text=lambda text: isinstance(text, bs4.Comment)))
+ comment = str(soup.find(string=lambda text: isinstance(text, bs4.Comment)))
if comment is None:
raise InvalidFile(test_name, "no comment found")
@@ -107,11 +107,18 @@ def test_hints(test_name, zoom_text_only, zoom_level, find_implementation,
quteproc.set_setting('zoom.text_only', str(zoom_text_only))
quteproc.set_setting('hints.find_implementation', find_implementation)
quteproc.send_cmd(':zoom {}'.format(zoom_level))
+
# follow hint
quteproc.send_cmd(':hint all normal')
- quteproc.wait_for(message='hints: a', category='hints')
- quteproc.send_cmd(':hint-follow a')
- quteproc.wait_for_load_finished('data/' + parsed.target)
+
+ if parsed.target is None:
+ msg = quteproc.wait_for(message='No elements found.', category='message')
+ msg.expected = True
+ else:
+ quteproc.wait_for(message='hints: a', category='hints')
+ quteproc.send_cmd(':hint-follow a')
+ quteproc.wait_for_load_finished('data/' + parsed.target)
+
# reset
quteproc.send_cmd(':zoom 100')
if not request.config.webengine:
diff --git a/tests/end2end/test_insert_mode.py b/tests/end2end/test_insert_mode.py
index e829f25a6..47e481b3d 100644
--- a/tests/end2end/test_insert_mode.py
+++ b/tests/end2end/test_insert_mode.py
@@ -22,7 +22,7 @@
import pytest
-@pytest.mark.parametrize(['file_name', 'elem_id', 'source', 'input_text'], [
+@pytest.mark.parametrize('file_name, elem_id, source, input_text', [
('textarea.html', 'qute-textarea', 'clipboard', 'qutebrowser'),
('textarea.html', 'qute-textarea', 'keypress', 'superqutebrowser'),
('input.html', 'qute-input', 'clipboard', 'amazingqutebrowser'),
diff --git a/tests/end2end/test_invocations.py b/tests/end2end/test_invocations.py
index b860feed0..5a34d0357 100644
--- a/tests/end2end/test_invocations.py
+++ b/tests/end2end/test_invocations.py
@@ -56,8 +56,8 @@ def _base_args(config):
else:
args += ['--backend', 'webkit']
- if config.webengine:
- args += testutils.seccomp_args(qt_flag=True)
+ if config.webengine and testutils.disable_seccomp_bpf_sandbox():
+ args += testutils.DISABLE_SECCOMP_BPF_ARGS
args.append('about:blank')
return args
@@ -336,7 +336,7 @@ def test_command_on_start(request, quteproc_new):
quteproc_new.wait_for_quit()
-@pytest.mark.parametrize('python', ['python2', 'python3.5'])
+@pytest.mark.parametrize('python', ['python2', 'python3.6'])
def test_launching_with_old_python(python):
try:
proc = subprocess.run(
@@ -346,7 +346,7 @@ def test_launching_with_old_python(python):
except FileNotFoundError:
pytest.skip(f"{python} not found")
assert proc.returncode == 1
- error = "At least Python 3.6.1 is required to run qutebrowser"
+ error = "At least Python 3.7 is required to run qutebrowser"
assert proc.stderr.decode('ascii').startswith(error)
@@ -723,7 +723,8 @@ def test_dark_mode(webengine_versions, quteproc_new, request,
)
-def test_dark_mode_mathml(quteproc_new, request, qtbot):
+@pytest.mark.parametrize("suffix", ["inline", "display"])
+def test_dark_mode_mathml(quteproc_new, request, qtbot, suffix):
if not request.config.webengine:
pytest.skip("Skipped with QtWebKit")
@@ -734,7 +735,7 @@ def test_dark_mode_mathml(quteproc_new, request, qtbot):
]
quteproc_new.start(args)
- quteproc_new.open_path('data/darkmode/mathml.html')
+ quteproc_new.open_path(f'data/darkmode/mathml-{suffix}.html')
quteproc_new.wait_for_js('Image loaded')
# First make sure loading finished by looking outside of the image
@@ -826,3 +827,81 @@ def test_json_logging_without_debug(request, quteproc_new, runtime_tmpdir):
quteproc_new.exit_expected = True
quteproc_new.start(args, env={'XDG_RUNTIME_DIR': str(runtime_tmpdir)})
assert not quteproc_new.is_running()
+
+
+@pytest.mark.qtwebkit_skip
+@pytest.mark.parametrize(
+ 'sandboxing, has_namespaces, has_seccomp, has_yama, expected_result', [
+ ('enable-all', True, True, True, "You are adequately sandboxed."),
+ ('disable-seccomp-bpf', True, False, True, "You are NOT adequately sandboxed."),
+ ('disable-all', False, False, False, "You are NOT adequately sandboxed."),
+ ]
+)
+def test_sandboxing(
+ request, quteproc_new, sandboxing,
+ has_namespaces, has_seccomp, has_yama, expected_result,
+):
+ if not request.config.webengine:
+ pytest.skip("Skipped with QtWebKit")
+ elif sandboxing == "enable-all" and testutils.disable_seccomp_bpf_sandbox():
+ pytest.skip("Full sandboxing not supported")
+
+ args = _base_args(request.config) + [
+ '--temp-basedir',
+ '-s', 'qt.chromium.sandboxing', sandboxing,
+ ]
+ quteproc_new.start(args)
+
+ quteproc_new.open_url('chrome://sandbox')
+ text = quteproc_new.get_content()
+ print(text)
+
+ not_found_msg = ("The webpage at chrome://sandbox/ might be temporarily down or "
+ "it may have moved permanently to a new web address.")
+ if not_found_msg in text.split("\n"):
+ line = quteproc_new.wait_for(message='Load error: ERR_INVALID_URL')
+ line.expected = True
+ pytest.skip("chrome://sandbox/ not supported")
+
+ bpf_text = "Seccomp-BPF sandbox"
+ yama_text = "Ptrace Protection with Yama LSM"
+
+ if "\n\n\n" in text:
+ # Qt 5.12
+ header, rest = text.split("\n", maxsplit=1)
+ rest, result = rest.rsplit("\n\n", maxsplit=1)
+ lines = rest.replace("\t\n", "\t").split("\n\n\n")
+
+ expected_status = {
+ "Namespace Sandbox": "Yes" if has_namespaces else "No",
+ "Network namespaces": "Yes" if has_namespaces else "No",
+ "PID namespaces": "Yes" if has_namespaces else "No",
+ "SUID Sandbox": "No",
+
+ bpf_text: "Yes" if has_seccomp else "No",
+ f"{bpf_text} supports TSYNC": "Yes" if has_seccomp else "No",
+
+ "Yama LSM Enforcing": "Yes" if has_yama else "No",
+ }
+ else:
+ header, *lines, empty, result = text.split("\n")
+ assert not empty
+
+ expected_status = {
+ "Layer 1 Sandbox": "Namespace" if has_namespaces else "None",
+
+ "PID namespaces": "Yes" if has_namespaces else "No",
+ "Network namespaces": "Yes" if has_namespaces else "No",
+
+ bpf_text: "Yes" if has_seccomp else "No",
+ f"{bpf_text} supports TSYNC": "Yes" if has_seccomp else "No",
+
+ f"{yama_text} (Broker)": "Yes" if has_yama else "No",
+ f"{yama_text} (Non-broker)": "No",
+ }
+
+ assert header == "Sandbox Status"
+ assert result == expected_result
+
+ status = dict(line.split("\t") for line in lines)
+ assert status == expected_status
diff --git a/tests/helpers/fixtures.py b/tests/helpers/fixtures.py
index cd3778b8a..cc362290f 100644
--- a/tests/helpers/fixtures.py
+++ b/tests/helpers/fixtures.py
@@ -186,7 +186,7 @@ def testdata_scheme(qapp):
pass
@qutescheme.add_handler('testdata')
- def handler(url): # pylint: disable=unused-variable
+ def handler(url):
file_abs = os.path.abspath(os.path.dirname(__file__))
filename = os.path.join(file_abs, os.pardir, 'end2end',
url.path().lstrip('/'))
@@ -632,7 +632,7 @@ def redirect_webengine_data(data_tmpdir, monkeypatch):
monkeypatch.setenv('HOME', str(data_tmpdir))
-@pytest.fixture()
+@pytest.fixture
def short_tmpdir():
"""A short temporary directory for a XDG_RUNTIME_DIR."""
with tempfile.TemporaryDirectory() as tdir:
diff --git a/tests/helpers/logfail.py b/tests/helpers/logfail.py
index ae4ac9bc0..15d5a9253 100644
--- a/tests/helpers/logfail.py
+++ b/tests/helpers/logfail.py
@@ -39,8 +39,7 @@ class LogFailHandler(logging.Handler):
if logger.name == 'messagemock':
return
- if (logger.level == record.levelno or
- root_logger.level == record.levelno):
+ if record.levelno in (logger.level, root_logger.level):
# caplog.at_level(...) was used with the level of this message,
# i.e. it was expected.
return
diff --git a/tests/helpers/testutils.py b/tests/helpers/testutils.py
index 8bb622133..c607718ab 100644
--- a/tests/helpers/testutils.py
+++ b/tests/helpers/testutils.py
@@ -31,14 +31,9 @@ import importlib.machinery
import pytest
-from PyQt5.QtCore import qVersion
from PyQt5.QtGui import QColor
-try:
- from PyQt5.QtWebEngine import PYQT_WEBENGINE_VERSION_STR
-except ImportError:
- PYQT_WEBENGINE_VERSION_STR = None
-from qutebrowser.utils import qtutils, log, utils
+from qutebrowser.utils import qtutils, log, utils, version
ON_CI = 'CI' in os.environ
@@ -267,35 +262,38 @@ def easyprivacy_txt():
return _decompress_gzip_datafile("easyprivacy.txt.gz")
-def seccomp_args(qt_flag):
- """Get necessary flags to disable the seccomp BPF sandbox.
+DISABLE_SECCOMP_BPF_FLAG = "--disable-seccomp-filter-sandbox"
+DISABLE_SECCOMP_BPF_ARGS = ["-s", "qt.chromium.sandboxing", "disable-seccomp-bpf"]
+
+
+def disable_seccomp_bpf_sandbox():
+ """Check whether we need to disable the seccomp BPF sandbox.
This is needed for some QtWebEngine setups, with older Qt versions but
newer kernels.
-
- Args:
- qt_flag: Add a '--qt-flag' argument.
"""
+ try:
+ from PyQt5 import QtWebEngine # pylint: disable=unused-import
+ except ImportError:
+ # no QtWebEngine available
+ return False
+
affected_versions = set()
for base, patch_range in [
- # 5.12.0 to 5.12.7 (inclusive)
- ('5.12', range(0, 8)),
+ # 5.12.0 to 5.12.10 (inclusive)
+ ('5.12', range(0, 11)),
# 5.13.0 to 5.13.2 (inclusive)
('5.13', range(0, 3)),
# 5.14.0
('5.14', [0]),
+ # 5.15.0 to 5.15.2 (inclusive)
+ ('5.15', range(0, 3)),
]:
for patch in patch_range:
- affected_versions.add('{}.{}'.format(base, patch))
-
- version = (PYQT_WEBENGINE_VERSION_STR
- if PYQT_WEBENGINE_VERSION_STR is not None
- else qVersion())
- if version in affected_versions:
- disable_arg = 'disable-seccomp-filter-sandbox'
- return ['--qt-flag', disable_arg] if qt_flag else ['--' + disable_arg]
+ affected_versions.add(utils.VersionNumber.parse(f'{base}.{patch}'))
- return []
+ versions = version.qtwebengine_versions(avoid_init=True)
+ return versions.webengine in affected_versions
def import_userscript(name):
diff --git a/tests/unit/api/test_cmdutils.py b/tests/unit/api/test_cmdutils.py
index 77e648eed..b6650078c 100644
--- a/tests/unit/api/test_cmdutils.py
+++ b/tests/unit/api/test_cmdutils.py
@@ -17,8 +17,6 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>.
-# pylint: disable=unused-variable
-
"""Tests for qutebrowser.api.cmdutils."""
import sys
@@ -292,7 +290,6 @@ class TestRegister:
class Enum(enum.Enum):
- # pylint: disable=invalid-name
x = enum.auto()
y = enum.auto()
diff --git a/tests/unit/browser/test_caret.py b/tests/unit/browser/test_caret.py
index 288471ea0..86014040d 100644
--- a/tests/unit/browser/test_caret.py
+++ b/tests/unit/browser/test_caret.py
@@ -70,7 +70,7 @@ class Selection:
self._qtbot.wait(50)
- assert False, 'Failed to get selection!'
+ pytest.fail('Failed to get selection!')
def check_multiline(self, expected, *, strip=False):
self.check(textwrap.dedent(expected).strip(), strip=strip)
diff --git a/tests/unit/browser/test_history.py b/tests/unit/browser/test_history.py
index 7906d385c..51a9effb8 100644
--- a/tests/unit/browser/test_history.py
+++ b/tests/unit/browser/test_history.py
@@ -141,7 +141,7 @@ class TestDelete:
class TestAdd:
- @pytest.fixture()
+ @pytest.fixture
def mock_time(self, mocker):
m = mocker.patch('qutebrowser.browser.history.time')
m.time.return_value = 12345
diff --git a/tests/unit/browser/test_inspector.py b/tests/unit/browser/test_inspector.py
index f7f532050..61ac9510d 100644
--- a/tests/unit/browser/test_inspector.py
+++ b/tests/unit/browser/test_inspector.py
@@ -151,4 +151,5 @@ def test_detach_after_toggling(hidden_again, needs_recreate,
else:
with qtbot.assert_not_emitted(fake_inspector.recreate):
fake_inspector.set_position(inspector.Position.window)
- assert fake_inspector.isVisible() and fake_inspector.isWindow()
+ assert fake_inspector.isVisible()
+ assert fake_inspector.isWindow()
diff --git a/tests/unit/browser/webkit/http/test_http.py b/tests/unit/browser/webkit/http/test_http.py
index 4db78f4ff..d50f1c277 100644
--- a/tests/unit/browser/webkit/http/test_http.py
+++ b/tests/unit/browser/webkit/http/test_http.py
@@ -44,6 +44,21 @@ def test_no_content_disposition(stubs, url, expected):
assert filename == expected
+@pytest.mark.parametrize('value', [
+ # https://github.com/python/cpython/issues/87112
+ 'inline; 0*²'.encode("iso-8859-1"),
+ # https://github.com/python/cpython/issues/81672
+ b'"',
+ # https://github.com/python/cpython/issues/93010
+ b'attachment; 0*00="foo"',
+ # FIXME: Should probably have more tests if this is still relevant after
+ # dropping QtWebKit.
+])
+def test_parse_content_disposition_invalid(value):
+ with pytest.raises(http.ContentDispositionError):
+ http.ContentDisposition.parse(value)
+
+
@pytest.mark.parametrize('template', [
'{}',
'attachment; filename="{}"',
diff --git a/tests/unit/browser/webkit/test_webkitelem.py b/tests/unit/browser/webkit/test_webkitelem.py
index 593896e96..7df91922d 100644
--- a/tests/unit/browser/webkit/test_webkitelem.py
+++ b/tests/unit/browser/webkit/test_webkitelem.py
@@ -181,13 +181,31 @@ class SelectionAndFilterTests:
# We can't easily test <frame>/<iframe> as they vanish when setting
# them via QWebFrame::setHtml...
+ ('<img src="bar">', ['all', 'images', 'url']),
+ ('<summary>bar</summary>', ['all']),
+ ('<link />', ['all']),
+
+ ('<p contenteditable />', ['all', 'inputs']),
+ ('<p contenteditable="false" />', []),
('<p onclick="foo" foo="bar"/>', ['all']),
('<p onmousedown="foo" foo="bar"/>', ['all']),
('<p role="option" foo="bar"/>', ['all']),
+ ('<p role="tab" foo="bar"/>', ['all']),
+ ('<p role="checkbox" foo="bar"/>', ['all']),
+ ('<p role="menuitem" foo="bar"/>', ['all']),
+ ('<p role="menuitemcheckbox" foo="bar"/>', ['all']),
+ ('<p role="menuitemradio" foo="bar"/>', ['all']),
+ ('<p role="treeitem" foo="bar"/>', ['all']),
('<p role="button" foo="bar"/>', ['all']),
('<p role="button" href="bar"/>', ['all', 'url']),
- ('<span tabindex=0 />', ['all']),
+ ('<span tabindex="0" />', ['all']),
+ ('<span tabindex="-1" />', []),
+
+ ('<span ng-click=""></span>', ['all']),
+ ('<span ngClick=""></span>', ['all']),
+ ('<span data-ng-click=""></span>', ['all']),
+ ('<span x-ng-click=""></span>', ['all']),
]
GROUPS = ['all', 'links', 'images', 'url', 'inputs']
@@ -849,8 +867,6 @@ class TestIsEditable:
@pytest.mark.parametrize('setting, tagname, attributes, editable', [
(True, 'embed', {}, True),
- (True, 'embed', {}, True),
- (False, 'applet', {}, False),
(False, 'applet', {}, False),
(True, 'object', {'type': 'application/foo'}, True),
(False, 'object', {'type': 'application/foo'}, False),
diff --git a/tests/unit/commands/test_cmdexc.py b/tests/unit/commands/test_cmdexc.py
new file mode 100644
index 000000000..f80131e29
--- /dev/null
+++ b/tests/unit/commands/test_cmdexc.py
@@ -0,0 +1,40 @@
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2022 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
+#
+# This file is part of qutebrowser.
+#
+# qutebrowser is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# qutebrowser is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>.
+
+"""Tests for qutebrowser.commands.cmdexc."""
+
+import re
+import pytest
+
+from qutebrowser.commands import cmdexc
+
+
+def test_empty_command_error():
+ with pytest.raises(cmdexc.NoSuchCommandError, match="No command given"):
+ raise cmdexc.EmptyCommandError
+
+
+@pytest.mark.parametrize("all_commands, msg", [
+ ([], "testcmd: no such command"),
+ (["fastcmd"], "testcmd: no such command (did you mean :fastcmd?)"),
+ (["thisdoesnotmatch"], "testcmd: no such command"),
+])
+def test_no_such_command_error(all_commands, msg):
+ with pytest.raises(cmdexc.NoSuchCommandError, match=re.escape(msg)):
+ raise cmdexc.NoSuchCommandError.for_cmd("testcmd", all_commands=all_commands)
diff --git a/tests/unit/commands/test_parser.py b/tests/unit/commands/test_parser.py
index b851ad3b0..a0c2fe8f3 100644
--- a/tests/unit/commands/test_parser.py
+++ b/tests/unit/commands/test_parser.py
@@ -19,6 +19,8 @@
"""Tests for qutebrowser.commands.parser."""
+import re
+
import pytest
from qutebrowser.misc import objects
@@ -64,7 +66,7 @@ class TestCommandParser:
and https://github.com/qutebrowser/qutebrowser/issues/1773
"""
p = parser.CommandParser()
- with pytest.raises(cmdexc.NoSuchCommandError):
+ with pytest.raises(cmdexc.EmptyCommandError):
p.parse_all(command)
@pytest.mark.parametrize('command, name, args', [
@@ -135,3 +137,13 @@ class TestCompletions:
result = p.parse('tw')
assert result.cmd.name == 'two'
+
+
+@pytest.mark.parametrize("find_similar, msg", [
+ (True, "tabfocus: no such command (did you mean :tab-focus?)"),
+ (False, "tabfocus: no such command"),
+])
+def test_find_similar(find_similar, msg):
+ p = parser.CommandParser(find_similar=find_similar)
+ with pytest.raises(cmdexc.NoSuchCommandError, match=re.escape(msg)):
+ p.parse_all("tabfocus", aliases=False)
diff --git a/tests/unit/completion/test_completer.py b/tests/unit/completion/test_completer.py
index 467357d32..0f59f50ab 100644
--- a/tests/unit/completion/test_completer.py
+++ b/tests/unit/completion/test_completer.py
@@ -22,10 +22,9 @@
import unittest.mock
import pytest
-from PyQt5.QtCore import QObject
from PyQt5.QtGui import QStandardItemModel
-from qutebrowser.completion import completer
+from qutebrowser.completion import completer, completionwidget
from qutebrowser.commands import command
from qutebrowser.api import cmdutils
@@ -48,12 +47,12 @@ class FakeCompletionModel(QStandardItemModel):
self.info = info
-class CompletionWidgetStub(QObject):
+class CompletionWidgetStub(completionwidget.CompletionView):
"""Stub for the CompletionView."""
def __init__(self, parent=None):
- super().__init__(parent)
+ super().__init__(cmd=None, win_id=0, parent=parent)
self.hide = unittest.mock.Mock()
self.show = unittest.mock.Mock()
self.set_pattern = unittest.mock.Mock()
diff --git a/tests/unit/completion/test_completionwidget.py b/tests/unit/completion/test_completionwidget.py
index 89390cbf1..074228332 100644
--- a/tests/unit/completion/test_completionwidget.py
+++ b/tests/unit/completion/test_completionwidget.py
@@ -37,13 +37,13 @@ def completionview(qtbot, status_command_stub, config_stub, win_registry,
mocker.patch('qutebrowser.completion.completer.Completer', autospec=True)
mocker.patch(
'qutebrowser.completion.completiondelegate.CompletionItemDelegate',
- new=lambda *_: None)
+ return_value=None)
view = completionwidget.CompletionView(cmd=status_command_stub, win_id=0)
qtbot.add_widget(view)
return view
-@pytest.fixture()
+@pytest.fixture
def model():
return completionmodel.CompletionModel()
@@ -163,16 +163,7 @@ def test_completion_item_focus_no_model(which, completionview, model, qtbot):
@pytest.mark.skip("Seems to disagree with reality, see #5897")
def test_completion_item_focus_fetch(completionview, model, qtbot):
- """Test that on_next_prev_item moves the selection properly.
-
- Args:
- which: the direction in which to move the selection.
- tree: Each list represents a completion category, with each string
- being an item under that category.
- expected: expected argument from on_selection_changed for each
- successive movement. None implies no signal should be
- emitted.
- """
+ """Test that on_next_prev_item moves the selection properly."""
cat = mock.Mock(spec=[
'layoutChanged', 'layoutAboutToBeChanged', 'canFetchMore',
'fetchMore', 'rowCount', 'index', 'data'])
diff --git a/tests/unit/completion/test_models.py b/tests/unit/completion/test_models.py
index c20fe293c..2c00acf68 100644
--- a/tests/unit/completion/test_models.py
+++ b/tests/unit/completion/test_models.py
@@ -76,7 +76,7 @@ def _check_completions(model, expected):
assert sum(model.column_widths) == 100
-@pytest.fixture()
+@pytest.fixture
def cmdutils_stub(monkeypatch, stubs):
"""Patch the cmdutils module to provide fake commands."""
return monkeypatch.setattr(objects, 'commands', {
@@ -93,7 +93,7 @@ def cmdutils_stub(monkeypatch, stubs):
})
-@pytest.fixture()
+@pytest.fixture
def configdata_stub(config_stub, monkeypatch, configdata_init):
"""Patch the configdata module to provide fake data."""
monkeypatch.setattr(configdata, 'DATA', collections.OrderedDict([
@@ -867,6 +867,34 @@ def test_tab_completion_delete(qtmodeltester, fake_web_tab, win_registry,
QUrl('https://duckduckgo.com')]
+def test_tab_focus_completion_delete(qtmodeltester, fake_web_tab, win_registry,
+ tabbed_browser_stubs, info):
+ """Verify closing a tab by deleting it from the completion widget."""
+ tabbed_browser_stubs[0].widget.tabs = [
+ fake_web_tab(QUrl('https://github.com'), 'GitHub', 0),
+ fake_web_tab(QUrl('https://wikipedia.org'), 'Wikipedia', 1),
+ fake_web_tab(QUrl('https://duckduckgo.com'), 'DuckDuckGo', 2)
+ ]
+ tabbed_browser_stubs[1].widget.tabs = [
+ fake_web_tab(QUrl('https://wiki.archlinux.org'), 'ArchWiki', 0),
+ ]
+ model = miscmodels.tab_focus(info=info)
+ model.set_pattern('')
+ qtmodeltester.check(model)
+
+ parent = model.index(0, 0)
+ idx = model.index(1, 0, parent)
+
+ # sanity checks
+ assert model.data(parent) == "Tabs"
+ assert model.data(idx) == '2'
+
+ model.delete_cur_item(idx)
+ actual = [tab.url() for tab in tabbed_browser_stubs[0].widget.tabs]
+ assert actual == [QUrl('https://github.com'),
+ QUrl('https://duckduckgo.com')]
+
+
def test_tab_completion_not_sorted(qtmodeltester, fake_web_tab, win_registry,
tabbed_browser_stubs):
"""Ensure that the completion row order is the same as tab index order.
diff --git a/tests/unit/components/test_hostblock.py b/tests/unit/components/test_hostblock.py
index 00a7a5f8f..5949f92f8 100644
--- a/tests/unit/components/test_hostblock.py
+++ b/tests/unit/components/test_hostblock.py
@@ -427,7 +427,7 @@ def test_invalid_utf8(config_stub, tmp_path, caplog, host_blocker_factory, locat
with caplog.at_level(logging.ERROR):
current_download.successful = True
current_download.finished.emit()
- expected = r"Failed to decode: " r"b'https://www.example.org/\xa0localhost"
+ expected = r"Failed to decode: b'https://www.example.org/\xa0localhost"
assert caplog.messages[-2].startswith(expected)
else:
current_download.successful = True
@@ -565,9 +565,13 @@ def test_adblock_benchmark(data_tmpdir, benchmark, host_blocker_factory):
benchmark(lambda: blocker._is_blocked(url))
-def test_subdomain_blocking(config_stub, host_blocker_factory):
+@pytest.mark.parametrize("block_subdomains", [True, False])
+def test_subdomain_blocking(config_stub, host_blocker_factory, block_subdomains):
config_stub.val.content.blocking.method = "hosts"
config_stub.val.content.blocking.hosts.lists = None
+ config_stub.val.content.blocking.hosts.block_subdomains = block_subdomains
host_blocker = host_blocker_factory()
host_blocker._blocked_hosts.add("example.com")
- assert host_blocker._is_blocked(QUrl("https://subdomain.example.com"))
+ is_blocked = host_blocker._is_blocked(QUrl("https://subdomain.example.com"))
+ # block_subdomains is also expected result of is_blocked
+ assert is_blocked == block_subdomains
diff --git a/tests/unit/components/test_readlinecommands.py b/tests/unit/components/test_readlinecommands.py
index 334e2ef9e..af815b075 100644
--- a/tests/unit/components/test_readlinecommands.py
+++ b/tests/unit/components/test_readlinecommands.py
@@ -17,6 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>.
+import os
import re
import inspect
@@ -94,18 +95,19 @@ class LineEdit(QLineEdit):
return ''.join(chars)
-def _validate_deletion(lineedit, method, text, deleted, rest):
+def _validate_deletion(lineedit, method, args, text, deleted, rest):
"""Run and validate a text deletion method on the ReadLine bridge.
Args:
lineedit: The LineEdit instance.
method: Reference to the method on the bridge to test.
+ args: Arguments to pass to the method.
text: The starting 'augmented' text (see LineEdit.set_aug_text)
deleted: The text that should be deleted when the method is invoked.
rest: The augmented text that should remain after method is invoked.
"""
lineedit.set_aug_text(text)
- method()
+ method(*args)
assert readlinecommands.bridge._deleted[lineedit] == deleted
assert lineedit.aug_text() == rest
lineedit.clear()
@@ -127,7 +129,9 @@ def test_none(qtbot):
assert QApplication.instance().focusWidget() is None
for name, method in inspect.getmembers(readlinecommands,
inspect.isfunction):
- if name.startswith('rl_'):
+ if name == "rl_rubout":
+ method(delim=" ")
+ elif name.startswith('rl_'):
method()
@@ -218,7 +222,8 @@ def test_rl_backward_delete_char(text, expected, lineedit):
])
def test_rl_unix_line_discard(lineedit, text, deleted, rest):
"""Delete from the cursor to the beginning of the line and yank back."""
- _validate_deletion(lineedit, readlinecommands.rl_unix_line_discard,
+ _validate_deletion(lineedit,
+ readlinecommands.rl_unix_line_discard, [],
text, deleted, rest)
@@ -230,7 +235,8 @@ def test_rl_unix_line_discard(lineedit, text, deleted, rest):
])
def test_rl_kill_line(lineedit, text, deleted, rest):
"""Delete from the cursor to the end of line and yank back."""
- _validate_deletion(lineedit, readlinecommands.rl_kill_line,
+ _validate_deletion(lineedit,
+ readlinecommands.rl_kill_line, [],
text, deleted, rest)
@@ -243,9 +249,14 @@ def test_rl_kill_line(lineedit, text, deleted, rest):
marks=fixme),
('test del<ete >foobar', 'del', 'test |ete foobar'), # wrong
])
-def test_rl_unix_word_rubout(lineedit, text, deleted, rest):
+@pytest.mark.parametrize("method, args", [
+ (readlinecommands.rl_unix_word_rubout, []), # deprecated
+ (readlinecommands.rl_rubout, [" "]), # equivalent
+])
+def test_rl_unix_word_rubout(lineedit, text, deleted, rest, method, args):
"""Delete to word beginning and see if it comes back with yank."""
- _validate_deletion(lineedit, readlinecommands.rl_unix_word_rubout,
+ _validate_deletion(lineedit,
+ method, args,
text, deleted, rest)
@@ -256,9 +267,35 @@ def test_rl_unix_word_rubout(lineedit, text, deleted, rest):
('open -t |github.com/foo/bar', '-t ', 'open |github.com/foo/bar'),
('open foo/bar.baz|', 'bar.baz', 'open foo/|'),
])
-def test_rl_unix_filename_rubout(lineedit, text, deleted, rest):
+@pytest.mark.parametrize("method, args", [
+ (readlinecommands.rl_unix_filename_rubout, []), # deprecated
+ (readlinecommands.rl_rubout, [" /"]), # equivalent
+])
+def test_rl_unix_filename_rubout(lineedit, text, deleted, rest, method, args):
+ """Delete filename segment and see if it comes back with yank."""
+ _validate_deletion(lineedit,
+ method, args,
+ text, deleted, rest)
+
+
+@pytest.mark.parametrize('os_sep, text, deleted, rest', [
+ ('/', 'path|', 'path', '|'),
+ ('/', '/path|', 'path', '/|'),
+ ('/', '/path/sub|', 'sub', '/path/|'),
+ ('/', '/path/trailing/|', 'trailing/', '/path/|'),
+ ('/', '/test/path with spaces|', 'path with spaces', '/test/|'),
+ ('/', r'/test/path\backslashes\eww|', r'path\backslashes\eww', '/test/|'),
+ ('\\', 'path|', 'path', '|'),
+ ('\\', r'C:\path|', 'path', r'C:\|'),
+ ('\\', r'C:\path\sub|', 'sub', r'C:\path\|'),
+ ('\\', r'C:\test\path with spaces|', 'path with spaces', r'C:\test\|'),
+ ('\\', r'C:\path\trailing\|', 'trailing\\', r'C:\path\|'),
+])
+def test_filename_rubout(os_sep, monkeypatch, lineedit, text, deleted, rest):
"""Delete filename segment and see if it comes back with yank."""
- _validate_deletion(lineedit, readlinecommands.rl_unix_filename_rubout,
+ monkeypatch.setattr(os, "sep", os_sep)
+ _validate_deletion(lineedit,
+ readlinecommands.rl_filename_rubout, [],
text, deleted, rest)
@@ -275,7 +312,8 @@ def test_rl_unix_filename_rubout(lineedit, text, deleted, rest):
])
def test_rl_kill_word(lineedit, text, deleted, rest):
"""Delete to word end and see if it comes back with yank."""
- _validate_deletion(lineedit, readlinecommands.rl_kill_word,
+ _validate_deletion(lineedit,
+ readlinecommands.rl_kill_word, [],
text, deleted, rest)
@@ -290,7 +328,8 @@ def test_rl_kill_word(lineedit, text, deleted, rest):
])
def test_rl_backward_kill_word(lineedit, text, deleted, rest):
"""Delete to word beginning and see if it comes back with yank."""
- _validate_deletion(lineedit, readlinecommands.rl_backward_kill_word,
+ _validate_deletion(lineedit,
+ readlinecommands.rl_backward_kill_word, [],
text, deleted, rest)
diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py
index b88bc2f8d..ab1fba789 100644
--- a/tests/unit/config/test_config.py
+++ b/tests/unit/config/test_config.py
@@ -782,7 +782,7 @@ class TestContainer:
assert len(configapi.errors) == 1
error = configapi.errors[0]
assert error.text == "While getting 'tabs.foobar'"
- assert str(error.exception) == "No option 'tabs.foobar'"
+ assert str(error.exception).startswith("No option 'tabs.foobar'")
def test_confapi_missing_prefix(self, container):
configapi = types.SimpleNamespace(errors=[])
@@ -793,7 +793,7 @@ class TestContainer:
error1 = configapi.errors[0]
assert error1.text == "While getting 'content.host_blocking'"
- assert str(error1.exception) == "No option 'content.host_blocking'"
+ assert str(error1.exception).startswith("No option 'content.host_blocking'")
error2 = configapi.errors[1]
assert error2.text == "While setting 'content.host_blocking.lists'"
diff --git a/tests/unit/config/test_configcommands.py b/tests/unit/config/test_configcommands.py
index 0375503e8..6dd09cee3 100644
--- a/tests/unit/config/test_configcommands.py
+++ b/tests/unit/config/test_configcommands.py
@@ -27,7 +27,7 @@ from PyQt5.QtCore import QUrl
from qutebrowser.config import configcommands
from qutebrowser.api import cmdutils
-from qutebrowser.utils import usertypes, urlmatch
+from qutebrowser.utils import usertypes, urlmatch, utils
from qutebrowser.keyinput import keyutils
from qutebrowser.misc import objects
@@ -529,7 +529,7 @@ class TestSource:
pyfile = config_tmpdir / 'sourced.py'
arg = 'sourced.py'
else:
- assert False, location
+ raise utils.Unreachable(location)
pyfile.write_text('\n'.join(['config.load_autoconfig(False)',
'c.content.javascript.enabled = False']),
@@ -725,15 +725,19 @@ class TestBind:
"""Get a dict with no bindings."""
return {'normal': {}}
+ @pytest.mark.parametrize("mode, url", [
+ ("normal", QUrl("qute://bindings")),
+ ("passthrough", QUrl("qute://bindings#passthrough")),
+ ])
def test_bind_no_args(self, commands, config_stub, no_bindings,
- tabbed_browser_stubs):
+ tabbed_browser_stubs, mode, url):
"""Run ':bind'.
Should open qute://bindings."""
config_stub.val.bindings.default = no_bindings
config_stub.val.bindings.commands = no_bindings
- commands.bind(win_id=0)
- assert tabbed_browser_stubs[0].loaded_url == QUrl('qute://bindings')
+ commands.bind(win_id=0, mode=mode)
+ assert tabbed_browser_stubs[0].loaded_url == url
@pytest.mark.parametrize('command', ['nop', 'nope'])
def test_bind(self, commands, config_stub, no_bindings, key_config_stub,
diff --git a/tests/unit/config/test_configexc.py b/tests/unit/config/test_configexc.py
index a236494f9..b289d3cc8 100644
--- a/tests/unit/config/test_configexc.py
+++ b/tests/unit/config/test_configexc.py
@@ -32,13 +32,27 @@ def test_validation_error():
assert str(e) == "Invalid value 'val' - msg"
-@pytest.mark.parametrize('deleted, renamed, expected', [
- (False, None, "No option 'opt'"),
- (True, None, "No option 'opt' (this option was removed from qutebrowser)"),
- (False, 'new', "No option 'opt' (this option was renamed to 'new')"),
+@pytest.mark.parametrize('deleted, renamed, all_names, expected', [
+ (False, None, [], "No option 'opt'"),
+ (True, None, [], "No option 'opt' (this option was removed from qutebrowser)"),
+ (False, 'new', [], "No option 'opt' (this option was renamed to 'new')"),
+ (False, None, ["opto"], "No option 'opt' (did you mean 'opto'?)"),
+ (False, None, ["thisdoesnotmatch"], "No option 'opt'"),
+ (
+ True,
+ None,
+ ["opto"],
+ "No option 'opt' (this option was removed from qutebrowser)",
+ ),
+ (False, 'new', ["opto"], "No option 'opt' (this option was renamed to 'new')"),
])
-def test_no_option_error(deleted, renamed, expected):
- e = configexc.NoOptionError('opt', deleted=deleted, renamed=renamed)
+def test_no_option_error(deleted, renamed, all_names, expected):
+ e = configexc.NoOptionError(
+ 'opt',
+ deleted=deleted,
+ renamed=renamed,
+ all_names=all_names,
+ )
assert e.option == 'opt'
assert str(e) == expected
diff --git a/tests/unit/config/test_configfiles.py b/tests/unit/config/test_configfiles.py
index 65952ddb4..760992e15 100644
--- a/tests/unit/config/test_configfiles.py
+++ b/tests/unit/config/test_configfiles.py
@@ -873,7 +873,7 @@ class TestConfigPyModules:
confpy.read()
expected = {'normal': {',a': 'message-info foo'}}
assert config.instance.get_obj('bindings.commands') == expected
- assert "qbmodule" not in sys.modules.keys()
+ assert "qbmodule" not in sys.modules
assert tmp_path not in sys.path
def test_restore_sys_on_err(self, confpy, qbmodulepy, tmp_path):
@@ -884,7 +884,7 @@ class TestConfigPyModules:
assert error.text == "Unhandled exception"
assert isinstance(error.exception, ZeroDivisionError)
- assert "qbmodule" not in sys.modules.keys()
+ assert "qbmodule" not in sys.modules
assert tmp_path not in sys.path
def test_fail_on_nonexistent_module(self, confpy, qbmodulepy, tmp_path):
@@ -1481,7 +1481,6 @@ class TestConfigPyWriter:
def init_patch(qapp, fake_save_manager, config_tmpdir, data_tmpdir,
config_stub, monkeypatch):
monkeypatch.setattr(configfiles, 'state', None)
- yield
def test_init(init_patch, config_tmpdir):
diff --git a/tests/unit/config/test_configtypes.py b/tests/unit/config/test_configtypes.py
index 66b152937..e4351d619 100644
--- a/tests/unit/config/test_configtypes.py
+++ b/tests/unit/config/test_configtypes.py
@@ -19,7 +19,6 @@
"""Tests for qutebrowser.config.configtypes."""
import re
-import sys
import json
import math
import warnings
@@ -224,6 +223,7 @@ class TestAll:
"""Patch aliases so Command works."""
config_stub.val.aliases = {}
+ # pylint: disable-next=too-many-function-args
@pytest.fixture(params=list(gen_classes()))
def klass(self, request):
return request.param
@@ -1501,12 +1501,8 @@ class TestRegex:
pytest.param('(' * 500, id='too many parens'),
pytest.param(r'foo\Xbar', id='invalid escape X'),
pytest.param(r'foo\Cbar', id='invalid escape C'),
- pytest.param(r'[[]]', id='nested set', marks=pytest.mark.skipif(
- sys.hexversion < 0x03070000,
- reason="Warning was added in Python 3.7")),
- pytest.param(r'[a||b]', id='set operation', marks=pytest.mark.skipif(
- sys.hexversion < 0x03070000,
- reason="Warning was added in Python 3.7")),
+ pytest.param(r'[[]]', id='nested set'),
+ pytest.param(r'[a||b]', id='set operation'),
])
def test_to_py_invalid(self, klass, val):
with pytest.raises(configexc.ValidationError):
@@ -1876,7 +1872,6 @@ class TestShellCommand:
@pytest.mark.parametrize('kwargs, val', [
({'placeholder': True}, '[foo, bar]'),
({'placeholder': True}, '[foo, "{", "}", bar'),
- ({'placeholder': True}, '[foo, bar]'),
({'placeholder': True}, '[foo, "{fi", "le}", bar'),
# Like valid ones but with wrong placeholder
diff --git a/tests/unit/config/test_configutils.py b/tests/unit/config/test_configutils.py
index e7ce15aff..3fc5cd561 100644
--- a/tests/unit/config/test_configutils.py
+++ b/tests/unit/config/test_configutils.py
@@ -318,13 +318,11 @@ class TestFontFamilies:
@pytest.mark.parametrize('families, quote, expected', [
(['family'], True, 'family'),
(['family1', 'family2'], True, 'family1, family2'),
- (['family'], True, 'family'),
(['space family', 'alien'], True, '"space family", alien'),
(['comma,family', 'period'], True, '"comma,family", period'),
(['family'], False, 'family'),
(['family1', 'family2'], False, 'family1, family2'),
- (['family'], False, 'family'),
(['space family', 'alien'], False, 'space family, alien'),
(['comma,family', 'period'], False, 'comma,family, period'),
])
@@ -367,9 +365,13 @@ class TestFontFamilies:
# Check the requested font to make sure CSS parsing worked
assert label.font().family() == families.family
+ # Skipping the rest of the test as WORKAROUND for
+ # https://bugreports.qt.io/browse/QTBUG-94090
+ return
+
# Try to find out whether the monospace font did a fallback on a non-monospace
# font...
- fallback_label = QLabel()
+ fallback_label = QLabel() # pylint: disable=unreachable
qtbot.add_widget(label)
fallback_label.setText("fallback")
@@ -382,9 +384,5 @@ class TestFontFamilies:
if info.family() == fallback_family:
return
- if info.family() == 'Noto Sans Mono':
- # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-94090
- return
-
# If we didn't fall back, we should've gotten a fixed-pitch font.
assert info.fixedPitch(), info.family()
diff --git a/tests/unit/config/test_qtargs.py b/tests/unit/config/test_qtargs.py
index 4c31c5b07..076ff6e3c 100644
--- a/tests/unit/config/test_qtargs.py
+++ b/tests/unit/config/test_qtargs.py
@@ -249,7 +249,7 @@ class TestWebEngineArgs:
('single-process', True),
])
def test_process_model(self, config_stub, parser, process_model, added):
- config_stub.val.qt.process_model = process_model
+ config_stub.val.qt.chromium.process_model = process_model
parsed = parser.parse_args([])
args = qtargs.qt_args(parsed)
@@ -267,7 +267,7 @@ class TestWebEngineArgs:
('never', '--disable-low-end-device-mode'),
])
def test_low_end_device_mode(self, config_stub, parser, low_end_device_mode, arg):
- config_stub.val.qt.low_end_device_mode = low_end_device_mode
+ config_stub.val.qt.chromium.low_end_device_mode = low_end_device_mode
parsed = parser.parse_args([])
args = qtargs.qt_args(parsed)
@@ -277,6 +277,28 @@ class TestWebEngineArgs:
else:
assert arg in args
+ @pytest.mark.parametrize('sandboxing, arg', [
+ ('enable-all', None),
+ ('disable-seccomp-bpf', '--disable-seccomp-filter-sandbox'),
+ ('disable-all', '--no-sandbox'),
+ ])
+ def test_sandboxing(self, config_stub, parser, sandboxing, arg):
+ config_stub.val.qt.chromium.sandboxing = sandboxing
+ parsed = parser.parse_args([])
+ args = qtargs.qt_args(parsed)
+
+ remaining_flags = {
+ '--no-sandbox',
+ '--disable-seccomp-filter-sandbox',
+ }
+ if arg is not None:
+ remaining_flags.remove(arg)
+
+ if arg is not None:
+ assert arg in args
+
+ assert not set(args) & remaining_flags
+
@pytest.mark.parametrize('qt_version, referer, arg', [
# 'always' -> no arguments
('5.15.0', 'always', None),
@@ -324,7 +346,6 @@ class TestWebEngineArgs:
("light", "5.15.0", False),
("light", "5.15.1", False),
("light", "5.15.2", False),
- ("light", "5.15.2", False),
("light", "5.15.3", False),
("light", "6.0.0", False),
@@ -333,7 +354,6 @@ class TestWebEngineArgs:
("auto", "5.15.0", False),
("auto", "5.15.1", False),
("auto", "5.15.2", False),
- ("auto", "5.15.2", False),
("auto", "5.15.3", False),
("auto", "6.0.0", False),
])
diff --git a/tests/unit/config/test_websettings.py b/tests/unit/config/test_websettings.py
index 7601c7c24..f42563727 100644
--- a/tests/unit/config/test_websettings.py
+++ b/tests/unit/config/test_websettings.py
@@ -24,7 +24,7 @@ from qutebrowser.misc import objects
from qutebrowser.utils import usertypes
-@pytest.mark.parametrize([
+@pytest.mark.parametrize([ # noqa: PT006
'user_agent', 'os_info', 'webkit_version',
'upstream_browser_key', 'upstream_browser_version', 'qt_key'
], [
diff --git a/tests/unit/extensions/test_loader.py b/tests/unit/extensions/test_loader.py
index feb5dd347..e9b8055aa 100644
--- a/tests/unit/extensions/test_loader.py
+++ b/tests/unit/extensions/test_loader.py
@@ -35,16 +35,10 @@ def test_on_walk_error():
def test_walk_normal():
- names = [info.name for info in loader._walk_normal()]
+ names = [info.name for info in loader.walk_components()]
assert 'qutebrowser.components.scrollcommands' in names
-def test_walk_pyinstaller():
- # We can't test whether we get something back without being frozen by
- # PyInstaller, but at least we can test that we don't crash.
- list(loader._walk_pyinstaller())
-
-
def test_load_component(monkeypatch):
monkeypatch.setattr(objects, 'commands', {})
diff --git a/tests/unit/keyinput/test_basekeyparser.py b/tests/unit/keyinput/test_basekeyparser.py
index 30ee36301..84068bf47 100644
--- a/tests/unit/keyinput/test_basekeyparser.py
+++ b/tests/unit/keyinput/test_basekeyparser.py
@@ -346,3 +346,14 @@ def test_clear_keystring_empty(qtbot, keyparser):
keyparser._sequence = keyseq('')
with qtbot.assert_not_emitted(keyparser.keystring_updated):
keyparser.clear_keystring()
+
+
+def test_respect_config_when_matching_counts(keyparser, config_stub):
+ """Don't match counts if disabled in the config."""
+ config_stub.val.input.match_counts = False
+
+ info = keyutils.KeyInfo(Qt.Key_1, Qt.NoModifier)
+ keyparser.handle(info.to_event())
+
+ assert not keyparser._sequence
+ assert not keyparser._count
diff --git a/tests/unit/mainwindow/statusbar/test_textbase.py b/tests/unit/mainwindow/statusbar/test_textbase.py
index 631c6ce44..33c4ffd76 100644
--- a/tests/unit/mainwindow/statusbar/test_textbase.py
+++ b/tests/unit/mainwindow/statusbar/test_textbase.py
@@ -79,11 +79,11 @@ def test_text_elide_none(mocker, qtbot):
label = TextBase()
qtbot.add_widget(label)
label.setText('')
- mocker.patch('qutebrowser.mainwindow.statusbar.textbase.TextBase.'
- 'fontMetrics')
+ mock = mocker.patch(
+ 'qutebrowser.mainwindow.statusbar.textbase.TextBase.fontMetrics')
label._update_elided_text(20)
- assert not label.fontMetrics.called
+ assert not mock.called
def test_unset_text(qtbot):
diff --git a/tests/unit/mainwindow/test_prompt.py b/tests/unit/mainwindow/test_prompt.py
index 5b774bdaa..3218959a5 100644
--- a/tests/unit/mainwindow/test_prompt.py
+++ b/tests/unit/mainwindow/test_prompt.py
@@ -48,9 +48,9 @@ class TestFileCompletion:
return _get_prompt_func
@pytest.mark.parametrize('steps, where, subfolder', [
- (1, 'next', '..'),
+ (1, 'next', 'a'),
(1, 'prev', 'c'),
- (2, 'next', 'a'),
+ (2, 'next', 'b'),
(2, 'prev', 'b'),
])
def test_simple_completion(self, tmp_path, get_prompt, steps, where,
@@ -84,9 +84,7 @@ class TestFileCompletion:
# For some reason, this isn't always called when using qtbot.keyPress.
prompt._set_fileview_root(prompt._lineedit.text())
- # '..' and 'foo' should get completed from 'f'
- prompt.item_focus('next')
- assert prompt._lineedit.text() == str(tmp_path)
+ # 'foo' should get completed from 'f'
prompt.item_focus('next')
assert prompt._lineedit.text() == str(testdir / 'foo')
@@ -94,15 +92,15 @@ class TestFileCompletion:
for _ in range(3):
qtbot.keyPress(prompt._lineedit, Qt.Key_Backspace)
- # We should now show / again, so tabbing twice gives us .. -> bar
+ # We should now show / again, so tabbing twice gives us bar -> foo
prompt.item_focus('next')
prompt.item_focus('next')
- assert prompt._lineedit.text() == str(testdir / 'bar')
+ assert prompt._lineedit.text() == str(testdir / 'foo')
@pytest.mark.parametrize("keys, expected", [
- ([], ['..', 'bar', 'bat', 'foo']),
- ([Qt.Key_F], ['..', 'foo']),
- ([Qt.Key_A], ['..', 'bar', 'bat']),
+ ([], ['bar', 'bat', 'foo']),
+ ([Qt.Key_F], ['foo']),
+ ([Qt.Key_A], ['bar', 'bat']),
])
def test_filtering_path(self, qtbot, tmp_path, get_prompt, keys, expected):
testdir = tmp_path / 'test'
diff --git a/tests/unit/misc/test_checkpyver.py b/tests/unit/misc/test_checkpyver.py
index 52e022a44..35f8cfeec 100644
--- a/tests/unit/misc/test_checkpyver.py
+++ b/tests/unit/misc/test_checkpyver.py
@@ -28,19 +28,18 @@ import pytest
from qutebrowser.misc import checkpyver
-TEXT = (r"At least Python 3.6.1 is required to run qutebrowser, but it's "
+TEXT = (r"At least Python 3.7 is required to run qutebrowser, but it's "
r"running with \d+\.\d+\.\d+.")
@pytest.mark.not_frozen
-@pytest.mark.parametrize('python', ['python2', 'python3.5'])
+@pytest.mark.parametrize('python', ['python2', 'python3.6'])
def test_old_python(python):
"""Run checkpyver with old python versions."""
try:
proc = subprocess.run(
[python, checkpyver.__file__, '--no-err-windows'],
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
+ capture_output=True,
check=False)
except FileNotFoundError:
pytest.skip(f"{python} not found")
diff --git a/tests/unit/misc/test_editor.py b/tests/unit/misc/test_editor.py
index 8e5597a0e..91bdce26b 100644
--- a/tests/unit/misc/test_editor.py
+++ b/tests/unit/misc/test_editor.py
@@ -19,6 +19,7 @@
"""Tests for qutebrowser.misc.editor."""
+import sys
import time
import pathlib
import os
@@ -55,31 +56,33 @@ class TestArg:
def test_placeholder(self, config_stub, editor):
"""Test starting editor with placeholder argument."""
- config_stub.val.editor.command = ['bin', 'foo', '{}', 'bar']
+ config_stub.val.editor.command = [sys.executable, 'foo', '{}', 'bar']
editor.edit("")
editor._proc._proc.start.assert_called_with(
- "bin", ["foo", editor._filename, "bar"])
+ sys.executable, ["foo", editor._filename, "bar"])
def test_placeholder_inline(self, config_stub, editor):
"""Test starting editor with placeholder arg inside of another arg."""
- config_stub.val.editor.command = ['bin', 'foo{}', 'bar']
+ config_stub.val.editor.command = [sys.executable, 'foo{}', 'bar']
editor.edit("")
editor._proc._proc.start.assert_called_with(
- "bin", ["foo" + editor._filename, "bar"])
+ sys.executable, ["foo" + editor._filename, "bar"])
class TestFileHandling:
"""Test creation/deletion of tempfile."""
- def test_ok(self, editor):
+ @pytest.mark.parametrize('remove_file', [True, False])
+ def test_ok(self, editor, remove_file, config_stub):
"""Test file handling when closing with an exit status == 0."""
+ config_stub.val.editor.remove_file = remove_file
editor.edit("")
filename = pathlib.Path(editor._filename)
assert filename.exists()
assert filename.name.startswith('qutebrowser-editor-')
editor._proc._proc.finished.emit(0, QProcess.NormalExit)
- assert not filename.exists()
+ assert filename.exists() != config_stub.val.editor.remove_file
@pytest.mark.parametrize('touch', [True, False])
def test_with_filename(self, editor, tmp_path, touch):
diff --git a/tests/unit/misc/test_elf.py b/tests/unit/misc/test_elf.py
index 86060bbde..7d3248da2 100644
--- a/tests/unit/misc/test_elf.py
+++ b/tests/unit/misc/test_elf.py
@@ -75,6 +75,23 @@ def test_result(qapp, caplog):
assert ua.upstream_browser_version == versions.chromium
+@pytest.mark.parametrize("data, expected", [
+ # Simple match
+ (
+ b"\x00QtWebEngine/5.15.9 Chrome/87.0.4280.144\x00",
+ elf.Versions("5.15.9", "87.0.4280.144"),
+ ),
+ # Ignoring garbage string-like data
+ (
+ b"\x00QtWebEngine/5.15.9 Chrome/87.0.4xternalclearkey\x00\x00"
+ b"QtWebEngine/5.15.9 Chrome/87.0.4280.144\x00",
+ elf.Versions("5.15.9", "87.0.4280.144"),
+ ),
+])
+def test_find_versions(data, expected):
+ assert elf._find_versions(data) == expected
+
+
@hypothesis.given(data=hst.builds(
lambda *a: b''.join(a),
hst.sampled_from([b'', b'\x7fELF', b'\x7fELF\x02\x01\x01']),
diff --git a/tests/unit/misc/test_guiprocess.py b/tests/unit/misc/test_guiprocess.py
index be86bf215..aaff5154e 100644
--- a/tests/unit/misc/test_guiprocess.py
+++ b/tests/unit/misc/test_guiprocess.py
@@ -26,12 +26,12 @@ import pytest
from PyQt5.QtCore import QProcess, QUrl
from qutebrowser.misc import guiprocess
-from qutebrowser.utils import usertypes, utils
+from qutebrowser.utils import usertypes, utils, version
from qutebrowser.api import cmdutils
from qutebrowser.qt import sip
-@pytest.fixture()
+@pytest.fixture
def proc(qtbot, caplog):
"""A fixture providing a GUIProcess and cleaning it up after the test."""
p = guiprocess.GUIProcess('testprocess')
@@ -46,7 +46,7 @@ def proc(qtbot, caplog):
p._proc.waitForFinished()
-@pytest.fixture()
+@pytest.fixture
def fake_proc(monkeypatch, stubs):
"""A fixture providing a GUIProcess with a mocked QProcess."""
p = guiprocess.GUIProcess('testprocess')
@@ -319,8 +319,8 @@ def test_start_env(monkeypatch, qtbot, py_proc):
def test_start_detached(fake_proc):
"""Test starting a detached process."""
- cmd = 'foo'
- args = ['bar']
+ cmd = sys.executable
+ args = ['--version']
fake_proc._proc.startDetached.return_value = (True, 0)
fake_proc.start_detached(cmd, args)
fake_proc._proc.startDetached.assert_called_with(cmd, args, None)
@@ -394,8 +394,11 @@ def test_running(qtbot, proc, py_proc):
proc.outcome.was_successful()
-def test_failing_to_start(qtbot, proc, caplog, message_mock):
+@pytest.mark.parametrize('is_flatpak', [True, False])
+def test_failing_to_start(qtbot, proc, caplog, message_mock, monkeypatch, is_flatpak):
"""Test the process failing to start."""
+ monkeypatch.setattr(version, 'is_flatpak', lambda: is_flatpak)
+
with caplog.at_level(logging.ERROR, 'message'):
with qtbot.wait_signal(proc.error, timeout=5000):
proc.start('this_does_not_exist_either', [])
@@ -405,8 +408,11 @@ def test_failing_to_start(qtbot, proc, caplog, message_mock):
"Testprocess 'this_does_not_exist_either' failed to start:")
if not utils.is_windows:
- assert msg.text.endswith(
- "(Hint: Make sure 'this_does_not_exist_either' exists and is executable)")
+ expected_msg = (
+ "Hint: Make sure 'this_does_not_exist_either' exists and is executable")
+ if is_flatpak:
+ expected_msg += ' inside the Flatpak container'
+ assert msg.text.endswith(expected_msg)
assert not proc.outcome.running
assert proc.outcome.status is None
diff --git a/tests/unit/misc/test_keyhints.py b/tests/unit/misc/test_keyhints.py
index 8d9c53c93..922f39331 100644
--- a/tests/unit/misc/test_keyhints.py
+++ b/tests/unit/misc/test_keyhints.py
@@ -30,8 +30,8 @@ def expected_text(*args):
"""Helper to format text we expect the KeyHintView to generate.
Args:
- args: One tuple for each row in the expected output.
- Tuples are of the form: (prefix, color, suffix, command).
+ *args: One tuple for each row in the expected output.
+ Tuples are of the form: (prefix, color, suffix, command).
"""
text = '<table>'
for group in args:
diff --git a/tests/unit/misc/test_miscwidgets.py b/tests/unit/misc/test_miscwidgets.py
index 6e1919ec3..3c5c1eef1 100644
--- a/tests/unit/misc/test_miscwidgets.py
+++ b/tests/unit/misc/test_miscwidgets.py
@@ -38,7 +38,7 @@ class TestCommandLineEdit:
cmd_edit.set_prompt(':')
qtbot.add_widget(cmd_edit)
assert cmd_edit.text() == ''
- yield cmd_edit
+ return cmd_edit
def test_position(self, qtbot, cmd_edit):
"""Test cursor position based on the prompt."""
diff --git a/tests/unit/misc/userscripts/test_qute_lastpass.py b/tests/unit/misc/userscripts/test_qute_lastpass.py
index 24272a048..ebd9a7591 100644
--- a/tests/unit/misc/userscripts/test_qute_lastpass.py
+++ b/tests/unit/misc/userscripts/test_qute_lastpass.py
@@ -122,7 +122,7 @@ class TestQuteLastPassComponents:
subprocess_mock.assert_called_once_with(
['lpass', 'show', '-x', '-j', '-G', '\\bexample\\.com'],
- stdout=ANY, stderr=ANY)
+ capture_output=True)
def test_pass_returns_candidates(self, subprocess_mock):
"""Test if pass_ returns expected lpass site entry."""
@@ -263,7 +263,7 @@ class TestQuteLastPassMain:
subprocess_mock.assert_has_calls([
call(['lpass', 'show', '-x', '-j', '-G', '\\bwww\\.example\\.com'],
- stdout=ANY, stderr=ANY),
+ capture_output=True),
call(['rofi', '-dmenu'],
input=b'12345 | www.example.com | https://www.example.com | fake@fake.com\n23456 | Sites/www.example.com | https://www.example.com | john.doe@fake.com',
stdout=ANY)
@@ -328,13 +328,13 @@ class TestQuteLastPassMain:
subprocess_mock.assert_has_calls([
call(['lpass', 'show', '-x', '-j', '-G', '\\bwww\\.example\\.com'],
- stdout=ANY, stderr=ANY),
+ capture_output=True),
call(['lpass', 'show', '-x', '-j', '-G', '\\bexample\\.com'],
- stdout=ANY, stderr=ANY),
+ capture_output=True),
call(['lpass', 'show', '-x', '-j', '-G', '\\bwwwexample'],
- stdout=ANY, stderr=ANY),
+ capture_output=True),
call(['lpass', 'show', '-x', '-j', '-G', '\\bexample'],
- stdout=ANY, stderr=ANY),
+ capture_output=True),
call(['rofi', '-dmenu'],
input=b'12345 | www.example.com | https://www.example.com | fake@fake.com\n23456 | Sites/www.example.com | https://www.example.com | john.doe@fake.com\n345 | example.com | https://example.com | joe.doe@fake.com\n456 | Sites/example.com | http://example.com | jane.doe@fake.com',
stdout=ANY)
diff --git a/tests/unit/test_qutebrowser.py b/tests/unit/test_qutebrowser.py
index d9275631d..36b4065a1 100644
--- a/tests/unit/test_qutebrowser.py
+++ b/tests/unit/test_qutebrowser.py
@@ -22,6 +22,8 @@
(Mainly commandline flag parsing)
"""
+import re
+
import pytest
from qutebrowser import qutebrowser
@@ -75,3 +77,61 @@ class TestJsonArgs:
# pylint: disable=no-member
assert args.debug
assert not args.temp_basedir
+
+
+class TestValidateUntrustedArgs:
+
+ @pytest.mark.parametrize('args', [
+ [],
+ [':nop'],
+ [':nop', '--untrusted-args'],
+ [':nop', '--debug', '--untrusted-args'],
+ [':nop', '--untrusted-args', 'foo'],
+ ['--debug', '--untrusted-args', 'foo'],
+ ['foo', '--untrusted-args', 'bar'],
+ ])
+ def test_valid(self, args):
+ qutebrowser._validate_untrusted_args(args)
+
+ @pytest.mark.parametrize('args, message', [
+ (
+ ['--untrusted-args', '--debug'],
+ "Found --debug after --untrusted-args, aborting.",
+ ),
+ (
+ ['--untrusted-args', ':nop'],
+ "Found :nop after --untrusted-args, aborting.",
+ ),
+ (
+ ['--debug', '--untrusted-args', '--debug'],
+ "Found --debug after --untrusted-args, aborting.",
+ ),
+ (
+ [':nop', '--untrusted-args', '--debug'],
+ "Found --debug after --untrusted-args, aborting.",
+ ),
+ (
+ [':nop', '--untrusted-args', ':nop'],
+ "Found :nop after --untrusted-args, aborting.",
+ ),
+ (
+ [
+ ':nop',
+ '--untrusted-args',
+ ':nop',
+ '--untrusted-args',
+ 'https://www.example.org',
+ ],
+ (
+ "Found multiple arguments (:nop --untrusted-args "
+ "https://www.example.org) after --untrusted-args, aborting."
+ )
+ ),
+ (
+ ['--untrusted-args', 'okay1', 'okay2'],
+ "Found multiple arguments (okay1 okay2) after --untrusted-args, aborting.",
+ ),
+ ])
+ def test_invalid(self, args, message):
+ with pytest.raises(SystemExit, match=re.escape(message)):
+ qutebrowser._validate_untrusted_args(args)
diff --git a/tests/unit/utils/test_log.py b/tests/unit/utils/test_log.py
index 7682c1156..20938d6fb 100644
--- a/tests/unit/utils/test_log.py
+++ b/tests/unit/utils/test_log.py
@@ -27,7 +27,7 @@ import warnings
import dataclasses
import pytest
-import _pytest.logging
+import _pytest.logging # pylint: disable=import-private-name
from PyQt5 import QtCore
from qutebrowser import qutebrowser
@@ -53,8 +53,7 @@ def restore_loggers():
saved_level_to_name = logging._levelToName.copy()
logger_states = {}
for name in saved_loggers:
- logger_states[name] = getattr(saved_loggers[name], 'disabled',
- None)
+ logger_states[name] = getattr(saved_loggers[name], 'disabled', None)
finally:
logging._releaseLock()
@@ -86,9 +85,9 @@ def restore_loggers():
logger_dict = logging.getLogger().manager.loggerDict
logger_dict.clear()
logger_dict.update(saved_loggers)
- for name in logger_states:
- if logger_states[name] is not None:
- saved_loggers[name].disabled = logger_states[name]
+ for name, state in logger_states.items():
+ if state is not None:
+ saved_loggers[name].disabled = state
finally:
logging._releaseLock()
@@ -237,6 +236,7 @@ class TestInitLog:
"""Tests for init_log."""
def _get_default_args(self):
+ # pylint: disable-next=unused-variable
return argparse.Namespace(debug=True, loglevel='debug', color=True,
loglines=10, logfilter=None,
force_color=False, json_logging=False,
@@ -349,7 +349,7 @@ class TestHideQtWarning:
"""Tests for hide_qt_warning/QtWarningFilter."""
- @pytest.fixture()
+ @pytest.fixture
def qt_logger(self):
return logging.getLogger('qt-tests')
diff --git a/tests/unit/utils/test_qtutils.py b/tests/unit/utils/test_qtutils.py
index 2d98feece..ad9adf032 100644
--- a/tests/unit/utils/test_qtutils.py
+++ b/tests/unit/utils/test_qtutils.py
@@ -51,9 +51,7 @@ else:
test_file = None
-# pylint: disable=bad-continuation
-@pytest.mark.parametrize(['qversion', 'compiled', 'pyqt', 'version', 'exact',
- 'expected'], [
+@pytest.mark.parametrize('qversion, compiled, pyqt, version, exact, expected', [
# equal versions
('5.14.0', None, None, '5.14.0', False, True),
('5.14.0', None, None, '5.14.0', True, True), # exact=True
@@ -75,7 +73,6 @@ else:
# dev suffix
('5.15.1', '5.15.1', '5.15.2.dev2009281246', '5.15.0', False, True),
])
-# pylint: enable=bad-continuation
def test_version_check(monkeypatch, qversion, compiled, pyqt, version, exact,
expected):
"""Test for version_check().
@@ -559,7 +556,8 @@ if test_file is not None:
qiodev.mode = mode
# Create empty TESTFN file because the Python tests try to unlink
# it.after the test.
- open(test_file.TESTFN, 'w', encoding='utf-8').close()
+ with open(test_file.TESTFN, 'w', encoding='utf-8'):
+ pass
return qiodev
class PyAutoFileTests(PyIODeviceTestMixin, test_file.AutoFileTests,
@@ -742,7 +740,7 @@ class TestPyQIODevice:
with pytest.raises(io.UnsupportedOperation):
pyqiodev.seek(0, whence)
- @pytest.mark.flaky()
+ @pytest.mark.flaky
def test_qprocess(self, py_proc):
"""Test PyQIODevice with a QProcess which is non-sequential.
diff --git a/tests/unit/utils/test_resources.py b/tests/unit/utils/test_resources.py
index 738fadd37..6a11ff588 100644
--- a/tests/unit/utils/test_resources.py
+++ b/tests/unit/utils/test_resources.py
@@ -65,7 +65,7 @@ class TestReadFile:
'qutebrowser/html/unrelatedhtml',
]
- yield zipfile.Path(zip_path) / 'qutebrowser'
+ return zipfile.Path(zip_path) / 'qutebrowser'
@pytest.fixture(params=['pathlib', 'zipfile'])
def resource_root(self, request):
diff --git a/tests/unit/utils/test_standarddir.py b/tests/unit/utils/test_standarddir.py
index e05b5d31f..0ca635ae2 100644
--- a/tests/unit/utils/test_standarddir.py
+++ b/tests/unit/utils/test_standarddir.py
@@ -31,7 +31,7 @@ import subprocess
from PyQt5.QtCore import QStandardPaths
import pytest
-from qutebrowser.utils import standarddir, utils, qtutils
+from qutebrowser.utils import standarddir, utils, qtutils, version
# Use a different application name for tests to make sure we don't change real
@@ -399,12 +399,18 @@ class TestSystemData:
"""Test system data path."""
@pytest.mark.linux
- def test_system_datadir_exist_linux(self, monkeypatch, tmpdir):
+ @pytest.mark.parametrize("is_flatpak, expected", [
+ (True, "/app/share/qute_test"),
+ (False, "/usr/share/qute_test"),
+ ])
+ def test_system_datadir_exist_linux(self, monkeypatch, tmpdir,
+ is_flatpak, expected):
"""Test that /usr/share/qute_test is used if path exists."""
monkeypatch.setenv('XDG_DATA_HOME', str(tmpdir))
monkeypatch.setattr(os.path, 'exists', lambda path: True)
+ monkeypatch.setattr(version, 'is_flatpak', lambda: is_flatpak)
standarddir._init_data(args=None)
- assert standarddir.data(system=True) == "/usr/share/qute_test"
+ assert standarddir.data(system=True) == expected
@pytest.mark.linux
def test_system_datadir_not_exist_linux(self, monkeypatch, tmpdir,
@@ -492,7 +498,7 @@ def test_no_qapplication(qapp, tmpdir, monkeypatch):
monkeypatch.setenv('HOME', str(home_dir))
proc = subprocess.run([sys.executable, str(pyfile)] + sys.path,
- universal_newlines=True,
+ text=True,
check=True,
stdout=subprocess.PIPE)
sub_locations = json.loads(proc.stdout)
diff --git a/tests/unit/utils/test_urlmatch.py b/tests/unit/utils/test_urlmatch.py
index 35ccc94fe..caf52c76d 100644
--- a/tests/unit/utils/test_urlmatch.py
+++ b/tests/unit/utils/test_urlmatch.py
@@ -37,24 +37,30 @@ from PyQt5.QtCore import QUrl
from qutebrowser.utils import urlmatch
+# pylint: disable=line-too-long
+
@pytest.mark.parametrize('pattern, error', [
### Chromium: kMissingSchemeSeparator
## TEST(ExtensionURLPatternTest, ParseInvalid)
# ("http", "No scheme given"),
- ("http:", "Invalid port: Port is empty"),
- ("http:/", "Invalid port: Port is empty"),
- ("about://", "Pattern without path"),
- ("http:/bar", "Invalid port: Port is empty"),
+ pytest.param("http:", "Invalid port: Port is empty", id='scheme-no-slash'),
+ pytest.param("http:/", "Invalid port: Port is empty", id='scheme-single-slash'),
+ pytest.param("about://", "Pattern without path", id='scheme-no-path'),
+ pytest.param(
+ "http:/bar",
+ "Invalid port: Port is empty",
+ id='scheme-single-slash-path',
+ ),
### Chromium: kEmptyHost
## TEST(ExtensionURLPatternTest, ParseInvalid)
- ("http://", "Pattern without host"),
- ("http:///", "Pattern without host"),
- ("http://:1234/", "Pattern without host"),
- ("http://*./", "Pattern without host"),
+ pytest.param("http://", "Pattern without host", id='host-double-slash'),
+ pytest.param("http:///", "Pattern without host", id='host-triple-slash'),
+ pytest.param("http://:1234/", "Pattern without host", id='host-port'),
+ pytest.param("http://*./", "Pattern without host", id='host-pattern'),
## TEST(ExtensionURLPatternTest, IPv6Patterns)
- ("http://[]:8888/*", "Pattern without host"),
+ pytest.param("http://[]:8888/*", "Pattern without host", id='host-ipv6'),
### Chromium: kEmptyPath
## TEST(ExtensionURLPatternTest, ParseInvalid)
@@ -63,53 +69,132 @@ from qutebrowser.utils import urlmatch
### Chromium: kInvalidHost
## TEST(ExtensionURLPatternTest, ParseInvalid)
- ("http://\0www/", "May not contain NUL byte"),
+ pytest.param("http://\0www/", "May not contain NUL byte", id='host-nul'),
## TEST(ExtensionURLPatternTest, IPv6Patterns)
# No closing bracket (`]`).
- ("http://[2607:f8b0:4005:805::200e/*", "Invalid IPv6 URL"),
+ pytest.param(
+ "http://[2607:f8b0:4005:805::200e/*",
+ "Invalid IPv6 URL",
+ id='host-ipv6-no-closing',
+ ),
# Two closing brackets (`]]`).
- pytest.param("http://[2607:f8b0:4005:805::200e]]/*", "Invalid IPv6 URL", marks=pytest.mark.xfail(reason="https://bugs.python.org/issue34360")),
+ pytest.param(
+ "http://[2607:f8b0:4005:805::200e]]/*",
+ "Invalid IPv6 URL",
+ marks=pytest.mark.xfail(reason="https://bugs.python.org/issue34360"),
+ id='host-ipv6-two-closing',
+ ),
# Two open brackets (`[[`).
- ("http://[[2607:f8b0:4005:805::200e]/*", r"""Expected '\]' to match '\[' in hostname; source was "\[2607:f8b0:4005:805::200e"; host = """""),
+ pytest.param(
+ "http://[[2607:f8b0:4005:805::200e]/*",
+ r"""Expected '\]' to match '\[' in hostname; source was "\[2607:f8b0:4005:805::200e"; host = """"",
+ id='host-ipv6-two-open',
+ ),
# Too few colons in the last chunk.
- ("http://[2607:f8b0:4005:805:200e]/*", 'Invalid IPv6 address; source was "2607:f8b0:4005:805:200e"; host = ""'),
+ pytest.param(
+ "http://[2607:f8b0:4005:805:200e]/*",
+ 'Invalid IPv6 address; source was "2607:f8b0:4005:805:200e"; host = ""',
+ id='host-ipv6-colons',
+ ),
# Non-hex piece.
- ("http://[2607:f8b0:4005:805:200e:12:bogus]/*", 'Invalid IPv6 address; source was "2607:f8b0:4005:805:200e:12:bogus"; host = ""'),
+ pytest.param(
+ "http://[2607:f8b0:4005:805:200e:12:bogus]/*",
+ 'Invalid IPv6 address; source was "2607:f8b0:4005:805:200e:12:bogus"; host = ""',
+ id='host-ipv6-non-hex',
+ ),
### Chromium: kInvalidHostWildcard
## TEST(ExtensionURLPatternTest, ParseInvalid)
- ("http://*foo/bar", "Invalid host wildcard"),
- ("http://foo.*.bar/baz", "Invalid host wildcard"),
- ("http://fo.*.ba:123/baz", "Invalid host wildcard"),
- ("http://foo.*/bar", "Invalid host wildcard"),
+ pytest.param("http://*foo/bar", "Invalid host wildcard", id='host-wildcard-no-dot'),
+ pytest.param(
+ "http://foo.*.bar/baz",
+ "Invalid host wildcard",
+ id='host-wildcard-middle',
+ ),
+ pytest.param(
+ "http://fo.*.ba:123/baz",
+ "Invalid host wildcard",
+ id='host-wildcard-middle-port',
+ ),
+ pytest.param("http://foo.*/bar", "Invalid host wildcard", id='host-wildcard-end'),
### Chromium: kInvalidPort
## TEST(ExtensionURLPatternTest, Ports)
- ("http://foo:/", "Invalid port: Port is empty"),
- ("http://*.foo:/", "Invalid port: Port is empty"),
- ("http://foo:com/", "Invalid port: .* 'com'"),
- ("http://foo:123456/", "Invalid port: Port out of range 0-65535"),
- ("http://foo:80:80/monkey", "Invalid port: .* '80:80'"),
- ("chrome://foo:1234/bar", "Ports are unsupported with chrome scheme"),
+ pytest.param("http://foo:/", "Invalid port: Port is empty", id='port-empty'),
+ pytest.param(
+ "http://*.foo:/",
+ "Invalid port: Port is empty",
+ id='port-empty-wildcard',
+ ),
+ pytest.param("http://foo:com/", "Invalid port: .* 'com'", id='port-alpha'),
+ pytest.param(
+ "http://foo:123456/",
+ "Invalid port: Port out of range 0-65535",
+ id='port-range',
+ ),
+ pytest.param(
+ "http://foo:80:80/monkey",
+ "Invalid port: .* '80:80'",
+ id='port-double',
+ ),
+ pytest.param(
+ "chrome://foo:1234/bar",
+ "Ports are unsupported with chrome scheme",
+ id='port-chrome',
+ ),
# No port specified, but port separator.
- ("http://[2607:f8b0:4005:805::200e]:/*", "Invalid port: Port is empty"),
+ pytest.param(
+ "http://[2607:f8b0:4005:805::200e]:/*",
+ "Invalid port: Port is empty",
+ id='port-empty-ipv6',
+ ),
### Additional tests
- ("http://[", "Invalid IPv6 URL"),
- ("http://[fc2e::bb88::edac]", 'Invalid IPv6 address; source was "fc2e::bb88::edac"; host = ""'),
- ("http://[fc2e:0e35:bb88::edac:fc2e:0e35:bb88:edac]", 'Invalid IPv6 address; source was "fc2e:0e35:bb88::edac:fc2e:0e35:bb88:edac"; host = ""'),
- ("http://[fc2e:0e35:bb88:af:edac:fc2e:0e35:bb88:edac]", 'Invalid IPv6 address; source was "fc2e:0e35:bb88:af:edac:fc2e:0e35:bb88:edac"; host = ""'),
- ("http://[127.0.0.1:fc2e::bb88:edac]", r'Invalid IPv6 address; source was "127\.0\.0\.1:fc2e::bb88:edac'),
- ("http://[fc2e::bb88", "Invalid IPv6 URL"),
- ("http://[fc2e:bb88:edac]", 'Invalid IPv6 address; source was "fc2e:bb88:edac"; host = ""'),
- ("http://[fc2e:bb88:edac::z]", 'Invalid IPv6 address; source was "fc2e:bb88:edac::z"; host = ""'),
- ("http://[fc2e:bb88:edac::2]:2a2", "Invalid port: .* '2a2'"),
- ("://", "Missing scheme"),
+ pytest.param("http://[", "Invalid IPv6 URL", id='ipv6-single-open'),
+ pytest.param(
+ "http://[fc2e::bb88::edac]",
+ 'Invalid IPv6 address; source was "fc2e::bb88::edac"; host = ""',
+ id='ipv6-double-double',
+ ),
+ pytest.param(
+ "http://[fc2e:0e35:bb88::edac:fc2e:0e35:bb88:edac]",
+ 'Invalid IPv6 address; source was "fc2e:0e35:bb88::edac:fc2e:0e35:bb88:edac"; host = ""',
+ id='ipv6-long-double',
+ ),
+ pytest.param(
+ "http://[fc2e:0e35:bb88:af:edac:fc2e:0e35:bb88:edac]",
+ 'Invalid IPv6 address; source was "fc2e:0e35:bb88:af:edac:fc2e:0e35:bb88:edac"; host = ""',
+ id='ipv6-long',
+ ),
+ pytest.param(
+ "http://[127.0.0.1:fc2e::bb88:edac]",
+ r'Invalid IPv6 address; source was "127\.0\.0\.1:fc2e::bb88:edac',
+ id='ipv6-ipv4',
+ ),
+ pytest.param("http://[fc2e::bb88", "Invalid IPv6 URL", id='ipv6-trailing'),
+ pytest.param(
+ "http://[fc2e:bb88:edac]",
+ 'Invalid IPv6 address; source was "fc2e:bb88:edac"; host = ""',
+ id='ipv6-short',
+ ),
+ pytest.param(
+ "http://[fc2e:bb88:edac::z]",
+ 'Invalid IPv6 address; source was "fc2e:bb88:edac::z"; host = ""',
+ id='ipv6-z',
+ ),
+ pytest.param(
+ "http://[fc2e:bb88:edac::2]:2a2",
+ "Invalid port: .* '2a2'",
+ id='ipv6-port',
+ ),
+ pytest.param("://", "Missing scheme", id='scheme-naked'),
])
def test_invalid_patterns(pattern, error):
with pytest.raises(urlmatch.ParseError, match=error):
urlmatch.UrlPattern(pattern)
+# pylint: enable=line-too-long
+
@pytest.mark.parametrize('host', ['.', ' ', ' .', '. ', '. .', '. . .', ' . '])
def test_whitespace_hosts(host):
diff --git a/tests/unit/utils/test_urlutils.py b/tests/unit/utils/test_urlutils.py
index 97ff268ca..e5773e25e 100644
--- a/tests/unit/utils/test_urlutils.py
+++ b/tests/unit/utils/test_urlutils.py
@@ -334,7 +334,6 @@ def test_get_search_url_open_base_url(config_stub, url, host):
Args:
url: The "URL" to enter.
host: The expected search machine host.
- query: The expected search query.
"""
config_stub.val.url.open_base_url = True
url = urlutils._get_search_url(url)
diff --git a/tests/unit/utils/test_utils.py b/tests/unit/utils/test_utils.py
index 57adc883c..c833aa677 100644
--- a/tests/unit/utils/test_utils.py
+++ b/tests/unit/utils/test_utils.py
@@ -30,7 +30,7 @@ import shlex
import math
import operator
-from PyQt5.QtCore import QUrl, QRect
+from PyQt5.QtCore import QUrl, QRect, QPoint
from PyQt5.QtGui import QClipboard
import pytest
import hypothesis
@@ -603,6 +603,15 @@ class TestSanitizeFilename:
LONG_EXTENSION = (LONG_FILENAME.replace("filename", ".extension")
.replace(".txt", ""))
+ # first four-byte unicode char
+ U10K = "\U00010000"
+
+ LONG_4BYTE = U10K * 64
+ LONG_4BYTE_SHORTENED = U10K * 60
+
+ LONG_4BYTE_EXT = f"{U10K * 8}.{U10K * 64}"
+ LONG_4BYTE_EXT_SHORTENED = f"{U10K}.{U10K * 59}"
+
@pytest.mark.parametrize('inp, expected', [
pytest.param('normal.txt', 'normal.txt',
marks=pytest.mark.fake_os('windows')),
@@ -629,6 +638,14 @@ class TestSanitizeFilename:
LONG_EXTENSION.replace("this is a very long .extension",
"this .extension"),
),
+ (
+ LONG_4BYTE,
+ LONG_4BYTE_SHORTENED,
+ ),
+ (
+ LONG_4BYTE_EXT,
+ LONG_4BYTE_EXT_SHORTENED,
+ )
])
@pytest.mark.linux
def test_shorten(self, inp, expected):
@@ -809,8 +826,11 @@ class TestYaml:
assert utils.yaml_load("[1, 2]") == [1, 2]
def test_load_float_bug(self):
- with pytest.raises(yaml.YAMLError):
+ try:
utils.yaml_load("._")
+ except yaml.YAMLError:
+ # Either no exception or YAMLError, not ValueError
+ pass
def test_load_file(self, tmp_path):
tmpfile = tmp_path / 'foo.yml'
@@ -928,7 +948,7 @@ def test_parse_duration_hypothesis(duration):
@pytest.mark.parametrize('mimetype, extension', [
('application/pdf', '.pdf'), # handled by Python
- ('text/plain', '.txt'), # wrong in Python 3.6, overridden
+ ('text/plain', '.txt'), # was wrong in Python 3.6, handled now
('application/manifest+json', '.webmanifest'), # newer
('text/xul', '.xul'), # strict=False
('doesnot/exist', None),
@@ -1023,3 +1043,44 @@ class TestParseRect:
utils.parse_rect(s)
except ValueError as e:
print(e)
+
+
+class TestParsePoint:
+
+ @pytest.mark.parametrize('value, expected', [
+ ('1,1', QPoint(1, 1)),
+ ('123,789', QPoint(123, 789)),
+ ('-123,-789', QPoint(-123, -789)),
+ ])
+ def test_valid(self, value, expected):
+ assert utils.parse_point(value) == expected
+
+ @pytest.mark.parametrize('value, message', [
+ ('1x1', "String 1x1 does not match X,Y"),
+ ('1e0,1', "String 1e0,1 does not match X,Y"),
+ ('a,1', "String a,1 does not match X,Y"),
+ ('¹,1', "String ¹,1 does not match X,Y"),
+ ('1,,1', "String 1,,1 does not match X,Y"),
+ ('1', "String 1 does not match X,Y"),
+ ])
+ def test_invalid(self, value, message):
+ with pytest.raises(ValueError) as excinfo:
+ utils.parse_point(value)
+ assert str(excinfo.value) == message
+
+ @hypothesis.given(strategies.text())
+ def test_hypothesis_text(self, s):
+ try:
+ utils.parse_point(s)
+ except ValueError as e:
+ print(e)
+
+ @hypothesis.given(strategies.tuples(
+ strategies.integers(),
+ strategies.integers(),
+ ).map(lambda t: ",".join(map(str, t))))
+ def test_hypothesis_sophisticated(self, s):
+ try:
+ utils.parse_point(s)
+ except ValueError as e:
+ print(e)
diff --git a/tests/unit/utils/test_version.py b/tests/unit/utils/test_version.py
index 6c57cb3d3..64df0ece2 100644
--- a/tests/unit/utils/test_version.py
+++ b/tests/unit/utils/test_version.py
@@ -484,17 +484,20 @@ class TestGitStrSubprocess:
@needs_git
def test_real_git(self, git_repo):
"""Test with a real git repository."""
- branch_name = subprocess.run(
- ['git', 'config', 'init.defaultBranch'],
- check=False,
- stdout=subprocess.PIPE,
- encoding='utf-8',
- ).stdout.strip()
- if not branch_name:
- branch_name = 'master'
+ def _get_git_setting(name, default):
+ return subprocess.run(
+ ['git', 'config', '--default', default, name],
+ check=True,
+ stdout=subprocess.PIPE,
+ encoding='utf-8',
+ ).stdout.strip()
ret = version._git_str_subprocess(str(git_repo))
- assert ret == f'6e4b65a on {branch_name} (1970-01-01 01:00:00 +0100)'
+ branch_name = _get_git_setting('init.defaultBranch', 'master')
+ abbrev_length = int(_get_git_setting('core.abbrev', '7'))
+ expected_sha = '6e4b65a529c0ab78fb370c1527d5809f7436b8f3'[:abbrev_length]
+
+ assert ret == f'{expected_sha} on {branch_name} (1970-01-01 01:00:00 +0100)'
def test_missing_dir(self, tmp_path):
"""Test with a directory which doesn't exist."""
@@ -718,7 +721,7 @@ class TestModuleVersions:
Args:
attribute: The name of the version attribute.
- expected: The expected return value.
+ expected_modules: The expected modules with that attribute.
"""
import_fake.version_attribute = attribute
@@ -1460,7 +1463,11 @@ def test_uptime(monkeypatch, qapp):
monkeypatch.setattr(qapp, "launch_time", launch_time, raising=False)
class FakeDateTime(datetime.datetime):
- now = lambda x=datetime.datetime(1, 1, 1, 1, 1, 1, 2): x
+
+ @classmethod
+ def now(cls, tz=None):
+ return datetime.datetime(1, 1, 1, 1, 1, 1, 2)
+
monkeypatch.setattr(datetime, 'datetime', FakeDateTime)
uptime_delta = version._uptime()
diff --git a/tox.ini b/tox.ini
index 4be5b8620..19975a8a3 100644
--- a/tox.ini
+++ b/tox.ini
@@ -13,17 +13,16 @@ minversion = 3.15
setenv =
PYTEST_QT_API=pyqt5
pyqt{,512,513,514,515,5150}: LINK_PYQT_SKIP=true
- pyqt{,512,513,514,515,5150}: QUTE_BDD_WEBENGINE=true
cov: PYTEST_ADDOPTS=--cov --cov-report xml --cov-report=html --cov-report=
-passenv = PYTHON DISPLAY XAUTHORITY HOME USERNAME USER CI XDG_* QUTE_* DOCKER QT_QUICK_BACKEND PY_COLORS DBUS_SESSION_BUS_ADDRESS
+passenv = PYTHON DISPLAY XAUTHORITY HOME USERNAME USER CI XDG_* QUTE_* DOCKER QT_QUICK_BACKEND FORCE_COLOR DBUS_SESSION_BUS_ADDRESS
basepython =
py: {env:PYTHON:python3}
py3: {env:PYTHON:python3}
- py36: {env:PYTHON:python3.6}
py37: {env:PYTHON:python3.7}
py38: {env:PYTHON:python3.8}
py39: {env:PYTHON:python3.9}
py310: {env:PYTHON:python3.10}
+ py311: {env:PYTHON:python3.11}
deps =
-r{toxinidir}/requirements.txt
-r{toxinidir}/misc/requirements/requirements-tests.txt
@@ -42,7 +41,6 @@ commands =
basepython = {env:PYTHON:python3}
setenv =
PYTEST_QT_API=pyqt5
- QUTE_BDD_WEBENGINE=true
pip_pre = true
deps = -r{toxinidir}/misc/requirements/requirements-tests-bleeding.txt
commands_pre = pip install --index-url https://www.riverbankcomputing.com/pypi/simple/ --pre --upgrade PyQt5 PyQtWebEngine
@@ -80,7 +78,7 @@ commands =
{[testenv:vulture]commands}
[testenv:pylint]
-basepython = {env:PYTHON:python3.8}
+basepython = {env:PYTHON:python3}
ignore_errors = true
passenv =
deps =
@@ -160,7 +158,7 @@ passenv = APPDATA HOME PYINSTALLER_DEBUG
deps =
-r{toxinidir}/requirements.txt
-r{toxinidir}/misc/requirements/requirements-pyinstaller.txt
- -r{toxinidir}/misc/requirements/requirements-pyqt-pyinstaller.txt
+ -r{toxinidir}/misc/requirements/requirements-pyqt.txt
commands =
{envbindir}/pyinstaller --noconfirm misc/qutebrowser.spec